跳到主要内容Java 反射机制详解:从原理到实战 | 极客日志Javajava
Java 反射机制详解:从原理到实战
Java 反射机制允许程序在运行时动态获取类信息、创建对象及调用方法。本文涵盖 Class 对象获取、构造方法、字段与方法反射操作,结合通用打印器、依赖注入、ORM 等实战案例。重点解析性能优化、缓存策略、MethodHandle 替代方案及安全注意事项,帮助开发者掌握反射核心用法与最佳实践。
BigDataPan3 浏览 Java 反射机制详解
运行时动态获取类信息、创建对象、调用方法完整教程
一、反射概述
1.1 什么是反射
反射(Reflection)是 Java 提供的一种机制,允许程序在运行时检查和操作类的结构(类、方法、字段等)。
反射的核心功能:
- 运行时获取类的信息
- 动态创建对象
- 动态调用方法
- 动态访问和修改字段
1.2 反射的应用场景
- 框架开发:Spring、Hibernate 等框架大量使用反射
- 动态代理:AOP 面向切面编程
- 注解处理:运行时处理注解
- 插件系统:动态加载类
- 序列化/反序列化:JSON、XML 转换
1.3 反射的优缺点
优点:
- 灵活性高,可以动态操作类
- 降低代码耦合度
- 适合框架和工具开发
缺点:
- 性能开销大(比直接调用慢 10-100 倍)
- 破坏封装性(可访问私有成员)
- 代码可读性差
- 编译时无法检查类型安全
二、获取 Class 对象
2.1 三种获取方式
public class GetClassDemo {
public static void main(String[] args) throws Exception {
Class<String> clazz1 = String.class;
String str = "Hello";
Class<? extends String> clazz2 = str.getClass();
Class<?> clazz3 = Class.forName("java.lang.String");
System.out.println(clazz1 == clazz2);
System.out.println(clazz2 == clazz3);
}
}
2.2 获取类信息
public class ClassInfoDemo {
public static void main(String[] args) {
Class<String> clazz = String.class;
System.out.println("简单名:" + clazz.getSimpleName());
System.out.println("全限定名:" + clazz.getName());
System.out.println("规范名:" + clazz.getCanonicalName());
System.out.println("包名:" + clazz.getPackage().getName());
System.out.println("父类:" + clazz.getSuperclass());
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
System.out.println("接口:" + i.getName());
}
int modifiers = clazz.getModifiers();
System.out.println("是否 public: " + java.lang.reflect.Modifier.isPublic(modifiers));
System.out.println("是否 final: " + java.lang.reflect.Modifier.isFinal(modifiers));
System.out.println("是否 abstract: " + java.lang.reflect.Modifier.isAbstract(modifiers));
System.out.println("是否 interface: " + java.lang.reflect.Modifier.isInterface(modifiers));
}
}
2.3 判断类型
public class TypeCheckDemo {
public static void main(String[] args) {
Class<String> clazz = String.class;
System.out.println("是否接口:" + clazz.isInterface());
System.out.println("是否数组:" + clazz.isArray());
System.out.println("是否枚举:" + clazz.isEnum());
System.out.println("是否注解:" + clazz.isAnnotation());
System.out.println("是否基本类型:" + clazz.isPrimitive());
Class<?> arrayClass = int[].class;
System.out.println("是否数组:" + arrayClass.isArray());
System.out.println("数组元素类型:" + arrayClass.getComponentType());
System.out.println("String 是否是 Object 的子类:" + Object.class.isAssignableFrom(String.class));
System.out.println("Object 是否是 String 的子类:" + String.class.isAssignableFrom(Object.class));
}
}
2.4 获取注解
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Author {
String name();
String date();
}
@Author(name = "张三", date = "2025-01-01")
class MyClass {}
public class AnnotationDemo {
public static void main(String[] args) {
Class<MyClass> clazz = MyClass.class;
if (clazz.isAnnotationPresent(Author.class)) {
Author author = clazz.getAnnotation(Author.class);
System.out.println("作者:" + author.name());
System.out.println("日期:" + author.date());
}
Annotation[] annotations = clazz.getAnnotations();
for (Annotation ann : annotations) {
System.out.println("注解:" + ann);
}
}
}
三、构造方法反射
3.1 获取构造方法
import java.lang.reflect.Constructor;
public class Person {
private String name;
private int age;
public Person() {}
public Person(String name) { this.name = name; }
public Person(String name, int age) {
this.name = name;
this.age = age;
}
private Person(int age) { this.age = age; }
}
public class ConstructorDemo {
public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
Constructor<?>[] constructors = clazz.getConstructors();
for (Constructor<?> c : constructors) {
System.out.println(c);
}
Constructor<?>[] allConstructors = clazz.getDeclaredConstructors();
Constructor<Person> constructor1 = clazz.getConstructor();
Constructor<Person> constructor2 = clazz.getConstructor(String.class);
Constructor<Person> constructor3 = clazz.getConstructor(String.class, int.class);
Constructor<Person> privateConstructor = clazz.getDeclaredConstructor(int.class);
}
}
3.2 创建对象
public class CreateInstanceDemo {
public static void main(String[] args) throws Exception {
Class<Person> clazz = Person.class;
Person p1 = clazz.getDeclaredConstructor().newInstance();
Constructor<Person> constructor = clazz.getConstructor(String.class, int.class);
Person p2 = constructor.newInstance("张三", 25);
Constructor<Person> privateConstructor = clazz.getDeclaredConstructor(int.class);
privateConstructor.setAccessible(true);
Person p3 = privateConstructor.newInstance(30);
System.out.println(p2);
}
}
四、字段反射
4.1 获取字段
import java.lang.reflect.Field;
public class Student {
public String name;
private int age;
protected String school;
static String country = "China";
}
public class FieldDemo {
public static void main(String[] args) throws Exception {
Class<Student> clazz = Student.class;
Field[] fields = clazz.getFields();
Field[] allFields = clazz.getDeclaredFields();
Field nameField = clazz.getField("name");
Field ageField = clazz.getDeclaredField("age");
System.out.println("字段名:" + nameField.getName());
System.out.println("字段类型:" + nameField.getType());
System.out.println("修饰符:" + nameField.getModifiers());
}
}
4.2 访问和修改字段
public class FieldAccessDemo {
public static void main(String[] args) throws Exception {
Student student = new Student();
Class<Student> clazz = Student.class;
Field nameField = clazz.getField("name");
nameField.set(student, "李四");
String name = (String) nameField.get(student);
System.out.println("姓名:" + name);
Field ageField = clazz.getDeclaredField("age");
ageField.setAccessible(true);
ageField.set(student, 20);
int age = (int) ageField.get(student);
System.out.println("年龄:" + age);
Field countryField = clazz.getDeclaredField("country");
countryField.set(null, "USA");
String country = (String) countryField.get(null);
System.out.println("国家:" + country);
}
}
五、方法反射
5.1 获取方法
import java.lang.reflect.Method;
import java.util.Arrays;
public class Calculator {
public int add(int a, int b) { return a + b; }
private int subtract(int a, int b) { return a - b; }
public static int multiply(int a, int b) { return a * b; }
}
public class MethodDemo {
public static void main(String[] args) throws Exception {
Class<Calculator> clazz = Calculator.class;
Method[] methods = clazz.getMethods();
Method[] allMethods = clazz.getDeclaredMethods();
Method addMethod = clazz.getMethod("add", int.class, int.class);
Method subtractMethod = clazz.getDeclaredMethod("subtract", int.class, int.class);
System.out.println("方法名:" + addMethod.getName());
System.out.println("返回类型:" + addMethod.getReturnType());
System.out.println("参数类型:" + Arrays.toString(addMethod.getParameterTypes()));
}
}
5.2 调用方法
public class MethodInvokeDemo {
public static void main(String[] args) throws Exception {
Calculator calculator = new Calculator();
Class<Calculator> clazz = Calculator.class;
Method addMethod = clazz.getMethod("add", int.class, int.class);
Object result1 = addMethod.invoke(calculator, 10, 20);
System.out.println("10 + 20 = " + result1);
Method subtractMethod = clazz.getDeclaredMethod("subtract", int.class, int.class);
subtractMethod.setAccessible(true);
Object result2 = subtractMethod.invoke(calculator, 30, 10);
System.out.println("30 - 10 = " + result2);
Method multiplyMethod = clazz.getMethod("multiply", int.class, int.class);
Object result3 = multiplyMethod.invoke(null, 5, 6);
System.out.println("5 * 6 = " + result3);
}
}
六、实战案例
6.1 通用对象打印器
import java.lang.reflect.Field;
public class ObjectPrinter {
public static void print(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
System.out.println("=== " + clazz.getSimpleName() + " ===");
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String name = field.getName();
Object value = field.get(obj);
System.out.println(name + " = " + value);
}
}
public static void main(String[] args) throws Exception {
Person person = new Person("张三", 25);
print(person);
}
}
6.2 简单依赖注入
import java.lang.annotation.*;
import java.lang.reflect.Field;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Inject {}
class UserService {
public void save() { System.out.println("保存用户"); }
}
class UserController {
@Inject
private UserService userService;
public void register() { userService.save(); }
}
public class SimpleContainer {
public static void inject(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Inject.class)) {
field.setAccessible(true);
Class<?> fieldType = field.getType();
Object instance = fieldType.getDeclaredConstructor().newInstance();
field.set(obj, instance);
}
}
}
public static void main(String[] args) throws Exception {
UserController controller = new UserController();
inject(controller);
controller.register();
}
}
6.3 通用对象复制
import java.lang.reflect.Field;
public class ObjectCopier {
public static <T> T copy(T source) throws Exception {
Class<?> clazz = source.getClass();
@SuppressWarnings("unchecked")
T target = (T) clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object value = field.get(source);
field.set(target, value);
}
return target;
}
public static void main(String[] args) throws Exception {
Person original = new Person("张三", 25);
Person copied = copy(original);
System.out.println("原对象:" + original);
System.out.println("复制对象:" + copied);
System.out.println("是否同一对象:" + (original == copied));
}
}
6.4 动态代理
import java.lang.reflect.*;
interface UserDao {
void save();
void delete();
}
class UserDaoImpl implements UserDao {
@Override
public void save() { System.out.println("保存用户"); }
@Override
public void delete() { System.out.println("删除用户"); }
}
class LogHandler implements InvocationHandler {
private Object target;
public LogHandler(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) {
UserDao userDao = new UserDaoImpl();
UserDao proxy = (UserDao) Proxy.newProxyInstance(
userDao.getClass().getClassLoader(),
userDao.getClass().getInterfaces(),
new LogHandler(userDao)
);
proxy.save();
System.out.println();
proxy.delete();
}
}
6.5 配置文件加载
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Properties;
class AppConfig {
private String appName;
private int port;
private String database;
}
public class ConfigLoader {
public static <T> T load(Class<T> clazz, String configFile) throws Exception {
Properties props = new Properties();
InputStream is = clazz.getClassLoader().getResourceAsStream(configFile);
props.load(is);
T instance = clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String key = field.getName();
String value = props.getProperty(key);
if (value != null) {
if (field.getType() == int.class) {
field.set(instance, Integer.parseInt(value));
} else if (field.getType() == String.class) {
field.set(instance, value);
}
}
}
return instance;
}
}
七、高级应用
7.1 泛型信息获取
import java.lang.reflect.*;
import java.util.*;
public class GenericDemo {
private List<String> stringList;
private Map<String, Integer> map;
public List<String> getList() { return null; }
public <T> T genericMethod(T param) { return param; }
public static void main(String[] args) throws Exception {
Class<GenericDemo> clazz = GenericDemo.class;
Field field = clazz.getDeclaredField("stringList");
Type genericType = field.getGenericType();
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
System.out.println("原始类型:" + pt.getRawType());
Type[] actualTypes = pt.getActualTypeArguments();
System.out.println("泛型参数:" + actualTypes[0]);
}
Field mapField = clazz.getDeclaredField("map");
Type mapType = mapField.getGenericType();
if (mapType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) mapType;
Type[] types = pt.getActualTypeArguments();
System.out.println("Key 类型:" + types[0]);
System.out.println("Value 类型:" + types[1]);
}
Method method = clazz.getMethod("getList");
Type returnType = method.getGenericReturnType();
System.out.println("返回类型:" + returnType);
Method genericMethod = clazz.getMethod("genericMethod", Object.class);
TypeVariable<?>[] typeParams = genericMethod.getTypeParameters();
for (TypeVariable<?> tv : typeParams) {
System.out.println("泛型参数名:" + tv.getName());
}
}
}
7.2 数组操作
import java.lang.reflect.Array;
public class ArrayReflectionDemo {
public static void main(String[] args) {
Object intArray = Array.newInstance(int.class, 5);
Array.set(intArray, 0, 10);
Array.set(intArray, 1, 20);
System.out.println("第一个元素:" + Array.get(intArray, 0));
System.out.println("数组长度:" + Array.getLength(intArray));
Object multiArray = Array.newInstance(int.class, 3, 4);
System.out.println("多维数组类型:" + multiArray.getClass());
int[] oldArray = {1, 2, 3};
int[] newArray = (int[]) expandArray(oldArray, 6);
System.out.println("新数组长度:" + newArray.length);
System.out.println("第一个元素:" + newArray[0]);
}
public static Object expandArray(Object oldArray, int newLength) {
Class<?> componentType = oldArray.getClass().getComponentType();
int oldLength = Array.getLength(oldArray);
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(oldArray, 0, newArray, 0, Math.min(oldLength, newLength));
return newArray;
}
}
7.3 性能优化
import java.lang.reflect.*;
import java.util.concurrent.ConcurrentHashMap;
public class ReflectionCache {
private static final ConcurrentHashMap<String, Class<?>> classCache = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Method> methodCache = new ConcurrentHashMap<>();
private static final ConcurrentHashMap<String, Field> fieldCache = new ConcurrentHashMap<>();
public static Class<?> getClass(String className) throws ClassNotFoundException {
return classCache.computeIfAbsent(className, key -> {
try {
return Class.forName(key);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
});
}
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes) {
String key = clazz.getName() + "#" + methodName;
return methodCache.computeIfAbsent(key, k -> {
try {
Method method = clazz.getMethod(methodName, paramTypes);
method.setAccessible(true);
return method;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
public static Field getField(Class<?> clazz, String fieldName) {
String key = clazz.getName() + "#" + fieldName;
return fieldCache.computeIfAbsent(key, k -> {
try {
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
return field;
} catch (NoSuchFieldException e) {
throw new RuntimeException(e);
}
});
}
public static void main(String[] args) throws Exception {
Class<String> clazz = String.class;
Method method = clazz.getMethod("length");
long start1 = System.nanoTime();
for (int i = 0; i < 100000; i++) {
Method m = clazz.getMethod("length");
}
long end1 = System.nanoTime();
System.out.println("不使用缓存:" + (end1 - start1) / 1000000 + "ms");
long start2 = System.nanoTime();
for (int i = 0; i < 100000; i++) {
Method m = getMethod(clazz, "length");
}
long end2 = System.nanoTime();
System.out.println("使用缓存:" + (end2 - start2) / 1000000 + "ms");
}
}
7.4 方法句柄(MethodHandle)
MethodHandle 是 JDK 7 引入的,性能比反射更好。
import java.lang.invoke.*;
public class MethodHandleDemo {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(int.class);
MethodHandle mh = lookup.findVirtual(String.class, "length", mt);
String str = "Hello";
int length = (int) mh.invoke(str);
System.out.println("字符串长度:" + length);
performanceTest();
}
public static void performanceTest() throws Throwable {
String str = "Hello World";
int iterations = 10000000;
long start1 = System.nanoTime();
for (int i = 0; i < iterations; i++) {
int len = str.length();
}
long end1 = System.nanoTime();
System.out.println("直接调用:" + (end1 - start1) / 1000000 + "ms");
Method method = String.class.getMethod("length");
long start2 = System.nanoTime();
for (int i = 0; i < iterations; i++) {
int len = (int) method.invoke(str);
}
long end2 = System.nanoTime();
System.out.println("反射调用:" + (end2 - start2) / 1000000 + "ms");
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodType mt = MethodType.methodType(int.class);
MethodHandle mh = lookup.findVirtual(String.class, "length", mt);
long start3 = System.nanoTime();
for (int i = 0; i < iterations; i++) {
int len = (int) mh.invoke(str);
}
long end3 = System.nanoTime();
System.out.println("MethodHandle: " + (end3 - start3) / 1000000 + "ms");
}
}
- 直接调用:~5ms
- MethodHandle:~50ms(比直接调用慢 10 倍)
- 反射调用:~500ms(比直接调用慢 100 倍)
7.5 简单 ORM 框架
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.sql.*;
import java.util.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Table {
String value();
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface Column {
String value();
}
@Table("users")
class User {
@Column("id")
private Long id;
@Column("username")
private String username;
@Column("age")
private Integer age;
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public String getUsername() { return username; }
public void setUsername(String username) { this.username = username; }
public Integer getAge() { return age; }
public void setAge(Integer age) { this.age = age; }
@Override
public String toString() {
return "User{id=" + id + ", username='" + username + "', age=" + age + "}";
}
}
public class SimpleORM {
public static <T> List<T> findAll(Class<T> clazz, Connection conn) throws Exception {
Table table = clazz.getAnnotation(Table.class);
String tableName = table.value();
String sql = "SELECT * FROM " + tableName;
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(sql);
List<T> results = new ArrayList<>();
while (rs.next()) {
T obj = mapResultSet(clazz, rs);
results.add(obj);
}
rs.close();
stmt.close();
return results;
}
public static <T> T findById(Class<T> clazz, Connection conn, Object id) throws Exception {
Table table = clazz.getAnnotation(Table.class);
String tableName = table.value();
String sql = "SELECT * FROM " + tableName + " WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(sql);
pstmt.setObject(1, id);
ResultSet rs = pstmt.executeQuery();
T result = null;
if (rs.next()) {
result = mapResultSet(clazz, rs);
}
rs.close();
pstmt.close();
return result;
}
public static <T> void insert(T obj, Connection conn) throws Exception {
Class<?> clazz = obj.getClass();
Table table = clazz.getAnnotation(Table.class);
String tableName = table.value();
StringBuilder columns = new StringBuilder();
StringBuilder values = new StringBuilder();
List<Object> params = new ArrayList<>();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
field.setAccessible(true);
Object value = field.get(obj);
if (value != null) {
if (columns.length() > 0) {
columns.append(", ");
values.append(", ");
}
columns.append(column.value());
values.append("?");
params.add(value);
}
}
}
String sql = "INSERT INTO " + tableName + " (" + columns + ") VALUES (" + values + ")";
PreparedStatement pstmt = conn.prepareStatement(sql);
for (int i = 0; i < params.size(); i++) {
pstmt.setObject(i + 1, params.get(i));
}
pstmt.executeUpdate();
pstmt.close();
}
private static <T> T mapResultSet(Class<T> clazz, ResultSet rs) throws Exception {
T obj = clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (field.isAnnotationPresent(Column.class)) {
Column column = field.getAnnotation(Column.class);
String columnName = column.value();
field.setAccessible(true);
Object value = rs.getObject(columnName);
field.set(obj, value);
}
}
return obj;
}
public static void main(String[] args) {
System.out.println("简单 ORM 框架示例");
System.out.println("使用反射实现对象关系映射");
}
}
7.6 Bean 拷贝工具
import java.lang.reflect.Field;
import java.util.*;
public class BeanUtils {
public static <T> T shallowCopy(T source) throws Exception {
if (source == null) return null;
@SuppressWarnings("unchecked")
Class<T> clazz = (Class<T>) source.getClass();
T target = clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
Object value = field.get(source);
field.set(target, value);
}
return target;
}
public static void copyProperties(Object source, Object target) throws Exception {
if (source == null || target == null) return;
Class<?> sourceClass = source.getClass();
Class<?> targetClass = target.getClass();
Field[] sourceFields = sourceClass.getDeclaredFields();
Map<String, Field> targetFieldMap = new HashMap<>();
for (Field field : targetClass.getDeclaredFields()) {
targetFieldMap.put(field.getName(), field);
}
for (Field sourceField : sourceFields) {
String fieldName = sourceField.getName();
Field targetField = targetFieldMap.get(fieldName);
if (targetField != null && sourceField.getType() == targetField.getType()) {
sourceField.setAccessible(true);
targetField.setAccessible(true);
Object value = sourceField.get(source);
targetField.set(target, value);
}
}
}
public static Map<String, Object> toMap(Object obj) throws Exception {
if (obj == null) return null;
Map<String, Object> map = new HashMap<>();
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String key = field.getName();
Object value = field.get(obj);
map.put(key, value);
}
return map;
}
public static <T> T fromMap(Map<String, Object> map, Class<T> clazz) throws Exception {
if (map == null) return null;
T obj = clazz.getDeclaredConstructor().newInstance();
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
field.setAccessible(true);
String key = field.getName();
Object value = map.get(key);
if (value != null) {
field.set(obj, value);
}
}
return obj;
}
public static void main(String[] args) throws Exception {
User user = new User();
user.setId(1L);
user.setUsername("张三");
user.setAge(25);
Map<String, Object> map = toMap(user);
System.out.println("对象转 Map: " + map);
User user2 = fromMap(map, User.class);
System.out.println("Map 转对象:" + user2);
}
}
八、最佳实践与注意事项
8.1 性能优化建议
public class ReflectionBestPractices {
public void badPractice(Object obj) throws Exception {
for (int i = 0; i < 10000; i++) {
Method method = obj.getClass().getMethod("toString");
method.invoke(obj);
}
}
private static final Map<String, Method> methodCache = new ConcurrentHashMap<>();
public void goodPractice(Object obj) throws Exception {
Method method = methodCache.computeIfAbsent(
obj.getClass().getName() + "#toString",
k -> {
try {
Method m = obj.getClass().getMethod("toString");
m.setAccessible(true);
return m;
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
}
);
for (int i = 0; i < 10000; i++) {
method.invoke(obj);
}
}
}
- 缓存 Class、Method、Field、Constructor 对象
- 提前调用
setAccessible(true),避免每次检查
- 使用 MethodHandle 替代反射(JDK 7+)
- 避免在循环中使用反射
- 考虑使用字节码生成(如 ASM、Javassist)
8.2 异常处理
public class ReflectionExceptionHandling {
public static Object invokeMethod(Object obj, String methodName, Object... args) {
try {
Class<?> clazz = obj.getClass();
Class<?>[] paramTypes = new Class[args.length];
for (int i = 0; i < args.length; i++) {
paramTypes[i] = args[i].getClass();
}
Method method = clazz.getMethod(methodName, paramTypes);
return method.invoke(obj, args);
} catch (NoSuchMethodException e) {
System.err.println("方法不存在:" + methodName);
} catch (IllegalAccessException e) {
System.err.println("无法访问方法:" + methodName);
} catch (InvocationTargetException e) {
System.err.println("方法执行异常:" + e.getCause());
} catch (Exception e) {
System.err.println("未知异常:" + e.getMessage());
}
return null;
}
}
| 异常 | 说明 | 解决方案 |
|---|
ClassNotFoundException | 类不存在 | 检查类名和类路径 |
NoSuchMethodException | 方法不存在 | 检查方法名和参数类型 |
NoSuchFieldException | 字段不存在 | 检查字段名 |
IllegalAccessException | 无法访问 | 使用 setAccessible(true) |
InvocationTargetException | 方法执行异常 | 获取 getCause() 查看真实异常 |
InstantiationException | 无法实例化 | 确保有无参构造方法 |
8.3 安全性考虑
public class ReflectionSecurity {
public static void main(String[] args) {
SecurityManager sm = System.getSecurityManager();
if (sm != null) {
try {
sm.checkPermission(new ReflectPermission("suppressAccessChecks"));
} catch (SecurityException e) {
System.err.println("没有反射权限");
return;
}
}
try {
Field field = SomeClass.class.getDeclaredField("privateField");
field.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
String className = getUserInput();
if (!isValidClassName(className)) {
throw new IllegalArgumentException("非法类名");
}
}
private static boolean isValidClassName(String className) {
return className.matches("^[a-zA-Z0-9.]+$");
}
private static String getUserInput() {
return "com.example.MyClass";
}
static class SomeClass {
private String privateField;
}
}
- 不要反射用户输入的类名(可能导致代码注入)
- 谨慎使用
setAccessible(true)
- 在生产环境考虑禁用反射
- 使用白名单验证类名和方法名
- 注意 SecurityManager 的限制
8.4 线程安全
public class ReflectionThreadSafety {
private static final ConcurrentHashMap<String, Method> cache = new ConcurrentHashMap<>();
public static Method getMethod(Class<?> clazz, String methodName) throws NoSuchMethodException {
String key = clazz.getName() + "#" + methodName;
return cache.computeIfAbsent(key, k -> {
try {
return clazz.getMethod(methodName);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
}
});
}
}
8.5 常见陷阱
public class ReflectionPitfalls {
public static void main(String[] args) throws Exception {
Method method1 = MyClass.class.getMethod("method", int.class);
Method arrayMethod = MyClass.class.getMethod("arrayMethod", int[].class);
Method varargMethod = MyClass.class.getMethod("varargMethod", String[].class);
Constructor<MyClass> constructor = MyClass.class.getDeclaredConstructor();
constructor.setAccessible(true);
MyClass obj = constructor.newInstance();
Field staticField = MyClass.class.getDeclaredField("staticField");
staticField.set(null, "new value");
Method staticMethod = MyClass.class.getMethod("staticMethod");
staticMethod.invoke(null);
}
static class MyClass {
private static String staticField = "old value";
private MyClass() {}
public void method(int param) {}
public void arrayMethod(int[] array) {}
public void varargMethod(String... args) {}
public static void staticMethod() {}
}
}
九、快速参考
反射核心类
| 类 | 说明 | 主要方法 |
|---|
Class | 类的字节码对象 | forName(), newInstance(), getMethod() |
Constructor | 构造方法 | newInstance(), setAccessible() |
Field | 字段 | get(), set(), setAccessible() |
Method | 方法 | invoke(), setAccessible() |
Modifier | 修饰符工具类 | isPublic(), isStatic(), isFinal() |
常用方法对比
| 方法 | 说明 | 访问范围 |
|---|
getXxx() | 获取 public 成员 | public(含继承) |
getDeclaredXxx() | 获取所有成员 | 所有(不含继承) |
获取 Class 对象的三种方式
Class<String> clazz1 = String.class;
String str = "Hello";
Class<? extends String> clazz2 = str.getClass();
Class<?> clazz3 = Class.forName("java.lang.String");
反射操作速查表
| 操作 | 代码示例 |
|---|
| 创建对象 | clazz.getDeclaredConstructor().newInstance() |
| 获取字段值 | field.get(obj) |
| 设置字段值 | field.set(obj, value) |
| 调用方法 | method.invoke(obj, args) |
| 访问私有成员 | member.setAccessible(true) |
| 判断类型 | clazz.isAssignableFrom(subClass) |
| 获取注解 | clazz.getAnnotation(Ann.class) |
| 获取泛型 | field.getGenericType() |
性能对比(参考值)
| 操作方式 | 相对性能 | 适用场景 |
|---|
| 直接调用 | 1x(基准) | 常规开发 |
| MethodHandle | 10x | 需要动态调用且关注性能 |
| 反射(有缓存) | 50x | 框架开发 |
| 反射(无缓存) | 100x | 避免使用 |
最佳实践清单
- 仅在必要时使用反射(框架、工具类)
- 缓存 Class、Method、Field 对象
- 提前调用
setAccessible(true)
- 使用 try-catch 处理所有可能的异常
- 考虑使用 MethodHandle 替代反射
- 在性能敏感场景避免使用反射
- 使用白名单验证类名和方法名
- 充分的代码注释和文档
- 不要在循环中频繁使用反射
- 不要反射用户输入的类名(安全风险)
- 不要忽略异常处理
- 不要在性能关键路径使用反射
- 不要过度使用
setAccessible(true)
- 不要在多线程环境使用非线程安全的缓存
十、总结
1. 反射概述
- 反射的定义和核心功能
- 应用场景(框架开发、动态代理、注解处理)
- 优缺点分析
2. 获取 Class 对象
- 三种获取方式及使用场景
- 获取类信息(名称、包、父类、接口、修饰符)
- 类型判断和继承关系检查
- 注解获取
3. 构造方法反射
- 获取构造方法(public 和 private)
- 创建对象的多种方式
- 访问私有构造方法
4. 字段反射
5. 方法反射
- 获取方法信息
- 调用方法(public、private、static)
- 方法参数和返回值处理
6. 实战案例
- 通用对象打印器
- 简单依赖注入
- 通用对象复制
- 动态代理
- 配置文件加载
7. 高级应用
- 泛型信息获取
- 数组操作
- 性能优化(缓存机制)
- MethodHandle(性能更好的替代方案)
- 简单 ORM 框架
- Bean 拷贝工具
8. 最佳实践
- 性能优化建议
- 异常处理
- 安全性考虑
- 线程安全
- 常见陷阱
关键要点
| 主题 | 核心内容 |
|---|
| 获取 Class | 三种方式:类名.class、对象.getClass()、Class.forName() |
| 访问控制 | 使用 setAccessible(true) 访问私有成员 |
| 性能优化 | 缓存反射对象,考虑使用 MethodHandle |
| 异常处理 | 处理多种反射异常,获取真实异常原因 |
| 安全性 | 验证输入,谨慎使用 setAccessible |
| 线程安全 | 使用 ConcurrentHashMap 缓存 |
使用场景
| 场景 | 是否使用反射 | 说明 |
|---|
| 框架开发 | ✅ 推荐 | Spring、Hibernate 等 |
| 动态代理 | ✅ 推荐 | AOP 实现 |
| 注解处理 | ✅ 推荐 | 运行时处理注解 |
| 插件系统 | ✅ 推荐 | 动态加载类 |
| 序列化 | ✅ 推荐 | JSON、XML 转换 |
| 常规业务 | ❌ 不推荐 | 性能差,可读性差 |
| 性能敏感 | ❌ 不推荐 | 使用直接调用 |
学习路径
- 入门阶段:掌握 Class 对象获取、基本的字段和方法操作
- 进阶阶段:理解构造方法、注解、泛型信息获取
- 高级阶段:掌握性能优化、MethodHandle、动态代理
- 实战阶段:实现简单框架(依赖注入、ORM)
推荐资源
下一步
- 深入学习动态代理(JDK 动态代理、CGLIB)
- 了解字节码操作(ASM、Javassist、ByteBuddy)
- 学习注解处理器(APT)
- 研究主流框架的反射应用(Spring、MyBatis)
- 实践:开发一个简单的依赖注入容器
记住:反射是一把双刃剑,强大但有代价。在追求灵活性的同时,要权衡性能、安全性和代码可维护性。只在真正需要的地方使用反射,并遵循最佳实践。
相关免费在线工具
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
- Base64 字符串编码/解码
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
- Base64 文件转换器
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online