Java中的try-catch-finally用法以及字节码原理

文章目录

    • 基础知识
    • 基本使用
      • 1. 基本的try-catch-finally使用,不带异常
      • 2. 基本的try-catch-finally使用,带异常
    • 带return的各种情况
      • 1. try{ return; }catch(){} finally{} return;
      • 2. try{} catch(){return;} finally{} return;
      • 3. try{ return; }catch(){} finally{return;}
      • 4. try{} catch(){return;}finally{return;}
    • 字节码原理

本文参考:

try catch finally 用法 - aspirant - 博客园 (cnblogs.com)

try catch finally的底层原理_lwd512768098的博客-CSDN博客

try、catch、finally、return执行顺序超详解析(针对面试题)_来盘海参炒面不要面的博客-CSDN博客

基础知识

try { //执行的代码,其中可能有异常。一旦发现异常,则立即跳到catch执行。否则不会执行catch里面的内容 }

catch { //除非try里面执行代码发生了异常,否则这里的代码不会执行 }

finally { //不管什么情况都会执行,包括try catch 里面用了return }

catch可以有多个,也可以没有,每个catch可以处理一个特定的异常。程序按照你catch的顺序查找异常处理块,如果找到,则进行处理,如果找不到,则向上一层次抛出。如果没有上一层次,则向用户抛出,此时,如果你在调试,程序将中断运行,如果是部署的程序,将会中止。

finally可以没有,也可以只有一个。即使你在try块内用return返回了,在返回前,finally总是要执行,这以便让你有机会能够在异常处理最后做一些清理工作,如关闭数据库连接等等。

基本使用

1. 基本的try-catch-finally使用,不带异常

package com.jxz.trycatch;

import org.junit.Test;


/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/17
 */
public class TryCatchTest {
    public static void main(String[] args) {
        System.out.println(f1());
    }

    public static String f1(){
        try{
            int i = 1/1;
        }catch (Exception e){
            System.out.println("catch exception..."); 
            e.printStackTrace();
        }finally {
            System.out.println("finally..."); // finally无论如何都会执行
        }
        System.out.println("main continue..."); // 没有异常可以继续向下执行
        return "f1"; // 最后方法返回f1
    }
}

输出:

finally...
main continue...
f1

2. 基本的try-catch-finally使用,带异常

public static String f2(){
    try{
        int i = 1/0;
    }catch (Exception e){
        System.out.println("catch exception..."); // 捕获到异常
        e.printStackTrace();
    }finally {
        System.out.println("finally..."); // finally无论如何都会执行
  }
    System.out.println("main continue..."); // 捕获完异常可以继续向下执行
    return "f2"; // 最后方法返回f2
}

输出:

catch exception...
finally...
main continue...
f2
java.lang.ArithmeticException: / by zero
	at com.jxz.trycatch.TryCatchTest.f2(TryCatchTest.java:32)
	at com.jxz.trycatch.TryCatchTest.main(TryCatchTest.java:14)

带return的各种情况

总结:

即使try-catch中有return,也都会执行finally语句块;先执行try或者catch中的return(return中自带逻辑也要执行完),但不返回,将变量存入临时栈中,然后去执行finally语句块;如果finally中有return,则程序会在finally执行完后直接return了;如果没有,则会执行完finally后返回前面临时栈中的值。

参考try、catch、finally、return执行顺序超详解析(针对面试题)_来盘海参炒面不要面的博客-CSDN博客

1. try{ return; }catch(){} finally{} return;

  • 先执行try块中return 语句(包括return语句中的表达式运算),但不返回;

  • 执行finally语句中全部代码

  • 最后执行try中return返回

示例:无异常

package com.jxz.trycatch;

/**
 * @Author jiangxuzhao
 * @Description
 * @Date 2023/6/18
 */
public class TryCatchFinallyReturnTest {
    public static void main(String[] args) {
        System.out.println(f1()); 
//        System.out.println(f2()); 下面的示例代码都自带这个打印
//        System.out.println(f3());
    }
    public static int f1(){
        int temp = 1;
        try {
            System.out.println(temp);
            return temp;
        } catch (Exception e) {
            System.out.println(temp);
        } finally {
            ++ temp;
            System.out.println(temp);
        }
        return temp;
    }
}

输出:121

先执行try中的打印为1,执行return,但是不返回;然后执行finally块,++temp,temp更新为2,同时打印为2;由于finally块中没有return语句,因此要返回try块中,返回其临时栈的值1,外层打印1.

2. try{} catch(){return;} finally{} return;

程序先执行try,如果遇到异常执行catch块,最终都会执行finally中的代码块;

  • 有异常:
    • 执行catch中的语句和return中的表达式运算,但在那时不返回
    • 执行finally语句中全部代码,
    • 最后执行catch块中return返回。 finally块后的return语句不再执行。
  • 无异常:执行完try再finally再return…

示例1:有异常

public static int f2(){
      int temp = 1;
      try {
          System.out.println(temp);
          int i = 1/0;
      } catch (Exception e) {
          System.out.println(temp);
          return ++ temp;
      } finally {
          ++ temp;
          System.out.println(temp);
      }
      return temp;
}

输出:1132

先执行try中的打印为1;出现异常进入catch块,先打印1,然后执行return ++temp,temp目前为2,但是仍然不返回;最后执行finally块,++temp,此时temp更新为3,同时打印3;由于finally块中没有return语句,因此要返回catch块中,返回其临时栈中的值2,外层打印2.

示例2:无异常

public static int f3(){
    int temp = 1;
    try {
        System.out.println(temp);
//            int i = 1/0;
    } catch (Exception e) {
        System.out.println(temp);
        return ++ temp;
    } finally {
        ++ temp;
        System.out.println(temp);
    }
    return temp;
}

输出:122

先执行try中的打印为1;再执行finally,++temp,temp更新为2,并打印为2;此时temp为2,返回为2,打印为2.

3. try{ return; }catch(){} finally{return;}

  • 执行try块中的代码,和return语句(包括return语句中的表达式运算),但不返回(try中return的表达式运算的结果放在临时栈);
  • 再执行finally块,
  • 执行finally块(和return中的表达式运算,字节码中装载此刻的变量),从这里返回。

此时finally块的return值,就是代码执行完后的值

示例: 无异常

public static int f4() {
    int temp = 1;
    try {
        System.out.println(temp);
        return temp;
    } catch (Exception e) {
        System.out.println(temp);
    } finally {
        System.out.println(temp);
        return ++temp;
    }
}

输出:112

先执行try中的打印为1,和return语句,但是并不返回,然后执行fially块,打印temp为1,执行return语句,++temp,temp更新为2并返回外层;外层打印为2.

4. try{} catch(){return;}finally{return;}

  • 执行try中的语句块,
  • 有无异常
    • 有异常:程序执行catch块中return语句(包括return语句中的表达式运算,,并将结果保存到临时栈),但不返回;
    • 无异常:直接执行下面的
  • 再执行finally块,
  • 执行finally块(和return中的表达式运算,字节码中装载此刻的变量),从这里返回。

示例1:无异常:

public static int f5() {
    int temp = 1;
    try {
        System.out.println(temp);
    } catch (Exception e) {
        System.out.println(temp);
        return ++temp;
    } finally {
        System.out.println(temp);
        return ++temp;
    }
}

输出: 112

先执行try中的打印1;然后执行finally块中的打印1,++temp,temp更新为2,并从这里返回,外层打印2.

有异常:

public static int f6() {
    int temp = 1;
    try {
        System.out.println(temp);
        int i = 1/0;
    } catch (Exception e) {
        System.out.println(temp);
        return ++temp;
    } finally {
        System.out.println(temp);
        return ++temp;
    }
}

输出: 1123

先执行try中的打印1;出现异常然后执行catch中的打印1,++temp,temp更新为2,将结果存起来没有返回;执行finally中的打印为2,++temp,temp更新为3,这里finally块中有return直接从此处返回,外层打印3.

字节码原理

  1. 从上面的实验可以看出来,finally总会执行,那么jvm底层是如何实现的呢?

参考java异常处理(二)—从字节码层面看throw关键字及try…catch…finally的实现_程序猿成长轨迹的博客-CSDN博客,其实底层就是将finally块的字节码复制了三份,这三份代码分别放在了不同位置

  • try块最后(如果try中有ireturn,则在ireturn前)
  • catch块最后(如果catch中有ireturn,则在ireturn前)
  • 位于异常执行路径:如果try中有异常但没有被catch捕获,或者catch又抛异常,那么就执行最终的finally代码
  1. 上面带return的各种情况在jvm如何解释呢?

主要和字节码ireturn有关,参考try catch finally的底层原理_lwd512768098的博客-CSDN博客,finally语句中是否带return,差别体现在字节码ireturn语句前是iload_0还是iload_1。

  1. 异常又是如何处理的呢?
  • 带有try…catch的方法中,方法会携带一个异常表
  • 异常表中每个条目都是一个异常处理器
  • 触发异常时,JVM会遍历异常表,比较触发异常行数(方法开始的偏移量)是否在异常处理器from指针到to指针范围内
  • 范围匹配后,会比较异常类型和异常处理器中的type是否相同
  • 类型匹配后,会跳转到target指针所指向的字节码(catch代码块开始位置)
  • 如果没有匹配到异常处理器,会弹出当前方法对应的Java栈帧,并对调用者重复操作

你可能感兴趣的:(Java学习之路,java,开发语言,jvm)