如何正确使用goto语句

33

我正在上高中AP计算机科学课程。

我决定在我们的实验中插入一个goto语句来玩一下,但是我得到了这个错误。

Exception in thread "main" java.lang.Error: Unresolved compilation problems: 
    Syntax error on token "goto", assert expected
    restart cannot be resolved to a variable
at Chapter_3.Lab03_Chapter3.Factorial.main(Factorial.java:28)

我去了Stackoverflow上一个关于goto的问题,想找到正确的用法,并且我按照答案中演示的方式做了。但我真的不明白编译器为什么要求有一个assert语句(至少我认为是这样),也不知道如何使用assert。它似乎希望goto restart;中的restart重新启动部分是一个变量,但是restart只是一个标签,将程序带回到第10行,以便用户可以输入一个有效的int。如果它要求restart成为一个变量,那我该怎么办呢?

import java.util.*;

public class Factorial 
{
    public static void main(String[] args) 
    {
        int x = 1;
        int factValue = 1;
        Scanner userInput = new Scanner(System.in);
        restart:
        System.out.println("Please enter a nonzero, nonnegative value to be factorialized.");
        int factInput = userInput.nextInt();

        while(factInput<=0)
        {
            System.out.println("Enter a nonzero, nonnegative value to be factorialized.");
            factInput = userInput.nextInt();
        }

        if(x<1)//This is another way of doing what the above while loop does, I just wanted to have some fun.
        {
            System.out.println("The number you entered is not valid. Please try again.");
            goto restart;
        }
        while(x<=factInput)
        {
            factValue*=x;
            x++;
        }
        System.out.println(factInput+"! = "+factValue);
        userInput.close();
    }
}

1
欢迎来到Java。虽然goto是Java中的一个关键字,但它没有功能,并且会导致编译错误(正如您在问题中所指出的)。 - DwB
1
即使你找到了完美的用例并且它非常合理,也要寻找另一种方法来实现它。最好的情况是大多数和你合作的人会觉得你不够优秀。这并不是真的很糟糕,但你永远无法摆脱这种污名。 - Bill K
2
关于您想要使用标签的操作,顺便说一下,break和continue可以指定一个标签(就像您期望goto一样)。 - Bill K
2
@JohnnyCoder 这种污名感源于程序员以前使用 goto 的方式。换句话说,不仅是为了跳出循环 - 而是任何他们想要的地方,到处都是。你听说过“意大利面代码”这个术语吗?如果你画出 goto 开始和结束的线,它看起来像一盘意大利面。 (附言:我已经编程很长时间了,记得当时意大利面代码是我和周围所有人编写程序的方式。我们还不知道更好的方法。) - ajb
7
很多初学编程的人不明白,在大多数情况下代码的目标不是让计算机做某些事情,而是尽可能地让下一个人能够轻松理解、修复和维护它。经过十年或二十年的编程——被难以理解和维护的垃圾代码所困扰后——大多数程序员都明白了可维护性的重要性,并很快忘记了其他任何思考方式。跳转到代码的广泛区域会使维护变得非常具有挑战性。break和continue不能像goto那样随意跳转到任何位置。 - Bill K
显示剩余8条评论
8个回答

95

正如所有答案已经指出的那样,goto 是Java中的保留字,不在该语言中使用。

restart: 被称为标识符后跟着一个冒号。

如果您希望实现类似的行为,则需要注意以下几点 -

outer:                  // Should be placed exactly before the loop
loopingConstructOne  {  // We can have statements before the outer but not inbetween the label and the loop          
    inner:
    loopingConstructTwo {
        continue;       // This goes to the top of loopingConstructTwo and continue.
        break;          // This breaks out of loopingConstructTwo.
        continue outer; // This goes to the outer label and reenters loopingConstructOne.
        break outer;    // This breaks out of the loopingConstructOne.
        continue inner; // This will behave similar to continue.
        break inner;    // This will behave similar to break.
    }
}

我不确定是否应该说“相似”,因为我已经这样说过了。


4
太有帮助了,我需要打印出这个答案并放到我的夹子里学习和使用。赞一个!你应该得到100分,但我做不到。 :) - Ungeheuer
6
这是一个很好的答案,点赞+1,但如果你有这种欲望,那么就做错了——应该进行重构,拆分成一些命名合理的其他方法。如果你使用这种结构,最好的情况是避免了需要进行的重构,让你的代码变得杂乱无章,并用很少有程序员理解的结构来混淆它。更有可能的是,你将被团队中的其他人认为是一个糟糕的程序员,因为你不能编写更清晰的代码。这种欲望应该被视为非常糟糕的代码气味。 - Bill K
3
@BillK嗯,我认为通过基本的注释,你可以清晰地做到这一点。只要团队成员能理解这些注释,对我来说似乎就可以了。话虽如此,我在团队方面有些缺乏经验,也许不知道自己在说什么。 - Jax
这是一个哲学问题。我在C语言中使用了标记的goto语句,在Java中使用了break/continue语句,以便让代码更清晰,通过提前退出某些部分,避免嵌套和过多的条件判断。 - undefined

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

这样做可能是为了将它添加到以后的Java版本中。

如果goto不在列表中,而它后来被添加到语言中,那么使用单词goto作为标识符(变量名、方法名等)的现有代码将会出现问题。但因为goto是一个关键字,这样的代码甚至无法编译,在目前仍然可以使其实际上执行某些操作,而不会破坏现有的代码。


