異常相關知識點梳理

一. 異常是什麼

異常是指阻止當前方法或作用域繼續執行的問題。比如你讀取的文件不存在,數組越界,進行除法時,除數爲0等都會導致異常。

一個文件找不到的異常

public class TestException {
    public static void main(String[] args) throws IOException {
        InputStream is = new FileInputStream("jaywei.txt");
        int b;
        while ((b = is.read()) != -1) {
        }    
    }
}

運行結果:

Exception in thread "main" java.io.FileNotFoundException: jaywei.txt (系統找不到指定的文件。)
	at java.io.FileInputStream.open0(Native Method)
	at java.io.FileInputStream.open(FileInputStream.java:195)    
	at java.io.FileInputStream.<init>(FileInputStream.java:138)    
	at java.io.FileInputStream.<init>(FileInputStream.java:93)    
	at exception.TestException.main(TestException.java:10)

二. 異常的層次結構

從前從前,有位老人,他的名字叫Throwable,他生了兩個兒子,大兒子叫Error,二兒子叫Exception。記住,萬物皆可拋

Error

表示編譯時或者系統錯誤,如虛擬機相關的錯誤,OutOfMemoryError等,error是無法處理的。

Exception

代碼異常,Java程序員關心的基類型通常是Exception。它能被程序本身可以處理,這也是它跟Error的區別。

它可以分爲RuntimeException(運行時異常)和CheckedException(可檢查的異常)。

常見的RuntimeException異常:

  • NullPointerException 空指針異常
  • ArithmeticException 出現異常的運算條件時,拋出此異常
  • IndexOutOfBoundsException 數組索引越界異常
  • ClassNotFoundException 找不到類異常
  • IllegalArgumentException(非法參數異常)

常見的 Checked Exception 異常:

  • IOException (操作輸入流和輸出流時可能出現的異常)
  • ClassCastException(類型轉換異常類)

Checked Exception就是編譯器要求你必須處置的異常。與之相反的是,Unchecked Exceptions,它指編譯器不要求強制處置的異常,它包括Error和RuntimeException 以及他們的子類。

三、異常處理

當異常出現後,會在堆上創建異常對象。當前的執行路徑被終止,並且從當前環境中彈出對異常對象的引用。這時候異常處理程序,使程序從錯誤狀態恢復,使程序繼續運行下去。

異常處理主要有拋出異常、捕獲異常、聲明異常。

捕獲異常

try{
    // 程序代碼
}catch(Exception e){
    //Catch 塊
}finaly{ 
    //無論如何,都會執行的代碼塊
}

我們可以通過 try...catch...捕獲異常代碼,再通過 finaly執行最後的操作,如關閉流等操作。

聲明拋出異常

除了 try...catch...捕獲異常,我們還可以通過throws聲明拋出異常。

當你定義了一個方法時,可以用 throws關鍵字聲明。使用了 throws關鍵字表明,該方法不處理異常,而是把異常留給它的調用者處理。舉例來說,我們不會在持久化層及服務層處理異常,而是選擇將異常拋出,由高層的控制器層(controller)去捕獲,從而方便進行聲明式事務的異常回滾。

//該方法通過throws聲明瞭IO異常。 
private void readFile() throws IOException {        
    InputStream is = new FileInputStream("jaywei.txt");        
    int b;        
    while ((b = is.read()) != -1) {
    }    
}

從方法中聲明拋出的任何異常都必須使用throws子句。

拋出異常

throw關鍵字作用是拋出一個 Throwable類型的異常,它一般出現在函數體中。在異常處理中,try語句要捕獲的是一個異常對象,其實此異常對象也可以自己拋出。

例如拋出一個 RuntimeException 類的異常對象:

throw new RuntimeException(e);

任何Java代碼都可以通過 Java 的throw語句拋出異常。

注意點

  • 非檢查異常(Error、RuntimeException 或它們的子類)不可使用 throws 關鍵字來聲明要拋出的異常。
  • 一個方法出現編譯時異常,就需要 try-catch/ throws 處理,否則會導致編譯錯誤。

四、try-catch-finally-return執行順序

try-catch-finally-return 執行描述

  • 如果不發生異常,不會執行catch部分。
  • 不管有沒有發生異常,finally都會執行到。
  • 即使try和catch中有return時,finally仍然會執行
  • finally是在return後面的表達式運算完後再執行的。(此時並沒有返回運算後的值,而是先把要返回的值保存起來,若finally中無return,則不管finally中的代碼怎麼樣,返回的值都不會改變,仍然是之前保存的值),該情況下函數返回值是在finally執行前確定的)
  • finally部分就不要return了,要不然,就回不去try或者catch的return了。

