Java 异常处理中篇:finally 中的陷阱(finally 中 return 会发生什么)

文章目录

  • 前言
  • 版本
  • `finally` 中的陷阱
    • finally 中使用 return
    • `finally` 中修改数据的影响
      • 基本类型
      • 引用类型
    • `finally` 中的代码 “非最后” 执行
    • `finally` 代码块一定会执行?
    • 异常丢失
    • finally 底层原理分析
  • 总结
  • 个人简介

前言

  • 在上一篇文章中,我们介绍了 Java 异常的基本概念,Throwable 、异常处理关键字:try-catch-finally、throw、throws;本篇文章我们将更加深入的了解 finally 在异常处理中的常见问题和底层原理。

版本

  • Java 8

finally 中的陷阱

  • 我们知道无论是否发生异常还是 try 或 catch 中存在 returnfinally 都会执行,下面我们来看看下面几种场景:

finally 中使用 return

  • 当我们在 finally 中使用 return 时,trycatch 中的 return 会失效或异常丢失(见下文),会在 finally 直接返回。
public class Main {public static void main(String[] args) {System.out.println(extracted());}private static int extracted() {int a = 1;try {a = 2;a = a / 0;return a;} catch (Exception e) {System.out.println(e);return a;} finally {System.out.println("this is finally");return -1;}}
}// 输出 finally 中直接 return -1 
java.lang.ArithmeticException: / by zero
this is finally
-1 

finally 中修改数据的影响

  • 如果你在 finally 代码块中修改了数据,你可能会有一些奇妙的体验。

基本类型

public class Main {public static void main(String[] args) {System.out.println(extracted());}private static int extracted() {int a = 1;try {a = 2;return a;} finally {System.out.println("this is finally");a += 3;}}
}// 输出
this is finally
2
  • 我们可以得出结论在 finally 中修改基本类型不会影响 try 、catchreturn 中的返回值(但是会影响 finally 中的 return ,见下面的案例)。
public class Main {public static void main(String[] args) {System.out.println(extracted());}private static int extracted() {int a = 1;try {a = 2;return a;} finally {System.out.println("this is finally");a += 3;return a;}}
}// 输出
this is finally
5

引用类型

// 案例一
public class Main {public static void main(String[] args) {System.out.println(extracted());}private static Object extracted() {Person person = new Person();try {return person;} finally {System.out.println("this is finally");person.age = 5;}}
}class Person {int age;@Overridepublic String toString() {return "Person age= " + age;}
}// try 中的 return 被修改
this is finally
Person age= 5// 案例二
public class Main {public static void main(String[] args) {System.out.println(extracted());}private static Object extracted() {Person person = new Person();try {return person;} finally {System.out.println("this is finally");person =new Person();person.age = 3;}}
}class Person {int age;@Overridepublic String toString() {return "Person age= " + age;}
}// try 中的 return 没有被修改
this is finally
Person age= 0
  • 上面的结果看着有点奇怪但实际上很好理解,我们在以前的文章中讲过,Java 实际上只有值传递而不存在引用传递,当为返回值为引用类型时,返回的其实是一个地址,在案例一中我们使用地址修改了原内容,而在案例二中,我们其实将 person 指向了新的地址(new Person()),因此并没有修改原返回值地址的内容。

finally 中的代码 “非最后” 执行

  • 有时候我们发现 finally 中的代码 “非最后” 执行,那么有可能是并行执行了,比如:
public class Main {public static void main(String[] args) {extracted();}private static void extracted() {try {throw new IllegalStateException();} catch (Exception e) {e.printStackTrace();} finally {System.out.println("this is finally");}}
}// 比较难出现
this is finally
java.lang.IllegalStateExceptionat Main.extracted(Main.java:9)at Main.main(Main.java:4)
  • 实际上是因为 e.printStackTrace() 使用的是 System.err,而 System.out.println 使用的是 System.out,标准输出流和标准错误输出流是彼此独立执行的,且 JVM 为了高效的执行会让二者并行运行,所以会出现finally 中的代码 “非最后” 执行的场景。

finally 代码块一定会执行?

  • 虽然这里有一定抬杠的嫌疑,但实际上确实有一些场景下 finally 代码块不会执行,比如:
在 try-catch 语句中执行了 System.exit
在 try-catch 语句中出现了死循环
在 finally 执行之前 JVM 崩溃
  • try-catch 语句中执行了 System.exit
public class Main {public static void main(String[] args) {extracted();}private static void extracted() {try {// 此代码块执行完程序退出System.exit(0);throw new IllegalStateException();} catch (Exception e) {e.printStackTrace();} finally {System.out.println("this is finally");}}
}

异常丢失

