Java反射机制详解与使用场景
反射(Reflection)是 Java 的强大特性之一,它允许程序在运行时检查和操作类、方法、字段等。本文从基础用法到高级应用,全面解析反射机制。
什么是反射
反射是指程序在运行期间可以获取任意类的内部信息,并直接操作任意对象的内部属性和方法。
Class<?> clazz = Class.forName("java.util.ArrayList"); Object instance = clazz.newInstance();
|
获取 Class 对象的三种方式
Class<String> clazz1 = String.class;
String str = "hello"; Class<?> clazz2 = str.getClass();
Class<?> clazz3 = Class.forName("java.lang.String");
|
反射的核心操作
创建实例
Class<?> clazz = Class.forName("com.example.User");
Object obj = clazz.newInstance();
Constructor<?> constructor = clazz.getDeclaredConstructor(); Object obj2 = constructor.newInstance();
Constructor<?> paramCtor = clazz.getDeclaredConstructor(String.class, int.class); Object obj3 = paramCtor.newInstance("张三", 25);
|
操作方法
Method method = clazz.getDeclaredMethod("sayHello", String.class); method.setAccessible(true); Object result = method.invoke(obj, "世界");
|
操作字段
Field field = clazz.getDeclaredField("name"); field.setAccessible(true); field.set(obj, "李四"); Object value = field.get(obj);
|
反射在框架中的应用
Spring 的依赖注入
@Component public class UserService { @Autowired private UserDao userDao; }
|
Spring 通过反射扫描 @Autowired 注解,找到对应类型的 Bean 并注入。
MyBatis 的结果集映射
List<User> users = sqlSession.selectList("select * from user");
|
JUnit 单元测试
public class CalculatorTest { @Test public void testAdd() { } }
|
反射的性能问题
反射比直接调用慢 10~100 倍,主要原因:
- 类型检查绕过编译期优化
- 方法访问需要权限检查
- 无法内联优化
优化方案
1. setAccessible(true)
Method method = clazz.getDeclaredMethod("privateMethod"); method.setAccessible(true);
|
2. 反射结果缓存
public class ReflectionCache { private static final Map<String, Method> METHOD_CACHE = new ConcurrentHashMap<>(); public static Method getMethod(Class<?> clazz, String name, Class<?>... params) { String key = clazz.getName() + "." + name; return METHOD_CACHE.computeIfAbsent(key, k -> { try { Method m = clazz.getDeclaredMethod(name, params); m.setAccessible(true); return m; } catch (NoSuchMethodException e) { throw new RuntimeException(e); } }); } }
|
3. 使用 MethodHandle(JDK 7+)
MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.findVirtual(String.class, "length", MethodType.methodType(int.class)); int len = (int) handle.invokeExact("hello");
|
4. 使用 LambdaMetafactory(JDK 8+)
MethodHandles.Lookup lookup = MethodHandles.lookup(); CallSite site = LambdaMetafactory.metafactory( lookup, "apply", MethodType.methodType(Function.class), MethodType.methodType(Object.class, Object.class), handle, MethodType.methodType(Integer.class, String.class) );
|
反射的安全限制
SecurityManager
System.setSecurityManager(new SecurityManager() { @Override public void checkPackageAccess(String pkg) { if (pkg.startsWith("java.lang.reflect")) { throw new SecurityException("Reflection not allowed"); } } });
|
JDK 9+ 模块化限制
module myapp { opens com.example.internal to spring.core; }
|
实际开发建议
- 业务代码中尽量避免反射:影响可读性和性能
- 框架开发中合理使用:Spring、MyBatis 等底层实现
- 做好异常处理:反射操作抛出大量 Checked Exception
- 注意访问权限:setAccessible 可能受 SecurityManager 限制
- 缓存反射结果:避免重复获取 Method/Field
总结
反射是 Java 的”双刃剑”:
- 优点:极高的灵活性,是框架开发的基石
- 缺点:性能开销大,破坏封装性,代码可读性差
理解反射的原理和适用场景,才能在正确的场合使用这一强大工具。