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

Java Class 類核心機制深度解析:類加載、反射、動態實例化全流程

     編輯:Java 相關知識

java.lang.Class 是 Java 反射的入口,每個類在 JVM 中對應唯一的 Class 對象。本文系統講解Class 對象本質、三種獲取方式、類字面常量、instanceof 與 isInstance、反射 API 核心用法、動態調用完整示例,助力掌握 Java 動態編程精髓。

一、Class 對象本質與類加載

  • 每個類編譯生成 .class 文件,運行時由類加載器讀入內存生成 Class 對象
  • Class 對象包含類的完整元數據:字段、方法、構造器、註解、父類、接口等
  • JVM 保證同一類加載器下同一全限定名類只有一個 Class 實例
  • 類首次主動使用時觸發加載(new、靜態字段/方法調用、反射、子類初始化等)

演示:類加載時機

class Candy { static { System.out.println("Loading Candy"); } }
class Gum   { static { System.out.println("Loading Gum"); } }
class Cookie{ static { System.out.println("Loading Cookie"); } }

public class SweetShop {
    public static void main(String[] args) {
        System.out.println("inside main");
        new Candy();                              // 被動引用:僅創建對象,觸發 Candy 加載
        System.out.println("After creating Candy");
        Class.forName("com.zj.sample.Gum");       // 主動引用:觸發 Gum 加載
        System.out.println("After Class.forName");
        new Cookie();                             // 觸發 Cookie 加載
        System.out.println("After creating Cookie");
    }
}

輸出:

inside main
Loading Candy
After creating Candy
Loading Gum
After Class.forName("Gum")
Loading Cookie
After creating Cookie

關鍵點:Class.forName() 立即觸發類初始化(執行 static 塊);new 也觸發;單純引用類字面常量 Gum.class 不 觸發初始化。

二、獲取 Class 實例的三種方式

方式代碼特點觸發初始化
對象.getClass()obj.getClass()運行期已有對象否(對象已存在說明類已初始化)
Class.forName()Class.forName("全限定名")字符串動態指定、可跨類加載器是(默認)
類字面常量Type.class / int.class編譯期綁定、類型安全、性能最優否

基本類型與包裝類對應

Class c1 = int.class;        // 基本類型 Class 對象
Class c2 = Integer.TYPE;     // 同 c1
Class c3 = Integer.class;    // 包裝類 Class 對象
System.out.println(c1.getName()); // "int"
System.out.println(c3.getName()); // "java.lang.Integer"

三、關鍵 API 深度解析

1. newInstance():動態創建實例

Class c = Class.forName("com.zj.sample.Point");
Point p = (Point) c.newInstance(); // 調用無參構造器
// 需拋出 InstantiationException、IllegalAccessException

要求類有 public 無參構造器。僅能調用無參構造器,帶參構造需用 Constructor.newInstance()。

2. isInstance(obj):動態 instanceof

if (c.isInstance(obj)) { ... } // 等價於 obj instanceof Point

3. 獲取類層次結構

Class superClass = c.getSuperclass();        // 直接父類
Class[] interfaces = c.getInterfaces();      // 直接實現接口
// 遞歸可獲取完整繼承鏈

4. 反射核心三要素:Field、Method、Constructor

均實現 Member 接口,由 JVM 運行時創建:

  • Field:get()/set() 讀寫字段,getFields()/getDeclaredFields()
  • Method:invoke(obj, args) 調用方法,getMethods()/getDeclaredMethods()
  • Constructor:newInstance(args) 創建對象,getConstructors()/getDeclaredConstructors()

getXxx() 返回 public 成員(含繼承);getDeclaredXxx() 返回本類聲明所有成員(含 private),不含繼承。

四、實戰:運行時檢視未知類結構

Class c = Class.forName("com.zj.sample.Point");
Constructor[] cons = c.getDeclaredConstructors();
for (Constructor con : cons) System.out.println(con);
// 輸出: com.zj.sample.Point(int,int)

Method[] methods = c.getDeclaredMethods();
for (Method m : methods) System.out.println(m);
// 輸出: void com.zj.sample.Point.output()

五、實戰:完全動態實例化與調用(代碼中零硬編碼類名)

Class c = Class.forName("com.zj.sample.Point"); // 假設類名來自配置文件
Constructor[] cons = c.getDeclaredConstructors();
Constructor targetCon = cons[0]; // 取第一個構造器

// 分析構造器參數類型
Class[] paramTypes = targetCon.getParameterTypes();
Object[] paramValues = new Object[paramTypes.length];
for (int i = 0; i < paramTypes.length; i++) {
    if (paramTypes[i].isPrimitive()) {
        // 簡化:基本類型用包裝類默認值
        if (paramTypes[i] == int.class) paramValues[i] = 0;
        else if (paramTypes[i] == double.class) paramValues[i] = 0.0;
        // ... 其他基本類型
    } else {
        paramValues[i] = null; // 引用類型傳 null
    }
}

// 創建實例
Object obj = targetCon.newInstance(paramValues);

// 調用第一個方法(無參)
Method[] methods = c.getDeclaredMethods();
methods[0].invoke(obj, null); // 等價於 obj.output()

輸出:x=0,y=1(示例中構造器參數賦值邏輯導致)

此模式是插件系統、腳本引擎、框架核心(Spring IOC、MyBatis Mapper 代理)的基石。

六、性能與安全提示

  • 反射調用比直接調用慢 10-100 倍(JIT 優化有限),熱點路徑慎用
  • setAccessible(true) 可繞過訪問檢查,但破壞封裝,需謹慎
  • Java 9+ 模塊系統限制反射訪問非導出包,需 --add-opens 或 opens 聲明
  • 緩存 Class、Method、Constructor 對象,避免重複反射解析

掌握 Class 機制,即掌握了 Java 動態性的核心鑰匙。從簡單的插件加載到複雜的 AOP 代理、ORM 映射,無不建立在此基礎之上。

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