CodeGym /课程 /JAVA 25 SELF /finally 和 throw:收尾与抛出异常

finally 和 throw:收尾与抛出异常

JAVA 25 SELF
第 11 级 , 课程 3
可用

1. 认识 finally 块

当你处理资源——文件、网络连接、数据库——时,必须确保它们总是关闭或释放,即使在执行过程中发生了错误。为此,Java 提供了一个专门的代码块——finally

finally 如何工作?

finally 块是 try-catch-finally 结构的一部分。finally 中的代码总会执行(如果编写了该块)——无论是否发生异常。即使在 try 中有 return 或抛出了异常——finally 仍然会执行(除非计算机被关闭,或者通过 System.exit(0) 强制终止了程序)。

语法:

try {
    // 可能抛出异常的代码
} catch (ExceptionType e) {
    // 错误处理
} finally {
    // 这段代码总会执行!
}

示例

try {
    System.out.println("开始运行");
    int result = 10 / 0; // 这里会发生错误
    System.out.println("结果:" + result);
} catch (ArithmeticException e) {
    System.out.println("错误:被零除");
} finally {
    System.out.println("无论如何这段代码都会执行");
}

运行结果:

开始运行
错误:被零除
无论如何这段代码都会执行

发生了什么?

  1. try 块中,我们尝试将两个数相除并发生错误。
  2. 如果在除法时发生错误——catch 会处理它。
  3. 但是! 无论如何 finally 都会执行,并向控制台打印信息。

2. 没有 catch 的 finally

构造有 3 种变体:

  • 完整形式:try-catch-finally
  • 没有 finallytry-catch
  • 没有 catchtry-finally

第三种变体用于由上层方法来捕获并处理错误的场景。但需要 finally 块来保证执行特定的代码:

  • 关闭文件、网络连接、数据库。
  • 释放任何资源(例如锁)。
  • 日志记录:写入操作完成的信息。

示例:

try {
    System.out.println("进行除法");
    int result = 10 / 0;   // 错误!
    System.out.println("结果:" + result);
} finally {
    System.out.println("finally 块已执行");
}

结果:

进行除法
finally 块已执行
Exception in thread "main" java.lang.ArithmeticException: / by zero

finally 何时不会执行?

几乎总会执行。例外情况包括:

  1. 使用 System.exit(0) 强制结束程序。
  2. 执行 finally 的线程被强制“杀死”。
  3. 计算机被关闭。

3. throw 语句:如何手动抛出异常

有时 Java 会自行“抛出”异常(例如,被零除、数组越界)。但也有一些情况需要由明确指出:“这是错误!我无法继续执行!”。为此,Java 提供了 throw 语句。

类比:如果你在商店看到过期商品——你会“提出”投诉。代码中也是如此:如果不对劲——就抛出异常。

throw 的语法

throw new ExceptionType("错误信息");

ExceptionType —— 任何继承自 Throwable 的类(通常是 ExceptionRuntimeException)。括号里的消息有助于理解哪里出了问题。

示例:方法参数校验

public static int safeDivide(int a, int b) {
    if (b == 0) {
        throw new IllegalArgumentException("除数不能为零");
    }
    return a / b;
}

用法:

public static void main(String[] args) {
    try {
        int result = safeDivide(10, 0);
        System.out.println("结果:" + result);
    } catch (IllegalArgumentException e) {
        System.out.println("错误:" + e.getMessage());
    }
}

结果:

错误:除数不能为零

何时使用 throw?

  • 方法参数校验(例如传入了 null 或无效数据)。
  • 检查对象状态(例如试图从余额为 0 欧元的账户取钱)。
  • catch 中——如果想把异常“向上抛”(例如添加更多信息)。

4. 组合使用 try-catch-finally 与 throw

有时这些结构会一起配合使用。例如,你捕获到一个错误,但随后决定抛出一个更具信息量的新异常。

public static int parseAndDivide(String text, int divisor) {
    try {
        int number = Integer.parseInt(text);
        if (divisor == 0) {
            throw new IllegalArgumentException("除数不能为零");
        }
        return number / divisor;
    } catch (NumberFormatException e) {
        throw new IllegalArgumentException("字符串 '" + text + "' 不是一个数字");
    } finally {
        System.out.println("尝试处理字符串:" + text);
    }
}

用法:

try {
    int result = parseAndDivide("42a", 2);
    System.out.println("结果:" + result);
} catch (IllegalArgumentException e) {
    System.out.println("错误:" + e.getMessage());
}

结果:

尝试处理字符串:42a
错误:字符串 '42a' 不是一个数字

重要细节:return 与 finally

即使在 try 块中有 returnfinally 仍然会执行!

public static int getValue() {
    try {
        return 10;
    } finally {
        System.out.println("finally 仍然会执行!");
    }
}

调用 getValue() 将输出:

finally 仍然会执行!

5. 使用 finally 和 throw 时的常见错误

错误 №1:没有使用 finally 关闭资源。
非常常见的问题:打开了文件却未关闭——导致资源泄漏。务必使用 finally(或稍后会提到的 try-with-resources)。

错误 №2:抛出了异常却没有处理。
如果你用 throw 抛出了异常,但没有在任何地方捕获它(没有 try-catch),程序会异常终止。始终考虑由谁来捕获你的异常。

错误 №3:在 finally 中 return。
如果你不小心在 finally 中写了 return,它会“覆盖”之前的 returnthrow。这会导致非常隐蔽的 Bug。强烈不建议这样做!

public int tricky() {
    try {
        return 1;
    } finally {
        return 2; // 危险:返回的是 2,而不是 1!
    }
}

结果: 将返回 2,尽管在 try 中是 1

错误 №4:丢失异常信息。
如果你捕获了一个异常,然后抛出一个新的,但没有保留原始异常(e)的信息,你会丢失调用栈,从而增加调试难度。更好的写法是:

catch (NumberFormatException e) {
    throw new IllegalArgumentException("转换错误", e);
}
评论
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION