web-dev-qa-db-ja.com

Javaで子クラスを初期化しても、静的変数の値は変更されません

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
    }
}

静的はすべてのサブクラス間で共有されるため、値は更新されることになっています。

その背後にある理由は何でしょうか?

32
rawat

フィールドyは、クラスChecksによって宣言されていません。

静的フィールドの読み取りは、そのクラスがフィールドが宣言されているクラスでない限り、参照されるクラス(Checks)の初期化をトリガーしません(以下のJLSの引用を参照)。この例では、yChecksを介してアクセスされた場合でも、ParParを宣言するクラスであるため、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)は、サブクラス、サブインターフェイス、またはクラスの名前で参照される場合でも、実際に宣言するクラスまたはインターフェイスのみを初期化しますインターフェースを実装します。

最後のメモは、サブクラスが初期化されない理由を説明しています。

29
ernest_k

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を呼び出した後、静的コンストラクターが呼び出されます。

9
Diadistis

ここに:

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)への参照は、実際にそれを宣言するクラスまたはインターフェイスのみを初期化しますサブクラス、サブインターフェース、またはインターフェースを実装するクラスの名前を通して。

4
davidxxx

staticクラスのchecksブロックが実行されないためです。クラスchecksについて言及しましたが、JVMはそれをまったくロードしません。

これを確認するには、静的ブロック内に別のSystem.out.printlnを追加します。

class checks extends par {

    static {
        System.out.println("Test");
        y = 5;
    }
}

Word Testは印刷されません。


セクションを読んでください 12.4.1。初期化が発生するとき Java言語仕様もっと知る。

4

これまで言及されていなかった、新しいJavaプログラマーを混乱させるかもしれない側面:ソースコードをどのように編成するかは、ある程度重要ではありません!

1つのファイルまたは1つの例に、2つのクラス(Java命名規則)に従う親と子の名前である必要があります)があります。ランタイム。

ただし、実行時には、クラスごとに個別のクラスファイルがあります。そして、他の人が言ったように:コード内の何も子クラスを参照していません。したがって、そのクラスはロードされないため、割り当ては発生しません!

2
GhostCat

他の人が言及するように、クラスが 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;
    }
}
0
user7294900