Java中是否有goto语句?

289

我对此感到困惑。我们大多数人都被告知Java中没有goto语句。

但我发现它是Java中的关键字之一。它能在哪里使用?如果不能使用,那么为什么将其包含在Java中作为关键字?

我对此感到困惑。我们大多数人都被告知Java中没有goto语句。但我发现它是Java中的关键字之一。它在语法上有效,但Java编程规范建议不要使用它,因为过度或错误使用goto语句可能会导致代码变得难以理解和维护。所以,虽然Java允许使用goto语句,但实际上几乎所有的Java程序都避免使用它。


18
建议您永远不要使用goto这个关键字。 - lostiniceland
7
迪科斯彻大师说:“考虑到语句的转移在程序设计中被认为是有害的”。请看这里:https://dev59.com/nXVD5IYBdhLWcg3wOo9h - n00ki3
10
大家都知道“GOTO”是有害的,但这并不是问题的答案,对吗? - Martin Lazar
25
“g**o”一词在大多数编程语言中被视为淫秽,这是真正的原因。Java设计者只是在保护那些年轻无知的程序员免受腐败影响。( :-) ) - Stephen C
44
对goto的“巫狩”引发了我生命中最令人恼火的时刻之一。我见过人们通过在函数中隐藏功能、在多层嵌套循环中加入荒谬的退出逻辑以及以其他可能的方式进行各种过度设计,只是为了避免有人在代码审查中批评他们的代码。这个规则,以及“避免多次返回”的谬论,都是无稽之谈。有时使用goto会使事情变得不易维护,但也有时使用goto会使事情更易维护。我曾经非常希望这场“巫狩”能在80年代就结束。 - user4229245
显示剩余7条评论
23个回答

248

James Gosling最初创建了支持goto语句的JVM,但后来他删除了这个特性,因为它是不必要的。 goto语句通常可以用更易读的语句(如break/continue)或将一部分代码提取成一个方法来替换。

来源:James Gosling, Q&A session


27
"......或者通过将一段代码提取成一个方法" - 如果没有适当的尾调用消除,那一定非常酷。 - Display Name
62
有时候,明智地使用 goto 是表达某些内容最清晰易读的方式,因此强行使用其他方式反而会使它变得不那么易读。 - Deduplicator
2
@Deduplicator 使用goto语句虽然可能是明智的,但总是容易出错。 - Çelebi Murat
3
Java 中保留字 Goto 的存在非常好,因为它可以防止人们将标签命名为 "goto:"。 - Winter
2
grml...现在我不得不重写一小段代码,使其更大且不太容易阅读,因为Java™不支持goto... - mirabilos
显示剩余5条评论

217

Java关键字列表指定了goto关键字,但标记为“未使用”。

在最初的JVM中(参见VitaliiFedorenko的回答),它存在,但后来被删除。它很可能被保留为保留关键字,以防它被添加到Java的后续版本中。

如果goto不在列表中,并且稍后将其添加到语言中,则使用单词goto作为标识符(变量名,方法名等)的现有代码将会出错。但由于goto是一个关键字,在现有代码中甚至不能编译,因此仍然可以在以后实际上使其起作用,而不会破坏现有代码。


29
这可能是为了方便将其添加到以后版本的Java中而这样做。实际上,主要原因略有不同(请参见下面我的回答)。 - Vitalii Fedorenko
2
这是好的和有趣的信息,但它并没有解释为什么它仍然是一个保留关键字。 - Thomas
@Thomas,你难道没有解释为什么它是保留字吗?它是一个关键字,所以现在不能使用;稍后goto可能会重新启用而不会引起问题。如果它不是保留字,现在就可以使用它来生成代码(int goto = 2;),但如果goto被引入,这将导致代码出错。 - user146043

163

该关键字存在,但尚未实现。

我能想到使用goto的唯一好理由是:

for (int i = 0; i < MAX_I; i++) {
    for (int j = 0; j < MAX_J; j++) {
        // do stuff
        goto outsideloops; // to break out of both loops
    }
}
outsideloops:

在Java中,你可以这样做:

loops:
for (int i = 0; i < MAX_I; i++) {
    for (int j = 0; j < MAX_J; j++) {
        // do stuff
        break loops;
    }
}

