在类加载阶段,类加载器根据类的全限定名找到并读取 .class 文件,然后创建一个 Class 对象来表示这个类。其实这个过程中会创建两个Class对象,一个是Klass对象,C++定义的真实对象模型,存放在元空间中,另外一个就是我们常用的Class对象,是暴露给Java应用程序的对类元数据的一个Java层面的镜像,这个对象存放在堆中。那么这两个对象之间到底有什么区别和联系呢?
一:Class对象
1.本质:JVM(以HotSpot为例)在方法区(Metaspace) 中用于表示一个Java类的内部C++对象。
2.目的:它是JVM运行时真正的“蓝图”。JVM需要它来:
实例化对象:当使用 new
关键字时,JVM根据 Klass
的信息来确定对象需要多少内存、字段的偏移量等。
方法分派:Klass
中包含了类的方法表(vtable) 和接口表(itable),这是动态方法调用的核心。
维护继承关系:Klass
保存了指向其父类和已实现接口的 Klass
的指针。
存储类元信息:如类名、访问修饰符、常量池、字段信息、方法信息等。
值得注意的是,它对Java程序员是不可见的,它是一个纯粹的JVM实现细节,位于JVM的本地内存中,Java代码无法直接访问。
二:java.lang.Class对象
1.本质:它是一个普通的Java类(java.lang.Class
)的实例,存在于 Java堆 中。
2.目的:它是Java 反射机制 的入口点。Java应用程序通过它来:
获取类的名称、修饰符、父类、接口等信息。
获取类的字段、方法、构造函数等,并对其进行操作(如调用方法、获取/设置字段值)。
创建类的实例。
它对Java程序员完全可见,你可以通过Object.getClass(),类名.class或者Class.forName来获取它。
三:二者之间的关系
1.创建时机:当一个类被JVM的类加载器加载时,JVM会同时创建两个对象:在方法区(Metaspace)创建一个 Klass
对象。在Java堆中创建一个 java.lang.Class
实例。
2.互相引用:Klass
指向 Class
,Klass
对象内部有一个名为 _java_mirror
的指针,直接指向堆中的那个 java.lang.Class
实例。这就像是“本体”知道了它的“镜像”在哪里。Class
指向 Klass,
java.lang.Class
实例内部也有一个隐藏的、指向方法区中对应 Klass
对象的指针。这个指针通常被称为 “Class 对象元数据指针” 或类似的东西。
3.交互流程(以反射为例):当你写下一段反射代码,例如 myClass.getMethods()
:
Java代码通过 myClass
(一个 Class
实例)调用 getMethods()
。
JVM会通过这个 Class
实例内部的隐藏指针,找到对应的 Klass
对象。
JVM访问 Klass
对象中存储的完整方法元数据。
JVM将这些信息封装成 java.lang.reflect.Method
对象数组,返回给Java应用程序。
简单来说,Klass
就像是公司的 真实组织架构图,存储在总裁办公室的保险柜里(方法区)。它详细定义了每个部门的职责、汇报关系、工作流程。只有公司高管(JVM)能看,用于实际运营。java.lang.Class
就像是公司对外的 官网“关于我们”页面(Java堆)。它向公众(Java应用程序)展示了公司的部门组成、联系方式等公开信息。任何人都可以通过访问这个页面来了解公司。当你(Java程序)想了解公司(一个类)的信息时,你访问的是官网页面(Class
对象),而这个页面背后的数据,最终来源于那份真实的组织架构图(Klass
对象)。