Java异常处理机制

157 阅读3分钟

异常机制和分类

  • 什么是异常?
    • 异常(Exception)是指再程序运行期间可能会发生的错误或意外情况,这些情况可以干扰程序的正常执行流程,导致程序出现异常或崩溃。

Design for failure

通过使用异常处理机制,程序可以更好地应对错误和意外情况,提高程序的健壮性和可靠性

  • 关于异常的 What/Where/Why

    • 异常类型回答了 “什么” 被抛出
    • 异常堆栈跟踪回答了 “在哪” 抛出
    • 异常信息回答了 “为什么” 会抛出
  • 异常分类

    • 按继承结构
      • Error 与 Exception
    • 按编译器检查角度
      • CheckedException 与 UnCheckedException

image.png

如何进行异常处理

  • 异常关键字

    • try:把可能出现异常代码放在try语句块之内,当try语句块内发生异常时,异常就被抛出
    • catch:用于捕获异常。catch用于捕获try语句块中发生的异常
    • finally:用于try中打开的资源回收,常常用try-catch-finally
    • throw:用于抛出异常
    • throws:用在方法签名中,用于声明该方法可能抛出的异常
  • 3种异常的处理机制

    • 声明受检异常
    • 抛出运行时异常
    • 捕获异常
  • 如何自定义异常

    • 自定义非受检异常:继承RuntimeException
    • 自定义受检异常:继承Exception
    • 自定义异常最佳实践分析:AWS Java SDK异常设计案例

异常处理的最佳实践

  • 不要忽略捕捉的异常
public static void method2(String filePath) {
    File file = new File(filePath);
    String line;
    try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
        while ((line = reader.readLine()) != null) {
            System.out.println("Line: " + line);
        }
    } catch (IOException e) { // 错误示范:什么都不做,没有任何意义

    }
}
  • 捕获具体的异常而不是 Exception
public static void method1(String filePath) {
    File file = new File(filePath);
    String line;
    try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
        while ((line = reader.readLine()) != null) {
            System.out.println("Line: " + line);
        }
    } catch (Exception e) { // 错误示范:不要写Exception,没有任何意义
        System.out.println("File Not Found!!!");
        e.printStackTrace();
        // throw new RuntimeException(e);
    }
}
  • 只捕获实际可处理的异常(不会发生的不处理)
public static void method3(String filePath) {
    File file = new File(filePath);
    String line;
    try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
        while ((line = reader.readLine()) != null) {
            System.out.println("Line: " + line);
        }
    } catch (NoSuchMethodException e) { // 错误示范:Exception 'java.lang.NoSuchMethodException' is never thrown in the corresponding try block
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();  // 生产环境不要用 printStackTrace()
    }
}
  • 生产环境不要用 printStackTrace()
  • 在方法签名上要声明具体受检异常并在javadoc中体现
/**
 * Do something according to complete flag
 * @param complete flag indicated whether complete or not
 * @throws IOException
 * @throws SQLException
 */
public static void doSomething(boolean complete) throws IOException, SQLException {
    System.out.println("Doing something");

    if (!complete) {
        // 抛出一个异常
        // throw new RuntimeException("The flag complete is false");
        throw new IOException("The flag complete is false");
    }

    System.out.println("Completed!");
    throw new SQLException("SQL Exception!!!");
}
  • finallytry-with-resource关闭或释放资源
  • 先捕获子类异常,再捕获父类异常
public static String readFileV2(String filePath) {
    File file = new File(filePath);
    String line;
    try (BufferedReader reader = new BufferedReader(new FileReader(file))) {
        while ((line = reader.readLine()) != null) {
            System.out.println("Line: " + line);
        }
        return "OK";
    } catch (FileNotFoundException e) { // 把子类,范围小的放在前面catch,进行处理
        System.out.println("File Not Found!!!");
        throw new RuntimeException(e);
    } catch (IOException e) {
        throw new RuntimeException(e);
    }
}
  • Throw early, catch late.(late 不一定是越晚越好)
  • 捕获异常不要捕获 Throwable
  • 封装异常再抛出时不要忘记原来的 cause
public static void testUncheckedException() {
    IndexOutOfBoundsException exception = new IndexOutOfBoundsException("Index is out of bounds");
    throw new CustomizeUncheckedException("Unchecked Exception met!", exception);   // 不要忘记原始的cause(exception)
}
  • 能尽量使用JDK标准异常就不要自定义
  • 不要同时抛出异常和log异常