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
アクセス修飾子によりクラスがロードされない場合、変数はどのようにメモリを取得できますか?
static final int
フィールドはコンパイル時定数であり、その値はそのオリジンへの参照なしで宛先クラスにハードコーディングされています。具体的には、コンパイルされたバイトコードはこれに対応します。
public static void main(String arg[]){
System.out.println("**MAIN METHOD");
System.out.println(9090)
System.out.println(9190)
}
final
を削除するとすぐに、コンパイル時の定数ではなくなり、上記の特別な動作は適用されません。 Mno
クラスは期待どおりにロードされ、その静的初期化子が実行されます。
クラスがロードされない理由は、VAL
がfinal
[〜#〜] 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; }
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
Mno.VALはコンパイル時定数であるため、「11: sipush 9090
」で静的な最終値が直接使用されていることが明確にわかります。したがって、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)実際には、そのMnoクラスを拡張していないため、コンパイル開始時に変数VALの定数が生成され、その変数がメモリからロードされる必要があるときに実行が開始されるため、静的参照が実行されないようにクラス参照は必要ありません。
2)クラスがその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);
}
}
私の知る限り、それは出現順に実行されます。例えば :
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()"を実行しました)。