看一個例子

 public static void main(String[] args) throws IOException { 
     System.out.println("result:" + test());    
 }    
private static int test() {
    int temp = 1;       
    try {            
        System.out.println("start execute try,temp is:"+temp);      
        return ++temp;      
    } catch (Exception e) {    
        System.out.println("start execute catch temp is: "+temp);     
        return ++temp;       
    } finally {        
        System.out.println("start execute finally,temp is:" + temp);    
        ++temp;     
    }  
}

運行結果:

start execute try,temp is:1
start execute finally,temp is:2
result:2

分析

  • 先執行try部分,輸出日誌,執行 ++temp表達式,temp變爲2,這個值被保存起來。
  • 因爲沒有發生異常,所以catch代碼塊跳過。
  • 執行finally代碼塊,輸出日誌,執行 ++temp表達式.
  • 返回try部分保存的值2.

五、Java異常類的幾個重要方法

getMessage

Returns the detail message string of this throwable.

getMessage會返回Throwable的 detailMessage屬性,而 detailMessage就表示發生異常的詳細消息描述。

舉個例子, FileNotFoundException異常發生時,這個 detailMessage就包含這個找不到文件的名字。

getLocalizedMessage

Creates a localized description of this throwable.Subclasses may override thismethod in order to produce alocale-specific message. For subclasses that do notoverride thismethod, the default implementation returns the same resultas getMessage()

throwable的本地化描述。子類可以重寫此方法,以生成特定於語言環境的消息。對於不覆蓋此方法的子類,默認實現返回與相同的結果 getMessage()。

getCause

Returns the cause of this throwable or null if thecause is nonexistent or unknown.

返回此可拋出事件的原因,或者,如果原因不存在或未知,返回null。

printStackTrace

Prints this throwable and its backtrace to thestandard error stream.The first line of output contains the result of the toString() method forthis object.Remaining lines represent data previously recorded by themethod fillInStackTrace().

該方法將堆棧跟蹤信息打印到標準錯誤流。

輸出的第一行,包含此對象toString()方法的結果。剩餘的行表示,先前被方法fillInStackTrace()記錄的數據。如下例子:

java.lang.NullPointerException
	at MyClass.mash(MyClass.java:9)         
	at MyClass.crunch(MyClass.java:6)         
	at MyClass.main(MyClass.java:3)

六、自定義異常

自定義異常通常是定義一個繼承自 Exception 類的子類。

那麼,爲什麼需要自定義異常?

  • Java提供的異常體系不可能預見所有的錯誤。
  • 業務開發中,使用自定義異常,可以讓項目代碼更加規範,也便於管理。

下面是我司自定義異常類的一個簡單demo

public class BizException extends Exception {    
    //錯誤信息 
    private String message; 
    //錯誤碼   
    private String errorCode; 
    public BizException() {
    }    
    public BizException(String message, String errorCode) { 
        this.message = message;    
        this.errorCode = errorCode;  
    }  
    @Override
    public String getMessage() {  
        return message; 
    }  
    public void setMessage(String message) { 
        this.message = message;  
    }  
    public String getErrorCode() {  
        return errorCode;  
    }  
    public void setErrorCode(String errorCode) {  
        this.errorCode = errorCode;  
    }
}

跑個main方測試一下

public class TestBizException {   
    public static void testBizException() throws BizException {       
        System.out.println("throwing BizException from testBizException()");   
        throw new BizException("100","哥,我錯了");   
    }  
    public static void main(String[] args) { 
        try {       
            testBizException();  
        } catch (BizException e) {     
            System.out.println("自己定義的異常");       
            e.printStackTrace();      
        }  
    }
}

運行結果:

exception.BizException: 100throwing BizException from testBizException()自己定義的異常
	at exception.TestBizException.testBizException(TestBizException.java:7)
	at exception.TestBizException.main(TestBizException.java:12)

七、Java7 新的 try-with-resources語句

try-with-resources,是Java7提供的一個新功能,它用於自動資源管理。

  • 資源是指在程序用完了之後必須要關閉的對象。
  • try-with-resources保證了每個聲明瞭的資源在語句結束的時候會被關閉
  • 什麼樣的對象才能當做資源使用呢?只要實現了java.lang.AutoCloseable接口或者java.io.Closeable接口的對象,都OK。

在 try-with-resources出現之前

try{  
    //open resources like File, Database connection, Sockets etc
	}
catch (FileNotFoundException e) {  
    // Exception handling like FileNotFoundException, IOException etc
}finally{  
    // close resources
}

Java7, try-with-resources出現之後,使用資源實現

