反射-探索
文章目录
- 1、什么是反射?
- 2、反射的作用是什么?
- 1. 动态类型检查与操作
- 2. 灵活性与解耦
- 3. 通用代码与工具开发
- 4. 绕过访问限制
- 5. 注解处理
- 3、反射适用场景是什么?
- 典型应用场景
- 4、反射使用有什么好处和坏处?
- **优点**
- **缺点**
- 5、Java中怎么实现反射?
- 5.1 场景案例
- 5.2 代码
- **1. 获取 Class 对象**
- **(1) `Class.forName("全限定类名")`**
- **(2) `类名.class`**
- **(3) `对象.getClass()`**
- **2. 获取类的构造方法(Constructor)**
- **(1) 获取所有构造方法**
- **(2) 获取特定构造方法**
- **(3) 创建对象**
- **3. 获取类的字段(Field)**
- **(1) 获取所有字段**
- **(2) 获取特定字段**
- **(3) 读取/修改字段值**
- **4. 获取类的方法(Method)**
- **(1) 获取所有方法**
- **(2) 获取特定方法**
- **(3) 调用方法**
- **5. 反射的应用示例**
- **示例1:动态创建对象并调用方法**
- **示例2:修改私有字段**
- **示例3:调用静态方法**
- **总结**
1、什么是反射?
反射是一种自然现象,表现为受刺激物对刺激物的逆反应。反射的外延宽泛。物理学领域是指声波、光波或其他电磁波遇到别的媒质分界面而部分仍在原物质中传播的现象;生物学领域里反射是在中枢神经系统参与下,机体对内外环境刺激所作出的规律性反应;
计算机领域中反射是提供封装程序集、模块和类型的对象;
电子领域中就是在传输线上的回波。研究反射产生机制,把握反射规律,有利于人类更好的利用反射进行各种相关工作。其应用领域是相当广泛的。
2、反射的作用是什么?
反射(Reflection)是编程语言提供的一种机制,允许程序在运行时动态地检查、访问和修改自身的结构或行为。它的核心作用包括以下几点:
1. 动态类型检查与操作
- 运行时类型信息(RTTI):反射允许程序在运行时获取对象的类信息(如类名、方法、字段、注解等)。
- 动态调用:通过反射可以调用对象的方法、访问或修改字段(甚至是私有成员),而无需在编译时确定这些操作。
示例(Java):
Class<?> clazz = obj.getClass();
Method method = clazz.getMethod("methodName", String.class);
method.invoke(obj, "参数");
2. 灵活性与解耦
- 动态加载类:通过类名字符串加载类(如
Class.forName("com.example.MyClass")
),避免硬编码依赖。 - 框架设计:许多框架(如Spring、Hibernate)利用反射实现依赖注入、ORM映射等,无需提前知道用户定义的类。
3. 通用代码与工具开发
- 序列化/反序列化:如JSON库(Gson、Jackson)通过反射读取对象的字段生成字符串,或从字符串还原对象。
- 测试工具:单元测试框架(如JUnit)通过反射动态识别并运行测试方法。
4. 绕过访问限制
- 反射可以访问或修改类的私有成员(通过
setAccessible(true)
),但需注意破坏封装性带来的风险。
5. 注解处理
- 运行时通过反射读取注解(如
@GetMapping
、@Autowired
),实现基于配置的行为动态化。
反射的核心价值是将静态语言动态化,但应权衡其灵活性与设计复杂度。
3、反射适用场景是什么?
典型应用场景
- Spring框架:依赖注入、AOP。
- IDE开发工具:如自动补全、调试器。
- 插件系统:动态加载外部模块。
4、反射使用有什么好处和坏处?
优点
- 动态性:可以在运行时加载类、调用方法、修改字段。
- 灵活性:适用于框架开发(如 Spring、Hibernate)。
- 通用性:可以编写通用工具(如 JSON 序列化)。
缺点
- 性能较低:反射比直接调用慢(但 JVM 优化后差距减小)。
- 安全性问题:可以绕过访问控制(如访问
private
成员)。可能破坏封装性,需谨慎使用。 - 代码可读性差:动态调用使代码难以维护。
5、Java中怎么实现反射?
5.1 场景案例
5.2 代码
在 Java 中,反射(Reflection)是通过 java.lang.reflect
包和 Class
类实现的,允许程序在运行时动态获取类的信息并操作类或对象。以下是 Java 实现反射的详细步骤和示例:
1. 获取 Class 对象
要使用反射,首先需要获取目标类的 Class
对象。有以下几种方式:
(1) Class.forName("全限定类名")
Class<?> clazz = Class.forName("java.lang.String"); // 加载 String 类
(2) 类名.class
Class<String> stringClass = String.class; // 直接获取 String 的 Class 对象
(3) 对象.getClass()
String str = "Hello";
Class<?> clazz = str.getClass(); // 获取 str 的运行时类
2. 获取类的构造方法(Constructor)
可以通过 Class
对象获取构造方法,并创建新实例:
(1) 获取所有构造方法
Constructor<?>[] constructors = clazz.getConstructors(); // 获取所有 public 构造方法
Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); // 获取所有构造方法(包括 private)
(2) 获取特定构造方法
Constructor<?> constructor = clazz.getConstructor(String.class); // 获取 public 构造方法(参数类型)
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(int.class); // 获取 private 构造方法
(3) 创建对象
Object obj = constructor.newInstance("Hello"); // 调用构造方法创建对象
privateConstructor.setAccessible(true); // 强制访问 private 构造方法
Object privateObj = privateConstructor.newInstance(123);
3. 获取类的字段(Field)
可以获取类的字段(成员变量),并读取或修改值:
(1) 获取所有字段
Field[] fields = clazz.getFields(); // 获取所有 public 字段
Field[] declaredFields = clazz.getDeclaredFields(); // 获取所有字段(包括 private)
(2) 获取特定字段
Field field = clazz.getField("fieldName"); // 获取 public 字段
Field privateField = clazz.getDeclaredField("privateField"); // 获取 private 字段
(3) 读取/修改字段值
Object obj = clazz.newInstance(); // 创建对象
field.set(obj, "newValue"); // 修改 public 字段的值
privateField.setAccessible(true); // 强制访问 private 字段
Object value = privateField.get(obj); // 读取 private 字段的值
4. 获取类的方法(Method)
可以获取类的方法,并动态调用:
(1) 获取所有方法
Method[] methods = clazz.getMethods(); // 获取所有 public 方法(包括继承的)
Method[] declaredMethods = clazz.getDeclaredMethods(); // 获取所有方法(不包括继承的)
(2) 获取特定方法
Method method = clazz.getMethod("methodName", String.class); // 获取 public 方法(方法名 + 参数类型)
Method privateMethod = clazz.getDeclaredMethod("privateMethod", int.class); // 获取 private 方法
(3) 调用方法
Object obj = clazz.newInstance(); // 创建对象
method.invoke(obj, "参数"); // 调用 public 方法
privateMethod.setAccessible(true); // 强制访问 private 方法
privateMethod.invoke(obj, 123); // 调用 private 方法
5. 反射的应用示例
示例1:动态创建对象并调用方法
Class<?> clazz = Class.forName("com.example.User");
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object user = constructor.newInstance("张三", 25);Method getNameMethod = clazz.getMethod("getName");
String name = (String) getNameMethod.invoke(user);
System.out.println("用户名:" + name); // 输出:用户名:张三
示例2:修改私有字段
Class<?> clazz = Class.forName("com.example.User");
Object user = clazz.newInstance();Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true); // 允许访问 private 字段
ageField.set(user, 30);System.out.println(user); // 输出:User{age=30}
示例3:调用静态方法
Class<?> clazz = Class.forName("com.example.MathUtils");
Method addMethod = clazz.getMethod("add", int.class, int.class);
int result = (int) addMethod.invoke(null, 10, 20); // 静态方法,obj 传 null
System.out.println("结果:" + result); // 输出:结果:30
总结
Java 反射的核心步骤:
- 获取
Class
对象(Class.forName()
、.class
、getClass()
)。 - 获取构造方法(
getConstructor()
、newInstance()
)。 - 获取字段(
getField()
、set()
/get()
)。 - 获取方法(
getMethod()
、invoke()
)。
反射广泛应用于框架(Spring、MyBatis)、动态代理、测试工具等场景,但应谨慎使用以避免性能和安全问题。