Java反射详解
1. 反射概述
什么是反射
反射(Reflection)是Java中的一种强大机制,它允许程序在运行时检查和操作类、对象、方法和属性。
核心思想:
- 将一个Java类看成是一个对象,用
Class类型的对象来表示,称为类对象
- 可以获取这个类对象中的组成部分:变量、方法、注释、修饰符等信息
- 类中的每个组成部分也可以当做对象,用对应的类实例来表示
反射的核心类
反射中类和类中成员对应的具体类:
| 类名 | 用途 | 包位置 |
|---|
Class | 代表一个Java类对象 | java.lang |
Field | 代表类或接口的单个字段 | java.lang.reflect |
Method | 代表类中的方法 | java.lang.reflect |
Constructor | 代表类中的构造方法 | java.lang.reflect |
Array | 代表数组操作工具 | java.lang.reflect |
Package | 代表包信息 | java.lang |
定义: 这种将Java类和类中的成员映射成对象并对其操作的机制,就叫做反射。
2. Class类详解
Class类型对象代表一个Java类对象,是反射的入口点。
2.1 获取Class对象的三种方法
Class<?> class1 = Person.class;
Class<?> class2 = Class.forName("java.lang.Integer");
Class<?> class3 = "abc".getClass();
2.2 Class类主要方法
| 方法名 | 功能描述 |
|---|
forName(String className) | 静态方法,获取指定类名的Class对象 |
newInstance() | 创建该类的实例对象 |
getName() | 获取类的完整名称 |
getSimpleName() | 获取类的简单名称 |
注解相关方法
| 方法名 | 功能描述 |
|---|
getAnnotation(Class<A> annotationClass) | 获取指定类型的注解,没有返回null |
getAnnotations() | 获取此元素上存在的所有注解 |
getDeclaredAnnotations() | 获取直接在此元素上的所有注解 |
构造方法相关
| 方法名 | 功能描述 |
|---|
getDeclaredConstructor(Class<?>... parameterTypes) | 获取本类声明的指定参数类型的构造方法 |
getDeclaredConstructors() | 获取本类所有构造方法 |
getConstructor(Class<?>... parameterTypes) | 获取公共构造方法 |
字段相关
| 方法名 | 功能描述 |
|---|
getField(String name) | 获取公共成员字段 |
getDeclaredField(String name) | 获取本类声明的指定字段(包括私有字段) |
getFields() | 获取所有公共字段 |
getDeclaredFields() | 获取本类声明的所有字段 |
方法相关
| 方法名 | 功能描述 |
|---|
getMethod(String name, Class<?>... parameterTypes) | 获取公共方法 |
getDeclaredMethod(String name, Class<?>... parameterTypes) | 获取本类声明的指定方法 |
getMethods() | 获取所有公共方法 |
getDeclaredMethods() | 获取本类声明的所有方法 |
其他实用方法
| 方法名 | 功能描述 |
|---|
getClassLoader() | 获取类加载器 |
getComponentType() | 获取数组组件的Class |
getPackage() | 获取类所属的包 |
getSuperclass() | 获取父类 |
getInterfaces() | 获取实现的接口 |
判断方法
| 方法名 | 功能描述 |
|---|
isAnnotation() | 判断是否为注解类 |
isArray() | 判断是否为数组类 |
isEnum() | 判断是否为枚举类 |
isInterface() | 判断是否为接口 |
3. Constructor类详解
Constructor类代表某个类中的一个构造方法。
主要方法
| 方法名 | 功能描述 |
|---|
newInstance(Object... initargs) | 调用该构造方法,实例化对象 |
getAnnotation(Class<A> annotationClass) | 获取指定类型的注解 |
getDeclaredAnnotations() | 获取该元素上的所有注解 |
getName() | 获取构造方法名称 |
getParameterTypes() | 获取参数类型 |
4. Field类详解
Field类代表类或接口的单个字段信息。
主要方法
获取字段值
| 方法名 | 功能描述 |
|---|
get(Object obj) | 获取指定对象上该字段的值 |
getBoolean(Object obj) | 获取boolean类型字段值 |
getByte(Object obj) | 获取byte类型字段值 |
getChar(Object obj) | 获取char类型字段值 |
getInt(Object obj) | 获取int类型字段值 |
getDouble(Object obj) | 获取double类型字段值 |
设置字段值
| 方法名 | 功能描述 |
|---|
set(Object obj, Object value) | 给该字段赋值 |
setBoolean(Object obj, boolean value) | 设置boolean值 |
setInt(Object obj, int value) | 设置int值 |
setDouble(Object obj, double value) | 设置double值 |
其他方法
| 方法名 | 功能描述 |
|---|
getName() | 获取字段名称 |
getType() | 获取字段类型 |
getModifiers() | 获取修饰符 |
setAccessible(boolean flag) | 设置是否可访问(用于访问私有字段) |
5. Method类详解
Method类提供了对类或接口中方法的描述,代表类中的方法。
常用方法
| 方法名 | 功能描述 |
|---|
invoke(Object obj, Object... args) | 调用该方法 |
getName() | 获取方法名 |
getGenericParameterTypes() | 获取方法的参数类型列表 |
getModifiers() | 获取方法修饰符的整数表现形式 |
getReturnType() | 获取返回值类型 |
getParameterTypes() | 获取参数类型数组 |
setAccessible(boolean flag) | 设置是否可访问 |
重要特性: 可以不用创建对象实例,直接获取某类的方法,调用invoke()来实现该方法的应用。
6. Array类详解
Array类代表数组操作工具类,提供了对数组的反射操作。
主要方法
| 方法名 | 功能描述 |
|---|
get(Object array, int index) | 获取数组指定索引的元素 |
set(Object array, int index, Object value) | 设置数组指定索引的元素值 |
getLength(Object array) | 获取数组长度 |
newInstance(Class<?> componentType, int length) | 创建新数组 |
newInstance(Class<?> componentType, int... dimensions) | 创建多维数组 |
7. 反射应用实战
7.1 示例类定义
首先定义一个类似JavaBean的Person类:
package Reflect;
class Person {
private int age = 0;
public String name = "unknownName";
public Person() {
super();
}
public Person(int age, String name) {
super();
this.age = age;
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return this.name + " " + this.age;
}
}
7.2 反射应用案例
import java.lang.annotation.Target;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
public class Demo {
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException, SecurityException,
NoSuchMethodException, IllegalArgumentException,
InvocationTargetException, NoSuchFieldException {
System.out.println("=== 案例0:获取Class对象 ===");
Class<?> class1 = Person.class;
Class<?> class2 = Class.forName("java.lang.Integer");
Class<?> class3 = (new int[]{1, 3}).getClass();
System.out.println(class1.getName());
System.out.println(class2.getName());
System.out.println(class3.getName());
System.out.println("\n=== 案例1:获取父类和接口 ===");
Class<?> supers = Class.forName("java.util.Hashtable").getSuperclass();
System.out.println("父类:" + supers.getName());
Class<?>[] interfaces = Class.forName("java.util.HashSet").getInterfaces();
for (int i = 0; i < interfaces.length; i++) {
System.out.println("接口" + (i + 1) + ": " + interfaces[i].getName());
}
System.out.println("\n=== 案例2:反射创建对象 ===");
Person p1 = (Person) class1.newInstance();
Constructor<?> constructor = class1.getConstructor(int.class,
Class.forName("java.lang.String"));
Person p2 = (Person) constructor.newInstance(19, "lisi");
System.out.println(p1);
System.out.println(p2);
System.out.println("\n=== 案例3:操作对象属性 ===");
Field field = p1.getClass().getDeclaredField("name");
field.set(p1, "zhangsan");
System.out.println(Modifier.toString(field.getModifiers()) + " "
+ field.getType() + " " + field.getName());
System.out.println(p1);
System.out.println("\n=== 案例4:调用方法 ===");
Method method = class1.getMethod("setAge", int.class);
method.invoke(p2, 20);
System.out.println(p2);
System.out.println("\n=== 案例5:数组反射操作 ===");
int[] arr = {1, 2, 3};
Class<?> componentType = arr.getClass().getComponentType();
System.out.println("数组的类型:" + componentType.getName());
System.out.println("数组的长度:" + Array.getLength(arr));
System.out.println("数组的第一个元素:" + Array.get(arr, 0));
int[] newArr = (int[]) Array.newInstance(componentType, 4);
System.arraycopy(arr, 0, newArr, 0, Array.getLength(arr));
Array.setInt(newArr, 3, 4);
for (int i = 0; i < newArr.length; i++) {
System.out.print(newArr[i] + " ");
}
System.out.println();
System.out.println("\n=== 案例6:注解反射 ===");
System.out.println("Demo类是否为注解类:" + Demo.class.isAnnotation());
boolean hasTargetAnnotation = Demo.class.getMethod("main", String[].class)
.isAnnotationPresent(Target.class);
System.out.println("main方法是否有Target注解:" + hasTargetAnnotation);
}
}
7.3 运行结果示例
Reflect.Person
java.lang.Integer
[I
父类:java.util.Dictionary
接口1: java.util.Map
接口2: java.lang.Cloneable
接口3: java.io.Serializable
unknownName 0
lisi 19
public java.lang.String name
zhangsan 0
lisi 20
数组的类型:int
数组的长度:3
数组的第一个元素:1
1 2 3 4
Demo类是否为注解类:false
main方法是否有Target注解:false
8. 反射的高级应用
8.1 私有字段和方法的访问
public class PrivateAccessDemo {
public static void main(String[] args) throws Exception {
Person person = new Person();
Class<?> clazz = person.getClass();
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(person, 25);
System.out.println("私有字段age的值:" + ageField.get(person));
}
}
8.2 动态代理示例
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface UserService {
void save(String user);
String find(String id);
}
class UserServiceImpl implements UserService {
@Override
public void save(String user) {
System.out.println("保存用户:" + user);
}
@Override
public String find(String id) {
return "用户" + id;
}
}
class LogInvocationHandler implements InvocationHandler {
private Object target;
public LogInvocationHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法调用前:" + method.getName());
Object result = method.invoke(target, args);
System.out.println("方法调用后:" + method.getName());
return result;
}
}
public class ProxyDemo {
public static void main(String[] args) {
UserService target = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
new LogInvocationHandler(target)
);
proxy.save("张三");
String user = proxy.find("001");
System.out.println(user);
}
}
8.3 反射工具类
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
public class ReflectionUtils {
public static List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
while (clazz != null) {
Field[] declaredFields = clazz.getDeclaredFields();
for (Field field : declaredFields) {
fields.add(field);
}
clazz = clazz.getSuperclass();
}
return fields;
}
public static Object getFieldValue(Object obj, String fieldName) {
try {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
return field.get(obj);
} catch (Exception e) {
throw new RuntimeException("获取字段值失败", e);
}
}
public static void setFieldValue(Object obj, String fieldName, Object value) {
try {
Field field = obj.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(obj, value);
} catch (Exception e) {
throw new RuntimeException("设置字段值失败", e);
}
}
public static Object invokeMethod(Object obj, String methodName, Object... args) {
try {
Class<?>[] paramTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
paramTypes[i] = args[i].getClass();
}
Method method = obj.getClass().getMethod(methodName, paramTypes);
return method.invoke(obj, args);
} catch (Exception e) {
throw new RuntimeException("方法调用失败", e);
}
}
@SuppressWarnings("unchecked")
public static <T> T createInstance(Class<T> clazz) {
try {
return clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException("创建实例失败", e);
}
}
}
9. 反射的优缺点
9.1 优点
- 运行时获取类信息:可以在运行时检查类的结构
- 动态创建对象:不需要在编译时确定类型
- 调用任意方法:包括私有方法
- 访问任意字段:包括私有字段
- 框架开发基础:Spring、Hibernate等框架的核心技术
9.2 缺点
- 性能开销:反射操作比直接调用慢
- 安全限制:可能违反封装性
- 代码复杂性:增加代码的复杂度和维护难度
- 编译时检查缺失:失去编译时的类型检查
9.3 使用场景
| 场景 | 说明 |
|---|
| 框架开发 | Spring IoC、ORM框架等 |
| 配置文件解析 | 根据配置动态创建对象 |
| 插件系统 | 动态加载和使用插件 |
| 序列化/反序列化 | JSON、XML等数据转换 |
| 单元测试 | 测试私有方法和字段 |
| 代码生成工具 | 自动生成代码 |
10. 常见异常
异常类型及处理
| 异常名称 | 异常描述 | 常见原因 |
|---|
ClassNotFoundException | 未找到类异常 | 类名错误或类路径问题 |
InstantiationException | 无法实例化异常 | 抽象类或接口无法实例化 |
IllegalAccessException | 非法存取异常 | 访问权限不足 |
SecurityException | 安全管理器异常 | 安全策略限制 |
NoSuchMethodException | 方法未找到异常 | 方法名或参数类型错误 |
IllegalArgumentException | 不合法参数异常 | 参数类型或数量不匹配 |
InvocationTargetException | 调用目标异常 | 被调用方法内部抛出异常 |
NoSuchFieldException | 字段未找到异常 | 字段名错误 |
异常处理示例
public class ExceptionHandlingDemo {
public static void main(String[] args) {
try {
Class<?> clazz = Class.forName("com.example.NonExistentClass");
Object instance = clazz.newInstance();
Field field = clazz.getDeclaredField("nonExistentField");
field.get(instance);
} catch (ClassNotFoundException e) {
System.err.println("类未找到:" + e.getMessage());
} catch (InstantiationException e) {
System.err.println("实例化失败:" + e.getMessage());
} catch (IllegalAccessException e) {
System.err.println("访问权限不足:" + e.getMessage());
} catch (NoSuchFieldException e) {
System.err.println("字段未找到:" + e.getMessage());
} catch (Exception e) {
System.err.println("其他异常:" + e.getMessage());
e.printStackTrace();
}
}
}
11. 反射性能优化
11.1 缓存反射对象
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.Map;
public class ReflectionCache {
private static final Map<String, Method> methodCache = new ConcurrentHashMap<>();
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
String key = clazz.getName() + "#" + methodName;
return methodCache.computeIfAbsent(key, k -> {
try {
return clazz.getMethod(methodName, paramTypes);
} catch (NoSuchMethodException e) {
throw new RuntimeException("方法不存在:" + methodName, e);
}
});
}
}
11.2 使用MethodHandle(Java 7+)
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
public class MethodHandleDemo {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle handle = lookup.findVirtual(
String.class,
"substring",
MethodType.methodType(String.class, int.class)
);
String result = (String) handle.invoke("Hello World", 6);
System.out.println(result);
}
}
12. 总结
反射的核心价值
- 动态性:程序可以在运行时获取和操作类信息
- 灵活性:不需要在编译时确定具体的类和方法
- 通用性:可以编写处理任意类型的通用代码
- 框架基础:是许多Java框架的核心技术
最佳实践
- 谨慎使用:只在确实需要动态性时使用反射
- 性能考虑:缓存反射获取的对象,避免重复获取
- 异常处理:妥善处理反射可能抛出的异常
- 安全性:使用
setAccessible()时要考虑安全性
- 可维护性:反射代码要有充分的注释和文档
适用场景建议
| 推荐使用 | 谨慎使用 | 不推荐使用 |
|---|
| 框架开发 | 业务逻辑中偶尔使用 | 频繁的性能敏感操作 |
| 配置驱动的系统 | 工具类开发 | 简单的对象创建 |
| 插件系统 | 测试代码 | 已知类型的操作 |
| 序列化框架 | 代码生成 | 大量重复调用 |
反射是Java中强大而危险的特性,正确理解和使用反射,能够让我们编写出更加灵活和通用的代码,但也要注意其带来的性能和安全性问题。
本文全面介绍了Java反射机制的原理、核心类、实际应用和注意事项,希望对深入理解和掌握Java反射技术有所帮助。