抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了什么是反射,以及如何使用反射。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8

1 简介

反射被视为动态语言的关键,反射机制允许程序在执行期借助于API取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。

这个Class类型的对象就像一面镜子,可以透过这个镜子看到类的结构,所以形象的称之为反射。

2 原理

2.1 Class

2.1.1 定义

Class是用来描述类的类,封装了当前对象所对应的类的信息,通过Class可以得到一个类中所有被加载的结构。

将书看做一个对象,一本书有名称,作者,字数等,使用Book类描述所有书的信息。根据万物皆对象,可以将类看做一个对象,一个类中有属性,方法,构造器等,使用Class类描述所有类的信息。

Class类是一特殊的类,对于每个类而言,虚拟机都为其保留一个不变的Class类的对象。Class类的对象只能由虚拟机创建,一个类只能有一个Class类的对象。

支持Class实例的类型:类,接口,枚举,注解,数组,基本数据类型(包含Void类但不存在Null类)。

2.1.2 方法

类的信息:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 获取指定类名的Class对象
static Class<?> forName(String className);
// 获取该Class对象对应的类的实例
T newInstance();
// 获取全类名
String getName();
// 获取包名
Package getPackage();
// 获取修饰符标志数
int getModifiers();
// 获取类加载器
ClassLoader getClassLoader();
// 获取读取的文件流
InputStream getResourceAsStream(String name);

属性和方法:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 获取公共的指定名称的属性,包括父类的
Field getField(String name);
// 获取指定名称的属性,不包括父类的
Field getDeclaredField(String name);
// 获取公共的所有的属性,包括父类的
Field[] getFields();
// 获取所有的属性,不包括父类的
Field[] getDeclaredFields();
// 获取公共的指定名称和参数的方法,包括父类的
Method getMethod(String name, Class<?>... parameterTypes);
// 获取指定名称和参数的方法,不包括父类的
Method getDeclaredMethod(String name, Class<?>... parameterTypes);
// 获取公共的所有的方法,包括父类的
Method[] getMethods();
// 获取所有的方法,不包括父类的
Method[] getDeclaredMethods();
// 获取公共的指定参数的构造方法
Constructor<T> getConstructor(Class<?>... parameterTypes);
// 获取指定参数的方法
Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);
// 获取公共的所有的构造方法
Constructor<?>[] getConstructors();
// 获取所有的构造方法
Constructor<?>[] getDeclaredConstructors();

继承和实现:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// 获取公共的所有的注解
Annotation[] getAnnotations();
// 获取所有的注解
Annotation[] getDeclaredAnnotations();
// 获取公共的指定类型的注解
<A extends Annotation> A getAnnotation(Class<A> annotationClass);
// 获取指定类型的注解
<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass);
// 获取实现的所有接口
Class<?>[] getInterfaces();
// 获取实现的所有接口,包含泛型
Type[] getGenericInterfaces();
// 获取继承的父类
Class<? super T> getSuperclass();
// 获取继承的父类,包含泛型
Type getGenericSuperclass();

2.1.3 获取

Class没有公共构造方法,Class对象是在加载类时由虚拟机创建的。

获取Class类的实例有四种方式:

  • 直接调用运行时类的.class属性:
    java
    1
    Class clazz = String.class;
  • 通过调用运行时类的对象的getClass()方法:
    java
    1
    Class clazz = "www.demo.com".getClass();
  • 调用Class的forName()方法:
    java
    1
    Class clazz = Class.forName("refl.Person");
  • 使用ClassLoader的loadClass()方法:
    java
    1
    2
    ClassLoader cl = this.getClass().getClassLoader();
    Class clazz = cl.loadClass("refl.Person");

使用Class的forName()方法和使用ClassLoader的loadClass()方法都能获取指定类的Class实例,也都会进行类的加载,都能将class字节码文件加载到虚拟机中。

其区别是:

  • 在调用Class的forName()方法时,会进行类的加载以外,也会进行类的初始化,初始化静态成员变量和执行静态代码块,但不会执行构造方法创建对象。只有在调用了newInstance()方法后,才会调用空参的构造方法创建对象。
  • 在调用ClassLoader的loadClass()方法时,只会进行类的加载,不会进行类的初始化,也不会执行构造方法创建对象。

2.2 ClassLoader

2.2.1 类的加载过程

当程序主动使用了某个类时,该类还未被加载到内存中,那么系统会通过下面三个步骤对该类进行初始化:

  • 类的加载:将类的class文件读入内存,并为之创建一个Class对象。由类加载器完成。
  • 类的连接:将类的二进制数据合并到JRE中。
  • 类的初始化:JVM负责对类进行初始化。

