一、Exception 和 Error有什麼區別?運行時異常與通常異常有什麼區別?html
Exception和Error都繼承自java.lang.Throwable。在Java中只有Throwable的實例才能夠被拋出和捕獲,是異常處理機制的基本組成類型。java
之因此區分 checked/unchecked exception,JAVA的設計思想是區分從類/方法設計者角度來看兩種不一樣的異常: 一種是設計者認爲這個方法在使用過程當中使用者可以處理的異常,這些每每做爲checked exception。
好比一個IO系統的設計者會認爲諸如物理文件不存在或者介質沒法讀取等異常時極可能發生,而使用者徹底可能捕獲這個異常,經過讓用戶 從新輸入文件名等方式從新進行這個操做,也就是說,
這是一個可恢復的操做。因此我會在諸如 read()/write()等操做中throw 一個 IOException(checked exception)。 第二種是設計者認爲使用者不可以處理的異常,好比我寫一個函數要求傳入的參數是個正數,那麼當我發現使用者傳 了個負數進來時,合理的預期是程序中出bug了。若是我拋出一個異常描述這件事,
即便我要求調用者捕獲這個異常,他確定也不知道該怎麼辦(總不能隨便傳一 個正數進來吧)。這時候我就會拋出一個IllegalArgumentException(uncheck exception),這裏面的潛臺詞
是:小子,我知道你也是幫人背黑鍋的,處理不了這個,你仍是交給你的領導(調用你的程序)去處理這個異常吧。 同理,當JVM發現除數爲0時,拋出的ArithmeticException也是一個
unchecked exception。
從這裏能夠看出,checked exception和 unchecked exception的根本區別在於設計者認爲使用者是否可以而且應該處理這個異常。不幸的是,因爲Java使用者水平的良莠不齊,大量的 unchecked
exception該被設計成了checked exception,而對於真正的checked exception,又有太多被catch了以後啥都不做就悄無聲息了。尤爲是不聲不響吞噬exception的行爲,不但達不到設計者原本的
要求(進行 恢復處理),甚至問題更大(連 unchecked exception那種最後報錯的效果都沒了)。
二、常見的Error異常數據庫
java.lang.AbstractMethodError 抽象方法錯誤。當應用試圖調用抽象方法時拋出。 java.lang.AssertionError 斷言錯。用來指示一個斷言失敗的狀況。 java.lang.ClassCircularityError 類循環依賴錯誤。在初始化一個類時,若檢測到類之間循環依賴則拋出該異常。 java.lang.ClassFormatError 類格式錯誤。當Java虛擬機試圖從一個文件中讀取Java類,而檢測到該文件的內容不符合類的有效格式時拋出。 java.lang.Error 錯誤。是全部錯誤的基類,用於標識嚴重的程序運行問題。這些問題一般描述一些不該被應用程序捕獲的反常狀況。 java.lang.ExceptionInInitializerError 初始化程序錯誤。當執行一個類的靜態初始化程序的過程當中,發生了異常時拋出。靜態初始化程序是指直接包含於類中的static語句段。 java.lang.IllegalAccessError 違法訪問錯誤。當一個應用試圖訪問、修改某個類的域(Field)或者調用其方法,可是又違反域或方法的可見性聲明,則拋出該異常。 java.lang.IncompatibleClassChangeError 不兼容的類變化錯誤。當正在執行的方法所依賴的類定義發生了不兼容的改變時,拋出該異常。通常在修改了應用中的某些類的聲明定義而沒有對整個應用從新編譯而直接運行的狀況下,容易引起該錯誤。 java.lang.InstantiationError 實例化錯誤。當一個應用試圖經過Java的new操做符構造一個抽象類或者接口時拋出該異常. java.lang.InternalError 內部錯誤。用於指示Java虛擬機發生了內部錯誤。 java.lang.LinkageError 連接錯誤。該錯誤及其全部子類指示某個類依賴於另一些類,在該類編譯以後,被依賴的類改變了其類定義而沒有從新編譯全部的類,進而引起錯誤的狀況。 java.lang.NoClassDefFoundError 未找到類定義錯誤。當Java虛擬機或者類裝載器試圖實例化某個類,而找不到該類的定義時拋出該錯誤。 java.lang.NoSuchFieldError 域不存在錯誤。當應用試圖訪問或者修改某類的某個域,而該類的定義中沒有該域的定義時拋出該錯誤。 java.lang.NoSuchMethodError 方法不存在錯誤。當應用試圖調用某類的某個方法,而該類的定義中沒有該方法的定義時拋出該錯誤。 java.lang.OutOfMemoryError 內存不足錯誤。當可用內存不足以讓Java虛擬機分配給一個對象時拋出該錯誤。 java.lang.StackOverflowError 堆棧溢出錯誤。當一個應用遞歸調用的層次太深而致使堆棧溢出時拋出該錯誤。 java.lang.ThreadDeath 線程結束。當調用Thread類的stop方法時拋出該錯誤,用於指示線程結束。 java.lang.UnknownError 未知錯誤。用於指示Java虛擬機發生了未知嚴重錯誤的狀況。 java.lang.UnsatisfiedLinkError 未知足的連接錯誤。當Java虛擬機未找到某個類的聲明爲native方法的本機語言定義時拋出。 java.lang.UnsupportedClassVersionError 不支持的類版本錯誤。當Java虛擬機試圖從讀取某個類文件,可是發現該文件的主、次版本號不被當前Java虛擬機支持的時候,拋出該錯誤。 java.lang.VerifyError 驗證錯誤。當驗證器檢測到某個類文件中存在內部不兼容或者安全問題時拋出該錯誤。 java.lang.VirtualMachineError 虛擬機錯誤。用於指示虛擬機被破壞或者繼續執行操做所需的資源不足的狀況。
三、常見的可檢查異常編程
java.lang.Exception 根異常。用以描述應用程序但願捕獲的狀況。 java.lang.ClassNotFoundException 找不到類異常。當應用試圖根據字符串形式的類名構造類,而在遍歷CLASSPAH以後找不到對應名稱的class文件時,拋出該異常 java.lang.IllegalAccessException 當應用程序試圖反射性地建立一個實例(而不是數組)、設置或獲取一個字段,或者調用一個方法,但當前正在執行的方法沒法訪問指定類、字段、方法或構造方法的定義時,拋出 IllegalAccessException java.lang.IOException 當發生某種 I/O 異常時,拋出此異常。此類是失敗或中斷的 I/O 操做生成的異常的通用類。 java.lang.SQLException 提供關於數據庫訪問錯誤或其餘錯誤信息的異常。
java.lang.InterruptedException
線程在等待,睡眠或以其餘方式佔用時拋出,線程在活動以前或活動期間中斷。
四、常見的運行時異常數組
java.lang.RuntimeException 運行時異常。是全部Java虛擬機正常操做期間能夠被拋出的異常的父類。 java.lang.ClassCastException 當試圖將對象強制轉換爲不是實例的子類時,拋出該異常。 java.lang.IllegalArgumentException 拋出的異常代表向方法傳遞了一個不合法或不正確的參數。 java.lang.IndexOutOfBoundsException 指示某排序索引(例如對數組、字符串或向量的排序)超出範圍時拋出。 java.lang.NullPointerException 當應用程序試圖在須要對象的地方使用 null 時,拋出該異常。
五、 檢查代碼反映了異常處理中哪些不當之處?安全
public static void main(String[] args) { try{ // 業務代碼 Thread.sleep(1000L); } catch (Exception e){ //Ignore it }
}
這段代碼違反了異常處理的兩個原則:函數
第一:儘可能不要捕獲Exception這樣的通用異常,應該捕獲特定異常Thread.sleep()拋出的是java.lang.InterruptedException,另外咱們也要保證程序不會捕獲到咱們不但願捕獲的異常,例如運行時異常RuntimeException。另外除非特殊狀況不要去捕獲Throwable或者Error,這樣很難保證咱們可以正常程序處理java.lang.OutOfMemoryError;性能
第二:不要生吞(swallow)異常,這樣極可能致使很是難以診斷的詭異狀況;優化
public static void main(String[] args) { try{ // 業務代碼 } catch (Exception e){ e.printStackTrace(); } }
這段代碼若是做爲實驗代碼沒有問題,若是做爲生產代碼就有問題了。ui
java.lang.Throwable中的printStackTrace()方法是將此Throwable和其追溯打印到標準錯誤流。 此方法在錯誤輸出流上爲該Throwable
對象打印一個堆棧跟蹤,該值爲字段System.err
的值。
問題就出在這裏,在稍微複雜的生產環境中,標準出錯(stderr)不是一個合適的輸出選項,由於你很難判斷出到底輸出到哪了。最好使用產品日誌,詳細的輸入到日誌系統中。
public void readPreferences(String fileName){ //...perform operations... InputStream in = new FileInputStream(fileName); //...read the preferences fil... }
Throw early,catch late 原則
若是參數fileName爲null,則會拋出java.lang.NullPointerException。因爲沒有第一時間暴露問題,堆棧信息可能很是使人費解。在生產環境可能有各類各樣的場景,若是在發現問題的時候,能第一時間拋出,能更加清晰的反應問題。下面是咱們修改後的代碼,讓Throw early,異常信息就很是直觀了:
public void readPreferences(String fileName){ Objects. requireNonNull(fileName); //...perform operations... InputStream in = new FileInputStream(fileName); //...read the preferences fil... }
catch late 咱們通常有兩種處理方式,一種就是生吞,本質其實就是掩蓋異常。另外一種就是保留原有異常的cause信息,直接拋出或者構建新的異常拋出。在更高層面由於有了清晰的業務邏輯,每每更清楚怎麼處理。
六、自定義異常
有時候咱們會根據須要自定義異常,這個時候除了保證提供足夠的信息,還須要考慮兩點:
第一:是否須要定義成Check Exception,這種類型的設計初衷更是爲了從異常狀況恢復;另外若是一個異常所表示的並非代碼自己的不足所致使的非正常狀態,而是一系列應用自己也沒法控制的狀況,那麼咱們將須要使用Checked Exception。
第二:在保證診斷信息足夠的同時,也須要考慮避免包含敏感信息;例如:java.net.ConnectException出錯的信息是相似「Connection refused」而不包含具體的機器名,IP等。
業界有一種爭論,java語言的Check Exception也許是一個設計錯誤:
Check Exception的假設是咱們捕獲了異常,而後恢復程序。但實際大多數狀況不可能恢復;
Check Exception不兼容functional編程;
七、從性能角度解析Java的異常處理機制
參考:
https://time.geekbang.org/column/article/6849
http://www.importnew.com/27834.html