`try-finally` 里的 `return`:为什么 `finally` 会悄悄改掉返回值、吞掉异常

Solidworks二次开发(C# .NET API):从零构建装配体与虚拟零件 本文详细介绍了使用C#进行Solidworks二次开发,从零构建装配体与虚拟零件的完整流程。通过.NET API实现设计自动化,涵盖环境搭建、核心对象模型、虚拟零件创建与外部化保存等关键技术,帮助工程师将重复性建模工作程序化,大幅提升设计效率与标准化水平。 阅读详情

前言

try-finally 大家天天写,但下面这段代码的返回值,能一眼答对的人不多:

public int getValue() {
    int x = 1;
    try {
        return x;      // 这里 return 的是 1?
    } finally {
        x = 2;         // finally 又把 x 改成了 2
    }
}

返回 1 还是 2?

更刁钻的是,如果 finally 里也写了 return,或者 finally 里抛了异常,会发生什么?这些不是脑筋急转弯,而是真实项目里踩过的坑——尤其"finally 吞掉异常",能让一个本该炸出来的错误悄无声息地消失,排查起来能让人怀疑人生。

这篇文章讲清楚 finally 和 return、异常之间那些容易被忽略的执行细节。

环境说明:本文基于 JDK 8。


一、先复现

复现 1:finally 改了变量,返回值却没变

先揭晓开头那题的答案:

public static int getValue() {
    int x = 1;
    try {
        return x;
    } finally {
        x = 2;
    }
}
// 调用:System.out.println(getValue());
1

返回的是 1,不是 2。 finally 里明明把 x 改成了 2,返回值却还是 1。很反直觉。

复现 2:finally 里加个 return,结果就变了

只把 finally 里的赋值换成 return:

public static int getValue2() {
    int x = 1;
    try {
        return x;         // 想返回 1
    } finally {
        return 2;         // finally 里也 return
    }
}
2

这次返回的是 2。try 里的 return x 好像被 finally 的 return 覆盖了。

复现 3:finally 里的 return 把异常吞了

最危险的一个:

public static int getValue3() {
    try {
        throw new RuntimeException("出错了!");   // 抛异常
    } finally {
        return -1;                               // finally 里 return
    }
}
-1

注意:程序正常返回了 -1,那个 RuntimeException 凭空消失了! 调用方完全不知道里面出过错。这就是臭名昭著的"finally 吞异常"。

三个现象,指向同一组问题:finally 到底在什么时候、以什么顺序执行?它为什么能改返回值、吞异常?


二、根因

一句话总纲:try 里的 return 并不是"立刻返回",它会先把返回值"暂存"起来,然后一定要等 finally 执行完,才真正返回。 finally 就是在这个"暂存之后、真正返回之前"的空档里插了一脚。

2.1 复现 1:返回值在 return 那一刻就被"定格"了

return x 的执行分两步:

  1. 计算并暂存返回值:把 x 当前的值(1)复制到一个临时位置(可以理解为"返回值寄存器"),这个值此刻就定格了;
  2. 执行 finally;
  3. 真正返回那个暂存的值。

关键在于:finally 里的 x = 2 改的是局部变量 x,而返回值早在第 1 步就已经被复制走、和 x 脱钩了。所以改 x 影响不到已经暂存的返回值 1。

在这里插入图片描述

小坑提醒:如果返回的是对象引用,情况有点不同。暂存的是"引用(地址)“,finally 里若通过这个引用去修改对象内部的属性,改动是生效的(因为对象是同一个);但若在 finally 里让变量指向一个新对象,则不影响已暂存的旧引用。记住:暂存的是"那一刻的值/引用”。

2.2 复现 2 和 3:finally 里的 return 会"抢占"返回

如果 finally 里自己也有 return,就完全是另一回事了:finally 的 return 会直接终止方法,用它自己的返回值覆盖掉 try 里暂存的那个,并且丢弃 try 中待处理的 return 或异常。

  • 复现 2:try 暂存了返回值 1,但 finally 执行到 return 2 时,直接带着 2 结束方法——暂存的 1 被丢弃。
  • 复现 3:try 里抛出的异常本应向上传播,但 finally 执行到 return -1,方法直接正常返回 -1——那个正在传播的异常被丢弃了,调用方永远收不到。

道理是一致的:finally 里的 return(或 throw)会"截胡",让 try 里原本要返回的值、要抛的异常统统作废。 异常被吞,就是这么发生的。

在这里插入图片描述


三、正解:finally 只做清理,别在里面 return、别在里面抛异常

这些坑的根源,都是在 finally 里做了"改返回值/中断控制流"的事。规避原则很简单:

finally 块只用来做资源清理(关流、解锁、还连接),绝不放 return、throw,也不去修改要返回的变量。

// 反例:finally 里 return,吞掉异常
public int bad() {
    try {
        return riskyCall();
    } finally {
        return -1;            // ✗ 吞掉 riskyCall 的返回值和异常
    }
}

// 正解:finally 只清理,让返回值和异常正常传播
public int good() throws Exception {
    Resource r = open();
    try {
        return r.process();   // 返回值/异常都能正常出去
    } finally {
        r.close();            // ✓ 只做清理
    }
}

更进一步,如果只是为了关资源,优先用 try-with-resources(JDK 7+)。它会自动、安全地关闭资源,代码更短,也没有手写 finally 的这些坑:

// 最推荐:try-with-resources 自动关闭,无需手写 finally
public int best() throws Exception {
    try (Resource r = open()) {
        return r.process();
    }
}

如果确实需要在清理阶段处理异常,也应该在 finally 里 try-catch 住并记录日志,而不是让它中断主流程或吞掉主异常。


四、常见误区与面试高频问答

Q:finally 一定会执行吗?

绝大多数情况会,包括 try 里有 return、break、continue、抛异常时。唯二的例外:一是执行到 System.exit() 直接终止 JVM;二是线程被强制杀死或断电这类极端情况。正常代码里,可以认为"finally 必定执行"。

Q:try 有 return、finally 也有 return,最终返回哪个?

finally 的。finally 里的 return 会覆盖 try(或 catch)里的 return,并丢弃待抛的异常。正因如此,别在 finally 里写 return。

Q:为什么 finally 改了变量,返回值没变?

因为 return x 在执行时就把 x 的值复制到返回值暂存位置了,返回的是那个副本。finally 里改 x 改的是变量本身,和已经复制出去的返回值无关。(返回对象引用时,改对象内部属性会生效,改引用指向不生效。)

Q:finally 里抛异常会怎样?

如果 try 里也抛了异常,finally 里的新异常会覆盖掉 try 里的原始异常向上抛出——原始异常(往往是更关键的那个)就丢了。所以 finally 里的代码也要保证不抛异常,或自己 try-catch 处理掉。


总结

try-finally 遇上 return 和异常,几个容易被忽略的点:

  • try 里的 return 会先把返回值暂存,再执行 finally,最后返回暂存的值——所以 finally 改局部变量,改不动已经暂存的返回值。
  • finally 里如果有 return 或 throw,会截胡:覆盖 try 的返回值、并吞掉 try 中正在传播的异常——这是异常凭空消失的元凶。
  • 正解:finally 只做清理,不写 return、不抛异常、不改返回变量;关资源优先用 try-with-resources。

一句话记忆: try 的 return 先定格返回值再走 finally;finally 里千万别 return,否则它会悄悄改掉返回值、吞掉异常。finally 只配做清理。

【GB28181】wvp-GB28181-pro++ZLMediaKit编译安装国标流媒体服务器教程(Windows平台) 本文基于ZMLMediaKit+WVP-PRO在windows平台进行编译部署GB28181平台,网上教程也比较多,都大同小异,实际操作过程中容易出错的地方也比较多:redis安装无密码,mysql安装后密码的修改以及wvp数据可的创建,配置文件侧修改,尤其是和端口相关的项,再就是运行服务时各种错误需要注意,常见的错误已经做了汇总,遇到问题大家不要怕,找到报错的地方查看定位即可。 阅读详情

相关推荐

告别GIS软件!用R语言ggplot2+ggmapcn绘制出版级世界地图(附5种投影对比图)

本文提供了一份完整的实践指南,教你如何利用R语言及其强大的ggplot2生态系统,结合ggmapcn包,绘制出出版级的世界地图。文章详细对比了五种经典地图投影(如摩尔维德、罗宾森投影)的代码实现与视觉差异,并讲解了如何添加比例尺、指北针等专业元素,最终导出高清PDF,实现从数据分析到专业可视化的全流程代码化操作,彻底告别传统GIS软件。

p5l2m9n4o6q的博客 407

Java语法:finally中修改返回结果

Java的try语句在返回前将其他所有的操作执行完,保留好要返回的值,而后转入执行finally中的语句,而后分为以下三种情况: 如果finally中有return语句,则会将try中的return语句”覆盖“掉,直接执行finally中的return语句。 如果finally中没有return语句,也没有改变要返回值,则执行完finally中的语句后接着执行try中的return语句。 如果finally中没有return语句,但是改变了要返回的值,分以下两种情况: 1)如果retu

