21

所以前几天我在查看 Java 字节码的 wikipedia 页面时遇到了这个例子:

考虑以下 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 代码中被禁止,但在编译时似乎直接放回你的代码中。

4

9 回答 9

15

Java 结构化编程功能(例如循环 ( for/ while))在字节码级别通过条件分支 ( IF..) 和无条件跳转 ( GOTO) 指令实现。

breakcontinue外部循环在结构化编程中也被认为足够有用和合法,Java 语言具有这些特性(中断/继续标记)。

在 JVM/字节码级别,这些也是用GOTO.

看:

于 2013-11-15T09:01:04.027 回答
8

您应该区分语言关键字goto和字节码或汇编指令goto

在高级代码中使用goto跳转是不好的做法,比如在 C 中。因此在 Java 中是不允许的。

goto 上的 Edsger W. Dijkstra 原始论文

在编译代码中使用无条件跳转指令 goto是完全可以的。它由编译器放在那里,它不会忘记在代码中跳转的含义,包括数据初始化、释放内存等。

于 2013-11-15T09:02:24.513 回答
7

一般背景资料:

任何微处理器的硬件部分只知道它需要从一个内存地址开始连续执行每条指令——它甚至不知道在哪个内存地址停止执行指令。

汇编语言是一个从“命令”到“二进制微指令”的非常简单的转换器。“命令”列表根本不包括控制流语句,您所拥有的只是跳转指令(简单跳转或条件跳转),就是这样(好吧,有一条用于无条件无限循环和条件循环的指令)。

因此,在高级语言(如 C)中可用的控制流语句是使用这些跳转指令实现的,因为没有其他方法可以实现它们。正如goto在 C 中发生的那样,它被编译成二进制指令,只是一个简单的无条件跳转指令。

Java 和 JVM 原理:

许多不同的硬件架构对于“二进制微指令”和不同的指令集具有不同的标准/格式。JVM 有自己的标准和自己的指令集。

这允许 Java 编译器始终输出相同的指令,无论可执行文件在何种硬件架构上运行;将指令从它自己的标准翻译成当前机器的标准是 JVM 的工作。

所以本质上JVM字节码是“java虚拟机”的“汇编语言”。这意味着它没有控制流指令。它有无条件跳转指令(恰好被命名为goto)。

break并且continue在最低级别恰好被实现为jump(或goto)。goto问题的关键是,如果您使用更高级别的语言,即使它可用(如在 C 中),您也希望避免使用,并且会使用更具可读性的控制结构。

有一些特殊情况(例如在 C 中),即使是尊重所有“最佳编码实践”的程序员也会使用goto例如协程实现。

其他一些为了更好或更可靠的性能而牺牲编码标准的例子是内核开发人员拥有特定于体系结构的汇编代码(C 允许您编写汇编指令)。

于 2013-11-15T09:48:21.127 回答
4

字节码是虚拟机的某种汇编语言。机器语言中有跳转指令是很常见的。goto 是无条件跳转指令。

Java 编译器将方法体内的几乎所有控制流语句都翻译成 goto 指令。

goto 关键字可能在 Java 中保留,以保留将其添加到更高版本的选项,以防它的存在变得至关重要。从机器的角度来看,goto 实际上没有任何问题。它的名声不好,因为它允许编写人类难以阅读的代码。Java 语言允许您使用带有标签的 break 和 continue 来代替 goto。

于 2013-11-15T09:02:06.127 回答
2

goto在机器级别很好,java编译器不编写代码,它只是将代码从java源代码转换为字节码。

对于编写代码的人来说,这是不同的故事,goto 指令难以阅读和分析,而且代码在多次 goto 跳转后变得一团糟。这就是为什么人们应该使用 OO 概念而不是跳转指令的原因。

于 2013-11-15T09:05:14.943 回答
1

您正在查看 JVM 的机器代码等价物。Java中是否goto允许与字节码中是否允许无关,就像JVM字节码中不允许使用指针一样,但JVM肯定会将字节码编译或解释为使用指针的机器代码。

于 2013-11-15T09:00:17.683 回答
0

字节码不是Java,其他语言的程序,比如Groovy,都可以编译成字节码,你可以直接使用BCEL等工具编写字节码。至于 goto,你不能在低级别没有它。

于 2013-11-15T09:03:50.223 回答
0
  1. SpaceTrucker 提到(在对 head 问题的评论中)Java 语言本身和字节码之间的区别。goto关键字和指令goto不一样。唯一常见的就是名字。在字节码的情况下,它只是JUMP( JMP) 的指令;
  2. 基本上,goto在编程/编码中被认为是一种不好的做法,因为实现了“spagetti”代码并使代码的可读性变差。
于 2013-11-15T09:05:22.810 回答
0

编程中的 Goto 语句是一种方式语句,其中函数调用是两种方式切换,即它将返回到代码的被调用部分。

要使用这些唯一的字节码,请在其中使用 goto。如果允许用户使用 goto 意味着我们可能会以一种低效的方式使用它(比如无条件的 goto 语句),这永远不会让程序终止。

jvm 非常聪明,绝不会让程序无限运行。

于 2013-11-15T09:09:49.670 回答