Java学习——反射

Java学习——反射

摘要:本文主要讲述了什么是反射,使用反射有什么好处,以及如何使用反射。

部分内容来自以下博客:

https://www.cnblogs.com/tech-bird/p/3525336.html

https://www.cnblogs.com/jiaoyiping/p/6130355.html

什么是反射

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

加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有一个Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。

Class类

Class类是什么

Class是用来描述类的类,封装了当前对象所对应的类的信息

一个类中有属性,方法,构造器等,比如说有一个Person类,一个Order类,一个Book类,这些都是不同的类,现在需要一个类,用来描述类,这就是Class,它应该有类名,属性,方法,构造器等。

Class类是一个对象照镜子的结果,对象可以看到自己有哪些属性,方法,构造器,实现了哪些接口等等。

对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个类的有关信息。

Class对象只能由系统建立对象,一个类(而不是一个对象)在JVM中只会有一个Class实例

通过Class可以完整地得到一个类中的所有被加载的结构。

Class类是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。

Java中的哪些类型可以有Class类的实例

类,接口,枚举,注解,数组,基本数据类型,void。

常用方法

static Class<?> forName(String className):返回指定类名的Class对象。

T newInstance():返回该Class对象对应的类的一个实例。

Field[] getFields():获取所有的公共属性。

Field[] getDeclaredFields():获取所有的属性。

Field getField(String name):获取指定名称的公共属性。

Field getDeclaredField(String name):获取指定名称的属性。

Method[] getMethods():获取所有的公共方法。

Method[] getDeclaredMethods():获取所有的方法。

Method getMethod(String name, Class<?>... parameterTypes):返回指定名称、参数列表的公共方法。

Method getDeclaredMethod(String name, Class<?>... parameterTypes):返回指定名称、参数列表的方法。

Constructor<?>[] getConstructors():获取所有的公共构造方法。

Constructor<?>[] getDeclaredConstructors():获取所有的构造方法。

Constructor<T> getConstructor(Class<?>... parameterTypes):获取指定参数列表的公共构造方法。

Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes):获取指定参数列表的构造方法。

<A extends Annotation> A getAnnotation(Class<A> annotationClass):获取指定类型的公共注解。

<A extends Annotation> A getDeclaredAnnotation(Class<A> annotationClass):获取指定类型的注解。

Annotation[] getAnnotations():获取所有的公共注解。

Annotation[] getDeclaredAnnotations():获取所有的注解。

Class<?>[] getInterfaces():获取实现的所有接口。

Type[] getGenericInterfaces():获取带泛型的接口。

Class<? super T> getSuperclass():获取继承的父类。

Type getGenericSuperclass():获取带泛型的父类。

String getName():返回全类名。

Package getPackage():返回包名。

int getModifiers():返回修饰符。

ClassLoader getClassLoader():返回类加载器。

InputStream getResourceAsStream(String name):返回读取的文件流。

如何获取Class类的实例

Class没有公共构造方法。

Class对象是在加载类时由Java虚拟机以及通过调用类加载器中的defineClass方法自动构造的。

Java程序经过编译以后,生成关于类的字节码文件。在解释运行时,需要将类的字节码文件加载到内存中(通过JVM的类的加载器实现的加载)。此时加载到内存中的类,我们就称作运行时类,此类本身就充当了java.lang.Class的一个实例。

反过来讲:一个Class的实例就对应着一个加载到内存中的运行时类。

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

1)直接调用运行时类的.class属性

1 Class clazz = String.class;

2)通过调用运行时类的对象的getClass()方法

1 Class clazz = "www.atguigu.com".getClass();

3)调用Class的静态方法forName()获取

1 Class clazz = Class.forName("refl.Person");

4)使用类的加载器

1 ClassLoader cl = this.getClass().getClassLoader();
2 Class clazz = cl.loadClass("refl.Person");

使用forName()和loadClass()的区别

相同:

使用Class的静态方法forName()和使用ClassLoader的成员方法loadClass()都能获取指定类的Class实例。

也都会进行类的加载,即将.class文件读取到JVM中。

不同:

在调用Class的静态方法forName()时,除了会进行类的加载(即将.class文件加载到JVM中)以外,还会进行类的初始化(即初始化静态成员变量和执行静态代码块)。但调用forName()方法并不会执行构造方法创建对象,只要在调用了成员方法newInstance()以后,才会调用空参的构造方法创建对象。

当调用ClassLoader的成员方法loadClass()时,只进行类的加载(即将.class文件加载到JVM中),不会进行类的初始化(即初始化静态成员变量和执行静态代码块),也不会执行构造方法。

ClassLoader类

类的加载过程

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

1)类的加载:将类的class文件读入内存,并为之创建一个Class对象。由类加载器完成。

2)类的连接:将类的二进制数据合并到JRE中。

3)类的初始化:JVM负责对类进行初始化。

