JavaSE基礎:異常處理

異常處理

1.爲何要處理異常?

在學習過程或者將來工做當中,咱們重來不但願寫的代碼有錯誤,不會出現問題,用戶操做永遠邏輯清晰而正確,一切都按照咱們祈禱的那樣運行,然而這是不可能的。必然會有錯誤必然會要咱們去處理,可是錯誤的處理並非咱們代碼的核心。html

Java 異常機制可使程序中異常處理代碼和正常業務代碼分離,保證程序代碼更加優雅,並提升程序健壯性。(這段話須要咱們經歷了才能去理解,慢慢來)java

在有效使用異常的狀況下,異常能清晰的回答 what, where, why 這 3 個問題:異常類型回答了「什麼」被拋出,異常堆棧跟蹤回答了「在哪「拋出,異常信息回答了「爲何「會拋出。(熟練使用以後就行了)程序員

Java的異常機制依靠於try,catch,finally,throw,throws關鍵字,其中try塊中一般放置可能引起異常的代碼,catch後對應異常類型和響應的異常處理代碼塊,finally塊在java異常機制中老是會被執行,一般用於回收try塊中打開的物理資源,throw用於拋出一個實際的異常(異常的實例),throws主要在方法簽名中使用,用於聲明該方法可能會拋出的異常,方便或者提醒方法的使用者來捕獲並處理異常。數組

2.概念

Java 異常是 Java 提供的用於處理程序中錯誤的一種機制。網絡

所謂錯誤是指在程序運行的過程當中發生的一些異常事件(如:除 0 溢出,數組下標越界,所要讀取的文件不存在等)。函數

設計良好的程序應該在異常發生時提供處理這些錯誤的方法,使得程序不會應爲異常的發生而阻斷或產生不可預見的結果。性能

  • Java 程序的執行過程當中如出現異常事件,能夠生成一個異常類對象,該異常對象封裝了異常事件的信息並提交給 Java 運行時系統,這個過程稱爲拋出(throw)異常。
  • 當 Java 運行時系統接受到異常對象時,會尋找能處理這一異常的代碼並把當前異常對象交給其處理,這一過程稱爲捕獲(catch)異常。

3.分類

請必定要記住這張圖

###(1) Throwable(老祖宗)學習

有兩個重要的子類:Exception 和 Error,兩者都是 Java 異常處理的重要子類,各自都包含大量子類。spa

(2) Error(無能爲力)

是程序沒法處理的錯誤,一般發生於虛擬機自身,表示運行應用程序中較嚴重問題。例如,Java虛擬機運行錯誤(Virtual MachineError),當 JVM 再也不有繼續執行操做所需的內存資源時,將出現 OutOfMemoryError。這些異常發生時,Java 虛擬機(JVM)通常會選擇線程終止。線程

不須要咱們進行捕獲處理

