web-dev-qa-db-ja.com

Javaの継承により作成されるオブジェクトの数

私には3つのクラスがあるとしましょう:

class A {
    A() {
        // super(); 
        System.out.println("class A");
    }
}
class B extends A {
    B() {
        // super(); 
        System.out.println("class B");
    }
}
class C extends B {
    public static void main(String args[]) {
        C c = new C(); //Parent constructor will get called
    }
}

クラスCのインスタンスを作成すると、スーパークラスのコンストラクターが呼び出されます。では、作成されているオブジェクトは複数ありますか? 1つのオブジェクトのみが作成される場合、super()は他のクラスのコンストラクターとどのように似ていますか? super()メソッドは内部的にオブジェクトを作成しますか?私が知っているのは、コンストラクターもメソッドであるということです(間違っているかもしれません)。

私の質問は:

  1. この場合、いくつのオブジェクトが作成されますか?
  2. 1つのオブジェクトが作成された場合、Super()は内部的に親クラスコンストラクターをどのように呼び出しますか?
53
Java_begins

いい質問ですね。調査しているのは、Javaオブジェクトを初期化する方法)であり、多くの手順が含まれます。

コンストラクターもメソッドであることがわかります(たぶん私は間違っています)。

ほぼ正しい。コンストラクターは特別なメソッドです。クラスファイルを逆コンパイルすると、コンストラクターの名前が_<init>_に変更されます。 _<init>_は他のメソッドとは異なる方法で処理され、たとえば、キーワードnewまたはsuperを使用しない限り、明示的に呼び出すことはできません。これは非常に基本的なため、Java言語で定義されたものではなく、JVM自体に実装されます。

この場合に作成されるオブジェクトの数。

1つのオブジェクト-Cのインスタンスが作成されます。

Cはさらに、Bのインスタンスであり、Aのインスタンスであり、Objectのインスタンスでもあります。

1つのオブジェクトが作成された場合、super()が内部的にParentクラスConstructorを呼び出す方法。 Superが親クラスコンストラクターを呼び出す方法。

