该知识点是自己从书籍中学习的笔记。
第16条告诉了我们对一个不是继承而设计并且没有文档的类是很危险的。那么一个为了设计而使用继承并且有很好的文档则应该做以下事情:
该类的文档必须精确地说明每个改写的方法作用以及影响。好的API文档:应该是描述该方法做了什么工作,而不是如何做的。
一个为了继承而设计的类(超类)应该是:
a.为了允许被继承,无论是直接还是间接,构造方法都不能够调用非final方法。否则程序有可能失败。由于超类的构造方法比子类的构造方法先执行,所以子类中改写版本的方法将会在子类的构造方法运行之前先被调用。如果该改写版本的方法依赖于子类构造方法所执行的初始化工作,那么该方法将不会如预期执行。如下代码:
import java.util.Date;
class Super {
public Super() {
method();
}
public void method() {
}
}
public class Sub extends Super {
private final Date date;
public Sub() {
date = new Date();
}
public void method() {
System.out.println(date);
}
public static void main(String[] args) {
Sub s = new Sub();
s.method();
}
}
打印结果:
null
Fri Jan 06 15:59:38 CST 2012
这明显不是我们想要的结果,出现了bug。原因就是上面所说的。
b.设计的超类实现Serializable或者Cloneable接口都是很麻烦的事情。因为clone和readObject都类似一个构造方法。所以超类要实现这两个接口就必须遵循这个规则:clone和readObject都不能够调用可改写的方法,无论是直接还是间接方式。对于readObject方法,子类改写版本的方法将在子类的状态被反序列化之前先执行;对于clone方法,改变版本的方法将在子类的clone方法有机会修正被克隆对象的状态之前先被执行。无论哪种情况,都可能导致程序失败,在clone方法中,这种失败会导致损害原始对象以及克隆的对象本身。
c.如果超类实现了Serializable接口,并且该类有一个readResolve或者writeReplace方法,那么就必须是readResolve或者writeReplace方法成为protectd,而不是private。如果是私有的话,那么子类会忽略掉这两个方法。
另外,对于一些不需要被继承的类,最好是把它定义为不能够被继承的类。可以使用final或者提供静态工厂方式来提供对象。