Java反射技术文章大纲
1. 引言
- 什么是反射?
Java反射是Java语言的一种特性,它允许程序在运行时自我检查并对内部成员进行操作。这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。
对于任意一个类,都能够知道这个类的所有属性和方法;
对于任意一个对象,哦都能够调用他的任意方法和属性,并且能改变它的属性; - 为什么需要反射?
在运行是判断任意一个对象所属的类;
在运行时实例化任意一个类的对象;
在运行时获取任意类的名称、package信息、所有属性、方法、注解、类型、类加载器等;
在运行时获取任意对象的属性,并且能改变对象的属性;
在运行时调用任意对象方法。 - 文章目标
记录我个人学习反射的心得体会。
2. Java反射的核心API
- Class类
描述其它类的,描述类的类。- 获取
Class对象的方法:Class.forName()、对象.getClass()、类名.class。
- 获取
package chapter3;
public class Test1 {
public static void main(String[] args) throws ClassNotFoundException {
//第1种方式: 类名.class
Class<?> clazz1 = Student.class;
//第2种方式: 对象.getClass()
Student student = new Student("张三", 18);
Class<?> clazz2 = student.getClass();
//第3种方式: Class.forName()
Class<?> clazz3 = Class.forName("chapter3.Student");
// 比较两个类的引用是否相等
System.out.println(clazz1 == clazz2);
// 比较两个类的引用是否相等
System.out.println(clazz1 == clazz3);
// 获取类的全名
System.out.println(clazz1.getName());
// 获取类的简称
System.out.println(clazz1.getSimpleName());
// 判断类是否为基本数据类型
System.out.println(clazz1.isPrimitive());
// 判断类是否为数组
System.out.println(clazz1.isArray());
// 获取类的包名
System.out.println(clazz1.getPackage().getName());
}
}
- Constructor类
描述构造函数的。- 获取构造器:
getConstructor()、getDeclaredConstructor()。 - 创建对象实例:
newInstance()方法。
- 获取构造器:
package chapter3;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Test4 {
public static void main(String[] args) throws InvocationTargetException, InstantiationException, IllegalAccessException {
// 创建一个Student对象
Student student = new Student("小美", 20);
// 获取Student类的Class对象
Class<?> clazz = Student.class;
// 获取Student类的所有构造函数
Constructor<?>[] cs = clazz.getDeclaredConstructors();
// 输出构造函数的个数
System.out.println(cs.length);
// 获取第一个构造函数
Constructor<?> c1 = cs[0];
// 输出第一个构造函数的名称
System.out.println(c1.getName());
// 使用第一个构造函数创建一个新的Student对象
Object obj = c1.newInstance("小丽", 18);
// 输出新创建的Student对象的类名
System.out.println(obj.getClass().getSimpleName());
// 判断新创建的Student对象是否是Student类的实例
if(obj instanceof Student student1) {
// 输出新创建的Student对象的名字
System.out.println(student1.getName());
}
}
}
- Method类
描述成员方法的。- 获取方法:
getMethod()、getDeclaredMethod()。 - 调用方法:
invoke()方法。
- 获取方法:
// 获取类的所有方法
Method[] methods = clazz1.getDeclaredMethods();
for (Method method : methods) {
// 打印方法的修饰符
System.out.print(Modifier.toString(method.getModifiers()));
System.out.print(" ");
// 打印方法的返回类型
System.out.print(method.getReturnType().getSimpleName());
System.out.print(" ");
// 打印方法名
System.out.print(method.getName());
System.out.print("(");
// 获取方法的参数列表
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
System.out.print(parameter.getType().getSimpleName());
System.out.print(" ");
System.out.print(parameter.getName());
}
System.out.println(")");
}
- Field类
描述字段的类,描述成员属性。- 获取字段:
getField()、getDeclaredField()。
- 获取字段:
// 获取类的所有属性
Field[] fields = clazz1.getDeclaredFields();
for (Field field : fields) {
System.out.println(field.getName() + " " + field.getType());
}
- Annotation类
描述注解的类。
package chapter3;
import java.lang.annotation.Annotation;
public class Test5 {
public static void main(String[] args) {
// 获取Student类的Class对象
Class<?> clazz = Student.class;
// 获取Student类的所有注解
Annotation[] annotations = clazz.getDeclaredAnnotations();
// 输出注解的数量
System.out.println(annotations.length);
// 获取第一个注解
Annotation a = annotations[0];
// 输出注解的类型
System.out.println(a.annotationType().getName());
// 判断Student类是否包含Test注解
if (clazz.isAnnotationPresent(Test.class)) {
// 将注解转换为Test类型
Test test = (Test) a;
// 输出注解的name属性
System.out.println(test.name());
}
}
}
3. 反射的基本操作与示例
- 步骤1: 获取Class对象
演示如何加载类:例如,使用Class.forName("java.lang.String")。 - 步骤2: 创建对象实例
展示动态创建对象:通过Constructor.newInstance()。- 示例代码:使用反射创建
ArrayList实例。
- 示例代码:使用反射创建
- 步骤3: 调用方法
动态调用方法:使用Method.invoke()。-
package chapter3; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class Test3 { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 创建一个Student对象 Student student = new Student("小美", 20); // 获取Student类的Class对象 Class<?> clazz = Student.class; // 获取Student类中的sayhello方法 Method m = clazz.getDeclaredMethod("sayhello", String.class); // 设置sayhello方法可访问 m.setAccessible(true); // 调用sayhello方法,传入参数"小丽" m.invoke(student, "小丽"); } }
-
- 步骤4: 访问和修改字段
操作字段值:使用Field.get()和Field.set()。- 示例代码:修改对象的私有字段(需处理访问权限)。
-
package chapter3; import java.lang.reflect.Field; public class Test2 { public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException { // 创建一个Student对象 Student student = new Student("小美", 20); // 获取Student类的Class对象 Class<?> clazz = Student.class; // 获取Student类中的name字段 Field f1 = clazz.getDeclaredField("name"); // 输出Student对象的name属性 //System.out.println(student.name); // 设置name字段可访问 f1.setAccessible(true); // 输出Student对象的name属性 System.out.println(f1.get(student)); // 修改Student对象的name属性 f1.set(student, "小丽"); // 输出修改后的Student对象的name属性 System.out.println(f1.get(student)); } }
- 处理异常
讨论常见错误:ClassNotFoundException、NoSuchMethodException等,并提供异常处理建议。
4. 高级反射特性
- 访问私有成员
讲解如何突破访问限制:使用setAccessible(true)。- 应用场景:测试私有方法或字段。
- 动态代理
介绍反射在代理模式中的作用:基于Proxy和InvocationHandler。- 示例:创建动态代理对象处理接口调用。
- 注解处理
结合反射处理注解:使用getAnnotation()方法。- 实际用途:框架中的自定义注解解析。
5. 反射的实际应用场景
- 框架开发
分析反射在流行框架中的应用:如Spring的依赖注入、Hibernate的ORM映射。 - 测试工具
讨论反射在测试中的作用:如JUnit的动态测试用例生成或Mockito的模拟对象。 - 序列化和反序列化
解释反射在数据转换中的角色:如JSON库(Jackson)如何利用反射解析对象。 - 插件系统
展示反射在动态加载类中的应用:例如,IDE插件或模块化系统。
6. 反射的优缺点分析
- 优点
- 灵活性:允许运行时动态决策。
- 扩展性:支持新功能的无缝集成。
- 调试辅助:便于内部类结构的检查。
- 缺点
- 性能开销:反射操作比直接调用慢,需谨慎在高频场景使用。
- 安全风险:可能绕过访问控制,导致安全漏洞。
- 代码可读性:过度使用会使代码难以维护。
7. 最佳实践
- 避免滥用
建议:仅在必要时使用反射,优先考虑接口或设计模式。 - 性能优化
技巧:缓存Class、Method等对象减少开销。 - 安全措施
指导:使用SecurityManager限制反射权限,避免暴露敏感数据。
8. 结论
- 总结价值
重申反射的核心优势:为Java开发提供强大动态能力。 - 注意事项
强调合理使用:平衡灵活性与性能、安全性。
9. 参考资料
- 官方文档:Java Reflection API(Oracle官网)。
- 推荐书籍:《Effective Java》相关章节。
- 在线资源:Stack Overflow常见问题、GitHub开源项目示例。
444

被折叠的 条评论
为什么被折叠?



