首页  >  文章  >  Java  >  Java 中的异常处理:完整指南

Java 中的异常处理:完整指南

Susan Sarandon
Susan Sarandon原创
2024-11-06 07:17:02880浏览

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