Day(23)--反射
反射
反射允许对成员变量,成员方法和构造方法的信息进行编程访问
获取class对象
-
Class.forName("全类名");
全类名 = 包名 + 类名
-
类名.class
-
对象.getClass();
利用反射获取构造方法
在 Java 里,反射机制可以让你在运行时获取类的信息,其中就包含构造方法。下面我会给出一个示例代码,展示怎样通过反射获取构造方法。
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; class MyClass {// 无参构造方法public MyClass() {System.out.println("调用无参构造方法");} // 有参构造方法public MyClass(String message) {System.out.println("调用有参构造方法,参数为: " + message);} } public class ReflectionConstructorExample {public static void main(String[] args) {try {// 获取类的 Class 对象Class<?> clazz = MyClass.class; // 获取所有公共构造方法Constructor<?>[] constructors = clazz.getConstructors(); // 输出所有公共构造方法System.out.println("所有公共构造方法:");for (Constructor<?> constructor : constructors) {System.out.println(constructor);} // 获取无参构造方法Constructor<?> noArgConstructor = clazz.getConstructor();// 使用无参构造方法创建对象MyClass obj1 = (MyClass) noArgConstructor.newInstance(); // 获取有参构造方法Constructor<?> argConstructor = clazz.getConstructor(String.class);// 使用有参构造方法创建对象MyClass obj2 = (MyClass) argConstructor.newInstance("Hello, Reflection!"); } catch (NoSuchMethodException | InstantiationException |IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}} }
代码解释
-
MyClass
类:定义了一个类,包含无参构造方法和有参构造方法。 -
ReflectionConstructorExample
类:
-
clazz.getConstructors()
:获取类的所有公共构造方法。 -
clazz.getConstructor()
:获取无参构造方法。 -
clazz.getConstructor(String.class)
:获取带有一个String
类型参数的构造方法。 -
constructor.newInstance()
:使用构造方法创建对象。
-
注意事项
-
若要获取非公共的构造方法,可使用
getDeclaredConstructors()
和getDeclaredConstructor()
方法。 -
在使用
newInstance()
方法创建对象时,可能会抛出InstantiationException
、IllegalAccessException
和InvocationTargetException
异常,需要进行异常处理。
利用反射获取成员变量
在 Java 中,通过反射机制能够在运行时获取类的成员变量。下面给出示例代码,展示如何使用反射来获取成员变量:
import java.lang.reflect.Field; class Student {// 公共成员变量public String name;// 私有成员变量private int age;// 受保护成员变量protected String gender; public Student(String name, int age, String gender) {this.name = name;this.age = age;this.gender = gender;} } public class ReflectionFieldExample {public static void main(String[] args) {try {// 获取 Student 类的 Class 对象Class<?> studentClass = Student.class; // 创建 Student 类的实例Student student = new Student("Alice", 20, "Female"); // 获取所有公共成员变量Field[] publicFields = studentClass.getFields();System.out.println("所有公共成员变量:");for (Field field : publicFields) {System.out.println(field.getName() + " : " +()); field.getType// 获取公共成员变量的值System.out.println("值为: " + field.get(student));} // 获取所有声明的成员变量(包括私有、受保护和公共的)Field[] declaredFields = studentClass.getDeclaredFields();System.out.println("\n所有声明的成员变量:");for (Field field : declaredFields) {System.out.println(field.getName() + " : " + field.getType());// 如果是私有成员变量,需要设置可访问性if (!field.isAccessible()) {field.setAccessible(true);}// 获取成员变量的值System.out.println("值为: " + field.get(student));} } catch (IllegalAccessException e) {e.printStackTrace();}} }
代码解释
-
Student
类:定义了一个包含公共、私有和受保护成员变量的类,并且提供了一个构造方法用于初始化这些变量。 -
ReflectionFieldExample
类:
-
studentClass.getFields()
:获取类的所有公共成员变量。 -
studentClass.getDeclaredFields()
:获取类声明的所有成员变量,包括私有、受保护和公共的。 -
field.get(student)
:获取指定对象中该成员变量的值。对于私有成员变量,需要先调用field.setAccessible(true)
来设置可访问性。
-
注意事项
-
getFields()
方法只能获取公共的成员变量,而getDeclaredFields()
方法可以获取类中声明的所有成员变量。 -
访问私有成员变量时,需要调用
setAccessible(true)
方法来绕过 Java 的访问控制检查,但这可能会破坏类的封装性,使用时要谨慎。
反射获取成员方法
在 Java 里,借助反射机制可以在运行时获取类的成员方法。下面是一个示例代码,展示如何通过反射获取成员方法并调用它们:
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; class Calculator {// 无参方法public int add() {return 1 + 2;} // 有参方法public int multiply(int a, int b) {return a * b;} } public class ReflectionMethodExample {public static void main(String[] args) {try {// 获取 Calculator 类的 Class 对象Class<?> calculatorClass = Calculator.class; // 创建 Calculator 类的实例Calculator calculator = calculatorClass.getDeclaredConstructor().newInstance(); // 获取无参方法Method addMethod = calculatorClass.getMethod("add");// 调用无参方法int result1 = (int) addMethod.invoke(calculator);System.out.println("无参方法 add 的结果: " + result1); // 获取有参方法Method multiplyMethod = calculatorClass.getMethod("multiply", int.class, int.class);// 调用有参方法int result2 = (int) multiplyMethod.invoke(calculator, 3, 4);System.out.println("有参方法 multiply 的结果: " + result2); } catch (NoSuchMethodException | InstantiationException |IllegalAccessException | InvocationTargetException e) {e.printStackTrace();}} }
代码解释
-
Calculator
类:定义了两个方法,一个是无参的add
方法,另一个是带有两个int
类型参数的multiply
方法。 -
ReflectionMethodExample
类:
-
calculatorClass.getMethod("add")
:获取名为add
的无参公共方法。 -
calculatorClass.getMethod("multiply", int.class, int.class)
:获取名为multiply
且参数类型为两个int
类型的公共方法。 -
method.invoke(calculator, ...)
:调用指定对象的方法。对于无参方法,直接传入对象;对于有参方法,需要传入对象和相应的参数。
-
注意事项
-
getMethod
方法只能获取公共方法,如果要获取非公共方法,可以使用getDeclaredMethod
方法。 -
在调用
invoke
方法时,可能会抛出InvocationTargetException
异常,该异常的getCause
方法可以获取实际抛出的异常。 -
若要调用非公共方法,需要先调用
method.setAccessible(true)
来设置可访问性。
反射的作用
反射是 Java 提供的一种强大机制,允许程序在运行时动态地获取类的信息、创建对象、调用方法以及访问和修改成员变量等。下面详细介绍反射的作用:
1. 动态创建对象
在 Java 里,一般情况下创建对象使用 new
关键字,但在某些场景下,在编译时并不知道要创建哪个类的对象,这时反射就能发挥作用。比如,在配置文件中指定类名,程序运行时根据配置文件的内容动态创建对象。
java
import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; class ExampleClass {public ExampleClass() {System.out.println("ExampleClass 实例已创建");} } public class DynamicObjectCreation {public static void main(String[] args) {try {Class<?> clazz = Class.forName("ExampleClass");Constructor<?> constructor = clazz.getConstructor();ExampleClass obj = (ExampleClass) constructor.newInstance();} catch (ClassNotFoundException | NoSuchMethodException |InstantiationException | IllegalAccessException |InvocationTargetException e) {e.printStackTrace();}} }
2. 动态调用方法
借助反射,能够在运行时动态地调用对象的方法。这在框架开发里十分有用,比如在测试框架中,可根据测试用例动态调用相应的测试方法。
java
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; class MethodCallExample {public void printMessage(String message) {System.out.println("消息: " + message);} } public class DynamicMethodCall {public static void main(String[] args) {try {MethodCallExample example = new MethodCallExample();Class<?> clazz = example.getClass();Method method = clazz.getMethod("printMessage", String.class);method.invoke(example, "Hello, Reflection!");} catch (NoSuchMethodException | IllegalAccessException |InvocationTargetException e) {e.printStackTrace();}} }
3. 访问和修改私有成员
虽然 Java 有访问控制机制来限制对私有成员的访问,但反射可以绕过这些限制,在运行时访问和修改私有成员变量和方法。不过要谨慎使用,因为这可能破坏类的封装性。
java
import java.lang.reflect.Field; class PrivateAccessExample {private String privateField = "私有字段的值"; } public class AccessPrivateMembers {public static void main(String[] args) {try {PrivateAccessExample example = new PrivateAccessExample();Class<?> clazz = example.getClass();Field field = clazz.getDeclaredField("privateField");field.setAccessible(true);String value = (String) field.get(example);System.out.println("私有字段的值: " + value);field.set(example, "新的值");System.out.println("修改后的私有字段的值: " + field.get(example));} catch (NoSuchFieldException | IllegalAccessException e) {e.printStackTrace();}} }
4. 实现通用的工具类
反射能够用于实现通用的工具类,比如序列化和反序列化工具。通过反射,可以遍历对象的所有字段,将其转换为字节流或者从字节流中恢复对象。
5. 框架开发
众多 Java 框架(如 Spring、Hibernate 等)广泛使用反射机制。例如,Spring 框架通过反射来创建和管理 Bean,根据配置文件或者注解动态地注入依赖。
6. 插件化开发
在插件化开发中,主程序可以通过反射加载和使用插件类,无需在编译时知道插件的具体实现。这样能增强程序的扩展性和灵活性。
综上所述,反射机制为 Java 程序带来了高度的灵活性和可扩展性,不过也存在一些缺点,像性能开销较大、破坏封装性等,所以在使用时要权衡利弊。