前言
每天一小步,进步一大步。java 反射
0x01 java.lang.Class类
getClass()方法
Object类有一个getClass()
方法,它返回对的引用类对象的类对象。
以下代码显示如何获取对Test类的Class对象的引用:
1 | package com.example.demo; |
结果如下:
1 | class com.example.demo.Perso |
forName()方法
Class类forName()
static方法返回对Class对象的引用。
它的重载方法是
1 | Class<?> forName(String className) |
forName()
方法的第一个版本接受完全该类的限定名作为参数,并加载该类,并返回其对象引用。
如果类已经加载,它将返回对Class对象的引用。
第二版本方法可以控制是否初始化或不初始化该类在加载时。 我们也可以传入类加载器。
下面的代码显示了如何加载一个类并获取对它的Class对象的引用。
1 | package com.example.demo; |
Java类反射
我们可以使用Java反射来获取关于类的信息,例如作为其包名称,其访问修饰符等。
要获得简单的类名,请使用 Class 中的 getSimpleName()方法。
String simpleName = c.getSimpleName();
类的修饰符是关键字之前的关键字类在类声明中,如 abstract , public 。
Class 中的 getModifiers()方法返回类的所有修饰符。
getModifiers()方法返回一个整数。我们必须调用 java.lang.reflect.Modifier.toString(int modifiers)以获得修饰符的文本形式。
要获取超类的名称,请使用 Class 中的 getSuperclass()方法。
如果对Object类调用getSuperclass()方法,它将返回null,因为它没有超类。
要获取类实现的所有接口的名称,请使用 getInterfaces()。
1 | Class[] interfaces = c.getInterfaces(); |
例子:
1 | package com.example.demo; |
结果如下:
1 | class MyClass<T> extends Object implements Cloneable, Serializable |
java字段反射
我们可以使用java.lang.reflect.Field类来获取关于类中的字段的信息。
以下四种方法在Class类可以返回关于字段的 Field 对象。
1 | Field[] getFields() |
getFields()
方法返回所有可访问的公共字段在类中声明或继承自超类。
getDeclaredFields()
方法返回所有字段只出现在类的声明中(不是从继承的字段)。
getField(String name)
和getDeclaredField(String name)
通过字段名获取Field
对象。
1 | package com.example.demo; |
结果如下:
1 | Declared Fields for com.example.demo.MyClass |
java方法反射
java.lang.reflect.Method
类的实例表示一个方法。 java.lang.reflect.Constructor
类的实例表示一个构造函数。
方法
和构造方法
继承自一个通用的抽象超类可执行
。
可执行文件中的参数由Parameter
类的对象表示
Executable
类中的getParameters()方法获取所有参数作为Parameter
的数组。
默认情况下,参数名称不存储在类文件中。参数类的名称将类似于arg0,arg1等。我们可以通过编译源来保存类文件中的实际参数名代码使用-parameters
选项与javac
编译器。
可执行文件中的getExceptionTypes()
方法类返回一个由Executable抛出的异常数组。
Executable类的getModifiers()方法将修饰符作为int返回。
来自Executable类的getTypeParameters()方法返回一个TypeVariable数组,该数组表示通用方法或构造函数的类型参数。
例子:
1 | package com.example.demo; |
结果如下:
1 | getInt |
反射方法
以下四个方法在Class类中返回有关联的方法的信息
1 | Method[] getMethods() |
getMethods()
方法返回该类的所有可访问的公共方法无论从类中还是继承自超类。
getDeclaredMethods()
方法返回所有只在中声明的方法该类(不包括从超类继承的方法)。
getMethod(String name,Class … parameterTypes)和getDeclaredMethod(String name,Class … parameterTypes)通过方法名和参数类型获取Method对象。
Method类中的getReturnType()方法返回包含有关返回类型信息的Class对象。
1 | package com.example.demo; |
结果如下:
1 | Declared Methods for com.example.demo.MyClass |
java构造函数反射
以下四种方法来自Class
类获取有关构造函数的信息:
1 | Constructor[] getConstructors() |
getConstructors()
方法返回当前和超类的所有公共构造函数。
getDeclaredConstructors()
方法返回当前类的所有声明的构造函数。
getConstructor(Class … parameterTypes)和getDeclaredConstructor(Class … parameterTypes)通过参数类型获取构造函数对象。
以下代码显示了如何对构造函数执行反射。
1 | package com.example.demo; |
结果如下:
1 | Constructors for com.example.demo.MyClass |
0x06 java反射对象创建
Java反射 - Java反射对象创建
我们可以使用反射动态创建类的对象。通过调用其中一个构造函数。
然后我们可以访问对象的字段的值,设置它们的值,并调用它们的方法。
有两种方法来创建对象:
- 使用no-args构造函数
- 使用带参数的构造函数
无参数构造函数
如果你有一个
Class
对象的引用,你可以创建一个对象该类对Class类使用newInstance()
方法。此方法不使用参数,并且是等效的使用new运算符的类的no-args构造函数。
1
MyClass m = myObject.newInstance();
1 | package com.example.demo; |
结果如下:
1 | called |
带参数的构造函数
您可以通过调用特定的构造函数使用反射创建对象。它涉及两个步骤。
- 获取构造函数的实例
- 调用newInstance来调用它
你可以得到这个构造函数的引用,如下所示:
1
Constructor<MyClass> cons = myClass.getConstructor(int.class, String.class);
然后调用带有参数的newInstance()
方法来创建一个对象。
1 | package com.example.demo; |
结果如下:
1 | called |
调用方法
我们可以通过方法引用使用反射调用方法。
要调用方法,请调用方法引用的 invoke()方法。
它的第一个参数是它来自和的对象第二个参数是同一顺序中所有参数的varargs作为方法的声明。
在静态方法的情况下,我们只需要为第一个参数指定null。
1 | package com.example.demo; |
结果如下:
1 | abc |
0x08 java反射字段访问
Java反射 - Java反射字段访问
我们可以使用反射在两个步骤中获取或设置字段。
- 获取字段的引用。
- 要读取字段的值,请在字段上调用getXxx()方法,其中Xxx是字段的数据类型。
- 要设置字段的值,请调用相应的setXxx()方法。
以相同的方式访问静态和实例字段。
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
32package com.example.demo;
import java.lang.reflect.Field;
class MyClass {
public String name = "Unknown";
public MyClass() {
}
public String toString() {
return "name=" + this.name;
}
}
public class Test12 {
public static void main(String[] args) {
Class<MyClass> ppClass = MyClass.class;
try {
MyClass p = ppClass.newInstance();
Field name = ppClass.getField("name");
String nameValue = (String) name.get(p);
System.out.println("Current name is " + nameValue);
name.set(p, "abc");
nameValue = (String) name.get(p);
System.out.println("New name is " + nameValue);
} catch (InstantiationException | IllegalAccessException | NoSuchFieldException | SecurityException
| IllegalArgumentException e) {
System.out.println(e.getMessage());
}
}
}
结果如下:
1 | Current name is Unknown |
绕过辅助功能检查
使用反射访问类的非可访问字段,方法和构造函数从 AccessibleObject 类调用 setAccessible(boolean flag)方法。
我们需要使用true参数调用此方法,以使该字段,方法和构造函数可访问。
1 | package com.example.demo; |
结果如下:
1 | Current name is Unknown |
0x09 java数组反射
Java反射 - Java数组反射
我们可以使用Class类中的isArray()方法来检查类是否是数组。
我们可以创建一个数组,使用反射通过读取和修改其元素的值java.lang.reflect.Array
类。
Array类的getLength()
方法获取数组的长度。
Array类中的所有方法都是静态的。
要创建数组,请使用Array类中的重载静态方法newInstance()。
1 | Object newInstance(Class<?> componentType, int arrayLength) |
第一个方法根据指定的组件类型和数组长度创建一个数组。
第二个版本创建指定组件类型和尺寸的数组。
newInstance()方法的返回类型是Object,我们需要将它转换为实际的数组类型。
下面的代码创建一个长度为5的int
数组。
1 | int[] ids = (int[])Array.newInstance(int.class, 5); |
下面的代码创建一个长度为5乘3的int
数组。
1 | int[][] matrix = (int[][])Array.newInstance(int.class, 5, 3); |
以下代码显示了如何动态创建数组并操作其元素。
1 | package com.example.demo; |
结果如下:
1 | n1 = 0,n2=0 |
获取数组的维度
Java支持array数组。
类中的getComponentType()
方法返回数组的元素类型的Class对象。
以下代码说明了如何获取数组的维度。
1 | package com.example.demo; |
结果如下:
1 | int[][][] dimension is 3 |
展开数组
Java数组是一个固定长度的数据结构。
要放大数组,我们可以创建一个更大尺寸的数组,并将旧数组元素复制到新数组元素。
以下代码显示如何使用反射展开数组。
1 | package com.example.demo; |
结果如下:
1 | 2 |