反射是 Java 語言的核心特性之一,賦予程序在運行時檢查、修改自身結構與行為的能力。這種「自省」機制讓框架開發、動態代理、插件架構成為可能。本文系統梳理反射核心 API,並通過 9 個實戰示例覆蓋類信息獲取、方法調用、對象創建、字段操作、數組處理等全場景。
// 1. 類字面量(編譯期已知)
Class<String> c1 = String.class;
// 2. 對象實例獲取(運行期已知)
String s = "hello";
Class<?> c2 = s.getClass();
// 3. 全限定名動態加載(最常用,框架核心模式)
Class<?> c3 = Class.forName("java.lang.String");
// 基本類型對應的 Class
Class<Integer> c4 = Integer.TYPE; // 等同於 int.class
import java.lang.reflect.*;
public class DumpMethods {
public static void main(String[] args) {
try {
Class<?> c = Class.forName(args[0]); // 如 java.util.Stack
Method[] methods = c.getDeclaredMethods();
for (Method m : methods) {
System.out.println(m.toString());
}
} catch (Throwable e) { e.printStackTrace(); }
}
}
輸出:包含修飾符、返回類型、方法名、參數列表、異常聲明的完整簽名。
Class<?> cls = Class.forName("A");
boolean b1 = cls.isInstance(new Integer(37)); // false
boolean b2 = cls.isInstance(new A()); // true
Class<?> cls = Class.forName("method1");
Method[] methods = cls.getDeclaredMethods();
for (Method m : methods) {
System.out.println("name = " + m.getName());
System.out.println("decl class = " + m.getDeclaringClass());
for (Class<?> p : m.getParameterTypes())
System.out.println("param = " + p);
for (Class<?> e : m.getExceptionTypes())
System.out.println("exc = " + e);
System.out.println("return type = " + m.getReturnType());
System.out.println("-----");
}
關鍵區別:getDeclaredMethods() 僅獲取當前類聲明方法(含 private);getMethods() 獲取所有 public 方法(含繼承)。
Class<?> cls = Class.forName("constructor1");
Constructor<?>[] ctors = cls.getDeclaredConstructors();
for (Constructor<?> ct : ctors) {
System.out.println("name = " + ct.getName());
for (Class<?> p : ct.getParameterTypes())
System.out.println("param = " + p);
}
// 動態調用帶參構造器
Class<?>[] paramTypes = {int.class, double.class};
Constructor<?> ct = cls.getConstructor(paramTypes);
Object obj = ct.newInstance(37, 3.14);
Class<?> cls = Class.forName("field1");
Field[] fields = cls.getDeclaredFields();
for (Field f : fields) {
System.out.println("name = " + f.getName());
System.out.println("type = " + f.getType());
System.out.println("modifiers = " + Modifier.toString(f.getModifiers()));
}
Modifier 工具類:將修飾符位掩碼轉為規範字符串(如 public static final)。
Class<?> cls = Class.forName("method2");
Method add = cls.getMethod("add", int.class, int.class);
Object result = add.invoke(new method2(), 37, 47); // 返回 Integer(84)
核心場景:方法名在運行期確定(如插件系統、RPC 框架、脚本引擎)。
Constructor<?> ct = cls.getConstructor(int.class, int.class);
Object instance = ct.newInstance(37, 47); // 等同於 new Constructor2(37, 47)
實現工廠模式、依賴注入容器的基礎機制。
Field f = cls.getField("d"); // 僅能獲取 public 字段
Field f2 = cls.getDeclaredField("privateField"); // 可獲取私有字段
f2.setAccessible(true); // 破壞封裝,慎用
f2.setDouble(obj, 12.34);
安全警示:setAccessible(true) 受 SecurityManager 限制,模塊化系統(JPMS)可能禁止深度反射。
// 一維數組
Class<String> cls = String.class;
Object arr = Array.newInstance(cls, 10);
Array.set(arr, 5, "test");
String val = (String) Array.get(arr, 5);
// 三維數組
int[] dims = {5, 10, 15};
Object arr3d = Array.newInstance(int.class, dims);
Array.setInt(Array.get(Array.get(arr3d, 3), 5), 10, 37);
int[][][] cast = (int[][][]) arr3d;
System.out.println(cast[3][5][10]); // 37
本質:多維數組是數組的數組,Array.get 逐層降維。
| 場景 | 建議 |
|---|---|
| 高頻調用 | 緩存 Method/Constructor/Field 對象,避免重複 getMethod 開銷 |
| 性能敏感 | 考慮 MethodHandle / VarHandle(Java 7+)或代碼生成 |
| 安全環境 | 避免 setAccessible(true),優先使用公共 API |
| 模塊化 | 導出包 opens com.xxx to java.base 允許深度反射 |
| 異常處理 | 捕獲 ReflectiveOperationException 父類統一處理 |
反射打破了編譯期靜態綁定,是 Spring、Hibernate、MyBatis 等框架的基石。掌握 Class、Method、Constructor、Field、Array 五大核心類協作模式,能讓你從框架使用者進階為框架設計者。但請記住:反射威力巨大,代價是性能、安全與可維護性的妥協,非必要勿濫用。