(3) Exception(是非分明)

  • Checked異常(檢查異常或者編譯異常)

    必須處理的異常:Checked異常是Java特有的,在java設計哲學中,Checked異常被認爲是能夠被處理或者修復的異常,因此Java程序必須顯式處理Checked異常,當咱們使用或者出現Checked異常類的時候,程序中要麼顯式try- catch捕獲該異常並修復,要麼顯式聲明拋出該異常,不然程序沒法經過編譯。(Checked異常某種程度上下降了開發生產率和代碼執行率,在java領域是一個備受爭論的問題,我我的堅持使用異常處理業務邏輯,那麼點效率能夠忽略)

    演示代碼:會有錯誤報出來,咱們須要對程序代碼進行處理

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.InputStream;
    
    public class Demo01 {
        public static void main(String[] args) {
            InputStream in = new FileInputStream("C:/temp/ddd");
        }
    }
    複製代碼

    處理方式一:try...catch

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    
    public class Demo01 {
        public static void main(String[] args) {
            try {
                InputStream in = new FileInputStream("C:/temp/ddd");
            } catch (FileNotFoundException e) {
                e.printStackTrace();//在控制檯輸出錯誤信息,給開發人員使用
                //異常出現後,須要如何處理的代碼
            }
        }
    }
    
    複製代碼

    處理方式二:throws

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.InputStream;
    
    public class Demo01 {
        public static void main(String[] args) throws FileNotFoundException {//沒有對異常進行處理,只是返回給調用者
            InputStream in = new FileInputStream("C:/temp/ddd");
        }
    }
    複製代碼
  • Runtime異常(運行時異常)

    可處理可不處理的異常(RuntimeException):運行時異常的特色是 Java 編譯器不會檢查它,也就是說,當程序中可能出現這類異常,即便沒有用 try-catch 語句捕獲它,也沒有用 throws 子句聲明拋出它,也會編譯經過。

    package com.shxt.demo01;
    
    public class Demo02 {
        public static void main(String[] args) {
           String[] array = {"1","0","abc"};
            try{
                int a = Integer.parseInt(array[0]);
                //int b = Integer.parseInt(array[3]);//IndexOutOfBoundsException
                //int b = Integer.parseInt(array[2]);//NumberFormatException
                int b = Integer.parseInt(array[1]);//NumberFormatException
                int c = a/b;//當b是0的時候,ArithmeticException
                System.out.println("您輸出的結果是"+c);
            }catch(IndexOutOfBoundsException ie){
                System.out.println("數組越界,輸入的參數不夠");
            }
            catch(NumberFormatException ne){
                System.out.println("數字格式異常:程序只能接收整數參數");
            }
            catch(ArithmeticException ae){
                System.out.println("算術法異常");
            }
            catch(Exception e){
                System.out.println("出現異常");
            }
    
        }
    }
    複製代碼

    程序說明:

    程序中通常將Exception放在最後,先捕獲小異常(子類異常),再捕獲大異常。若是順序顛倒,還會出現編譯錯誤

4.捕獲異常 try、catch 和 finally

(1) try...catch

語法結構:

try{
  // 須要被檢測的代碼
}catch(異常類 變量){
 // 處理方式
}catch(異常類 變量){
 // 處理方式
}
複製代碼

語句說明:

  • try ... catch 是最多見的異常捕獲語句,try就是對你代碼不自信,O(∩_∩)O哈哈~,也就是業務代碼,若是try塊中出現問題或者異常,系統自動生成一個異常對象,該異常對象提交到Java運行環境,java運行環境收到異常對象後,會尋找可以處理該異常的catch塊,若是找到,就將異常對象交給該catch塊處理,若是沒有找到,就終止程序運行。

  • catch塊中如何處理異常:參考上面的運行時異常的示例理解下面的一段話

    一個try塊以後可能存在多個catch塊,java運行時與catch塊()內的異常類進行比較,判斷該異常是否 instanceof 該類,若是屬於該類,就將該異常對象傳給catch塊內的異常形參,catch塊後能夠對該異常進行處理,獲取相關異常的詳細信息等。注意系統生成的異常實例對象是相對具體的子類異常對象,而進入一個catch塊後就不會再進入下一個catch塊,因此這也是咱們儘可能將小異常放在大異常前面的緣由。

在Throwable類的主要方法:輸出出現異常提示信息

編號 方法名稱 類型 描述
1 public String getMessage() 普通方法 返回詳細描述字符串(經常使用)
2 public void printStackTrace() 普通方法 異常名稱、異常信息、異常出現位置,程序員使用
3 public void printStackTrace(PrintStream s) 普通方法 跟蹤棧信息輸出到指定輸出流
4 public StackTraceElement[] getStackTrace() 普通方法 返回跟蹤棧信息,在J2EE的時候有可能用到

好好理解下面的一段話

採用別的替選數據或方案或者提示用戶從新操做或者從新拋出異常,進行異常轉譯,從新包裝,

交給上層調用者來對該異常進行處理,我開發過程當中常用這種方式處理一些業務邏輯的判斷

捕獲多個異常的JDK7後的新寫法

package com.shxt.demo01;

public class Demo03 {
    public static void main(String[] args) {
       String[] array = {"1","0","abc"};
        try{
            int a = Integer.parseInt(array[0]);
            //int b = Integer.parseInt(array[3]);//IndexOutOfBoundsException
            //int b = Integer.parseInt(array[2]);//NumberFormatException
            int b = Integer.parseInt(array[1]);//NumberFormatException
            int c = a/b;//當b是0的時候,ArithmeticException
            System.out.println("您輸出的結果是"+c);
        }catch(IndexOutOfBoundsException | NumberFormatException | ArithmeticException e){
            e.printStackTrace();
        }catch(Exception e){
            System.out.println("出現異常");
        }

    }
}
複製代碼