25
嗯,不完全是这样。标签用于标记外部循环。然后break loops的意思是“跳出名为loops的循环”。也许事后来看,给标签取一个更好的名称可能是outer - jonnystoten
12
使用goto语句的唯一好处是,对于需要无条件无限循环的快速简单程序,它非常实用。使用while (true) { ... }会显得有些复杂。尽管goto经常因为不当使用而受到指责,但我认为与布尔字面值进行不必要的比较比使用goto更糟糕。 - Alexander
1
循环的标签不应该放在循环之后吗? - GC_
3
我最近发现,这种标记技术与 continue LABEL; 语句结合使用也非常有用。因此,您可以继续执行外部循环。 - infotoni91
1
在我看来,Java中标签的使用比C语言中使用goto的常规标签更加混乱和容易出错。他们试图消除goto,但失败了,并最终得到了更糟糕的东西。 - Georgie
显示剩余5条评论

43

15
“unsigned”根本就不在这里!(叹气) - Matthieu
有道理,因为Java中的所有原始类型都是有符号的。 - mwieczorek
2
@mwieczorek 不是所有的都是这样。"char" 是一种原始类型,它是无符号的。 - Sergey Krivenkov
@SergeyKrivenkov 是的,但是 char 不是设计用来存储数字的。它被设计成可以存储更多类型的字符。然而,与 C 或 C++ 不同,Java 不支持 unsigned 数字。 - Tech Expert Wizard
1
@mwieczorek 我的意思是他们甚至没有“为将来保留”,这意味着Java中的未签名支持永远不能被考虑。这是最令人难过的事情... - Matthieu

30

所以如果语言设计者感觉有需要,这些关键字可能某一天会被使用。

另外,如果来自具有这些关键字的语言(例如C、C++)的程序员错误地使用它们,那么Java编译器可以提供有用的错误提示信息。

或许只是为了阻止程序员使用goto指令 :)


1
Java中缺少的goto功能足以成为不使用goto的理由 - 没有必要将其保留为关键字。按照您的逻辑,一切都将成为关键字,因为它要么被其他语言使用,要么Java设计人员可能会在未来使用它... - stuXnet
1
我认为我的逻辑并不意味着所有东西都是关键字。在许多编程语言中,有一小组关键字可以在 Java 中检测和处理。Java 保留了“goto”和“const”,反映了其 C / C++ 继承,但它们仍未实现(尽管有关于实现后者的辩论)。还有一些像“assert”和“enum”这样的关键字最初没有被保留,但也许应该保留,因为它们已经被实现了。但是事后诸葛亮很容易。 - dave

24

这些关键词是保留字,供将来使用(参见:Java语言关键词)。

即使目前不被使用,关键词const和goto也是已保留的。

为什么Java中没有goto语句的原因可以在《Java语言环境》中找到:

Java没有goto语句。研究表明,goto常常被滥用,只是因为“它存在”。消除goto导致语言变得简单——没有规则说明goto进入for语句中间的效果,例如。 对大约10万行C代码的研究确定,大约90%的goto语句纯粹用于获得退出嵌套循环的效果。 如上所述,多级break和continue消除了大部分goto语句的需求。


“goto”是保留字,但不是为将来使用而保留的。 - user207421
2
那么当需要这10%的时候怎么办? - user310291

18

在Java中使用“continue”标签的示例:

public class Label {
    public static void main(String[] args) {
        int temp = 0;
        out: // label
        for (int i = 0; i < 3; ++i) {
            System.out.println("I am here");
            for (int j = 0; j < 20; ++j) {
                if(temp==0) {
                    System.out.println("j: " + j);
                    if (j == 1) {
                        temp = j;
                        continue out; // goto label "out"
                    }
                }
            }
        }
        System.out.println("temp = " + temp);
    }
}

结果:

I am here // i=0
j: 0
j: 1
I am here // i=1
I am here // i=2
temp = 1

