Java 反射

反射(Reflection)让程序在运行时"看穿"自己:动态获取类的构造方法、方法、字段,甚至直接调用它们。IDE 提示、Spring、MyBatis、JSON 序列化底层都离不开反射。

获取 Class 对象的三种方式

要反射一个类,先拿到它的 Class 对象。同一个类只对应一个 Class,三种方式结果相同:

// 方式一:对象.getClass()
String s = "hi";
Class<?> c1 = s.getClass();

// 方式二:类名.class
Class<?> c2 = String.class;

// 方式三:Class.forName(全限定名)
Class<?> c3 = Class.forName("java.lang.String");

System.out.println(c1 == c2 && c2 == c3);
// 输出:true

方式三按字符串类名加载,类找不到时抛 ClassNotFoundException,需要 try-catch 或向上 throws。

反射创建对象并调用方法

先准备一个普通的目标类(放在与 main 同一个文件里即可):

class Person {
    private String name;
    public Person() { }
    public Person(String name) { this.name = name; }
    public void say() { System.out.println("你好," + name); }
    private void secret() { System.out.println("私有方法被调用"); }
}

片段需放进 main 方法中运行(并 import java.lang.reflect.*):

Class<?> cls = Class.forName("Person");
Object p = cls.getConstructor(String.class).newInstance("小明");
cls.getMethod("say").invoke(p);         // 调用 public 方法
// 输出:你好,小明

Method m = cls.getDeclaredMethod("secret");
m.setAccessible(true);                  // 绕过访问检查
m.invoke(p);                            // 调用 private 方法
// 输出:私有方法被调用

反射操作私有字段

字段用 getDeclaredField 获取,private 字段要 setAccessible(true) 后才能读写:

Field f = cls.getDeclaredField("name");
f.setAccessible(true);                  // 打开私有字段访问
f.set(p, "小红");                       // 直接改字段值
cls.getMethod("say").invoke(p);
// 输出:你好,小红

方法、字段、构造器分别对应 Method、Field、Constructor 三个类,invoke()、get()/set()、newInstance() 完成实际动作。

反射的用途

  • 框架与配置:Spring 按配置文件或注解里的类名,反射创建对象并注入依赖。
  • 序列化 / ORM:Jackson、Gson、MyBatis 反射读写对象的字段。
  • 动态代理:JDK 动态代理、AOP 切面底层用反射生成代理。
  • 开发工具:IDE 自动补全、调试器查看对象结构。

性能与安全注意

  • 性能:反射比直接调用慢(含解析与安全检查),高并发热点路径应避免;确需使用时缓存 Method/Field 对象可显著提速。
  • 安全:setAccessible 能突破 private 限制,恶意代码可借此窥探内部;模块系统(JPMS)下反射也受到限制,生产环境应控制反射的使用范围。

反射是 Java 的"自省能力":三种方式拿到 Class,再通过 Constructor、Method、Field 动态创建与调用,甚至访问私有成员。它是框架的基石,但慢且越权,性能敏感处慎用、对外部输入尤其要防。

笔记加载中…