问题描述:在网上看到一个笔试题,由下列代码输出打印结果为null,为什么?
详细查看http://blog.csdn.net/two_water/article/details/53891952这篇博客
package test;
public class Base {
private String name ="base";
public Base() {
callName();
}
public void callName() {
System.out.println(name);
}
static class Sub extends Base {
private String name = "sub";
public void callName() {
System.out.println(name);
}
}
public static void main(final String[] args) {
Base base = new Sub();
}
}
突然间发现自己也不是很清楚,于是阅读了上面的那篇文章,写的很好,浅显易懂。最关键的是理解下面这段代码以及构造器的初始化顺序大概是:父类静态块 ->子类静态块 ->父类初始化语句 ->父类构造函器 ->子类初始化语句 -> 子类构造器,也就清楚了代码的执行流程。
public Sub(){
super();
baseName = "sub";
}
但是一个同事看后问我,此时Base类中的name的值是什么?这由提出了另外一个问题,也就是在类的构造函数中,super()、类变量初始化、代码块中代码,他们的顺序又是怎么样?为了得出答案,我通过javap -verbose Base指令将Base.class文件打印出来查看Base.:()V的具体指令时发现了答案。
aload_0
invokespecial #1; //Method java/lang/Object.””:()V
aload_0
ldc #2; //String base
putfield #3; //Field name:Ljava/lang/String;
aload_0
invokevirtual #4; //Method callName:()V
return
从上面可以看出,一个类的构造方法的指令是从父类构造方法先执行,然后进行类变量初始化,最后才是放在构造方法中代码的执行,也即
public Base() {
super(); //先父类构造方法
name = "base"; //接着类变量初始化
callName(); // 最后才是代码块的执行
}
问题似乎得到解决,那么在来一波程序进行分析,此时答案为多少?
package test;
public class InitialClass {
public int i = methodI();
public static int j = methodJ();
public int k = 0;
public InitialClass() {
System.out.println(1);
}
public int methodI() {
System.out.println(2);
return 2;
}
public static int methodJ() {
System.out.println(3);
return 3;
}
static class B extends InitialClass {
public int m = methodM();
public static int n = methodN();
public int t = 0;
public B() {
System.out.println(4);
}
public int methodM() {
System.out.println(5);
return 5;
}
public static int methodN() {
System.out.println(6);
return 6;
}
}
public static void main(final String[] args) {
System.out.println(7);
InitialClass a = new B();
a.toString();
}
}
只要记住上面提到过的类构造器的初始化顺序大概是:父类静态块 ->子类静态块 ->父类初始化语句 ->父类构造函器 ->子类初始化语句 -> 子类构造器。一般会认为答案是
7
3
6
2
1
5
4
但是通过编译、执行发现答案是
3
7
6
2
1
5
4
依据类构造器的顺序我们可以理解3、6的顺序,那么为什么7会在3、6之间输出呢?问题似乎又变得有意思起来。其实转弯理解main方法是InitialClass类的静态方法,jvm是从InitialClass类的main作为程序入口进行执行,那么首先得加载InitialClass类,此时会初始化静态变量j,也就执行了methodJ()静态方法,在进行new B()时,此时父类InitialClass已经加载,则执行B中的静态类变量初始,在依次执行父类构造器、子类构造器从而得出结果。