我已经用Java编程好几年了,但我最近才回到学校获得一个正式学位。我很惊讶地发现,在我的上一个作业中,我因为使用下面这样的循环而失分。

do{
     //get some input.
     //if the input meets my conditions, break;
     //Otherwise ask again.
} while(true)

现在对于我的测试,我只是扫描一些控制台输入,但我被告知这种循环是不鼓励的,因为使用break类似于goto,我们只是不这样做。

我完全理解goto和它的Java兄弟break:label的陷阱,而且我很明智地不使用它们。我也意识到,一个更完整的程序会提供一些其他的逃避方式,比如结束程序,但我的教授并没有提到这一点,所以……

do-while(正确)怎么了?


当前回答

如果您的循环在后台线程上运行,这可能会很糟糕,因此当您通过终止一个UI线程来关闭应用程序时,这段代码将继续执行。正如其他人已经说过的,你应该总是使用某种支票来提供一种取消的方式。

其他回答

也许是我运气不好。或许我只是缺乏经验。但是每次我回忆起处理while(true)内部有break时,可以改进将Extract Method应用到while-block的代码,它保留了while(true),但(巧合吗?)将所有的break转换为返回。

根据我的经验,没有休息(即返回或抛出)是相当舒服和容易理解的。


  void handleInput() {
      while (true) {
          final Input input = getSomeInput();
          if (input == null) {
              throw new BadInputException("can't handle null input");
          }
          if (input.isPoisonPill()) {
              return;
          }
          doSomething(input);
      }
  }

我不会说这很糟糕——但同样地,我通常至少会寻找另一种选择。

如果这是我写的第一件事,我几乎总是至少试着把它重构成更清晰的东西。有时这是没有办法的(或者另一种选择是使用bool变量,它除了指示循环的结束之外没有任何意义,比break语句更不清楚),但至少值得一试。

作为一个使用break比使用flag更清楚的例子,考虑:

while (true)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        break;
    }
    actOnInput(input);
}

现在让我们强制它使用一个标志:

boolean running = true;
while (running)
{
    doStuffNeededAtStartOfLoop();
    int input = getSomeInput();
    if (testCondition(input))
    {
        running = false;
    }
    else
    {
        actOnInput(input);
    }
}

我认为后者读起来更复杂:它有一个额外的else块,actOnInput更缩进,如果您试图弄清楚当testCondition返回true时发生了什么,您需要仔细检查块的其余部分,以检查else块之后是否有什么东西会发生,无论running是否被设置为false。

break语句更清楚地传达了意图,并让块的其余部分继续做它需要做的事情,而不必担心前面的条件。

请注意,这与人们对方法中多个return语句的争论完全相同。例如,如果我可以在前几行内计算出一个方法的结果(例如,因为一些输入是空的,或者是空的,或者是零),我发现直接返回答案比用一个变量来存储结果,然后是一整个代码块,最后是一个返回语句更清楚。

这是你的枪,你的子弹和你的脚…

说它不好是因为你在自找麻烦。你或本页上的任何其他海报都不会有简短的while循环的例子。

麻烦将在未来某个非常随机的时间开始。可能是其他程序员造成的。可能是安装软件的人。可能是最终用户。

为什么?我必须找出为什么700K的LOC应用程序会逐渐开始消耗100%的CPU时间,直到每个CPU都饱和。这是一个神奇的while (true)循环。这件事又大又恶心,但归结起来就是:

x = read_value_from_database()
while (true) 
 if (x == 1)
  ...
  break;
 else if (x ==2)
  ...
  break;
and lots more else if conditions
}

没有最后的其他分支。如果值不匹配If条件,循环将一直运行到时间结束。

当然,程序员指责最终用户没有选择程序员期望的值。(然后我在代码中删除了while(true)的所有实例。)

恕我直言,使用while(true)这样的结构并不是很好的防御性编程。它会回来困扰你的。

(但我确实记得,如果我们不评论每一行,教授就会扣分,即使是i++;)

早在1967年,Edgar Dijkstra在一份行业杂志上写了一篇文章,讲述了为什么应该从高级语言中删除goto以提高代码质量。一种叫做“结构化编程”的编程范式由此而来,尽管当然不是每个人都同意goto自动意味着糟糕的代码。

结构化编程的关键本质上是代码的结构应该决定它的流,而不是在任何可能的情况下使用goto或break或continue来决定流。类似地,在该范例中也不鼓励有多个循环或函数的入口和出口点。

显然,这不是唯一的编程范式,但通常它可以很容易地应用到其他范式,如面向对象编程(比如Java)。

你的老师可能已经被教导过,并且正试图教导你的班级,我们最好通过确保我们的代码是结构化的,并遵循结构化编程的隐含规则来避免“意大利面条代码”。

While there is nothing inherently "wrong" with an implementation that uses break, some consider it significantly easier to read code where the condition for the loop is explicitly specified within the while() condition, and eliminates some possibilities of being overly tricky. There are definitely pitfalls to using a while(true) condition that seem to pop up frequently in code by novice programmers, such as the risk of accidentally creating an infinite loop, or making code that is hard to read or unnecessarily confusing.

具有讽刺意味的是,异常处理是一个与结构化编程有偏差的领域,并且随着您对Java编程的深入了解,这种偏差肯定会出现。

也有可能你的老师希望你展示你使用特定循环结构或语法的能力,而你写的代码在功能上是等价的,你可能没有展示你应该在那一课中学习的特定技能。

您可以使用一个布尔标志来指示何时结束while循环。中断和转向是软件难以维护的原因——软件危机(tm)——应该避免,也可以很容易地避免。

这是一个你是否务实的问题。实用的程序员在这种简单的情况下可能只使用break。

但是最好养成不使用它们的习惯,否则可能会在不合适的情况下使用它们,比如在复杂的嵌套循环中,代码的可读性和可维护性因使用break而变得更加困难。