4

「Javaチュートリアル-フィールドの初期化」に、インスタンス初期化ブロック(インスタンス初期化子)についての説明があります。

Javaコンパイラは、初期化ブロックをすべてのコンストラクタにコピーします。したがって、このアプローチを使用して、複数のコンストラクター間でコードのブロックを共有できます。

言い換えが正しければ、次のコードについて:</ p>

public class ConstructorTest {

    public static void main(String[] args) {
        Child c = new Child();
    }
}

class Parent {
    Parent() {
        System.out.println("Parent non-argument Constructor");
    }
}

class Child extends Parent {

    {
        System.out.println("Child Instance Initialization Block");
    }

    Child() {
        this(2);
        System.out.println("Child no-argument Constructor");

    }

    Child(int i) {
        this(10, i);
        System.out.println("Child 1-argument Constructor");
    }

    Child(int i, int j) {
        System.out.println("Child 2-argument Constructor");
    }
}

出力は次のようになります:</ p>

Parent non-parm Constructor
Child Instance Initialization Block
Child 2-argument Constructor
Child Instance Initialization Block
Child 1-argument Constructor
Child Instance Initialization Block
Child no-argument Constructor

しかし、実際の出力は次のとおりです:</ p>

Parent non-argument Constructor
Child Instance Initialization Block
Child 2-argument Constructor
Child 1-argument Constructor
Child no-argument Constructor

その文の意味を誤解したか、説明が十分に正確ではありませんか?</ p>

そして、明示的なコンストラクターの呼び出しに関するもう1つの疑問:

2つの基本に基づく:</ p>

  • 存在する場合、別のコンストラクターの呼び出しは、コンストラクターの最初の行である必要があります。
  • コンストラクター内から、this()を使用して別のコンストラクターを呼び出し、super()を使用して直接スーパークラスの対応するコンストラクターを呼び出します。

MEANSがサブクラスのコンストラクター内でthis()を使用すると、スーパークラスの引数なしのコンストラクターへのデフォルトの呼び出しが暗黙的に削除されますか?

返信いただきありがとうございます。

4

3 に答える 3

5

編集: 読みにくいですが、結局のところ、JLS は正確であることがわかります。詳細はセクション 12.5で説明しています。

新しく作成されたオブジェクトへの参照が結果として返される直前に、指定されたコンストラクターが処理され、次の手順を使用して新しいオブジェクトが初期化されます。

  1. コンストラクターの引数を、このコンストラクター呼び出し用に新しく作成されたパラメーター変数に割り当てます。

  2. このコンストラクターが、(this を使用して) 同じクラス内の別のコンストラクターの明示的なコンストラクター呼び出し (§8.8.7.1) で始まる場合、引数を評価し、これらの同じ 5 つの手順を使用してそのコンストラクター呼び出しを再帰的に処理します。そのコンストラクターの呼び出しが突然完了すると、このプロシージャは同じ理由で突然完了します。そうでない場合は、ステップ 5 に進みます。

  3. このコンストラクターは、(this を使用して) 同じクラス内の別のコンストラクターの明示的なコンストラクター呼び出しで開始されません。このコンストラクターが Object 以外のクラス用である場合、このコンストラクターは、(super を使用して) スーパークラス コンストラクターの明示的または暗黙的な呼び出しから開始します。これらの同じ 5 つの手順を使用して、引数を評価し、そのスーパークラス コンストラクター呼び出しを再帰的に処理します。そのコンストラクターの呼び出しが突然完了すると、このプロシージャは同じ理由で突然完了します。それ以外の場合は、ステップ 4 に進みます。

  4. このクラスのインスタンス初期化子とインスタンス変数初期化子を実行し、インスタンス変数初期化子の値を対応するインスタンス変数に割り当てます。この順序は、クラスのソース コードにテキストで表示される左から右の順序です。これらの初期化子のいずれかを実行すると例外が発生した場合、それ以上の初期化子は処理されず、このプロシージャは同じ例外で突然終了します。それ以外の場合は、ステップ 5 に進みます。

  5. このコンストラクターの残りの本体を実行します。その実行が突然完了した場合、このプロシージャは同じ理由で突然完了します。それ以外の場合、この手順は正常に完了します。