  • 如果我们在 finally 代码块中抛出异常或使用 retrun,将会导致我们 try-catch 中的异常丢失。
// 案例一
public class Main {public static void main(String[] args) throws Exception {extracted();}private static void extracted() throws Exception {try {throw new IllegalStateException();} finally {throw new Exception("Exception");}}
}// 输出
Exception in thread "main" java.lang.Exception: Exceptionat Main.extracted(Main.java:11)at Main.main(Main.java:4)// 案例二
public class Main {public static void main(String[] args) throws Exception {extracted();}private static int extracted() throws Exception {try {throw new IllegalStateException();} finally {return 1;}}
}

finally 底层原理分析

  • 《The JavaTM Virtual Machine Specification, Second Edition》 一书中我们可以知道 Java 虚拟机是如何编译 finally:实际上,Java 虚拟机会把 finally 语句块作为 subroutine 直接插入到 try 语句块或者 catch 语句块的控制转移语句之前。还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch 语句块会保留其返回值(基本类型值或地址)到本地变量表(Local Variable Table)中,待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过 return 或者 throw 语句将其返回给该方法的调用者(invoker)。
  • 理解了 JVMfinally 的实现,我们其实就很好理解 finally 中修改数据的影响 中的案例,有兴趣的朋友可以下去深入了解。

总结

