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 /* same */ } 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 /* same_locals_1_stack_item */ stack = [ class java/lang/NullPointerException ] frame_type = 70 /* same_locals_1_stack_item */ stack = [ class java/nio/file/FileSystemAlreadyExistsException ] frame_type = 3 /* same */ }
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 /* same */ }
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 /* same_locals_1_stack_item */ stack = [ class java/lang/Throwable ] frame_type = 5 /* same */ }
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】,让我们一起成长,谢谢。