1. 程式人生 > 實用技巧 >Java程式設計師必備:異常的十個關鍵知識點

Java程式設計師必備:異常的十個關鍵知識點

前言

總結了Java異常十個關鍵知識點,面試或者工作中都有用哦,加油。

一. 異常是什麼

異常是指阻止當前方法或作用域繼續執行的問題。比如你讀取的檔案不存在,陣列越界,進行除法時,除數為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關鍵字表明,該方法不處理異常,而是把異常留給它的呼叫者處理。是不是覺得TA不負責任?

哈哈,看一下demo吧

//該方法通過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 this
method in order to produce alocale-specific message. For subclasses that do not
override thismethod, the default implementation returns the same result
as 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 for
this object.Remaining lines represent data previously recorded by the 
method 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: 100
throwing 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);
    }
複製程式碼

執行結果:

我們可以看到異常資訊有儲存下來的,如果把cause(也就是FileNotFoundException 的e)去掉呢,看一下執行結果:

可以發現,少了Throwable 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

字串轉換為數字異常

版權所屬:Jay_huaxiao

java交流學習資源大全檔案下載地址:https://jq.qq.com/?_wv=1027&k=jx7ERZrp學習手冊,面試題,開發工具,PDF文件書籍教程,練手專案+原始碼

Java自學資料學習交流群:926452303

聲援博主:您的肯定就是我進步的動力。如果你感覺還不錯,就請鼓勵一下吧!記得隨手點波推薦不要忘記哦!!!

別忘了點推薦留下您來過的痕跡

作者:Jay_huaxiao
連結:https://juejin.im/post/5dc68c0f51882528957fadb3
來源:掘金