为了保证父类良好的封装性,不会被子类随意改变,设计父类通常应该遵循如下规则:
- 尽量隐藏父类的内部数据。尽量把父类的所有属性都设置成 private 访问类型,不能让子类直接访问父类的属性。
- 不要让子类可以随意访问、修改父类的方法。父类中那些仅为辅助其他的工具方法,应该使用 private 访问控制符修饰,让子类无法访问该方法;如果父类中的方法需要被外部类调用,必须以 public 修饰,但又不希望子类重写该方法,可以使用 final 修饰符;如果希望父类的某个方法被子类重写,但不希望被其它类自由访问,可以使用 protected 来修饰该方法。
- 尽量不要再父类构造器中调用将要被子类重写的方法。例如:
class Base{
public Base(){
test();
}
public void test(){ // ① 号 test 方法
System.out.println("将被子类重写的方法");
}
}
class Sub extends Base{
private String name;
public void test(){ // ② 号 test 方法
System.out.println("子类重写父类的方法,其 name 字符串的长度" + name.length());
}
public static void main(String[] args){
// 下面代码会引发空指针异常
Sub s = new Sub();
}
}
当系统试图创建 Sub 对象时,同样会先执行其父类的构造器,如果父类的构造器调用了被其子类重写的方法,则变成了调用被子类重写后的方法。当创建 Sub 对象时,会先执行 Base 类中的 Base 构造器。而 Base 构造器中调用了 test 方法——并不是① 号 test 方法,而是调用了② 号 test 方法,此时 Sub 对象的 name 属性是 null,因此将引发空指针异常。
- 如果想把某些类设置为最终类,即不能被当成父类,则可以使用 final 修饰这个类,例如 JDK 提供的 java.lang.String 和 java.lang.System 类。除此之外,或者使用 private 修饰这个类中的所有构造器,从而保证子类无法调用该类的构造器,也就无法继承该类。对于把所有构造器都是用 private 修饰的父类而言,可以另外提供一个静态的方法,用于创建该类的实例。