构造器是静态方法吗?New一个子类对象的时候是否创建了父类的对象

在thinking in java中,第96页提到,“即使没有显示地使用static关键字,构造器实际上也是静态方法“,对此我产生了疑惑,于是找相关资料进行确认,在一篇大神的博客中得到了答案,那就是构造器不是静态方法。

主要是以下几点原因:

Java虚拟机规范第二版中定义了四种不同的字节码指令来处理Java程序中不同种类的方法的调用:
· invokestatic - 用于调用类(静态)方法 
· invokespecial - 用于调用实例方法,特化于super方法调用、private方法调用与构造器调用 
· invokevirtual - 用于调用一般实例方法(包括声明为final但不为private的实例方法) 
· invokeinterface - 用于调用接口方法 

其中,invokestatic与invokespecial调用的目标必然是可以静态绑定的(比如不被继承),因为它们都无法参与子类型多态;invokevirtual与invokeinterface的则一般需要做运行时绑定,JVM实现可以有选择的根据final或实际运行时类层次或类型反馈等信息试图进行静态绑定。

那么Java中的实例构造器是不是“静态方法”呢?从Java语言规范中给出的“静态方法”的定义来看,答案是“否”——首先从Java语言规范对“方法”的定义来说,构造器根本不是“方法”;其次,实例构造器有一个隐式参数,“this”,在实例构造器中可以访问“this”,可以通过“this”访问到正在初始化的对象实例的所有实例成员。 

实例构造器无法被隐藏或覆写,不参与多态,因而可以做静态绑定。从这个意义上可以认为实例构造器是“静态”的,但这种用法与Java语言定义的“静态方法”是两码事。 

另外需要注意的是,Java语言中,实例构造器只能在new表达式(或别的构造器)中被调用,不能通过方法调用表达式来调用。new表达式作为一个整体保证了对象的创建与初始化是打包在一起进行的,不能分开进行;但实例构造器只负责对象初始化的部分,“创建对象”的部分是由new表达式本身保证的。 

这个例子的注意点在于: 
1、Java的实例构造器只负责初始化,不负责创建对象;Java虚拟机的字节码指令的设计也反映了这一点,有一个new指令专门用于创建对象实例,而调用实例构造器则使用invokespecial指令。 

2、“this”是作为实例构造器的第一个实际参数传入的。 

首先:很坚决的说没有创建父类的对象!!!

New一个子类对象的时候是否创建了父类的对象

1.首先从子类到直接父类,依次执行父类的构造方法(没有显示调用构造方法的情况下),这个过程执行父类成员的初始化。 
我仔细把书上继承那章又读了好几遍!!!书上说的很明确,“创键子类的对象的时候,会先调用父类的构造函数!会先调用父类构造函数!会先调用父类的构造函数!”(重要的事情说三遍)!!并没有说会创建父类的对象,没说!!!!书上其实写的很微妙!应该直接指明这一点,防止学生误解!

2.最后调用本类的构造方法。

以上两步的结果是创建了本类对象,这个对象如图: 
这里写图片描述

从图中可知,压根没有父类对象,只有子类对象,而且this完全引用这个对象,super只是引用了这个对象中从父类继承来的成员,也就是说,除了super不能访问子类定义的成员之外,super和this是同一个对象,这可以通过toString()方法验证(如果子类没有重写toString()方法,则调用父类的该方法,如果父类没有,再找父类的父类,直到Object的toString()方法)。

Object类toString方法:

public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
}

getClass 和 getName 都是Object类的方法。

总之,没有super这个对象,他只是引用了子类对象的一部分数据(也就是从父类继承到的数据),所有无法输出super。那么为什么能输出super.toString()呢,原因在前面图中,这个调用过程是先试图调用从子类对象的直接父类继承的toString方法(注意:super.toString()是显式调用),如果没有则向上追述知道Object。


猜你喜欢

转载自blog.csdn.net/qijingwang/article/details/80356066
今日推荐