當有繼承發生時,會伴隨着重載、覆寫(包括接口方法的實現)、構造器的重寫等行爲。此時,如果基類或是接口的方法存在異常聲明,那麼導出類或是接口的實現類的對應方法該如何聲明異常?同時對這些方法的調用該如何捕捉異常?下面就這2個問題進行探討,你會看到,針對覆寫和構造器的重寫是2種完全不同的處理方式(針對重載則沒有任何限制)。代碼如下:
//@file Example1.java
class ExtException extends Exception {}
class AnotherException extends Exception {}
class Base
{
public void func() {}
public void func2() throws Exception {}
public void func3() throws ExtException {}
public Base() throws Exception {}
public Base(int i) throws Exception {}
//public Base(float f) throws ExtException{} //Error No.5: every other constructor will call the default constructor; same as Error No.4
}
interface Inf
{
public void func2() throws ExtException;
//public void func3() throws AnotherException; //Error No.3: base and interface conflicts
public void func4() throws Exception;
public void func5() throws Exception;
}
class ExtImp extends Base implements Inf
{
//public void func() throws ExtException{} //Error No.1: if base function has no exception declaration, overriding CANNOT add one
//public void func2() throws Exception {} //Error No.2: cannot implement interface; base and interface conflicts
public void func2() throws ExtException {} //implement interface and use Hint No.2
//public void func3() throws ExtException {} //Error No.3
//public void func3() throws AnotherException {} //Error No.3
public void func4() {} //Hint No.1: if base function has exception declaration, overriding CAN ommit
public void func5() throws ExtException {} //Hint No.2: if base function has exception declaration, overriding CAN declare ext exception
//public ExtImp() {} //Error No.4: ext's construcor must declare the exception the base constructor declare
public ExtImp() throws Exception {}
//public ExtImp() throws ExtException {} //Error No.4
public ExtImp(int i) throws Exception, ExtException {} //Hint No.3: once ext decalres throwing the base's exception, it CAN declare other exception (不一定非要是父類聲明異常的子類), see Example2.java
//public ExtImp(float f) throws ExtException{} //Error No.5
}
//@file Example2.java
class ExtException extends Exception {}
class AnotherException extends Exception {}
class ThirdException extends Exception {}
class FourthException extends ThirdException {}
class Base
{
public Base() {}
public Base(int i) throws ExtException {}
public Base(float f) throws ExtException {}
public void func() throws ThirdException { System.out.println("Base.func()");}
}
class Ext extends Base
{
public Ext() throws Exception {} //Hint No.4: ext's constructor CAN add exception declaration
public Ext(int i) throws ExtException, AnotherException {} //Hint No.4
public Ext(float f) throws Exception {} //Hint No.5: ext's constructor can declare base exception
public void func() throws FourthException { System.out.println("Ext.func()");}
}
public class Example2
{
public static void main(String[] args)
{
try
{
Ext e = new Ext(5);
e.func();
}
catch (ExtException ee) {}
catch (AnotherException ae) {}
catch (FourthException fe) {} // ***DIFFERENCE***
try
{
Base b = new Ext(5);
b.func();
}
catch (ExtException ee) {}
catch (AnotherException ae) {}
catch (ThirdException te) {} // ***DIFFERENCE***
}
}
//output:
/*
Ext.func()
Ext.func()
*/
針對覆寫方法,有以下幾點原則:
1. 如果基類方法沒有聲明異常,那麼導出類的覆寫方法也不能聲明異常(Error No.1)。
2. 如果基類方法有聲明異常,那麼導出類的覆寫方法可以:(1)不聲明異常;(2)聲明拋出基類方法的異常;(3)聲明拋出基類方法異常的導出類。(Hint No.1 & Hint No.2)
3. 如果基類和接口有同簽名方法,且導出類實現了接口,如果基類方法和接口方法聲明的異常不同,則稱基類與接口衝突。如果基類方法拋出的異常和接口方法聲明的異常存在繼承關係,則實現接口的導出類必須聲明拋出導出異常(子異常)(Error No.2);如果如果基類方法聲明的異常和接口方法聲明的異常不存在繼承關係,則衝突不可調和,需要修改基類或是接口(Error No.3)。
4. 由Example2.java可見,對於向上轉型 Base b = new Ext(5),調用b.func()雖然會動態綁定調用Ext的func()方法,可是異常捕捉必須按照Base的func()方法的異常聲明來捕捉(見DIFFERENCE處) 。
針對構造器的重寫,有以下幾點原則:
1. 這裏應該持這麼一種觀點,基類的帶參構造器和導出類的所有構造器都默認調用了基類的默認構造器,Base(int i)調用了Base(),Ext()調用了super(),Ext(int i)調用了super(i),依次類推。所以一旦基類的默認構造器聲明瞭異常,那麼基類的帶參構造器和導出類的所有構造器都必須聲明異常,異常類型可以是基類默認構造器的異常或是其基類,而決不能是其導出類(Error No.4, Error No.5 & Hint No.5) (與覆寫方法拋異常的情況剛好相反) 。
p.s. 導出類構造器雖然不能聲明導出異常,不過可以拋出導出異常,如:
class Base
{
public Base() throws NullPointerException {}
}
class Ext extends Base
{
public Ext() throws Exception
{
throw new NullPointerException();
}
}
所以牢記:聲明異常和實際拋出異常完全是兩碼事 。
2. 如1.所說,構造器的重寫實際是調用關係,所以一旦默認構造器沒有聲明異常,那麼其他構造器就可以隨便添加異常聲明(Hint No.3 & Hint No.4)。