何时会进行类的初始化

1)类的主动引用一定会发生类的初始化:

当虚拟机启动,先初始化main方法所在的类

使用new关键字创建了一个类的对象。

调用类的静态成员(除了final常量)和静态方法

使用java.lang.reflect包的方法对类进行反射调用

当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。

2)类的被动引用不会发生类的初始化

当访问一个静态域时,只有真正声明这个域的类才会被初始化。

当通过子类引用父类的静态变量,不会导致子类初始化。

通过数组定义类引用,不会触发此类的初始化。

引用常量不会触发此类的初始化,常量在链接阶段就存入调用类的常量池中了。

类加载器的作用

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

类加载器的分类

引导类加载器:用C++编写的,是JVM自带的类装载器,负责Java平台核心库,用来装载核心类库。无法直接获取。

扩展类加载器:负责jre/lib/ext目录下的JAR包或–D java.ext.dirs指定目录下的JAR包装入工作库。

系统类加载器:负责java –classpath或–D java.class.path所指的目录下的类与JAR包装入工作,最常用的加载器。

常用方法

Class<?> loadClass(String name):将Class文件加载到内存时,并不会执行类的初始化。

static ClassLoader getSystemClassLoader():获取系统类加载器。

ClassLoader getParent():获取当前类加载器的上级类加载器。

static InputStream getSystemResourceAsStream(String name):和getResourceAsStream用法类似。

InputStream getResourceAsStream(String name):默认则是从ClassPath根下获取,path不能以'/'开头,最终是由ClassLoader获取资源。

如何获取类加载器

获取系统类加载器(可以获取): ClassLoader.getSystemClassLoader(); 

获取扩展类加载器(可以获取): ClassLoader.getSystemClassLoader().getParent(); 

获取引导类加载器(不可以获取): ClassLoader.getSystemClassLoader().getParent().getParent(); 

测试类加载器

测试代码如下:

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

运行结果如下:

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

测试加载资源

测试代码如下:

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

使用反射

获取类的信息

 1 // 根据类名获取类的Class实例
 2 Class<?> cla = Class.forName("test.Student");
 3 // 获取全类名
 4 System.out.println("cla.getName() >>> " + cla.getName());// cla.getName() >>> test.Student
 5 // 获取包名
 6 System.out.println("cla.getPackage() >>> " + cla.getPackage());// cla.getPackage() >>> package test
 7 // 获取访问修饰符
 8 System.out.println("cla.getModifiers() >>> " + cla.getModifiers());// cla.getModifiers() >>> 0
 9 // 获取翻译后的访问修饰符
10 System.out.println("cla.getModifiers() >>> " + Modifier.toString(cla.getModifiers()));// cla.getModifiers() >>> 
11 // 获取加载器
12 System.out.println("cla.getClassLoader() >>> " + cla.getClassLoader());// cla.getClassLoader() >>> sun.misc.Launcher$AppClassLoader@18b4aac2
13 // 获取父类
14 Class<?> superclass = cla.getSuperclass();
15 System.out.println("cla.getSuperclass() >>> " + superclass);// cla.getSuperclass() >>> class test.People
16 // 获取带泛型的父类
17 Type genericSuperclass = cla.getGenericSuperclass();
18 System.out.println("cla.getGenericSuperclass() >>> " + genericSuperclass);
19 // 获取带泛型的父类的泛型类型
20 ParameterizedType paramType = (ParameterizedType) genericSuperclass;
21 Type[] actualTypeArguments = paramType.getActualTypeArguments();
22 for (Type type : actualTypeArguments) {
23     System.out.println("genericSuperclass.getActualTypeArguments() >>> " + type.getTypeName());
24 }
25 // 获取接口
26 Class<?>[] interfaces = cla.getInterfaces();
27 for (Class<?> interfaceClass : interfaces) {
28     System.out.println("cla.getInterfaces() >>> " + interfaceClass);
29     // cla.getInterfaces() >>> interface test.School
30     // cla.getInterfaces() >>> interface test.Home
31 }
32 // 获取带泛型的接口
33 Type[] genericInterfaces = cla.getGenericInterfaces();
34 for (Type genericInterface : genericInterfaces) {
35     System.out.println("cla.getGenericInterfaces() >>> " + genericInterface);
36     // cla.getGenericInterfaces() >>> test.School<J>
37     // cla.getGenericInterfaces() >>> test.Home<K>
38 }
39 // 使用反射创建类的实例
40 Student student = (Student) cla.newInstance();// >>> Student()
41 System.out.println("student.toString() >>> " + student);// student.toString() >>> [age:0][grade:0.0][sex:null]

