在java中inheritance创build多less个对象?

假设我有三个类:

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

当我创build一个类C的实例时,它调用超类的构造函数。 那么,是不是有更多的对象被创build? 如果只创build一个对象,那么super()如何像另一个类的构造函数? super()方法在内部创build一个对象吗? 我所知道的是,构造函数也是一种方法(我可能是错的)。

我的问题是:

  1. 在这种情况下创build了多less个Object?
  2. 如果创build了一个对象,那么Super()如何在内部调用父类构造函数?

伟大的问题。 你正在摸索的是Java如何初始化对象 – 并且涉及到很多步骤。

我知道是构造函数也是一种方法(也许我错了)。

几乎正确。 构造函数是一种特殊的方法。 如果你反编译一个类文件,你会看到构造函数被重命名为<init><init>与其他方法的处理方式不同,例如,除了使用关键字newsuper之外,不能明确地调用它们。 这是非常基础的,它在JVM本身中实现,而不是在Java语言中定义的东西。

在这种情况下创build了多less个Object。

一个对象被创build – C一个实例。

C同时是B一个实例,也是A一个实例,也是Object

如果创build了一个对象,那么内部super()如何调用父类构造函数。 Super如何能够调用父类的构造函数。

这是我们进入初始化的地方 – 初始化是JVM如何创build对象的新实例并设置所有成员值 – 特定类和超类的成员值。 有几个阶段涉及:

  • 加载所有引用的类并初始化这些类。 类初始化本身并不重要,所以我不会在这里介绍。 这是值得一读的。
  • 分配一块内存来存放实例的成员,包括ABC的所有成员。 注意这解释了你的问题的一个方面:基类及其子类的构造函数如何更新或引用同一个对象 – 所有类中实例的所有成员都依次存储在同一块内存中
  • 将所有成员初始化为默认值 。 例如, intfloat成员将被设置为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中有一个使用不足的工具,在构造函数执行之前运行任意代码来初始化值。 这些代码块在这个时候再次严格按照声明的顺序执行:

     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 ),但在初始化的第二阶段尚未将其设置为10

总之 – 只有一个对象,它是分阶段创build和初始化的。 在这些阶段,根据定义,这个对象是没有完全定义的。

在Code中,只有一个对象会被创build,并且超级调用父类的构造函数。

对象创build的certificate:

 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(); } } 

这将certificate只会创build一个对象。

还有关于Super() 。 我知道它调用父类的构造函数。 和每个构造函数ahve Super()作为你的代码中提到的第一个语句。 好让你知道

我不知道如何在内部调用超类的构造函数。

希望这会让你明白只有你在程序中创build的例子

  1. 将会有唯一的一个对象将被创build,即。 一个对象。

  2. 你可以想像当A类扩展B时,所有的方法和variables都被复制到A类中。

  1. 在你的情况下,只有1个对象正在创build。
  2. 当调用子类构造函数时,它会在内部调用超类的构造函数来初始化超类的成员。

调用构造函数并不意味着你正在创build对象。 当调用构造函数时,对象已经创build。对象由JVM首先创build(即内存分配在堆上,然后调用构造函数)。

构造函数用于初始化对象的成员。

你的课堂将被内部转换成这样的东西

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

在这种情况下创build了多less个Object。

只有一个是C实例,调用super()只是调用 父类构造函数 ,并不创build对象

如果创build了一个对象,那么Super()如何在内部调用父类构造函数。 Super如何能够调用父类的构造函数。

当你创buildC的实例。 C的构造函数被调用,它首先调用B的构造函数,然后调用A的构造函数

如果按照这个 SO回答来看待对象分配的dynamic性,那么必须明确的是,使用new运算符,每个语句只能创build一个对象。 为了进一步澄清只有一个被创build的对象的疑问,通过这个程序:

 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 

如果你可以注意到,每次创build子类对象时,super构造函数都会隐式调用,但是由于new操作符只被使用一次,所以只有一个对象被实际分配了空间。 通过C对象c修改aInstanceVariable ,我们实际上正在改变aInstanceVariableaInstance 。 所以清楚地certificate,实际上有一个对象。

调用构造函数创build对象时创build对象的步骤:

  1. 使用init的内存分配完成。 这个init进行一次系统调用来为对象创build分配内存

  2. 然后调用你的构造函数来初始化对象的字段。

  3. 然后它调用超类构造函数(如果有超类),并重复步骤1到3。

使用javap反编译类文件时看到的内容显示了不同的调用。 init使系统调用来初始化内存分配,但是在构造函数的代码运行时对象的字段被初始化。

我不确定在GC时多态性/覆盖是如何工作的。

但是应该值得尝试在所有类中重写finalize方法,并检查JVM何时退出主方法。

  • 如果只创build了C对象,则应该调用“C”来finalize
  • 如果创build了所有ABC对象,则应调用ABC finalize

我认为这是最简单的检查你可以申请。

 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"); } } 

我同意之前发布的答案,但希望在此问题上添加对最终权威的参考,即Java语言规范。

new C()expression式是一个“类实例创buildexpression式”。 第15.9.4节“类实例创buildexpression式的运行时评估”描述了创build对象所涉及的运行时间步骤。 请注意,它指的是“对象”,只分配空间一次,但状态为“下一步,指定的类types的选定构造函数被调用,这将导致为类types的每个超类调用至less一个构造函数”。

通过区分创build新对象和调用构造函数,这一切变得更加清晰。 调用构造函数只做对象创build的一部分,运行初始化器的部分,超类构造器和构造器的主体。 因为C也是B,所以B构造函数必须在创buildC时运行。

super关键字使子类可以调用其超类的方法和字段。 它不是超类对象的实例, 而是告诉编译器要引用哪些方法或字段的一种方法。 效果与子类调用其自己的方法之一相同。 例子:

考虑一个扩展它的父类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. 在你的情况一个对象被创build

  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()将使所有的构造函数引用一个类。 (为了便于理解:就像所有的成员函数都在同一个类中)。它只会调用所有的构造方法。

所以它只完成了调用构造函数的工作,所以super()不会做任何对象创build。 它只是指成员函数。

 How many number of Object is created in this case. 

当你通过C cInstance = new C();创build一个C类的实例C cInstance = new C(); 创build类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. 

这就是inheritance的工作原理。 当一个新的对象被创build时,它会调用它的超类构造函数,而这个超类将会调用它的超类构造函数,等等。 在其他普通函数中,你必须显式调用super()。 所以调用超类的构造函数是自下而上的,而执行是inheritance层次树的自顶向下的方式

如果你再添加一行代码System.out.println(this.hashCode())将会消除你的困惑。

这里在所有情况下, hashCode()将始终打印相同的hashCode 。 这意味着只有一个唯一的Object被创build。

 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成员variables。 我想如果你知道super()关键字的概念,它调用parent类的构造函数并初始化parent类的成员variables。