0%

java反射

java反射

之前只是浅浅了解了一下,现在来深度学习一下

什么是java反射机制?

Java反射机制是指在运行时动态地获取一个类的信息,以及在运行时动态地创建对象、调用方法、访问属性等操作。Java反射机制提供了许多方法来获取一个类的信息,包括类的名称、父类、接口、构造方法、方法、字段等。通过反射,可以在运行时动态地创建对象,而不需要在编译时确定对象的类型。

相关的类

java.lang.Class 代表整个字节码。代表一个类型,代表整个类。

java.lang.reflect.Method 代表字节码中的方法字节码。代表类中的方法。

java.lang.reflect.Constructor 代表字节码中的构造方法字节码。代表类中的构造方法。

java.lang.reflect.Field 代表字节码中的属性字节码。代表类中的成员变量(静态变量+实例变量)。

注意:必须要先获得类,才能进一步获得类的方法和属性

获取class的三种方法

要操作一个类的字节码,需要首先获取到这个类的字节码,我们应该怎么获取java.lang.Class实例呢?

1
2
3
4
5
Class.forName(“完整类名带包名”):使用这种方法会调用该类的静态代码模块,例如Class.forName("java.lang.String")

对象.getClass()

任何类型.class

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package src;

import java.util.Arrays;

public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//1.获取一个类的结构信息(类对象 Class对象)
// 1.1Class.forName(类的完整路径字符串);
//Class clazz = Class.forName("java.lang.String");
//1.2 类名.class
// Class clazz = String.class;
//1.3 对象名.getClass()
String str = "ycxlo";
Class clazz = str.getClass();
//Integer in = new Integer(20);
//2.从类对象中获取类的各种结构信息
System.out.println(clazz.getName());
System.out.println(clazz.getSimpleName());
System.out.println(clazz.getSuperclass());
System.out.println(Arrays.toString(clazz.getInterfaces()));
}
}

输出:

1
2
3
4
java.lang.String
String
class java.lang.Object
[interface java.io.Serializable, interface java.lang.Comparable, interface java.lang.CharSequence]

通过反射获取实例化对象

1
对象.newInstance()

注:newInstance()方法内部实际上调用了无参数构造方法,必须保证无参构造存在才可以。
否则会抛出java.lang.InstantiationException异常。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
package src;

import java.util.Arrays;

public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//不使用反射创建对象
//Dog dog = new Dog();
//使用反射创建对象
//1.获取类的完整路径字符串
String className = "src.test";
//2.根据完整路径字符串获取Class对象信息
Class clazz = Class.forName(className);
//3.直接使用Class的方法创建对象
Object obj = clazz.newInstance();
System.out.println(obj.toString());
}
}

test.java

1
2
3
4
5
6
7
8
9
package src;

import java.util.Scanner;

public class test{
public String toString(){
return "ycxlo";
}
}

img

也可以通过Constructor的newInstance()方法创建对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package src;

import java.lang.reflect.Constructor;

public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//不使用反射创建对象
//Dog dog = new Dog();
//使用反射创建对象
//1.获取类的完整路径字符串
String className = "src.test";
//2.根据完整路径字符串获取Class对象信息
Class clazz = Class.forName(className);
//3.获取无参数构造方法
Constructor con = clazz.getConstructor();
//4.使用无参数构造方法来创建对象
Object obj = con.newInstance();
System.out.println(obj);
}
}

还可以这样:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package src;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class ReflectTest2 {
public static void main(String[] args) throws Exception {
//不使用反射创建对象
//Dog dog = new Dog();
//使用反射创建对象
//1.获取类的完整路径字符串
String className = "src.test";
//2.根据完整路径字符串获取Class对象信息
Class clazz = Class.forName(className);
Object test = clazz.getConstructor().newInstance();
Method m1 = clazz.getMethod("toString");
System.out.println(m1.invoke(test));
}
}
1
2
3
4
5
6
7
8
9
10
11
invoke() 是 Java 反射 API 中的一个方法,用于动态地调用方法或构造函数。

在 Java 中,我们可以使用 Method 类的 invoke() 方法来调用具有相应参数的方法。类似地,我们也可以使用 Constructor 类的 newInstance() 方法来创建对象。

invoke() 方法接受两个参数:

第一个参数是要调用方法或构造函数的对象实例,如果方法是静态的,则传入 null。
第二个参数是要传递给方法或构造函数的参数列表,以可变参数的形式提供。
调用 invoke() 方法后,它将执行相应的方法或构造函数,并且返回结果(如果方法有返回值)。如果方法或构造函数抛出异常,invoke() 方法也会抛出相应的异常。

