Java 反射 (Reflection)

反射允許在執行時期檢視和操作類別、方法、欄位等,常用於框架開發、測試工具、動態代理等場景。

取得 Class 物件

// 方法 1:透過類別
Class<String> clazz1 = String.class;

// 方法 2:透過實例
String str = "Hello";
Class<?> clazz2 = str.getClass();

// 方法 3:透過完整類別名稱
Class<?> clazz3 = Class.forName("java.lang.String");

類別資訊

Class<?> clazz = Person.class;

// 基本資訊
String name = clazz.getName();           // com.example.Person
String simpleName = clazz.getSimpleName(); // Person
Package pkg = clazz.getPackage();
Class<?> superClass = clazz.getSuperclass();
Class<?>[] interfaces = clazz.getInterfaces();

// 修飾符
int modifiers = clazz.getModifiers();
boolean isPublic = Modifier.isPublic(modifiers);
boolean isAbstract = Modifier.isAbstract(modifiers);

// 類型判斷
boolean isInterface = clazz.isInterface();
boolean isEnum = clazz.isEnum();
boolean isArray = clazz.isArray();
boolean isRecord = clazz.isRecord();

建構子

Class<?> clazz = Person.class;

// 取得所有公開建構子
Constructor<?>[] constructors = clazz.getConstructors();

// 取得指定建構子
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);

// 建立實例
Person person = (Person) constructor.newInstance("Alice", 25);

// 無參數建構子
Person person2 = (Person) clazz.getDeclaredConstructor().newInstance();

欄位

Class<?> clazz = Person.class;

// 取得所有公開欄位(含繼承)
Field[] publicFields = clazz.getFields();

// 取得所有宣告的欄位(含私有)
Field[] allFields = clazz.getDeclaredFields();

// 取得指定欄位
Field nameField = clazz.getDeclaredField("name");

// 存取私有欄位
nameField.setAccessible(true);
Person person = new Person("Alice", 25);
String name = (String) nameField.get(person);
nameField.set(person, "Bob");

方法

Class<?> clazz = Person.class;

// 取得所有公開方法(含繼承)
Method[] publicMethods = clazz.getMethods();

// 取得所有宣告的方法
Method[] allMethods = clazz.getDeclaredMethods();

// 取得指定方法
Method method = clazz.getMethod("setName", String.class);

// 呼叫方法
Person person = new Person();
method.invoke(person, "Alice");

// 呼叫靜態方法
Method staticMethod = clazz.getMethod("staticMethod");
staticMethod.invoke(null);

// 呼叫私有方法
Method privateMethod = clazz.getDeclaredMethod("privateMethod");
privateMethod.setAccessible(true);
privateMethod.invoke(person);

取得泛型資訊

public class Container<T> {
    private List<String> items;
}

Field field = Container.class.getDeclaredField("items");
Type type = field.getGenericType();

if (type instanceof ParameterizedType) {
    ParameterizedType pt = (ParameterizedType) type;
    Type[] actualTypes = pt.getActualTypeArguments();
    System.out.println(actualTypes[0]);  // java.lang.String
}

取得註解

Class<?> clazz = MyClass.class;

// 類別註解
Annotation[] annotations = clazz.getAnnotations();
MyAnnotation ann = clazz.getAnnotation(MyAnnotation.class);

// 方法註解
Method method = clazz.getMethod("myMethod");
if (method.isAnnotationPresent(Test.class)) {
    Test test = method.getAnnotation(Test.class);
}

// 參數註解
Parameter[] params = method.getParameters();
for (Parameter param : params) {
    Annotation[] paramAnns = param.getAnnotations();
}

動態代理

public interface UserService {
    void save(String name);
}

// 建立代理
UserService proxy = (UserService) Proxy.newProxyInstance(
    UserService.class.getClassLoader(),
    new Class<?>[] { UserService.class },
    (proxyObj, method, args) -> {
        System.out.println("Before: " + method.getName());
        // 這裡可以呼叫實際實作
        System.out.println("After: " + method.getName());
        return null;
    }
);

proxy.save("Alice");
// Before: save
// After: save

實用範例

簡單的 IoC 容器

public class SimpleContainer {
    private Map<Class<?>, Object> beans = new HashMap<>();
    
    public <T> void register(Class<T> clazz) throws Exception {
        T instance = clazz.getDeclaredConstructor().newInstance();
        beans.put(clazz, instance);
    }
    
    @SuppressWarnings("unchecked")
    public <T> T get(Class<T> clazz) {
        return (T) beans.get(clazz);
    }
}

物件轉 Map

public static Map<String, Object> toMap(Object obj) throws Exception {
    Map<String, Object> map = new HashMap<>();
    
    for (Field field : obj.getClass().getDeclaredFields()) {
        field.setAccessible(true);
        map.put(field.getName(), field.get(obj));
    }
    
    return map;
}

注意事項

  1. 效能:反射比直接呼叫慢
  2. 安全性:可以繞過存取控制
  3. 維護性:編譯時不檢查,容易出錯
  4. 模組化:Java 9+ 模組系統可能限制反射存取
// 效能優化:快取 Method/Field
private static final Method METHOD;
static {
    try {
        METHOD = MyClass.class.getMethod("myMethod");
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

重點整理

  • 反射允許在執行時檢視和操作程式結構
  • 使用 Class.forName().class 取得 Class 物件
  • setAccessible(true) 可存取私有成員
  • 動態代理用於 AOP、Mock 等場景
  • 反射效能較差,應謹慎使用
  • 許多框架(Spring、Hibernate)大量使用反射