获取类的属性

 1 // 获取所有的公共属性,包括父类的公共属性
 2 Field[] fields = cla.getFields();
 3 for (Field field : fields) {
 4     System.out.println("cla.getFields() >>> " + field);
 5     // cla.getFields() >>> public int test.Student.age
 6     // cla.getFields() >>> public java.lang.String test.People.name
 7 }
 8 // 获取已声明的所有属性,包括非公共属性,但不包括父类的属性
 9 Field[] declaredFields = cla.getDeclaredFields();
10 for (Field field : declaredFields) {
11     System.out.println("cla.getDeclaredFields() >>> " + field);
12     // cla.getDeclaredFields() >>> public int test.Student.age
13     // cla.getDeclaredFields() >>> double test.Student.grade
14     // cla.getDeclaredFields() >>> private java.lang.String test.Student.sex
15 }
16 // 获取指定名称的属性
17 // Field sex = cla.getField("sex");
18 Field sex = cla.getDeclaredField("sex");
19 System.out.println("cla.getDeclaredField() >>> " + sex);// cla.getDeclaredField() >>> private java.lang.String test.Student.sex
20 // 获取访问修饰符
21 System.out.println("sex.getModifiers() >>> " + Modifier.toString(sex.getModifiers()));// sex.getModifiers() >>> private
22 // 获取类型
23 System.out.println("sex.getType() >>> " + sex.getType().getName());// sex.getType() >>> java.lang.String
24 // 获取名称
25 System.out.println("sex.getName() >>> " + sex.getName());// sex.getName() >>> sex
26 // 获取和修改属性值,非公共属性需要执行setAccessible()方法
27 sex.setAccessible(true);
28 System.out.println("sex.get() >>> " + sex.get(student));// sex.get() >>> null
29 sex.set(student, "男");
30 System.out.println("student.getSex() >>> " + student.getSex());// student.getSex() >>> 男

获取类的方法

 1 // 获取所有的公共方法,包括父类的公共方法和接口的公共方法
 2 Method[] methods = cla.getMethods();
 3 for (Method method : methods) {
 4     System.out.println("cla.getMethods() >>> " + method);
 5     // cla.getMethods() >>> public int test.Student.getAge()
 6     // cla.getMethods() >>> public void test.Student.setAge(int)
 7     // cla.getMethods() >>> public double test.Student.getGrade()
 8     // cla.getMethods() >>> public void test.Student.setGrade(double)
 9     // cla.getMethods() >>> public java.lang.String test.Student.getSex()
10     // cla.getMethods() >>> public void test.Student.setSex(java.lang.String)
11     // cla.getMethods() >>> public java.lang.String test.Student.toString()
12     // cla.getMethods() >>> public void test.Student.showSchool(java.lang.Object)
13     // cla.getMethods() >>> public void test.Student.showHome(java.lang.Object)
14     // cla.getMethods() >>> public java.lang.String test.People.getName()
15     // cla.getMethods() >>> public void test.People.setName(java.lang.String)
16     // cla.getMethods() >>> public boolean java.lang.Object.equals(java.lang.Object)
17     // cla.getMethods() >>> public native int java.lang.Object.hashCode()
18     // cla.getMethods() >>> public final native java.lang.Class java.lang.Object.getClass()
19     // cla.getMethods() >>> public final native void java.lang.Object.notify()
20     // cla.getMethods() >>> public final native void java.lang.Object.notifyAll()
21     // cla.getMethods() >>> public final void java.lang.Object.wait() throws java.lang.InterruptedException
22     // cla.getMethods() >>> public final void java.lang.Object.wait(long,int) throws java.lang.InterruptedException
23     // cla.getMethods() >>> public final native void java.lang.Object.wait(long) throws java.lang.InterruptedException
24     // cla.getMethods() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException
25 }
26 // 获取已声明的所有方法,包括非公共方法和重写的方法,但不包括父类的方法
27 Method[] declaredMethods = cla.getDeclaredMethods();
28 for (Method method : declaredMethods) {
29     System.out.println("cla.getDeclaredMethods() >>> " + method);
30     // cla.getDeclaredMethods() >>> public int test.Student.getAge()
31     // cla.getDeclaredMethods() >>> public void test.Student.setAge(int)
32     // cla.getDeclaredMethods() >>> public double test.Student.getGrade()
33     // cla.getDeclaredMethods() >>> public void test.Student.setGrade(double)
34     // cla.getDeclaredMethods() >>> public java.lang.String test.Student.getSex()
35     // cla.getDeclaredMethods() >>> public void test.Student.setSex(java.lang.String)
36     // cla.getDeclaredMethods() >>> public java.lang.String test.Student.toString()
37     // cla.getDeclaredMethods() >>> public void test.Student.showSchool(java.lang.Object)
38     // cla.getDeclaredMethods() >>> public void test.Student.showHome(java.lang.Object)
39     // cla.getDeclaredMethods() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException
40     // cla.getDeclaredMethods() >>> private void test.Student.doTestStudent()
41 }
42 // 获取指定名称和参数类型的方法
43 // Method testStudent = cla.getMethod("testStudent", String.class);
44 Method testStudent = cla.getDeclaredMethod("testStudent", String.class);
45 System.out.println("cla.getDeclaredMethod() >>> " + testStudent);// cla.getDeclaredMethod() >>> public java.lang.String test.Student.testStudent(java.lang.String) throws java.lang.NullPointerException,java.lang.ArithmeticException
46 // 获取注解
47 Annotation[] annotations = testStudent.getAnnotations();
48 for (Annotation annotation : annotations) {
49     System.out.println("testStudent.getAnnotations() >>> " + annotation);
50     // testStudent.getAnnotations() >>> @java.lang.Deprecated()
51 }
52 // 获取访问修饰符
53 System.out.println("testStudent.getModifiers() >>> " + Modifier.toString(testStudent.getModifiers()));// testStudent.getModifiers() >>> public
54 // 获取返回值类型
55 System.out.println("testStudent.getReturnType() >>> " + testStudent.getReturnType().getName());// testStudent.getReturnType() >>> java.lang.String
56 // 获取名称
57 System.out.println("testStudent.getName() >>> " + testStudent.getName());// testStudent.getName() >>> testStudent
58 // 获取参数
59 Parameter[] parameters = testStudent.getParameters();
60 for (Parameter parameter : parameters) {
61     System.out.println("testStudent.getParameters() >>> " + parameter.getName());
62     // testStudent.getParameters() >>> name
63 }
64 // 获取参数列表
65 Class[] parameterTypes = testStudent.getParameterTypes();
66 for (Class parameterType : parameterTypes) {
67     System.out.println("testStudent.getParameterTypes() >>> " + parameterType.getName());
68     // testStudent.getParameterTypes() >>> java.lang.String
69 }
70 // 获取异常
71 Class[] exceptionTypes = testStudent.getExceptionTypes();
72 for (Class exceptionType : exceptionTypes) {
73     System.out.println("testStudent.getExceptionTypes() >>> " + exceptionType.getName());
74     // testStudent.getExceptionTypes() >>> java.lang.NullPointerException
75     // testStudent.getExceptionTypes() >>> java.lang.ArithmeticException
76 }
77 // 调用方法,非公共属性需要执行setAccessible()方法
78 Object testStudentResult = testStudent.invoke(student, "abc");// >>> testStudent()
79 System.out.println("testStudent.invoke() >>> " + testStudentResult);// testStudent.invoke() >>> abc
80 Method doTestStudent = cla.getDeclaredMethod("doTestStudent");
81 // Object doTestStudentResult = doTestStudent.invoke(student);// java.lang.IllegalAccessException
82 doTestStudent.setAccessible(true);
83 Object doTestStudentResult = doTestStudent.invoke(student);// >>> doTestStudent()
84 System.out.println("doTestStudent.invoke() >>> " + doTestStudentResult);// doTestStudent.invoke() >>> null

