一、反射的概述
反射(Reflection) 是Java语言的一种核心特性,允许程序在运行时(Runtime)动态获取类的元数据(如类名、方法、属性、构造函数等),并能直接操作类或对象的内部属性与方法。通过java.lang.reflect
包中的API,Java程序可以:
-
动态加载类
-
获取类的构造方法、方法、字段等信息
-
调用对象的方法或修改字段值
-
创建对象实例(即使构造方法私有)
反射机制绕过了编译时的静态类型检查,使得代码具备更强的灵活性和动态性,但同时也带来了性能开销和安全风险。
二、Java反射的作用
-
动态加载类:通过类名(字符串形式)加载未在编译时确定的类。
-
获取类结构:解析类的构造方法、方法、字段、注解等元信息。
-
操作私有成员:通过
setAccessible(true)
突破访问权限限制。 -
动态代理:实现AOP(面向切面编程)如Spring的拦截器。
-
框架设计:如Spring的IoC容器通过反射创建和管理Bean。
三、Java反射的优点
-
灵活性高:无需在编码时确定具体类,适合编写通用框架。
-
动态性支持:可在运行时根据条件加载不同类或调用不同方法。
-
突破封装:访问和修改私有成员,用于测试、调试等场景。
四、获取class的三种方式
-
类名.class语法
Class<?> clazz = String.class;
-
对象.getClass()方法
String str = "Hello"; Class<?> clazz = str.getClass();
-
Class.forName()动态加载
Class<?> clazz = Class.forName("java.lang.String");
五、Java反射机制的应用场景
-
框架开发:如Spring通过反射实现依赖注入。
-
动态代理:如JDK动态代理生成接口实现类。
-
注解处理器:解析运行时注解(如JUnit的
@Test
)。 -
序列化/反序列化:Jackson/GSON通过反射读取对象字段。
-
单元测试:Mock框架(如Mockito)模拟私有方法。
六、是否可以通过反射去获取一个接口的实例化对象,为什么?是否可以通过反射去获取一个抽象的实例化对象,为什么?
不能通过反射直接实例化接口,因为接口没有构造方法。尝试调用newInstance()
会抛出InstantiationException
。需通过动态代理生成代理对象。
// 错误示例
Class<?> clazz = Runnable.class;
Runnable instance = clazz.newInstance(); // 抛出异常!
不能直接实例化抽象类,必须提供具体子类的Class对象。反射仅能创建已实现所有抽象方法的子类实例。
七、通过New创建的对象和通过反射获取的对象有什么不同
特性 | New操作符 | 反射(Constructor.newInstance()) |
---|---|---|
编译时检查 | 必须明确类名,编译时验证 | 类名可为字符串,运行时可能抛出异常 |
性能 | 高效,JVM直接优化 | 较慢,需解析类信息并安全检查 |
访问权限 | 受限于访问修饰符(如私有构造器不可用) | 可调用setAccessible(true) 绕过权限控制 |
灵活性 | 静态绑定,代码耦合度高 | 动态绑定,适合解耦和扩展 |