2.2.2 类的初始化

类的主动引用一定会发生类的初始化,类的被动引用不会发生类的初始化。

类的主动引用场景:

  • 当虚拟机启动,先初始化main方法所在的类。
  • 使用new关键字创建了一个类的对象。
  • 调用类的静态成员(除了final常量)和静态方法。
  • 使用java.lang.reflect包的方法对类进行反射调用。
  • 当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。

类的被动引用场景:

  • 当访问一个静态域时,只有真正声明这个域的类才会被初始化。
  • 当通过子类引用父类的静态变量,不会导致子类初始化。
  • 通过数组定义类引用,不会触发此类的初始化。
  • 引用常量不会触发此类的初始化,常量在连接阶段就存入调用类的常量池中了。

2.2.3 类加载器的作用

将class文件字节码内容加载到内存中,并将这些静态数据转换成方法区的运行时数据结构,然后在堆中生成一个代表这个类的Class对象,作为方法区中类数据的访问入口。

2.2.4 类加载器的分类

类加载器分为三种:

  • 引导类加载器:用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。无法直接获取。
  • 扩展类加载器:负责jre/lib/ext目录下的JAR包或–D java.ext.dirs指定目录下的JAR包装入工作库。
  • 系统类加载器:负责java –classpath–D java.class.path所指的目录下的类与JAR包装入工作,最常用的加载器。

2.2.5 常用方法

常用方法:

java
1
2
3
4
5
6
7
8
9
10
// 将Class文件加载到内存时,并不会执行类的初始化
Class<?> loadClass(String name);
// 获取系统类加载器
static ClassLoader getSystemClassLoader();
// 获取当前类加载器的上级类加载器
ClassLoader getParent();
// 和getResourceAsStream用法类似
static InputStream getSystemResourceAsStream(String name);
// 从类路径下获取资源
InputStream getResourceAsStream(String name);

2.2.6 获取类加载器

获取系统类加载器(可以获取):

java
1
ClassLoader.getSystemClassLoader();

获取扩展类加载器(可以获取):

java
1
ClassLoader.getSystemClassLoader().getParent();

获取引导类加载器(不可以获取):

java
1
ClassLoader.getSystemClassLoader().getParent().getParent();

2.2.7 测试类加载器

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public static void main(String[] args) {
// 获取系统类加载器
ClassLoader appClassLoader = ClassLoader.getSystemClassLoader();
System.out.println(appClassLoader);
// 获取扩展类加载器
ClassLoader extClassLoader = appClassLoader.getParent();
System.out.println(extClassLoader);
// 获取引导类加载器
ClassLoader bootClassLoader = extClassLoader.getParent();
System.out.println(bootClassLoader);
// 自定义类使用的是系统类加载器
System.out.println(ClassLoaderTest.class.getClassLoader());
// JDK提供的类使用的是引导类加载器
System.out.println(String.class.getClassLoader());
}

结果:

java
1
2
3
4
5
sun.misc.Launcher$AppClassLoader@18b4aac2
sun.misc.Launcher$ExtClassLoader@6d9c638
null
sun.misc.Launcher$AppClassLoader@18b4aac2
null

2.2.8 测试加载资源

示例:

java
1
2
3
4
// InputStream is1 = ClassLoader.getSystemClassLoader().getResourceAsStream("test.txt");// null
// InputStream is2 = ClassLoader.getSystemClassLoader().getResourceAsStream("/com/test/test.txt");// null
InputStream is3 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/test/test.txt");
InputStream is4 = ClassLoader.getSystemResourceAsStream("com/test/test.txt");

3 使用

3.1 定义类和接口

定义接口:

java
1
2
3
4
public interface Home<H> {
public String address = "";
public String location();
}

定义父类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public abstract class People<P> {
public People() {
System.out.println("do >>> People()");
}
private Integer age;
public String name;
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String talk(String topic) {
System.out.println("do >>> talk() " + topic);
return name;
}
@Override
public String toString() {
String ageStr = "age=" + age;
String nameStr = "name=" + name;
return "People{" + ageStr + ", " + nameStr + "}";
}
}

定义子类:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
public class Student extends People<Integer> implements Serializable, Home<String> {
public String teacher;
private Integer grade;
public Student() {
System.out.println("do >>> Student()");
}
private Student(Integer grade) {
System.out.println("do >>> Student(grade)");
this.grade = grade;
}
public String getTeacher() {
return teacher;
}
public void setTeacher(String teacher) {
this.teacher = teacher;
}
public Integer getGrade() {
return grade;
}
public void setGrade(Integer grade) {
this.grade = grade;
}
@Deprecated
private Integer exam(String name) throws ArithmeticException {
System.out.println("do >>> exam() " + name);
return 98;
}
@Override
public String location() {
return address;
}
@Override
public String toString() {
String teacherStr = "teacher=" + teacher;
String gradeStr = "grade=" + grade;
return "Student{" + teacherStr + ", " + gradeStr + "}";
}
}

