12

デフォルトでは、Sun の JVM は、クラスを遅延ロードし、遅延初期化 (つまり、それらの<clinit>メソッドを呼び出す) の両方を行います。ブロック中にClinitBombをスローする次のクラスを考えてみましょう。Exceptionstatic{}

public class ClinitBomb {
    static {
        explode();
    }   
    private static void explode() {
        throw new RuntimeException("boom!");
    }       
}

ここで、爆弾をトリガーする方法を検討してください。

public class Main {
    public static void main(String[] args) {
        System.out.println("A");
        try {
            Class.forName("ClinitBomb");
        } catch (Exception e) {
            e.printStackTrace(System.out);
        }
        System.out.println("B");
        ClinitBomb o2 = new ClinitBomb();
        System.out.println("C");
    }
}

forNameのドキュメントにそう書かれているので、点 B の前に爆発が起こることが保証されています。問題は、ポイント A の前 ( がロードされるとき) に発生するかどうかMainです。Sun の JVM では、main()への静的参照が含まれていても、ポイント A のClinitBomb後に発生します。

JVM が初期化ClinitBombされるとすぐにロードして初期化するように指示する方法が必要です (したがって、爆弾はポイント AのMainで爆発します)。クラス Y 参照します。」

それを行う方法はありますか?

4

2 に答える 2

10

これを行う方法はありません。JLSは、§12.4.1で初期化が発生したとき(私の強調):

クラスの初期化は、その静的初期化子と、クラスで宣言された静的フィールドの初期化子の実行で構成されます。[...]

クラスまたはインターフェイスタイプTは、次のいずれかが最初に発生する直前に初期化されます

  • Tはクラスであり、Tのインスタンスが作成されます。
  • Tはクラスであり、Tによって宣言された静的メソッドが呼び出されます。
  • Tによって宣言された静的フィールドが割り当てられます。
  • Tによって宣言された静的フィールドが使用され、フィールドは定数変数ではありません(§4.12.4)。
  • Tは最上位クラスであり、T内に字句的にネストされたassertステートメント(§14.10)が実行されます。

クラスClassおよびパッケージjava.lang.reflectで特定のリフレクティブメソッドを呼び出すと、クラスまたはインターフェイスが初期化されます。クラスまたはインターフェースは、他の状況では初期化されません

クラスがロードされるとすぐにクラスを初期化するJava実装は、JLSに違反します。

ただし、JVMインストルメンテーションAPIを使用して、参照されるクラスを明示的に初期化するすべてのクラスに静的ブロックを追加するClassFileTransformerを作成することもできます(おそらくClass.forNameを介して)。1つのクラスが初期化されるとすぐに、そこから到達可能なすべてのクラスが初期化されます。それはあなたが求めている結果をあなたに与えるかもしれません。しかし、それはかなりの作業です!

于 2011-12-13T23:35:46.563 に答える
1
Class.forName("...", true /*initialize*/, getClassLoader());

あなたは途中でそこにいました。

于 2011-12-13T23:34:32.847 に答える