异常机制
JVM如何处理异常?
exception table
概念
方法的异常表
from :可能发生异常的起始点
to :可能发生异常的结束点
target :上述from和to之前发生异常后的异常处理者的位置
type :异常处理者处理的异常的类信息
什么时候被使用?
异常发生的时候
如何被使用?
1.JVM会在当前出现异常的方法中,查找异常表,是否有合适的处理者来处理
2.如果当前方法异常表不为空,并且异常符合处理者的from和to节点,并且type也匹配,则JVM调用位于target的调用者来处理。
3.如果上一条未找到合理的处理者,则继续查找异常表中的剩余条目
4.如果当前方法的异常表无法处理,则向上查找(弹栈处理)刚刚调用该方法的调用处,并重复上面的操作。
5.如果所有的栈帧被弹出,仍然没有处理,则抛给当前的Thread,Thread则会终止。
6.如果当前Thread为最后一个非守护线程,且未处理异常,则会导致JVM终止运行。
try...catch示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | public static void simpleTryCatch() { try { testNPE(); } catch (Exception e) { e.getMessage(); } } 字节码: public static void simpleTryCatch(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack= 1 , locals= 1 , args_size= 0 0 : invokestatic # 3 // Method testNPE:()V 3 : goto 12 6 : astore_0 7 : aload_0 8 : invokevirtual # 5 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 11 : pop 12 : return Exception table: from to target type 0 3 6 Class java/lang/Exception |
try...catch...finally示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | public static void simpleTryCatch() { try { testNPE(); } catch (Exception e) { e.getMessage(); } finally { System.out.println( "finally" ); } } 字节码: public static void simpleTryCatch(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack= 2 , locals= 2 , args_size= 0 0 : invokestatic # 3 // Method testNPE:()V 3 : getstatic # 4 // Field java/lang/System.out:Ljava/io/PrintStream; 6 : ldc # 5 // String finally 8 : invokevirtual # 6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 11 : goto 42 14 : astore_0 15 : aload_0 16 : invokevirtual # 8 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 19 : pop 20 : getstatic # 4 // Field java/lang/System.out:Ljava/io/PrintStream; 23 : ldc # 5 // String finally 25 : invokevirtual # 6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 28 : goto 42 31 : astore_1 32 : getstatic # 4 // Field java/lang/System.out:Ljava/io/PrintStream; 35 : ldc # 5 // String finally 37 : invokevirtual # 6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40 : aload_1 41 : athrow 42 : return Exception table: from to target type 0 3 14 Class java/lang/Exception 0 3 31 any 14 20 31 any |
异常表中,有三条数据,而我们仅仅捕获了一个Exception, 异常表的后两个item的type为any; 上面的三条异常表item的意思为:
如果0到3之间,发生了Exception类型的异常,调用14位置的异常处理者。
如果0到3之间,无论发生什么异常,都调用31位置的处理者
如果14到20之间(即catch部分),不论发生什么异常,都调用31位置的处理者。
finally为什么总是被执行?
finally逻辑 被提取到 try、catch位置
1 2 3 4 5 6 7 8 9 | public static void simpleTryCatch() { try { testNPE(); } catch (Exception e) { e.getMessage(); } finally { System.out.println( "finally" ); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | public static void simpleTryCatch(); descriptor: ()V flags: ACC_PUBLIC, ACC_STATIC Code: stack= 2 , locals= 2 , args_size= 0 0 : invokestatic # 3 // Method testNPE:()V 3 : getstatic # 4 // Field java/lang/System.out:Ljava/io/PrintStream; 6 : ldc # 5 // String finally 8 : invokevirtual # 6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 11 : goto 42 // try提取finally逻辑,如果无异常,goto 42,执行结束 14 : astore_0 15 : aload_0 16 : invokevirtual # 8 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 19 : pop 20 : getstatic # 4 // Field java/lang/System.out:Ljava/io/PrintStream; 23 : ldc # 5 // String finally 25 : invokevirtual # 6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 28 : goto 42 // catch提取finally逻辑,如果无异常,goto 42,执行结束 31 : astore_1 32 : getstatic # 4 // Field java/lang/System.out:Ljava/io/PrintStream; 35 : ldc # 5 // String finally 37 : invokevirtual # 6 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 40 : aload_1 41 : athrow // 如果发生异常,未被处理,执行完finally逻辑,将异常抛给调用方 42 : return Exception table: from to target type 0 3 14 Class java/lang/Exception 0 3 31 any 14 20 31 any |
return和finally的问题
在try、catch中使用return
finally都会正常被执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | public static String simpleTryCatch() { try { testNPE(); return "ok" ; } catch (Exception e) { e.getMessage(); return "catch" ; } finally { System.out.println( "finally" ); } } 字节码: public static java.lang.String simpleTryCatch(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC, ACC_STATIC Code: stack= 2 , locals= 3 , args_size= 0 0 : invokestatic # 5 // Method testNPE:()V 3 : ldc # 6 // String ok 5 : astore_0 6 : getstatic # 3 // Field java/lang/System.out:Ljava/io/PrintStream; 9 : ldc # 7 // String finally try块的finally逻辑 11 : invokevirtual # 4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14 : aload_0 15 : areturn 16 : astore_0 17 : aload_0 18 : invokevirtual # 9 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 21 : pop 22 : ldc # 10 // String catch 24 : astore_1 25 : getstatic # 3 // Field java/lang/System.out:Ljava/io/PrintStream; 28 : ldc # 7 // String finally catch块的finally逻辑 30 : invokevirtual # 4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 33 : aload_1 34 : areturn 35 : astore_2 36 : getstatic # 3 // Field java/lang/System.out:Ljava/io/PrintStream; 39 : ldc # 7 // String finally 41 : invokevirtual # 4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 44 : aload_2 45 : athrow Exception table: from to target type 0 6 16 Class java/lang/Exception 0 6 35 any 16 25 35 any |
在finally中使用return(禁止)
finally中使用return,finally逻辑执行完,执行执行return;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | public static String simpleTryCatch() { try { testNPE(); return "ok" ; } catch (Exception e) { e.getMessage(); return "catch" ; } finally { System.out.println( "finally" ); return "finally" ; } } 字节码: public static java.lang.String simpleTryCatch(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC, ACC_STATIC Code: stack= 2 , locals= 3 , args_size= 0 0 : invokestatic # 5 // Method testNPE:()V 3 : ldc # 6 // String ok 5 : astore_0 6 : getstatic # 3 // Field java/lang/System.out:Ljava/io/PrintStream; 9 : ldc # 7 // String finally 11 : invokevirtual # 4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 14 : ldc # 7 // String finally 执行完finally逻辑,执行return 16 : areturn 17 : astore_0 18 : aload_0 19 : invokevirtual # 9 // Method java/lang/Exception.getMessage:()Ljava/lang/String; 22 : pop 23 : ldc # 10 // String catch 25 : astore_1 26 : getstatic # 3 // Field java/lang/System.out:Ljava/io/PrintStream; 29 : ldc # 7 // String finally 31 : invokevirtual # 4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 34 : ldc # 7 // String finally 执行完finally逻辑,执行return 36 : areturn 37 : astore_2 38 : getstatic # 3 // Field java/lang/System.out:Ljava/io/PrintStream; 41 : ldc # 7 // String finally 43 : invokevirtual # 4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 46 : ldc # 7 // String finally 执行完finally逻辑,执行return 48 : areturn Exception table: from to target type 0 6 17 Class java/lang/Exception 0 6 37 any 17 26 37 any |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· .NET10 - 预览版1新功能体验(一)