強調表示された部分に注意してください - 連鎖コンストラクターが実行され、インスタンス初期化子を実行するステップ 4 をスキップします

実際には、出力からわかるように、インスタンスとフィールドの初期化子は 1 回だけ実行されます。

非公式には、手順を次のように説明するのが正確だと思います。

  • this(...)で始まらないコンストラクター本体に到達するまで、同じクラス ( ) 内でコンストラクターをチェーンし続けますthis
  • 適切なスーパーコンストラクターを実行する
  • インスタンス変数初期化子とインスタンス初期化子を実行する
  • 「最も内側」のコンストラクターの本体を実行する
  • 「エントリ」コンストラクターになるまで、コンストラクター本体のスタックをポップし続けます

サブクラスのコンストラクター内で this() を使用すると、スーパークラスの引数のないコンストラクターへのデフォルトの呼び出しが暗黙的に削除されるということですか?

はい。クラス内のコンストラクターのチェーンのどこかで、super暗黙的または明示的に呼び出すコンストラクターになることが保証されています。これは、呼び出される唯一のスーパークラス コンストラクターです。

編集:引用したチュートリアルは明らかに間違っていることに注意してください。

サンプルクラス:

public class Test {
    {
        System.out.println("Foo");
    }
    
    public Test() {
    }
    
    public Test(int i)  {
        this();
    }
}

からの出力javap -c:

public class Test {
  public Test();
    Code:
       0: aload_0
       1: invokespecial #1  // Method java/lang/Object."<init>": ()V
       4: getstatic     #2  // Field java/lang/System.out:Ljava/io/PrintStream;
       7: ldc           #3  // String Foo
       9: invokevirtual #4  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      12: return

  public Test(int);
    Code:
       0: aload_0
       1: invokespecial #5                  // Method "<init>":()V
       4: return
}

ご覧のとおり、コンストラクターには、インスタンス コンストラクターのコードがコンパイルされてTest(int)いません。

基本的に、スーパークラス コンストラクターを直接呼び出すコンストラクターのみに、インスタンス初期化コードがコピーされます。もちろん、他のすべてのコンストラクターは、スーパークラス コンストラクターを呼び出すコンストラクターを介してインスタンス初期化コードを実行することになります。

于 2012-09-03T20:38:53.523 に答える
0

初期化ブロックは、クラスのオブジェクトをインスタンス化するときに 1 回だけ実行されます (任意のコンストラクターを使用する <= ここでは疑問です)。静的初期化ブロックは、クラスがクラス ローダーによってロードされるときに 1 回だけ実行されます。

サブクラスのコンストラクター内で this() を使用すると、スーパークラスの引数のないコンストラクターへのデフォルトの呼び出しが暗黙的に削除されるということですか?

いいえ、 this() は同じクラスの別のコンストラクターを呼び出します。この場合、デフォルトのコンストラクター (存在する場合) を呼び出します。このコンストラクター (デフォルトのコンストラクター) は、super() を呼び出します。

于 2012-09-03T20:38:21.230 に答える
0

Java 言語仕様の方が正確だと思います: http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.6

クラスで宣言されたインスタンス初期化子は、クラスのインスタンスが作成されるときに実行されます

http://docs.oracle.com/javase/specs/jls/se7/html/jls-12.html#jls-12.5の手順 4 と 5では、観察した出力について説明します。

4 . このクラスのインスタンス初期化子とインスタンス変数初期化子を実行します ...

5. このコンストラクタの残りの本体を実行します ...

于 2012-09-03T20:53:21.650 に答える