我们先看一个经典的例子:
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
System.out.println("Root的无参构造器");
}
}
class Mid extends Root {
static {
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
System.out.println("Mid的无参构造器");
}
public Mid(String msg){
this();
System.out.println("Mid的带参的构造器,其参数值是:" + msg);
}
}
class Leaf extends Mid{
static {
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
super("我的测试");
System.out.println("Leaf的无参构造器");
}
}
public class test9{
public static void main(String[] args) {
new Leaf();
new Leaf();
}
}
它的输出结果如下:
输出结果:
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参构造器
Mid的普通初始化块
Mid的无参构造器
Mid的带参的构造器,其参数值是:我的测试
Leaf的普通初始化块
Leaf的无参构造器
Root的普通初始化块
Root的无参构造器
Mid的普通初始化块
Mid的无参构造器
Mid的带参的构造器,其参数值是:我的测试
Leaf的普通初始化块
Leaf的无参构造器
说明:
一,程序第一次编译执行时,会先从基类依次往下加载类,加载类时会初始化静态成员,不调用构造器,加载类和初始化静态成员的动作仅发生一次不会重复执行。加载完毕后才能创建对象。加载类对应输出结果的前三行。
二,很多人对于Mid的无参构造器和有参构造器那里的输出顺序有些迷惑,我是这样理解的:
创建对象时,会先找对应类的构造器(想想构造器名和类名为什么是相同的?),我们new出一个Leaf后,找到Leaf构造器,但并不会马上执行,因为有继承关系,要保证基类先完成初始化,所以会接着找它的父类。这里关键的地方在于,Leaf构造器调用了super("我的测试"),Leaf构造器虽然不会被马上执行,但是程序却知道了这个super("我的测试"),当leaf类的父类Mid类执行本身的构造器时,就会执行有参构造器,参数是"我的测试"。先接着往下看,程序一直找父类直到找到基类Root类。在同一个类中,静态成员(包括代码块)先初始化,然后是非静态成员,最后是构造器。本例中的静态代码块在加载类时已经初始化了,不用重复执行。需要强调的是,这里的初始化顺序的前提是在同一个类中。当有super语句时,就会发生跨类的情况。
所以当执行到Mid类时,先执行普通代码块,然后关键来了,由于Leaf构造器中有super("我的测试")语句,所以Mid类会执行有参构造器,这个有参构造器中有this() 语句,所以会先调用当前对象的无参构造器。之后执行到Leaf构造器时,super语句其实已经执行过了,不会重复执行。
这是我对此程序输出顺序的理解,不知是否准确,希望大神能指点一下。
本文通过一个Java示例程序详细解析了类的构造器、初始化块及super关键字的调用顺序。具体分析了静态成员、普通成员及构造器的初始化过程,并解释了为何子类构造器中super关键字的调用会导致父类构造器的不同顺序。
1064

被折叠的 条评论
为什么被折叠?



