Java 中有 goto 吗?

goto 是 Java 中的关键字, 但还处于保留状态, 在实际的开发中并不能使用. 本文列举了 Java 中的关键字以及引入时间, 同时讨论了和 goto 效果类似的 break label 的语法以及使用的 demo. 最后从将 demo 进行了反编译并逐条分析了 Java 字节码的执行, 得出的结论是 break label 底层比较简单就是一行 goto xx 的字节码指令. 在分析字节码的过程中重温了一下 Java 基于栈实现的执行引擎运行.

原文地址:Java 中有 goto 吗?

欢迎访问我的博客: blog.duhbb.com/

Java 关键字

下表中列举了 Java 中的关键字, 这些关键字都不可以作为标识符. constgo 是保留关键字, 虽然没有正式使用, 但是你也不能把它们作为标识符. true, falsenull 虽然看上去像关键字, 但是它们实际上是 literals, 它们也不能作为标识符使用.

abstract continue for new switch
assert*** default goto* package synchronized
boolean do if private this
break double implements protected throw
byte else import public throws
case enum**** instanceof return transient
catch extends int short try
char final interface static void
class finally long strictfp** volatile
const* float native super while

备注:

  • *: 未使用
  • **: 1.2 中引入
  • ***:1.4 中引入
  • ****:5.0 中引入

以上来自:Java Language Keywords

可见 Java 中确实有 goto, 但是是保留的关键字, 并不能实际使用.So, 我们不能 goto anywhere.

break label 用法

虽然 Java 中没有 C 语言中 goto 的那种用法, 但是有一个"类似"的.

语法

语法格式: break label, label 你可以自己定义, 只要不冲突就行.

break 语句可以终止带标签的语句的执行; 它并不会将控制流转移到标签上, 而是将控制流立即转移到了带标签语句的下一条语句中.

例子

first:
  for( int i = 0; i < 10; i++) {
    second:
      for(int j = 0; j < 5; j ++ ) {
        break xxx;
      }
  }

third:
  for( int a = 0; a < 10; a++) {

  }
复制代码
  • xxx 只能是 first 或者 second, 而不能是 third, 也就是只能 break 包裹 break 的语句; break third 会报编译错误.
  • break first 会跳出最外层的 for 循环, 而 break second 则会跳出内层的 for 循环, 外层的 for 循环继续.

查看字节码

源代码:Main.java

public class Main {
    public static void main(String[] args) {
        first:
        for (int i = 0; i < 10; i++) {
            second:
            for (int j = 0; j < 5; j++) {
                System.out.println("i = " + i + ", j = " + j);
                break first;
            }
        }

        third:
        for (int a = 0; a < 10; a++) {
            System.out.println(a);
        }
    }
}
复制代码

javap -v Main.class 反编译后的字节:

duhbb@debian:/mnt/data/IdeaProjects/test/target/classes$ javap -v Main.class 
Classfile /mnt/data/IdeaProjects/test/target/classes/Main.class
  Last modified 2022-6-8; size 913 bytes
  MD5 checksum f56881b622c0cfceb531278564352491
  Compiled from "Main.java"
public class Main
  minor version: 0
  major version: 52
  flags: ACC_PUBLIC, ACC_SUPER
