一、反射机制原理
1. 核心原理
反射允许程序在运行时动态地:
-
获取类的完整信息(类名、方法、字段、注解等)
-
创建对象实例
-
调用方法和访问字段
-
修改字段值(包括私有字段)
2. 实现机制
java
复制
下载
// Class对象的获取方式
// 1. 类名.class
Class<?> clazz1 = String.class;
// 2. 对象.getClass()
String str = "hello";
Class<?> clazz2 = str.getClass();
// 3. Class.forName() - 最常用
Class<?> clazz3 = Class.forName("java.lang.String");
// 4. ClassLoader加载
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?> clazz4 = loader.loadClass("java.lang.String");
3. 类加载与Class对象
java
复制
下载
// Class对象包含类的元数据
public final class Class<T> {
// 类名
private final String name;
// 方法信息
private Method[] methods;
// 字段信息
private Field[] fields;
// 构造方法
private Constructor<?>[] constructors;
// 注解信息
private Annotation[] annotations;
// 父类
private Class<? super T> superclass;
}
4. 内存模型
text
复制
下载
JVM方法区(元空间) 堆内存
┌─────────────────┐ ┌─────────────┐
│ 类的元数据 │ │ Class对象 │
│ - 方法字节码 │◄───│ - 指向元数据 │
│ - 字段描述符 │ │ - 静态变量 │
│ - 常量池 │ └─────────────┘
└─────────────────┘ ▲
▲ │
│ ┌───┴────┐
│ │ 实例对象 │
└──────────────────┤ - 字段值 │
└─────────┘
二、核心API详解
1. 获取类信息
java
复制
下载
Class<?> clazz = User.class;
// 获取类名
String className = clazz.getName(); // 全限定名: "com.example.User"
String simpleName = clazz.getSimpleName(); // 简单名: "User"
// 获取包信息
Package pkg = clazz.getPackage();
String packageName = pkg.getName();
// 获取修饰符
int modifiers = clazz.getModifiers();
Modifier.isPublic(modifiers); // 是否是public
Modifier.isAbstract(modifiers); // 是否是abstract
// 获取父类和接口
Class<?> superClass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();
// 获取注解
Annotation[] annotations = clazz.getAnnotations();
MyAnnotation myAnno = clazz.getAnnotation(MyAnnotation.class);
2. 操作字段(Field)
java
复制
下载
class User {
private String name;
public int age;
private static final String COUNTRY = "China";
}
// 获取字段
Field[] fields = clazz.getDeclaredFields(); // 所有字段(包括private)
Field publicField = clazz.getField("age"); // 仅public字段
// 获取/设置字段值
User user = new User();
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 突破private限制
// 读取值
Object value = nameField.get(user); // 获取name值
// 设置值
nameField.set(user, "张三"); // 设置name为"张三"
// 静态字段操作
Field countryField = clazz.getDeclaredField("COUNTRY");
countryField.setAccessible(true);
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(countryField, countryField.getModifiers() & ~Modifier.FINAL);
countryField.set(null, "USA"); // 修改final静态字段(不推荐)
3. 操作方法(Method)
java
复制
下载
class Calculator {
public int add(int a, int b) { return a + b; }
private void log(String message) { System.out.println(message); }
}
// 获取方法
Method[] methods = clazz.getDeclaredMethods();
Method addMethod = clazz.getDeclaredMethod("add", int.class, int.class);
Method logMethod = clazz.getDeclaredMethod("log", String.class);
// 调用方法
Calculator calc = new Calculator();
Object result = addMethod.invoke(calc, 10, 20); // 返回30
// 调用私有方法
logMethod.setAccessible(true);
logMethod.invoke(calc, "test message");
// 静态方法调用
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
staticMethod.invoke(null); // 静态方法传null
4. 操作构造器(Constructor)
java
复制
下载
class Person {
private String name;
private int age;
public Person() {}
private Person(String name) { this.name = name; }
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
// 获取构造器
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
Constructor<?> defaultConstructor = clazz.getDeclaredConstructor(); // 无参
Constructor<?> paramConstructor = clazz.getDeclaredConstructor(String.class, int.class);
// 创建实例
Person p1 = (Person) defaultConstructor.newInstance();
Person p2 = (Person) paramConstructor.newInstance("张三", 25);
// 创建私有构造器的实例
Constructor<?> privateConstructor = clazz.getDeclaredConstructor(String.class);
privateConstructor.setAccessible(true);
Person p3 = (Person) privateConstructor.newInstance("李四");
三、应用场景
1. 框架开发(最常用)
java
复制
下载
// Spring IOC容器实现原理示例
public class SimpleContainer {
private Map<String, Object> beans = new HashMap<>();
public void register(Class<?> clazz) throws Exception {
// 通过注解获取Bean名称
Component anno = clazz.getAnnotation(Component.class);
String beanName = anno != null ? anno.value() :
clazz.getSimpleName().toLowerCase();
// 创建实例(支持依赖注入)
Object bean = createInstance(clazz);
beans.put(beanName, bean);
}
private Object createInstance(Class<?> clazz) throws Exception {
// 1. 获取所有构造方法
Constructor<?>[] constructors = clazz.getConstructors();
// 2. 选择@Autowired标注的构造方法
for (Constructor<?> constructor : constructors) {
if (constructor.isAnnotationPresent(Autowired.class)) {
// 3. 获取参数类型
Class<?>[] paramTypes = constructor.getParameterTypes();
Object[] params = new Object[paramTypes.length];
// 4. 递归创建参数实例
for (int i = 0; i < paramTypes.length; i++) {
params[i] = getBean(paramTypes[i]);
}
// 5. 创建实例
return constructor.newInstance(params);
}
}
// 6. 使用默认构造方法
return clazz.newInstance();
}
}
// 使用注解定义Bean
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@interface Component {
String value() default "";
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.CONSTRUCTOR)
@interface Autowired {}
2. 动态代理
java
复制
下载
// JDK动态代理(基于接口)
public class DynamicProxyHandler implements InvocationHandler {
private Object target;
public DynamicProxyHandler(Object target) {
this.target = target;
}
public Object createProxy() {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(),
this
);
}
@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;
}
}
// 使用示例
interface UserService {
void save(User user);
}
class UserServiceImpl implements UserService {
public void save(User user) {
// 业务逻辑
}
}
// 创建代理
UserService proxy = (UserService) new DynamicProxyHandler(new UserServiceImpl()).createProxy();
proxy.save(user);
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
3. 注解处理器
java
复制
下载
// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@interface LogExecutionTime {
}
// 注解处理器
public class LogExecutionTimeProcessor {
public static void process(Object obj) throws Exception {
Class<?> clazz = obj.getClass();
for (Method method : clazz.getDeclaredMethods()) {
if (method.isAnnotationPresent(LogExecutionTime.class)) {
// 创建代理方法
Method originalMethod = method;
// 动态生成代理类(简化示例)
System.out.println("Logging execution time for: " + method.getName());
long startTime = System.currentTimeMillis();
originalMethod.invoke(obj);
long endTime = System.currentTimeMillis();
System.out.println("Execution time: " + (endTime - startTime) + "ms");
}
}
}
}
// 使用
class Service {
@LogExecutionTime
public void processData() {
// 耗时操作
}
}
4. 配置文件映射
java
复制
下载
// properties文件:config.properties
// database.url=jdbc:mysql://localhost:3306/test
// database.username=root
// database.password=123456
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@interface ConfigValue {
String key();
}
class DatabaseConfig {
@ConfigValue(key = "database.url")
private String url;
@ConfigValue(key = "database.username")
private String username;
@ConfigValue(key = "database.password")
private String password;
// getters and setters
}
public class ConfigLoader {
public static <T> T load(Class<T> clazz, Properties props) throws Exception {
T instance = clazz.newInstance();
for (Field field : clazz.getDeclaredFields()) {
ConfigValue annotation = field.getAnnotation(ConfigValue.class);
if (annotation != null) {
String value = props.getProperty(annotation.key());
if (value != null) {
field.setAccessible(true);
// 类型转换
Object convertedValue = convertValue(value, field.getType());
field.set(instance, convertedValue);
}
}
}
return instance;
}
private static Object convertValue(String value, Class<?> targetType) {
if (targetType == String.class) return value;
if (targetType == int.class || targetType == Integer.class)
return Integer.parseInt(value);
if (targetType == boolean.class || targetType == Boolean.class)
return Boolean.parseBoolean(value);
// 其他类型转换...
return value;
}
}
5. ORM框架
java
复制
下载
// 简易ORM实现
public class SimpleORM {
public static <T> T queryOne(Class<T> clazz, String sql, Object... params) throws Exception {
// 执行SQL获取ResultSet
ResultSet rs = executeQuery(sql, params);
if (rs.next()) {
T instance = clazz.newInstance();
// 获取ResultSetMetaData
ResultSetMetaData metaData = rs.getMetaData();
int columnCount = metaData.getColumnCount();
for (int i = 1; i <= columnCount; i++) {
String columnName = metaData.getColumnLabel(i);
Object columnValue = rs.getObject(i);
// 使用反射设置字段值
Field field = findField(clazz, columnName);
if (field != null) {
field.setAccessible(true);
field.set(instance, columnValue);
}
}
return instance;
}
return null;
}
private static Field findField(Class<?> clazz, String columnName) {
// 支持驼峰转换:user_name -> userName
String fieldName = toCamelCase(columnName);
try {
return clazz.getDeclaredField(fieldName);
} catch (NoSuchFieldException e) {
// 查找父类字段
if (clazz.getSuperclass() != null) {
return findField(clazz.getSuperclass(), columnName);
}
return null;
}
}
}
6. 插件化系统
java
复制
下载
// 插件接口
interface Plugin {
void execute();
String getName();
}
// 插件管理器
public class PluginManager {
private List<Plugin> plugins = new ArrayList<>();
public void loadPlugins(String pluginDir) throws Exception {
File dir = new File(pluginDir);
for (File jarFile : dir.listFiles((d, name) -> name.endsWith(".jar"))) {
// 创建自定义ClassLoader
URLClassLoader loader = new URLClassLoader(
new URL[]{jarFile.toURI().toURL()},
getClass().getClassLoader()
);
// 读取插件配置
Properties props = new Properties();
try (InputStream is = loader.getResourceAsStream("plugin.properties")) {
props.load(is);
}
String className = props.getProperty("main.class");
if (className != null) {
// 加载插件类
Class<?> pluginClass = loader.loadClass(className);
// 检查是否实现了Plugin接口
if (Plugin.class.isAssignableFrom(pluginClass)) {
Plugin plugin = (Plugin) pluginClass.newInstance();
plugins.add(plugin);
}
}
}
}
public void executeAll() {
for (Plugin plugin : plugins) {
plugin.execute();
}
}
}
7. 序列化/反序列化
java
复制
下载
public class JsonSerializer {
public static String toJson(Object obj) throws IllegalAccessException {
if (obj == null) return "null";
Class<?> clazz = obj.getClass();
// 处理基本类型
if (clazz.isPrimitive() || isWrapperType(clazz)) {
return obj.toString();
}
// 处理String
if (clazz == String.class) {
return "\"" + obj + "\"";
}
// 处理数组
if (clazz.isArray()) {
return arrayToJson(obj);
}
// 处理集合
if (Collection.class.isAssignableFrom(clazz)) {
return collectionToJson((Collection<?>) obj);
}
// 处理Map
if (Map.class.isAssignableFrom(clazz)) {
return mapToJson((Map<?, ?>) obj);
}
// 处理普通对象
return objectToJson(obj);
}
private static String objectToJson(Object obj) throws IllegalAccessException {
StringBuilder json = new StringBuilder("{");
Class<?> clazz = obj.getClass();
Field[] fields = clazz.getDeclaredFields();
boolean first = true;
for (Field field : fields) {
field.setAccessible(true);
Object value = field.get(obj);
if (value != null) {
if (!first) json.append(",");
json.append("\"").append(field.getName()).append("\":")
.append(toJson(value));
first = false;
}
}
json.append("}");
return json.toString();
}
}
四、性能考虑
1. 性能对比
java
复制
下载
// 直接调用 vs 反射调用
public class PerformanceTest {
private static final int LOOP_COUNT = 1000000;
public void directCall() {
User user = new User();
for (int i = 0; i < LOOP_COUNT; i++) {
user.getName(); // 直接调用
}
}
public void reflectionCall() throws Exception {
User user = new User();
Class<?> clazz = user.getClass();
Method method = clazz.getMethod("getName");
for (int i = 0; i < LOOP_COUNT; i++) {
method.invoke(user); // 反射调用
}
}
public void optimizedReflection() throws Exception {
User user = new User();
Class<?> clazz = user.getClass();
Method method = clazz.getMethod("getName");
method.setAccessible(true); // 关闭访问检查
for (int i = 0; i < LOOP_COUNT; i++) {
method.invoke(user);
}
}
}
篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc
需要全套面试笔记及答案
【点击此处即可/免费获取】
2. 性能优化方案
java
复制
下载
// 1. 缓存Class对象和Method对象
class MethodCache {
private static final Map<String, Method> METHOD_CACHE = new ConcurrentHashMap<>();
public static Method getMethod(Class<?> clazz, String methodName, Class<?>... paramTypes)
throws NoSuchMethodException {
String key = clazz.getName() + "#" + methodName;
return METHOD_CACHE.computeIfAbsent(key, k -> {
try {
Method method = clazz.getMethod(methodName, paramTypes);
method.setAccessible(true);
return method;
} catch (Exception e) {
throw new RuntimeException(e);
}
});
}
}
// 2. 使用MethodHandle(Java 7+)
public class MethodHandleExample {
public static void main(String[] args) throws Throwable {
MethodHandles.Lookup lookup = MethodHandles.lookup();
MethodHandle mh = lookup.findVirtual(String.class, "length",
MethodType.methodType(int.class));
String str = "hello";
int length = (int) mh.invokeExact(str); // 性能接近直接调用
}
}
// 3. 使用Unsafe(不推荐,绕过安全检查)
public class UnsafeExample {
private static final Unsafe unsafe;
private static final long nameOffset;
static {
try {
Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");
theUnsafe.setAccessible(true);
unsafe = (Unsafe) theUnsafe.get(null);
nameOffset = unsafe.objectFieldOffset(User.class.getDeclaredField("name"));
} catch (Exception e) {
throw new Error(e);
}
}
public static void setUserName(User user, String name) {
unsafe.putObject(user, nameOffset, name);
}
}
五、安全考虑
1. 安全管理器
java
复制
下载
// 启用安全管理器限制反射
System.setSecurityManager(new SecurityManager() {
@Override
public void checkMemberAccess(Class<?> clazz, int which) {
// 禁止访问私有成员
if (which == Member.PRIVATE) {
throw new SecurityException("Access to private members is not allowed");
}
}
@Override
public void checkPackageAccess(String pkg) {
// 禁止访问某些包
if (pkg.startsWith("sun.") || pkg.startsWith("com.sun.")) {
throw new SecurityException("Access to " + pkg + " is not allowed");
}
}
});
2. 安全使用建议
java
复制
下载
// 1. 避免过度使用setAccessible(true)
Field field = clazz.getDeclaredField("secret");
// field.setAccessible(true); // 谨慎使用
// 2. 限制可反射的类
public class SafeReflection {
private static final Set<String> ALLOWED_CLASSES = new HashSet<>(
Arrays.asList("com.example.User", "com.example.Product")
);
public static Class<?> safeForName(String className) throws ClassNotFoundException {
if (!ALLOWED_CLASSES.contains(className)) {
throw new SecurityException("Class not allowed: " + className);
}
return Class.forName(className);
}
}
// 3. 使用MethodHandles.Lookup限制访问
MethodHandles.Lookup lookup = MethodHandles.privateLookupIn(
TargetClass.class, MethodHandles.lookup()
);
// 只能访问TargetClass的私有成员
六、总结
适用场景:
-
框架开发:Spring、MyBatis等
-
动态代理:AOP、RPC、事务管理
-
注解处理:自定义注解处理器
-
配置文件映射:属性文件到对象的映射
-
ORM框架:数据库结果集到对象的映射
-
插件系统:动态加载和执行插件
-
序列化:JSON/XML序列化
-
测试框架:JUnit的@Test注解处理
-
代码生成:动态生成类和方法
不适用场景:
-
性能敏感:高频调用的核心业务逻辑
-
简单场景:可以直接调用的简单方法
-
安全敏感:需要严格控制访问权限的场景
-
类型安全要求高:编译时类型检查重要的场景
最佳实践:
-
缓存反射对象:Class、Method、Field、Constructor
-
最小化setAccessible(true):只在必要时使用
-
考虑性能开销:避免在循环中使用反射
-
提供安全边界:限制可反射的类和成员
-
使用替代方案:考虑MethodHandle、LambdaMetafactory等
-
错误处理:妥善处理NoSuchMethodException等异常
反射是Java强大的特性,但也是一把双刃剑。合理使用可以极大提高代码的灵活性和可扩展性,滥用则会导致性能问题和安全隐患。