首頁  >  文章  >  Java  >  Java 中的異常處理:完整指南

Java 中的異常處理:完整指南

Susan Sarandon
Susan Sarandon原創
2024-11-06 07:17:02792瀏覽

Gestion des Exceptions en Java : Guide Complet

Java 中的異常是健壯程式設計的重要組成部分。事實上,它們允許以有組織且可預測的方式管理錯誤。本文深入探討了 Java 中的異常處理系統,提供了最佳實踐,並包含實際範例來說明所涵蓋的概念。

1. 異常情況介紹

在 Java 中,異常是在程式執行過程中發生的意外事件,並擾亂了正常的指令流。異常允許程式處理錯誤而不崩潰。

簡單錯誤範例:除以零:

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

2. Java中的異常類型

Java 將異常分為三大類:

2.1.檢查異常

這些異常必須在方法簽名中用 throws 捕獲或聲明。它們通常來自外部事件,例如文件存取或網路連接。

  • IOException:輸入/輸出操作失敗時發生。例如,存取不存在的檔案 (FileNotFoundException)。
  • SQLException:在資料庫操作錯誤期間發生。
  • ClassNotFoundException:當 JVM 找不到指定的類別時拋出異常。
  • InstantiationException:當嘗試實例化抽象類別或介面時。
  • InterruptedException:等待執行緒中斷時發生。
  • ParseException:解析資料時發生錯誤所引起,尤其是日期。

2.2.未經檢查的異常

未檢查異常繼承自RuntimeException,不需要捕獲或宣告。它們通常是由程式錯誤引起的。

  • NullPointerException:嘗試使用空引用。
  • ArrayIndexOutOfBoundsException:存取無效的陣列索引。
  • ArithmeticException:無效的數學運算,例如除以零。
  • IllegalArgumentException:在方法中使用不適當的參數。
  • NumberFormatException:將非數字字串轉換為數字(例如 Integer.parseInt("abc"))。
  • IllegalStateException:在物件的不適當狀態下呼叫方法。

2.3.錯誤

Java 中的錯誤是嚴重的,通常與系統相關的問題,程式通常無法處理。

  • OutOfMemoryError:當 JVM 記憶體不足時引發。
  • StackOverflowError:當過度遞歸超過堆疊大小時發生。
  • VirtualMachineError:關鍵 JVM 錯誤的父類,例如 InternalError 和 UnknownError。
  • AssertionError:斷言失敗觸發的錯誤。

其他一些已知的特定例外情況

  • StringIndexOutOfBoundsException:嘗試存取字串中的越界字元位置時。
  • ClassCastException:嘗試將物件轉換為不相容的類型。
  • UnsupportedOperationException:呼叫目前實作不支援的操作。
  • ConcurrentModificationException:迭代期間集合的修改。

這些異常涵蓋了Java中各種可能出現的錯誤,為處理應用程式內的各種錯誤場景提供了全面的基礎。

3. 處理異常

在Java中,異常處理主要基於try、catch、finally和throw區塊。以下是其用途的詳細概述:

嘗試並捕獲塊

try 區塊封裝了可以產生異常的程式碼。如果發生異常,則執行相應的catch區塊來捕獲並處理該異常,防止程式崩潰。

範例:
讓我們想像一個場景,我們想要將字串轉換為整數,但該字串可能不是有效的數字。

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

在此範例中,如果使用者輸入無法轉換為整數的字串,例如“abc”,catch 區塊會捕獲 NumberFormatException 並顯示錯誤訊息,從而避免程式中斷。

最後阻止

無論是否拋出例外,finally 區塊都會在 try 和 catch 區塊之後執行。它通常用於釋放資源(例如,關閉檔案或網路連線),以確保資源始終正確清理。

範例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            int number = Integer.parseInt("abc"); // Provoquera une NumberFormatException
        } catch (NumberFormatException e) {
            System.out.println("Erreur : la chaîne de caractères n'est pas un nombre valide.");
        }
    }
}

這裡,finally 區塊會關閉文件,無論是否找到文件,以釋放資源。

拋出關鍵字

throw 關鍵字用來明確拋出例外。這對於程式碼中的特定驗證非常有用。

範例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("fichier.txt");
            // Lire le fichier
        } catch (FileNotFoundException e) {
            System.out.println("Erreur : fichier non trouvé.");
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("Erreur lors de la fermeture du fichier.");
                }
            }
        }
    }
}

在此範例中,如果年齡為負數,則 throw 明確拋出 IllegalArgumentException,從而指示邏輯錯誤。

4. 傳播異常

有時,傳播異常比立即捕獲異常更好,特別是在目前方法無法正確處理異常的情況下。這是透過方法聲明中的 throws 關鍵字完成的。

