Java编程中一不小心就出错的点
最近在贴吧看到一个帖子,里面是一段简单的代码,要求说出执行结果。乍一看不就是个实例化子类过程中调用父类构造方法的点么,于是我轻易地下了定论,既然是轻易地就下了定论,那就表示我也轻易地答错了。。。

话不多说,代码如下,请写出执行结果。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的赋值顺序是按照转换前的顺序

至此,这个看似简单却容易出错的点就解释完了。或许有人会说这东西有点钻墙角的感觉了吧,有必要分析这些么,无不无聊?我想说的是,存在即意义,存在即价值。再小的点哪怕再基础再不常见它也是知识点,作为一名合格的开发者这些东西不正是需要我们注意的吗。
It's
欢迎访问本站,欢迎留言、分享、点赞。愿您阅读愉快!
*转载请注明出处,严禁非法转载。
https://www.devsong.org
QQ留言 邮箱留言
头像
引用:
取消回复
提交
涂鸦
涂鸦
热门