所以有一天,当我查看维基百科页面上的Java字节码时,我遇到了这个例子:
考虑以下Java代码:
outer:
for (int i = 2; i < 1000; i++) {
for (int j = 2; j < i; j++) {
if (i % j == 0)
continue outer;
}
System.out.println (i);
}
Java编译器可能会将上面的Java代码转换为字节代码,如下所示,假设上面的代码放在一个方法中:
0: iconst_2
1: istore_1
2: iload_1
3: sipush 1000
6: if_icmpge 44
9: iconst_2
10: istore_2
11: iload_2
12: iload_1
13: if_icmpge 31
16: iload_1
17: iload_2
18: irem
19: ifne 25
22: goto 38
25: iinc 2, 1
28: goto 11
31: getstatic #84; //Field java/lang/System.out:Ljava/io/PrintStream;
34: iload_1
35: invokevirtual #85; //Method java/io/PrintStream.println:(I)V
38: iinc 1, 1
41: goto 2
44: return
我注意到goto小字出现了几次,在检查JVM规范时是有效的。 我的问题是为什么? GOTO是Java中保留但不可用的关键字,那么为什么当我们编写和编译java代码时,它似乎是用goto编译回来的。我想知道这只是在较低级别的编程中始终完成的事情,或者是否因为JVM被信任更有效地使用goto字。最后,我很好奇为什么goto被认为是一种不好的做法,它在java代码中被禁止,但似乎在编译时会直接回到你的代码中。
答案 0 :(得分:12)
Java结构化编程功能(例如循环(for
/ while
)在字节码级别使用条件分支(IF..
)和无条件跳转(GOTO
)指令实现
break
或continue
也被视为足够有用&amp;在结构化编程中合法,Java语言具有这些功能(中断/继续标记)。
在JVM /字节码级别,这些也是用GOTO
实现的。
见:
答案 1 :(得分:8)
您应该区分语言关键字goto
和字节码或汇编指令goto
。
在高级代码中使用goto
跳转是不好的做法,例如在C中。因此在Java中不允许这样做。
The original Edsger W. Dijkstra paper on goto
在编译代码中使用无条件跳转指令 goto
完全没问题。它由编译器放在那里,它不会忘记跳转代码的含义,包括数据初始化,释放内存等。
答案 2 :(得分:7)
任何微处理器的硬件部分只知道它需要从存储器地址开始连续执行每条指令 - 它甚至不知道停止执行指令的存储器地址。
汇编语言是从“命令”到“二进制微指令”的非常薄的转换器。 “命令”列表中不包括控制流语句,只有跳转指令(简单跳转或条件跳转),就是它(好的,有无条件无限循环和条件循环的指令)。
因此,使用这些跳转指令实现了C等高级语言中可用的控制流语句,因为没有其他方法可以实现它们。因此,C中的goto
被编译成二进制指令,只是一个简单的无条件跳转指令。
许多不同的硬件架构对于“二进制微指令”和不同的指令集具有不同的标准/格式。 JVM有自己的标准和自己的指令集。
这允许Java编译器始终输出相同的指令,无论可执行文件将在什么硬件体系结构上运行;将指令从自己的标准转换为当前机器的标准是JVM的工作。
因此,实质上JVM字节码是“java虚拟机”的“汇编语言”。这意味着它没有控制流程指令。它具有无条件跳转指令(恰好命名为goto
)。
break
和continue
恰好以jump
(或goto
)的形式实施。问题的关键在于,如果你使用更高级别的语言,你可能希望避免使用goto
即使它可用(如在C中),并且会使用更易读的控制结构。
有一些特殊情况(例如C表示例),即使是尊重所有“最佳编码实践”的程序员也会使用goto
作为协程实现。
为了更好或更可靠的性能而牺牲编码标准的其他一些例子是当内核开发人员具有特定于体系结构的汇编代码时(C允许您编写汇编指令)。
答案 3 :(得分:4)
字节码是虚拟机的某种汇编语言。在机器语言中使用跳转指令是很常见的。 goto是一个无条件的跳转指令。
Java编译器将方法体内的几乎所有控制流语句转换为goto指令。
goto中可能保留了goto关键字,以保留将其添加到更高版本的选项,以防它的存在本来就是关键。从机器的角度来看,goto实际上没有任何问题。它的声誉很差,因为它允许编写人类难以阅读的代码。 Java语言允许您使用break并继续使用标签作为goto的替代。
答案 4 :(得分:2)
goto
在机器级别很好,java编译器不编写代码,它只将代码从java源代码转换为字节码。
对于编写代码的人来说这是一个不同的故事,goto指令在很多goto跳转之后难以阅读和分析并且代码是混乱。这就是为什么人们应该使用OO概念而不是跳转指令。
答案 5 :(得分:0)
您正在查看JVM的等效机器代码。 Java中是否允许goto
与字节码中是否允许它无关,就像JVM字节码中不允许指针一样,但JVM肯定会将字节码编译或解释为机器码确实使用了指针。
答案 6 :(得分:0)
字节码不是Java,其他语言的程序,如Groovy,可以编译成字节码,你可以使用像BCEL这样的工具直接编写字节码。至于goto,你不能没有它在低级别。
答案 7 :(得分:0)
goto
关键字和goto
指令不一样。唯一常见的是名字。在字节码的情况下,它只是JUMP
(JMP
); goto
被认为是编程/编码中的一种不好的做法,因为它实现了'spagetti'代码并且使代码的可读性更差。答案 8 :(得分:0)
编程中的Goto语句是一种方式语句,其中函数调用是双向切换,它将返回到代码的被调用部分。
要使用那些只有字节码,请在其中使用goto。如果允许用户使用goto意味着我们可能以低效的方式使用它(比如无条件的goto语句),它永远不会让程序终止。
Jvm是如此聪明,永远不会让程序无限运行。