本博客為個人原創(chuàng)以现,轉載需在明顯位置注明出處
多態(tài)在實際項目開發(fā)中是經常用到的一個概念约啊,那么多態(tài)中的異常是怎樣的呢佣赖?下面我們就來討論一下,為了節(jié)省時間憎蛤,我直接用了《Thinking in Java》中的栗子(由于是多態(tài),肯定有一系列的繼承關系萎胰,所以代碼較多技竟,請仔細閱讀):
class BaseballException extends Exception {
}
class Foul extends BaseballException {
}
class Strike extends BaseballException {
}
首先是三個自定義異常類榔组,Foul和Strike均是BaseballException的子類
abstract class Inning {
public Inning() throws BaseballException {
}
public Inning(String s) {
}
public void event() throws BaseballException {
}
public void walk() {
}
public abstract void atBat() throws Strike, Foul;
}
接下來是一個抽象類瓷患,其中atBat()是抽象方法遣妥,注意每個方法拋出的異常爱态,下面會有逐個總結和解釋
class StormException extends Exception {
}
class RainedOut extends StormException {
}
class PopFoul extends Foul {
}
又是三個自定義異常锦担,注意洞渔,RainedOut是剛剛定義的StormException的子類缚态,PopFoul是上面定義的Foul的子類
interface Storm {
public void event() throws RainedOut;
public void rainHard() throws RainedOut;
}
擦玫芦,還有一個接口桥帆,感覺整個人都不好了,在堅持一下茫多,馬上就到最后了地梨。注意一下宝剖,接口里面有一個event()方法万细,上面抽象類Inning也有一個event()方法赖钞,只是拋出的異常不同
class StormyInning extends Inning implements Storm {
public StormyInning() throws RainedOut, BaseballException {
// 構造方法必須throws父類構造方法的Exception
// 子類構造方法調用super雪营,不可捕獲父類的異常
}
public StormyInning(String s) {
super("");
}
@Override
public void event() {
// 父類event方法和接口event方法拋出的異常不一致,所以只能重寫方法谴餐,不拋出任何異常
}
@Override
public void rainHard() {
// 子類重寫方法岂嗓,可以將拋出的異常范圍縮小或者不拋出異常
}
@Override
public void atBat() throws PopFoul {
// 子類重寫方法厌殉,可以拋出父類拋出異常的子類異常
// PopFoul extends Foul
}
// @Override
// public void walk() throws StormException {
// 如果父類方法未拋出異常年枕,子類重寫方法不可拋出異常
// }
}
終于結束了,最后一個實現類StormInning摩桶,集成抽象類Inning硝清,實現接口Storm芦拿,內部重寫了一些方法蔗崎,下面我們就挨個總結:
重寫方法###
1.父類方法不throws異常缓苛,子類重寫方法不可throws
看代碼中的walk方法未桥,父類Inning中walk方法是沒有拋出異常的冬耿,但是在子類StormInning中重寫并且添加異常說明,編譯不通過的染乌『杀铮回顧一下多態(tài)的概念勒庄,簡單來說就是父類引用指向子類對象,試想一下局装,父類方法中沒有拋出異常铐尚,子類卻拋出了異常宣增,假設可以編譯通過帖旨,那么父類的引用去調用這個方法時解阅,是不需要捕獲異常的,但是真正執(zhí)行的是子類中重寫的方法碉熄,只要子類方法中出現問題拋出異常锈津,結果都是致命的
2.子類重寫方法可以縮小異常范圍或不throws異常
這一點跟1恰恰相反,看rainedOut方法茎杂,在接口中添加了異常說明,但是重寫時去掉了異常說明刽脖。這個是可行的,因為無論怎樣院水,父類引用在調用rainedOut方法時都會提示要捕獲異常衙耕,子類重寫方法中不拋異常沒問題,拋了異常也可以捕獲到厅瞎。
3.子類重寫方法可throws父類異常的子類
看下代碼中的atBat方法,父類Inning中定義拋出的是Foul異常,子類重寫時拋出了Foul的子類PopFoul異常爽柒,這個也是ok的。關于這點可以參考上一篇文章中的異常匹配部分心墅,捕獲異常的時候,父類異常是可以匹配子類異常的铐姚。
4.父類和接口包含同一方法,但定義拋出的異常不同,子類只能重寫方法并且不可throws任何異常
Inning類和Storm接口都有一個相同的event方法悍手,區(qū)別只是拋出的異常類型不同竣付,一個拋出BaseballException異常,另一個是RainedOut異常逸绎,我們在子類StormInning中重寫該方法時朗儒,若只拋出BaseballException醉锄,那么不匹配接口中的定義纲爸,若只拋出RainedOut,就不匹配父類中的定義,兩個異常都拋出冕茅,父類和接口都不匹配,真是里外都不是人。所以這種情況下徒溪,重寫方法時不能拋出任何異常腺阳,正好符合上述總結2腰耙。
構造方法###
1.子類構造方法可以擴大異常范圍
與重寫方法只能縮小異常范圍相反,子類構造方法是可以擴大異常范圍的,為什么运挫?《Thinking in java》中只有結論沒有做任何解釋,說說我的理解:還是多態(tài)的概念碾牌,父類引用在指向具體的對象時,需要new出具體的類對象择膝,子類對象和父類對象是不一樣的誓琼,而在調用方法時父類與子類就沒有區(qū)分,方法名都是一致的肴捉,只是在運行時執(zhí)行的地方不一樣腹侣。那么子類構造方法中擴大異常范圍,增加一些其他異常的拋出齿穗,完全不會影響調用方的異常捕獲傲隶。可以參考代碼中StormInning類的無參構造方法缤灵,比父類多拋出了RainedOut異常
2.如果子類構造方法調用了父類的有異常拋出的構造方法伦籍,必須throws父類構造方法的異常蓝晒,該異常不可再子類中捕獲
還是StormInning的無參構造方法,若是刪除BaseballException的異常說明帖鸦,編譯器就會報錯芝薇,為什么?因為雖然沒有顯示調用作儿,但是StormInning的無參構造方法實際上調用了父類的默認構造方法洛二,而父類Inning的默認構造方法有異常拋出,所以子類必須拋出這個異常攻锰×浪唬或許你會說,我完全可以在子類構造方法中調用一下super()娶吞,然后try-catch垒迂,這樣子類就不需要拋出異常了。一開始我也以為可以妒蛇,實際上卻不行机断,因為我們是無法在子類捕獲父類構造方法拋出的異常的,必須拋出绣夺,至于為什么吏奸,我也不是太清楚,有知道的盆友還請不吝賜教陶耍。
3.父類引用和子類引用在調用方法時奋蔚,捕獲的異常可能不同
這有個前提條件就是父類方法和子類方法拋出的異常要不同才行烈钞,就拿atBat方法為例:
@Test
public void testPolymophism() {
try {
StormyInning sinn = new StormyInning();
sinn.atBat();
} catch (PopFoul e) {
e.printStackTrace();
} catch (RainedOut e) {
e.printStackTrace();
} catch (BaseballException e) {
e.printStackTrace();
}
try {
Inning inn = new StormyInning();
inn.atBat();
} catch (Strike e) {
e.printStackTrace();
} catch (Foul e) {
e.printStackTrace();
} catch (RainedOut e) {
e.printStackTrace();
} catch (BaseballException e) {
e.printStackTrace();
}
}
首先構造方法拋出的異常是一致的泊碑,atBat在子類重寫時修改了拋出異常的類型為Foul的子類,所以子類引用在調用時棵磷,只需要捕獲PopFoul異常即可蛾狗;而父類引用在調用時就必須捕獲父類方法定義的兩個Strike和Foul異常。
好了仪媒,關于異常的基本技術細節(jié)就介紹到這里沉桌,很少寫這種純理論的東西,看著讓人昏昏欲睡算吩,但是語言基礎就是這么枯燥留凭,想成為一名優(yōu)秀的程序員,你必須克服偎巢!