首頁 > 軟體

Java 超詳細講解異常的處理

2022-04-02 13:01:52

1、異常的概念和體系結構

1.1異常的概念

Java中,在程式執行過程中發生的不正常行為稱為異常。比如之前一直遇到的:

(1)算數異常

System.out.prinntln(10/0);

(2)陣列越界異常

int[] arr={1,2,3};
System.out.println(arr[5]);

(3)空指標異常

int[] arr=null;
System.out.println(arr.length());

1.2異常的體系結構及分類

  • Throwable:是異常體系的頂尖層,派生出兩個鍾要的子類:而Error、Exception
  • Error:指的是Java虛擬機器器無法解決的嚴重問題
  • Exception:就是我們平常所說的異常。程式設計師可以以通過程式碼進行處理,使程式繼續執行。

【異常的分類】

  • 執行時異常(受查異常):RuntimeException下的所有異常為執行時異常
  • 編譯時異常(非受查異常):IOException、ClassNotFoundException、CloneNotSupportedException為編譯時異常

【注】編譯時出現的語法錯誤,不能稱之為異常。執行時指的是程式已經編譯通過得到class檔案了,再由JVM執行過程中出現的錯誤。

2、異常的處理

在Java中,例外處理主要的五個關鍵字:throw、try、catch、final、throws

2.1防禦式程式設計

(1)LBYL:look before you leap,在操作之前就做充足的檢查(事前防禦型)

boolean ret=false;
ret=登陸游戲();
if(!ret){
    處理登陸游戲錯誤;
    return;
}
ret=開始匹配();
if(!ret){
    處理匹配錯誤;
    return;
}
………………

缺陷:正常流程和錯誤處理程式碼混在一起,程式碼整體顯得比較混亂。

(2)EAFP:it is easier to ask forgiveness than permission,先操作,遇到問題再解決。(事後認錯型)

try{
   登陸游戲();
   開始匹配();
}catch(登陸游戲異常){
    處理登陸游戲異常;
}catch(開始匹配異常){
    處理匹配異常;
}

2.2異常地丟擲

在Java中,可以藉助throw關鍵字,丟擲自定義異常,將錯誤資訊告知給呼叫者。語法如下:

throw  new  XXXException("異常產生的原因");

【例】實現一個方法,獲取陣列中任意下標位置的元素

public static int getElement(int[] array, int index){
        if(null == array){
            throw new NullPointerException("傳遞的陣列為null");
        }
        if(index < 0 || index >= array.length){
            throw new ArrayIndexOutOfBoundsException("傳遞的陣列下標越界");
        }
        return array[index];
    }

【注】

  • throw必須寫在方法的內部
  • 丟擲的物件必須是Exception或Exception的子類物件
  • 如果丟擲的是編譯時異常,使用者必須處理,否則無法通過編譯
  • 如果丟擲的是RuntimeException或其子類,可以不用處理,直接交給JVM來處理
  • 異常一旦丟擲,後面的程式碼不會再執行

2.3異常的捕獲

異常的具體處理方式,主要有兩種:異常宣告throws和try-catch捕獲處理

printStackTrace()列印異常

(1)異常宣告throws

當前方法不處理異常,提醒方法的呼叫者處理異常。

語法格式:

修飾符 返回值型別 方法名(參數列) throws 異常型別1,異常型別2...{

【例】載入指定的組態檔

public class Config {
        File file;
        /*
        FileNotFoundException : 編譯時異常,表明檔案不存在
        此處不處理,也沒有能力處理,應該將錯誤資訊報告給呼叫者,讓呼叫者檢查檔案名字是否給錯誤了
        */
        public void OpenConfig(String filename) throws FileNotFoundException {
            if(filename.equals("config.ini")){
                throw new FileNotFoundException("組態檔名字不對");
            }
        // 開啟檔案
        }

【注】

  • throws必須跟在參數列的後面。
  • 聲名的異常必須是Exception或Exception的子類。
  • 方法的內部如果丟擲多個異常,throws後面必須跟多個異常型別,用逗號隔開。如果丟擲的異常之間有父子關係,直接宣告父類別異常即可。
  • 呼叫宣告丟擲異常的方法時,呼叫者必須對異常進行處理,或者繼續使用throws。

(2)try-catch捕獲並處理

throws並沒有對異常真正處理,而是將異常報告給異常方法的呼叫者。如果真正要對異常進行處理,需要try-catch。

【語法如下】

try{
    // 將可能出現異常的程式碼放在這裡
}catch(要捕獲的異常型別 e){
    // 如果try中的程式碼丟擲異常了,此處catch捕獲時異常型別與try中丟擲的異常型別一致時,或者是try中丟擲異常的父類別時,就會被捕獲到
    // 對異常就可以正常處理,處理完成後,跳出try-catch結構,繼續執行後序程式碼
}【catch(異常型別 e){
    // 對異常進行處理
}finally{
    // 此處程式碼一定會被執行到
}】
// 後序程式碼
// 當異常被捕獲到時,異常就被處理了,這裡的後序程式碼一定會執行
// 如果捕獲了,由於捕獲時型別不對,那就沒有捕獲到,這裡的程式碼就不會被執行

注意: 
1. 【】中表示可選項,可以新增,也可以不用新增 
2. try中的程式碼可能會丟擲異常,也可能不會

【注意】

  • try塊中丟擲異常位置後的程式碼不會被執行
  • 如果丟擲異常型別與catch型別不匹配,即異常不會被成功捕獲,則需要JVM來處理異常------異常是按照型別來捕獲的。
  • rey中可能會丟擲多個不同的異常物件,必須用多個catch來捕獲。
  • 如果異常之間有父子類關係,必須子類異常在前catch,父類別異常在後catch,否則語法錯誤。
  • 可以通過一個catch來捕獲多個異常(不推薦)。

由於Exception類是所有異常的子類,因此可以用這個型別來捕捉所有異常。catch進行型別匹配時,不光會匹配相同型別的異常物件,也會捕捉目標異常型別的子類物件。

(3)finally

在寫程式時,有些特定的程式碼,無論程式是否發生異常,都需要執行,比如程式正常開啟的資源,有時候必須對資源進行回收。另外,異常會引發程式的跳轉,可能導致有些語句執行不到,此時需要finally來解決這個問題。

【語法格式】

try{
    // 可能會發生異常的程式碼
}catch(異常型別 e){
    // 對捕獲到的異常進行處理
}finally{
    // 此處的語句無論是否發生異常,都會被執行到
}
// 如果沒有丟擲異常,或者異常被捕獲處理了,這裡的程式碼也會執行

【問題】既然finally和try-catch-finally後的程式碼都會被執行,那為什麼還要有finally呢?

當catch沒有捕獲到異常時,此時需要JVM來捕獲異常,程式可能不能正常執行,finally後面的程式碼就不會被執行。而finally中的程式碼一定會被執行。

【注】finally中的程式碼一定會被執行,一般在其中進行資源清理的掃尾工作。

如下程式碼:

public static int func(){
   try{
      return 10;
   }finally{
      return 20;
   }
}
//此時返回20;

finally執行的時機是方法返回之前(try或者catch中如果有return會在return之前執行finally)。但是如果finally中也存在return語句,那麼就會執行finally中的return,從而不會執行到try中原有的return。

2.4異常的處理流程

關於“呼叫棧”:

方法之間存在相互呼叫的關係,可以用“呼叫棧”來描述。在JVM中有一塊記憶體空間稱之為:“虛擬機器器棧”專門儲存方法之間的呼叫關係。當程式碼中出現異常的時候,我們就可以使用e.printStackTrace();的方法檢視出現異常程式碼的呼叫棧。

如果本方法中沒有合適的處理異常的方法,就會沿著呼叫棧向上傳遞,如果一直向上傳遞都沒有找到合適的方法,最終會交給JVM來處理,程式就會異常終止。

【程式例外處理的流程】

  • 程式先執行try中的程式碼
  • 如果try中的程式碼出現異常,就會結束try,在catch中進行異常匹配
  • 如果找到匹配的異常型別,就會執行catch中的程式碼
  • 如果沒有找到,就會將異常向上傳遞到上層呼叫者
  • 無論是否找到匹配的異常型別,finally中的程式碼都會執行(在該方法結束之前執行)
  • 如果上層呼叫者也不能處理異常,就繼續向上傳遞
  • 一直到main方法也沒有合適的程式碼處理異常,就會交給JVM來處理,此時程式異常終止。

3、自定義異常類

具體方法:

  • 自定義異常類,然後繼承自Exception或者RunTimeException。
  • 實現一個帶有String型別引數的構造方法。
class UserNameException extends Exception {
    public UserNameException(String message) {
        super(message);
    }
}

【注】

  • 自定義異常通常會繼承自Exception或者RunTimeException。
  • 繼承自Exception的異常預設為受查異常。
  • 繼承自RunTimeException的異常預設為非受查異常。

到此這篇關於Java 超詳細講解異常的處理的文章就介紹到這了,更多相關Java 異常內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com