5 に答える
JLS 12.4.1から:
クラスまたはインターフェイスの型 T は、次のいずれかが最初に発生する直前に初期化されます。
- T はクラスであり、T のインスタンスが作成されます。
- T はクラスであり、T によって宣言された静的メソッドが呼び出されます。
- T によって宣言された static フィールドが割り当てられます。
- T によって宣言された静的フィールドが使用され、そのフィールドは定数変数ではありません (§4.12.4)。
- T は最上位クラスであり、T 内でレキシカルにネストされた assert ステートメント (§14.10) が実行されます。
ご覧のとおり、これらはコード内では何も発生しないため (ではなく でname
宣言されていることに注意してください)、したがっては初期化されず、静的ブロックは実行されません。Parent
Child
Child
の初期化をトリガーするために何かを行うとChild
、期待される出力が得られます。
new Child();
System.out.println(Child.name);
ただし、静的フィールドは継承されないためChild.name
、Parent.name
実際には同じフィールドを参照することに注意してください。そのため、実際にあなたの例に似たコードを使用してもあまり意味がありません。
Child.name
また、 が実際に を参照しているにもかかわらず、バイトコードParent.name
のように参照さChild.name
れているため、コードは のロードをトリガーしますChild
が、初期化はトリガーしません。
As Child.name
is really Parent.name
, Child is not needed.
You might find this interesting.
public class ClassResolution {
static class Parent {
public static String name;
static {
System.out.println("this is Parent");
name = "Parent";
}
}
static class Child extends Parent {
static {
System.out.println("this is Child");
name = "Child";
}
static String word ="hello";
}
public static void main(String[] args) {
System.out.println(Child.name);
System.out.println(Child.word);
System.out.println(Child.name);
}
}
prints
this is Parent
Parent
this is Child
hello
Child
The javap
for this class prints that the references to Child
are retained.
C:\>javap -c -classpath . ClassResolution
Compiled from "ClassResolution.java"
public class ClassResolution {
public ClassResolution();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: getstatic #3 // Field ClassResolution$Child.name:Ljava/lang/String;
6: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
9: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
12: getstatic #5 // Field ClassResolution$Child.word:Ljava/lang/String;
15: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
18: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
21: getstatic #3 // Field ClassResolution$Child.name:Ljava/lang/String;
24: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
27: return
}
静的フィールド (§8.3.1.1) への参照は、サブクラス、サブインターフェイス、またはインターフェイスを実装するクラスの名前を介して参照される可能性がある場合でも、実際にそれを宣言するクラスまたはインターフェイスのみを初期化します。
私は上記がそれをすべて言っていると思います..
つまり、Child.name
は と等しくParent.name
、コンパイラはそのようにコンパイルします。
name
は Parent クラスの static フィールドであるため、Child ではなく、Parent のクラス メソッドです。Java コンパイラーは、子クラスが静的な親クラスのメソッド/フィールドを独自のクラスからのものであるかのように呼び出すことができるショートカットを許可しますが、内部的には親クラスに関してコンパイルされます。
コードは を参照していますが、コンパイラによって処理されるように、Child.name
内部的にはです。Parent.name
その後、Child クラスが初期化されないため、<clinit>
静的初期化ブロックは実行されず、name
「親」のままになります。
おっと、私は間違っていました。Child への参照は削除されず、クラスは実際にロードされましたが、初期化されていませんでした。おめでとうございます。JVM のバグが見つかりました。Oracle サイトにアクセスしてファイルします。やりたくない場合は言ってください、私は自分でやります。
編集:私はまた間違っていました。以下のコメントを参照してください。これはバグではなく、「落とし穴」です。