3
4

5 に答える 5

4

JLS 12.4.1から:

クラスまたはインターフェイスの型 T は、次のいずれかが最初に発生する直前に初期化されます。

  • T はクラスであり、T のインスタンスが作成されます。
  • T はクラスであり、T によって宣言された静的メソッドが呼び出されます。
  • T によって宣言された static フィールドが割り当てられます。
  • T によって宣言された静的フィールドが使用され、そのフィールドは定数変数ではありません (§4.12.4)。
  • T は最上位クラスであり、T 内でレキシカルにネストされた assert ステートメント (§14.10) が実行されます。

ご覧のとおり、これらはコード内では何も発生しないため (ではなく でname宣言されていることに注意してください)、したがっては初期化されず、静的ブロックは実行されません。ParentChildChild

の初期化をトリガーするために何かを行うとChild、期待される出力が得られます。

new Child();
System.out.println(Child.name); 

ただし、静的フィールドは継承されないためChild.nameParent.name実際には同じフィールドを参照することに注意してください。そのため、実際にあなたの例に似たコードを使用してもあまり意味がありません。

Child.nameまた、 が実際に を参照しているにもかかわらず、バイトコードParent.nameのように参照さChild.nameれているため、コードは のロードをトリガーしますChildが、初期化はトリガーしません。

于 2012-09-21T12:06:28.697 に答える
3

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
}
于 2012-09-21T11:53:59.950 に答える
2

JLS#12.4.1. 初期化が発生する場合

静的フィールド (§8.3.1.1) への参照は、サブクラス、サブインターフェイス、またはインターフェイスを実装するクラスの名前を介して参照される可能性がある場合でも、実際にそれを宣言するクラスまたはインターフェイスのみを初期化します。

私は上記がそれをすべて言っていると思います..

于 2012-09-21T12:07:05.047 に答える
1

つまり、Child.nameは と等しくParent.name、コンパイラはそのようにコンパイルします。

nameは Parent クラスの static フィールドであるため、Child ではなく、Parent のクラス メソッドです。Java コンパイラーは、子クラスが静的な親クラスのメソッド/フィールドを独自のクラスからのものであるかのように呼び出すことができるショートカットを許可しますが、内部的には親クラスに関してコンパイルされます。

コードは を参照していますが、コンパイラによって処理されるように、Child.name内部的にはです。Parent.nameその後、Child クラスが初期化されないため、<clinit>静的初期化ブロックは実行されず、name「親」のままになります。

于 2012-09-21T11:58:29.910 に答える
0

おっと、私は間違っていました。Child への参照は削除されず、クラスは実際にロードされましたが、初期化されていませんでした。おめでとうございます。JVM のバグが見つかりました。Oracle サイトにアクセスしてファイルします。やりたくない場合は言ってください、私は自分でやります。

編集:私はまた間違っていました。以下のコメントを参照してください。これはバグではなく、「落とし穴」です。

于 2012-09-21T12:29:46.120 に答える