获取类的构造方法

 1 // 获取所有的公共构造方法
 2 Constructor<?>[] constructors = cla.getConstructors();
 3 for (Constructor<?> constructor : constructors) {
 4     System.out.println("cla.getConstructors() >>> " + constructor);
 5     // cla.getConstructors() >>> public test.Student()
 6     // cla.getConstructors() >>> public test.Student(java.lang.String)
 7 }
 8 // 获取所有的构造方法,包括非公共构造方法
 9 Constructor<?>[] declaredConstructors = cla.getDeclaredConstructors();
10 for (Constructor<?> constructor : declaredConstructors) {
11     System.out.println("cla.getDeclaredConstructors() >>> " + constructor);
12     // cla.getDeclaredConstructors() >>> public test.Student()
13     // cla.getDeclaredConstructors() >>> public test.Student(java.lang.String)
14     // cla.getDeclaredConstructors() >>> private test.Student(int,double,java.lang.String)
15 }
16 // 获取参数类型的构造方法
17 // Constructor<?> peopleConstructor = cla.getConstructor(String.class);
18 Constructor<?> studentConstructor = cla.getDeclaredConstructor(int.class, double.class, String.class);
19 System.out.println("cla.getDeclaredConstructor() >>> " + studentConstructor);// cla.getDeclaredConstructor() >>> private test.Student(int,double,java.lang.String)
20 // 调用方法,非公共属性需要执行setAccessible()方法
21 // student = (Student) studentConstructor.newInstance(10, 3.5, "女");// java.lang.IllegalAccessException
22 studentConstructor.setAccessible(true);
23 student = (Student) studentConstructor.newInstance(10, 3.5, "女");
24 System.out.println("student.toString() >>> " + student);// student.toString() >>> [age:10][grade:3.5][sex:女]
原文地址:https://www.cnblogs.com/shamao/p/10984331.html