java:类对象(class对象)——类是类,对象是对象,类对象是什么鬼?“类对象”和“类的对象”是一样的吗?

一、“类对象”是什么意思?

1. 概念

一句话总结,类对象是JVM在加载一个类时,创建的用于描述这个类、记录这个类的一些信息的这么一个对象。

2. 解释

在 Java 中,每一个 .class 文件(比如 User.class)在程序运行时都会被 JVM 加载。

当 JVM 加载一个类时,会在内存中创建一个属于该类的“描述对象”,这个对象的类型是:

java.lang.Class

所以:

Java 源代码中的类运行时中的对应对象
class User {}一个 Class<User> 对象

比如你写了:

class User {...}

运行时,会存在一个对象:

Class<User> clazz = User.class;

这个对象就叫 类对象(Class Object)


 二、类对象的作用

作用类别具体作用示例代码说明
1. 描述类结构(元数据)获取类的字段、方法、构造器等clazz.getDeclaredFields()Class 对象保存了 类的“说明书”,包含所有结构信息
2. 创建类实例(反射)使用反射 new 对象A a = clazz.newInstance();不通过 new A(),而是通过类对象创建实例
3. 作为类锁(synchronized)同步静态方法或 class 代码块synchronized(A.class) {}Class 对象是类级别唯一的,用来做类锁
4. 类的唯一标识JVM 中一个类对应唯一一个 Class 对象A.class == a.getClass()判断两个对象是否是同一类型的根本依据
5. 类加载相关信息获取类加载器clazz.getClassLoader()Class 对象记录类由谁加载(比如 AppClassLoader)
6. 反射操作字段/方法访问与调用字段、方法field.set(a, 10);必须先拿到类对象才能继续反射
7. 泛型检查(擦除后类型信息)在运行时检查对象类型obj.getClass()instanceof 与 getClass 均依赖类对象
8. 获取父类与接口信息查看继承结构clazz.getSuperclass()查看类的继承链、实现的接口
9. 资源加载(常见技巧)通过类对象读取资源文件clazz.getResourceAsStream("config.txt")类对象会找到与它同路径的资源

三、类对象 和 类的对象 区别

1. 具体区别

一个类A,它的类对象不是他的对象。

A的类对象:是一个 Class 类型实例,由 JVM 在加载 A 时创建,它不是 A 的实例,而是 “描述 A 的对象”。

A的对象:是一个A类型的实例。

下面是具体区别

对比项目类对象(Class 对象)类的对象(实例对象)
是什么描述某个类的“说明书”该类真正创建出来的对象
类型Class<A>A
创建者JVM 自动创建(类加载时,由类加载器ClassLoader创建)使用 new A() 或反射创建
数量每个类只有 1 个 类对象可以有 无数个实例对象
生命周期在类加载时产生,直到类卸载才消失new 出来,GC 后销毁
用途反射、类锁、查看类结构执行对象的行为,存数据
是否包含成员变量数据❌ 不包含实例字段数据✔ 包含自己的字段(state)
是否能调用方法✔ 能反射调用方法(但不能执行具体实例行为)✔ 直接调用实例方法
是否能当锁使用✔ 可以(类锁)synchronized(A.class)✔ 可以(对象锁)synchronized(obj)
获取方式A.classobj.getClass()new A()
代表的含义类的元数据(Metadata)类的具体数据对象(Instance)
举例(类型)Class<Person>Person p = new Person()
2. 在JVM中的区别
2.1 JVM内存结构

JVM(HotSpot)中和类/对象相关的区域主要有:

内存区域内容
方法区(Method Area / MetaSpace)存类的元数据
堆(Heap)存实例对象(包括类对象 Class 信息)
栈(Stack)存局部变量(引用等)
程序计数器/本地方法栈不相关,此处略

所以:

  • 类的元数据存在方法区(元空间),因为它是类真正的本体,一个模板

  • 类对象(Class 对象)存在堆,因为它是对象

  • 实例对象存在堆


