1. 前言
在OOP的世界里,万物皆对象。也就是说,我们可以将任何东西抽象成一个对象。
比如人,可以抽象成一个Person类,通过new Person()来实例化一个对象;再比如鸭子,可以抽象成一个Duck类,也可以对其进行实例化……那么这一个个类本身是不是也可以抽象成一个类呢?Java提供了一个特殊的类Class,用来描述类的内部信息,是反射的核心类。
下图是本篇讲述内容:
2. Java反射机制概述
Java反射(Reflection)允许应用程序在运行时借助于反射API,来获取所有类或接口的内部信息,并且能直接操作任意对象的内部属性及方法。反射机制的核心类为java.lang.Class。类加载完后,会在堆内存的方法区中产生一个Class类型的对象。
Class类没有公开的构造函数,是由类加载器的defineClass方法构造而成。所以Class对象不是“new”出来的,而是通过方法来获取的。
这个Class对象具有类的完整结构信息,并且一个类只有一个Class对象。
3. 获取Class对象
获取Class对象有以下四种方式:通过类对象获取;
通过类直接调用class获取;
通过Class.forName获取;
通过类加载器获取。
下面使用代码展示获取 Person 类的Class对象的四种方式:@Test
public void testClassFor() {
// 1.通过类实例获取
Person person = new Person();
Class extends Person> clazz1 = person.getClass();
System.out.println("01 - " + clazz1);
// 2.通过类直接调用class获取
Class clazz2 = Person.class;
System.out.println("02 - " + clazz2);
// 3.通过Class.forName获取
Class> clazz3 = null;
try {
clazz3 = Class.forName("io.github.gozhuyinglong.reflection.Person");
} catch (ClassNotFoundException e) {
// 当找不到指定类时,会抛出此异常
e.printStackTrace();
}
System.out.println("03 - " + clazz3);
// 4.通过类加载器获取
ClassLoader classLoader = this.getClass().getClassLoader();
Class> clazz4 = null;
try {
clazz4 = classLoader.loadClass("io.github.gozhuyinglong.reflection.Person");
} catch (ClassNotFoundException e) {
// 当找不到指定类时,会抛出此异常
e.printStackTrace();
}
System.out.println("04 - " + clazz4);
// hashCode相等,说明这四种方式获取的是同一个实例
System.out.println("05 - " + clazz1.hashCode());
System.out.println("06 - " + clazz2.hashCode());
System.out.println("07 - " + clazz3.hashCode());
System.out.println("08 - " + clazz4.hashCode());
}
输出结果:01 - class io.github.gozhuyinglong.reflection.Person
02 - class io.github.gozhuyinglong.reflection.Person
03 - class io.github.gozhuyinglong.reflection.Person
04 - class io.github.gozhuyinglong.reflection.Person
05 - 721748895
06 - 721748895
07 - 721748895
08 - 721748895
通过上面的输出结果可以看出,这四个Class对象的hashCode相同,说明使用这四种方式获取的是同一个对象。
4. 一些特殊的类和接口的Class对象
在源码注释中提到一些特殊的类和接口:枚举是一种类。
注解是一种接口。
数组也属于一个反映为Class对象的类。具有相同元素类型和维数的数组,也具有相同的Class对象(也就是说,元素类型不同,或数组维数不同,其Class对象也不同)。
原始Java类型(boolean, byte, char, short, int, long, float,double)和关键字 void 也表示为Class对象。
下面通过代码来验证:@Test
public void testClassOther() {
// 枚举是一种类
Class clazz1 = PersonEnum.class;
System.out.println("01 - " + clazz1);
// 注解是一种接口
Class clazz2 = PersonAnnotation.class;
System.out.println("02 - " + clazz2);
// 数组也属于一个反应 Class 实例的类
Person[] personArray3 = new Person[1];
Class extends Person[]> clazz3 = personArray3.getClass();
System.out.println("03 - " + clazz3);
// 具有相同元素类型和维数的数组,也具有相同的 Class 实例
Person[] personArray4 = new Person[4];
Class> clazz4 = personArray4.getClass();
Person[][] personArray5 = new Person[1][];
Class> clazz5 = personArray5.getClass();
// 两个一维数组的 hashCode 相等,说明是同一实例
System.out.println("04 - " + clazz3.hashCode());
System.out.println("05 - " + clazz4.hashCode());
// 一维数组与二维数组的 hashCode 不相等,说明不是同一实例
System.out.println("06 - " + clazz5.hashCode());
// 原始 Java 类型和关键字 void 也表示为 Class 实例
Class clazz6 = int.class;
System.out.println("07 - " + clazz6);
Class clazz7 = double.class;
System.out.println("08 - " + clazz7);
Class clazz8 = void.class;
System.out.println("09 - " + clazz8);
}
输出结果:01 - class io.github.gozhuyinglong.re