反射是Java的强大特性,允许程序在运行时检查和操作类、方法、字段。Spring、MyBatis等框架的核心都依赖反射。本文系统介绍反射的用法和原理。
什么是反射
反射(Reflection)允许程序在运行时:
- 获取任意类的信息(类名、方法、字段、注解等)
- 创建任意类的实例
- 调用任意方法(包括私有方法)
- 访问和修改任意字段(包括私有字段)
1 2 3 4 5 6 7 8 9
| Person p = new Person(); p.setName("Tom");
Class<?> clazz = Class.forName("com.example.Person"); Object obj = clazz.getDeclaredConstructor().newInstance(); Method method = clazz.getMethod("setName", String.class); method.invoke(obj, "Tom");
|
获取Class对象
反射的入口是 Class 对象,有三种获取方式:
1 2 3 4 5 6 7 8 9 10 11 12 13
| Class<Person> clazz1 = Person.class;
Person p = new Person(); Class<?> clazz2 = p.getClass();
Class<?> clazz3 = Class.forName("com.example.Person");
System.out.println(clazz1 == clazz2); System.out.println(clazz2 == clazz3);
|
获取类信息
基本信息
1 2 3 4 5 6 7 8
| Class<?> clazz = Person.class;
clazz.getName(); clazz.getSimpleName(); clazz.getPackageName(); clazz.getSuperclass(); clazz.getInterfaces(); clazz.getModifiers();
|
获取构造函数
1 2 3 4 5 6 7 8
| Constructor<?>[] constructors = clazz.getConstructors();
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
|
获取方法
1 2 3 4 5 6 7 8
| Method[] methods = clazz.getMethods();
Method[] declaredMethods = clazz.getDeclaredMethods();
Method method = clazz.getDeclaredMethod("setName", String.class);
|
获取字段
1 2 3 4 5 6 7 8
| Field[] fields = clazz.getFields();
Field[] declaredFields = clazz.getDeclaredFields();
Field field = clazz.getDeclaredField("name");
|
创建实例
1 2 3 4 5 6 7 8 9 10 11 12
| Class<?> clazz = Class.forName("com.example.Person");
Object obj1 = clazz.getDeclaredConstructor().newInstance();
Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class); Object obj2 = constructor.newInstance("Tom", 25);
constructor.setAccessible(true); Object obj3 = constructor.newInstance("Jerry", 30);
|
调用方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| Class<?> clazz = Person.class; Object person = clazz.getDeclaredConstructor().newInstance();
Method setName = clazz.getMethod("setName", String.class); setName.invoke(person, "Tom");
Method privateMethod = clazz.getDeclaredMethod("secretMethod"); privateMethod.setAccessible(true); Object result = privateMethod.invoke(person);
Method staticMethod = clazz.getMethod("staticMethod"); staticMethod.invoke(null);
|
访问和修改字段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| Class<?> clazz = Person.class; Object person = clazz.getDeclaredConstructor().newInstance();
Field nameField = clazz.getDeclaredField("name"); nameField.setAccessible(true);
String name = (String) nameField.get(person);
nameField.set(person, "Jerry");
Field finalField = clazz.getDeclaredField("id"); finalField.setAccessible(true); finalField.set(person, 100);
|
处理泛型
1 2 3 4 5 6 7 8 9 10 11 12 13
| class Container<T> { private List<String> names; }
Field field = Container.class.getDeclaredField("names"); Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) { ParameterizedType pt = (ParameterizedType) genericType; System.out.println(pt.getRawType()); System.out.println(pt.getActualTypeArguments()[0]); }
|
处理注解
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface MyAnnotation { String value(); }
class Person { @MyAnnotation("test") private String name; }
Field field = Person.class.getDeclaredField("name"); MyAnnotation annotation = field.getAnnotation(MyAnnotation.class); System.out.println(annotation.value());
boolean hasAnnotation = field.isAnnotationPresent(MyAnnotation.class);
|
动态代理
反射的重要应用是创建动态代理:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| interface UserService { void save(String name); }
UserService proxy = (UserService) Proxy.newProxyInstance( UserService.class.getClassLoader(), new Class[]{UserService.class}, (proxyObj, method, args) -> { System.out.println("Before: " + method.getName()); System.out.println("After: " + method.getName()); return null; } );
proxy.save("Tom");
|
输出:
1 2
| Before: save After: save
|
反射的应用场景
1. 框架开发
1 2 3 4 5 6 7 8 9 10
| public void inject(Object bean) throws Exception { for (Field field : bean.getClass().getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { field.setAccessible(true); Object dependency = getBean(field.getType()); field.set(bean, dependency); } } }
|
2. ORM映射
1 2 3 4 5 6 7 8 9 10
| public <T> T mapRow(ResultSet rs, Class<T> clazz) throws Exception { T obj = clazz.getDeclaredConstructor().newInstance(); for (Field field : clazz.getDeclaredFields()) { field.setAccessible(true); Object value = rs.getObject(field.getName()); field.set(obj, value); } return obj; }
|
3. 序列化/反序列化
1 2 3 4 5 6 7 8 9 10 11 12
| public String toJson(Object obj) throws Exception { StringBuilder sb = new StringBuilder("{"); Field[] fields = obj.getClass().getDeclaredFields(); for (int i = 0; i < fields.length; i++) { fields[i].setAccessible(true); sb.append("\"").append(fields[i].getName()).append("\":"); sb.append("\"").append(fields[i].get(obj)).append("\""); if (i < fields.length - 1) sb.append(","); } return sb.append("}").toString(); }
|
反射的性能问题
反射比直接调用慢,主要原因:
- 无法内联优化:JIT编译器无法优化反射调用
- 安全检查:每次调用都要进行访问权限检查
- 参数装箱:
invoke() 的参数是 Object[],基本类型需要装箱
性能优化建议
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| private static final Method cachedMethod; static { cachedMethod = Person.class.getMethod("getName"); cachedMethod.setAccessible(true); }
MethodHandles.Lookup lookup = MethodHandles.lookup(); MethodHandle handle = lookup.findVirtual(Person.class, "getName", MethodType.methodType(String.class)); String name = (String) handle.invoke(person);
|
反射的限制
| 限制 |
说明 |
| 性能开销 |
比直接调用慢10-100倍 |
| 安全限制 |
Java 9+模块系统限制了跨模块反射 |
| 类型安全 |
编译器无法检查,运行时才报错 |
| 代码可读性 |
反射代码难以理解和维护 |
总结
| API |
用途 |
Class.forName() |
根据类名获取Class对象 |
clazz.getDeclaredConstructor() |
获取构造函数 |
clazz.getDeclaredMethod() |
获取方法 |
clazz.getDeclaredField() |
获取字段 |
setAccessible(true) |
突破private限制 |
constructor.newInstance() |
创建实例 |
method.invoke() |
调用方法 |
field.get() / set() |
读写字段 |
反射是把双刃剑:
- 优点:极大的灵活性,是框架的基石
- 缺点:性能差、破坏封装、类型不安全
使用原则:框架可以用,业务代码尽量避免。