Java反射学习指南
一、反射基础概念
1.1 什么是反射?
Java反射(Reflection)允许程序在运行时:
- 获取类的完整结构信息
- 动态创建对象
- 动态调用方法
- 访问和修改字段值
- 处理注解
1.2 核心类
java
// 获取类信息
Class<?> clazz = Class.forName("完整类名");
// 创建对象
Constructor<?> constructor = clazz.getConstructor();
Object obj = constructor.newInstance();
// 调用方法
Method method = clazz.getMethod("方法名", 参数类型.class);
method.invoke(obj, 参数);
// 访问字段
Field field = clazz.getField("字段名");
field.set(obj, 值);
二、详细使用示例
2.1 获取Class对象的三种方式
java
public class ReflectionDemo {
public static void main(String[] args) throws Exception {
// 方式1: 类名.class
Class<String> stringClass = String.class;
// 方式2: 对象.getClass()
String str = "Hello";
Class<? extends String> strClass = str.getClass();
// 方式3: Class.forName()
Class<?> clazz = Class.forName("java.lang.String");
System.out.println(stringClass == strClass); // true
System.out.println(strClass == clazz); // true
}
}
2.2 获取构造方法并创建对象
java
public class Person {
private String name;
private int age;
public Person() {}
public Person(String name) {
this.name = name;
}
private Person(String name, int age) {
this.name = name;
this.age = age;
}
// getter/setter省略
}
public class ConstructorExample {
public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
// 1. 获取所有公共构造方法
Constructor<?>[] publicConstructors = clazz.getConstructors();
// 2. 获取所有构造方法(包括私有)
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
// 3. 获取特定构造方法
Constructor<Person> defaultConstructor = clazz.getConstructor();
Constructor<Person> stringConstructor = clazz.getConstructor(String.class);
// 4. 创建对象
Person p1 = defaultConstructor.newInstance();
Person p2 = stringConstructor.newInstance("张三");
// 5. 访问私有构造方法
Constructor<Person> privateConstructor =
clazz.getDeclaredConstructor(String.class, int.class);
privateConstructor.setAccessible(true); // 设置可访问
Person p3 = privateConstructor.newInstance("李四", 25);
}
}
2.3 获取和调用方法
java
public class MethodExample {
public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
// 获取公共方法(包括继承的)
Method[] publicMethods = clazz.getMethods();
// 获取所有声明的方法(不包括继承的)
Method[] declaredMethods = clazz.getDeclaredMethods();
// 获取特定方法
Method setNameMethod = clazz.getMethod("setName", String.class);
Method getNameMethod = clazz.getMethod("getName");
// 调用方法
setNameMethod.invoke(person, "王五");
String name = (String) getNameMethod.invoke(person);
System.out.println("Name: " + name); // 王五
// 调用私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(person);
}
}
2.4 访问和修改字段
java
public class FieldExample {
public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
Person person = clazz.newInstance();
// 获取公共字段
Field[] publicFields = clazz.getFields();
// 获取所有字段(包括私有)
Field[] allFields = clazz.getDeclaredFields();
// 获取特定字段
Field nameField = clazz.getDeclaredField("name");
Field ageField = clazz.getDeclaredField("age");
// 设置字段可访问(对私有字段)
nameField.setAccessible(true);
ageField.setAccessible(true);
// 设置字段值
nameField.set(person, "赵六");
ageField.set(person, 30);
// 获取字段值
String name = (String) nameField.get(person);
int age = (int) ageField.get(person);
System.out.println("Name: " + name + ", Age: " + age);
}
}
2.5 注解处理
java
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@interface MyAnnotation {
String value() default "";
int version() default 1;
}
@MyAnnotation(value = "测试类", version = 2)
class AnnotatedClass {
@MyAnnotation("测试方法")
public void annotatedMethod() {}
}
public class AnnotationExample {
public static void main(String[] args) throws Exception {
Class<AnnotatedClass> clazz = AnnotatedClass.class;
// 获取类上的注解
MyAnnotation classAnnotation = clazz.getAnnotation(MyAnnotation.class);
if (classAnnotation != null) {
System.out.println("Class annotation value: " + classAnnotation.value());
System.out.println("Class annotation version: " + classAnnotation.version());
}
// 获取方法上的注解
Method method = clazz.getMethod("annotatedMethod");
MyAnnotation methodAnnotation = method.getAnnotation(MyAnnotation.class);
if (methodAnnotation != null) {
System.out.println("Method annotation value: " + methodAnnotation.value());
}
}
}
三、实际应用场景
3.1 动态代理
java
interface Service {
void doSomething();
}
class RealService implements Service {
public void doSomething() {
System.out.println("Real service working...");
}
}
class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before method: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After method: " + method.getName());
return result;
}
}
public class ProxyExample {
public static void main(String[] args) {
RealService realService = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
Service.class.getClassLoader(),
new Class[]{Service.class},
new DynamicProxyHandler(realService)
);
proxy.doSomething();
}
}
3.2 对象拷贝工具
java
public class BeanUtils {
public static <T> T copyProperties(T source, Class<T> targetClass)
throws Exception {
T target = targetClass.newInstance();
Class<?> sourceClass = source.getClass();
Field[] sourceFields = sourceClass.getDeclaredFields();
for (Field sourceField : sourceFields) {
try {
Field targetField = targetClass.getDeclaredField(
sourceField.getName());
sourceField.setAccessible(true);
targetField.setAccessible(true);
Object value = sourceField.get(source);
targetField.set(target, value);
} catch (NoSuchFieldException e) {
// 字段不存在则跳过
}
}
return target;
}
}
四、性能考虑和最佳实践
4.1 性能优化
java
public class ReflectionPerformance {
// 缓存Class对象
private static final Map<String, Class<?>> CLASS_CACHE = new HashMap<>();
// 缓存Method对象
private static final Map<String, Method> METHOD_CACHE = new HashMap<>();
public static Class<?> getCachedClass(String className) throws Exception {
Class<?> clazz = CLASS_CACHE.get(className);
if (clazz == null) {
clazz = Class.forName(className);
CLASS_CACHE.put(className, clazz);
}
return clazz;
}
public static Object invokeCachedMethod(Object obj, String methodName,
Object... args) throws Exception {
String key = obj.getClass().getName() + "." + methodName;
Method method = METHOD_CACHE.get(key);
if (method == null) {
Class<?>[] paramTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
paramTypes[i] = args[i].getClass();
}
method = obj.getClass().getMethod(methodName, paramTypes);
METHOD_CACHE.put(key, method);
}
return method.invoke(obj, args);
}
}
4.2 最佳实践
- 慎用反射:反射会破坏封装性,降低性能
- 使用缓存:缓存Class、Method、Field对象
- 处理异常:反射操作会抛出多种检查异常
- 考虑安全:setAccessible(true)会破坏封装
- 性能测试:在性能敏感场景谨慎使用
五、常见面试题
Q1:反射的优缺点?
优点:
- 动态性:运行时操作
- 灵活性:可操作私有成员
- 框架基础:Spring、Hibernate等框架的核心
缺点:
- 性能开销:比直接调用慢
- 安全性问题:可绕过访问控制
- 维护困难:破坏封装性
Q2:如何提高反射性能?
- 缓存反射对象
- 使用MethodHandle(Java 7+)
- 避免频繁反射调用
Q3:反射能修改final字段吗?
可以,但需要先调用setAccessible(true):
java
Field field = clazz.getDeclaredField("finalField");
field.setAccessible(true);
field.set(obj, newValue);
六、学习资源
- 官方文档:Java Reflection API
- 书籍推荐 :
- 《Java核心技术 卷I》
- 《深入理解Java虚拟机》
- 实践项目 :
- 实现简单的IOC容器
- 写一个对象映射工具
- 实现注解处理器
通过系统学习以上内容,你将掌握Java反射的核心概念和使用技巧,能够在实际开发中合理应用反射技术。