广告
返回顶部
首页 > 资讯 > 前端开发 > html >Try-Catch-Finally中的坑有哪些
  • 594
分享到

Try-Catch-Finally中的坑有哪些

2024-04-02 19:04:59 594人浏览 薄情痞子
摘要

这篇文章主要讲解了“Try-Catch-Finally中的坑有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Try-Catch-Finally中的坑有哪

这篇文章主要讲解了“Try-Catch-Finally中的坑有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Try-Catch-Finally中的坑有哪些”吧!

坑1:finally中使用return

若在 finally 中使用 return,那么即使 try-catch 中有 return  操作,也不会立马返回结果,而是再执行完 finally 中的语句再返回。此时问题就产生了:如果 finally 中存在 return 语句,则会直接返回  finally 中的结果,从而无情的丢弃了 try 中的返回值。

① 反例代码

public static void main(String[] args) throws FileNotFoundException {     System.out.println("执行结果:" + test()); }  private static int test() {     int num = 0;     try {         // num=1,此处不返回         num++;         return num;     } catch (Exception e) {         // do something     } finally {         // num=2,返回此值         num++;         return num;     } }

以上代码的执行结果如下:

Try-Catch-Finally中的坑有哪些

② 原因分析

如果在 finally 中存在 return 语句,那么 try-catch 中的  return 值都会被覆盖,如果程序员在写代码的时候没有发现这个问题,那么就会导致程序的执行结果出错。

③ 解决方案

如果 try-catch-finally 中存在 return 返回值的情况,一定要确保 return 语句只在方法的尾部出现一次。

④ 正例代码

public static void main(String[] args) throws FileNotFoundException {     System.out.println("执行结果:" + testAmend()); } private static int testAmend() {     int num = 0;     try {         num = 1;     } catch (Exception e) {         // do something     } finally {         // do something     }     // 确保 return 语句只在此处出现一次     return num; }

坑2:finally中的代码“不执行

”如果说上面的示例比较简单,那么下面这个示例会给你不同的感受,直接来看代码。

① 反例代码

public static void main(String[] args) throws FileNotFoundException {     System.out.println("执行结果:" + getValue()); } private static int getValue() {     int num = 1;     try {         return num;     } finally {         num++;     } }

以上代码的执行结果如下:

Try-Catch-Finally中的坑有哪些

② 原因分析

本以为执行的结果会是 2,但万万没想到竟然是 1,用马大师的话来讲:「我大意了啊,没有闪」。

有人可能会问:如果把代码换成 ++num,那么结果会不会是 2 呢?

很抱歉的告诉你,并不会,执行的结果依然是 1。那为什么会这样呢?想要真正的搞懂它,我们就得从这段代码的字节码说起了。

以上代码最终生成的字节码如下:

// class version 52.0 (52) // access flags 0x21 public class com/example/basic/FinallyExample {    // compiled from: FinallyExample.java    // access flags 0x1   public <init>()V    L0     LINENUMBER 5 L0     ALOAD 0     INVOKESPECIAL java/lang/Object.<init> ()V     RETURN    L1     LOCALVARIABLE this Lcom/example/basic/FinallyExample; L0 L1 0     MAXSTACK = 1     MAXLOCALS = 1    // access flags 0x9   public static main([Ljava/lang/String;)V throws java/io/FileNotFoundException     L0     LINENUMBER 13 L0     GETSTATIC java/lang/System.out : Ljava/io/PrintStream;     NEW java/lang/StringBuilder     DUP     INVOKESPECIAL java/lang/StringBuilder.<init> ()V     LDC "\u6267\u884c\u7ed3\u679c:"     INVOKEVIRTUAL java/lang/StringBuilder.append (Ljava/lang/String;)Ljava/lang/StringBuilder;     INVOKESTATIC com/example/basic/FinallyExample.getValue ()I     INVOKEVIRTUAL java/lang/StringBuilder.append (I)Ljava/lang/StringBuilder;     INVOKEVIRTUAL java/lang/StringBuilder.toString ()Ljava/lang/String;     INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/String;)V    L1     LINENUMBER 14 L1     RETURN    L2     LOCALVARIABLE args [Ljava/lang/String; L0 L2 0     MAXSTACK = 3     MAXLOCALS = 1    // access flags 0xA   private static getValue()I     TRYCATCHBLOCK L0 L1 L2 null    L3     LINENUMBER 18 L3     ICONST_1     ISTORE 0    L0     LINENUMBER 20 L0     ILOAD 0     ISTORE 1    L1     LINENUMBER 22 L1     IINC 0 1    L4     LINENUMBER 20 L4     ILOAD 1     IRETURN    L2     LINENUMBER 22 L2    FRAME FULL [I] [java/lang/Throwable]     ASTORE 2     IINC 0 1    L5     LINENUMBER 23 L5     ALOAD 2     ATHROW    L6     LOCALVARIABLE num I L0 L6 0     MAXSTACK = 1     MAXLOCALS = 3 }

这些字节码的简易版本如下图所示:

Try-Catch-Finally中的坑有哪些

想要读懂这些字节码,首先要搞懂这些字节码所代表的含义,这些内容可以从 oracle  的官网查询到(英文文档):https://docs.oracle.com/javase/specs/JVMs/se8/html/jvms-6.html

磊哥在这里对这些字节码做一个简单的翻译

iconst 是将 int 类型的值压入操作数栈。istore 是将 int 存储到局部变量。iload 从局部变量加载 int 值。iinc  通过下标递增局部变量。ireturn 从操作数堆栈中返回 int 类型的值。astore 将引用存储到局部变量中。

有了这些信息之后,我们来翻译一下上面的字节码内容:

0 iconst_1   在操作数栈中存储数值 1 1 istore_0   将操作数栈中的数据存储在局部变量的位置 0 2 iload_0    从局部变量读取值到操作数栈 3 istore_1   将操作数栈中存储 1 存储在局部变量的位置 1 4 iinc 0 by 1 把局部变量位置 0 的元素进行递增(+1)操作 7 iload_1 将局部位置 1 的值加载到操作数栈中 8 ireturn 返回操作数栈中的 int 值

通过以上信息也许你并不能直观的看出此方法的内部执行过程,没关系磊哥给你准备了方法执行流程图:

Try-Catch-Finally中的坑有哪些

Try-Catch-Finally中的坑有哪些

Try-Catch-Finally中的坑有哪些

通过以上图片我们可以看出:在 finally 语句(iinc 0,  1)执行之前,本地变量表中存储了两个信息,位置 0 和位置 1 都存储了一个值为 1 的 int 值。而在执行 finally(iinc 0, 1)之前只把位置  0 的值进行了累加,之后又将位置 1 的值(1)返回给了操作数栈,所以当执行返回操作(ireturn)时会从操作数栈中读到返回值为 1 的结果,因此最终的执行是  1 而不是 2。

③ 解决方案

关于 Java 虚拟机是如何编译 finally 语句块的问题,有兴趣的读者可以参考《The JavaTM Virtual Machine  Specification, Second Edition》中 7.13 节 Compiling finally。那里详细介绍了 Java 虚拟机是如何编译  finally 语句块。

实际上,Java 虚拟机会把 finally 语句块作为 subroutine(对于这个 subroutine  不知该如何翻译为好,干脆就不翻译了,免得产生歧义和误解)直接插入到 try 语句块或者 catch  语句块的控制转移语句之前。但是,还有另外一个不可忽视的因素,那就是在执行 subroutine(也就是 finally 语句块)之前,try 或者 catch  语句块会保留其返回值到本地变量表(Local Variable Table)中,待 subroutine 执行完毕之后,再恢复保留的返回值到操作数栈中,然后通过  return 或者 throw 语句将其返回给该方法的调用者(invoker)。

因此如果在 try-catch-finally 中如果有 return 操作,**一定要确保 return 语句只在方法的尾部出现一次!**这样就能保证  try-catch-finally 中所有操作代码都会生效。

④ 正例代码

private static int getValueByAmend() {     int num = 1;     try {         // do something     } catch (Exception e) {         // do something     } finally {         num++;     }     return num; }

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

① 反例代码

public static void main(String[] args) throws  FileNotFoundException { execErr();}private static void execErr() { try { throw  new RuntimeException(); } catch (RuntimeException e) { e.printStackTrace(); }  finally { System.out.println("执行 finally."); }}

以上代码的执行结果如下:

Try-Catch-Finally中的坑有哪些

从以上结果可以看出 finally 中的代码并不是最后执行的,而是在 catch  打印异常之前执行的,这是为什么呢?

② 原因分析

产生以上问题的真实原因其实并不是因为 try-catch-finally,当我们打开 e.printStackTrace  的源码就能看出一些端倪了,源码如下:

Try-Catch-Finally中的坑有哪些

从上图可以看出,当执行 e.printStackTrace() 和 finally  输出信息时,使用的并不是同一个对象。finally 使用的是标准输出流:System.out,而 e.printStackTrace()  使用的却是标准错误输出流:System.err.println,它们执行的效果等同于:

public static void main(String[] args) {     System.out.println("我是标准输出流");     System.err.println("我是标准错误输出流"); }

而以上代码执行结果的顺序也是随机的,而产生这一切的原因,我们或许可以通过标准错误输出流(System.err)的注释和说明文档中看出:

Try-Catch-Finally中的坑有哪些

Try-Catch-Finally中的坑有哪些

我们简单的对以上的注释做一个简单的翻译:

“标准”错误输出流。该流已经打开,并准备接受输出数据。通常,此流对应于主机环境或用户指定的显示输出或另一个输出目标。按照惯例,即使主要输出流(out  输出流)已重定向到文件或其他目标位置,该输出流(err 输出流)也能用于显示错误消息或其他信息,这些信息应引起用户的立即注意。

从源码的注释信息可以看出,标准错误输出流(System.err)和标准输出流(System.out)使用的是不同的流对象,即使标准输出流并定位到其他的文件,也不会影响到标准错误输出流。那么我们就可以大胆的猜测:二者是独立执行的,并且为了更高效的输出流信息,二者在执行时是并行执行的,因此我们看到的结果是打印顺序总是随机的。

为了验证此观点,我们将标准输出流重定向到某个文件,然后再来观察 System.err 能不能正常打印,实现代码如下:

public static void main(String[] args) throws FileNotFoundException {     // 将标准输出流的信息定位到 log.txt 中     System.setOut(new PrintStream(new FileOutputStream("log.txt")));     System.out.println("我是标准输出流");     System.err.println("我是标准错误输出流"); }

以上代码的执行结果如下:

Try-Catch-Finally中的坑有哪些

当程序执行完成之后,我们发现在项目的根目录出现了一个新的 log.txt  文件,打开此文件看到如下结果:

Try-Catch-Finally中的坑有哪些

从以上结果可以看出标准输出流和标准错误输出流是彼此独立执行的,且 JVM 为了高效的执行会让二者并行运行,所以最终我们看到的结果是 finally 在  catch 之前执行了。

③ 解决方案

知道了原因,那么问题就好处理,我们只需要将 try-catch-finally 中的输出对象,改为统一的输出流对象就可以解决此问题了。

④ 正例代码

private static void execErr() {     try {         throw new RuntimeException();     } catch (RuntimeException e) {         System.out.println(e);     } finally {         System.out.println("执行 finally.");     } }

改成了统一的输出流对象之后,我手工执行了 n 次,并没有发现任何问题。

坑4:finally中的代码“不执行”f

inally  中的代码一定会执行吗?如果是之前我会毫不犹豫的说“是的”,但在遭受了社会的毒打之后,我可能会这样回答:正常情况下 finally  中的代码一定会执行的,但如果遇到特殊情况 finally 中的代码就不一定会执行了,比如下面这些情况:

  • 在 try-catch 语句中执行了 System.exit;

  • 在 try-catch 语句中出现了死循环;

  • 在 finally 执行之前掉电或者 JVM 崩溃了。

如果发生了以上任意一种情况,finally  中的代码就不会执行了。虽然感觉这一条有点“抬杠”的嫌疑,但墨菲定律告诉我们,如果一件事有可能会发生,那么他就一定会发生。所以从严谨的角度来说,这个观点还是成立的,尤其是对于新手来说,神不知鬼不觉的写出一个自己发现不了的死循环是一件很容易的事,不是嘛?

① 反例代码

public static void main(String[] args) {     noFinally(); } private static void noFinally() {     try {         System.out.println("我是 try~");         System.exit(0);     } catch (Exception e) {         // do something     } finally {         System.out.println("我是 fially~");     } }

以上代码的执行结果如下:

Try-Catch-Finally中的坑有哪些

从以上结果可以看出 finally 中的代码并没有执行。

② 解决方案

排除掉代码中的 System.exit 代码,除非是业务需要,但也要注意如果在 try-cacth 中出现了 System.exit  的代码,那么 finally 中的代码将不会被执行。

感谢各位的阅读,以上就是“Try-Catch-Finally中的坑有哪些”的内容了,经过本文的学习后,相信大家对Try-Catch-Finally中的坑有哪些这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是编程网,小编将为大家推送更多相关知识点的文章,欢迎关注!

--结束END--

本文标题: Try-Catch-Finally中的坑有哪些

本文链接: https://www.lsjlt.com/news/83924.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Try-Catch-Finally中的坑有哪些
    这篇文章主要讲解了“Try-Catch-Finally中的坑有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Try-Catch-Finally中的坑有哪...
    99+
    2022-10-19
  • 使用try-catch-finally常遇到的坑有哪些
    本篇内容主要讲解“使用try-catch-finally常遇到的坑有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“使用try-catch-finally常遇到的坑有哪些”吧!坑1:final...
    99+
    2023-06-15
  • Java中的异常处理(try,catch,finally,throw,throws)
    目录抛出异常throwObjects非空判断声明异常throws捕获异常try…catchfinally 代码块异常注意事项前言: Java异常处理的五个关键字:try...
    99+
    2022-11-13
  • js中try、catch、finally执行规则的示例分析
    这篇文章将为大家详细讲解有关js中try、catch、finally执行规则的示例分析,小编觉得挺实用的,因此分享给大家做个参考,希望大家阅读完这篇文章后可以有所收获。try:  语句测试代码块的...
    99+
    2022-10-19
  • Java的try、catch、finally语句中有return各类情况是什么
    本篇内容主要讲解“Java的try、catch、finally语句中有return各类情况是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java的try、catch、finally语句中有...
    99+
    2023-06-16
  • Java中try-catch-finally异常处理机制的示例分析
    小编给大家分享一下Java中try-catch-finally异常处理机制的示例分析,相信大部分人都还不怎么了解,因此分享这篇文章给大家参考一下,希望大家阅读完这篇文章后大有收获,下面让我们一起去了解一下吧!一、异常处理异常(Excepti...
    99+
    2023-06-20
  • Java中try-catch-finally语法块的执行顺序是怎样的
    这篇文章主要讲解了“Java中try-catch-finally语法块的执行顺序是怎样的”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java中try-catch-finally语法块的执行...
    99+
    2023-06-03
  • JavaScript中错误处理try..catch...finally+涵盖throw+TypeError+RangeError的示例分析
    这篇文章主要介绍JavaScript中错误处理try..catch...finally+涵盖throw+TypeError+RangeError的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完!1.用途通常,如...
    99+
    2023-06-22
  • JavaScript中不要在循环内部使用try-catch-finally的示例分析
    这篇文章主要为大家展示了“JavaScript中不要在循环内部使用try-catch-finally的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Ja...
    99+
    2022-10-19
  • JS中的try catch语句有什么作用
    这期内容当中小编将会给大家带来有关JS中的try catch语句有什么作用,文章内容丰富且以专业的角度为大家分析和叙述,阅读完这篇文章希望大家可以有所收获。程序是从上到下顺序执行的,同时可以通过一些控制语句...
    99+
    2022-10-19
  • Java中final,finally,finalize有哪些区别
    本篇内容主要讲解“Java中final,finally,finalize有哪些区别”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java中final,finally,finalize有哪些区别”...
    99+
    2023-06-21
  • Java中常见的坑有哪些
    今天小编给大家分享一下Java中常见的坑有哪些的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。1.前言同一个代码“坑”,踩第一...
    99+
    2023-06-27
  • golang for range中的坑有哪些
    本篇内容主要讲解“golang for range中的坑有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“golang for range中的坑有哪些”吧!go 语言 for range 中的...
    99+
    2023-07-04
  • PHP编程中的坑有哪些
    本篇内容介绍了“PHP编程中的坑有哪些”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!1、由于使用单引号,以“ ”为分割符,使用PHP函数ex...
    99+
    2023-06-17
  • Java CPP的坑有哪些
    这篇文章主要讲解了“Java CPP的坑有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Java CPP的坑有哪些”吧!1.分清楚System.load与System.loadLibra...
    99+
    2023-06-04
  • Java中ReentrantLock常见的坑有哪些
    这篇文章主要介绍“Java中ReentrantLock常见的坑有哪些”,在日常操作中,相信很多人在Java中ReentrantLock常见的坑有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java中Re...
    99+
    2023-06-30
  • 编写Java的坑有哪些
    本篇内容主要讲解“编写Java的坑有哪些”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“编写Java的坑有哪些”吧!1、对象比较方法JDK 1.7 提供的 Objects.equals 方法,非常...
    99+
    2023-06-16
  • 关于C++的坑有哪些
    这篇文章主要讲解了“关于C++的坑有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“关于C++的坑有哪些”吧!1. string的字符串拼接,导致coredump该问题的核心点在于第9行,...
    99+
    2023-06-16
  • 使用Python的坑有哪些
    这篇文章主要讲解了“使用Python的坑有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“使用Python的坑有哪些”吧!坑01 - 整数比较的坑在 Python 中一切都是对象,整数也是...
    99+
    2023-06-02
  • BigDecimal遇到的坑有哪些
    这篇文章主要介绍“BigDecimal遇到的坑有哪些”,在日常操作中,相信很多人在BigDecimal遇到的坑有哪些问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”BigDec...
    99+
    2022-10-19
软考高级职称资格查询
推荐阅读
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作