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

《Thinking in Java》讀書筆記:第 14 章 類型信息(RTTI、反射、動態代理、空對象模式)

     編輯:Java 相關知識

《Thinking in Java》第 14 章深入探討運行時類型信息(RTTI)與反射機制,是理解 Java 動態特性、框架原理(Spring、Hibernate、MyBatis)的關鍵。本文系統整理核心知識點:Class 對象、類字面量、instanceof/isInstance、反射 API、動態代理、空對象模式、接口與類型信息。

RTTI:運行時類型識別

核心觀點

  • Java 在運行時識別對象類型,支持動態綁定(多態)。
  • 代碼應盡量針對接口/基類編程,僅在必要時使用 RTTI 處理特殊情況。
  • RTTI 允許查詢引用的確切類型,從而分離特殊情況處理邏輯。

Class 對象:類型信息的運行時載體

  • 每個類加載時,JVM 在堆中創建一個 Class 對象(僅一份),包含類的完整元數據(字段、方法、構造器、註解、父類、接口等)。
  • 獲取 Class 引用三種方式:
    1. Class.forName("全限定名"):立即初始化類(執行靜態塊)。
    2. 對象.getClass():獲取運行時實際類型。
    3. 類名.class:不觸發初始化(延遲到首次訪問靜態成員/構造器)。
  • 基本類型包裝類有 TYPE 字段:Integer.TYPE == int.class。

類字面量與泛型 Class 引用

Class<String> c1 = String.class;           // 精確類型
Class<? extends Number> c2 = Integer.class; // 通配符上界綁定
Class<?> c3 = String.class;                // 非特定類型(比原始 Class 更明確意圖)
  • 泛型 Class 引用提供編譯期類型檢查,Class<?> 表示「有意圖的非特定類型引用」。
  • newInstance() 返回 Object,因編譯器無法知曉確切類型。

新式轉型語法:cast()

Object obj = "hello";
String s = String.class.cast(obj); // 等同 (String) obj,但類型由 Class 對象決定
  • 適用於類型在運行時才確定的場景(如反射調用)。

轉型前檢查:instanceof vs isInstance

反射:運行時類信息的終極形態

RTTI vs 反射本質區別

方式語法特點
instanceofobj instanceof Type只能比較具體命名類型,不能用 Class 變量
Class.isInstance()clazz.isInstance(obj)動態 instanceof,可用 Class 變量,消除 instanceof 表達式
Class.equals() / ==obj.getClass() == Target.class精確類型匹配,不考慮繼承(instanceof 考慮繼承)

反射核心能力

  • 動態獲取類的所有方法/字段/構造器/註解(getDeclaredMethods() 等)。
  • 運行時創建對象(constructor.newInstance())、調用方法(method.invoke(obj, args))、讀寫字段(field.get/set(obj))。
  • 繞過訪問控制(setAccessible(true)),慎用(破壞封裝、安全風險、性能損耗)。

方法提取器示例

public static void printMethods(Class clazz) {
    for (Method m : clazz.getDeclaredMethods()) {
        System.out.println(m.getReturnType().getSimpleName() + " " + m.getName() + 
                           Arrays.toString(m.getParameterTypes()));
    }
}

動態代理:AOP 基石

  • java.lang.reflect.Proxy + InvocationHandler 創建接口的動態實現類。
  • 所有方法調用委派給單一 invoke(proxy, method, args),實現橫切關注點(事務、日誌、權限、緩存)與業務邏輯分離。
  • 構造器通常傳入「真實對象」引用,代理內部決定:前置處理 → 調用真實對象 → 後置處理 → 返回。

動態代理模板

public class LoggingHandler implements InvocationHandler {
    private final Object target;
    public LoggingHandler(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;
    }
}

// 使用
UserService real = new UserServiceImpl();
UserService proxy = (UserService) Proxy.newProxyInstance(
    UserService.class.getClassLoader(),
    new Class[]{UserService.class},
    new LoggingHandler(real)
);
proxy.saveUser(new User()); // 觸發代理邏輯

空對象模式:消除 null 檢查

  • 定義實現接口的 NullObject,方法返回中性值(空集合、0、false、空字符串),不拋 NPE。
  • 適用於「更接近數據層」的領域實體,避免調用鏈層層 if (obj != null)。
  • 通常為單例:static final User ANONYMOUS = new NullUser();。
  • 配合動態代理可自動生成任意接口的空對象實現。

空對象 vs Mock/Stub

維度RTTI反射
.class 文件可用性編譯期可用編譯期不可用(運行時才加載)
典型場景已知類層次、編譯期可依賴插件系統、遠程調用、序列化、IDE、框架容器
核心 APIClass, instanceofClass, Method, Field, Constructor, Annotation

接口與類型信息

接口是類型信息的「純粹形式」——無實現細節,僅定義契約。反射結合接口,構建了 Java 生態中極其強大的框架基礎設施:依賴注入容器、ORM 映射、序列化引擎、RPC 框架、測試 Mock 庫。

總結

第 14 章揭示了 Java「運行時不擦除類型信息」的設計哲學:Class 對象是元數據入口,RTTI 解決已知類型的動態分派,反射解決未知類型的動態操作,動態代理實現橫切邏輯解耦,空對象模式消除防衛性編程。掌握這些機制,不僅能讀懂 Spring/MyBatis 核心源碼,更能在架構設計中靈活運用動態特性,構建可擴展、低耦合的系統。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved
模式目的行為
Null Object替代 null,提供安全默認行為返回中性值,不拋異常
Stub測試替身,返回預設數據硬編碼返回值
Mock測試替身,驗證交互記錄調用、斷言參數/次序