【JVM类加载及字节码技术】栈中的执行过程-异常处理(四)

本文详细解读了JVM中异常处理的执行流程,包括try-catch结构的案例与字节码指令,多个catch处理,finally块的特性,特别是finally中的return与异常吞噬现象。通过实例演示和反编译分析,揭示了异常处理机制的工作原理。
摘要由CSDN通过智能技术生成


前言

本博客讲解的是 JVM中对于异常处理的执行流程。

提示:以下是本篇文章正文内容,下面案例可供参考

一、异常处理?

异常是程序中的一些错误,但并不是所有的错误都是异常,并且错误有时候是可以避免的。异常处理就是针对这一类异常情况,进行捕获异常,让程序能处理运行时出现的任何意外或异常情况的方法。这个功能理清了编程者的思绪,也帮助代码增强了可读性,方便了维护者的阅读和理解。

二、try-catch

1.案例代码

代码如下(示例):

public class Demo1 {
	public static void main(String[] args) {
		int i = 0;
		try {
			i = 10;
		}catch (Exception e) {
			i = 20;
		}
	}
}

2.对应字节码指令

代码如下(示例):

Code:
     stack=1, locals=3, args_size=1
        0: iconst_0
        1: istore_1
        2: bipush        10
        4: istore_1
        5: goto          12
        8: astore_2
        9: bipush        20
       11: istore_1
       12: return
     //多出来一个异常表
     Exception table:
        from    to  target type
            2     5     8   Class java/lang/Exception

该处使用的的是javap进行反编译获取到的字节码指令。

  1. 我们能看到上面多出一个Exception table的异常表,上图from to代表从[ 2 , 5)行指令在运行过程中出现异常,会匹配后面的type异常类型,如果匹配上,将会被target跳转到第8条指令处继续执行。

  2. 第8条指令astore_2,是将异常对象对象存入局部变量位置为2的局部变量e


三、多个single-catch

1.案例代码

代码如下:

public class Demo1 {
	public static void main(String[] args) {
		int i = 0;
		try {
			i = 10;
		}catch (ArithmeticException e) {
			i = 20;
		}catch (Exception e) {
			i = 30;
		}
	}
}

2、对应的字节码指令

Code:
     stack=1, locals=3, args_size=1
        0: iconst_0
        1: istore_1
        2: bipush        10
        4: istore_1
        5: goto          19
        8: astore_2      //被共用的局部变量 e
        9: bipush        20
       11: istore_1
       12: goto          19
       15: astore_2      //被共用的局部变量 e
       16: bipush        30
       18: istore_1
       19: return
     Exception table:
        from    to  target type
            2     5     8   Class java/lang/ArithmeticException
            2     5    15   Class java/lang/Exception
  • 因为异常出现,只能进入Exception table中的一个分支,所以局部变量表slot 2( e )是被共用的。

四、finally

1.案例代码

代码如下:

public class Demo2 {
	public static void main(String[] args) {
		int i = 0;
		try {
			i = 10;
		} catch (Exception e) {
			i = 20;
		} finally {
			i = 30;
		}
	}
}

2、对应的字节码指令

Code:
     stack=1, locals=4, args_size=1
        0: iconst_0
        1: istore_1
        //try块
        2: bipush        10
        4: istore_1
        //try块执行完后,会执行finally    
        5: bipush        30
        7: istore_1
        8: goto          27
       //catch块     
       11: astore_2 //异常信息放入局部变量表的2号槽位
       12: bipush        20
       14: istore_1
       //catch块执行完后,会执行finally        
       15: bipush        30
       17: istore_1
       18: goto          27
       //出现异常,但未被Exception捕获,会抛出其他异常,这时也需要执行finally块中的代码   
       21: astore_3
       22: bipush        30
       24: istore_1
       25: aload_3
       26: athrow  //抛出异常
       27: return
     Exception table:
        from    to  target type
            2     5    11   Class java/lang/Exception
            2     5    21   any
           11    15    21   any
  • 上图中我们发现finally块的代码指令被复制了3份,分别放在try块指令后catch指令后,以及catch剩余的异常类型流程后。

注意:我们得注意虽然finally复制了3份,但是只会被执行一次


四、finally中的return

1.案例代码

代码如下:

public class Demo3 {
	public static void main(String[] args) {
		int i = Demo3.test();
        //结果为20
		System.out.println(i);
	}

	public static int test() {
		int i;
		try {
			i = 10;
			return i;
		} finally {
			i = 20;
			return i;
		}
	}
}

2、对应的字节码指令

Code:
     stack=1, locals=3, args_size=0
     	//try块
        0: bipush        10
        2: istore_0
        3: iload_0
        4: istore_1    //暂存返回值,记住这里的操作,会将暂存值保存
        //finally块
        5: bipush        20
        7: istore_0
        8: iload_0
        9: ireturn	//ireturn会返回操作数栈顶的整型值20
       //如果出现异常,还是会执行finally块中的内容,没有抛出异常
       10: astore_2
       11: bipush        20
       13: istore_0
       14: iload_0
       15: ireturn	//这里没有athrow了,也就是如果在finally块中如果有返回操作的话,且try块中出现异常,会吞掉异常!
     Exception table:
        from    to  target type
            0     5    10   any
  1. 我们在上面发现只要finally块中存在return语句,就算try块中同样存在return也只是会将返回值暂存,并不会返回,依然执行finally块中的语句。

  2. 我们还发现如果出现了任何异常,都会跳转到第10条指令,但是直到结束我们也并没有看到athrow抛出异常,所以只要finally存在return语句,就会吞掉异常。我们将在下面的案例进一步解释:

五、finally中的吞噬异常

1.案例代码

代码如下:

public class Demo3 {
   public static void main(String[] args) {
      int i = Demo3.test();
      //最终结果为20
      System.out.println(i);
   }

   public static int test() {
      int i;
      try {
         i = 10;
         //这里应该会抛出异常
         i = i/0;
         return i;
      } finally {
         i = 20;
         return i;
      }
   }
}

上面的代码执行也并未见抛出异常,不信的你们可以去试试 (v_v) 。

如果将上面finally中的return去掉就会出现报错了。


总结

以上就是今天要讲的内容,本文仅仅介绍了JVM中异常处理的指令,以及执行流程。

  1. 如果添加异常捕获,就会多出一个Exception table的异常表。
  2. from to代表从[ 2 , 5)行指令在运行过程中出现异常,将会匹配后面的type异常类型,target就是将要跳转的第几条指令。
  3. 异常出现,只能进入Exception table中的一个分支,所以会共享e局部变量。
  4. finally块的代码指令会被,放在try块指令后catch指令后,以及catch剩余的异常类型流程后,并且只会被执行一次。
  5. 只要finally存在return语句,就会吞掉异常try块中如存在return也只是会将返回值暂存
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值