ここで初期化を開始します。初期化は、JVMがオブジェクトの新しいインスタンスを作成し、すべてのメンバー値(特定のクラスとスーパークラスのメンバー値)を設定する方法です。関連するいくつかの段階があります。

  • 参照されているすべてのクラスをロードし、それらのクラスを初期化します。クラスの初期化自体は重要なことなので、ここでは説明しません。読む価値は十分にあります。
  • インスタンスのメンバーを保持するためにメモリのチャンクを割り当てます。これには、AB、およびCのすべてのメンバーが含まれます。 [〜#〜] note [〜#〜]これは、質問の1つの側面を説明します。基本クラスとそのサブクラスのコンストラクターが同じオブジェクトを更新または参照する方法-すべてのクラスのインスタンスのすべてのメンバーは、メモリの同じチャンクに次々に保存されます
  • すべてのメンバーをそれらのデフォルト値に初期化します。たとえば、intおよびfloatメンバーは0および0.0fに設定されます。
  • メンバー初期化子を実行または計算します。例:

    _private int a = 10;
    private int b = a * 5;
    private String c = Singleton.getInstance().getValue();
    _
  • 注(1)メンバーの初期化は、クラスでメンバーが宣言された順序で厳密に行われます。これは、宣言の後半のメンバーへの参照が壊れていることを意味します。

    _private int a = b * 5; // Forward reference; won't compile
    private int b = 10;
    _
  • (2)Javaに未使用の機能があり、任意のコードを実行して値を初期化するbeforeこれらのコードブロックは、この時点で厳密に宣言順に実行されます。

    _private int a;
    private int b = 1;
    {
        // Initization occurs after b but before c.
        // c cannot be referenced here at all
        int i = SomeClass.getSomeStatic();
        a = i * 2;
    }
    private int c = 99;
    _
  • Cのコンストラクターを実行します。コンストラクターはスーパークラスからコンストラクターを直接呼び出す必要があります。そうしないと、コンパイラーはsuper()をコンストラクターの最初の行として自動的に追加します。これは、コンストラクターが次の順序で厳密に実行されることを意味します。

    1. Object
    2. A
    3. B
    4. C

これでオブジェクトが初期化され、使用できる状態になりました。インスタンスメソッドを使用して値を初期化すると、いくつかの危険なことができます。

_public class Wrong {
    int a = getB(); // Don't do this!
    int b = 10;
    public int getB() {
         return b;
    }
}
_

ここで、aは_0_に初期化されます。これは、getB()が呼び出された時点で、Javaはbの値をデフォルト(_0_)にクリアしたが、初期化の第2フェーズでは、まだ_10_に設定していません。

要約すると、オブジェクトは1つだけであり、段階的に作成および初期化されます。これらの段階では、オブジェクトは定義上、完全には定義されていません。

56
Andrew Alcock

コードでは、1つのオブジェクトのみが作成され、親クラスコンストラクターをスーパーコールします。

オブジェクト作成の証明:

_package one;

public class A {
    public static A super_var;

    public A() {
        super_var = this;
        System.out.println("Constrcutor of A invoked");
    }
}

package two;

public class B extends A {
    public static A sub_var;

    public B() {
        sub_var = this;
        System.out.println("Constructor of B invoked");
    }

    public void confirm() {
        if (sub_var == A.super_var)
            System.out.println("There is only one object is created");
        else
            System.out.println("There are more than one object created");
    }

    public static void main(String Args[]) {
        B x = new B();
        x.confirm();
    }
}
_

これにより、オブジェクトが1つしか作成されないことが証明されます。

そして、Super()について。私が知っていることは、それがParentクラスコンストラクタを呼び出すことです。そして、各コンストラクターは、コードで言及したように、最初のステートメントとしてSuper()を持ちます。あなたが知っているように

内部でどのようにスーパークラスコンストラクタを呼び出すのかわかりません。

これにより、プログラムで作成するインスタンスのみがあることを理解できることを願っています

6
twister_void
  1. 作成されるオブジェクトは1つだけです。オブジェクト。

  2. クラスAがBを拡張し、すべてのメソッドと変数がクラスAにコピーされる場合を想像できます。

6
pankaj
  1. あなたの場合のみ、1つのオブジェクトが作成されます。
  2. サブクラスコンストラクターが呼び出されると、スーパークラスのコンストラクターを内部的に呼び出して、スーパークラスのメンバーを初期化します。

コンストラクターを呼び出しても、オブジェクトを作成しているわけではありません。オブジェクトは、コンストラクターを呼び出すときにすでに作成されています。オブジェクトは、最初にJVMによって作成されます(つまり、ヒープにメモリが割り当てられ、コンストラクターが呼び出されます)。

コンストラクタはオブジェクトのメンバーを初期化するためのものです。

2
JRR

クラスは内部的にこのようなものに変換されます

_class A
{
    A(){
        super(); 
        System.out.println("class A");
    }
}

class B extends A{
    B(){
        super(); 
        System.out.println("class B");
    }
}

public class C extends B
{
    public static void main(String args[])
    {
        C c  = new C(); //Parent constructor will get call 
    }
}
_

この場合に作成されるオブジェクトの数。

Cのインスタンスであるsuper()だけを呼び出す1つだけinvokesconstructor of parent classオブジェクトを作成しません

1つのオブジェクトが作成された場合、Super()がどのように内部でParentクラスConstructorを呼び出しているか。 Superが親クラスコンストラクターを呼び出す方法。

Cのインスタンスを作成するとき。 Cのコンストラクターが呼び出され、最初にBのコンストラクターが呼び出され、次にAのコンストラクターが呼び出されます

2
sanbhat
  1. Case Oneオブジェクトで作成されます

  2. 次の処理中に、このsuper()はコンパイラによって暗黙的に提供されます

    class A {
    A() {
        System.out.println("class A");
    }
    }
    class B extends A {
    B() {
        System.out.println("class B");
    }
    }
    class C extends B {
    public static void main(String args[]) {
        C c = new C(); //
    }
    }
    

メソッド内でsuper()を呼び出すことに似ています

    B() {
        super();
        System.out.println("class B");
    }

Superキーワードは、現在のクラスでメソッドがオーバーライドされているが、スーパークラスメソッドを呼び出す場合にも使用できます。

super()は、すべてのコンストラクターが1つのクラスを参照するようにします。 (理解を容易にするため:すべてのメンバー関数が同じクラスの下にあるように。)すべてのコンストラクターメソッドのみを呼び出します。

そのため、コンストラクターの呼び出しのみが行われるため、super()はオブジェクトの作成を行いません。メンバー関数を参照するだけです。

1
Pandiyan Cool

コンストラクターを呼び出してオブジェクトを作成するときのオブジェクト作成の手順:

  1. initを使用したメモリ割り当てが行われます。このinitは、オブジェクト作成のためにメモリを割り当てるシステムコールを作成します。

  2. 次に、コンストラクタを呼び出してオブジェクトのフィールドを初期化します。

  3. 次に、スーパークラスコンストラクターを呼び出し(スーパークラスがある場合)、ステップ1から3を繰り返します。

javapを使用してクラスファイルを逆コンパイルすると、さまざまな呼び出しが表示されます。 initは、メモリ割り当てを初期化するシステムコールを行いますが、オブジェクトのフィールドは、コンストラクターのコードが実行されると初期化されます。

1
Ajay Bhojak

GCの時点でポリモーフィズム/オーバーライドがどのように機能するのかわかりません。

ただし、すべてのクラスでfinalizeメソッドをオーバーライドし、JVMがmainメソッドを終了するタイミングをチェックする価値があります。

  • Cオブジェクトのみが作成される場合、 'C'に対してfinalizeを呼び出す必要があります。
  • すべてのABCオブジェクトが作成された場合、finalizeAに対してBを呼び出す必要があります。 C

これはあなたが適用できる最も簡単なチェックだと思います。

class A {
    A() {
        //Super(); 
        System.out.println("class A");
    }

    public void finalize(){
    System.out.println("Class A object destroyed");
    }
}
class B extends A {
    B() {
       //Super(); 
        System.out.println("class B");
    }

    public void finalize(){
    System.out.println("Class B object destroyed");
    }
}
class C extends B {
    public static void main(String args[]) {
        C c = new C(); //Parent constructor will get call 
    }

    public void finalize(){
    System.out.println("Class C object destroyed");
    } 
}
1
Learn More

以前に投稿した回答に同意しますが、この問題に関する究極の権威であるJava Language Specification。

new C()は、「クラスインスタンス作成式」です。セクション 15.9.4クラスインスタンス作成式の実行時評価 は、オブジェクトの作成に関係する実行時ステップを説明しています。 「オブジェクト」を参照し、スペースを1回だけ割り当てるが、「次に、指定されたクラスタイプの選択されたコンストラクターが呼び出されます。これにより、クラスタイプのスーパークラスごとに少なくとも1つのコンストラクターが呼び出されます」

これはすべて、新しいオブジェクトの作成とコンストラクターの呼び出しを区別することで、より明確になります。コンストラクターの呼び出しは、オブジェクト作成の一部、初期化子、スーパークラスコンストラクター、およびコンストラクターの本体を実行する部分のみを行います。 CはBでもあるため、Cの作成中にBコンストラクターを実行する必要があります。

1

this SO answer)に従ってオブジェクト割り当てのダイナミクスを見る場合、new演算子を使用して、ステートメントごとに1つのオブジェクトのみを作成することを明確にする必要があります。作成されているオブジェクトが1つだけであるという疑いがある場合は、このプログラムを使用してください。

public class A {
    public static int aInstanceCount=0;
    public static A aInstance;
    public String aInstanceVariable;
    A() {
//Super();
        aInstanceCount++;
        aInstanceVariable="aInstanceVar";
        System.out.println("class A");
        aInstance=this;
    }
}

class B extends A {
    public static int bInstanceCount=0;
    public static B bInstance;
    public String bInstanceVariable;
    B() {
//Super();
        bInstanceCount++;
        bInstanceVariable="bInstanceVar";
        System.out.println("class B");
        bInstance=this;
    }
}

class C extends B {
    public static void main(String args[]) {
        int instanceCount=0;
        C c = new C(); //Parent constructor will get call
        if(A.aInstance!=null){
            instanceCount++;
            System.out.println("Value of aInstanceVariable: "+A.aInstance.aInstanceVariable);

        }
        if(B.bInstance!=null){
            instanceCount++;
            System.out.println("Value of bInstanceVariable: "+B.bInstance.bInstanceVariable);
        }
        A a=A.aInstance;
        B b=B.bInstance;
        System.out.println("bInstanceVariable of B earlier: " + B.bInstance.bInstanceVariable);
        //Now we are changing the bInstanceVariable of c which is inherited from B
        c.bInstanceVariable="bInstance After modified by C";
        System.out.println("bInstanceVariable of B after: " + B.bInstance.bInstanceVariable);
        System.out.println("aInstanceVariable of A earlier: " + A.aInstance.aInstanceVariable);
        //Now we are changing the aInstanceVariable of c which is inherited from A
        c.aInstanceVariable="aInstance After modified by C";
        System.out.println("bInstanceVariable of A after: " + A.aInstance.aInstanceVariable);
    }
}

出力:

class A
class B
Value of aInstanceVariable: aInstanceVar
Value of bInstanceVariable: bInstanceVar
bInstanceVariable of B earlier: bInstanceVar
bInstanceVariable of B after: bInstance After modified by C
aInstanceVariable of A earlier: aInstanceVar
bInstanceVariable of A after: aInstance After modified by C

サブクラスオブジェクトが作成されると、スーパーコンストラクターは毎回暗黙的に呼び出されますが、new演算子は1回しか使用されないため、実際にスペースが割り当てられるオブジェクトは1つだけです。そして、aInstanceVariableオブジェクトCを介してcを変更することにより、実際にaInstanceVariableaInstanceを変更しています。したがって、実際には1つのオブジェクトがあることが明らかになります。

1
rahulserver

Superキーワードを使用すると、サブクラスはそのスーパークラスのメソッドとフィールドを呼び出すことができます。これはスーパークラスオブジェクトのインスタンスではありませんただし、参照するメソッドまたはフィールドをコンパイラに伝える方法です。効果は、サブクラスが独自のメソッドの1つを呼び出している場合と同じです。例:

スーパークラスのPersonを拡張するサブクラスEmployeeを考えてみましょう。

public class Employee extends Person{

   public Employee()
   {
     //reference the superclass constructor 
     super(); 
   }

   public String getName()
   {
     //reference superclass behaviors
     return super.getFirstName() + " " + super.getLastName();
   }
 } 

Superキーワードを使用して、Personクラスのコンストラクタ、またはアクセスできる動作またはフィールドのいずれかを参照できます(例:getFirstName()およびgetLastName())。

1
Backtrack
_How many number of Object is created in this case.
_

C cInstance = new C();によってクラスCのインスタンスを作成すると、クラスCの単一のインスタンス(オブジェクト)が作成されます(AとBのいずれもなし)。ただし、CはBを拡張し、BはAを拡張するため、CにはクラスAとBのすべてのメソッドがあります(実際には使用されるアクセス修飾子に依存しますが、この場合はパブリックまたはデフォルトです)。

_If one object is created then how internally Super() is calling Parent class Constructor
. How Super is able to call parent class constructor.
_

それが継承の仕組みです。新しいオブジェクトが作成されると、そのオブジェクトはスーパークラスコンストラクターを呼び出し、そのスーパークラスはスーパークラスコンストラクターなどを呼び出します。他の通常の関数では、明示的にsuper()を呼び出す必要があります。したがって、スーパークラスコンストラクターの呼び出しはボトムアップ式になり、実行は継承階層ツリーのトップダウン式になります

1
Aniket Thakur

3つのコンストラクターが呼び出します

コード:

class A
{
    A()
    {
        System.out.println("In A");
    }
}

class B extends A
{
    B()
    {
        System.out.println("In B");
    }
}

class C extends B
{
    C()
    {
        System.out.println("In C");
    }
}

public class InheritanceTest {
    public static void main(String args[])



    {
        C c1=new C();
    }

}

出力:

Bで

Cで

0
Usman Yaqoob

コードをもう1行追加すると、System.out.println(this.hashCode())は混乱を取り除きます。

ここでは、すべてのケースでhashCode()は常に同じhashCodeを出力します。これは、一意のObjectが1つだけ作成されることを意味します。

_class A {
    A() {
        // super(); 
        System.out.println(this.hashCode()); // it will print 2430287
        System.out.println("class A");
    }
}
class B extends A {
    B() {
        // super(); 
        System.out.println(this.hashCode()); // it will print 2430287
        System.out.println("class B");
    }
}
class C extends B {
    public static void main(String args[]) {
        C c = new C(); //Parent constructor will get called
        System.out.println(this.hashCode()); // it will print 2430287
    }
}
_

ただし、Parentメンバー変数を初期化するために2つのConstructorが呼び出されます。 parentクラスのコンストラクターを呼び出し、parentクラスのメンバー変数を初期化するsuper()キーワードの概念を知っていると思います。

0
Vikrant Kashyap