11
重要的是要明白,goto 结构是从程序员使用机器语言和汇编语言的时代遗留下来的。由于这些语言非常基本(即每个指令只执行一件事),程序控制流完全通过 goto 语句进行(但在汇编语言中,这些被称为跳转或分支指令)。
现在,尽管 C 语言相当低级,但可以将其视为非常高级的汇编语言 - C 中的每个语句和函数都可以轻松地分解为汇编语言指令。尽管 C 不是主要用于计算机编程的语言,但仍然广泛用于低级应用程序,例如嵌入式系统。因为 C 的功能与汇编语言的功能非常接近,所以在 C 中包括 goto 是有意义的。
很明显,Java 是 C/C++ 的演化。Java 继承了许多 C 的特性,但抽象出更多的细节,因此写法不同。Java 是一种非常高级的语言,因此没有必要像低级特性那样使用 goto,而是使用更高级别的结构,比如函数、for、foreach 和 while 循环来控制程序流程。想象一下,如果你在一个函数中执行一个 goto 到另一个函数的标签,当另一个函数返回时会发生什么?这个想法是荒谬的。
这并不一定回答了为什么 Java 包含 goto 语句但不允许其编译,但重要的是要知道为什么在较低级别的应用程序中首先使用了 goto,以及为什么在 Java 中使用它是没有意义的。

程序控制流完全使用goto完成。嗯,并不总是无条件的goto。 - Peter Mortensen
1
在C/C++中,使用“goto”从一个函数跳转到另一个函数中的标签也是不可能的。C/C++中的goto仅限于单个函数块。 - user2328447
“当更高级别的结构可以使用时,像goto这样的低级特性就不是必要的” - 记住“必要性和充分性”。它只是“不必要的”。因此没有理由。什么是理由:如果您想跳转到“可执行代码”,则“Goto”作为汇编语言JMP、BNE等的“高级”替代品是合理的,但Java是字节码,因此不是“可执行的”。;-) - reichhart
我听说C和C++有goto语句。虽然我从未使用过,所以无法验证这一点。自1975年以来,我一直在使用C。据说,C是一种具备汇编代码所有强大能力和表现力优雅的计算机语言。因此,goto仅存在于机器码上的语法糖中,就像语法糖一样,只不过更假。 - flounder
“我从未使用过,所以无法验证。” - 这就是语言规范的作用 :-) - Stephen C

10
因为它不被支持,而且你为什么需要一个什么也不做的goto关键字或一个名为goto的变量呢?
虽然你可以使用break label;continue label;语句有效地完成goto所做的事情。但我不建议这样做。
public static void main(String [] args) {

     boolean t = true;

     first: {
        second: {
           third: {
               System.out.println("Before the break");

               if (t) {
                  break second;
               }

               System.out.println("Not executed");
           }

           System.out.println("Not executed - end of second block");
        }

        System.out.println("End of third block");
     }
}

10
为什么我不能使用名为“goto”的变量或方法? - Michael Borgwardt
4
因为它在编程领域有一个含义,而这个含义不适用于Java。同时,它也是一个不太恰当的变量名称。 - pjp
变量“print”通常用于什么?你提到的这些词更像是方法名而不是变量或关键字。 - pjp
4
首先,Java不支持“goto”流程控制语句,并不意味着这就是它拥有“goto”关键字但没有任何作用的原因。其次,“goto”可能是一个糟糕的变量名,但可以成为一个优秀的方法名,只是我们不能使用它,因为“goto”是一个关键字。第三,"break label"和"continue label"存在很好的理由,因此“不建议使用它”并不是非常有用。第四,既然Sun公司表示“目前未使用”,这很可能意味着他们曾计划实现它,但在决定否决它时不想删除该关键字。 - Vojislav Stojkovic
“不建议使用它” = 它的风格很差 - 并不是breakcontinue不好。 - pjp
2
@VojislavStojkovic 这只是疯狂的说法。1)它不支持这个功能,所以它什么也不做。根据JLS的规定,“const”和“goto”关键字虽然没有被使用,但是它们是保留字。如果这些C++关键字错误地出现在程序中,这可能会使Java编译器产生更好的错误消息。这意味着“我们不使用它,因此如果您来自C++背景,我们将不允许您使用它。” 2)“goto”是一个可怕的方法名。抱歉。3)建议使用break和continue,但不要像这里使用。4)“很可能”[需要引用]。 - corsiKa

10

不,不使用goto语句,但是您可以定义标签并通过标签离开循环。您可以使用breakcontinue跟着标签。这样,您就可以跳出多个循环层次。请参阅教程


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接