JVM 為每個**載入的類型** (類、介面、枚舉、註解、基本類型 `int.class`、陣列 `String[].class`、Void `void.class`) 創建**唯一**的 `java.lang.Class` 實例。**`Class` 無公共構造器**,由類載入器 `defineClass` 產生。運行時類型識別 (RTTI)、反射、動態代理、框架掃描、序列化、JNI 皆建立在 `Class` 物件之上。
| 方式 | 代碼示例 | 特點 | 適用場景 |
|---|---|---|---|
1. Object.getClass() | `Class> c = "hello".getClass();` | **已有實例**,最常用。返回實際運行時類型 (多態下為子類)。 | 實例方法中獲取自身類型、日誌記錄實際類型。 |
2. Class.forName(String) | `Class> c = Class.forName("java.lang.String");` | **字串名稱**,**觸發類初始化** (執行 ` | 配置驅動 (JDBC Driver、框架掃描)、熱載入、SPI 實現類發現。 |
3. 類型.class 字面量 | `Class `Class | **編譯期常量**,**不觸發初始化**,類型安全 (泛型保留)。 | 註解處理、泛型 API 參數 (`List |
| 方法 | 返回值 | 說明 |
|---|---|---|
getName() | String | 類全限定名 (基本類型返回 `int`、`boolean` 等;陣列返回 `[Ljava.lang.String;`、`[[I` 等內部格式)。 |
getSimpleName() | String | 簡單類名 (無包名)。 |
getCanonicalName() | String | 規範名 (可用於 `forName`),陣列/內部類與 `getName` 異。 |
newInstance() | T | **已棄用 (JDK 9)**。調用 **無參構造器**,拋出檢查異常。替代: `clazz.getDeclaredConstructor().newInstance()` (可訪問私有構造)。 |
getClassLoader() | ClassLoader | 載入此類的類載入器。Bootstrap Loader 返回 `null`。判斷類來源關鍵。 |
getSuperclass() | Class super T> | 父類 `Class` (Object 返回 `null`)。介面無父類。 |
getInterfaces() | Class>[] | 直接實現的介面。 |
isInterface() / isArray() / isPrimitive() / isAnnotation() / isEnum() / isAnonymousClass() / isLocalClass() / isMemberClass() | boolean | 類型分類判斷。 |
getComponentType() | Class> | 陣列元素類型 (多維遞歸調用)。 |
getModifiers() | int | 修飾符位掩碼,配合 `Modifier` 工具類解析。 |
getFields() / getDeclaredFields() | Field[] | 公開繼承域 / 本類聲明域 (含私有)。 |
getMethods() / getDeclaredMethods() | Method[] | 公開繼承方法 / 本類聲明方法。 |
getConstructors() / getDeclaredConstructors() | Constructor>[] | 公開構造器 / 本類聲明構造器。 |
getAnnotation(Class) | A | 獲取指定註解 (含繼承)。 |
getDeclaredAnnotations() | Annotation[] | 本類聲明註解。 |
getResource(String) | URL | 類路徑下資源定位 (相對類包路徑 / 絕對 `/` 為 classpath 根)。 |
getResourceAsStream(String) | InputStream | 資源流讀取。**推薦用於載入配置/資源檔**。 |
**`Class.forName`** vs **`ClassLoader.loadClass`** — 理解差異是**熱部署、模組隔離、SPI、框架掃描**正確性的關鍵。
| 特性 | Class.forName(name) | Class.forName(name, initialize, loader) | ClassLoader.loadClass(name) | ClassLoader.loadClass(name, resolve) |
|---|---|---|---|---|
| 類載入器 | 調用者類的載入器 (調用棧敏感) | 指定 `loader` (null = Bootstrap) | 該 `ClassLoader` 實例 | 該 `ClassLoader` 實例 |
| 連接階段 | 是 | `initialize=true` 時是,否則可能 | 否 (僅載入) | `resolve=true` 時是 |
| 初始化 ( | **是 (預設)** | 由 `initialize` 參數控制 | **否** | **否** |
| 快取 | JVM 內部快取 (已載入直接返回) | 同左 | 載入器實例快取 | 同左 |
| 典型用途 | JDBC Driver 註冊、靜態塊初始化依賴 | 框架控制初始化時機 (OSGi、熱部署) | **自定義載入器核心邏輯**、延遲載入 | 需連接但不初始化 (罕見) |
| 維度 | newInstance() (反射) | new (關鍵字) |
|---|---|---|
| 類型檢查 | 運行時 (弱類型) | 編譯時 (強類型) |
| 構造器選擇 | **僅無參構造器** | 任意公開構造器 |
| 異常處理 | 拋檢查異常 (`InstantiationException`, `IllegalAccessException`) | 編譯期強制 try-catch / throws |
| 效能 | 低 (反射解析、訪問檢查、JIT 優化難) | 高 (JIT 內聯、逃逸分析、標量替換) |
| 前置條件 | 類已**載入 + 連接** (通常由 `forName` 完成) | 類可未載入 (首次 `new` 觸發載入/連接/初始化) |
| 解耦價值 | **高**:配置類名字串動態創建,框架核心機制 (Spring BeanFactory、Servlet 容器、插件系統) | 低:代碼硬編碼依賴具體類 |
**現代最佳實踐**:框架層用反射/構造器緩存實例化;業務代碼**盡量用 `new` / 依賴注入容器管理**,避免手寫反射創建。
// 1. Class 物件唯一性 → 可用 == 比較 (不需 equals)
if (obj.getClass() == String.class) { ... } // 精確類型匹配 (不含子類)
if (String.class.isAssignableFrom(obj.getClass())) { ... } // 是 String 或子類 (含介面實現)
// 2. 陣列類型判斷
int[] arr = new int[10];
Class> c = arr.getClass(); // class [I
c.isArray(); // true
c.getComponentType(); // int.class (Primitive)
// 3. 泛型類型擦除後獲取 (需 TypeToken / ParameterizedType)
Type type = new TypeToken>(){}.getType(); // Guava
// 或
Method method = ...;
Type genericReturnType = method.getGenericReturnType(); // 保留泛型資訊
`Class` 物件是 Java 動態性的核心樞紐。**掌握三種獲取方式的語義差異 (特別是初始化觸發)、熟練核心反射 API、理解類載入器隔離邊界、區分反射創建與靜態創建權衡**,是編寫框架、中間件、動態代理、熱部署、序列化、掃描器等**基礎設施級代碼**的必備內功。業務開發中,**優先依賴注入、介面編程、編譯期類型安全**,謹慎使用反射。