3.2 测试类的信息

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Demo {
public static void main(String[] args) throws Exception {
// 根据类名获取类的Class实例
Class<?> cla = Class.forName("test.Student");
// 使用反射创建类的实例
Student student = (Student) cla.newInstance();
System.out.println("student.toString() >>> " + student);
// 获取全类名
System.out.println("cla.getName() >>> " + cla.getName());
// 获取包名
System.out.println("cla.getPackage() >>> " + cla.getPackage());
// 获取修饰符标志数
System.out.println("cla.getModifiers() >>> " + cla.getModifiers());
// 获取翻译后的修饰符
System.out.println("cla.getModifiers() >>> " + Modifier.toString(cla.getModifiers()));
// 获取加载器
System.out.println("cla.getClassLoader() >>> " + cla.getClassLoader());
}
}

结果:

java
1
2
3
4
5
6
7
8
do >>> People()
do >>> Student()
student.toString() >>> Student{teacher=null, grade=null}
cla.getName() >>> test.Student
cla.getPackage() >>> package test
cla.getModifiers() >>> 1
cla.getModifiers() >>> public
cla.getClassLoader() >>> sun.misc.Launcher$AppClassLoader@18b4aac2

3.3 测试类的继承和实现

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
public class Demo {
public static void main(String[] args) throws Exception {
// 获取父类
System.out.println("cla.getSuperclass() >>> " + cla.getSuperclass().getName());
// 获取父类,包含泛型
Type genericSuperclass = cla.getGenericSuperclass();
System.out.println("cla.getGenericSuperclass() >>> " + genericSuperclass.getTypeName());
// 获取父类,查看泛型
ParameterizedType paramType = (ParameterizedType) genericSuperclass;
Type[] actualTypeArguments = paramType.getActualTypeArguments();
for (Type type : actualTypeArguments) {
System.out.println("cla.getGenericSuperclass().getActualTypeArguments() >>> " + type.getTypeName());
}
// 获取接口
Class<?>[] interfaces = cla.getInterfaces();
for (Class<?> interfaceClass : interfaces) {
System.out.println("cla.getInterfaces() >>> " + interfaceClass.getName());
}
// 获取接口,包含泛型
Type[] genericInterfaces = cla.getGenericInterfaces();
for (Type genericInterface : genericInterfaces) {
System.out.println("cla.getGenericInterfaces() >>> " + genericInterface.getTypeName());
}
}
}

结果:

java
1
2
3
4
5
6
7
cla.getSuperclass() >>> test.People
cla.getGenericSuperclass() >>> test.People<java.lang.Integer>
cla.getGenericSuperclass().getActualTypeArguments() >>> java.lang.Integer
cla.getInterfaces() >>> java.io.Serializable
cla.getInterfaces() >>> test.Home
cla.getGenericInterfaces() >>> java.io.Serializable
cla.getGenericInterfaces() >>> test.Home<java.lang.String>

3.4 测试类的属性

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public class Demo {
public static void main(String[] args) throws Exception {
// 获取公共的所有的属性,包括父类的
Field[] fields = cla.getFields();
for (Field field : fields) {
System.out.println("cla.getFields() >>> " + field);
}
// 获取所有的属性,不包括父类的
Field[] declaredFields = cla.getDeclaredFields();
for (Field field : declaredFields) {
System.out.println("cla.getDeclaredFields() >>> " + field);
}
// 获取公共的指定名称的属性,包括父类的
System.out.println("cla.getField(name) >>> " + cla.getField("name"));
// 获取指定名称的属性,不包括父类的
Field grade = cla.getDeclaredField("grade");
System.out.println("cla.getDeclaredField(grade) >>> " + grade);
// 获取属性访问修饰符
System.out.println("grade.getModifiers() >>> " + Modifier.toString(grade.getModifiers()));
// 获取属性类型
System.out.println("grade.getType() >>> " + grade.getType().getTypeName());
// 获取属性名称
System.out.println("grade.getName() >>> " + grade.getName());
// 获取和修改属性值,非公共属性需要执行setAccessible()方法
grade.setAccessible(true);
grade.set(student, 100);
System.out.println("grade.get(student) >>> " + grade.get(student));
}
}

结果:

java
1
2
3
4
5
6
7
8
cla.getFields() >>> public ...
cla.getDeclaredFields() >>> ...
cla.getField(name) >>> public java.lang.String test.People.name
cla.getDeclaredField(grade) >>> private java.lang.Integer test.Student.grade
grade.getModifiers() >>> private
grade.getType() >>> java.lang.Integer
grade.getName() >>> grade
grade.get(student) >>> 100

3.5 测试类的方法

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class Demo {
public static void main(String[] args) throws Exception {
// 获取公共的所有的方法,包括父类的
Method[] methods = cla.getMethods();
for (Method method : methods) {
System.out.println("cla.getMethods() >>> " + method);
}
// 获取所有的方法,不包括父类的
Method[] declaredMethods = cla.getDeclaredMethods();
for (Method method : declaredMethods) {
System.out.println("cla.getDeclaredMethods() >>> " + method);
}
// 获取公共的指定名称和参数的方法,包括父类的
System.out.println("cla.getMethod(talk, String.class) >>> " + cla.getMethod("talk", String.class));
// 获取指定名称和参数的方法,不包括父类的
Method exam = cla.getDeclaredMethod("exam", String.class);
System.out.println("cla.getDeclaredMethod(exam, String.class) >>> " + exam);
// 获取方法注解
Annotation[] annotations = exam.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println("exam.getAnnotations() >>> " + annotation);
}
// 获取方法访问修饰符
System.out.println("exam.getModifiers() >>> " + Modifier.toString(exam.getModifiers()));
// 获取方法返回值类型
System.out.println("exam.getReturnType() >>> " + exam.getReturnType().getTypeName());
// 获取方法名称
System.out.println("exam.getName() >>> " + exam.getName());
// 获取方法参数
Parameter[] parameters = exam.getParameters();
for (Parameter parameter : parameters) {
System.out.println("exam.getParameters() >>> " + parameter);
}
// 获取方法异常
Class[] exceptionTypes = exam.getExceptionTypes();
for (Class exceptionType : exceptionTypes) {
System.out.println("exam.getExceptionTypes() >>> " + exceptionType.getTypeName());
}
// 调用方法,非公共方法需要执行setAccessible()方法
exam.setAccessible(true);
Object result = exam.invoke(student, "Tom");
System.out.println("exam.invoke(student, Tom) >>> " + result);
}
}

结果:

java
1
2
3
4
5
6
7
8
9
10
11
12
cla.getMethods() >>> public ...
cla.getDeclaredMethods() >>> ...
cla.getMethod(talk, String.class) >>> public java.lang.String test.People.talk(java.lang.String)
cla.getDeclaredMethod(exam, String.class) >>> private java.lang.Integer test.Student.exam(java.lang.String) throws java.lang.ArithmeticException
exam.getAnnotations() >>> @java.lang.Deprecated()
exam.getModifiers() >>> private
exam.getReturnType() >>> java.lang.Integer
exam.getName() >>> exam
exam.getParameters() >>> java.lang.String name
exam.getExceptionTypes() >>> java.lang.ArithmeticException
do >>> exam() Tom
exam.invoke(student, Tom) >>> 98

3.6 测试类的构造方法

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Demo {
public static void main(String[] args) throws Exception {
// 获取公共的所有的构造方法
Constructor<?>[] constructors = cla.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println("cla.getConstructors() >>> " + constructor);
}
// 获取所有的构造方法
Constructor<?>[] declaredConstructors = cla.getDeclaredConstructors();
for (Constructor<?> constructor : declaredConstructors) {
System.out.println("cla.getDeclaredConstructors() >>> " + constructor);
}
// 获取公共的指定参数的构造方法
System.out.println("cla.getConstructor() >>> " + cla.getConstructor());
// 获取指定参数的方法
Constructor<?> constructor = cla.getDeclaredConstructor(Integer.class);
System.out.println("cla.getDeclaredConstructor() >>> " + constructor);
// 调用构造方法,非公共构造方法需要执行setAccessible()方法
constructor.setAccessible(true);
student = (Student) constructor.newInstance(10);
System.out.println("student >>> " + student);
}
}

结果:

java
1
2
3
4
5
6
7
8
cla.getConstructors() >>> public test.Student()
cla.getDeclaredConstructors() >>> public test.Student()
cla.getDeclaredConstructors() >>> private test.Student(java.lang.Integer)
cla.getConstructor() >>> public test.Student()
cla.getDeclaredConstructor() >>> private test.Student(java.lang.Integer)
do >>> People()
do >>> Student(grade)
student >>> Student{teacher=null, grade=10}

评论