做网站需要什么材料,音乐设计网站推荐,衡阳 网络 网站,免费申请试用网站文章目录 前言版本finally 中的陷阱finally 中使用 returnfinally 中修改数据的影响基本类型引用类型 finally 中的代码 “非最后” 执行finally 代码块一定会执行#xff1f;异常丢失finally 底层原理分析 总结个人简介 前言
在上一篇文章中#xff0c;我们介绍了 Java 异常… 文章目录 前言版本finally 中的陷阱finally 中使用 returnfinally 中修改数据的影响基本类型引用类型 finally 中的代码 “非最后” 执行finally 代码块一定会执行异常丢失finally 底层原理分析 总结个人简介 前言
在上一篇文章中我们介绍了 Java 异常的基本概念Throwable 、异常处理关键字try-catch-finally、throw、throws本篇文章我们将更加深入的了解 finally 在异常处理中的常见问题和底层原理。
版本
Java 8
finally 中的陷阱
我们知道无论是否发生异常还是 try 或 catch 中存在 returnfinally 都会执行下面我们来看看下面几种场景
finally 中使用 return
当我们在 finally 中使用 return 时try 或 catch 中的 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 、catch 中 return 中的返回值但是会影响 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。理解了 JVM 对 finally 的实现我们其实就很好理解 finally 中修改数据的影响 中的案例有兴趣的朋友可以下去深入了解。
总结
本文我们结合了 finally 在实际使用中可能出现的问题并进行分析对应的原因最后介绍了 finally 在 JVM 中的实现原理帮助我们在日常开发的更好的使用 finally下篇文章将会介绍实际异常处理中的一些最佳实践。
个人简介 你好我是 Lorin 洛林一位 Java 后端技术开发者座右铭Technology has the power to make the world a better place. 我对技术的热情是我不断学习和分享的动力。我的博客是一个关于Java生态系统、后端开发和最新技术趋势的地方。 作为一个 Java 后端技术爱好者我不仅热衷于探索语言的新特性和技术的深度还热衷于分享我的见解和最佳实践。我相信知识的分享和社区合作可以帮助我们共同成长。 在我的博客上你将找到关于Java核心概念、JVM 底层技术、常用框架如Spring和Mybatis 、MySQL等数据库管理、RabbitMQ、Rocketmq等消息中间件、性能优化等内容的深入文章。我也将分享一些编程技巧和解决问题的方法以帮助你更好地掌握Java编程。 我鼓励互动和建立社区因此请留下你的问题、建议或主题请求让我知道你感兴趣的内容。此外我将分享最新的互联网和技术资讯以确保你与技术世界的最新发展保持联系。我期待与你一起在技术之路上前进一起探讨技术世界的无限可能性。 保持关注我的博客让我们共同追求技术卓越。