Java 语言作为一种 OO 语言,支持类的扩展,其中有一些需要注意的问题。
可以简单表述为:子类方法的可见性要大。
当子类和父类出现方法的名称、参数类型相同时,子类的方法的可见性不能低于父类,而抛出异常的范围不能大于父类。对于可见性的规定,可以这样直观地表述:个体的可见性大于或等于总体的可见性。
示例代码:
public class Item60 {
public int sum(int a, int b) {
return a + b;
}
}
它有一个扩展类如下:
public class Item60Ext extends Item60 {
public int sum(int a, int b) {
return 0;
}
}
这样的代码是不会有什么问题的。但是,当我们把Item60Ext.sum(int, int)
的签名进行修改:
public class Item60Ext extends Item60 {
int sum(int a, int b) { // 不能通过编译,Eclipse 报错:Cannot reduce the visibility of the inherited method from Item60
return 0;
}
}
解析:int sum(int a, int b)
使用了默认的可见性:default
,它的可见性小于父类中设置的public
。覆盖的同名方法中,子类方法不能比父类方法的访问权限更严格。假如父类中方法是public
,子类中的同名方法就也只能是public
,而不能是protected
或者是private
。
值得一提的是,这种情况在「类」的级别是不存在的,如Item60Ext
这个类的可见性定义为protected
不会有什么问题。
为什么要这么设定?假如不这么设定,子类方法的可见性更小。根据父子类的关系,可以这样创建对象:
Item60 item = new Item60Ext();
再假如有一个方法调用它:
void hello() {
item.sum(1, 2);
}
现在我们把 Item60Ext.sum(int, int)
的可见性往大改,上面这段代码是不用修改的,但如果把 Item60Ext.sum(int, int)
的可见性改小,它就有可能访问不到了,这样原先的代码就不能正常运行了。
可以简单表述为:子类方法抛出的异常要小。
如果将上面提到的Item60Ext
修改为
public class Item60Ext extends Item60 {
public int sum(int a, int b) throws Exception { // 不能通过编译,Eclipse 报错 Exception is not compatible with throws clause in Item60.sum
return 0;
}
}
解析:父类中,sum
方法没有抛出异常,可认为父类的异常为空,那么子类中方法的异常必须为父类的子集,就只能也为空了,即不能抛出异常。另外,子类中的 override 的方法所抛出的异常不能比父类中原方法抛出的多。为什么要这么设定?因为根据父子类的关系,可以这样创建对象:
Item60 item = new Item60Ext();
再假如有一个方法调用它:
void hello() {
item.sum(1, 2);
}
现在我们把 Item60Ext.sum(int, int)
抛出的异常改小,没问题,调用位置捕获的范围不用调整,因为它能覆盖改小后的范围。但如果把 Item60Ext.sum(int, int)
的异常范围调大,就必须修改上面方法的代码了,它原先能处理的范围不够了。
在子类中若要使用父类中被隐藏的方法,应该使用super
关键字。
同名同入口参数的方法,如果父类是非静态的,子类不能是静态的。示例代码如下:
public class HelloWorldC {
void show(){}
}
public class HelloWorldD extends HelloWorldC {
static void show(){} // 不能通过编译,Eclipse 报错 the satic method cannot hide the instance method from HelloWorldC
}
解析:子类在 override 父类的方法时,父类的方法是动态的则子类的方法也必须是动态的,类似地,父类是静态的话,子类的方法也必须是静态的。静态地 field 和 method 和类是绑定的,不可以 override 或者继承,如果子类的静态 field 和 method 的名称和父类一样,子类中的只是隐藏了父类的。
如果前述代码合法,我们再定义一个对象
HelloWorldC hello = new HelloWorldD();
那么
hello.show();
这个方法到底算谁的?好像都可以吧。从类型的角度讲,可以算 HelloWorldC
的,从对象生成的角度讲可以算 HelloWorldD
的。如果不允许这么用,就没歧义了。
(本节同学某王亦有贡献)
@Override
? A: JDK 1.6+.super
關鍵字super()
為首條語句。如果超類沒有不帶自變量的構造函數,那麽你必須顯式地調用另一個構造函數。