You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java异常处理中finally关键字的使用疑问

Java异常处理中finally关键字的使用疑问

嘿,这个问题问得特别戳点!很多刚摸Java异常处理的同学都会有这个疑惑——既然catch之后的代码照样能跑,那finally岂不是多此一举?其实不然,咱们得看try或者catch块里有没有提前打断程序正常流程的操作,这时候finally的“兜底”作用就立马显现出来了。

先看你给的两个代码块,在当前这个场景下,确实看起来效果完全一样——不管文件能不能打开,那行打印语句都会执行。但这是因为你的try和catch块里都没有任何会让流程“跳走”的操作,比如return、抛出新异常、break之类的。一旦有这些情况,普通代码和finally代码的区别就天差地别了。

给你举几个必须用finally的场景:

  • 场景1:try/catch块里有return语句
    比如咱们写个带return的方法:

    public static int testReturn() {
        try {
            System.out.println("执行try块代码");
            return 1; // 这里直接return,后面的普通代码根本到不了
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("catch之后的普通代码");
        return 0;
    }
    

    调用这个方法,只会打印“执行try块代码”,然后直接return 1,后面的普通打印代码完全不会执行。但如果把打印语句移到finally里:

    public static int testReturn() {
        try {
            System.out.println("执行try块代码");
            return 1;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally里的代码");
        }
        return 0;
    }
    

    这时候调用方法,会先打印“执行try块代码”,接着执行finally里的打印,最后才return 1。哪怕try里直接return,finally的代码也一定会在return前执行。

  • 场景2:catch块里抛出了新的异常
    再比如catch块里处理完原异常后,又抛出了新的异常:

    public static void testNewException() {
        try {
            new FileReader("file.txt");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("触发新异常"); // 这里抛新异常,流程直接跳走
        }
        System.out.println("catch之后的普通代码"); // 这行永远不会被执行
    }
    

    但如果把打印语句放finally里:

    public static void testNewException() {
        try {
            new FileReader("file.txt");
        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException("触发新异常");
        } finally {
            System.out.println("finally里的代码"); // 这行依然会稳稳执行
        }
    }
    

    哪怕catch里抛出了新异常,finally的代码还是会被执行,不会被跳过。

  • 场景3:循环里的try/catch有break/continue
    要是在循环里的try块里用了break或者continue:

    for (int i = 0; i < 3; i++) {
        try {
            System.out.println("循环第" + i + "次");
            if (i == 1) {
                break; // 这里直接跳出循环
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        System.out.println("循环后的普通代码");
    }
    

    当i=1的时候,break会直接跳出循环,后面的普通打印代码根本不会执行。但如果把打印放finally里:

    for (int i = 0; i < 3; i++) {
        try {
            System.out.println("循环第" + i + "次");
            if (i == 1) {
                break;
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("finally里的代码");
        }
    }
    

    哪怕触发了break跳出循环,finally里的代码还是会完整执行一次。

而finally最常用的真实场景,其实是资源释放——比如打开的文件流、数据库连接、网络连接这些,不管操作成功还是失败,有没有异常,都必须手动关闭,否则会造成资源泄漏。这时候把close()方法放在finally里,就能确保它一定会被执行:

FileReader f = null;
try {
    f = new FileReader("file.txt");
    // 读取文件的业务操作
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (f != null) {
        try {
            f.close(); // 不管流程怎么跳,这里一定会执行,保证流被关闭
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

要是把close()放在catch之后的普通代码里,万一try里有return或者抛出了其他未捕获的异常,那close()就永远不会被执行,这个文件流就会一直占用系统资源。

总结一下:finally的核心使命就是保证某段代码一定会被执行,不管try/catch块里有没有出现return、抛出异常、break/continue这些打断正常流程的操作。而普通代码只有在程序流程正常走到它那里的时候,才会被执行。

备注:内容来源于stack exchange,提问作者Cường

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.14 09:00:31