对并没有文档说明的类进行继承是非常危险的,它的公有方法有可能被改变。在设计一个专门用来继承的类时必须注意以下几点(不适用于final类):
- 必须精确地描述改写每个方法带来的影响,虽然这样的描述违法了文档格言“好的API文档应该描述一个方法做了什么工作,而不是描述它如何做”,但这也是继承破坏了程序的封装性而导致的。
- 允许继承的类的构造函数一定不能调用可被改写的方法,无论是直接进行还是间接进行。因为超类的构造函数会在子类的构造函数之前运行,所以子类中改下版本的方法将会在子类的构造函数运行之前就被调用。如:
package com.ilibaba.test;
import java.util.*;
public class SubClass extends SuperClass {
private final Date date;
public SubClass() {
date = new Date();
}
public void m() {
System.out.println(date);
}
public static void main(String[] args) {
SubClass s = new SubClass();
s.m();
}
}
class SuperClass {
public SuperClass() {
m();
}
public void m() {
System.out.println("uuu");
}
}
package com.ilibaba.test;
import java.util.*;
public class SubClass extends SuperClass {
private final Date date;
public SubClass() {
date = new Date();
}
public void m() {
System.out.println(date);
}
public static void main(String[] args) {
SubClass s = new SubClass();
s.m();
}
}
class SuperClass {
public SuperClass() {
m();
}
public void m() {
System.out.println("uuu");
}
}
“函数一定不能调用可被改写的方法”的实现方式:把每个可改写的方法的代码体移到一个私有的“辅助方法”中,并且让每个可改写的方法调用他的私有辅助方法,然后用“直接调用可改写方法的私有辅助方法”来代替“可改写方法的每个自用调用”。
在为了继承而设计类的时候,不推荐实现Cloneable和Serializable接口。clone和readObject方法在行为上和构造 函数相似——“一定不能调用可被改写的方法,无论是直接进行还是间接进行”,对于readObject方法,子类中改写版本的方法将子类的状态被反序列化 之前运行,而对于clone方法,改写版本的方法将在子类的clone方法有机会修正被克隆对象的状态之前被运行,无论哪种情形,它们都会不可避免的导致 程序失败。
对于既不是final类,也不是为了子类化而设计和编写文档的普通类而言,防止出现问题的最好方法是禁止子类化,方法有两种:
- 把这个类声明为final的。
- 把所有的构造函数变成私有的,或者包级私有的,并增加一个公有的静态工厂方法来代替构造函数。
继承的另一个替代方案就是利用包装类模式,具体参见NO.14 复合优先于继承
本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/ilibaba/archive/2009/02/03/3860582.aspx