Java 中的異常是健壯程式設計的重要組成部分。事實上,它們允許以有組織且可預測的方式管理錯誤。本文深入探討了 Java 中的異常處理系統,提供了最佳實踐,並包含實際範例來說明所涵蓋的概念。
在 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 } }
Java 將異常分為三大類:
這些異常必須在方法簽名中用 throws 捕獲或聲明。它們通常來自外部事件,例如文件存取或網路連接。
未檢查異常繼承自RuntimeException,不需要捕獲或宣告。它們通常是由程式錯誤引起的。
Java 中的錯誤是嚴重的,通常與系統相關的問題,程式通常無法處理。
這些異常涵蓋了Java中各種可能出現的錯誤,為處理應用程式內的各種錯誤場景提供了全面的基礎。
在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,從而指示邏輯錯誤。
有時,傳播異常比立即捕獲異常更好,特別是在目前方法無法正確處理異常的情況下。這是透過方法聲明中的 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 區塊。
這種傳播在更複雜的體系結構中非常有用,在這些體系結構中,需要在調用層次結構中的更高層處理異常,例如,在表示層向用戶顯示錯誤訊息
。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 和自訂異常實現細粒度的錯誤控制、更好的程式碼可讀性以及專業應用程式中更好的使用者體驗。
原則:異常訊息必須明確,以提供有關錯誤的精確上下文。
不良做法:
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."); } } }
異常不應覆蓋像 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); } }
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); } }
避免捕捉 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 } }
異常日誌記錄使追蹤和解決問題變得更加容易。使用 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."); } } } } }
異常處理在非同步環境中更加複雜,例如 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."); } } }
要在保留初始上下文的同時重新拋出異常,請使用 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() 檢索以便更容易追蹤。
單元測試確保異常被正確拋出和處理。使用 JUnit,我們可以檢查方法是否拋出預期的例外。
範例:
public class SoldeInsuffisantException extends Exception { public SoldeInsuffisantException(String message) { super(message); } }
在這個範例中,assertThrows 檢查除以零時是否會拋出 ArithmeticException。
透過遵循這些異常處理的最佳實踐,您可以讓您的 Java 程式碼更加健壯和可維護。良好的錯誤處理不僅保證了應用程式的穩定性,還提高了錯誤的可追溯性,從而方便調試和持續改進。
總之,Java 中嚴格的異常處理增強了程式碼的可靠性和可維護性。透過採用最佳實踐(例如明確的錯誤訊息、明智地使用 try-with-resources 以及專注於異常的可讀性和可測試性),我們可以避免不必要的中斷並保證更穩定的使用者體驗。這使得有效地檢測、理解和糾正錯誤成為可能,同時提供強大的程式碼庫以供進化。
以上是Java 中的異常處理:完整指南的詳細內容。更多資訊請關注PHP中文網其他相關文章!