0 前言
关于java
字节码指令的含义,可以参考这篇博客:Java字节码指令含义解释与指令查询
1 try..catch..finally
1.1 catch
1.1.1 catch单个异常
查看相应的java
代码:
1 2 3 4 5 6 7 8 9 10
| public class FinallyDemo { public static void main(String[] args) { int i = 10; try{ i = 20; }catch (NegativeArraySizeException e){ i = 30; } } }
|
接下来通过javap -v xxx.class
命令,来查看该段源码的字节码反编译之后的信息:
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 52
| { public com.basic.FinallyDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/basic/FinallyDemo;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=3, args_size=1 0: bipush 10 2: istore_1 3: bipush 20 5: istore_1 6: goto 13 9: astore_2 10: bipush 30 12: istore_1 13: return Exception table: from to target type 3 6 9 Class java/lang/NegativeArraySizeException LineNumberTable: line 5: 0 line 7: 3 line 10: 6 line 8: 9 line 9: 10 line 11: 13 LocalVariableTable: Start Length Slot Name Signature 10 3 2 e Ljava/lang/NegativeArraySizeException; 0 14 0 args [Ljava/lang/String; 3 11 1 i I StackMapTable: number_of_entries = 2 frame_type = 255 offset_delta = 9 locals = [ class "[Ljava/lang/String;", int ] stack = [ class java/lang/NegativeArraySizeException ] frame_type = 3 } SourceFile: "FinallyDemo.java"
|
从反编译之后的字节码信息可以看到,之所以能执行catch中的代码,是因为有一个异常表(exception table)来存放try
和catch
的执行的起始位置,当检测到try
中有异常发生时,跳转到catch
中执行。
1.1.2 catch多个异常
先看java
源代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.nio.file.FileSystemAlreadyExistsException;
public class FinallyDemo { public static void main(String[] args) { int i = 10; try{ i = 20; }catch (NegativeArraySizeException e){ i = 30; }catch (NullPointerException e){ i = 40; }catch (FileSystemAlreadyExistsException e){ i = 50; } System.out.println(i); } }
|
源代码对应的字节码指令:
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
| { public com.basic.FinallyDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/basic/FinallyDemo;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: bipush 10 2: istore_1 3: bipush 20 5: istore_1 6: goto 27 9: astore_2 10: bipush 30 12: istore_1 13: goto 27 16: astore_2 17: bipush 40 19: istore_1 20: goto 27 23: astore_2 24: bipush 50 26: istore_1 27: getstatic #5 30: iload_1 31: invokevirtual #6 34: return Exception table: from to target type 3 6 9 Class java/lang/NegativeArraySizeException 3 6 16 Class java/lang/NullPointerException 3 6 23 Class java/nio/file/FileSystemAlreadyExistsException LineNumberTable: line 8: 0 line 10: 3 line 17: 6 line 11: 9 line 12: 10 line 17: 13 line 13: 16 line 14: 17 line 17: 20 line 15: 23 line 16: 24 line 18: 27 line 19: 34 LocalVariableTable: Start Length Slot Name Signature 10 3 2 e Ljava/lang/NegativeArraySizeException; 17 3 2 e Ljava/lang/NullPointerException; 24 3 2 e Ljava/nio/file/FileSystemAlreadyExistsException; 0 35 0 args [Ljava/lang/String; 3 32 1 i I StackMapTable: number_of_entries = 4 frame_type = 255 offset_delta = 9 locals = [ class "[Ljava/lang/String;", int ] stack = [ class java/lang/NegativeArraySizeException ] frame_type = 70 stack = [ class java/lang/NullPointerException ] frame_type = 70 stack = [ class java/nio/file/FileSystemAlreadyExistsException ] frame_type = 3 }
|
1.1.3 catch多个异常的简写
这是上一小节中的catch
多个异常的简写形式:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import java.nio.file.FileSystemAlreadyExistsException;
public class FinallyDemo { public static void main(String[] args) { int i = 10; try{ i = 20; }catch (NegativeArraySizeException | NullPointerException | FileSystemAlreadyExistsException e){ i = 30; } System.out.println(i); } }
|
对应的字节码信息和上一节中的相同:
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 52 53 54 55 56 57
| { public com.basic.FinallyDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/basic/FinallyDemo;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: bipush 10 2: istore_1 3: bipush 20 5: istore_1 6: goto 13 9: astore_2 10: bipush 30 12: istore_1 13: getstatic #5 16: iload_1 17: invokevirtual #6 20: return Exception table: from to target type 3 6 9 Class java/lang/NegativeArraySizeException 3 6 9 Class java/lang/NullPointerException 3 6 9 Class java/nio/file/FileSystemAlreadyExistsException LineNumberTable: line 8: 0 line 10: 3 line 13: 6 line 11: 9 line 12: 10 line 14: 13 line 15: 20 LocalVariableTable: Start Length Slot Name Signature 10 3 2 e Ljava/lang/RuntimeException; 0 21 0 args [Ljava/lang/String; 3 18 1 i I StackMapTable: number_of_entries = 2 frame_type = 255 offset_delta = 9 locals = [ class "[Ljava/lang/String;", int ] stack = [ class java/lang/RuntimeException ] frame_type = 3 }
|
1.2 finally
1.2.1 在finally中return *;
查看FinallyDemo.java
文件的代码,试问最终输出多少?10? or 20? or 30?
FinallyDemo.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class FinallyDemo { public static void main(String[] args) { FinallyDemo finallyDemo = new FinallyDemo(); int t = finallyDemo.test(); System.out.println(t); }
public int test(){ int i = 10; try { return 20; }finally { return 30; } } }
|
运行结果:
通过对其FinallyDemo.class
文件进行反编译,可以分析得到为什么最终结果会是30。为了简化代码,这里就直接将源代码对应的字节码信息粘贴过来。
FinallyDemo.class
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74
| { public com.basic.FinallyDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/basic/FinallyDemo;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: new #2 3: dup 4: invokespecial #3 7: astore_1 8: aload_1 9: invokevirtual #4 12: istore_2 13: getstatic #5 16: iload_2 17: invokevirtual #6 20: return LineNumberTable: line 5: 0 line 6: 8 line 7: 13 line 8: 20 LocalVariableTable: Start Length Slot Name Signature 0 21 0 args [Ljava/lang/String; 8 13 1 finallyDemo Lcom/basic/FinallyDemo; 13 8 2 t I
public int test(); descriptor: ()I flags: ACC_PUBLIC Code: stack=1, locals=4, args_size=1 0: bipush 10 2: istore_1 3: bipush 20 5: istore_2 6: bipush 30 8: ireturn 9: astore_3 10: bipush 30 12: ireturn Exception table: from to target type 3 6 9 any LineNumberTable: line 11: 0 line 13: 3 line 15: 6 LocalVariableTable: Start Length Slot Name Signature 0 13 0 this Lcom/basic/FinallyDemo; 3 10 1 i I StackMapTable: number_of_entries = 1 frame_type = 255 offset_delta = 9 locals = [ class com/basic/FinallyDemo, int ] stack = [ class java/lang/Throwable ] } SourceFile: "FinallyDemo.java"
|
1.2.2 在finally中使用return的弊端
使用try..catch
捕捉程序异常,但是有些时候,catch
不能将所有的异常都捕捉到,此时那些没有被catch
捕捉到的异常就会在finally
中被再次捕捉到,这样就提高了程序运行的安全性。但是当在finally
中使用return
语句后,那么finally
就会将其他的异常”吞掉“,也就是不再捕捉catch
中没有被捕捉的异常。
1.2.2.1 finally
中没有加return
语句:
1 2 3 4 5 6 7 8 9 10 11 12
| public class FinallyDemo { public static void main(String[] args) { int i = 10; try{ i = 20; }catch (NegativeArraySizeException e){ e.printStackTrace(); }finally { i = 30; } } }
|
为了简便,这里主要粘贴源代码对应的字节码信息:
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| { public com.basic.FinallyDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 4: return LineNumberTable: line 3: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/basic/FinallyDemo;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=4, args_size=1 0: bipush 10 2: istore_1 3: bipush 20 5: istore_1 6: bipush 30 8: istore_1 9: goto 29 12: astore_2 13: aload_2 14: invokevirtual #3 17: bipush 30 19: istore_1 20: goto 29 23: astore_3 24: bipush 30 26: istore_1 27: aload_3 28: athrow 29: return Exception table: from to target type 3 6 12 Class java/lang/NegativeArraySizeException 3 6 23 any 12 17 23 any LineNumberTable: line 5: 0 line 7: 3 line 11: 6 line 12: 9 line 8: 12 line 9: 13 line 11: 17 line 12: 20 line 11: 23 line 12: 27 line 13: 29 LocalVariableTable: Start Length Slot Name Signature 13 4 2 e Ljava/lang/NegativeArraySizeException; 0 30 0 args [Ljava/lang/String; 3 27 1 i I StackMapTable: number_of_entries = 3 frame_type = 255 offset_delta = 12 locals = [ class "[Ljava/lang/String;", int ] stack = [ class java/lang/NegativeArraySizeException ] frame_type = 74 stack = [ class java/lang/Throwable ] frame_type = 5 }
|
1.2.2.2 finally
中加了return
语句
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class FinallyDemo { public static void main(String[] args) { int j = test(); System.out.println(j); }
public static int test(){ int i = 0; try{ return 10; }finally { return 20; } } }
|
源代码对应的字节码:
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 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| { public com.basic.FinallyDemo(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aload_0 1: invokespecial #1 4: return LineNumberTable: line 6: 0 LocalVariableTable: Start Length Slot Name Signature 0 5 0 this Lcom/basic/FinallyDemo;
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=2, args_size=1 0: invokestatic #2 3: istore_1 4: getstatic #3 7: iload_1 8: invokevirtual #4 11: return LineNumberTable: line 8: 0 line 9: 4 line 10: 11 LocalVariableTable: Start Length Slot Name Signature 0 12 0 args [Ljava/lang/String; 4 8 1 j I
public static int test(); descriptor: ()I flags: ACC_PUBLIC, ACC_STATIC Code: stack=1, locals=3, args_size=0 0: iconst_0 1: istore_0 2: bipush 10 4: istore_1 5: bipush 20 7: ireturn 8: astore_2 9: bipush 20 11: ireturn Exception table: from to target type 2 5 8 any LineNumberTable: line 13: 0 line 15: 2 line 17: 5 LocalVariableTable: Start Length Slot Name Signature 2 10 0 i I StackMapTable: number_of_entries = 1 frame_type = 255 offset_delta = 8 locals = [ int ] stack = [ class java/lang/Throwable ] }
|
我们从字节码中可以看到,finally
中并没有捕捉异常,也没有抛出异常,所以此种写法对于程序而言非常危险。下面看一段错误代码,但是程序并没有抛出异常:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| public class FinallyDemo { public static void main(String[] args) { int j = test(); System.out.println(j); }
public static int test(){ int i = 1; try{ i = i / 0; return i; }finally { return i; } } }
|
运行结果:
所以,在写代码时,一定要主要不要写出这样的代码。
写在最后
欢迎大家关注鄙人的公众号【麦田里的守望者zhg】,让我们一起成长,谢谢。