(2) try...catch...finally

語法結構:

try{
  // 須要被檢測的代碼
}catch(異常類 變量){
 // 處理方式
}catch(異常類 變量){
 // 處理方式
}finally{
  //必定會執行
}
複製代碼

或者

try{
  // 須要被檢測的代碼
}finally{
  //必定會執行
}
複製代碼
  • try 塊: 用於捕獲異常。其後可接零個或多個 catch 塊,若是沒有 catch 塊,則必須跟一個 finally 塊。
  • catch 塊: 用於處理 try 捕獲到的異常。
  • finally 塊: 不管是否捕獲或處理異常,finally 塊裏的語句都會被執行。當在 try 塊或 catch 塊中遇到 return 語句時,finally 語句塊將在方法返回以前被執行。在如下 4 種特殊狀況下,finally 塊不會被執行:
    • 在 finally 語句塊中發生了異常。
    • 在前面的代碼中用了 System.exit() 退出程序。
    • 程序所在的線程死亡。
    • 關閉 CPU。
package com.shxt.demo01;

public class Demo04 {
    public static void main(String[] args) {
       String[] array = {"1","0","abc"};
        try{
            int a = Integer.parseInt(array[0]);
            //int b = Integer.parseInt(array[3]);//IndexOutOfBoundsException
            //int b = Integer.parseInt(array[2]);//NumberFormatException
            int b = Integer.parseInt(array[1]);//NumberFormatException
            int c = a/b;//當b是0的時候,ArithmeticException
            System.out.println("您輸出的結果是"+c);
        }catch(IndexOutOfBoundsException | NumberFormatException | ArithmeticException e){
            e.printStackTrace();
        }catch(Exception e){
            System.out.println("出現異常");
        }finally{
            array[0] = "999";
            System.out.println("First element value: " +array[0]);
            System.out.println("The finally statement is executed");
        }

    }
}

複製代碼

5.throws:聲明拋出異常

package com.shxt.demo01;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;

public class Demo01 {
    public static void main(String[] args) throws FileNotFoundException {//沒有對異常進行處理,只是返回給調用者
        InputStream in = new FileInputStream("C:/temp/ddd");
    }
}
複製代碼
  • throws聲明拋出異常,在方法簽名中使用,上面的Demo01就是其使用的例子。它能夠聲明拋出多個類,多個類之間用「,」隔開。

  • 咱們爲何要聲明拋出異常?

    • 當某個方法中程序的執行可能會出現異常,可是該方法並不知道如何處理異常
    • 咱們想把這個異常交給上層方法調用者來處理或者修復,那咱們給該方法加上關鍵字throws 異常,以聲明該方法可能會出現的異常
    • 加了throws關鍵字以後,該方法咱們就無需再用try—catch來捕獲異常了,由於這已經不是咱們這個方法須要操心的事情了
  • 使用throws聲明異常的時候,涉及到子類對父類的方法重寫時,子類聲明的異常類型應該是父類方法聲明的異常類型的子類或者相同類

  • (該段話能夠忽略)若是throws 聲明的是checked異常,根據checked異常的規定,咱們不能對該異常視而不見,由於咱們必須處理該異常,因此當拿到一個聲明瞭可能會發生checked異常的方法時,在調用該方法時,要麼放在try塊中來顯式捕捉該異常,要麼放在另一個帶throws聲明異常的方法中。

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.IOException;
    
    public class Demo05 {
        public static void main(String[] args)throws Exception{
            test();//test 聲明會產生checked 異常 所以main函數也須要聲明異常
            // 或者在try - catch 中捕獲該異常
        }
        public static void test() throws IOException {
            FileInputStream fis = new FileInputStream("a.text");
        }
    }
    複製代碼
  • (記住這句話)推薦使用Runtime異常,將Checked異常轉換爲Runtime異常

    package com.shxt.demo01;
    
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    
    public class Demo06 {
        public static void main(String[] args){
            //根據業務狀況,看看是否要處理運行異常
            try {
                test();
            }catch (RuntimeException e){
                System.out.println(e.getMessage());
            }
    
        }
        public static void test() {
            try {
                FileInputStream fis = new FileInputStream("a.text");
            } catch (FileNotFoundException e) {
                e.printStackTrace();//這句話不是處理異常
                throw new RuntimeException("文件不存在");
            }
        }
    }
    複製代碼

