java.lang.Class<T> 是 Java 反射機制的核心,JVM 為每個載入的類、介面、枚舉、註解、基本類型、陣列類型創建唯一的 Class 實例。它封裝了類的完整元數據:修飾符、父類、介面、構造器、方法、欄位、註解等。理解 Class 是掌握反射、依賴注入框架、序列化庫的基石。
public class ClassDemo {
public static void main(String[] args) throws ClassNotFoundException {
// 方式 1: 類字面常量 (編譯期已知類型,不觸發類初始化)
Class<String> c1 = String.class;
Class<int> c2 = int.class; // 基本類型也有 Class 對象
Class<String[]> c3 = String[].class; // 陣列類型
// 方式 2: 對象 getClass() (運行期獲取實際類型)
String str = "Hello";
Class<? extends String> c4 = str.getClass(); // 返回 String.class
// 方式 3: Class.forName() (字串名稱動態載入,會觸發類初始化)
Class<?> c5 = Class.forName("java.lang.String");
System.out.println(c1 == c4); // true: JVM 保證同一類載入器下 Class 實例唯一
System.out.println(c1 == c5); // true
}
}
| 方式 | 觸發類初始化 | 適用場景 | 異常處理 |
|---|---|---|---|
.class | 否 | 編譯期已知類型、註解處理器 | 編譯期檢查 |
obj.getClass() | 否 | 運行期獲取實例真實類型 | 無拋出檢查異常 |
Class.forName() | 是 | 配置驅動、SPI 機制、動態代理 | 拋出 ClassNotFoundException |
JDK 5 引入 Class<T> 泛型參數,讓反射操作獲得編譯期類型檢查:
// 1. 精確類型綁定
Class<String> stringClass = String.class;
String instance = stringClass.getDeclaredConstructor().newInstance(); // 返回 String,無需轉型
// 2. 通配符上界: 接受 Number 及其子類
Class<? extends Number> numClass = Integer.class;
numClass = Double.class; // 合法
// numClass = String.class; // 編譯錯誤
// 3. 通配符下界: 接受 Integer 及其父類
Class<? super Integer> intSuper = Integer.class;
intSuper = Number.class; // 合法
intSuper = Object.class; // 合法
// 4. 無界通配符: 接受任意類型 (相當於 Class<?>)
Class<?> anyClass = String.class;
anyClass = int[].class; // 合法
利用 Type 體系 (ParameterizedType) 可提取泛型實際類型參數:
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
class BaseDao<T> {
private Class<T> entityClass;
@SuppressWarnings("unchecked")
public BaseDao() {
Type genericSuperclass = getClass().getGenericSuperclass();
if (genericSuperclass instanceof ParameterizedType) {
Type[] actualTypeArgs = ((ParameterizedType) genericSuperclass).getActualTypeArguments();
this.entityClass = (Class<T>) actualTypeArgs[0];
}
}
public Class<T> getEntityClass() { return entityClass; }
}
class UserDao extends BaseDao<User> {} // User 為實體類
public class GenericTypeDemo {
public static void main(String[] args) {
UserDao dao = new UserDao();
System.out.println("實體類型: " + dao.getEntityClass().getName()); // 輸出: com.example.User
}
}
class User {}
Class<User> clazz = User.class;
// 方式 1: newInstance() (已棄用 JDK 9+, 僅能調用 public 無參構造器)
User u1 = clazz.newInstance();
// 方式 2: Constructor.newInstance() (推薦,支援私有構造器、帶參構造器)
User u2 = clazz.getDeclaredConstructor(String.class, int.class).newInstance("Tom", 18);
Class 類不僅是反射的入口,更是 Java 類型系統在運行時的鏡像。掌握 Class<T> 泛型語法、三種獲取方式的語義差異、以及 Type 體系對泛型擦除的補完,能讓你在框架開發、動態代理、序列化等進階場景中遊刃有餘。記住:.class 不初始化類,forName() 會初始化,這在靜態塊有副作用時至關重要。