前面我们谈到了使用new运算符实例化类时,如果类没有加载到内存,类加载器会把类的字节码文件读入内存,并创建一个java.lang.Class对象。java.lang.Class对象由JVM创建,它存储了被加载类在运行时态下的类信息,程序可以通过Class类提供的方法获取被加载类的类名、类的包名、类方法、类属性、类构造方法等类信息。
java编译器在编译Java类时,会创建Class对象数据,并存放在同名的.class文件中。类加载器加载该类时,JVM会检查该类是否已经加载到内存中,若是没有加载,则把class文件加载到内存并创建Class对象,创建的Class对象是该类所有实例化对象在内存中的代理。如下图所示:
Class对象是该类所有实例化对象在内存中的代理
案例1:建立ClassTest类,并在ClassTest类的main()方法分别使用三种方式创建Book类的实例化对象,然后输出Book类实例化对象的Class对象的哈希码值,观察Book类三个实例化对象的Class对象的哈希码值是否相同。
在reflex包下新建ClassTest类。代码如下:
package reflex;
/**
* @ClassName: ClassTest
* @Description: 反射(认识Class对象)案例1
* @author 编程训练营
* @date
*
*/
public class ClassTest {
/**
* @Title: main
* @Description: Java程序入口main方法
* @param @param args 参数
*
* @return void 返回类型 @throws
* @throws IllegalAccessException
* @throws InstantiationException
*/
public static void main(String[] args) throws InstantiationException, IllegalAccessException {
try {
// 使用Class.forName()
Class<?> obj1 = Class.forName("reflex.Book");
Book book1 = (Book) obj1.newInstance();
// 输出book1的哈希码
System.out.println("book1的Class对象的哈希码:---" + book1.getClass().hashCode());
// 获取Book类的Class对象
Class<?> obj2 = Book.class;
Book book2 = (Book) obj2.newInstance();
// 输出book2的哈希码
System.out.println("book2的Class对象的哈希码:---" + book2.getClass().hashCode());
// 使用new运算符实例化Book类
Book book3 = new Book();
// 输出rect2的Class对象的哈希码
System.out.println("book3的Class对象的哈希码:---" + book3.getClass().hashCode());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}ClassTest程序分别使用Class类的forName方法、Book类的class静态变量、new运算符三种方式对Book类进行实例化,并分别输出实例化对象的哈希码。
程序执行结果如下图所示:

从执行结果可以看出,Book类的三个实例化对象book1、book2、book3的Class对象的哈希码值完全相同。因此可以说类的Class对象是该类所有实例化对象在内存中的代理。
类的哈希码通是过哈希算法散列得来的,在逻辑上可以确保类对象的唯一性,当多个类对象的哈希码相同时,就认为这多个类对象是同一个对象。
如何获取类的Class对象?
有三种方式获得Class对象:一种方式是使用Class类的forName方法来获取类的Class对象;一种方式是使用类实例化对象的getClass方法获取类的Class对象;一种方式是使用类的class静态变量获取Class对象,使用这种方式获取Class类对象时,JVM不会自动加载该类。
(1)使用Class类的forName方法获取类的Class对象
Class类的forName方法是一个静态方法,它使用一个包含类路径的文本串 String 作为输入参数,并返回该类Class 对象的引用。forName方法会调用类加载器从磁盘读取该类的字节码文件,并转换成 java.lang.Class 类的一个实例对象,并进行初始化操作,执行类的静态语句,包括静态变量的声明还有静态代码块。forName方法在调用类加载器加载类之前,会检查此类是否被加载到内存,如果没有,才会调用类加载器执行加载操作。如果该类已被加载到内存,forName会直接返回该类Class对象的引用。
Class类重载了forName方法,forName方法分别定义如下:
public static Class<?> forName(String className) public static Class<?> forName(String name, boolean initialize, ClassLoader loader)
第一个forName重载方法只有一个参数,参数是包含类路径的String对象,类路径要包含类所在包的完整包路径和包名。
第二个forName重载方法有三个参数,第一个参数包含类路径的String对象,第二个参数是布尔值,设定是否初始化加载的类,第三个参数是类加载器对象,该参数用来加载由第一个参数指定的类。
(2) 使用类实例化对象的getClass方法获取类的Class对象
getClass()方法是在Object类定义的方法,方法说明如下:
● Class<?> getClass ()
该方法返回该实例化对象的Class对象。
例如:
// 获取Book类的Class对象 Book book = new Book(); Class<?> obj = book.getClass();
上面的例句调用book对象的getClass()方法,获取book对象的Class对象。
(3) 使用类的Class属性获取Class对象
每个类都有一个静态的Class属性,该属性指向Class对象。
例如:
// 获取Book类的Class对象 Class<?> obj = Book.class;
上面的例句获取Book类的Class对象。