做好自己 1500

关于影响力最大化问题中贪心算法的一些理解

在影响力最大化问题中(Influence Maximization,以下简称IM),贪心算法是最早提出的一种解决方法,并且一直沿用至今。它基于IM问题所具有的单调性(Monotonicity)和子模性(Submodularity)使用一种Hill Climbing策略,不断使用Monte Carlo模拟来寻找在当前情况下的具有最大传播范围的节点,添加至种子集合,得到最终结果,算法伪代码如下所示: 这里算法1其实并没有使用MonteCarlo模拟,而只是使用了贪心的策略...

nianjuan1990的专栏 2852

Java中finally是否改变try中return值

话不多说,直接上例子: public class test { public static void main(String[] args){ System.out.println(afresh()); // 0 } static int afresh(){ int xie=0; try{ return xie; ...

qq_45225798的博客 893

.NET 4.0下HttpWebRequest请求HTTPS报错?试试这个注册表修改方案

本文深入解析.NET 4.0下HttpWebRequest请求HTTPS报错问题,提供注册表修改方案以解决'基础连接已经关闭'错误。通过调整SchUseStrongCrypto注册表项,强制使用TLS 1.2等现代加密协议,确保安全连接。适用于企业环境部署和长期维护建议。

weixin_29310631的博客 356

COCO2017数据集类别分布深度解析

本文深入解析了COCO2017数据集的类别分布情况,包括80个常见物体的分类统计和样本数量分析。通过对比训练集与验证集的分布一致性,探讨了数据均衡性问题,并针对高频和低频类别提出了实用的数据预处理和模型训练建议,帮助优化计算机视觉任务的性能。

weixin_42533751的博客 370

finally中修改try中的return值,是否会影响return值呢?

今天面试时候遇到一道题,题目差不多就是下面的代码(这是我自己敲得,题大概就是这样)求返回值: public int summ(int m,int n) { int result = 0; try { result = n+m ; System.out.println( "try: "+result); return result; } finally { result = m-n; System...

王魂风气的博客 1335

从点击图标到Activity亮屏:AMS、Zygote、ActivityThread的Binder协作之旅

Launcher通过Context调用 startActivity(),最终会通过 Activity.startActivity() 传递到 ActivityManager.getService(),获取AMS的Binder代理对象(IActivityManager)。这里的 attach() 方法通过Binder调用AMS的 attachApplication(),将新进程的 IApplicationThread 代理对象传递给AMS。这一步的Binder通信,是整个启动流程的关键转折点,值得细细品味。

大模型大数据攻城狮的专栏 1071

finally能不能影响try的return值

finally能不能影响try的return值

李闪闪 741

Swin Transformer 与 CNN 结合实现图像分类

Swin Transformer 与 CNN 结合实现图像分类。(含swin transformer 结构讲解,cnn网络搭建,代码)

这里是Mae。 5026

物联网Node-Red开发教程-第6章 HTTP与Web服务

本章摘要(147字): 本章系统讲解了Node-RED中的HTTP节点应用,重点包括: 三大核心HTTP节点功能:http in(接收请求)、http response(返回响应)、http request(调用外部API) 详细演示了REST API开发流程,包括路径参数(req.params)处理和响应构造 提供了创建Hello World API、传感器数据接口等实用示例 涵盖HTTP方法特性、状态码设置及响应头配置等关键知识点 通过流程图和JSON配置直观展示节点连接方式,帮助快速掌握Web服务开发

shihui2003的博客 264

finally中修改try中的return值

这两天在看异常处理,书上的看不动所以找了一篇博客。(https://blog.youkuaiyun.com/yongyuai/article/details/79752608) 虽然很长但还是看完了。最后发现了一个问题(其实是看到评论里说了才发现的)。 无论try中的代码是否会抛出异常,也就是无论catch代码块中的代码是否会被执行,最后都会执行finally中的代码。并且如果在try/catch中发现了re...

qq_43230540的博客 1643

迈瑞BS系列全自动生化分析仪LIS协议接口手册(内含HL7和ASTM)

迈瑞BS系列全自动生化分析仪LIS协议接口手册,内含HL7和ASTM)

异常处理和异常表

一捕获异常 1代码 public void tryCatch() { try { File file = new File("d:/hello.txt"); FileInputStream fis = new FileInputStream(file); String info = "hello!"; } catch (FileNotFoundException e) { e.printStackTrace(); ..

实践求真知 332

IMSL CNL 7.0.0 (x86-64)Crack

IMSL CNL 7.0.0 (x86-64)Crack intel的算法库破解文件,这是C++版本的破解文件

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

JAVA中的异常详解

throws必须跟在方法的参数列表之后,调用声明抛出异常的方法时,调用者必须对该异常进行处理,或者继续使用。1.1在程序执行中遇到的不正常的行为称为异常,比如空指针异常,栈溢出异常,算数异常。抛出异常需要写在方法体内部,告知用户有异常,如果不处理则无法通过编译。如果try中有return,finall语句最后依然被执行。1.2为了把异常分类,有了如下的异常体系结构。2.2.2try-catch捕获并处理。2.2捕获异常(异常具体处理方式)

YJMMM_的博客 79

visio安装包

Microsoft Office Visio 是一款专业的制图软件。利用经过更新的形状、协作工具和数据链接图表,创建专业级图表以简化复杂的信息。

python中容易被忽视的三个点

异常处理中的return def some_func(): try: return 'from_try' finally: return 'from_finally' >>> some_func() 'from_finally' 当在 “try…finally” 语句的 try 中执行 return, break 或 continue 后, finally 子句依然会执行. 函数的返回值由最后执行的 return 语句决定. 由于 .

Neo 8028

java面试题operate()_java面试题

1、int j = 0;j = j++; 问j现在等于多少?2、一道邪恶的Java基础题——来自JavaEye的网友Java代码String a = "abc";String b = "abc";System.out.println("====>"+ a==b );请问输出结果是true还是false?3、try中包含return语句,finally是否还执行,在return之前还是之后?J...

weixin_42468321的博客 1566

分享3 个Python冷知识

转自/Python编程时光三个比较冷门的知识。第一个:神奇的字典键some_dict={} some_dict[5.5]="Ruby" some_dict[5.0]...

小詹学python的博客 108

finally块中包含return语句对返回值的影响

要想弄清楚这个问题,最好是从字节码指令的层次进行分析,我们以如下代码段进行举例:public int test() { int i=0; try { i = 1; return i; } finally { i = 2; return i;

Just Do IT 5161

异常中 finally中不能给变量赋新值来改变return的返回值

public class TestException1 { public static void main(String[] args) {   System.out.println(except()); } public static int except() {   int s = 0;   try {    System.out.println(2 / 0);

CYHJRX的专栏 1423

try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?如果return返回的变量在finally中被改变了返回值会改变吗?

目录try-catch-fina分析 try-catch-fina try-catch-finally 中,如果 catch 中 return 了,finally 还会执行吗?如果return返回的变量在finally中被改变了返回值会改变吗? @Override package Java; import java.util.Scanner; public class Jaa { public static void main(String[] args) { i

m0_45046813的博客 1451

Spring AI + GPT-5.5 + Ollama 配置教程:Base URL、超时与流式调用

由于不同 Spring AI、Spring Boot 小版本之间仍可能存在差异,创建项目时应检查 Spring Initializr、Spring AI BOM 和官方兼容矩阵,不要直接复制旧教程中的 Starter 名称和版本号。多人项目最好隔离不同客户端,并在脱敏前提下记录最终 URL、状态码、请求耗时、请求 ID 和模型 ID。Spring AI 接入模型 API,至少要同时确认 Starter、Base URL、请求路径、API Key、模型 ID、连接超时、读取超时和流式客户端这 8 项。

2601_95893075的博客 228

InnoDB 内存结构总览:Change Buffer、AHI 与 Log Buffer

本文系统梳理InnoDB内存结构中的ChangeBuffer、Adaptive Hash Index(AHI)与LogBuffer。ChangeBuffer缓存二级索引页不在内存时的变更,减少随机IO,仅在页加载时合并;聚簇索引修改立即执行,因数据本身不可延后。AHI基于高频等值查询自动构建哈希索引,加速定位,仅支持等值操作。LogBuffer为redo log内存缓冲,配合磁盘日志实现崩溃恢复。三者均服务于性能优化,其中ChangeBuffer与AHI共享BufferPool空间,而LogBuffer独立

bksczm的博客 104

V8 机制、手写核心函数与 TypeScript 类型体操

或固定初始化(如数组先定长、对象先给全字段)保持类型与形状稳定;这些都比"换框架"更基础。只能处理纯数据(丢函数、Date、RegExp、循环引用报错)。生产级深拷贝要处理:原始值短路、循环引用、V8(Chrome / Node 的 JS 引擎)不是"解释一行跑一行"那么简单,它用。图:V8 执行流程——解释执行 + 热点优化,优化失败会 Deopt 回退。JS 内存自动管理,但"自动"不等于"不会漏"。是"固定间隔最多执行一次"。手写 Promise 是"懂异步"的试金石。是"停下后才执行最后一次",

qq_38468358的博客 334

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

Java EE 进阶:8.MyBatis 操作数据库(入门)(第一弹)

要努力去发光,而不是被照亮~ 124

Spring Boot 数据源与 MyBatis(-Plus) 自定义配置详解

本文详解 Spring Boot 中单数据源环境下 HikariCP 与 MyBatis-Plus 的自定义配置。通过手动声明 SqlSessionFactory,实现下划线转驼峰、日志输出、一级缓存策略等精细化控制。结合 HikariCP 核心参数调优(如最大连接数、连接生命周期),确保高性能与稳定性。适用于仅需访问单一数据库的场景,避免多数据源复杂性,兼顾性能与可维护性。

BADAO_LIUMANG_QIZHI的博客 196

LeetCode Hot 100 二叉树专题(二)3 题(Java)

本篇文章共 3 题:543. 二叉树的直径、102. 二叉树的层序遍历、108. 将有序数组转换为二叉搜索树。 每道题都统一按「① 题目分析 & 解题思路 → ② 常见解法(附代码)及讲解 → ③ 知识点总结」的顺序组织;代码使用 Java 编写,节点定义沿用 LeetCode 默认的 TreeNode。

2401_88612756的博客 365
上一篇: DCL 单例为何要 `volatile`:一次半初始化对象引发的血案
下一篇: 时区的坑:为什么存进数据库的时间,差了 8 小时
Leighteen
博客等级 码龄5年 422粉丝 55原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Leighteen

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值