【震撼揭秘】 你是否曾想窥探Java类的内部结构? 是否好奇Spring框架如何实现"万物皆可注入"?✨ 本文将带你从反射小白晋升为反射高手,用一行代码透视任意类的构造方法、成员变量和私有方法!
/**
* Student类反射结构信息
*
* 由反射工具自动生成,展示类元数据
*/
public class com.my.reflect.Student extends java.lang.Object // 类声明
{
// 构造方法
public com.my.reflect.Student() // 默认构造器
public com.my.reflect.Student(java.lang.String, int) // 全参构造器
// 类方法
private void test(java.lang.String) // 私有工具方法
public java.lang.String getName() // 属性访问器
public void setName(java.lang.String) // 属性修改器
public int getAge()
public void setAge(int)
public java.lang.String toString() // 对象字符串表示
// 类字段
public java.lang.String name; // 姓名字段
public int age; // 年龄字段
}
反射(Reflection) 是Java在运行时(Runtime)动态获取类信息并操作类的能力。就像给Java装上了X光透视眼️️,无需知道类结构就能操作它。
动态加载未知类(如插件系统)
突破访问限制(调用私有方法)
⚙️ 框架设计的基石(Spring IOC/DI)
通用工具开发(如本文的类结构解析器)
操作 | 方法 | 示例 |
---|---|---|
获取Class对象 | Class.forName() |
Class> clazz = Class.forName("Student") |
获取构造方法 | getDeclaredConstructors() |
Constructor>[] cons = clazz.getDeclaredConstructors() |
获取方法 | getDeclaredMethods() |
Method[] methods = clazz.getDeclaredMethods() |
获取字段 | getDeclaredFields() |
Field[] fields = clazz.getDeclaredFields() |
获取修饰符 | Modifier.toString() |
String mods = Modifier.toString(method.getModifiers()) |
/**
* 学生实体类
*
* 注意:字段设计为public仅用于演示反射场景
* 实际项目中建议使用封装原则
*/
public class Student {
// 学生姓名(通常应私有化并通过getter访问)
public String name;
// 学生年龄(通常应私有化并通过getter访问)
public int age;
/** 默认构造器 - 框架操作需要 */
public Student() {}
/**
* 全参构造器
* @param name 学生姓名
* @param age 学生年龄
*/
public Student(String name, int age) {
this.name = name;
this.age = age;
}
/**
* 内部工具方法 - 反射测试用
* @param str 测试参数
*/
private void test(String str) {
System.out.println("私有方法被调用:" + str);
}
// 省略getter/setter和toString方法
}
步骤1:打印类声明
/**
* 打印类声明头信息
*
* 输出格式:`[修饰符] class [类全名] [继承关系] {`
*
* @param clazz 目标类的Class对象
*/
public static void printClassHeader(Class> clazz) {
// 获取直接父类(Object类特殊处理)
Class> superClass = clazz.getSuperclass();
// 构建继承关系信息(非Object类时添加extends)
String superInfo = (superClass != null && !Object.class.equals(superClass))
? " extends " + superClass.getName()
: "";
// 获取类修饰符(public/final/abstract等)
String modifiers = Modifier.toString(clazz.getModifiers());
// 输出格式化类声明
System.out.println(modifiers + " class " +
clazz.getName() +
superInfo +
"\n{");
}
步骤2:爆破构造函数
public static void printConstructors(Class> clazz) {
Constructor>[] constructors = clazz.getDeclaredConstructors();
for (Constructor> c : constructors) {
// 获取参数类型列表
Class>[] paramTypes = c.getParameterTypes();
String params = Arrays.stream(paramTypes)
.map(Class::getName)
.collect(Collectors.joining(", "));
System.out.println(" " +
Modifier.toString(c.getModifiers()) + " " +
c.getDeclaringClass().getName() + "(" + params + ")");
}
}
步骤3:捕获所有方法(包括私有!)
public static void printMethods(Class> clazz) {
Method[] methods = clazz.getDeclaredMethods();
for (Method m : methods) {
// 跳过合成方法(如内部类访问器)
if (m.isSynthetic()) continue;
String params = Arrays.stream(m.getParameterTypes())
.map(Class::getName)
.collect(Collectors.joining(", "));
System.out.println(" " +
Modifier.toString(m.getModifiers()) + " " +
m.getReturnType().getName() + " " +
m.getName() + "(" + params + ")");
}
}
步骤4:扫描所有字段
public static void printFields(Class> clazz) {
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
System.out.println(" " +
Modifier.toString(f.getModifiers()) + " " +
f.getType().getName() + " " +
f.getName() + ";");
}
}
// 获取私有方法
Method privateMethod = clazz.getDeclaredMethod("test", String.class);
// 突破访问限制(关键!)
privateMethod.setAccessible(true);
// 执行私有方法
privateMethod.invoke(new Student(), "反射太强了!");
// 获取带参构造
Constructor> constructor = clazz.getConstructor(String.class, int.class);
// 动态创建实例
Object instance = constructor.newInstance("反射专家", 25);
System.out.println(instance); // 输出:Student{name='反射专家', age=25}
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
// 突破final限制(JDK12+)
Field modifiersField = Field.class.getDeclaredField("modifiers");
modifiersField.setAccessible(true);
modifiersField.setInt(nameField, nameField.getModifiers() & ~Modifier.FINAL);
nameField.set(instance, "新名字"); // 成功修改final字段!
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;
public class ClassSpy {
public static void main(String[] args) throws Exception {
spyClass("com.my.reflect.Student");
}
public static void spyClass(String className) {
try {
Class> clazz = Class.forName(className);
printClassStructure(clazz);
} catch (Exception e) {
System.err.println("类加载失败: " + e.getMessage());
}
}
public static void printClassStructure(Class> clazz) {
// 打印类头
printClassHeader(clazz);
// 打印构造方法
System.out.println("\n // 构造方法");
printConstructors(clazz);
// 打印方法
System.out.println("\n // ⚙️方法");
printMethods(clazz);
// 打印字段
System.out.println("\n // 字段");
printFields(clazz);
System.out.println("}");
}
// 各打印方法实现见上文
}
// 1. ️缓存Class对象
private static final Map> CLASS_CACHE = new ConcurrentHashMap<>();
Class> clazz = CLASS_CACHE.computeIfAbsent(className, Class::forName);
// 2. 缓存Method对象
private static final Map METHOD_CACHE = new ConcurrentHashMap<>();
Method method = METHOD_CACHE.computeIfAbsent(methodName,
name -> clazz.getDeclaredMethod(name, paramTypes));
// 3. ⚡关闭安全检查(性能提升10倍!)
method.setAccessible(true);
避免频繁调用:反射比直接调用慢100倍
慎用setAccessible(true):破坏封装性
注意安全管理器:可能抛出SecurityException
❓处理NoSuchMethodException:方法不存在时要有降级方案
防范泛型擦除:反射无法获取运行时泛型类型
模块系统限制:JDK9+需要手动开放模块(opens)
尝试扩展以下功能:
递归打印父类成员
️ 解析方法上的注解信息
显示泛型签名
生成UML类图
实现简易IOC容器
反射是Java的元编程能力,掌握它等于拿到框架开发的通行证。本文从入门到高阶的实战技巧,已助你获得"透视"Java类的能力。接下来,是时候用反射创造你自己的黑科技了!
互动话题:你在项目中用过哪些反射黑科技?遇到过哪些坑?评论区见!