6.throw:拋出異常

java容許程序自行拋出異常,一般系統幫助咱們檢查是否發生一些廣泛定義的異常,可是有些異常可能不是廣泛定義的,只是與咱們業務不符,因此咱們能夠自行拋出異常,也能夠自行拋出一些咱們自定義的異常,而拋出異常的行爲與系統拋出異常的行爲必定程度上是等價的,後續處理方式也是同樣的,在這裏咱們使用throw關鍵字。

throw語句能夠單獨使用,注意它拋出的是一個異常實例

  • 當咱們自行拋出的異常是checked異常的時候,該throw語句要麼是在如上面例子中的try塊中,顯示捕獲,要麼是在一個已經用throws聲明會出現異常的方法中

    package com.shxt.demo01;
    
    public class Demo07 {
        public static void main(String[] args) {
           int a = -1;
           try {
               if(a<0){
                   throw new Exception("數據小於零,能夠單獨使用");
               }
           }catch (Exception e){
               e.printStackTrace();
           }
        }
    }
    複製代碼
  • 若是咱們拋出的是runtime異常,那麼狀況就很簡單了,它無需在try塊中,也不須要將對應的方法用throws聲明,若是咱們想要處理,就捕獲處理它,不論是在它自身方法體內,或者是對應方法者,也能夠不去理會,固然咱們本身拋出的異常,一般狀況下是要處理的,否則拋出去以後無論最後只能中斷程序運行了,只不過拋出是runtime異常時,在編譯時沒有那麼嚴格

    package com.shxt.demo01;
    
    public class Demo07 {
        public static void main(String[] args) {
           int a = -1;
    
           if(a<0){
               throw new RuntimeException("數據小於零,能夠單獨使用");
           }    
        }
    }
    複製代碼

自定義異常類:系統會拋出一些廣泛意義的異常,那麼咱們也就不必再本身操心throw了,一般throw的是自定義的異常類。

  • 自定義異常類都應該繼承Exception類或者Exception下的子類如RuntimeException異常(開發中大部分是基礎RuntimeException)

  • 定義異常類的時候須要提供兩個構造器,一個無參構造器,一個帶一個字符串參數的構造器,這串字符串其實是getMessage() 時返回的異常對象的詳細描述信息。

    package com.shxt.demo01;
    public class MyException extends RuntimeException {
        public MyException() {
            super();
        }
        public MyException(String message) {
            super(message);
        }
    }
    複製代碼

    catch中throw(拋出)異常:有時候咱們在本方法中捕捉了異常,咱們只能處理異常的一部分,咱們還須要別的方法來處理或者咱們想把產生了異常的這個信息告訴調用者,這個時候咱們一般捕捉了異常後會在catch塊中拋出咱們想拋出的異常 在企業級應用中,一般對異常處理分爲兩部分:應用後臺打印或者經過日誌記錄異常發生時詳細狀況(異常跟蹤棧)和向使用者傳達某種提示。(這種思想我常用)

7.異常使用的注意事項

  • 異常捕獲後不作任何處理,就是耍流氓,挖坑埋本身
  • 異常機制不要用來作流程或條件控制,由於處理效率較低(這句話不太贊同)
  • try-catch若不觸發catch是不影響性能的,可是try塊仍然不要濫用包裹大量代碼
  • 方法出錯該拋異常就拋異常,而不是返回一些錯誤碼

下面的規則來源網絡

http://www.importnew.com/27964.html

  • 在 Finally 清理資源或者使用 Try-With-Resource 特性
  • 優先明確異常
  • 記錄指定的異常
  • 使用描述性消息拋出異常
  • 優先捕獲最具體的異常
  • 不要捕獲 Throwable 類
  • 不要忽略異常
相關文章
相關標籤/搜索