Java 反射是框架開發的基石,賦予程式在運行時檢查、修改類結構與行為的能力。本文以案例驅動方式,系統講解 Class 獲取、實例化、構造器/方法/屬性操作、泛型擦除應對、動態代理、類加載器、以及反射賦能工廠模式的完整實戰。
// 1. Class.forName("全限定名") - 最常用,支援動態載入
Class> clazz1 = Class.forName("com.example.User");
// 2. 實例.getClass() - 已有實例時
User user = new User();
Class> clazz2 = user.getClass();
// 3. 類字面常量 - 編譯期可知,效能最好
Class> clazz3 = User.class;
// 4. 基本類型與陣列
Class intClass = int.class;
Class arrClass = int[].class;
// 方式 A:Class.newInstance() - 已棄用 (Java 9+)
User u1 = (User) clazz.newInstance(); // 需公開無參構造,拋出所有異常
// 方式 B:Constructor.newInstance() - 標準做法
Constructor ctor = clazz.getDeclaredConstructor();
ctor.setAccessible(true); // 支援 private
User u2 = ctor.newInstance();
// 獲取所有公開構造器
Constructor>[] ctors = clazz.getConstructors();
// 獲取指定參數構造器
Constructor ctor = clazz.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true);
User u3 = ctor.newInstance("Alice", 25);
// 獲取所有宣告屬性(含 private,不含繼承)
Field[] fields = clazz.getDeclaredFields();
for (Field f : fields) {
int mod = f.getModifiers();
System.out.println(Modifier.toString(mod) + " " + f.getType().getName() + " " + f.getName());
// 讀寫私有屬性
f.setAccessible(true);
Object val = f.get(obj); // 讀取
f.set(obj, newValue); // 寫入
}
// 靜態屬性 obj 參數傳 null
Field staticField = clazz.getDeclaredField("COUNT");
staticField.setAccessible(true);
staticField.set(null, 100); // 設定靜態值
// 獲取所有公開方法(含繼承)
Method[] methods = clazz.getMethods();
// 獲取指定方法
Method sayHello = clazz.getDeclaredMethod("sayHello", String.class, int.class);
sayHello.setAccessible(true);
// 調用實例方法
Object result = sayHello.invoke(obj, "World", 2024);
// 調用靜態方法
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
staticMethod.invoke(null); // obj 為 null
// 假設 class UserDao extends BaseDao
Type superclass = clazz.getGenericSuperclass();
if (superclass instanceof ParameterizedType pt) {
Type[] actualTypes = pt.getActualTypeArguments();
// actualTypes[0] -> User.class, actualTypes[1] -> Long.class
}
// 父類
Class> superClazz = clazz.getSuperclass();
// 實現的介面
Class>[] interfaces = clazz.getInterfaces();
// 註解
Annotation[] annotations = clazz.getAnnotations();
MyAnnotation ann = clazz.getAnnotation(MyAnnotation.class);
// 修飾符
int mod = clazz.getModifiers();
boolean isPublic = Modifier.isPublic(mod);
boolean isAbstract = Modifier.isAbstract(mod);
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); // 修改第一個元素
// 動態擴容陣列
int[] newArr = (int[]) Array.newInstance(compType, 15);
System.arraycopy(arr, 0, newArr, 0, arr.length);
interface Subject { String say(String name, int age); }
class RealSubject implements Subject {
public String say(String name, int age) { return name + " " + age; }
}
class MyInvocationHandler implements InvocationHandler {
private final Object target;
public MyInvocationHandler(Object target) { this.target = target; }
@Override
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 real = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(
real.getClass().getClassLoader(),
real.getClass().getInterfaces(),
new MyInvocationHandler(real)
);
proxy.say("Bob", 30);
// 需引入 cglib 依賴
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealSubject.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
System.out.println("CGLIB Before");
Object result = proxy.invokeSuper(obj, args);
System.out.println("CGLIB After");
return result;
});
Subject cglibProxy = (Subject) enhancer.create();
// 獲取類加載器
ClassLoader cl = clazz.getClassLoader(); // AppClassLoader
ClassLoader parent = cl.getParent(); // PlatformClassLoader (Ext)
ClassLoader bootstrap = parent.getParent(); // null (Bootstrap, C++實現)
// 線程上下文類加載器(SPI、JNDI、JDBC 常用)
Thread.currentThread().getContextClassLoader();
loadClass(如 Tomcat、OSGi、熱部署)。class FruitFactory {
public static Fruit getFruit(String name) {
if ("Apple".equals(name)) return new Apple();
if ("Orange".equals(name)) return new Orange();
return null; // 新增水果必須修改工廠類
}
}
class ReflectFruitFactory {
public static Fruit getFruit(String className) {
try {
return (Fruit) Class.forName(className).getDeclaredConstructor().newInstance();
} catch (Exception e) { throw new RuntimeException(e); }
}
}
// 使用:ReflectFruitFactory.getFruit("com.example.Apple")
// fruits.properties
apple=com.example.Apple
orange=com.example.Orange
class ConfigFruitFactory {
private static final Properties PROPS = loadProps();
public static Fruit getFruit(String key) {
String className = PROPS.getProperty(key);
if (className == null) throw new IllegalArgumentException("Unknown fruit: " + key);
return ReflectFruitFactory.getFruit(className);
}
}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface FruitType { String value(); }
@FruitType("apple")
class Apple implements Fruit {}
@ComponentScan("com.example")
class AutoFruitFactory {
private final Map registry = new ConcurrentHashMap<>();
public AutoFruitFactory() {
// 掃描 @FruitType 註解類,實例化註冊
}
public Fruit getFruit(String key) { return registry.get(key); }
}
ReflectPermission("suppressAccessChecks")。opens 關鍵字控制反射訪問權限。--add-opens 啟動參數強制開放。| 操作 | 推薦 API | 注意事項 |
|---|---|---|
| 獲取 Class | Type.class / Class.forName() | forName 可能拋 ClassNotFoundException |
| 實例化 | Constructor.newInstance() | 必須 setAccessible(true) 支援私有構造 |
| 讀寫屬性 | Field.get/set() | static 屬性 obj 傳 null |
| 調用方法 | Method.invoke() | 異常包裝在 InvocationTargetException 中 |
| 泛型類型 | ParameterizedType | 僅在聲明處保留,運行時擦除 |
| 動態代理 | Proxy.newProxyInstance() | 只能代理介面,類代理用 CGLIB/ByteBuddy |
Java 反射是「運行時元編程」的核心工具:
opens 聲明。掌握反射,即掌握了構建靈活、可擴展框架的鑰匙。