警惕java构造器中的多态

博客介绍了Java中Glyph构造器调用被覆盖的draw()方法时出现的问题,即radius值为0而非默认初始值1。阐述了对象初始化的实际过程,指出这样做可使所有东西至少初始化为零。还给出编写构造器的准则,即避免调用其他方法,可安全调用基类final方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
 * @Description 构造器中的多态
 * @Date 2021-01-13 10:06
 */
class Glyph{
    void draw(){
        System.out.println("Glyph draw");
    }
    Glyph(){
        System.out.println("Glyph before draw");
        draw();
        System.out.println("Glyph after draw");
    }

}
public class RoundGlyph extends Glyph{
    private int radius = 1;
    RoundGlyph(int r){
        radius = r;
        System.out.println("RoundGlyph.radius "+radius);
    }

    @Override
    void draw() {
        System.out.println("RoundGlyph.draw.radius "+radius);
    }

    public static void main(String[] args) {
        new RoundGlyph(5);
        //初始化顺序:
        // RoundGlyph构造器,发现有父类,找到Glyph构造器
        // 输出 Glyph before draw
        // Glyph.draw()方法,多态,找到RoundGlyph.draw(),但是这个时候RoundGlyph的radius值没被初始化,是最初的默认值(二进制的零),int型为0,输出 RoundGlyph.draw.radius 0
        // 输出 Glyph after draw (父类初始化完毕)
        // 回到RoundGlyph,radius初始化为1,5被传入,radius=5,输出 RoundGlyph.radius 5
    }
}

Glyph.draw()方法设计为将要被覆盖,这种覆盖是在RoundGlyph中发生的。但是Glyph构造器会调用这个方法,结果导致了对RoundGlyph.draw()的调用,这看起来似乎是我们的目的。但是如果看到输出结果,我们会发现当Glyph的构造器调用draw()方法时,radius不是默认初始值1,而是0。
初始化的实际过程:
1)在其他任何事物发生之前,将分配给对象的存储空间初始化成二进制的零
2)如前述那样调用基类构造器。此时,调用被覆盖后的draw()方法(要在调用RoundGlyph构造器之前调用),由于步骤1的缘故,我们此时会发现radius的值为0.
3)按照声明的顺序调用成员的初始化方法。
4)调用导出类的构造器主体。
这样做有一个优点,那就是所有东西都至少初始化成零(或者是某些特殊数据类型中与"零"等价的值),而不是仅仅留作垃圾。包括通过"组合"而嵌入一个类内部的对象引用,其值是null。所以如果忘记为该引用进行初始化,就会在运行时出现异常。
因此,编写构造器时有一条有效的准则:"用尽可能简单的方法使对象进入正常状态;如果可以的话,避免调用其他方法"。在构造器内唯一能够安全调用的那些方法使基类中的final方法(private方法都隐式地指定为是final)。这些方法不能被覆盖,因此也不会出现上述令人惊讶的问题。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值