inheritance,方法签名,方法重写和throws子句

我的Parent课是:

 import java.io.IOException; public class Parent { int x = 0; public int getX() throws IOException{ if(x<=0){ throw new IOException(); } return x; } } 

extend这个类来编写一个子类Child

 public class Child1 extends Parent{ public int getX(){ return x+10; } } 

请注意,在覆盖 Child类中的getX方法时,我已从方法定义中删除了throws子句。现在,它会导致编译器出现exception行为,这是预期的:

 new Parent().getX() ; 

如果没有将其包含在try-catch块中,则无法编译。

 new Child().getX() ; 

编译时不将其封闭在try-catch块中。

但是下面的代码行需要try-catch块。

 Parent p = new Child(); p.getX(); 

正如可以预见的那样,即在运行时多态性期间使用父类引用来调用子方法,为什么Java的设计者在重写特定的父类方法时没有强制要求在方法定义中包含throws子句? 我的意思是如果父类方法在其定义中有throws子句,那么在重写它时,重写方法也应该包括throws子句,不是吗?

不,这是合适的 – 重写的方法可以对它抛出(和返回)的内容进行更严格的限制,因为这对于在编译时知道他们将使用重写方法并且不想打扰的调用者非常有用。虽然不能发生例外,但它必须更具限制性,而不是更宽容 ,因此它不会让通过父声明访问它的呼叫者感到惊讶。

通过类型Parent的引用使用重写方法永远不会违反“它可能抛出IOException ”的约定 – 没有exception不会违反合同。 反过来(如果父级没有声明exception,但重写方法确实如此) 违反合同。

好吧,重写方法可能根本不会抛出任何exception(或至少更少的exception),因此您可以从throw子句(或整个throw子句)中删除exception。

假设覆盖方法捕获所有exception,记录它们并返回特殊值。 虽然这不是好的样式(它会改变方法的语义),但它仍然是可能的,因此如果你在编译时知道你正在处理一个Child ,你就不必捕获永远不会抛出的exception。

添加exception将不起作用,因为通过Parent引用访问它的类的用户不知道Child可能添加的任何exception。

您可以自由地覆盖没有throws关键字的方法,因为如果您想通过控制所有exception来开发方法,那么您可以通过覆盖没有任何throws子句的方法来实现。

但是请记住,如果要在子类方法中包含throws子句,那么throws子句必须与必须相同的exception或其超类方法抛出的exception的子类相关联。 例如-

 class Super{ void a()throws IOException{ ...... } } class Sub extends Super{ void a()throws IOException{ ...... } } 

类Sub的a()方法必须抛出IOException或IOException的任何子类,否则编译器将显示错误。

这意味着如果你写

 void a()throws Exception 

在Sub类中,则会导致编译错误。

容易记住

  1. 访问修饰符可以从限制更改为更少限制,
    例如从受保护到公共,但反之亦然
  2. throws签名可以是从父exception更改为子exception类,但反之亦然

此代码有效

 public class A { protected String foo() throws Exception{ return "a"; } class B extends A { @Override public String foo() throws IOException{ return "b"; } } } 

覆盖的foo方法具有公共访问权限,不受保护并抛出IOException,即Exception的子级

此代码无效

 public class A { public String foo() throws IOException{ return "a"; } class B extends A { @Override protected String foo() throws Exception{ return "b"; } } } 

覆盖的foo方法具有更多限制访问修饰符并抛出exception,即IOException的子代

顺便说一句,你可以从超类覆盖方法,而不是抛出ecxeptions

此代码有效

 public class A { public String foo() throws IOException{ return "a"; } class B extends A { @Override public String foo(){ return "b"; } } }