範例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            validerAge(-5); 
        } catch (IllegalArgumentException e) {
            System.out.println("Erreur : " + e.getMessage());
        }
    }

    public static void validerAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("L'âge ne peut pas être négatif.");
        }
    }
}

這裡,openFile 使用了 throws IOException,這表示它讓呼叫者處理異常。如果 FileReader 找不到該文件,則會拋出 FileNotFoundException 並傳遞給 main 方法的 catch 區塊。

這種傳播在更複雜的體系結構中非常有用,在這些體系結構中,需要在調用層次結構中的更高層處理異常,例如,在表示層向用戶顯示錯誤訊息

5. 自訂例外

Java 可讓您透過繼承 Exception 或 RuntimeException 類別來建立自訂異常。這些異常對於報告標準異常未涵蓋的特定於應用程式的錯誤非常有用。

自訂異常範例

假設我們創建一個銀行應用程序,用戶不能提取比他們擁有的更多的錢。我們將建立一個 InsufficientBalanceException。

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class PropagationException {
    public static void main(String[] args) {
        try {
            ouvrirFichier("fichier_inexistant.txt");
        } catch (IOException e) {
            System.out.println("Erreur lors de l'ouverture du fichier : " + e.getMessage());
        }
    }

    public static void ouvrirFichier(String nomFichier) throws IOException {
        FileReader lecteur = new FileReader(nomFichier); // Peut lever FileNotFoundException
        lecteur.close();
    }
}

然後我們在主程式中使用它來處理餘額不足的情況:

public class SoldeInsuffisantException extends Exception {
    public SoldeInsuffisantException(String message) {
        super(message);
    }
}

在此範例中,InsufficientBalanceException 是一個自訂異常,表示要提取的金額超過可用餘額。處理此錯誤透過提供明確的錯誤訊息來專門提高程式碼的可讀性和可維護性。

用戶驗證的自訂異常範例

讓我們想像一個使用者驗證系統,其中年齡必須在 18 歲到 65 歲之間。我們可以建立一個 AgeInvalideException。

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

用法:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            int number = Integer.parseInt("abc"); // Provoquera une NumberFormatException
        } catch (NumberFormatException e) {
            System.out.println("Erreur : la chaîne de caractères n'est pas un nombre valide.");
        }
    }
}

這裡,如果年齡不符合條件,則會拋出AgeInvalideException。此自訂異常提供精確的驗證檢查和清晰的錯誤訊息,從而改善使用者體驗。

Java 中的異常處理透過 try、catch、finally 和自訂異常實現細粒度的錯誤控制、更好的程式碼可讀性以及專業應用程式中更好的使用者體驗。

6. 異常處理的最佳實踐

6.1 使用有意義的異常

原則:異常訊息必須明確,以提供有關錯誤的精確上下文。

不良做法:

public class GestionDesExceptions {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("fichier.txt");
            // Lire le fichier
        } catch (FileNotFoundException e) {
            System.out.println("Erreur : fichier non trouvé.");
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("Erreur lors de la fermeture du fichier.");
                }
            }
        }
    }
}

改進:
使用特定的異常和清晰的訊息,並且如果它反映了常見的、特定於網域的錯誤,則更願意建立自訂異常。

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            validerAge(-5); 
        } catch (IllegalArgumentException e) {
            System.out.println("Erreur : " + e.getMessage());
        }
    }

    public static void validerAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("L'âge ne peut pas être négatif.");
        }
    }
}

6.2 避免流量控制異常

異常不應覆蓋像 if-else 這樣的控制結構。以這種方式使用異常會降低效能並降低程式碼的可讀性。

不良做法:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class PropagationException {
    public static void main(String[] args) {
        try {
            ouvrirFichier("fichier_inexistant.txt");
        } catch (IOException e) {
            System.out.println("Erreur lors de l'ouverture du fichier : " + e.getMessage());
        }
    }

    public static void ouvrirFichier(String nomFichier) throws IOException {
        FileReader lecteur = new FileReader(nomFichier); // Peut lever FileNotFoundException
        lecteur.close();
    }
}

改進:使用普通的控制結構來處理此類邏輯。

public class SoldeInsuffisantException extends Exception {
    public SoldeInsuffisantException(String message) {
        super(message);
    }
}

6.3 對封閉資源使用 try-with-resources

try-with-resources 區塊確保即使發生異常,檔案或連線等資源也會自動關閉。

不良做法:

public class CompteBancaire {
    private double solde;

    public CompteBancaire(double solde) {
        this.solde = solde;
    }

    public void retirer(double montant) throws SoldeInsuffisantException {
        if (montant > solde) {
            throw new SoldeInsuffisantException("Solde insuffisant pour ce retrait.");
        }
        solde -= montant;
    }

