摘要:本文学习了什么是反射,以及如何使用反射。
环境
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 方法
类的信息:
java1 2 3 4 5 6 7 8 9 10 11 12 13 14
| static Class<?> forName(String className);
T newInstance();
String getName();
Package getPackage();
int getModifiers();
ClassLoader getClassLoader();
InputStream getResourceAsStream(String name);
|
属性和方法:
java1 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();
|
继承和实现:
java1 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属性:
java1
| Class clazz = String.class;
|
- 通过调用运行时类的对象的getClass()方法:
java1
| Class clazz = "www.demo.com".getClass();
|
- 调用Class的forName()方法:
java1
| Class clazz = Class.forName("refl.Person");
|
- 使用ClassLoader的loadClass()方法:
java1 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 常用方法
常用方法:
java1 2 3 4 5 6 7 8 9 10
| Class<?> loadClass(String name);
static ClassLoader getSystemClassLoader();
ClassLoader getParent();
static InputStream getSystemResourceAsStream(String name);
InputStream getResourceAsStream(String name);
|
2.2.6 获取类加载器
获取系统类加载器(可以获取):
java1
| ClassLoader.getSystemClassLoader();
|
获取扩展类加载器(可以获取):
java1
| ClassLoader.getSystemClassLoader().getParent();
|
获取引导类加载器(不可以获取):
java1
| ClassLoader.getSystemClassLoader().getParent().getParent();
|
2.2.7 测试类加载器
示例:
java1 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()); System.out.println(String.class.getClassLoader()); }
|
结果:
java1 2 3 4 5
| sun.misc.Launcher$AppClassLoader@18b4aac2 sun.misc.Launcher$ExtClassLoader@6d9c638 null sun.misc.Launcher$AppClassLoader@18b4aac2 null
|
2.2.8 测试加载资源
示例:
java1 2 3 4
|
InputStream is3 = ClassLoader.getSystemClassLoader().getResourceAsStream("com/test/test.txt"); InputStream is4 = ClassLoader.getSystemResourceAsStream("com/test/test.txt");
|
3 使用
3.1 定义类和接口
定义接口:
java1 2 3 4
| public interface Home<H> { public String address = ""; public String location(); }
|
定义父类:
java1 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 + "}"; } }
|
定义子类:
java1 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 测试类的信息
示例:
java1 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<?> 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()); } }
|
结果:
java1 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 测试类的继承和实现
示例:
java1 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()); } } }
|
结果:
java1 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 测试类的属性
示例:
java1 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()); grade.setAccessible(true); grade.set(student, 100); System.out.println("grade.get(student) >>> " + grade.get(student)); } }
|
结果:
java1 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 测试类的方法
示例:
java1 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()); } exam.setAccessible(true); Object result = exam.invoke(student, "Tom"); System.out.println("exam.invoke(student, Tom) >>> " + result); } }
|
结果:
java1 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 测试类的构造方法
示例:
java1 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); constructor.setAccessible(true); student = (Student) constructor.newInstance(10); System.out.println("student >>> " + student); } }
|
结果:
java1 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}
|
条