在代码示例中,m1.invoke(test) 使用反射调用了 test 对象的 toString() 方法。这将会执行 test.toString(),并返回其结果。

反射Filed(字段)

class类方法

public T newInstance() 创建对象

public String getName() 返回完整类名带包

public String getSimpleName() 返回类名

public Field[] getFields() 返回类中public修饰的属性

public Field[] getDeclaredFields() 返回类中所有的属性

public Field getDeclaredField(String name) 根据属性名name获取指定的属性

public native int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】

public Method[] getDeclaredMethods() 返回类中所有的实例方法

public Method getDeclaredMethod(String name, Class<?>… parameterTypes) 根据方法名name和方法形参获取指定方法

public Constructor<?>[] getDeclaredConstructors() 返回类中所有的构造方法

public Constructor getDeclaredConstructor(Class<?>… parameterTypes) 根据方法形参获取指定的构造方法

public native Class<? super T> getSuperclass() 返回调用类的父类

public Class<?>[] getInterfaces() 返回调用类实现的接口集合

Filed类方法

public String getName() 返回属性名

public int getModifiers() 获取属性的修饰符列表,返回的修饰符是一个数字,每个数字是修饰符的代号【一般配合Modifier类的toString(int x)方法使用】

public Class<?> getType() 以Class类型,返回属性类型【一般配合Class类的getSimpleName()方法使用】

public void set(Object obj, Object value) 设置属性值

public Object get(Object obj) 读取属性值

示例

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
package src;

import java.lang.reflect.Field;

public class ReflectTest {
public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
// 获取类对象
Class<MyClass> myClassClass = MyClass.class;

// 获取指定名称的字段对象
Field field = myClassClass.getDeclaredField("myField");
field.setAccessible(true);//这个尽量往前面写,写在后面可能会报错

// 创建 MyClass 实例
MyClass myObject = new MyClass();
System.out.println("Field Value (Before Modification): " + field.get(myObject));

// 修改字段值
field.set(myObject, "New Value");
System.out.println("Field Value (After Modification): " + field.get(myObject));
}
}

class MyClass {
private String myField = "Initial Value";
}

输出:

1
2
Field Value (Before Modification): Initial Value
Field Value (After Modification): New Value

反射修改final static变量

演示代码

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
package src;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;

public class staticfinaltest {

public static void main(String[] args) throws Exception {
Field number = Test.class.getDeclaredField("NUMBER");
setFinalStatic(number, 3);
System.out.println(Test.getNumber());
}

private static void setFinalStatic(Field field, Object newValue) throws NoSuchFieldException, IllegalAccessException {
field.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);

modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);
field.set(null, newValue);
}
}


class Test{

private static final Integer NUMBER = 1;

public static Integer getNumber() {
return NUMBER;
}
}

一点解释:

1
2
3
4
5
field.setAccessible(true):将 field 对象的可访问性设置为 true,以便我们能够访问并修改该字段。
Field modifiersField = Field.class.getDeclaredField("modifiers"):通过 getDeclaredField 方法获取到 Field 类的名为 "modifiers" 的字段对象,它用于表示字段修饰符。
modifiersField.setAccessible(true):将 modifiersField 对象的可访问性设置为 true,以便我们能够访问并修改该字段的值。
modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL):通过调用 setInt 方法,将 field 对象的修饰符字段的值进行修改。通过按位与操作和取反操作,可以将 FIELD 修饰符中的 FINAL 标志位设为 0,从而达到去除 final 修饰符的目的。
field.set(null, newValue):最后,通过调用 set 方法将新的值 newValue 赋给 field 对象表示的 final static 变量。由于该变量是 static 的,因此使用 null 作为目标对象。对于静态字段,我们不需要通过实例来访问或修改其值,而是直接通过类名来访问。因此,在使用 field.set(null, newValue) 时,null 被用作代替实例的目标对象,表示我们不需要特定的实例来设置静态字段的值。

各个修饰符对应的modifiers的值

1
2
3
4
5
6
7
第 1 位(从右往左):public
第 2 位:private
第 3 位:protected
第 4 位:static
第 5 位:final
第 6 位:...
第 7 位:...

例如,10的二进制是1010,那么对应的就是private static

相当于modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL)是把原来的final修饰符给删去,以便执行set方法