Checks.y
(サブクラスであるy
)を使用して静的変数Checks
を呼び出すと、静的ブロックは実行されず、y
の値は実行されません更新されます。
class Par {
static int y = 4;
}
class Checks extends Par {
static {
y = 5;
}
}
public class Check {
public static void main(String args[]) {
System.out.println(Checks.y); // here printing 4
}
}
静的はすべてのサブクラス間で共有されるため、値は更新されることになっています。
その背後にある理由は何でしょうか?
フィールドy
は、クラスChecks
によって宣言されていません。
静的フィールドの読み取りは、そのクラスがフィールドが宣言されているクラスでない限り、参照されるクラス(Checks
)の初期化をトリガーしません(以下のJLSの引用を参照)。この例では、y
がChecks
を介してアクセスされた場合でも、Par
はPar
を宣言するクラスであるため、y
の初期化のみをトリガーします。 ] _。
言い換えれば、クラスChecks
はある意味では実行時に使用されません。
これはおそらく、サブクラスを介してstatic
メンバーにアクセスするのが間違っている理由の1つの例であり、コンパイル時の警告の原因となります。
仕様 には簡単な説明があります:
12.4.1。初期化が発生する場合
クラスまたはインターフェイスタイプTは、次のいずれかが最初に発生する直前に初期化されます。
Tはクラスであり、Tのインスタンスが作成されます。
Tによって宣言された静的メソッドが呼び出されます。
Tによって宣言された静的フィールドが割り当てられます。
Tによって宣言された静的フィールドが使用され、そのフィールドは定数変数ではありません(4.12.4)。
Tは最上位クラス(§7.6)であり、T(§8.1.3)内で字句的にネストされたassertステートメント(§14.10)が実行されます。
...
静的フィールドへの参照(8.3.1.1)は、サブクラス、サブインターフェイス、またはクラスの名前で参照される場合でも、実際に宣言するクラスまたはインターフェイスのみを初期化しますインターフェースを実装します。
最後のメモは、サブクラスが初期化されない理由を説明しています。
JLS 12.4.1 から:
クラスまたはインターフェイスタイプTは、次のいずれかが最初に発生する直前に初期化されます。
- Tはクラスであり、Tのインスタンスが作成されます。
- Tはクラスであり、Tによって宣言された静的メソッドが呼び出されます。
- Tによって宣言された静的フィールドが割り当てられます。
- Tによって宣言された静的フィールドが使用され、そのフィールドは定数変数ではありません(4.12.4)。
- Tはトップレベルクラス(§7.6)であり、T(§8.1.3)内で字句的にネストされたassertステートメント(§14.10)が実行されます。
Yはチェックで宣言されていないため、上記の基準はいずれも満たされていません。
この動作を説明する別の方法:
class par {
static int y = 4;
static {
System.out.println("static constructor of par");
}
}
class checks extends par {
static int x = 6;
static {
System.out.println("checks static constructor");
y = 5;
}
}
public class check{
public static void main(String args[]){
System.out.println(checks.y);
System.out.println(checks.x);
System.out.println(checks.y);
}
}
出力
static constructor of par
4
checks static constructor
6
5
したがって、2番目のルールを満たすchecks.x
を呼び出した後、静的コンストラクターが呼び出されます。
ここに:
System.out.println(checks.y); // Here printing 4
y
は、par
クラスのフィールドを指します。このフィールドアクセスにより、 [〜#〜] jls [〜#〜] (強調は私のもの)に従ってpar
クラス(親クラス)がロードされます。
12.4。クラスとインターフェースの初期化
....
12.4.1。初期化が発生するとき
クラスまたはインターフェイスタイプTは、次のいずれかが最初に発生する直前に初期化されます。Tはクラスであり、Tのインスタンスが作成されます。 Tによって宣言された静的メソッドが呼び出されます。
Tによって宣言された静的フィールドが割り当てられます。
Tによって宣言された静的フィールドが使用され、そのフィールドは定数変数ではありません(§4.12.4)。
Tは最上位クラス(§7.6)であり、T(§8.1.3)内で字句的にネストされたassertステートメント(§14.10)が実行されます。
しかし、それはchecks
クラスをロードしません(強調は私のものです):
静的フィールド(8.3.1.1)への参照は、実際にそれを宣言するクラスまたはインターフェイスのみを初期化しますサブクラス、サブインターフェース、またはインターフェースを実装するクラスの名前を通して。
static
クラスのchecks
ブロックが実行されないためです。クラスchecks
について言及しましたが、JVMはそれをまったくロードしません。
これを確認するには、静的ブロック内に別のSystem.out.println
を追加します。
class checks extends par {
static {
System.out.println("Test");
y = 5;
}
}
Word Test
は印刷されません。
セクションを読んでください 12.4.1。初期化が発生するとき Java言語仕様もっと知る。
これまで言及されていなかった、新しいJavaプログラマーを混乱させるかもしれない側面:ソースコードをどのように編成するかは、ある程度重要ではありません!
1つのファイルまたは1つの例に、2つのクラス(Java命名規則)に従う親と子の名前である必要があります)があります。ランタイム。
ただし、実行時には、クラスごとに個別のクラスファイルがあります。そして、他の人が言ったように:コード内の何も子クラスを参照していません。したがって、そのクラスはロードされないため、割り当ては発生しません!
他の人が言及するように、クラスが answered before として初期化されていないため、静的ブロックは実行されません。
クラスの規則名は大文字で始まることに注意してください。
オーバーライドクラスを示す明確な例は使用されません。
class Par {
static int y = 4;
public static void main(String args[]) {
System.out.println(Checks.y); // Here printing 4
System.out.println(new Checks().y); // Here printing 5
}
}
class Checks extends Par {
static {
y = 5;
}
}