反射是 Java 動態性的核心,允許運行時檢查類結構、創建對象、調用方法、操作字段、生成動態代理。本文通過 15+ 實戰案例,系統講解反射核心 API、類加載機制、動態代理、工廠模式重構、數組操作等進階主題。
Class> c1 = Class.forName("pkg.ClassName"); // 最常用,字符串配置
Class> c2 = new MyClass().getClass(); // 實例獲取
Class> c3 = MyClass.class; // 字面量,編譯期檢查
所有類的對象都是 Class 的實例,JVM 為每個類型創建唯一的 Class 對象。
System.out.println(c1.getName()); // 完整類名 pkg.ClassName
System.out.println(c1.getSimpleName()); // 簡單類名 ClassName
System.out.println(c1.getPackage()); // Package 對象
System.out.println(c1.getSuperclass()); // 父類 Class
System.out.println(Arrays.toString(c1.getInterfaces())); // 實現的接口數組
System.out.println(c1.getModifiers()); // 修飾符 (public/abstract/final 等)
Object obj = clazz.newInstance(); // 已棄用 Java 9
// 推薦
Constructor> cons = clazz.getDeclaredConstructor();
cons.setAccessible(true); // 破壞私有
Object obj = cons.newInstance();
Constructor> cons = clazz.getDeclaredConstructor(String.class, int.class);
cons.setAccessible(true);
Object obj = cons.newInstance("Rollen", 20);
for (Constructor> c : clazz.getDeclaredConstructors()) {
System.out.println(Modifier.toString(c.getModifiers()) + " " + c.getName() + Arrays.toString(c.getParameterTypes()));
}
Method m = clazz.getDeclaredMethod("methodName", String.class, int.class);
m.setAccessible(true);
Object result = m.invoke(obj, "arg1", 100); // 實例方法傳 obj,靜態方法傳 null
for (Method m : clazz.getMethods()) { // 只獲取 public,含繼承
System.out.println(Modifier.toString(m.getModifiers()) + " " + m.getReturnType().getSimpleName() + " " + m.getName() + Arrays.toString(m.getParameterTypes()));
}
for (Method m : clazz.getDeclaredMethods()) { // 所有聲明,不含繼承
// ...
}
Field f = clazz.getDeclaredField("fieldName");
f.setAccessible(true);
f.set(obj, "newValue"); // 設置值
Object val = f.get(obj); // 獲取值
getFields():只獲取 public 字段,含繼承getDeclaredFields():獲取所有聲明字段,不含繼承Java 泛型擦除導致運行時無法直接獲取泛型參數,但可通過 Type 體系 (ParameterizedType) 獲取字段、方法簽名上的泛型信息。
Field f = clazz.getDeclaredField("list");
Type genericType = f.getGenericType(); // 獲取泛型類型
if (genericType instanceof ParameterizedType) {
ParameterizedType pt = (ParameterizedType) genericType;
System.out.println("原始類型: " + pt.getRawType()); // interface List
System.out.println("實際類型參數: " + Arrays.toString(pt.getActualTypeArguments())); // [class String]
}
interface Subject { String say(String name, int age); }
class RealSubject implements Subject { public String say(String n, int a) { return n + " " + a; } }
class MyInvocationHandler implements InvocationHandler {
private Object target;
public Object bind(Object target) {
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before: " + method.getName());
Object result = method.invoke(target, args);
System.out.println("After: " + method.getName());
return result;
}
}
// 使用
Subject proxy = (Subject) new MyInvocationHandler().bind(new RealSubject());
proxy.say("Rollen", 20); // 輸出 Before/After 並返回結果
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("Before");
Object result = proxy.invokeSuper(obj, args);
System.out.println("After");
return result;
}
});
RealSubject proxy = (RealSubject) enhancer.create();
JAVA_HOME/jre/lib/rt.jar 核心庫jre/lib/ext 擴展庫classpath 應用類,默認加載器
System.out.println(String.class.getClassLoader()); // null (Bootstrap)
System.out.println(MyClass.class.getClassLoader()); // sun.misc.Launcher$AppClassLoader
Class 對象<clinit>() (靜態塊、靜態字段賦值)Class 對象無引用,GC 回收 (需自定義類加載器)
int[] arr = {1,2,3,4,5};
Class> compType = arr.getClass().getComponentType(); // int.class
int len = Array.getLength(arr); // 5
Object first = Array.get(arr, 0); // 1
Array.set(arr, 0, 100); // 修改第一個元素為 100
// 動態擴容數組
int[] newArr = (int[]) Array.newInstance(compType, 15);
System.arraycopy(arr, 0, newArr, 0, arr.length);
// newArr: [1,2,3,4,5,0,0,0,0,0,0,0,0,0,0]
class Factory { public static Fruit getInstance(String name) {
if ("Apple".equals(name)) return new Apple();
if ("Orange".equals(name)) return new Orange();
return null;
}}
class Factory { public static Fruit getInstance(String className) {
try { return (Fruit) Class.forName(className).newInstance(); }
catch (Exception e) { throw new RuntimeException(e); }
}}
// 調用: Factory.getInstance("pkg.Apple");
// fruit.properties
apple=pkg.Apple
orange=pkg.Orange
Properties props = new Properties();
props.load(new FileInputStream("fruit.properties"));
Fruit fruit = Factory.getInstance(props.getProperty("apple"));
| 方面 | 影響 | 優化建議 |
|---|---|---|
| 性能 | 反射調用比直接調用慢 10-100 倍 (方法訪問檢查、參數裝箱) | 緩存 Class/Method/Field/Constructor 對象;熱點路徑用 MethodHandle/VarHandle (Java 9+) |
| 安全 | 破壞封裝 (setAccessible)、任意代碼執行風險 | SecurityManager 限制;模塊化 (Java 9+) 導出控制;避免反序列化不可信數據 |
| 可讀性 | 代碼晦澀、編譯期無檢查 | 封裝為工具類;優先用接口/繼承/註解處理器替代 |
Class、Method、Field、Constructor 線程安全,可靜態緩存。getDeclaredConstructor/Method/Field + setAccessible(true) 而非 getConstructor/Method/Field,支持私有成員。TypeToken (Gson) 或 ParameterizedType 保留泛型信息。module-info.java 中 opens pkg to reflection.framework 才能深度反射。反射賦予 Java 動態靈魂,善用之則框架優雅,濫用之則代碼脆弱。理解本質,謹慎應用。