実行されていないJavaの静的ブロック


87
class Test {
    public static void main(String arg[]) {    
        System.out.println("**MAIN METHOD");
        System.out.println(Mno.VAL); // SOP(9090);
        System.out.println(Mno.VAL + 100); // SOP(9190);
    }

}

class Mno {
    final static int VAL = 9090;
    static {
        System.out.println("**STATIC BLOCK OF Mno\t: " + VAL);
    }
}

staticクラスが読み込まれたときにブロックが実行されることを知っています。しかし、この場合、クラス内のインスタンス変数がMnoあるfinalそのため、staticブロックが実行されていません。

どうしてこんなことに?また、を削除した場合final、問題なく動作しますか?

static final変数とstaticブロックのどちらが最初に割り当てられますか?

finalアクセス修飾子が原因でクラスがロードされない場合、変数はどのようにしてメモリを取得できますか?


1
あなたが得る正確なエラーとメッセージは何ですか?
Patashu 2013年

@Patashu、エラーはありません、その疑い
Sthita 2013年

回答:


132
  1. static final intフィールドは、コンパイル時定数とその値は、その起源を参照することなく先クラスにハードコードされています。
  2. したがって、メインクラスはフィールドを含むクラスの読み込みをトリガーしません。
  3. したがって、そのクラスの静的初期化子は実行されません。

具体的には、コンパイルされたバイトコードはこれに対応します。

public static void main(String arg[]){    
    System.out.println("**MAIN METHOD");
    System.out.println(9090)
    System.out.println(9190)
}

を削除するとfinal、それはコンパイル時の定数ではなくなり、上記の特別な動作は適用されなくなります。Mnoこのクラスは、あなたが期待するようにロードして実行初期化子その静的されます。


1
しかし、クラスの最後の変数の値は、クラスを読み込まずにどのように評価されるのでしょうか。
Sumit Desai 2013年

18
すべての評価はコンパイル時に行われ、最終結果は変数を参照するすべての場所にハードコードされます。
Marko Topolnik 2013年

1
したがって、プリミティブ変数の代わりに、それが何らかのオブジェクトである場合、そのようなハードコーディングは不可能です。だよね?それで、その場合、そのクラスがロードされ、静的ブロックが実行されますか?
Sumit Desai 2013年

2
Marko、Sumitの疑いは、プリミティブではなくオブジェクトである場合にも当てはまります。このようなハードコーディングは不可能です。だよね?それで、その場合、そのクラスがロードされ、静的ブロックが実行されますか?
Sthita 2013年

8
@SumitDesaiまさに、これはプリミティブ値と文字列リテラルに対してのみ機能します。詳細については、Java言語仕様の関連する章を参照してください
Marko Topolnik '31

8

クラスがロードされない理由は、それVALfinal ANDであり、定数式(9090)で初期化されているためです。これらの2つの条件が満たされた場合に限り、定数はコンパイル時に評価され、必要に応じて「ハードコード」されます。

コンパイル時に式が評価されないようにするには(およびJVMにクラスをロードさせるには)、次のいずれかを実行できます。

  • 最後のキーワードを削除します。

    static int VAL = 9090; //not a constant variable any more
  • または、右側の式を定数でないものに変更します(変数がまだ最終的な場合でも)。

    final static int VAL = getInt(); //not a constant expression any more
    static int getInt() { return 9090; }
    

5

を使用して生成されたバイトコードを確認するとjavap -v Test.class、main()は次のようになります。

public static void main(java.lang.String[]) throws java.lang.Exception;
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=1, args_size=1
         0: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
         3: ldc           #3                  // String **MAIN METHOD
         5: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
         8: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        11: sipush        9090
        14: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        17: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        20: sipush        9190
        23: invokevirtual #5                  // Method java/io/PrintStream.println:(I)V
        26: return        

11: sipush 9090Mno.VALはコンパイル時定数なので、「」で静的最終値が直接使用されていることがはっきりとわかります。したがって、Mnoクラスをロードする必要はありません。したがって、Mnoの静的ブロックは実行されません。

以下のようにMnoを手動でロードすることにより、静的ブロックを実行できます。

class Test{
    public static void main(String arg[]) throws Exception {
        System.out.println("**MAIN METHOD");
        Class.forName("Mno");                 // Load Mno
        System.out.println(Mno.VAL);
        System.out.println(Mno.VAL+100);
    }

}

class Mno{
    final static int VAL=9090;
    static{
        System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
    }
}

1
  1. 実際には、そのMnoクラスを拡張していないため、コンパイルが開始されると変数VALの定数が生成され、その変数が必要なときに実行が開始されると、メモリからの負荷が発生します。したがって、静的ブロックが実行されないようにクラスを参照する必要はありません。

  2. class Aがclassを拡張する場合Mno、静的ブロックはクラスに含まれAます。これを行うと、その静的ブロックが実行されます。例えば..

    public class A extends Mno {
    
        public static void main(String arg[]){    
            System.out.println("**MAIN METHOD");
            System.out.println(Mno.VAL);//SOP(9090);
            System.out.println(Mno.VAL+100);//SOP(9190);
        }
    
    }
    
    class Mno {
        final static int VAL=9090;
        static {
            System.out.println("**STATIC BLOCK OF Mno\t:"+VAL);
        }
    }
    

0

私の知る限り、出現順に実行されます。例えば ​​:

 public class Statique {
     public static final String value1 = init1();

     static {
         System.out.println("trace middle");
     }
     public static final String value2 = init2();


     public static String init1() {
         System.out.println("trace init1");
         return "1";
     }
     public static String init2() {
         System.out.println("trace init2");
         return "2";
     }
 }

印刷します

  trace init1
  trace middle
  trace init2

テストしたところ、クラス「Statique」が実際に使用され、別のコードで「実行」されたときに静的変数が初期化(=>印刷)されました(私の場合、「new Statique()」を実行しました)。


2
を実行してStatiqueクラスをロードしているため、この出力が得られますnew Statique()。質問されている間、Mnoクラスはまったくロードされません。
RAS 2013年

@Fabyen、このようなテストクラスでMnoのオブジェクトを作成している場合:Mno anc = New Mno(); 次に、その罰金ですが、現在のシナリオではそれを行っていません。最終的に削除する場合、静的ブロックは問題なく実行されます。そうでない場合、実行されません。なぜですか?
Sthita 2013年

1
以下のうん答えは完璧です。Main.classのバイトコード(Mno.VALを使用)では、9090がハードコードされています。finalを削除してコンパイルし、javap Mainを使用すると、getstatic#16が表示されます。//フィールドStatique.VAL:I。最終版に戻し、コンパイルしてから、javap Mainを使用すると、sipush 9090が表示されます。
Fabyen 2013年

1
Main.classにハードコーディングされているため、クラスMNOをロードする理由はなく、静的な初期化はありません。
Fabyen 2013年

これは2番目の質問に答えます。「どのメモリが最初に割り当てられるか、静的な最終変数または静的ブロック?」(語彙順)
Hauke Ingmar Schmidt 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.