读书笔记之为继承而设计的类

《Effective Java second Edition》读后而作。

        为继承而设计的类,要避免在构造器中调用允许子类覆盖的方法。这是在设计可继承的类时,要注意的一点。因为,在子类实例化的时候,会先调用父类的构造器。而父类构造器如果调用了某个允许子类覆盖的方法,就可能发生问题。而这种问题往往在进行子类设计的时候,是对子类隐藏的。

   比如,子类覆盖了某个方法,程序员认为该被覆盖的方法,只有在子类构造完成之后才调用。并且任意地覆盖该方法,最起码不会影响子类构造器的调用完成。但是,不良的父类设计,导致了子类在初始化工作完成之前,该方法就被父类调用。从而引发某些问题,例如,空指针异常等等。

  这段话有点绕,下面用一个例子来说明。

  

package demo;

import java.util.Date;

public class Sample
{

    public static void main(String[] args)
    {
        Parent p= new Parent();
        System.out.println("-------------分割线---------------");
        Child c = new Child();
        c.allow2override();
    }

}
class Parent {
    Parent(){
        System.out.println("Parent:构造开始");
        notAllow2override();
        System.out.println("这里调用了某个允许覆盖的方法,但是子类不知道我干了这件事");
        allow2override();   
        System.out.println("Parent:构造结束");
    }
    public void allow2override(){
        System.out.println("allow2override:这个方法在Parent够造的时候被调用");
    }
    public final void notAllow2override(){
        System.out.println("notAllow2override:这个方法是不允许被子里覆盖的");
    }
}
class Child extends Parent{
    static {
        System.out.println("我没有父类的源码。我使用默认无参的构造方法。我认为我的类没有问题");
    }
    private final Date date;
    Child(){
        super();//不写亦然
        this.date = new Date();
    }
    @Override
    public void allow2override()
    {
        System.out.println(this.date.getTime());
    }
    
}
这段代码运行的结果是:

Parent:构造开始
notAllow2override:这个方法是不允许被子里覆盖的
这里调用了某个允许覆盖的方法,但是子类不知道我干了这件事
allow2override:这个方法在Parent够造的时候被调用
Parent:构造结束
-------------分割线---------------
我没有父类的源码。我使用默认无参的构造方法。我认为我的类没有问题
Parent:构造开始
notAllow2override:这个方法是不允许被子里覆盖的
这里调用了某个允许覆盖的方法,但是子类不知道我干了这件事
Exception in thread "main" java.lang.NullPointerException
at demo3.Child.allow2override(Sample.java:44)
at demo3.Parent.<init>(Sample.java:22)
at demo3.Child.<init>(Sample.java:38)
at demo3.Sample.main(Sample.java:12)


    为什么为造成这种情况?子类在设计的时候是没有问题的,当子类构造的时候,第一步调用父类的构造器,然而父类构造器调用了一个可以覆盖的方法,但是子类覆盖的方法,依赖于子类的成功构造。因此,这个为继承而设计的父类就存在问题。

所以,在为了继承而设计类时就要注意:构造器决不能调用可被覆盖的方法,无论是直接还是间接调用。

由此引申:在为了继承而设计类时,Cloneable和Serializable接口的实现都出现了新的难点。无论是clone还是readObject,都不能调用可被覆盖的方法,无论是直接还是间接调用。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值