Constant pool:
   #1 = Methodref          #13.#32        // java/lang/Object."<init>":()V
   #2 = Fieldref           #33.#34        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Class              #35            // java/lang/StringBuilder
   #4 = Methodref          #3.#32         // java/lang/StringBuilder."<init>":()V
   #5 = String             #36            // i =
   #6 = Methodref          #3.#37         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #7 = Methodref          #3.#38         // java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
   #8 = String             #39            // , j =
   #9 = Methodref          #3.#40         // java/lang/StringBuilder.toString:()Ljava/lang/String;
  #10 = Methodref          #41.#42        // java/io/PrintStream.println:(Ljava/lang/String;)V
  #11 = Methodref          #41.#43        // java/io/PrintStream.println:(I)V
  #12 = Class              #44            // Main
  #13 = Class              #45            // java/lang/Object
  #14 = Utf8               <init>
  #15 = Utf8               ()V
  #16 = Utf8               Code
  #17 = Utf8               LineNumberTable
  #18 = Utf8               LocalVariableTable
  #19 = Utf8               this
  #20 = Utf8               LMain;
  #21 = Utf8               main
  #22 = Utf8               ([Ljava/lang/String;)V
  #23 = Utf8               j
  #24 = Utf8               I
  #25 = Utf8               i
  #26 = Utf8               a
  #27 = Utf8               args
  #28 = Utf8               [Ljava/lang/String;
  #29 = Utf8               StackMapTable
  #30 = Utf8               SourceFile
  #31 = Utf8               Main.java
  #32 = NameAndType        #14:#15        // "<init>":()V
  #33 = Class              #46            // java/lang/System
  #34 = NameAndType        #47:#48        // out:Ljava/io/PrintStream;
  #35 = Utf8               java/lang/StringBuilder
  #36 = Utf8               i =
  #37 = NameAndType        #49:#50        // append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
  #38 = NameAndType        #49:#51        // append:(I)Ljava/lang/StringBuilder;
  #39 = Utf8               , j =
  #40 = NameAndType        #52:#53        // toString:()Ljava/lang/String;
  #41 = Class              #54            // java/io/PrintStream
  #42 = NameAndType        #55:#56        // println:(Ljava/lang/String;)V
  #43 = NameAndType        #55:#57        // println:(I)V
  #44 = Utf8               Main
  #45 = Utf8               java/lang/Object
  #46 = Utf8               java/lang/System
  #47 = Utf8               out
  #48 = Utf8               Ljava/io/PrintStream;
  #49 = Utf8               append
  #50 = Utf8               (Ljava/lang/String;)Ljava/lang/StringBuilder;
  #51 = Utf8               (I)Ljava/lang/StringBuilder;
  #52 = Utf8               toString
  #53 = Utf8               ()Ljava/lang/String;
  #54 = Utf8               java/io/PrintStream
  #55 = Utf8               println
  #56 = Utf8               (Ljava/lang/String;)V
  #57 = Utf8               (I)V
{
  public Main();
    descriptor: ()V
    flags: ACC_PUBLIC
    Code:
      stack=1, locals=1, args_size=1
         0: aload_0
         1: invokespecial #1                  // Method java/lang/Object."<init>":()V
         4: return
      LineNumberTable:
        line 5: 0
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
            0       5     0  this   LMain;

  public static void main(java.lang.String[]);
    descriptor: ([Ljava/lang/String;)V
    flags: ACC_PUBLIC, ACC_STATIC
    Code:
      stack=3, locals=3, args_size=1
         0: iconst_0                          // 将常数 0 压入操作数栈
         1: istore_1                          // 将操作数出栈, 并给到本地变量表的第 1 位置的变量, 也就是 i
         2: iload_1                           // 就是变量表中第 1 个位置的 i 压栈到操作数栈顶
         3: bipush        10                  // 把常量 10 压入到操作数栈中
         5: if_icmpge     58                  // 进行比较, 如果为 false 则跳到 58 行 (我猜的)
         8: iconst_0                          // 将常数 0 压入操作数栈
         9: istore_2                          // 将其弹给并给到本地变量表的第 1 位置的变量, 也就是 j
        10: iload_2                           // 就是变量表中第 2 个位置的 i 压栈到操作数栈顶
        11: iconst_5                          // 把常量 5 压入到操作数栈中
        12: if_icmpge     52                  // 进行一通比较, 如果为 false 则跳到 52 行执行
        15: getstatic     #2                  // 获取类的静态字段 Field java/lang/System.out:Ljava/io/PrintStream;
        18: new           #3                  // 创建一个对象, 对象是通过字节索引在常量池中定位的 class java/lang/StringBuilder
        21: dup                               // 复制在栈顶的值
        22: invokespecial #4                  // 调用 StringBuilder 的方法进行初始化 Method java/lang/StringBuilder."<init>":()V
        25: ldc           #5                  // 字符串入栈 String i =
        27: invokevirtual #6                  // 调用 StringBuilder 的 append 方法 Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        30: iload_1                           // 就是变量表中第 1 个位置的 i 压栈到操作数栈顶 这里就是 i 了, 因为准备把 i 拼接到后面去
        31: invokevirtual #7                  // 调用 StringBuilder 的 append Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        34: ldc           #8                  // 字符串入栈 String , j =
        36: invokevirtual #6                  // 调用 StringBuilder 的 append 方法 Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
        39: iload_2                           // 就是变量表中第 2 个位置的 j 压栈到操作数栈顶 这里就是 j 了, 因为准备把 j 拼接到后面去
        40: invokevirtual #7                  // 调用 StringBuilder 的 append 方法 Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
        43: invokevirtual #9                  // 调用 StringBuilder 的 toString 方法 Method java/lang/StringBuilder.toString:()Ljava/lang/String;
        46: invokevirtual #10                 // 调用 PrintStream 的 println 方法 Method java/io/PrintStream.println:(Ljava/lang/String;)V
        49: goto          58                  // break first 毫无征兆的跳到了 58 行
        52: iinc          1, 1
        55: goto          2
        58: iconst_0                         // 开始了 third 部分的循环
        59: istore_1
        60: iload_1
        61: bipush        10
        63: if_icmpge     79
        66: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
        69: iload_1
        70: invokevirtual #11                 // Method java/io/PrintStream.println:(I)V
        73: iinc          1, 1
        76: goto          60
        79: return
      LineNumberTable:
        line 8: 0
        line 10: 8
        line 11: 15
        line 12: 49
        line 8: 52
        line 17: 58
        line 18: 66
        line 17: 73
        line 20: 79
      LocalVariableTable:
        Start  Length  Slot  Name   Signature
           10      42     2     j   I
            2      56     1     i   I
           60      19     1     a   I
            0      80     0  args   [Ljava/lang/String;
      StackMapTable: number_of_entries = 6
        frame_type = 252 /* append */
          offset_delta = 2
          locals = [ int ]
        frame_type = 252 /* append */
          offset_delta = 7
          locals = [ int ]
        frame_type = 250 /* chop */
          offset_delta = 41
        frame_type = 250 /* chop */
          offset_delta = 5
        frame_type = 252 /* append */
          offset_delta = 1
          locals = [ int ]
        frame_type = 250 /* chop */
          offset_delta = 18
}
SourceFile: "Main.java"
复制代码

常量入栈指令有 iconst, bipush, sipush, ldc, ldc2_w 分别对应不同的使用场景, 以下两个表简单总结了使用场景:

  • 八大基本类型场景表

file

  • 指令场景表

file

上面的两张图片来自于: Java 逆向基础之常量入栈指令

为啥要这么麻烦, 搞出这么多不通的入栈指令呢? 连 -1~5 这样的都要搞出来?

  • bipush 8 就是把 8 压到操作数栈中.
  • istore_1 就是操作数栈出栈, 存到本地变量表的第 1 位置;
  • iload_1, 就是变量表中第一个位置的 i 压栈到操作数栈顶
  • iinc 1 by 1, 就是变量表中第一个位置的
  • istore_1, 又把栈顶的 8 存回了变量表中的 i
  • dup 关于 dup 指令的作用, 在《深入理解 Java 虚拟机》这本书中是这么描述的. 这是一个操作数栈管理指令, 负责复制栈顶 (注意, 这个栈指的是操作数栈) 一个或者两个数值并将复制值或双份的复制值重新压人栈顶.

简单理解就是给操作数栈栈顶的元素弄了一个备份. 那么为什么要进行备份呢? 一开始是 new 指令在堆上分配了内存并向操作数栈压入了指向这段内存的引用, 之后 dup 指令又备份了一份, 那么操作数栈顶就有两个, 再后是调用 invokespecial #18 指令进行初始化, 此时会消耗一个引用作为传给构造器的 this 参数, 那么还剩下一个引用, 会被 astore_1 指令存储到局部变量表中.dup 来自: Java 字节码 new 之后为什么会有 dup

  • invokespecial 的参数哪儿来的?
  • ldc: 则是从常量池中将常量

Java 字节吗概述

Java 虚拟机采用基于栈的架构, 其指令由操作码和操作数组成.

操作码: 一个字节长度 (0~255), 意味着指令集的操作码个数不能操作 256 条. 操作数: 一条指令可以有零或者多个操作数, 且操作数可以是 1 个或者多个字节. 编译后的代码没有采用操作数长度对齐方式, 比如 16 位无符号整数需使用两个字节储存 (假设为 byte1 和 byte2), 那么真实值是 (byte1 << 8) | byte2. 放弃操作数对齐操作数对齐方案:

优势: 可以省略很多填充和间隔符号, 从而减少数据量, 具有更高的传输效率;Java 起初就是为了面向网络, 智能家具而设计的, 故更加注重传输效率. 劣势: 运行时从字节码里构建出具体数据结构, 需要花费部分 CPU 时间, 从而导致解释执行字节码会损失部分性能.

指令介绍

大多数指令包含了其操作所对应的数据类型信息, 比如 iload, 表示从局部变量表中加载 int 型的数据到操作数栈;而 fload 表示加载 float 型数据到操作数栈. 由于操作码长度只有 1Byte, 因此 Java 虚拟机的指令集对于特定操作只提供有限的类型相关指令, 并非为每一种数据类型都有相应的操作指令. 必要时, 有些指令可用于将不支持的类型转换为可被支持的类型.

对于 byte,short,char,boolean 类型, 往往没有单独的操作码, 通过编译器在编译期或者运行期将其扩展. 对于 byte,short 采用带符号扩展,chart,boolean 采用零位扩展. 相应的数组也是采用类似的扩展方式转换为 int 类型的字节码来处理. 下面分门别类来介绍 Java 虚拟机指令, 都以 int 类型的数据操作为例.

栈是指操作数栈.

这两个部分来自:Jvm 系列 3—字节码指令

字节码指令介绍:

指令 含义
aaload load onto the stack a reference from an array
aastore store a reference in an array
aconst_null push a null reference onto the stack
aload load a reference onto the stack from a local variable #index
aload_0 load a reference onto the stack from local variable 0
aload_1 load a reference onto the stack from local variable 1
aload_2 load a reference onto the stack from local variable 2
aload_3 load a reference onto the stack from local variable 3
anewarray create a new array of references of length count and component type identified by the class reference index (indexbyte1 << 8 | indexbyte2) in the constant pool
areturn return a reference from a method
arraylength get the length of an array
astore store a reference into a local variable #index
astore_0 store a reference into local variable 0
astore_1 store a reference into local variable 1
astore_2 store a reference into local variable 2
astore_3 store a reference into local variable 3
athrow throws an error or exception (notice that the rest of the stack is cleared, leaving only a reference to the Throwable)
baload load a byte or Boolean value from an array
bastore store a byte or Boolean value into an array
bipush push a byte onto the stack as an integer value
breakpoint reserved for breakpoints in Java debuggers; should not appear in any class file
caload load a char from an array
castore store a char into an array
checkcast checks whether an objectref is of a certain type, the class reference of which is in the constant pool at index (indexbyte1 << 8 | indexbyte2)
d2f convert a double to a float
d2i convert a double to an int
d2l convert a double to a long
dadd add two doubles
daload load a double from an array
dastore store a double into an array
dcmpg compare two doubles, 1 on NaN
dcmpl compare two doubles, -1 on NaN
dconst_0 push the constant 0.0 (a double) onto the stack
dconst_1 push the constant 1.0 (a double) onto the stack
ddiv divide two doubles
dload load a double value from a local variable #index
dload_0 load a double from local variable 0
dload_1 load a double from local variable 1
dload_2 load a double from local variable 2
dload_3 load a double from local variable 3
dmul multiply two doubles
dneg negate a double
drem get the remainder from a division between two doubles
dreturn return a double from a method
dstore store a double value into a local variable #index
dstore_0 store a double into local variable 0
dstore_1 store a double into local variable 1
dstore_2 store a double into local variable 2
dstore_3 store a double into local variable 3
dsub subtract a double from another
dup duplicate the value on top of the stack
dup_x1 insert a copy of the top value into the stack two values from the top. value1 and value2 must not be of the type double or long.
dup_x2 insert a copy of the top value into the stack two (if value2 is double or long it takes up the entry of value3, too) or three values (if value2 is neither double nor long) from the top
dup2 duplicate top two stack words (two values, if value1 is not double nor long; a single value, if value1 is double or long)
dup2_x1 duplicate two words and insert beneath third word (see explanation above)
dup2_x2 duplicate two words and insert beneath fourth word
f2d convert a float to a double
f2i convert a float to an int
f2l convert a float to a long
fadd add two floats
faload load a float from an array
fastore store a float in an array
fcmpg compare two floats, 1 on NaN
fcmpl compare two floats, -1 on NaN
fconst_0 push 0.0f on the stack
fconst_1 push 1.0f on the stack
fconst_2 push 2.0f on the stack
fdiv divide two floats
fload load a float value from a local variable #index
fload_0 load a float value from local variable 0
fload_1 load a float value from local variable 1
fload_2 load a float value from local variable 2
fload_3 load a float value from local variable 3
fmul multiply two floats
fneg negate a float
frem get the remainder from a division between two floats
freturn return a float
fstore store a float value into a local variable #index
fstore_0 store a float value into local variable 0
fstore_1 store a float value into local variable 1
fstore_2 store a float value into local variable 2
fstore_3 store a float value into local variable 3
fsub subtract two floats
getfield get a field value of an object objectref, where the field is identified by field reference in the constant pool index (indexbyte1 << 8 | indexbyte2)
getstatic get a static field value of a class, where the field is identified by field reference in the constant pool index (indexbyte1 << 8 | indexbyte2)
goto goes to another instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
goto_w goes to another instruction at branchoffset (signed int constructed from unsigned bytes branchbyte1 << 24 | branchbyte2 << 16 | branchbyte3 << 8 | branchbyte4)
i2b convert an int into a byte
i2c convert an int into a character
i2d convert an int into a double
i2f convert an int into a float
i2l convert an int into a long
i2s convert an int into a short
iadd add two ints
iaload load an int from an array
iand perform a bitwise AND on two integers
iastore store an int into an array
iconst_m1 load the int value −1 onto the stack
iconst_0 load the int value 0 onto the stack
iconst_1 load the int value 1 onto the stack
iconst_2 load the int value 2 onto the stack
iconst_3 load the int value 3 onto the stack
iconst_4 load the int value 4 onto the stack
iconst_5 load the int value 5 onto the stack
idiv divide two integers
if_acmpeq if references are equal, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
if_acmpne if references are not equal, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
if_icmpeq if ints are equal, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
if_icmpge if value1 is greater than or equal to value2, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
if_icmpgt if value1 is greater than value2, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
if_icmple if value1 is less than or equal to value2, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
if_icmplt if value1 is less than value2, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
if_icmpne if ints are not equal, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
ifeq if value is 0, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
ifge if value is greater than or equal to 0, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
ifgt if value is greater than 0, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
ifle if value is less than or equal to 0, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
iflt if value is less than 0, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
ifne if value is not 0, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
ifnonnull if value is not null, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
ifnull if value is null, branch to instruction at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2)
iinc increment local variable #index by signed byte const
iload load an int value from a local variable #index
iload_0 load an int value from local variable 0
iload_1 load an int value from local variable 1
iload_2 load an int value from local variable 2
iload_3 load an int value from local variable 3
impdep1 reserved for implementation-dependent operations within debuggers; should not appear in any class file
impdep2 reserved for implementation-dependent operations within debuggers; should not appear in any class file
imul multiply two integers
ineg negate int
instanceof determines if an object objectref is of a given type, identified by class reference index in constant pool (indexbyte1 << 8 | indexbyte2)
invokedynamic invokes a dynamic method and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 | indexbyte2)
invokeinterface invokes an interface method on object objectref and puts the result on the stack (might be void); the interface method is identified by method reference index in constant pool (indexbyte1 << 8 | indexbyte2)
invokespecial invoke instance method on object objectref and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 | indexbyte2)
invokestatic invoke a static method and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 | indexbyte2)
invokevirtual invoke virtual method on object objectref and puts the result on the stack (might be void); the method is identified by method reference index in constant pool (indexbyte1 << 8 | indexbyte2)
ior bitwise int OR
irem logical int remainder
ireturn return an integer from a method
ishl int shift left
ishr int arithmetic shift right
istore store int value into variable #index
istore_0 store int value into variable 0
istore_1 store int value into variable 1
istore_2 store int value into variable 2
istore_3 store int value into variable 3
isub int subtract
iushr int logical shift right
ixor int xor
jsr† jump to subroutine at branchoffset (signed short constructed from unsigned bytes branchbyte1 << 8 | branchbyte2) and place the return address on the stack
jsr_w† jump to subroutine at branchoffset (signed int constructed from unsigned bytes branchbyte1 << 24 | branchbyte2 << 16 | branchbyte3 << 8 | branchbyte4) and place the return address on the stack
l2d convert a long to a double
l2f convert a long to a float
l2i convert a long to a int
ladd add two longs
laload load a long from an array
land bitwise AND of two longs
lastore store a long to an array
lcmp push 0 if the two longs are the same, 1 if value1 is greater than value2, -1 otherwise
lconst_0 push 0L (the number zero with type long) onto the stack
lconst_1 push 1L (the number one with type long) onto the stack
ldc push a constant #index from a constant pool (String, int, float, Class, java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, or a dynamically-computed constant) onto the stack
ldc_w push a constant #index from a constant pool (String, int, float, Class, java.lang.invoke.MethodType, java.lang.invoke.MethodHandle, or a dynamically-computed constant) onto the stack (wide index is constructed as indexbyte1 << 8 | indexbyte2)
ldc2_w push a constant #index from a constant pool (double, long, or a dynamically-computed constant) onto the stack (wide index is constructed as indexbyte1 << 8 | indexbyte2)
ldiv divide two longs
lload load a long value from a local variable #index
lload_0 load a long value from a local variable 0
lload_1 load a long value from a local variable 1
lload_2 load a long value from a local variable 2
lload_3 load a long value from a local variable 3
lmul multiply two longs
lneg negate a long
lookupswitch a target address is looked up from a table using a key and execution continues from the instruction at that address
lor bitwise OR of two longs
lrem remainder of division of two longs
lreturn return a long value
lshl bitwise shift left of a long value1 by int value2 positions
lshr bitwise shift right of a long value1 by int value2 positions
lstore store a long value in a local variable #index
lstore_0 store a long value in a local variable 0
lstore_1 store a long value in a local variable 1
lstore_2 store a long value in a local variable 2
lstore_3 store a long value in a local variable 3
lsub subtract two longs
lushr bitwise shift right of a long value1 by int value2 positions, unsigned
lxor bitwise XOR of two longs
monitorenter enter monitor for object ("grab the lock" – start of synchronized() section)
monitorexit exit monitor for object ("release the lock" – end of synchronized() section)
multianewarray create a new array of dimensions dimensions of type identified by class reference in constant pool index (indexbyte1 << 8 | indexbyte2); the sizes of each dimension is identified by count1, [count2, etc.]
new create new object of type identified by class reference in constant pool index (indexbyte1 << 8 | indexbyte2)
newarray create new array with count elements of primitive type identified by atype
nop perform no operation
pop discard the top value on the stack
pop2 discard the top two values on the stack (or one value, if it is a double or long)
putfield set field to value in an object objectref, where the field is identified by a field reference index in constant pool (indexbyte1 << 8 | indexbyte2)
putstatic set static field to value in a class, where the field is identified by a field reference index in constant pool (indexbyte1 << 8 | indexbyte2)
ret† continue execution from address taken from a local variable #index (the asymmetry with jsr is intentional)
return return void from method
saload load short from array
sastore store short to array
sipush push a short onto the stack as an integer value
swap swaps two top words on the stack (note that value1 and value2 must not be double or long)
tableswitch continue execution from an address in the table at offset index
wide execute opcode, where opcode is either iload, fload, aload, lload, dload, istore, fstore, astore, lstore, dstore, or ret, but assume the index is 16 bit; or execute iinc, where the index is 16 bits and the constant to increment by is a signed 16 bit short
(no name) these values are currently unassigned for opcodes and are reserved for future use

这个是我从 List of Java bytecode instructions 这里扣下来的.

待完成

字节码指令翻译.

原文地址:Java 中有 goto 吗?

欢迎访问我的博客: blog.duhbb.com/

猜你喜欢

转载自juejin.im/post/7106822872353472542