Java反射机制完全指南
Java反射(Reflection)是Java语言最强大的特性之一,它允许程序在运行时检查和操作类、接口、字段和方法的信息。反射机制为Java提供了强大的动态编程能力,是Spring、Hibernate等主流框架的核心技术基础。
反射 = 运行时自省 + 动态调用 + 框架基础 + 元编程能力
- 🔍 自省机制:运行时检查类型信息,实现动态特性
- 🔄 动态调用:运行时创建对象、调用方法,提供极大灵活性
- 🏗️ 框架基础:支持依赖注入、序列化等底层框架能力
- 🛠️ 工具开发:编写各类开发工具,如调试器、代码生成器
- 🔌 扩展性设计:构建可配置、可扩展的应用架构
1. 反射基础概念与原理
1.1 反射机制深度解析
反射是指程序可以访问、检测和修改它本身状态或行为的一种能力。在Java中,反射主要通过java.lang.reflect包实现。
反射核心类对比表
| 核心类 | 作用 | 主要方法 | 使用场景 | 性能影响 |
|---|---|---|---|---|
| Class | 类信息表示 | forName(), newInstance() | 类加载、实例创建 | 中等 |
| Field | 字段操作 | get(), set(), setAccessible() | 属性注入、序列化 | 较高 |
| Method | 方法调用 | invoke(), getParameterTypes() | 动态方法调用 | 高 |
| Constructor | 构造器操作 | newInstance(), getParameters() | 对象创建 | 中等 |
| Parameter | 参数信息 | getName(), getType() | 参数解析 | 低 |
- Class对象获取
- 类信息获取
- 性能考虑
获取Class对象的多种方式
1public class ClassObjectDemo {2 public static void main(String[] args) throws ClassNotFoundException {3 // 方式1:通过类名.class获取(编译时确定)4 Class<String> clazz1 = String.class;5 System.out.println("方式1: " + clazz1.getName());6 7 // 方式2:通过对象.getClass()获取(运行时确定)8 String str = "Hello";9 Class<?> clazz2 = str.getClass();10 System.out.println("方式2: " + clazz2.getName());11 12 // 方式3:通过Class.forName()获取(动态加载)13 Class<?> clazz3 = Class.forName("java.lang.String");14 System.out.println("方式3: " + clazz3.getName());15 16 // 方式4:通过类加载器获取17 ClassLoader classLoader = Thread.currentThread().getContextClassLoader();18 Class<?> clazz4 = classLoader.loadClass("java.lang.String");19 System.out.println("方式4: " + clazz4.getName());20 21 // 验证是否为同一个Class对象22 System.out.println("是否为同一个Class对象: " + (clazz1 == clazz2));23 System.out.println("是否为同一个Class对象: " + (clazz2 == clazz3));24 }25}Class对象获取方式对比
| 获取方式 | 语法 | 使用场景 | 性能 | 异常处理 |
|---|---|---|---|---|
| 类名.class | String.class | 编译时已知类型 | 最快 | 无异常 |
| 对象.getClass() | obj.getClass() | 运行时获取对象类型 | 快 | 无异常 |
| Class.forName() | Class.forName("类名") | 动态加载类 | 较慢 | ClassNotFoundException |
| 类加载器 | classLoader.loadClass() | 自定义类加载 | 较慢 | ClassNotFoundException |
获取类的详细信息
1import java.lang.reflect.*;2import java.util.Arrays;34public class ClassInfoDemo {5 public static void analyzeClass(Class<?> clazz) {6 System.out.println("=== 分析类: " + clazz.getName() + " ===");7 8 // 1. 基本信息9 System.out.println("简单名称: " + clazz.getSimpleName());10 System.out.println("包名: " + clazz.getPackage().getName());11 System.out.println("是否为接口: " + clazz.isInterface());12 System.out.println("是否为抽象类: " + Modifier.isAbstract(clazz.getModifiers()));13 System.out.println("是否为final类: " + Modifier.isFinal(clazz.getModifiers()));14 15 // 2. 继承关系16 Class<?> superClass = clazz.getSuperclass();17 if (superClass != null) {18 System.out.println("父类: " + superClass.getName());19 }20 21 // 3. 实现的接口22 Class<?>[] interfaces = clazz.getInterfaces();23 if (interfaces.length > 0) {24 System.out.println("实现的接口: " + Arrays.toString(interfaces));25 }26 27 // 4. 泛型信息28 TypeVariable<?>[] typeParameters = clazz.getTypeParameters();29 if (typeParameters.length > 0) {30 System.out.println("泛型参数: " + Arrays.toString(typeParameters));31 }32 33 // 5. 注解信息34 Annotation[] annotations = clazz.getAnnotations();35 if (annotations.length > 0) {36 System.out.println("类注解: " + Arrays.toString(annotations));37 }38 39 // 6. 内部类40 Class<?>[] innerClasses = clazz.getDeclaredClasses();41 if (innerClasses.length > 0) {42 System.out.println("内部类: " + Arrays.toString(innerClasses));43 }44 45 System.out.println();46 }47 48 public static void main(String[] args) {49 // 分析不同类型的类50 analyzeClass(String.class);51 analyzeClass(java.util.ArrayList.class);52 analyzeClass(java.util.List.class);53 analyzeClass(Thread.class);54 }55}反射性能优化策略
1import java.lang.reflect.*;2import java.util.concurrent.ConcurrentHashMap;34public class ReflectionPerformanceOptimizer {5 // 缓存Class对象6 private static final ConcurrentHashMap<String, Class<?>> CLASS_CACHE = new ConcurrentHashMap<>();7 8 // 缓存Method对象9 private static final ConcurrentHashMap<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();10 11 // 缓存Field对象12 private static final ConcurrentHashMap<String, Field> FIELD_CACHE = new ConcurrentHashMap<>();13 14 /**15 * 优化的Class获取方法16 */17 public static Class<?> getClassOptimized(String className) throws ClassNotFoundException {18 return CLASS_CACHE.computeIfAbsent(className, key -> {19 try {20 return Class.forName(key);21 } catch (ClassNotFoundException e) {22 throw new RuntimeException(e);23 }24 });25 }26 27 /**28 * 优化的Method获取方法29 */30 public static Method getMethodOptimized(Class<?> clazz, String methodName, Class<?>... parameterTypes) 31 throws NoSuchMethodException {32 String key = clazz.getName() + "#" + methodName + "#" + Arrays.toString(parameterTypes);33 return METHOD_CACHE.computeIfAbsent(key, k -> {34 try {35 Method method = clazz.getDeclaredMethod(methodName, parameterTypes);36 method.setAccessible(true); // 预先设置访问权限37 return method;38 } catch (NoSuchMethodException e) {39 throw new RuntimeException(e);40 }41 });42 }43 44 /**45 * 优化的Field获取方法46 */47 public static Field getFieldOptimized(Class<?> clazz, String fieldName) throws NoSuchFieldException {48 String key = clazz.getName() + "#" + fieldName;49 return FIELD_CACHE.computeIfAbsent(key, k -> {50 try {51 Field field = clazz.getDeclaredField(fieldName);52 field.setAccessible(true); // 预先设置访问权限53 return field;54 } catch (NoSuchFieldException e) {55 throw new RuntimeException(e);56 }57 });58 }59 60 /**61 * 性能测试对比62 */63 public static void performanceTest() throws Exception {64 int iterations = 100000;65 Class<?> clazz = String.class;66 67 // 测试直接调用 vs 反射调用68 String testStr = "Hello World";69 70 // 直接调用性能测试71 long startTime = System.nanoTime();72 for (int i = 0; i < iterations; i++) {73 testStr.length();74 }75 long directCallTime = System.nanoTime() - startTime;76 77 // 反射调用性能测试(未优化)78 Method lengthMethod = clazz.getMethod("length");79 startTime = System.nanoTime();80 for (int i = 0; i < iterations; i++) {81 lengthMethod.invoke(testStr);82 }83 long reflectionCallTime = System.nanoTime() - startTime;84 85 // 反射调用性能测试(优化后)86 Method cachedMethod = getMethodOptimized(clazz, "length");87 startTime = System.nanoTime();88 for (int i = 0; i < iterations; i++) {89 cachedMethod.invoke(testStr);90 }91 long optimizedReflectionTime = System.nanoTime() - startTime;92 93 System.out.println("性能测试结果 (" + iterations + " 次调用):");94 System.out.println("直接调用: " + directCallTime / 1_000_000 + " ms");95 System.out.println("反射调用(未优化): " + reflectionCallTime / 1_000_000 + " ms");96 System.out.println("反射调用(优化后): " + optimizedReflectionTime / 1_000_000 + " ms");97 System.out.println("反射性能损失: " + (reflectionCallTime / (double) directCallTime) + "x");98 System.out.println("优化后性能损失: " + (optimizedReflectionTime / (double) directCallTime) + "x");99 }100 101 public static void main(String[] args) throws Exception {102 performanceTest();103 }104}- 缓存反射对象:Method、Field、Constructor对象应该缓存复用
- 预设访问权限:提前调用
setAccessible(true)避免重复检查 - 避免频繁反射:在性能敏感的代码路径中谨慎使用反射
- 使用MethodHandle:Java 7+可以考虑使用MethodHandle替代反射
2. 字段(Field)操作详解
字段反射允许我们在运行时访问和修改对象的字段值,包括私有字段。
- 字段访问操作
- 字段工具类
- 字段注解处理
字段访问与操作
1import java.lang.reflect.*;23// 示例类4class Person {5 private String name;6 protected int age;7 public String email;8 private static final String SPECIES = "Homo sapiens";9 10 public Person(String name, int age, String email) {11 this.name = name;12 this.age = age;13 this.email = email;14 }15 16 @Override17 public String toString() {18 return String.format("Person{name='%s', age=%d, email='%s'}", name, age, email);19 }20}2122public class FieldReflectionDemo {23 public static void main(String[] args) throws Exception {24 Person person = new Person("张三", 25, "zhangsan@example.com");25 Class<?> personClass = person.getClass();26 27 System.out.println("原始对象: " + person);28 29 // 1. 获取所有字段(包括私有字段)30 System.out.println("\n=== 所有声明的字段 ===");31 Field[] declaredFields = personClass.getDeclaredFields();32 for (Field field : declaredFields) {33 System.out.printf("字段: %s, 类型: %s, 修饰符: %s%n", 34 field.getName(), 35 field.getType().getSimpleName(),36 Modifier.toString(field.getModifiers())37 );38 }39 40 // 2. 获取公共字段41 System.out.println("\n=== 公共字段 ===");42 Field[] publicFields = personClass.getFields();43 for (Field field : publicFields) {44 System.out.println("公共字段: " + field.getName());45 }46 47 // 3. 访问私有字段48 System.out.println("\n=== 访问私有字段 ===");49 Field nameField = personClass.getDeclaredField("name");50 nameField.setAccessible(true); // 突破访问控制51 52 String originalName = (String) nameField.get(person);53 System.out.println("原始姓名: " + originalName);54 55 // 修改私有字段值56 nameField.set(person, "李四");57 System.out.println("修改后对象: " + person);58 59 // 4. 访问静态字段60 System.out.println("\n=== 访问静态字段 ===");61 Field speciesField = personClass.getDeclaredField("SPECIES");62 speciesField.setAccessible(true);63 String species = (String) speciesField.get(null); // 静态字段传null64 System.out.println("物种: " + species);65 66 // 5. 字段类型判断67 System.out.println("\n=== 字段类型判断 ===");68 for (Field field : declaredFields) {69 System.out.printf("字段 %s: ", field.getName());70 System.out.printf("是否静态=%s, ", Modifier.isStatic(field.getModifiers()));71 System.out.printf("是否final=%s, ", Modifier.isFinal(field.getModifiers()));72 System.out.printf("是否私有=%s%n", Modifier.isPrivate(field.getModifiers()));73 }74 }75}通用字段操作工具类
1import java.lang.reflect.*;2import java.util.*;34public class FieldUtils {5 6 /**7 * 获取对象的所有字段值(包括继承的字段)8 */9 public static Map<String, Object> getAllFieldValues(Object obj) {10 Map<String, Object> fieldValues = new HashMap<>();11 Class<?> clazz = obj.getClass();12 13 // 遍历类层次结构14 while (clazz != null && clazz != Object.class) {15 Field[] fields = clazz.getDeclaredFields();16 for (Field field : fields) {17 if (!Modifier.isStatic(field.getModifiers())) {18 try {19 field.setAccessible(true);20 Object value = field.get(obj);21 fieldValues.put(field.getName(), value);22 } catch (IllegalAccessException e) {23 System.err.println("无法访问字段: " + field.getName());24 }25 }26 }27 clazz = clazz.getSuperclass();28 }29 30 return fieldValues;31 }32 33 /**34 * 复制对象字段值35 */36 public static void copyFields(Object source, Object target) {37 Class<?> sourceClass = source.getClass();38 Class<?> targetClass = target.getClass();39 40 Field[] sourceFields = sourceClass.getDeclaredFields();41 42 for (Field sourceField : sourceFields) {43 if (Modifier.isStatic(sourceField.getModifiers()) || 44 Modifier.isFinal(sourceField.getModifiers())) {45 continue;46 }47 48 try {49 Field targetField = targetClass.getDeclaredField(sourceField.getName());50 51 // 检查类型兼容性52 if (targetField.getType().isAssignableFrom(sourceField.getType())) {53 sourceField.setAccessible(true);54 targetField.setAccessible(true);55 56 Object value = sourceField.get(source);57 targetField.set(target, value);58 59 System.out.println("复制字段: " + sourceField.getName() + " = " + value);60 }61 } catch (NoSuchFieldException e) {62 System.out.println("目标对象没有字段: " + sourceField.getName());63 } catch (IllegalAccessException e) {64 System.err.println("无法访问字段: " + sourceField.getName());65 }66 }67 }68 69 /**70 * 将对象转换为Map71 */72 public static Map<String, Object> objectToMap(Object obj) {73 Map<String, Object> map = new HashMap<>();74 Class<?> clazz = obj.getClass();75 76 Field[] fields = clazz.getDeclaredFields();77 for (Field field : fields) {78 if (!Modifier.isStatic(field.getModifiers())) {79 try {80 field.setAccessible(true);81 Object value = field.get(obj);82 map.put(field.getName(), value);83 } catch (IllegalAccessException e) {84 System.err.println("无法访问字段: " + field.getName());85 }86 }87 }88 89 return map;90 }91 92 /**93 * 从Map创建对象94 */95 public static <T> T mapToObject(Map<String, Object> map, Class<T> clazz) throws Exception {96 T instance = clazz.getDeclaredConstructor().newInstance();97 98 for (Map.Entry<String, Object> entry : map.entrySet()) {99 try {100 Field field = clazz.getDeclaredField(entry.getKey());101 field.setAccessible(true);102 field.set(instance, entry.getValue());103 } catch (NoSuchFieldException e) {104 System.out.println("字段不存在: " + entry.getKey());105 }106 }107 108 return instance;109 }110 111 /**112 * 查找带有特定注解的字段113 */114 public static List<Field> findFieldsWithAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {115 List<Field> annotatedFields = new ArrayList<>();116 117 Field[] fields = clazz.getDeclaredFields();118 for (Field field : fields) {119 if (field.isAnnotationPresent(annotationClass)) {120 annotatedFields.add(field);121 }122 }123 124 return annotatedFields;125 }126 127 // 使用示例128 public static void main(String[] args) throws Exception {129 Person person1 = new Person("张三", 25, "zhangsan@example.com");130 Person person2 = new Person("", 0, "");131 132 System.out.println("=== 获取所有字段值 ===");133 Map<String, Object> fieldValues = getAllFieldValues(person1);134 fieldValues.forEach((name, value) -> System.out.println(name + " = " + value));135 136 System.out.println("\n=== 复制字段值 ===");137 System.out.println("复制前 person2: " + person2);138 copyFields(person1, person2);139 System.out.println("复制后 person2: " + person2);140 141 System.out.println("\n=== 对象转Map ===");142 Map<String, Object> personMap = objectToMap(person1);143 System.out.println("Person Map: " + personMap);144 145 System.out.println("\n=== Map转对象 ===");146 Person person3 = mapToObject(personMap, Person.class);147 System.out.println("从Map创建的对象: " + person3);148 }149}字段注解处理
1import java.lang.annotation.*;2import java.lang.reflect.*;34// 自定义注解5@Retention(RetentionPolicy.RUNTIME)6@Target(ElementType.FIELD)7@interface Validate {8 String message() default "验证失败";9 int min() default 0;10 int max() default Integer.MAX_VALUE;11 boolean required() default false;12}1314@Retention(RetentionPolicy.RUNTIME)15@Target(ElementType.FIELD)16@interface JsonProperty {17 String value() default "";18}1920// 使用注解的示例类21class User {22 @Validate(required = true, message = "用户名不能为空")23 @JsonProperty("username")24 private String name;25 26 @Validate(min = 18, max = 100, message = "年龄必须在18-100之间")27 private int age;28 29 @Validate(required = true, message = "邮箱不能为空")30 @JsonProperty("email_address")31 private String email;32 33 public User(String name, int age, String email) {34 this.name = name;35 this.age = age;36 this.email = email;37 }38 39 // getters and setters...40}4142public class FieldAnnotationProcessor {43 44 /**45 * 验证对象字段46 */47 public static boolean validateObject(Object obj) {48 Class<?> clazz = obj.getClass();49 Field[] fields = clazz.getDeclaredFields();50 boolean isValid = true;51 52 for (Field field : fields) {53 if (field.isAnnotationPresent(Validate.class)) {54 Validate validate = field.getAnnotation(Validate.class);55 56 try {57 field.setAccessible(true);58 Object value = field.get(obj);59 60 // 检查必填项61 if (validate.required() && (value == null || value.toString().trim().isEmpty())) {62 System.err.println("验证失败 - " + field.getName() + ": " + validate.message());63 isValid = false;64 continue;65 }66 67 // 检查数值范围68 if (value instanceof Integer) {69 int intValue = (Integer) value;70 if (intValue < validate.min() || intValue > validate.max()) {71 System.err.println("验证失败 - " + field.getName() + ": " + validate.message());72 isValid = false;73 }74 }75 76 } catch (IllegalAccessException e) {77 System.err.println("无法访问字段: " + field.getName());78 isValid = false;79 }80 }81 }82 83 return isValid;84 }85 86 /**87 * 将对象转换为JSON格式的Map(基于注解)88 */89 public static Map<String, Object> toJsonMap(Object obj) {90 Map<String, Object> jsonMap = new HashMap<>();91 Class<?> clazz = obj.getClass();92 Field[] fields = clazz.getDeclaredFields();93 94 for (Field field : fields) {95 try {96 field.setAccessible(true);97 Object value = field.get(obj);98 99 String jsonKey = field.getName();100 if (field.isAnnotationPresent(JsonProperty.class)) {101 JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class);102 if (!jsonProperty.value().isEmpty()) {103 jsonKey = jsonProperty.value();104 }105 }106 107 jsonMap.put(jsonKey, value);108 109 } catch (IllegalAccessException e) {110 System.err.println("无法访问字段: " + field.getName());111 }112 }113 114 return jsonMap;115 }116 117 public static void main(String[] args) {118 // 测试验证功能119 User validUser = new User("张三", 25, "zhangsan@example.com");120 User invalidUser = new User("", 15, "");121 122 System.out.println("=== 验证有效用户 ===");123 boolean isValid1 = validateObject(validUser);124 System.out.println("验证结果: " + (isValid1 ? "通过" : "失败"));125 126 System.out.println("\n=== 验证无效用户 ===");127 boolean isValid2 = validateObject(invalidUser);128 System.out.println("验证结果: " + (isValid2 ? "通过" : "失败"));129 130 System.out.println("\n=== JSON转换 ===");131 Map<String, Object> jsonMap = toJsonMap(validUser);132 System.out.println("JSON Map: " + jsonMap);133 }134}3. 方法(Method)操作详解
方法反射是反射机制中最复杂也是最强大的部分,它允许我们在运行时动态调用对象的方法。
- 方法调用
- 方法工具类
- 方法拦截器
方法调用与参数处理
1import java.lang.reflect.*;2import java.util.*;34// 示例服务类5class CalculatorService {6 public int add(int a, int b) {7 System.out.println("执行加法: " + a + " + " + b);8 return a + b;9 }10 11 public double multiply(double a, double b) {12 System.out.println("执行乘法: " + a + " * " + b);13 return a * b;14 }15 16 private String formatResult(String operation, Object result) {17 return String.format("运算结果: %s = %s", operation, result);18 }19 20 public static String getServiceInfo() {21 return "计算器服务 v1.0";22 }23 24 // 重载方法25 public String process(String input) {26 return "处理字符串: " + input;27 }28 29 public String process(int input) {30 return "处理整数: " + input;31 }32 33 // 可变参数方法34 public int sum(int... numbers) {35 int total = 0;36 for (int num : numbers) {37 total += num;38 }39 return total;40 }41}4243public class MethodReflectionDemo {44 public static void main(String[] args) throws Exception {45 CalculatorService calculator = new CalculatorService();46 Class<?> calculatorClass = calculator.getClass();47 48 // 1. 调用公共方法49 System.out.println("=== 调用公共方法 ===");50 Method addMethod = calculatorClass.getMethod("add", int.class, int.class);51 Object result1 = addMethod.invoke(calculator, 10, 20);52 System.out.println("反射调用结果: " + result1);53 54 // 2. 调用私有方法55 System.out.println("\n=== 调用私有方法 ===");56 Method formatMethod = calculatorClass.getDeclaredMethod("formatResult", String.class, Object.class);57 formatMethod.setAccessible(true);58 Object result2 = formatMethod.invoke(calculator, "10 + 20", 30);59 System.out.println("私有方法结果: " + result2);60 61 // 3. 调用静态方法62 System.out.println("\n=== 调用静态方法 ===");63 Method staticMethod = calculatorClass.getMethod("getServiceInfo");64 Object result3 = staticMethod.invoke(null); // 静态方法传null65 System.out.println("静态方法结果: " + result3);66 67 // 4. 处理重载方法68 System.out.println("\n=== 处理重载方法 ===");69 Method processString = calculatorClass.getMethod("process", String.class);70 Method processInt = calculatorClass.getMethod("process", int.class);71 72 Object result4 = processString.invoke(calculator, "Hello");73 Object result5 = processInt.invoke(calculator, 42);74 System.out.println("重载方法结果1: " + result4);75 System.out.println("重载方法结果2: " + result5);76 77 // 5. 处理可变参数方法78 System.out.println("\n=== 处理可变参数方法 ===");79 Method sumMethod = calculatorClass.getMethod("sum", int[].class);80 Object result6 = sumMethod.invoke(calculator, new int[]{1, 2, 3, 4, 5});81 System.out.println("可变参数方法结果: " + result6);82 83 // 6. 获取方法信息84 System.out.println("\n=== 方法信息分析 ===");85 analyzeMethod(addMethod);86 analyzeMethod(formatMethod);87 }88 89 private static void analyzeMethod(Method method) {90 System.out.println("方法名: " + method.getName());91 System.out.println("返回类型: " + method.getReturnType().getSimpleName());92 System.out.println("参数类型: " + Arrays.toString(method.getParameterTypes()));93 System.out.println("修饰符: " + Modifier.toString(method.getModifiers()));94 System.out.println("是否可变参数: " + method.isVarArgs());95 System.out.println("异常类型: " + Arrays.toString(method.getExceptionTypes()));96 System.out.println("---");97 }98}通用方法操作工具类
1import java.lang.reflect.*;2import java.util.*;3import java.util.concurrent.ConcurrentHashMap;45public class MethodUtils {6 // 方法缓存7 private static final Map<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();8 9 /**10 * 安全调用方法(带异常处理)11 */12 public static Object safeInvoke(Object target, String methodName, Object... args) {13 try {14 Class<?> clazz = target.getClass();15 Class<?>[] paramTypes = getParameterTypes(args);16 17 Method method = findMethod(clazz, methodName, paramTypes);18 if (method == null) {19 throw new NoSuchMethodException("找不到方法: " + methodName);20 }21 22 method.setAccessible(true);23 return method.invoke(target, args);24 25 } catch (Exception e) {26 System.err.println("方法调用失败: " + methodName + " - " + e.getMessage());27 return null;28 }29 }30 31 /**32 * 查找方法(支持继承和重载)33 */34 public static Method findMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {35 String cacheKey = clazz.getName() + "#" + methodName + "#" + Arrays.toString(paramTypes);36 37 return METHOD_CACHE.computeIfAbsent(cacheKey, key -> {38 // 首先尝试精确匹配39 try {40 return clazz.getDeclaredMethod(methodName, paramTypes);41 } catch (NoSuchMethodException e) {42 // 尝试在父类中查找43 return findMethodInHierarchy(clazz, methodName, paramTypes);44 }45 });46 }47 48 /**49 * 在类层次结构中查找方法50 */51 private static Method findMethodInHierarchy(Class<?> clazz, String methodName, Class<?>[] paramTypes) {52 Class<?> currentClass = clazz;53 54 while (currentClass != null && currentClass != Object.class) {55 try {56 return currentClass.getDeclaredMethod(methodName, paramTypes);57 } catch (NoSuchMethodException e) {58 // 尝试模糊匹配(参数类型兼容)59 Method[] methods = currentClass.getDeclaredMethods();60 for (Method method : methods) {61 if (method.getName().equals(methodName) && 62 isParameterTypesCompatible(method.getParameterTypes(), paramTypes)) {63 return method;64 }65 }66 currentClass = currentClass.getSuperclass();67 }68 }69 70 return null;71 }72 73 /**74 * 检查参数类型兼容性75 */76 private static boolean isParameterTypesCompatible(Class<?>[] methodParams, Class<?>[] actualParams) {77 if (methodParams.length != actualParams.length) {78 return false;79 }80 81 for (int i = 0; i < methodParams.length; i++) {82 if (!methodParams[i].isAssignableFrom(actualParams[i])) {83 return false;84 }85 }86 87 return true;88 }89 90 /**91 * 获取参数类型数组92 */93 private static Class<?>[] getParameterTypes(Object... args) {94 if (args == null || args.length == 0) {95 return new Class<?>[0];96 }97 98 Class<?>[] types = new Class<?>[args.length];99 for (int i = 0; i < args.length; i++) {100 types[i] = args[i] != null ? args[i].getClass() : Object.class;101 }102 103 return types;104 }105 106 /**107 * 获取所有方法(包括继承的)108 */109 public static List<Method> getAllMethods(Class<?> clazz) {110 List<Method> allMethods = new ArrayList<>();111 Class<?> currentClass = clazz;112 113 while (currentClass != null && currentClass != Object.class) {114 Method[] methods = currentClass.getDeclaredMethods();115 allMethods.addAll(Arrays.asList(methods));116 currentClass = currentClass.getSuperclass();117 }118 119 return allMethods;120 }121 122 /**123 * 查找带有特定注解的方法124 */125 public static List<Method> findMethodsWithAnnotation(Class<?> clazz, Class<? extends Annotation> annotationClass) {126 List<Method> annotatedMethods = new ArrayList<>();127 List<Method> allMethods = getAllMethods(clazz);128 129 for (Method method : allMethods) {130 if (method.isAnnotationPresent(annotationClass)) {131 annotatedMethods.add(method);132 }133 }134 135 return annotatedMethods;136 }137 138 /**139 * 方法签名比较140 */141 public static boolean isSameSignature(Method method1, Method method2) {142 return method1.getName().equals(method2.getName()) &&143 Arrays.equals(method1.getParameterTypes(), method2.getParameterTypes());144 }145 146 /**147 * 创建方法代理148 */149 public static Object createMethodProxy(Object target, String methodName, Object... args) {150 return new Object() {151 public Object invoke() {152 return safeInvoke(target, methodName, args);153 }154 };155 }156 157 // 使用示例158 public static void main(String[] args) {159 CalculatorService calculator = new CalculatorService();160 161 System.out.println("=== 安全方法调用 ===");162 Object result1 = safeInvoke(calculator, "add", 10, 20);163 System.out.println("结果: " + result1);164 165 Object result2 = safeInvoke(calculator, "nonExistentMethod", "test");166 System.out.println("不存在的方法结果: " + result2);167 168 System.out.println("\n=== 查找所有方法 ===");169 List<Method> allMethods = getAllMethods(CalculatorService.class);170 allMethods.forEach(method -> System.out.println("方法: " + method.getName()));171 172 System.out.println("\n=== 方法签名比较 ===");173 try {174 Method method1 = CalculatorService.class.getMethod("add", int.class, int.class);175 Method method2 = CalculatorService.class.getMethod("multiply", double.class, double.class);176 System.out.println("方法签名相同: " + isSameSignature(method1, method2));177 } catch (NoSuchMethodException e) {178 e.printStackTrace();179 }180 }181}方法拦截器实现
1import java.lang.annotation.*;2import java.lang.reflect.*;3import java.util.Arrays;45// 方法执行时间注解6@Retention(RetentionPolicy.RUNTIME)7@Target(ElementType.METHOD)8@interface Timed {9 String value() default "";10}1112// 日志注解13@Retention(RetentionPolicy.RUNTIME)14@Target(ElementType.METHOD)15@interface Logged {16 boolean logParameters() default true;17 boolean logResult() default true;18}1920// 方法拦截器接口21interface MethodInterceptor {22 Object intercept(Object target, Method method, Object[] args) throws Exception;23}2425// 执行时间拦截器26class TimingInterceptor implements MethodInterceptor {27 @Override28 public Object intercept(Object target, Method method, Object[] args) throws Exception {29 long startTime = System.nanoTime();30 31 try {32 Object result = method.invoke(target, args);33 return result;34 } finally {35 long endTime = System.nanoTime();36 long duration = (endTime - startTime) / 1_000_000; // 转换为毫秒37 38 Timed timed = method.getAnnotation(Timed.class);39 String methodName = timed.value().isEmpty() ? method.getName() : timed.value();40 System.out.println(String.format("[TIMING] %s 执行时间: %d ms", methodName, duration));41 }42 }43}4445// 日志拦截器46class LoggingInterceptor implements MethodInterceptor {47 @Override48 public Object intercept(Object target, Method method, Object[] args) throws Exception {49 Logged logged = method.getAnnotation(Logged.class);50 51 if (logged.logParameters()) {52 System.out.println(String.format("[LOG] 调用方法: %s, 参数: %s", 53 method.getName(), Arrays.toString(args)));54 }55 56 Object result = method.invoke(target, args);57 58 if (logged.logResult()) {59 System.out.println(String.format("[LOG] 方法: %s, 返回值: %s", 60 method.getName(), result));61 }62 63 return result;64 }65}6667// 代理工厂68class ProxyFactory {69 public static <T> T createProxy(T target, MethodInterceptor... interceptors) {70 Class<?> targetClass = target.getClass();71 72 return (T) Proxy.newProxyInstance(73 targetClass.getClassLoader(),74 targetClass.getInterfaces(),75 new InvocationHandler() {76 @Override77 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {78 // 检查方法是否需要拦截79 boolean needsInterception = method.isAnnotationPresent(Timed.class) || 80 method.isAnnotationPresent(Logged.class);81 82 if (!needsInterception) {83 return method.invoke(target, args);84 }85 86 // 应用拦截器87 Object result = null;88 for (MethodInterceptor interceptor : interceptors) {89 if ((interceptor instanceof TimingInterceptor && method.isAnnotationPresent(Timed.class)) ||90 (interceptor instanceof LoggingInterceptor && method.isAnnotationPresent(Logged.class))) {91 result = interceptor.intercept(target, method, args);92 }93 }94 95 return result != null ? result : method.invoke(target, args);96 }97 }98 );99 }100}101102// 业务服务接口103interface BusinessService {104 String processData(String data);105 int calculate(int a, int b);106 void doSomething();107}108109// 业务服务实现110class BusinessServiceImpl implements BusinessService {111 112 @Timed("数据处理")113 @Logged(logParameters = true, logResult = true)114 @Override115 public String processData(String data) {116 // 模拟耗时操作117 try {118 Thread.sleep(100);119 } catch (InterruptedException e) {120 Thread.currentThread().interrupt();121 }122 return "处理后的数据: " + data.toUpperCase();123 }124 125 @Timed126 @Override127 public int calculate(int a, int b) {128 // 模拟复杂计算129 try {130 Thread.sleep(50);131 } catch (InterruptedException e) {132 Thread.currentThread().interrupt();133 }134 return a * b + (a + b);135 }136 137 @Logged(logParameters = false, logResult = false)138 @Override139 public void doSomething() {140 System.out.println("执行某些操作...");141 }142}143144public class MethodInterceptorDemo {145 public static void main(String[] args) {146 // 创建原始服务147 BusinessService originalService = new BusinessServiceImpl();148 149 // 创建代理服务150 BusinessService proxyService = ProxyFactory.createProxy(151 originalService,152 new TimingInterceptor(),153 new LoggingInterceptor()154 );155 156 System.out.println("=== 使用代理服务 ===");157 158 // 测试带时间和日志的方法159 String result1 = proxyService.processData("hello world");160 System.out.println("最终结果: " + result1);161 162 System.out.println();163 164 // 测试只带时间的方法165 int result2 = proxyService.calculate(10, 20);166 System.out.println("计算结果: " + result2);167 168 System.out.println();169 170 // 测试只带日志的方法171 proxyService.doSomething();172 }173}4. 构造器(Constructor)操作详解
构造器反射允许我们在运行时动态创建对象实例,这是依赖注入框架的核心技术。
5. 注解(Annotation)反射处理
注解反射是现代Java框架的核心技术,Spring、Hibernate等框架大量使用注解来简化配置。
- 注解基础
注解定义与使用
1import java.lang.annotation.*;2import java.lang.reflect.*;3import java.util.*;45// 1. 字段验证注解6@Retention(RetentionPolicy.RUNTIME)7@Target(ElementType.FIELD)8@interface Validate {9 String message() default "验证失败";10 int min() default 0;11 int max() default Integer.MAX_VALUE;12 boolean required() default false;13 String pattern() default "";14}1516// 2. 方法级注解17@Retention(RetentionPolicy.RUNTIME)18@Target(ElementType.METHOD)19@interface Cacheable {20 String key() default "";21 int expireTime() default 3600; // 秒22}2324@Retention(RetentionPolicy.RUNTIME)25@Target(ElementType.METHOD)26@interface Transactional {27 String value() default "default";28 boolean readOnly() default false;29}3031// 3. 类级注解32@Retention(RetentionPolicy.RUNTIME)33@Target(ElementType.TYPE)34@interface Entity {35 String tableName() default "";36}3738@Retention(RetentionPolicy.RUNTIME)39@Target(ElementType.TYPE)40@interface Service {41 String value() default "";42}4344// 使用注解的示例类45@Entity(tableName = "users")46class User {47 @Validate(required = true, message = "用户名不能为空")48 private String username;49 50 @Validate(min = 18, max = 100, message = "年龄必须在18-100之间")51 private int age;52 53 @Validate(required = true, pattern = "^[\\w-\\.]+@([\\w-]+\\.)+[\\w-]{2,4}$", message = "邮箱格式不正确")54 private String email;55 56 private String password;57 58 public User(String username, int age, String email, String password) {59 this.username = username;60 this.age = age;61 this.email = email;62 this.password = password;63 }64 65 // getters and setters...66 public String getUsername() { return username; }67 public int getAge() { return age; }68 public String getEmail() { return email; }69 public String getPassword() { return password; }70 71 @Override72 public String toString() {73 return String.format("User{username='%s', age=%d, email='%s'}", username, age, email);74 }75}7677@Service("userService")78class UserService {79 80 @Cacheable(key = "user:{0}", expireTime = 1800)81 @Transactional(readOnly = true)82 public User findById(Long id) {83 System.out.println("从数据库查询用户: " + id);84 // 模拟数据库查询85 return new User("user" + id, 25, "user" + id + "@example.com", "password");86 }87 88 @Transactional89 public void saveUser(User user) {90 System.out.println("保存用户到数据库: " + user);91 // 模拟保存操作92 }93 94 @Cacheable(key = "userList", expireTime = 600)95 public List<User> findAll() {96 System.out.println("从数据库查询所有用户");97 // 模拟查询所有用户98 return Arrays.asList(99 new User("alice", 28, "alice@example.com", "pass1"),100 new User("bob", 32, "bob@example.com", "pass2")101 );102 }103}104105public class AnnotationReflectionDemo {106 107 /**108 * 验证对象字段109 */110 public static boolean validateObject(Object obj) {111 Class<?> clazz = obj.getClass();112 Field[] fields = clazz.getDeclaredFields();113 boolean isValid = true;114 115 System.out.println("验证对象: " + obj.getClass().getSimpleName());116 117 for (Field field : fields) {118 if (field.isAnnotationPresent(Validate.class)) {119 Validate validate = field.getAnnotation(Validate.class);120 121 try {122 field.setAccessible(true);123 Object value = field.get(obj);124 125 // 检查必填项126 if (validate.required()) {127 if (value == null || (value instanceof String && ((String) value).trim().isEmpty())) {128 System.err.println(" ❌ " + field.getName() + ": " + validate.message());129 isValid = false;130 continue;131 }132 }133 134 // 检查数值范围135 if (value instanceof Integer) {136 int intValue = (Integer) value;137 if (intValue < validate.min() || intValue > validate.max()) {138 System.err.println(" ❌ " + field.getName() + ": " + validate.message());139 isValid = false;140 }141 }142 143 // 检查正则表达式144 if (!validate.pattern().isEmpty() && value instanceof String) {145 String strValue = (String) value;146 if (!strValue.matches(validate.pattern())) {147 System.err.println(" ❌ " + field.getName() + ": " + validate.message());148 isValid = false;149 }150 }151 152 if (isValid) {153 System.out.println(" ✅ " + field.getName() + ": 验证通过");154 }155 156 } catch (IllegalAccessException e) {157 System.err.println("无法访问字段: " + field.getName());158 isValid = false;159 }160 }161 }162 163 return isValid;164 }165 166 /**167 * 分析类的注解信息168 */169 public static void analyzeClassAnnotations(Class<?> clazz) {170 System.out.println("\n=== 分析类注解: " + clazz.getSimpleName() + " ===");171 172 // 类级注解173 Annotation[] classAnnotations = clazz.getAnnotations();174 if (classAnnotations.length > 0) {175 System.out.println("类注解:");176 for (Annotation annotation : classAnnotations) {177 System.out.println(" " + annotation);178 179 // 处理特定注解180 if (annotation instanceof Entity) {181 Entity entity = (Entity) annotation;182 String tableName = entity.tableName().isEmpty() ? 183 clazz.getSimpleName().toLowerCase() : entity.tableName();184 System.out.println(" 表名: " + tableName);185 }186 187 if (annotation instanceof Service) {188 Service service = (Service) annotation;189 String serviceName = service.value().isEmpty() ? 190 clazz.getSimpleName() : service.value();191 System.out.println(" 服务名: " + serviceName);192 }193 }194 }195 196 // 方法注解197 Method[] methods = clazz.getDeclaredMethods();198 for (Method method : methods) {199 Annotation[] methodAnnotations = method.getAnnotations();200 if (methodAnnotations.length > 0) {201 System.out.println("方法 " + method.getName() + " 的注解:");202 for (Annotation annotation : methodAnnotations) {203 System.out.println(" " + annotation);204 }205 }206 }207 }208 209 public static void main(String[] args) {210 // 测试对象验证211 User validUser = new User("alice", 25, "alice@example.com", "password123");212 User invalidUser = new User("", 15, "invalid-email", "pass");213 214 System.out.println("=== 验证有效用户 ===");215 boolean isValid1 = validateObject(validUser);216 System.out.println("验证结果: " + (isValid1 ? "✅ 通过" : "❌ 失败"));217 218 System.out.println("\n=== 验证无效用户 ===");219 boolean isValid2 = validateObject(invalidUser);220 System.out.println("验证结果: " + (isValid2 ? "✅ 通过" : "❌ 失败"));221 222 // 分析类注解223 analyzeClassAnnotations(User.class);224 analyzeClassAnnotations(UserService.class);225 }226}6. 反射在框架中的应用
反射是Java框架开发的核心技术,Spring、Hibernate等框架都大量使用反射。
- 依赖注入:Spring IoC容器通过反射创建和注入对象
- ORM映射:Hibernate通过反射实现对象关系映射
- AOP切面:动态代理和字节码增强
- 序列化:JSON、XML序列化框架
- 测试框架:JUnit通过反射发现和执行测试方法
通过掌握Java反射机制的核心概念和实践技巧,你可以更好地理解和使用各种Java框架,同时具备开发自己的框架和工具的能力。
7. 面试题精选
7.1 什么是Java反射?它的作用和应用场景有哪些?
答案: Java反射是一种在运行时检查、访问和修改类、接口、字段和方法的机制。它允许程序在运行时而非编译时获取类的信息并操作类的对象。
作用和应用场景:
- 依赖注入框架:Spring框架使用反射实现依赖注入和控制反转(IoC)
- ORM框架:Hibernate等ORM框架使用反射将对象映射到数据库表
- 单元测试框架:JUnit使用反射来识别和运行测试方法
- 序列化和反序列化:Jackson、Gson等JSON库使用反射自动序列化对象
- 动态代理:实现AOP(面向切面编程)
- 注解处理:在运行时处理和解释注解
- 插件和模块化系统:动态加载和使用模块或插件
- IDE开发工具:代码补全、调试器等功能
7.2 Java反射的核心API有哪些?如何获取Class对象?
答案: Java反射的核心API位于java.lang.reflect包中,主要包括:
- Class:表示类或接口的类型信息
- Field:表示类的字段(成员变量)
- Method:表示类的方法
- Constructor:表示类的构造方法
- Modifier:提供对类和成员访问修饰符的信息
- Array:提供动态创建和访问数组的静态方法
- Parameter:表示方法或构造函数参数(Java 8+)
获取Class对象的方式:
1// 方式1:通过类名.class(编译时确定)2Class<String> clazz1 = String.class;34// 方式2:通过对象的getClass()方法(运行时确定)5String str = "Hello";6Class<?> clazz2 = str.getClass();78// 方式3:通过Class.forName()(动态加载,最常用)9Class<?> clazz3 = Class.forName("java.lang.String");1011// 方式4:通过类加载器12ClassLoader classLoader = Thread.currentThread().getContextClassLoader();13Class<?> clazz4 = classLoader.loadClass("java.lang.String");7.3 Java反射的优缺点是什么?如何提高反射性能?
答案:
优点:
- 灵活性:允许在运行时检查和修改类的行为
- 可扩展性:支持创建可扩展的应用和框架
- 动态性:能够动态创建对象和调用方法
- 解耦:帮助实现松耦合设计
缺点:
- 性能损失:反射操作比直接代码调用慢,有性能开销
- 安全限制:可能违反访问控制规则
- 代码复杂性:反射代码难以理解和维护
- 编译时检查缺失:类型错误在运行时才会发现
提高反射性能的方法:
- 缓存反射对象:重用Class、Method、Field等反射对象
- 使用setAccessible(true):避免Java安全检查的开销
- 批量操作:一次获取所有需要的反射信息,避免重复查找
- 限制使用范围:只在必要时使用反射
- 使用MethodHandles:Java 7引入的MethodHandle比传统反射性能更好
7.4 反射中的setAccessible(true)作用是什么?为什么需要它?
答案: setAccessible(true)方法用于取消Java语言访问检查,使得可以访问和修改原本无法访问的成员(如private字段或方法)。
作用:
- 访问私有成员:允许访问类的private字段、方法和构造器
- 提高性能:绕过JVM的安全检查,提高反射操作的性能
为什么需要它:
- 框架开发:框架需要访问用户类的私有成员
- 测试目的:单元测试时可能需要访问私有方法或字段
- 序列化/反序列化:序列化库需要访问所有字段,包括私有字段
- 性能优化:在反射频繁使用的场景下,提高执行效率
1// 访问私有字段示例2Field field = targetClass.getDeclaredField("privateField");3field.setAccessible(true); // 关键步骤4field.set(targetObject, newValue);56// 调用私有方法示例7Method method = targetClass.getDeclaredMethod("privateMethod", String.class);8method.setAccessible(true); // 关键步骤9method.invoke(targetObject, "parameter");注意事项: 虽然setAccessible(true)可以绕过访问控制,但在实际应用中应谨慎使用,以免破坏封装性。从Java 9开始,模块系统可以限制非法反射访问。
7.5 如何通过反射调用方法?如何处理方法的异常?
答案: 通过Method对象的invoke()方法可以反射调用方法:
1public static void invokeMethod() {2 try {3 // 获取Class对象4 Class<?> clazz = Class.forName("java.util.ArrayList");5 6 // 创建实例7 Object list = clazz.getDeclaredConstructor().newInstance();8 9 // 获取add方法10 Method addMethod = clazz.getMethod("add", Object.class);11 12 // 调用add方法13 boolean result = (boolean)addMethod.invoke(list, "Hello");14 15 // 获取size方法16 Method sizeMethod = clazz.getMethod("size");17 18 // 调用size方法19 int size = (int)sizeMethod.invoke(list);20 21 System.out.println("添加结果: " + result);22 System.out.println("列表大小: " + size);23 24 } catch (ClassNotFoundException e) {25 System.out.println("类未找到: " + e.getMessage());26 } catch (NoSuchMethodException e) {27 System.out.println("方法未找到: " + e.getMessage());28 } catch (InstantiationException | IllegalAccessException e) {29 System.out.println("实例化错误: " + e.getMessage());30 } catch (InvocationTargetException e) {31 // 处理目标方法抛出的异常32 System.out.println("方法执行异常: " + e.getTargetException().getMessage());33 }34}处理反射方法的异常:
- InvocationTargetException:表示被调用的方法本身抛出了异常,通过getTargetException()可以获取原始异常
- NoSuchMethodException:当请求的方法不存在时抛出
- IllegalAccessException:当访问控制不允许执行操作时抛出
- IllegalArgumentException:当参数类型不匹配时抛出
最佳实践:
- 总是捕获并处理InvocationTargetException,它包装了目标方法的原始异常
- 检查参数类型的兼容性,避免类型转换异常
- 考虑使用try-with-resources语句处理可能需要关闭的资源
参与讨论