时间:2022-12-15 10:32:03 | 栏目:JAVA代码 | 点击:次
当一个方法开始执行后,只有两种方式可以退出这个方法。
一般来说,方法正常退出时,调用者的程序计数器的值可以作为返回地址,栈帧中很可能会保存这个计数器值。而方法异常退出时,返回地址是要通过异常处理器表来确定的,栈帧中一般不会保存这部分信息。
方法退出的过程实际上就等同于把当前栈帧出栈,因此退出时可能执行的操作有:恢复上层方法的局部变量表和操作数栈,把返回值(如果有的话)压入调用者栈帧的操作数栈中,调整程序计数器的值以指向方法调用指令后面的一条指令等。
虚拟机会使用针对每种返回类型的操作来返回,返回值将从操作数栈出栈并且入栈到调用方法的方法栈帧中,当前栈帧出栈,被调用方法的栈帧变成当前栈帧,程序计数器将重置为调用这个方法的指令的下一条指令。
存放调用该方法的 pc 寄存器的值。
一个方法的结束,有两种方式。
无论通过哪种方式退出,在方法退出后都返回到该方法被调用的位置。方法正常退出时,调用者的pc计数器的值作为返回地址,即调用该方法的指令的下一条指令的地址。而通过异常退出的,返回地址是要通过异常表来确定,栈帧中一般不会保存这部分信息。
当一个方法开始执行后,只有两种方式可以退出这个方法。
a 执行引擎遇到任意一个方法返回的字节码指令(return),会有返回值传递给上层的方法调用者,简称正常完成出口。
b 在方法执行过程中遇到异常(Exception),并且这个异常没有在方法内进行处理,也就是只要在本方法的异常表中没有搜索到匹配的异常处理器,就会导致方法退出,简称异常完成出口。
方法执行过程中,抛出异常时的异常处理,存储在一个异常处理表,方便在发生异常的时候找到处理异常的代码
import java.io.FileReader; import java.io.IOException; import java.util.Date; /** * 返回指令包含ireturn(当返回值是 boolean、byte、char、short和int类型时使用)、 * lreturn、freturn、dreturn以及areturn, * 另外还有一个return指令供声明为void的方法、 * 实例初始化方法、类和接口的初始化方法使用。 */ public class ReturnAddressTest { public boolean methodBoolean() { return false; } public byte methodByte() { return 0; } public short methodShort() { return 0; } public char methodChar() { return 'a'; } public int methodInt() { return 0; } public long methodLong() { return 0L; } public float methodFloat() { return 0.0f; } public double methodDouble() { return 0.0; } public String methodString() { return null; } public Date methodDate() { return null; } public void methodVoid() { } static { int i = 10; } public void method2() { methodVoid(); try { method1(); } catch (IOException e) { e.printStackTrace(); } } public void method1() throws IOException { FileReader fis = new FileReader("atguigu.txt"); char[] cBuffer = new char[1024]; int len; while ((len = fis.read(cBuffer)) != -1) { String str = new String(cBuffer, 0, len); System.out.println(str); } fis.close(); } }
public boolean methodBoolean(); descriptor: ()Z flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 15: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public byte methodByte(); descriptor: ()B flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 19: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public short methodShort(); descriptor: ()S flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 23: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public char methodChar(); descriptor: ()C flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: bipush 97 2: ireturn LineNumberTable: line 27: 0 LocalVariableTable: Start Length Slot Name Signature 0 3 0 this Lcom/atguigu/java3/ReturnAddressTest; public int methodInt(); descriptor: ()I flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: iconst_0 1: ireturn LineNumberTable: line 31: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public long methodLong(); descriptor: ()J flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: lconst_0 1: lreturn LineNumberTable: line 35: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public float methodFloat(); descriptor: ()F flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: fconst_0 1: freturn LineNumberTable: line 39: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public double methodDouble(); descriptor: ()D flags: ACC_PUBLIC Code: stack=2, locals=1, args_size=1 0: dconst_0 1: dreturn LineNumberTable: line 43: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public java.lang.String methodString(); descriptor: ()Ljava/lang/String; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aconst_null 1: areturn LineNumberTable: line 47: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public java.util.Date methodDate(); descriptor: ()Ljava/util/Date; flags: ACC_PUBLIC Code: stack=1, locals=1, args_size=1 0: aconst_null 1: areturn LineNumberTable: line 51: 0 LocalVariableTable: Start Length Slot Name Signature 0 2 0 this Lcom/atguigu/java3/ReturnAddressTest; public void methodVoid(); descriptor: ()V flags: ACC_PUBLIC Code: stack=0, locals=1, args_size=1 0: return LineNumberTable: line 56: 0 LocalVariableTable: Start Length Slot Name Signature 0 1 0 this Lcom/atguigu/java3/ReturnAddressTest; public void method2(); descriptor: ()V flags: ACC_PUBLIC Code: stack=1, locals=2, args_size=1 0: aload_0 1: invokevirtual #2 // Method methodVoid:()V 4: aload_0 5: invokevirtual #3 // Method method1:()V 8: goto 16 11: astore_1 12: aload_1 13: invokevirtual #5 // Method java/io/IOException.printStackTrace:()V 16: return Exception table: from to target type 4 8 11 Class java/io/IOException LineNumberTable: line 63: 0 line 65: 4 line 68: 8 line 66: 11 line 67: 12 line 69: 16 LocalVariableTable: Start Length Slot Name Signature 12 4 1 e Ljava/io/IOException; 0 17 0 this Lcom/atguigu/java3/ReturnAddressTest; StackMapTable: number_of_entries = 2 frame_type = 75 /* same_locals_1_stack_item */ stack = [ class java/io/IOException ] frame_type = 4 /* same */ public void method1() throws java.io.IOException; descriptor: ()V flags: ACC_PUBLIC Code: stack=5, locals=5, args_size=1 0: new #6 // class java/io/FileReader 3: dup 4: ldc #7 // String atguigu.txt 6: invokespecial #8 // Method java/io/FileReader."<init>":(Ljava/lang/String;)V 9: astore_1 10: sipush 1024 13: newarray char 15: astore_2 16: aload_1 17: aload_2 18: invokevirtual #9 // Method java/io/FileReader.read:([C)I 21: dup 22: istore_3 23: iconst_m1 24: if_icmpeq 50 27: new #10 // class java/lang/String 30: dup 31: aload_2 32: iconst_0 33: iload_3 34: invokespecial #11 // Method java/lang/String."<init>":([CII)V 37: astore 4 39: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream; 42: aload 4 44: invokevirtual #13 // Method java/io/PrintStream.println:(Ljava/lang/String;)V 47: goto 16 50: aload_1 51: invokevirtual #14 // Method java/io/FileReader.close:()V 54: return LineNumberTable: line 72: 0 line 73: 10 line 75: 16 line 76: 27 line 77: 39 line 78: 47 line 79: 50 line 80: 54 LocalVariableTable: Start Length Slot Name Signature 39 8 4 str Ljava/lang/String; 0 55 0 this Lcom/atguigu/java3/ReturnAddressTest; 10 45 1 fis Ljava/io/FileReader; 16 39 2 cBuffer [C 23 32 3 len I StackMapTable: number_of_entries = 2 frame_type = 253 /* append */ offset_delta = 16 locals = [ class java/io/FileReader, class "[C" ] frame_type = 252 /* append */ offset_delta = 33 locals = [ int ] Exceptions: throws java.io.IOException static {}; descriptor: ()V flags: ACC_STATIC Code: stack=1, locals=1, args_size=0 0: bipush 10 2: istore_0 3: return LineNumberTable: line 59: 0 line 60: 3 LocalVariableTable: Start Length Slot Name Signature }
可以观察一下各种方法的 return 字节码指令到底是什么。
体会一下异常表。
Exception table:
from to target type
4 8 11 Class java/io/IOException
本质上,方法的退出就是当前栈帧出栈的过程。此时,需要恢复上层方法的局部变量表、操作数栈、将返回值压入调用者栈帧的操作数栈、设置PC寄存器值等,让调用者方法继续执行下去。
正常完成出口和异常完成出口的区别在于:通过异常完成出口退出的不会给他的上层调用者产生任何的返回值。
栈帧中还允许携带与 Java 虚拟机实现相关的一些附加信息。例如:对程序调试提供支持的信息。