Java基础之try catch finally的原理

createh52周前 (12-16)技术教程16

简介

java做业务开发同学经常会使用try catch finally捕捉异常,而使用起来有还有三种组合,try finally 和 try catch 和 try catch finally。

示例

  • try finally的用法如下
        try{
            System.out.println("业务执行");
            int i =  1 / 0;
            System.out.println("业务执行完成");
          }finally {
            System.out.println("释放资源");
          }

复制代码

有的同学可能就要问了,写了try不写catch,还有这样写的吗? 这样写的意义是啥???

  • try catch
        try{
           System.out.println("业务执行");
           int i =  1 / 0;
           System.out.println("业务执行完成");
         }catch (Exception e){
           System.out.println("捕捉异常");
          }

复制代码
  • try catch finally 这种一般是我们平时用的做多写法 try { System.out.println("业务执行"); } catch (Exception e) { System.out.println("捕捉异常"); } finally { System.out.println("释放资源"); } 复制代码 既然java的语法支持这样写,那么就有它的用法。在这里我贴一段大名鼎鼎的线程池 的执行Work线程的代码,这里让你们看下这样的用法在java基础框架中被大量运用 ,只是我们只是还知道罢了。
 final void runWorker(Worker w) {
       Thread wt = Thread.currentThread();
       Runnable task = w.firstTask;
       w.firstTask = null;
       w.unlock(); // allow interrupts
       boolean completedAbruptly = true;
       try {
           while (task != null || (task = getTask()) != null) {
               w.lock();
               // If pool is stopping, ensure thread is interrupted;
               // if not, ensure thread is not interrupted.  This
               // requires a recheck in second case to deal with
               // shutdownNow race while clearing interrupt
               if ((runStateAtLeast(ctl.get(), STOP) ||
                    (Thread.interrupted() &&
                     runStateAtLeast(ctl.get(), STOP))) &&
                   !wt.isInterrupted())
                   wt.interrupt();
               try {
                   beforeExecute(wt, task);
                   try {
                       task.run();
                       afterExecute(task, null);
                   } catch (Throwable ex) {
                       afterExecute(task, ex);
                       throw ex;//抛出异常
                   }
                   //这里没有finnaly 因为没有最后一定需要释放的资源
               } finally {
                // 这里没有catch异常,主要原因是为后面的
                // completedAbruptly变量用来判断执行任务是否抛出异常
               //1.如果上面的代码抛出了异常,那么一定是程序里层的try部分                    // 里task执行,捕捉的异常,然后抛出的异常,此时程序是走不到
                //completedAbruptly = false;这一行,因为外层try是
               // 没有加catch异常的,
                //2.如果程序能completedAbruptly=false这一行,那么此时
                //程序外层try住部分是没有异常发生的。代表执行任务正常情况。
                   task = null;
                   w.completedTasks++;
                   w.unlock();
               }
           }
           completedAbruptly = false;
       } finally {
           processWorkerExit(w, completedAbruptly);
       }
   }

复制代码

关于try catch finally 中 return的执行原理

    public static int test() {
       int i = 0;
       try {
           System.out.println("业务执行");
           i = 1 / 0;
           System.out.println("业务执行完成");
           i++;
           return i;
       } catch (Exception e) {
           System.out.println("捕捉异常");
           i++;
           return i;
       } finally {
           System.out.println("释放资源");
           i++;
           return i;
       }
   }
复制代码

执行结果如下:



下面是上面的函数javap反编译出来的JVM的字节码,由于指令比较长,我会加好注释方便理解

 public static int test();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=0
         0: iconst_0       // 将常量0推送到操作数栈            
         1: istore_0    // 将操作数栈顶元素(这里是0) 存储到局部变量表的0的位置(注意: 这里局部变量表0一般是存放是this指针,由于这里是static方法,所以没有this指针)
         2: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream; // 获取 PrintStream的静态变量
         5: ldc           #25                 // String 业务执行 
         7: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V    //调用打印函数
        10: iconst_1  // 将常量 1 推送到操作数栈顶
        11: iconst_0  // 将常量 0 推送到操作数栈顶
        12: idiv    // 取出操作数栈两个数进行除法运算
        13: istore_0  //将运算结果保存到函数的局部变量表中第0个位置。
        14: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;  
        17: ldc           #30                 // String "业务执行完成"常量推送到操作数栈顶
        19: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V    //pop操作数顶元素,调用打印函数,
        22: iinc          0, 1             // 执行局部变量表中索引0号位置数据加1
        25: iload_0                        // 执行局部变量表中索引0号位置数据推送到操作数栈
        26: istore_1                       // 将操作数栈顶元素保存到局部变量表的1号位置
        27: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        30: ldc           #32                 // String 释放资源
        32: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V   调用函数打印"释放资源"
        35: iinc          0, 1          // 执行局部变量表中索引0号位置数据加1
        38: iload_0                     // 执行局部变量表中索引0号位置数据推送到操作数栈
        39: ireturn                     // 执行返回
        40: astore_1                   // 将操作数栈顶元素保存到局部变量表索引为1的位置
        41: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: ldc           #36                 // String 捕捉异常
        46: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        49: iinc          0, 1             // 执行局部变量表中索引0号位置数据加1
        52: iload_0                        // 执行局部变量表中索引0号位置数据推送到操作数栈
        53: istore_2                        // 将操作数栈顶元素保存到局部变量表索引为2的位置
        54: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        57: ldc           #32                 // String 释放资源
        59: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V    打印函数
        62: iinc          0, 1   // 执行局部变量表中索引0号位置数据加1
        65: iload_0             // 执行局部变量表中索引0号位置数据推送到操作数栈
        66: ireturn              //执行返回    
        67: astore_3             // 将操作数栈顶元素保存到局部变量表索引为2的位置
        68: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        71: ldc           #32                 // String 释放资源
        73: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V     
        76: iinc              0, 1   // 执行局部变量表中索引0号位置数据加1
        79: iload_0           // 执行局部变量表中索引0号位置数据推送到操作数栈
        80: ireturn          //执行返回操作数栈的元素
      Exception table:
         from    to  target type
             2    27    40   Class java/lang/Exception
             2    27    67   any
            40    54    67   any
      LineNumberTable:
        line 27: 0
        line 29: 2
        line 30: 10
        line 31: 14
        line 32: 22
        line 33: 25
        line 39: 27
        line 40: 35
        line 41: 38
        line 34: 40
        line 35: 41
        line 36: 49
        line 37: 52
        line 39: 54
        line 40: 62
        line 41: 65
        line 39: 67
        line 40: 76
        line 41: 79
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           41      26     1     e   Ljava/lang/Exception;
            2      79     0     i   I
      StackMapTable: number_of_entries = 2
        frame_type = 255 /* full_frame */
          offset_delta = 40
          locals = [ int ]
          stack = [ class java/lang/Exception ]
        frame_type = 90 /* same_locals_1_stack_item */
          stack = [ class java/lang/Throwable ]

