Java的異常機制有三種:java
一.Error類以及其子類表示的是錯誤,它是不須要程序員處理也不能處理的異常.好比VirtualMachineError虛擬機錯誤,ThreadDeath線程殭屍等.程序員
二.RuntimeException類及其子類表示的是非受檢查異常,是系統可能會拋出的異常,程序員能夠去處理,也能夠不去處理,最經典的就是NullPointerException空指針異常和IndexOutOfBoundsException越界異常.數據庫
三.Exception類及其子類(不包含非受檢查異常)表示的是受檢異常,這是程序員必須處理的異常,不處理則程序不能經過編譯,好比IOException表示I/O異常,SQLException表示數據庫訪問異常.ide
咱們知道 一個對象的建立,須要通過內存分配,靜態代碼初始化,構造函數執行等過程,對象生成的關鍵步驟是構造函數,那是否是容許在構造函數中拋出異常呢?從Java語法上來講,徹底能夠在構造函數中拋出異常,三類 異常均可以,函數
可是從系統設計和開發的角度來分析,則儘可能不要在構造函數中拋出異常,咱們以三種不一樣類型的異常來講明:spa
(1)構造函數拋出錯誤是程序員沒法處理的線程
(2)構造函數不該該拋出非受檢查異常設計
看以下的例子:指針
class Person{ public Person(int _age){ //不滿18歲得用戶對象不能創建 if(_age<18){ //throw new RuntimeException("年齡必須大於18歲。"); } } //看限制級的電影 public void seeMovie(){ System.out.println("看限制級電影"); } }
代碼的意圖很明顯,不滿18歲的用戶根本就不會生成一個Person實例對象,沒有對象,類行爲seeMovie方法就不能夠執行,想法很好,但這會致使不可預測的結果,好比咱們這樣引用Person類.code
public static void main(String[] args) { while(true){ Person p = new Person(17); p.seeMovie(); } /*其餘的邏輯處理*/ }
很明顯,p對象不能創建,由於是一個RuntimeException異常,開發人員能夠捕捉也能夠不捕捉,代碼看上去邏輯很正確,沒有任何的瑕疵,可是事實上,這段程序會拋出異常,沒法執行,這段代碼給了咱們兩個警示:
①加劇了上層代碼編寫者的負擔.
②後續代碼不會執行.
(3)構造函數儘量不要拋出受檢查異常
//父類 class Base{ //父類拋出IOException public Base() throws IOException{ throw new IOException(); } //父類方法拋出Exception public void method() throws Exception{ } } //子類 class Sub extends Base{ //子類拋出Exception異常 public Sub() throws Exception { } //子類方法的異常類型必須是覆寫方法的子類型 @Override public void method() throws IOException{ } }
就這麼一段代碼,展現了在構造函數中拋出受檢查異常的三個不利方面.
①致使子類代碼膨脹
上面的例子中,子類的無參構造函數不能省略,緣由是父類的無參構造函數拋出了IOException異常,子類的無參構造函數默認調用的是父類的構造函數,因此子類的無參構造函數必須拋出IOException或其父類.
②違背了里氏替換原則
里氏替換原則說"父類能出現的地方子類就能夠出現,並且將父類替換爲子類也不會產生任何異常",那咱們回過頭來看看Sub類是否能夠替換Base類,好比咱們的上層代碼是這樣寫的:
public static void main(String[] args) { try{ Base base = new Base(); }catch(IOException e){ //異常處理 } }
而後咱們但願把new Base()替換成new Sub(),並且代碼可以正常編譯和運行.很是惋惜編譯不經過..緣由是Sub的構造函數拋出了Exception異常,它比父類的構造函數拋出的異常範圍要寬,必須增長新的catch塊才能解決.
1 import java.io.IOException; 2 3 public class Client { 4 public static void main(String[] args) { 5 try{ 6 Base base = new Base(); 7 //Base base = new Sub();這樣是編譯不經過的,由於Sub的構造拋出Exception,它比父類的構造函數 8 //拋出的異常範圍要寬,必須增長新的catch塊才能解決. 9 }catch(IOException e){ 10 //異常處理 11 } 12 } 13 } 14 15 class Base{ 16 //父類拋出IOException 17 public Base() throws IOException{ 18 throw new IOException(); 19 } 20 } 21 22 class Sub extends Base{ 23 //子類拋出Exception異常 24 public Sub() throws Exception { 25 26 } 27 }
可能讀者會問,爲何Java的構造函數容許子類的構造函數拋出更普遍的異常類呢?這正好與類方法的異常機制相反,類方法的異常是這樣要求的:
class Base{ //父類方法拋出Exception public void method() throws Exception{ } } class Sub extends Base{ //子類方法的異常類型必須是覆寫方法的子類型 @Override public void method() throws IOException{ } }
子類方法能夠拋出多個異常,可是都必須是覆寫方法的子類型,對咱們的例子來講,Sub類的method方法拋出的異常必須是Exception的子類或者Exception類,這是Java覆寫的要求.構造函數之因此與此相反,是由於構造函數沒有覆寫的概念,只是構造函數間的引用調用而已,因此在構造函數中拋出受檢查異常會違背里氏替換原則,使咱們的程序缺少靈活性.
③子類構造函數擴展有限
以上彙總起來就是:非受檢查異常不要拋出,拋出了對人對己都是有害的...受檢查異常儘可能不拋出.總之一句話:在構造函數中儘量的不出現異常.