话不多说,代码如下,请写出执行结果。Demo01.java
package test;
public class Demo01 {
public static void main(String[] args) {
new Derived();
}
}
class Base{
private int i = 2;
public Base() {
display();
}
public void display() {
System.out.println(i);
}
}
class Derived extends Base{
private int i = 22;
public Derived() {
i = 222;
}
public void display(){
System.out.println(i);
}
}
在没有仔细想之前,你也许会有两个答案,2或者22。如果你认为是22,那么恭喜你,错误的程度比2小一点,因为此时虽然display方法的调用是在父类的构造方法里面,但是动作的发起者是子类,而子类重写了display方法,所以此处调用的是子类的display方法,所以自然联想到输出 22。但是错了就是错了,错在哪呢?没想明白的话就新建个Java文件编译执行一下看看吧。亲自动手之后你会发现输出为0。当然,如果你看了以上代码可以很明确地直接确定答案为 0,那么说明你的基本知识还是很扎实的。如果你的答案很模糊,那么还是老老实实动手试一下,不明白的话打几个断点,借助IDE在调试模式下一步步跟着走,执行之后你就了解过程了。
那么结果为什么是 0 呢。很关键的一点就是成员的赋值和初始化操作是不同时的,通常,在编译为class字节码文件之后,会做下面类似的转化:
package test;
public class Demo01 {
public static void main(String[] args) {
new Derived();
}
}
class Base{
private int i; //初始化默认值 0
public Base() {
i = 2; //对于成员的赋值操作都移到了构造函数内
display(); //调用的是子类重写的display方法
}
public void display() {
System.out.println(i);
}
}
class Derived extends Base{
private int i; //初始化默认值 0
public Derived() {
super(); //调用父类的无参构造方法
i = 22; //对于成员的赋值操作都移到了构造函数内
i = 222;
}
public void display(){
System.out.println(i);
}
}
现在看来应该可以很轻松地知道结果为什么是0了吧。也就是在调用display方法之前,i=22这个赋值操作还未执行【父类构造方法super()永远是第一个条目】,此时调用打印输出自然为0。说到这里,自然联想到静态成员变量的初始化和赋值操作,在编译为class文件的时候同样会做类似的转化,如下:
public class Test{
private static int i = 20;
private static int j;
static{
j = 20;
}
public Test(){}
}
会转化为类似下面的形式:
public class Test{
private static int i;
private static int j;
static{
i = 20;
j = 20;
}
public Test(){}
}
注意转换后i和j的赋值顺序是按照转换前的顺序。至此,这个看似简单却容易出错的点就解释完了。或许有人会说这东西有点钻墙角的感觉了吧,有必要分析这些么,无不无聊?我想说的是,存在即意义,存在即价值。再小的点哪怕再基础再不常见它也是知识点,作为一名合格的开发者这些东西不正是需要我们注意的吗。