复制代码

执行结果是 2,由于抛出异常所以try块里的i++没有执行,首先执行catch的i++,然后 执行finally的i++,然后执行返回,而catch的return从字节码里面都是是忽略的,因为 finally已经有return了 那么同样的程序,将finally中return去掉会怎么样

public static int test() {
        int i = 0;
        try {
            System.out.println("业务执行");
            i = 1 / 0;
            System.out.println("业务执行完成");
            i++;
            return i;
        } catch (Exception e) {
            System.out.println("捕捉异常");
            i++;
            return i;
        } finally {
            System.out.println("释放资源");
            i++;
        }
    }
复制代码

执行结果变成了1,这是为什么呢?

public static int test();
    descriptor: ()I
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=2, locals=4, args_size=0
         0: iconst_0
         1: istore_0
         2: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
         5: ldc           #25                 // String 业务执行
         7: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        10: iconst_1
        11: iconst_0
        12: idiv
        13: istore_0
        14: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        17: ldc           #30                 // String 业务执行完成
        19: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        22: iinc          0, 1
        25: iload_0
        26: istore_1
        27: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        30: ldc           #32                 // String 释放资源
        32: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        35: iinc          0, 1
        38: iload_1
        39: ireturn
        40: astore_1
        41: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        44: ldc           #36                 // String 捕捉异常
        46: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        49: iinc          0, 1
        52: iload_0
        53: istore_2
        54: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        57: ldc           #32                 // String 释放资源
        59: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        62: iinc          0, 1
        65: iload_2
        66: ireturn
        67: astore_3
        68: getstatic     #7                  // Field java/lang/System.out:Ljava/io/PrintStream;
        71: ldc           #32                 // String 释放资源
        73: invokevirtual #27                 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
        76: iinc          0, 1
        79: aload_3
        80: athrow
      Exception table:
         from    to  target type
             2    27    40   Class java/lang/Exception
             2    27    67   any
            40    54    67   any

复制代码

只是因为是由于try里面的return没有执行到,那么最终执行return由catch中执行返回, 从上图的字节码指令,catch的执行是将本地变量表中索引位置为1的位置加1后,然后load 到操作数栈,最后store到本地变量表的索引为2的位置。


而在执行finally里面的执行时索引变量0的索引此时是1加1,变成2,而最后执行会执行ireturn是前的操作,是从iload_2是本地变量表的2号位置拿的返回值,所以finally 里面的执行i++,只是改变了本地变量表的0号位置值,并不改变返回值。



作者:xjz1842
链接:https://juejin.cn/post/6985436629305393183
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。


相关文章

Windows脚本用于启停Java应用程序JAR包,并具备日志输出功能

1. 创建批处理脚本创建一个批处理文件(例如 app.bat),并在其中编写启动和停止 JAR 包的命令,并将输出重定向到日志文件。app.bat@echo offsetlocal:: 设置环境变量s...

从Java转储分析来改进Java应用程序的性能(上)

1 引言垃圾回收(Garbage Collection,GC)在Java的内存管理中扮演着至关重要的角色。它负责自动回收不再被使用的内存资源,从而避免内存泄漏。垃圾回收器通过一组专门的线程来执行内存回...

Java:什么是Spring框架? java中的spring框架

  Spring框架是一个开源应用程序框架,通过提供基础设施支持来支持Java应用程序的开发。它是著名的Java企业版框架之一。Spring通过使用普通旧Java对象 (POJO) 帮助开发人员创建高...

深入探究hprof文件:如何分析Java应用程序性能瓶颈

Java应用程序的性能问题是开发人员面临的一个常见挑战。为了解决这些问题,开发人员需要了解应用程序的性能瓶颈所在,并采取相应的措施来优化它们。在此过程中,hprof文件是一种非常有用的工具,可以帮助开...

JSTAT命令-对Java应用程序的资源和性能进行实时的监控

对于监控JVM,jdk也提供了很多工具,供我们来使用,其中命令jstat,是JDK自带的一个轻量级小工具,可以查看堆内存各部分的使用量,以及加载类的数量,对Java应用程序的资源和性能进行实时的监控...