  • 本文我们结合了 finally 在实际使用中可能出现的问题并进行分析对应的原因,最后介绍了 finallyJVM 中的实现原理,帮助我们在日常开发的更好的使用 finally,下篇文章将会介绍实际异常处理中的一些最佳实践。

个人简介

👋 你好,我是 Lorin 洛林,一位 Java 后端技术开发者!座右铭:Technology has the power to make the world a better place.

🚀 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。

🧠 作为一个 Java 后端技术爱好者,我不仅热衷于探索语言的新特性和技术的深度,还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。

💡 在我的博客上,你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法,以帮助你更好地掌握Java编程。

🌐 我鼓励互动和建立社区,因此请留下你的问题、建议或主题请求,让我知道你感兴趣的内容。此外,我将分享最新的互联网和技术资讯,以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进,一起探讨技术世界的无限可能性。

📖 保持关注我的博客,让我们共同追求技术卓越。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/657123.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Jmeter分布式场景

Jmeter分布式 1. 为什么使用Jmter分布式测试 1.1 需求 对学院接口(查询学院-所有)进行1000用户并发访问,测试服务器处理批量请求能力1.2 现状 我们单台电脑由于配置(CPU、内存)问题,最模拟500用户时,就出现卡死现象按照一般的压力机配置&…

[并发编程基础] Java线程的创建方式

文章目录 线程的创建方式继承 Thread实现 Runnable 接口实现 Callable 接口使用 Lambda使用线程池 线程创建相关的 jdk源码Thread类Runnable函数接口Callable<V>函数接口executors 线程的创建方式 继承 Thread 创建一个继承 Thread 类的子类。重写 Thread 类的 run() 方…

1. 两数之和(力扣LeetCode)

文章目录 1. 两数之和题目描述哈希表&#xff1a;map二分查找暴力&#xff1a;双重for循环 1. 两数之和 题目描述 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可…

Python - 整理 MySQL 慢查询日志

在实际的数据库管理和性能优化工作中&#xff0c;MySQL 慢查询日志&#xff08;slow query log&#xff09;是一个重要的工具。当系统中的 SQL 查询花费的时间超过阈值时&#xff0c;MySQL 会将这些查询记录在慢查询日志中&#xff0c;方便进行性能分析和调优。 本文将介绍如何…

24. 两两交换链表中的节点(力扣LeetCode)

文章目录 24. 两两交换链表中的节点题目描述解题思路只使用一个临时节点使用两个临时节点 24. 两两交换链表中的节点 题目描述 给你一个链表&#xff0c;两两交换其中相邻的节点&#xff0c;并返回交换后链表的头节点。你必须在不修改节点内部的值的情况下完成本题&#xff0…

angular2 开发遇到的问题

1&#xff1a;插件使用&#xff0c;要一同引入 不然报错 “ \ Changes detected. Rebuilding...X [ERROR] NG8001: sf-dashboard-overview is not a known element:”

微信扫码登录流程

微信官方文档使用 搜索“微信开放平台”点击导航栏的“资源中心”点击“网站应用”下的“微信登录功能”地址微信扫码登录是基于OAuth2的&#xff0c;所以需要第三方应用&#xff08;就是实现微信扫码登录的应用&#xff09;成为微信的客户端&#xff0c;获取AppId和AppSecret…

Linux 进程管理

一、简述 当运行一个程序的时候&#xff0c;那么运行的这个程序就叫做进程。程序&#xff0c;是一个静态的概念统称为软件&#xff0c;相当于一个被编译好可执行的二进制文件&#xff0c;同时程序可以长期存在系统中&#xff1b;进程&#xff0c;是一个动态的概念&#xff0c;…

在Python中如何在类中定义属性和方法

目录 1. 类的基本结构 2. 定义属性 3. 定义方法 4. 特殊方法和属性 5. 属性和方法的访问控制 6. 类属性与实例属性 总结 在Python中定义类的属性和方法是面向对象编程的核心概念之一。我将详细介绍如何在Python中定义类的属性和方法。 1. 类的基本结构 在Python中&…

多媒体测试资源

目录 简介自己整理的文件测试资源列表 简介 音视频测试时,需要许多源文件,这里整理了一些.会持续更新.当然可以使用ffmpeg转换获得需要的文件. 如果知道的这方面资源的,在评论区留言. 自己整理的文件 有视频,图片,音频. 链接&#xff1a;https://pan.baidu.com/s/1vatLmWk…

RHCE练习3

1.基于域名www.openlab.com可以访问网站内容为 welcome to openlab 2.给该公司创建三个子界面分别显示学生信息&#xff0c;教学资料和缴费网站&#xff0c;基于www.openlab.com/student 网站访问学生信息&#xff0c;www.openlab.com/data网站访问教学资料www.openlab.com/mo…

Unix环境高级编程-学习-04-匿名管道PIPE

目录 一、环境 二、介绍 三、C标准函数介绍 1、pipe 2、popen 3、pclose 4、注意 四、宏 五、常见的管道用法 1、一对一&#xff08;父进程读子进程写一条管道&#xff09; 2、一对一&#xff08;父进程写子进程读一条管道&#xff09; 3、一对多&#xff08;父进程…

leetcode—跳跃游戏—贪心算法

1 跳跃游戏1 给你一个非负整数数组 nums &#xff0c;你最初位于数组的 第一个下标 。数组中的每个元素代表你在该位置可以跳跃的最大长度。 判断你是否能够到达最后一个下标&#xff0c;如果可以&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 示例 1&a…

洛谷P1824 进击的奶牛

参考代码 #include<iostream> #include<algorithm> using namespace std; const int MAXLEN 1e5 10; int nums[MAXLEN]; long long n, c; bool check(int x) {int cnt 1, pre nums[0];//贪心&#xff0c;第一头牛放在第一个隔间for(int i 1; i < n; i) {i…

C#: 导入excel文件到 dataGridView 控件

说明&#xff1a;文档介绍将 excel文件导入到 dataGridView 控件中的方法。 1.创建一个 dataGridView 控件 dataGridView_import_data&#xff0c;然后放置一个按键&#xff0c;给按键添加一个触发事件函数&#xff0c;函数内容如下。 2.在事件函数末尾添加了内存回收代码 &a…

通过 Footprint 的钱包地址属性解密身份和意图

作者&#xff1a;shellyfootprint.network 编译&#xff1a;mingfootprint.network Footprint Analytics 的钱包地址属性揭示了加密货币市场中深层的洞察力&#xff0c;揭示了钱包地址背后的身份和意图。这些标签&#xff0c;如“交易所”&#xff0c;“矿工”或“大鲸”&…

vue-computed 计算属性

一、computed 计算属性 在Vue应用中&#xff0c;在模板中双向绑定一些数据或者表达式&#xff0c;但是表达式如果过长&#xff0c;或者逻辑更为复杂 时&#xff0c;就会变得臃肿甚至难以维护和阅读&#xff0c;例如&#xff1a; <div>写在双括号中的表达式太长了,不利于阅…

Linux 入门基础知识(一)—— Linux的基本使用

Linux 入门基础知识 一、Linux的基本使用和配置1.1、终端1.2、消耗内存1.3、运行级别1.6、登录前欢迎语1.5、登录后欢迎语1.6、shell1.7、ps aux1.8、设置主机名1.9、whoami和who am i1.10、命令提示符 二、Linux执行命令的过程详解和命令类型2.1、命令执行2.2、hash缓存表2.3、…

面试 CSS 框架八股文十问十答第二期

面试 CSS 框架八股文十问十答第二期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01;关注专栏后就能收到持续更新&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1&#xff09;对盒模型的理解 盒模型…

张维迎《博弈与社会》纳什均衡与囚徒困境博弈(2)囚徒困境博弈

囚徒困境大家应该都比较熟悉了&#xff0c;我觉得这篇的意义大概在与&#xff0c;经济学术语的运用&#xff1f; 囚徒困境&#xff1a;个人理性与集体理性的矛盾 假定有两个犯罪嫌疑人共同作案。警察抓住他们以后&#xff0c;分开拘押&#xff0c;并告诉他们&#xff1a;可以选…