    public static void main(String[] args) {
        CompteBancaire compte = new CompteBancaire(100.0);

        try {
            compte.retirer(150.0);
        } catch (SoldeInsuffisantException e) {
            System.out.println(e.getMessage());
        }
    }
}

改進:

public class AgeInvalideException extends Exception {
    public AgeInvalideException(String message) {
        super(message);
    }
}

6.4 不要捕獲一般異常

避免捕捉 Exception 或 Throwable 等一般異常,因為這可能會隱藏關鍵錯誤和意外錯誤。捕獲特定異常使程式碼更具可讀性和可維護性。

不良做法:

public class ValidationUtilisateur {
    public static void validerAge(int age) throws AgeInvalideException {
        if (age < 18 || age > 65) {
            throw new AgeInvalideException("Âge invalide : doit être entre 18 et 65 ans.");
        }
    }

    public static void main(String[] args) {
        try {
            validerAge(17); 
        } catch (AgeInvalideException e) {
            System.out.println(e.getMessage());
        }
    }
}

改進:針對特定異常進行更精確的錯誤處理。

public class DivisionParZero {
    public static void main(String[] args) {
        int a = 10;
        int b = 0;
        System.out.println(a / b);  // Lève une ArithmeticException
    }
}

6.5 異常記錄

異常日誌記錄使追蹤和解決問題變得更加容易。使用 Log4j 或 SLF4J 等日誌記錄框架來記錄錯誤,選擇適當的日誌記錄等級(錯誤、警告、資訊)。

不良做法:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            int number = Integer.parseInt("abc"); // Provoquera une NumberFormatException
        } catch (NumberFormatException e) {
            System.out.println("Erreur : la chaîne de caractères n'est pas un nombre valide.");
        }
    }
}

改進:

public class GestionDesExceptions {
    public static void main(String[] args) {
        FileReader fr = null;
        try {
            fr = new FileReader("fichier.txt");
            // Lire le fichier
        } catch (FileNotFoundException e) {
            System.out.println("Erreur : fichier non trouvé.");
        } finally {
            if (fr != null) {
                try {
                    fr.close();
                } catch (IOException e) {
                    System.out.println("Erreur lors de la fermeture du fichier.");
                }
            }
        }
    }
}

7. 先進概念

7.1 非同步環境中的異常

異常處理在非同步環境中更加複雜,例如 CompletableFuture,因為錯誤可能發生在主執行流程之外。

範例:

public class GestionDesExceptions {
    public static void main(String[] args) {
        try {
            validerAge(-5); 
        } catch (IllegalArgumentException e) {
            System.out.println("Erreur : " + e.getMessage());
        }
    }

    public static void validerAge(int age) {
        if (age < 0) {
            throw new IllegalArgumentException("L'âge ne peut pas être négatif.");
        }
    }
}

7.2 封裝異常

要在保留初始上下文的同時重新拋出異常,請使用 getCause()。這在處理應用程式較高層中的異常時特別有用。

範例:

import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;

public class PropagationException {
    public static void main(String[] args) {
        try {
            ouvrirFichier("fichier_inexistant.txt");
        } catch (IOException e) {
            System.out.println("Erreur lors de l'ouverture du fichier : " + e.getMessage());
        }
    }

    public static void ouvrirFichier(String nomFichier) throws IOException {
        FileReader lecteur = new FileReader(nomFichier); // Peut lever FileNotFoundException
        lecteur.close();
    }
}

在此範例中,e 是初始原因,可以透過 getCause() 檢索以便更容易追蹤。


8. 單元測試和異常處理

單元測試確保異常被正確拋出和處理。使用 JUnit,我們可以檢查方法是否拋出預期的例外。

範例:

public class SoldeInsuffisantException extends Exception {
    public SoldeInsuffisantException(String message) {
        super(message);
    }
}

在這個範例中,assertThrows 檢查除以零時是否會拋出 ArithmeticException。

透過遵循這些異常處理的最佳實踐,您可以讓您的 Java 程式碼更加健壯和可維護。良好的錯誤處理不僅保證了應用程式的穩定性,還提高了錯誤的可追溯性,從而方便調試和持續改進。

結論

總之,Java 中嚴格的異常處理增強了程式碼的可靠性和可維護性。透過採用最佳實踐(例如明確的錯誤訊息、明智地使用 try-with-resources 以及專注於異常的可讀性和可測試性),我們可以避免不必要的中斷並保證更穩定的使用者體驗。這使得有效地檢測、理解和糾正錯誤成為可能,同時提供強大的程式碼庫以供進化。

以上是Java 中的異常處理:完整指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述:
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn