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

Java 進階教程:RTTI 運行時類型識別與 Class 類核心機制

     編輯:Java 相關知識

RTTI 核心概念

RTTI (Run-Time Type Identification,運行時類型識別) 是 Java 實現多態的基礎。每個對象在運行時都攜帶類型信息,JVM 通過 Class 對象維護類的元數據,使得「向上轉型」後仍能正確調用實際類型的方法。

Class 類:類的類

java.lang.Class 是「類的類」。每個類加載後,JVM 在堆中創建一個對應的 Class 對象,包含類的完整元數據。

獲取 Class 對象的三種方式


// 1. 對象實例調用 getClass()
Human person = new Human();
Class c1 = person.getClass();

// 2. 類字面量
Class c2 = Human.class;

// 3. 字符串全限定名加載
Class c3 = Class.forName("com.example.Human");

Class 對象的關鍵特性

  • 單例性:同一類加載器下,每個類只有一個 Class 實例
  • 不可變:類加載後元數據固定
  • 懶加載:首次主動使用時才加載 (forName 立即加載)

多態的 RTTI 基礎


class Human {
    public void say() { System.out.println("Human"); }
}

class Woman extends Human {
    @Override public void say() { System.out.println("Woman"); }
}

public class Test {
    public static void main(String[] args) {
        Human h = new Woman();  // 向上轉型
        System.out.println(h.getClass().getName()); // 輸出 "Woman"
        h.say();  // 輸出 "Woman" - 動態分派
    }
}

機制:h 的靜態類型是 Human,但 h.getClass() 返回實際類型 Woman。方法調用通過 Class 對象中的方法表動態分派,實現多態。

Class 類核心 API

方法功能
getName()類全限定名
getSimpleName()類簡單名
getPackage()包對象
getSuperclass()父類 Class
getInterfaces()實現的接口數組
getModifiers()修飾符 (public/abstract 等)
newInstance()創建實例 (調用無參構造)
getFields()所有 public 字段 (含繼承)
getDeclaredFields()聲明的字段 (含私有、不含繼承)
getMethods()所有 public 方法 (含繼承)
getDeclaredMethods()聲明的方法 (含私有、不含繼承)
getConstructors()public 構造器
getDeclaredConstructors()聲明的構造器
isAnnotationPresent()檢查註解
getAnnotations()獲取註解

反射創建實例


// 無參構造
Human h1 = (Human) c1.newInstance();

// 帶參構造
Constructor cons = c1.getConstructor(String.class, int.class);
Human h2 = (Human) cons.newInstance("Alice", 25);

類加載機制

  1. 加載:讀取 .class 字節碼,生成 Class 對象
  2. 驗證:字節碼合法性檢查
  3. 準備:靜態字段分配內存、賦默認值
  4. 解析:符號引用轉直接引用
  5. 初始化:執行 <clinit> (靜態塊、靜態字段賦值)

觸發初始化:new、訪問靜態字段/方法、反射調用、子類初始化、main 類、動態語言調用。

實際應用場景

1. 框架通用處理 (如 ORM、JSON 序列化)


public void save(Object entity) {
    Class clazz = entity.getClass();
    Table table = clazz.getAnnotation(Table.class);
    String tableName = table.value();
    
    for (Field field : clazz.getDeclaredFields()) {
        field.setAccessible(true);
        Column col = field.getAnnotation(Column.class);
        String colName = col != null ? col.value() : field.getName();
        Object value = field.get(entity);
        // 構建 SQL...
    }
}

2. 動態代理


interface Service { void execute(); }

class ServiceImpl implements Service {
    public void execute() { System.out.println("執行業務"); }
}

Service proxy = (Service) Proxy.newProxyInstance(
    Service.class.getClassLoader(),
    new Class[]{Service.class},
    (proxy, method, args) -> {
        System.out.println("前置增強");
        method.invoke(new ServiceImpl(), args);
        System.out.println("後置增強");
        return null;
    }
);

3. SPI 服務發現


// META-INF/services/com.example.SpiInterface
// 內容: com.example.impl.SpiImplA

ServiceLoader loader = ServiceLoader.load(SpiInterface.class);
for (SpiInterface impl : loader) {
    impl.doSomething();
}

RTTI 的局限與補充

  • RTTI 需要編譯期知道類型:Class.forName 除外
  • 反射突破封裝:setAccessible(true) 可訪問私有成員
  • 性能開銷:反射比直接調用慢,熱點代碼可緩存 Method/Field 對象
  • 泛型擦除:運行時無法獲取泛型參數化類型 (需 TypeToken/ParameterizedType)

總結

RTTI 是 Java 動態性的核心,Class 對象是類型信息的運行時載體。掌握三種獲取方式、核心反射 API、類加載流程,能構建靈活的框架、實現動態代理、支援插件化架構。但反射破壞封裝、影響性能、繞過編譯期檢查,生產代碼中應謹慎使用,優先考慮多態、接口、註解處理器等靜態方案。

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