異常的總體結構
注意所有的異常都是trowable的子類,只有是這個類的子類異常才能被jvm拋出,一個好的程序必然是對我們的異常進行了大量的處理,使得我們程序的健壯性大大增強的。
異常的分類
1)根據錯誤程度
Error一般來說是硬件錯誤或者是JVM錯誤。無法在代碼中進行處理
Exception是異常,可以抓取并進行處理,良好的異常處理機制可以增強代碼的健壯性。
2)根據是否編譯前就能檢查出來
unchecked exception(未檢查的異常): Error 和 RuntimeException 及其子類
checked exceptions (檢查了的異常):除了未檢查的異常都是檢查了的異常
** checked exceptions**: 通常是從一個可以恢復的程序中拋出來的,并且最好能夠從這種異常中使用程序恢復。比如 FileNotFoundException, ParseException 等。檢查了的異常發生在編譯階段,必須要使用 try…catch(或者 throws )否則編譯不通過
unchecked exceptions: 通常是如果一切正常的話本不該發生的異常,但是的確發生了。發生在運行期,具有不確定性,主要是由于程序的邏輯問題所引起的。比如 ArrayIndexOutOfBoundException, ClassCastException 等。從語言本身的角度講,程序不該去 catch 這類異常,雖然能夠從諸如 RuntimeException 這樣的異常中 catch 并恢復,但是并不鼓勵終端程序員這么做,因為完全沒要必要。因為這類錯誤本身就是 bug,應該被修復,出現此類錯誤時程序就應該立即停止執行。 因此,面對 Errors 和 unchecked exceptions 應該讓程序自動終止執行,程序員不該做諸如 try/catch 這樣的事情,而是應該查明原因,修改代碼邏輯。
RuntimeException:RuntimeException體系包括錯誤的類型轉換、數組越界訪問和試圖訪問空指針等等。處理 RuntimeException 的原則是:如果出現 RuntimeException,那么一定是程序員的錯誤。例如,可以通過檢查數組下標和數組邊界來避免數組越界訪問異常。其他(IOException等等)checked 異常一般是外部錯誤,例如試圖從文件尾后讀取數據等,這并不是程序本身的錯誤,而是在應用環境中出現的外部錯誤。
當拋出異常之后,會有如下的事情發生。
1)在堆上創建異常對象。
2)當前執行路徑被終止
3)從當前環境彈出對異常對象的引用。
4)異常處理機制接管程序,
5)異常處理程序執行,將程序從錯誤的狀態中恢復,使得我們的程序可以從另一個地方繼續執行或者是繼續執行下去
捕獲異常基本模塊
/**
* 一般的關于異常的處理流程
*/
try{
//try塊,嘗試產生各種(可能產生異常的)方法的程序塊
}catch(RuntimeExceptione){
//catch 塊, 用于捕獲我們產生各種異常的程序塊,對我們的異常進行必要的處理
//注意我們這里有著不同的catch塊,但是我們這里并不是和switch一樣直接和某個case進行匹配
//而是說我們這類是依次的進行比較,進入第一個符合的catch塊,而后面的catch塊就不會再去嘗試匹配
//所以在使用的時候我們應該注意順序,從上到下范圍應該是逐漸增大的。否則如果第一個就是Catch(Exception e)
//那么后面的catch代碼都將不會執行
e.printStackTrace();
}catch(Exceptione){
e.printStackTrace();
}finally{
//finally塊 無論如何都會執行的代碼。一般用于將我們的對象恢復到安全狀態,
//比如流的關閉,比如說網絡的關閉等
//不能在這里有return語句,否則的話所有的異常都會丟失。
//還有一種異常丟失的可能性就是說我們的try塊中產生異常,然后在我們的finally塊中仍然產生了異常,這個
//時候我們的try塊中的異常就會丟失
}
異常處理和異常上拋
如果在方法或者說類的最后顯示的表明throws **Exception。這就意味著說是可能會拋出這個異常,向上拋出,自身不做處理
如果在try catch塊中進行處理的exception就是自己處理,沒有向上拋出。
/**
* 用于表示異常自身處理和異常拋出的用例,其中 FileNotFoundExceptiion是向上拋出,而IOException是自己處理
* 本方法的作用是說通過文件路徑獲得文件的內容
* @param filePath 文件的路徑
* @return 文件的內容
* @throws FileNotFoundException
*/
publicstaticStringgetFileContent(StringfilePath)throwsFileNotFoundException{
//數據的暫時存儲
Stringcontent="";
//創建輸入流對象
FileReaderfReader=newFileReader(newFile(filePath));
BufferedReaderbReader=newBufferedReader(fReader);
//將我們文件中的數據按行讀取,并對可能產生的ioException進行處理
try{
while(bReader.readLine()!=null){
content+=bReader.readLine();
}
}catch(IOExceptione){
e.printStackTrace();
}
returncontent;
}
定義屬于自己的異常
/**
* 自定義的異常類
* @author kingwen
*
*/
publicclassMyExceptionextendsException{
//構造函數:每次創建實例的時候會輸出this is myexception
publicMyException(){
System.out.println("this is myexception");
}
publicMyException(Strings){
super(s);
System.out.println("this is myexception"+s);
}
}
在我們的方法中進行拋出,注意,這里要顯示的表示我們這個類可能會拋出這個異常,然后用于讓使用這個方法的人自身通過try catch塊進行處理或者是繼續通過顯示說明來進行上拋。
publicclassInputAge{
publicstaticvoidAgeChecked(inti)throwsMyException{
if(0
System.out.println("年齡正常");
}else{
thrownewMyException("年齡不正常");
}
}
}
最后,我們調用這個方法,并通過try catch塊進行處理
/**
* 本例用于測試java異常處理
*/
publicclassDemo{
publicstaticvoidmain(String[]args){
try{
InputAge.AgeChecked(109);
}catch(MyExceptione){
System.out.println("自定義的Exception");
e.printStackTrace();
}catch(Exceptione){
System.out.println("excertion e");
e.printStackTrace();
}
}
}
這個是運行結果:
首先是先new了我們的異常對象,在構造方法的時候輸出了第一個語句
然后是在我們的catch塊中進行的處理,
最后是通過e.printStackTrace()方法將我們的方法調用順序打印了出來
(就是后面的棧軌跡)
關于異常處理我們平時應該注意的一些代碼部分。
嘗試使用try catch 嵌套塊:對于創建可能失敗,處理完之后需要釋放資源的代碼
publicstaticStringgetFileContent1(StringfilePath){
//數據的暫時存儲
Stringcontent="";
//創建輸入流對象
FileReaderfReader=null;
BufferedReaderbReader=null;
try{
fReader=newFileReader(newFile(filePath));
bReader=newBufferedReader(fReader);
//將我們文件中的數據按行讀取,并對可能產生的ioException進行處理
try{
while(bReader.readLine()!=null){
content+=bReader.readLine();
}
}catch(IOExceptione){
e.printStackTrace();
}finally{
try{
bReader.close();
}catch(IOExceptione){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}catch(FileNotFoundExceptione1){
// TODO Auto-generated catch block
e1.printStackTrace();
}finally{
try{
fReader.close();
}catch(IOExceptione){
// TODO Auto-generated catch block
e.printStackTrace();
}
}
returncontent;
}
對于構造不可能失敗但是需要使用完釋放資源的對象使用try-finally塊
在可去除對象之后緊跟try-finally 原則:
//創建對象
MyObjectobj=newMyObject()
try{
//對于我們的obj對象進行操作
}finally{
//釋放我們的對象資源
obj.dispose();
}
最后是我目前來說覺得不怎么重要的部分
棧軌跡:printStackTrace方法所提供的信息可以通過getStackTrace來進行直接訪問。
實際上就是我們通過方法e.printStackTrace();進行依次打印的東西。
我們的方法的調用信息實際上是通過一個棧來進行保存的,后進后出,
我們通過e.printStackTrace()將棧中的數據放入到一個數組中,
其中數組中的第一個元素是調用序列的最后一個,在打印的時候我們從第一個開始打印,
所以我們看到的依次是我們報錯的地方,調用它的地方,。。。。最后是main方法
重新拋出異常
方法在catch中得到我們的異常對象的引用之后,從當前位置重新拋出
try{
//try塊
}catch(Exceptione){
e.printStackTrace();
//重新拋出這個異常,但是這個異常的信息和原來的信息是一致的,
throwe;
//重新拋出這個異常,但是異常的信息變為當前位置的信息,原來的信息丟棄
//throw e.fillInStackTrace();
}
關于異常的知識暫時的了解是這樣的,歡迎覺得文章有問題的小伙伴們找我交流。