2.2 类对象内存结构(Class 对象)

每个类加载时,JVM 会在 堆区 中创建它的类对象(Class 实例)。

✔ 类对象内存结构示意图(Class<Person>)

堆(Heap)
┌───────────────────────────────────────────┐
│ 类对象(Class<Person>)                   │
│-------------------------------------------│
│ 类名:Person                              │
│ 字段列表(Field 信息)                    │
│   - name : String                          │
│   - age  : int                             │
│ 方法列表(Method 信息)                   │
│   - eat()                                  │
│   - run()                                  │
│ 构造方法信息                               │
│ 父类信息:Object                           │
│ 实现接口信息                               │
│ 类加载器引用                               │
│ 运行时常量池引用                           │
└───────────────────────────────────────────┘

注意:
类对象不保存实例字段的实际值(那是实例对象的数据)


2.3 实例对象内存结构(Person 对象)

实例对象被 new 出来后放在 堆中(Heap)

堆(Heap)
┌──────────────────────────────────────────┐
│ 实例对象(Person p1)                    │
│------------------------------------------│
│ 对象头(Mark Word)                      │
│ 类指针(指向 Class<Person>)-------------│ → 指向方法区的类对象
│------------------------------------------│
│ 实例字段实际值:                         │
│   name = "Tom"                           │
│   age = 20                               │
└──────────────────────────────────────────┘

✔ 实例对象中真正保存数据的是字段的值

这些值属于每个对象独立拥有。


2.4 类对象 vs 实例对象 对比图(完整版)
                    JVM 内存模型
                    ────────────
                      堆(Heap) 
                ┌─────────────────────────────┐
                │  Class<Person> 类对象        │
                │  ─────────────────────────   │
                │  字段列表(name, age)       │
                │  方法列表(eat(), run())    │
                │  父类、接口信息              │
                │  类加载器                    │
                └─────────────────────────────┘
                          ▲
                          │  类指针
                          │
          ┌───────────────┴───────────────┐
          │                               │
          ▼                               ▼
     堆(Heap)                          堆(Heap)
┌──────────────────┐             ┌──────────────────┐
│ Person 实例 p1    │             │ Person 实例 p2    │
│──────────────────│             │──────────────────│
│ 对象头            │             │ 对象头            │
│ class pointer →──┼────────────►│ class pointer    │
│ name="Tom"        │             │ name="Alice"     │
│ age=20            │             │ age=30            │
└──────────────────┘             └──────────────────┘

从图你看到:

  • 类对象只有一个,放堆区

  • 每个实例对象都在堆里

  • 每个对象内部的 class pointer 指向类对象(说明它是哪种类型)

四、类对象的获取方法

1. 编译期方式:类名.class(最常用、最简单)

Class<?> clazz = Person.class;

✔ 不需要创建对象
✔ 代码写在哪都可以
✔ 编译器保证安全

用途: 反射、类锁、读取元数据等。


2. 通过对象获取:obj.getClass()

Person p = new Person(); Class<?> clazz = p.getClass();

✔ 必须先有实例对象
✔ 运行时获取
✔ 不依赖具体类型(可以用在多态)

用途:

  • 判断两个对象是不是同一类型

  • 运行时动态获取其类结构


3. 通过 Class.forName("类的全限定名")(动态加载)

Class<?> clazz = Class.forName("com.example.Person");

特点:

✔ 字符串加载类
✔ 可以做 动态加载(最重要的用途)
✔ 常用于 JDBC、插件式框架、反射工具等

例如 JDBC:

Class.forName("com.mysql.jdbc.Driver");


4. 类加载器:ClassLoader.loadClass()

ClassLoader loader = Main.class.getClassLoader(); Class<?> clazz = loader.loadClass("com.example.Person");

✔ 专业级用法
✔ 在框架、容器、自定义类加载器中常用
✔ 能实现 “热加载”、“隔离加载” 等高级功能v

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值