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

深度解析 java.lang.Class:J2ME 環境下的反射限制與類載入機制

     編輯:J2ME

前言

java.lang.Class 是 Java 反射的核心入口。在 J2SE 中它提供豐富的 API(getFields()、getMethods()、getClassLoader() 等),但在 CLDC/MIDP 環境下,為了精簡體積,Class 被大幅裁剪。本文結合原始碼片段,剖析 J2ME 版 Class 的結構特徵、類初始化流程與開發實務上的限制。

一、關鍵原始碼節錄與註解

public final class Class {
    private Class() {}                     // 私有建構子,禁止外部實例化

    public static native Class forName(String className)
            throws ClassNotFoundException; // 核心靜態工廠:載入並回傳 Class 物件

    public native Object newInstance()
            throws InstantiationException, IllegalAccessException;

    public native boolean isInstance(Object obj);
    public native boolean isAssignableFrom(Class cls);
    public native boolean isInterface();
    public native boolean isArray();
    public native String getName();

    // 取得資源流(J2ME 常用於讀取 /res/ 目錄下的圖片、資料檔)
    public java.io.InputStream getResourceAsStream(String name) {
        try {
            if (name.length() > 0 && name.charAt(0) == '/') {
                name = name.substring(1);
            } else {
                String className = this.getName();
                int dotIndex = className.lastIndexOf('.');
                if (dotIndex >= 0) {
                    name = className.substring(0, dotIndex + 1).replace('.', '/') + name;
                }
            }
            return new ResourceInputStream(name);
        } catch (java.io.IOException x) {
            return null;
        }
    }

    // 類初始化狀態機
    private static final int VERIFIED = 1;
    private static final int IN_PROGRESS = 2;
    private static final int INITIALIZED = 3;
    private static final int ERROR = 4;

    private native Class getSuperclass();
    private transient Object vmClass;
    private int status;
    private Thread thread;

    void initialize() throws Throwable {
        verify();
        synchronized (this) {
            while (status == IN_PROGRESS && thread != Thread.currentThread()) {
                try { wait(); } catch (InterruptedException e) {}
            }
            if (status == IN_PROGRESS && thread == Thread.currentThread()) return;
            if (status == INITIALIZED) return;
            if (status == ERROR) throw new Error("NoClassDefFoundError:" + getName());

            status = IN_PROGRESS;
            thread = Thread.currentThread();
        }
        try {
            Class s = getSuperclass();
            if (s != null && s.status != INITIALIZED) s.initialize();
            invoke_clinit();               // 執行  靜態初始化區塊
            synchronized (this) {
                status = INITIALIZED;
                thread = null;
                notifyAll();
            }
        } catch (Throwable e) {
            synchronized (this) {
                status = ERROR;
                thread = null;
                notifyAll();
                throwError(e);
            }
        }
    }

    synchronized void verify() throws Throwable {
        if (status < VERIFIED) {
            getSuperclass().verify();
            invoke_verifier();             // 位元碼驗證
            status = VERIFIED;
        }
    }
    // ... 省略部分私有方法
}

二、J2ME 版 Class 的三大特徵

  1. 無公共建構子:private Class() 確保只能由 JVM 透過 forName() 或類載入過程產生實例。
  2. 極度精簡的反射 API:僅保留 forName()、newInstance()、isInstance()、isAssignableFrom()、isInterface()、isArray()、getName()、getResourceAsStream()。無法列舉欄位、方法、建構子,也無 getClassLoader()。
  3. 雙階段初始化:Verify → Initialize:原始碼中 initialize() 先呼叫 verify()(遞迴驗證父類別),再執行 invoke_clinit()。狀態機(VERIFIED、IN_PROGRESS、INITIALIZED、ERROR)確保多執行緒安全與循環依賴檢測。

三、實務影響與應對策略

限制影響替代方案
無法取得欄位/方法清單無法實作通用序列化、ORM、依賴注入框架採用固定協議(如手寫 toByteArray()/parse())、或編譯期產生代碼(Annotation Processor / Polish 預處理)
無 ClassLoader無法動態載入外部 JAR、熱插拔所有類別必須打包在同一 JAR;若需模組化,請在建構期用 Ant/Polish 依條件編譯
無 getClassLoader()無法用 ClassLoader.getResourceAsStream() 統一讀取資源統一用 Class.getResourceAsStream(),資源放在與類別相同包路徑或 /res/ 根路徑

四、類載入順序補充

J2ME 裝置通常單一 JVM 執行單一 MIDlet,載入順序為:

  1. 系統核心類別(java.*、javax.microedition.*)
  2. 應用程式類別(依 forName() 或首次主動使用觸發)

因無複雜類載入器層級,ClassNotFoundException 多半源於:

  • JAD/JAR 中 MIDlet-n 類別名拼寫錯誤
  • ProGuard 混淆時誤刪未被靜態引用但以反射 forName() 載入的類別 → 需在 proguard.cfg 加入 -keep class ** { *; } 或保留特定類別。

結語

理解 J2ME 版 Class 的精簡本質,能避開「想用反射卻發現 API 不存在」的坑。設計階段即以「靜態綁定、建構期代碼產生」取代動態反射,是輕量級 Java 環境的最佳實踐。

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