try(// open resources here)
    {   
        // use resources
    } catch (FileNotFoundException e) {  
        // exception handling
    }
    // resources are closed as soon as try-catch block is executed.

Java7使用資源demo

public class Java7TryResourceTest {   
    public static void main(String[] args) {    
        try (BufferedReader br = new BufferedReader(new FileReader("C:/jaywei.txt"))) {   
            System.out.println(br.readLine());    
        } catch (IOException e) {    
            e.printStackTrace(); 
        }   
    }
}

使用了 try-with-resources的好處

  • 代碼更加優雅,行數更少。
  • 資源自動管理,不用擔心內存泄漏問題。

八、異常鏈

我們常常會想要在捕獲一個異常後拋出另一個異常,並且希望把原始異常的信息保存下來,這被稱爲異常鏈

throw 拋出的是一個新的異常信息,這樣會導致原有的異常信息丟失。在JDk1.4以前,程序員必須自己編寫代碼來保存原始異常信息。現在所有 Throwable 子類在構造器中都可以接受一個 cause(異常因由) 對象作爲參數。

這個 cause就用來表示原始異常,這樣通過把原始異常傳遞給新的異常,使得即使當前位置創建並拋出了新的異常,也能通過這個異常鏈追蹤到異常最初發生的位置。

使用方式如下:

public class TestChainException {  
    public void readFile() throws MyException{    
        try {         
            InputStream is = new FileInputStream("jay.txt");        
            Scanner in = new Scanner(is);  
            while (in.hasNext()) {   
                System.out.println(in.next());     
            }      
        } catch (FileNotFoundException e) {  
            //e 保存異常信息     
            throw new MyException("文件在哪裏呢", e);  
        }  
    }   
    public void invokeReadFile() throws MyException{   
        try {       
            readFile();    
        } catch (MyException e) {     
            //e 保存異常信息     
            throw new MyException("文件找不到", e);  
        }  
    }   
    public static void main(String[] args) {    
        TestChainException t = new TestChainException();  
        try {    
            t.invokeReadFile();   
        } catch (MyException e) {     
            e.printStackTrace();     
        }  
    }
}
//MyException 構造器
public MyException(String message, Throwable cause) {    
    super(message, cause); 
}

九、異常匹配

拋出異常的時候,異常處理系統會按照代碼的書寫順序找出"最近"的處理程序。找到匹配的處理程序之後,它就認爲異常將得到處理,然後就不再繼續查找。

查找的時候並不要求拋出的異常同處理程序的異常完全匹配。派生類的對象也可以配備其基類的處理程序

看demo

package exceptions;
//: exceptions/Human.java
// Catching exception hierarchies.
class Annoyance extends Exception {}
class Sneeze extends Annoyance {}
public class Human {  
    public static void main(String[] args) { 
        // Catch the exact type: 
        try {   
            throw new Sneeze(); 
        } catch(Sneeze s) {   
            System.out.println("Caught Sneeze"); 
        } catch(Annoyance a) {   
            System.out.println("Caught Annoyance");  
        }   
        // Catch the base type: 
        try {  
            throw new Sneeze(); 
        } catch(Annoyance a) {   
            System.out.println("Caught Annoyance");
        } 
	}
}

catch(Annoyance a)會捕獲Annoyance以及所有從它派生的異常。捕獲基類的異常,就可以匹配所有派生類的異常

try {   
    throw new Sneeze();  
} catch(Annoyance a) {  
} catch(Sneeze s) { 
    //這句編譯器會報錯,因爲異常已由前面catch子句處理 
}

十、Java常見異常

NullPointerException

空指針異常,最常見的一個異常類。簡言之,調用了未經初始化的對象或者是不存在的對象,就會產生該異常。

ArithmeticException

算術異常類,程序中出現了除數爲0這樣的運算,就會出現這樣的異常。

ClassCastException

類型強制轉換異常,它是JVM在檢測到兩個類型間轉換不兼容時引發的運行時異常。

ArrayIndexOutOfBoundsException

數組下標越界異常,跟數組打交道時,需要注意一下這個異常。

FileNotFoundException

文件未找到異常,一般是要讀或者寫的文件,找不到,導致該異常。

SQLException

操作數據庫異常,它是Checked Exception(檢查異常);

IOException

IO異常,一般跟讀寫文件息息相關,它也是Checked Exception(檢查異常)。平時讀寫文件,記得IO流關閉!

NoSuchMethodException

方法未找到異常

NumberFormatException

字符串轉換爲數字異常

發佈了11 篇原創文章 · 獲贊 3 · 訪問量 3146
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章