首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 反射機制完全指南:Class 物件、方法/建構子/欄位反射、動態代理與效能優化

     編輯:Java 相關知識

前言

反射是 Java 動態性的核心,框架(Spring、Hibernate、MyBatis)的基石。本文系統闡述 **Class 物件獲取、方法/建構子/欄位反射操作、泛型擦除下的 TypeToken 模式、動態代理、效能優化與安全風險**。

一、Class 物件:反射的入口

1.1 三種獲取方式


public class User { private String name; }

// 1. 類字面常數(編譯期已知,最快)
Class c1 = User.class;

// 2. 實例 getClass()(運行期已知實例)
User user = new User();
Class c2 = user.getClass();

// 3. 字串全限定名(動態載入,最靈活)
Class c3 = Class.forName("com.example.User"); // 觸發靜態初始化
Class c4 = Class.forName("com.example.User", true, classLoader); // 指定載入器

1.2 基本型別、void、陣列也有 Class 物件


Class intArrayClass = int[].class;
Class integerClass = Integer.class; // 包裝類型
Class voidClass = void.class;
Class intClass = int.class; // 基本型別

1.3 Class 物件常用方法

二、方法反射

2.1 獲取方法


Class clazz = User.class;

// 所有 public 方法(含繼承)
Method[] publicMethods = clazz.getMethods();

// 所有宣告方法(含 private,不含繼承)
Method[] declaredMethods = clazz.getDeclaredMethods();

// 精確獲取單一方法
Method toString = clazz.getMethod("toString"); // 無參
Method setName = clazz.getDeclaredMethod("setName", String.class); // 有參

2.2 方法資訊解析


Method method = clazz.getDeclaredMethod("save", String.class, int.class);

method.getName();                    // "save"
method.getReturnType();              // 回傳型別 Class
method.getParameterTypes();          // 參數型別陣列
method.getParameterCount();          // 參數個數
method.getModifiers();               // public/private/static/final...
method.getAnnotation(Transactional.class); // 註解
method.getGenericReturnType();       // 泛型回傳型別(Type)
method.getGenericParameterTypes();   // 泛型參數型別

2.3 方法調用


User user = new User();
Method setName = clazz.getDeclaredMethod("setName", String.class);
setName.setAccessible(true); // 破壞封裝,存取 private

// 實例方法:第一參數為目標物件
setName.invoke(user, "Alice"); // user.setName("Alice")

// 靜態方法:第一參數為 null 或 Class 物件
Method staticMethod = clazz.getDeclaredMethod("staticMethod");
staticMethod.invoke(null); // 或 staticMethod.invoke(clazz)

2.4 異常處理

  • `IllegalAccessException`:權限不足,需 `setAccessible(true)`
  • `InvocationTargetException`:目標方法拋出異常,**真實異常在 `getCause()`**
  • `IllegalArgumentException`:參數型別/個數不匹配

三、建構子反射

3.1 獲取建構子


Constructor[] allConstructors = clazz.getConstructors(); // public
Constructor[] declaredConstructors = clazz.getDeclaredConstructors(); // 全部

// 精確獲取
Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);

3.2 實例化物件


Constructor ctor = clazz.getDeclaredConstructor(String.class);
ctor.setAccessible(true);
User user = ctor.newInstance("Bob"); // 等同 new User("Bob")

// 無參建構子快捷方式(已棄用 Java 9+,不推薦)
// User user = clazz.newInstance(); 

四、欄位反射

4.1 獲取欄位


Field[] publicFields = clazz.getFields(); // public 含繼承
Field[] allFields = clazz.getDeclaredFields(); // 全部含 private

Field nameField = clazz.getDeclaredField("name");

4.2 讀寫欄位值


User user = new User();
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);

// 讀取
String name = (String) nameField.get(user);

// 寫入
nameField.set(user, "Charlie");

// 靜態欄位:目標物件傳 null
Field staticField = clazz.getDeclaredField("COUNT");
staticField.setAccessible(true);
int count = (int) staticField.get(null);
staticField.set(null, 100);

4.3 泛型欄位型別獲取


class Container { List items; }

Field itemsField = Container.class.getDeclaredField("items");
Type genericType = itemsField.getGenericType(); // ParameterizedType
if (genericType instanceof ParameterizedType pt) {
    Type[] actualTypes = pt.getActualTypeArguments(); // [T]
    // 需具體化類型時才能解析 T,否則為 TypeVariable
}

五、泛型擦除下的 TypeToken 模式


// 核心類(Guava/Gson 模式)
abstract class TypeToken {
    final Type type;
    protected TypeToken() {
        Type superclass = getClass().getGenericSuperclass();
        this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
    }
    public Type getType() { return type; }
}

// 使用:匿名子類捕獲泛型資訊
TypeToken> token = new TypeToken>() {};
Type type = token.getType(); // ParameterizedType: List

六、動態代理

6.1 JDK 動態代理


interface Service { void execute(); }
class RealService implements Service { public void execute() { System.out.println("Real"); } }

Service real = new RealService();
Service proxy = (Service) Proxy.newProxyInstance(
    Service.class.getClassLoader(),
    new Class[]{Service.class},
    (proxyObj, method, args) -> {
        System.out.println("Before: " + method.getName());
        Object result = method.invoke(real, args);
        System.out.println("After: " + method.getName());
        return result;
    }
);

proxy.execute(); // 觸發攔截

6.2 CGLIB 代理(無介面)


// 依賴 cglib 套件
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(RealService.class);
enhancer.setCallback((MethodInterceptor) (obj, method, args, proxy) -> {
    System.out.println("Before CGLIB");
    Object result = proxy.invokeSuper(obj, args);
    System.out.println("After CGLIB");
    return result;
});
RealService proxy = (RealService) enhancer.create();
proxy.execute();

七、效能優化與最佳實踐

方法說明
getName()完整限定名(含包名)
getSimpleName()簡單類名
getPackage()Package 物件
getModifiers()修飾符位掩碼,配合 Modifier 工具類解析
isInterface() / isArray() / isEnum() / isAnnotation() / isPrimitive()型別判斷
getSuperclass()父類 Class
getInterfaces()實現的介面陣列
getTypeParameters()泛型型別參數
場景優化策略
頻繁反射調用緩存 Method/Constructor/Field 物件;`setAccessible(true)` 一次性設定
建構子實例化優先 `Constructor.newInstance()`;避免 `Class.newInstance()`(已棄用、不處理異常)
方法調用考慮 `MethodHandle` (Java 7+) 或 `VarHandle` (Java 9+),JIT 優化更好
啟動期掃描編譯期註解處理器 (APT) 生成索引,運行期讀取索引避免掃描
私有存取Java 9+ 模組系統需 `module-info.java` 開放 `opens com.pkg to java.base`

八、安全風險與模組化限制

  • 破壞封裝:`setAccessible(true)` 可存取私有成員,違反封裝原則
  • 模組系統限制:Java 9+ 強封裝,非開放包無法反射私有成員,需 `--add-opens` 或 `module-info.java` `opens`
  • 安全管理器:`SecurityManager` 可限制 `suppressAccessChecks` 權限
  • 序列化攻擊**:反射可構造惡意物件圖,配合 `ObjectInputFilter` 防禦

總結

反射賦予 Java 動態載入、檢查、修改類行為的能力,是框架級開發的利器。**核心掌握點**:Class 物件三種獲取、Method/Constructor/Field 三大反射操作、泛型 TypeToken 捕獲、動態代理兩大實現、效能緩存策略、模組化封裝限制。業務代碼慎用反射,框架代碼善用反射,始終遵循「編譯期可解決不拖到運行期」原則。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved