運行時檢查類結構、動態創建實例、調用方法、訪問欄位,**打破編譯期靜態綁定**,是 Spring、Hibernate、MyBatis、Jackson、動態代理、插件系統、熱重載等框架基石。
| 類/介面 | 用途 |
|---|---|
Class<T> | 類元資料入口,對應每個載入的類/介面/基本型別/陣列/void |
Constructor<T> | 構造器資訊與動態實例化 |
Field | 欄位資訊與動態讀寫 |
Method | 方法資訊與動態調用 |
Modifier | 修飾符解析工具類 |
// 1. 對象實例 .getClass() (最常用,運行時類型)
String str = "hello";
Class> c1 = str.getClass();
// 2. 類字面量 .class (編譯期已知,無初始化開銷)
Class c2 = String.class;
// 3. Class.forName("全限定名") (動態載入,觸發類初始化)
Class> c3 = Class.forName("java.lang.String");
// 4. 基本型別包裝類 .TYPE
Class c4 = Integer.TYPE; // == int.class
關鍵差異:
forName()會執行類的靜態初始化塊 (<clinit>),而.class和.TYPE不會。動態載入 JDBC 驅動、插件類必須用forName()。
| 目標 | 公開成員 (含繼承) | 所有已聲明成員 (含私有、不含繼承) |
|---|---|---|
| 構造器 | getConstructor(Class...) / getConstructors() | getDeclaredConstructor(Class...) / getDeclaredConstructors() |
| 欄位 | getField(String) / getFields() | getDeclaredField(String) / getDeclaredFields() |
| 方法 | getMethod(String, Class...) / getMethods() | getDeclaredMethod(String, Class...) / getDeclaredMethods() |
setAccessible(true)。
Class> clazz = User.class;
Object user = clazz.getDeclaredConstructor().newInstance(); // 需無參構造
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true); // 關鍵:暴力反射破壞封裝
nameField.set(user, "張三"); // 寫入
String name = (String) nameField.get(user); // 讀取
System.out.println(name); // 張三
// 實例方法
Method setAge = clazz.getMethod("setAge", int.class);
setAge.invoke(user, 25); // user.setAge(25)
// 靜態方法
Method staticMethod = clazz.getMethod("staticFactory", String.class);
Object instance = staticMethod.invoke(null, "param"); // null 表示靜態方法無實例
// 私有方法
Method privateMethod = clazz.getDeclaredMethod("internalCheck");
privateMethod.setAccessible(true);
privateMethod.invoke(user);
// 無參構造
Object obj1 = clazz.getDeclaredConstructor().newInstance();
// 帶參構造
Constructor> ctor = clazz.getDeclaredConstructor(String.class, int.class);
ctor.setAccessible(true); // 若構造器私有
Object obj2 = ctor.newInstance("李四", 30);
public class BeanMapper {
public static T mapToBean(Map map, Class beanClass) throws Exception {
T bean = beanClass.getDeclaredConstructor().newInstance();
for (Map.Entry entry : map.entrySet()) {
String fieldName = entry.getKey();
Object value = entry.getValue();
try {
Field field = beanClass.getDeclaredField(fieldName);
field.setAccessible(true);
// 類型轉換邏輯 (簡化版,實際建議用 BeanUtils/MapStruct)
if (field.getType().isAssignableFrom(value.getClass())) {
field.set(bean, value);
} else {
// 簡單類型轉換:String → 基本型別/包裝類
field.set(bean, convert(value, field.getType()));
}
} catch (NoSuchFieldException ignored) {
// 忽略 Bean 中不存在的欄位
}
}
return bean;
}
private static Object convert(Object value, Class> targetType) {
if (value == null) return null;
if (targetType == int.class || targetType == Integer.class) return Integer.valueOf(value.toString());
if (targetType == long.class || targetType == Long.class) return Long.valueOf(value.toString());
if (targetType == double.class || targetType == Double.class) return Double.valueOf(value.toString());
if (targetType == boolean.class || targetType == Boolean.class) return Boolean.valueOf(value.toString());
return value; // 其他直接賦值 (String, Date 等需擴展)
}
}
| 優化手段 | 原理 | 效果 |
|---|---|---|
| 緩存 Member 對象 | 反射解析 (getMethod 等) 較慢,Member 對象不可變可重用 | 首次慢,後續同直接調用 |
| setAccessible(true) 一次性設定 | 避免每次調用檢查權限 | 減少 10-20% 開銷 |
| MethodHandle / VarHandle (JDK 7/9+) | JVM 內部優化,直接生成調用點字節碼 | 接近直接調用效能 |
| 避免熱點路徑反射 | 高頻調用路徑改用代碼生成 (ASM/ByteBuddy) 或 Lambda 元工廠 | 根治效能問題 |
--add-opens 或模組宣告 opens。setAccessible,現代依賴模組封裝。sun.misc.Unsafe 等內部類受強封裝保護,遷移至標準 API (VarHandle, MethodHandle)。反射是 Java 動態能力的核心,**強大但昂貴**。掌握 Class 獲取差異、Declared 系列選型、setAccessible 破壞封裝、MethodHandle 效能優化、模組化訪問限制,能讓你在框架開發、通用工具編寫、舊系統整合中遊刃有餘。原則:**非必要不反射、熱點路徑不反射、反射必緩存、邊界必驗證**。