在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一个对象吗? 我所知道的是,构造函数也是一种方法(我可能是错的)。
我的问题是:
- 在这种情况下创build了多less个Object?
- 如果创build了一个对象,那么Super()如何在内部调用父类构造函数?
伟大的问题。 你正在摸索的是Java如何初始化对象 – 并且涉及到很多步骤。
我知道是构造函数也是一种方法(也许我错了)。
几乎正确。 构造函数是一种特殊的方法。 如果你反编译一个类文件,你会看到构造函数被重命名为<init>
。 <init>
与其他方法的处理方式不同,例如,除了使用关键字new
或super
之外,不能明确地调用它们。 这是非常基础的,它在JVM本身中实现,而不是在Java语言中定义的东西。
在这种情况下创build了多less个Object。
一个对象被创build – C
一个实例。
C
同时是B
一个实例,也是A
一个实例,也是Object
。
如果创build了一个对象,那么内部
super()
如何调用父类构造函数。 Super如何能够调用父类的构造函数。
这是我们进入初始化的地方 – 初始化是JVM如何创build对象的新实例并设置所有成员值 – 特定类和超类的成员值。 有几个阶段涉及:
- 加载所有引用的类并初始化这些类。 类初始化本身并不重要,所以我不会在这里介绍。 这是值得一读的。
- 分配一块内存来存放实例的成员,包括
A
,B
和C
的所有成员。 注意这解释了你的问题的一个方面:基类及其子类的构造函数如何更新或引用同一个对象 – 所有类中实例的所有成员都依次存储在同一块内存中 。 - 将所有成员初始化为默认值 。 例如,
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中有一个使用不足的工具,在构造函数执行之前运行任意代码来初始化值。 这些代码块在这个时候再次严格按照声明的顺序执行:
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()
作为构造函数的第一行。 这意味着构造函数按照以下顺序严格执行:-
Object
-
A
-
B
-
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的例子
-
将会有唯一的一个对象将被创build,即。 一个对象。
-
你可以想像当A类扩展B时,所有的方法和variables都被复制到A类中。
- 在你的情况下,只有1个对象正在创build。
- 当调用子类构造函数时,它会在内部调用超类的构造函数来初始化超类的成员。
调用构造函数并不意味着你正在创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
,我们实际上正在改变aInstanceVariable
的aInstance
。 所以清楚地certificate,实际上有一个对象。
调用构造函数创build对象时创build对象的步骤:
-
使用init的内存分配完成。 这个init进行一次系统调用来为对象创build分配内存 。
-
然后调用你的构造函数来初始化对象的字段。
-
然后它调用超类构造函数(如果有超类),并重复步骤1到3。
使用javap反编译类文件时看到的内容显示了不同的调用。 init使系统调用来初始化内存分配,但是在构造函数的代码运行时对象的字段被初始化。
我不确定在GC时多态性/覆盖是如何工作的。
但是应该值得尝试在所有类中重写finalize
方法,并检查JVM何时退出主方法。
- 如果只创build了
C
对象,则应该调用“C”来finalize
。 - 如果创build了所有
A
,B
,C
对象,则应调用A
,B
,C
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())。
-
在你的情况一个对象被创build
-
在执行以下操作时,这个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。