3
好的,谢谢。我希望他们能添加“goto”,这样会非常方便。 - Ungeheuer
2
我希望他们永远不会使用它。在使用Java语言(以及早期的C / C ++)超过10年后,我总是发现更好的方法来设计函数/方法,而不是使用那种“创造漏洞”的方式。只需查看苹果SSL实现安全漏洞。好吧,他们也忘记了另一个最佳实践:即使使代码更冗长,也要始终使用花括号... - рüффп
2
编程已经30多年了。从C语言开始学习。一开始就有goto,但我一直避免使用它。最初,其他程序员让我远离使用它。随着时间的推移,我看到了足够多的goto用法,形成了自己的信念和厌恶感。但是,我曾经看到过一些(非常少量)使用goto比不使用更加简洁的情况(可怜的其他程序员)。这些用法可能在后来被重构了。 - Les
在C#中,goto唯一有用的地方是为了让switch语句穿透。Java支持无需使用goto的穿透功能,因此在Java中真的没有必要使用goto。 - M.kazem Akhgary
“goto” 显然是任何编程语言中可用的最强大的结构之一,甚至比像条件分支这样依赖它构建的结构更加强大。如果能将其添加到语言中,那么Java就真正完整了。 - Kröw

10

如果你查找continue和break,它们都接受一个“标签”。可以尝试一下。但是goto本身不起作用。

public class BreakContinueWithLabel {
  
    public static void main(String args[]) {
    
        int[] numbers= new int[]{100,18,21,30};
      
        //Outer loop checks if number is multiple of 2
        OUTER:  //outer label
        for(int i = 0; i<numbers.length; i++){
            if(i % 2 == 0){
                System.out.println("Even number: " + i +
                                   ", continue from OUTER label");
                continue OUTER;
            }
          
            INNER:
            for(int j = 0; j<numbers.length; j++){
                System.out.println("Odd number: " + i +
                                   ", break  from INNER label");
                break INNER;
            }
        }      
    }
}

阅读更多


1
谢谢,这很有道理。非常感谢你。 - Ungeheuer
复制粘贴是犯错误的根源...在这个例子中,偶数和奇数被混淆了。 - Geodepe

5

Java不支持goto语句,它被保留为关键字以备将来版本添加。


4
他们应该加上它。这会让生活变得更轻松许多。 - Ungeheuer
1
@Ungeheuer 反直觉地,GOTO 被认为是有害的。Dijkstra 的论点 - Tarun Maganti
Goto语句对于方法的延迟/退出语句处理非常有用。代码中有很多返回语句是不好的。为了获得常见的退出块而构建大量的“if”语句也会使生活变得更加复杂。因此,goto语句非常受欢迎。 - Madars Vi

3

goto在Java中没有任何作用。


2

Java也不使用行号,这是GOTO函数的必要条件。与C/C++不同,Java没有goto语句,但Java支持标签。在Java中标签只有在嵌套循环语句之前才有用。我们可以使用break指定标签名称以退出特定的外部循环。


1

在Java世界中没有'goto'。主要原因是开发人员意识到,具有'goto'的复杂代码将导致代码真正糟糕,并且几乎不可能增强或维护代码。

然而,可以稍微修改此代码并使用“continue”和“break”的概念来使代码工作。

    import java.util.*;

public class Factorial 
{
    public static void main(String[] args) 
    {
        int x = 1;
        int factValue = 1;
        Scanner userInput = new Scanner(System.in);
        restart: while(true){
        System.out.println("Please enter a nonzero, nonnegative value to be factorialized.");
        int factInput = userInput.nextInt();

        while(factInput<=0)
        {
            System.out.println("Enter a nonzero, nonnegative value to be factorialized.");
            factInput = userInput.nextInt();
        }

        if(x<1)//This is another way of doing what the above while loop does, I just wanted to have some fun.
        {
            System.out.println("The number you entered is not valid. Please try again.");
            continue restart;
        }
        while(x<=factInput)
        {
            factValue*=x;
            x++;
        }
        System.out.println(factInput+"! = "+factValue);
        userInput.close();
        break restart;
}
    }
}

0

goto是一种未使用的保留字,因此没有goto。但是,如果你想要荒诞主义戏剧,你可以通过标记语言特性来引出一个。但是,与其标记有时有用的for循环,不如标记代码块。在该代码块内,您可以调用标签上的break,将您带到代码块的末尾,这基本上是一个向前跳转的goto,只不过是在代码中向前跳转。

    System.out.println("1");
    System.out.println("2");
    System.out.println("3");
    my_goto:
    {
        System.out.println("4");
        System.out.println("5");
        if (true) break my_goto;
        System.out.println("6");
    } //goto end location.
    System.out.println("7");
    System.out.println("8");

这将打印出1、2、3、4、5、7、8。由于跳出代码块,程序跳转到了代码块后面。你可以移动my_goto: {if (true) break my_goto;} //goto end location.语句。重要的是break必须在有标签的代码块内。

这比真正的goto还要丑陋。永远不要这样做。

但是,在使用标签和break时,有时候确实很有用,并且知道如果你为代码块添加标签而不是循环时,当你break时会向前跳转。因此,如果你从循环内部中断代码块,你不仅终止了循环,还跳过了循环结束和代码块之间的代码。


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