Java坑人面试题系列: 比对while与for循环(中级难度)

Java Magazine上面有一个专门坑人的面试题系列: https://blogs.oracle.com/javamagazine/quiz-2。

这些问题的设计宗旨,主要是测试面试者对Java语言的了解程度,而不是为了用弯弯绕绕的手段把面试者搞蒙。

如果你看过往期的问题,就会发现每一个都不简单。

这些试题模拟了认证考试中的一些难题。 而 “中级(intermediate)” 和 “高级(advanced)” 指的是试题难度,而不是说这些知识本身很深。 一般来说,“高级”问题会稍微难一点。

请回答一个问题: 在使用循环进行遍历时,你更喜欢使用哪种循环: for 还是 while

问题(中级难度)

问题的目标是比较Java中各种循环的语法。

假设需要编写程序,在控制台引导用户输入,并进行响应, 执行的步骤大致是这样的:

  1. 输出提示和引导信息;
  2. 读取用户输入的指令;
  3. 如果指令为 quit, 则退出程序;如果输入其他指令,则执行对应的逻辑,完成后跳转到第1步。

要实现上述功能,使用哪种语法和方式最好? 请选择:

  • A、 使用 for 循环和 break 语句;
  • B、 使用简写/增强的 for 循环;
  • C、 使用 while 循环和 continue 语句;
  • D、 使用 do/while 循环;

答案和解析

这个问题要求做一个最佳选择,一般不会让你选择多个答案。

有两个关键的地方,可以帮助我们进行判断。
一、 步骤1和步骤2都必须至少执行一次; 也就是说,至少要读取一个命令之后,代码才会退出。
二、 在读取命令之前,不可能做出退出程序的决定。

让我们考虑一下,如果使用 【while循环】需要怎么处理。
首先 while 循环在入口处执行条件判断; 该测试是在循环体执行之前执行的。
所以,如果使用while 循环来进行控制,一种可能的方式,是在循环开始之前打印提示并读取命令,伪代码所下所示:

Issue prompt                  // 打印提示引导信息
Read command                  // 读取用户指令
While (command is not "quit") // 判断如果命令不是quit才进入循环
  Execute the command         // 执行命令...
  ...

可能初看起来没有什么问题,但我们还需要提示并并读取后续命令。
每次的循环操作中都需要执行这个操作的相关代码。
也就是说会有代码重复。伪代码示例如下:

Issue prompt                  // 打印提示引导信息
Read command                  // 读取用户指令
While (command is not "quit") // 判断如果命令不是quit才进入循环
  Execute the command         // 执行命令
  Issue prompt                // 打印提示引导信息; 【重复代码】
  Read command                // 读取用户指令; 【重复代码】
End-while                     // while循环结束

重复代码不仅看起来不爽,在维护过程中也容易出错,对于追求卓越的程序员来说是不好的习惯。
另一种方式,是在循环之前将命令设置为某个虚拟值,并确保在碰到虚拟命令时不会执行任何操作。
这能避免重复代码,但也会带来额外的复杂性,以后阅读代码的人可能难以理解,因为必须使用特殊的值来进行“hack”。

这种情景直接使用 while 循环肯定不太合理,下面我们再来看看其他方案。

接下来,看看如果使用 【for循环】 怎么处理。
for 循环实际上是另一种方式的 while 循环, 目的是为了在某些场景下编程更加直观和容易理解。
常见的两种场景是:

  • 一、 将循环中使用的一些变量进行声明和初始化。
  • 二、 在迭代过程中更新某些变化量。

从根本上来说, forwhile 循环加一些额外部分形成的。
在这个场景中,这些额外部分可能会有用,但仍然会存在重复的 [提示和读取输入] 代码。

然后我们一起来考察【简写的for循环】(enhanced for loop,增强的for循环)。
增强的for循环提供了一种简洁的方式来遍历可迭代对象(Iterable object,如集合/数组)。
在本文指定的问题中,必须从控制台读取输入命令,当输入值为 quit 时循环终止。
虽然也可以创建一个 Iterable 对象来引导提示用户,并读取文本内容,如果输入不是quit时就返回字符串。 如果用户的输入是 quit,则终止迭代,但对于这个场景来说,这种实现方式就很复杂了,而且也不直观。
在这种情况下,似乎不能推荐使用增强的for循环,可以把它留下作为备选,看看有没有更好的方式。

剩下的选项是 do/while 循环。 这种方式将决定是否继续循环的测试条件放在 do/while 结构的末尾,
效果就是 do/while 循环的body至少会被执行一次,因为必须先执行body之后才能到达测试条件判断。
也就是循环体是在判断条件之前执行的。
do/while 主要就是适用于这样的场景: 可以在循环体中进行引导提示,并读取命令输入。
这样就没有重复的代码,确保提示和输入发生在条件判断之前,代码结构会比较干净和简洁。

那么很明显, 选项D非常适合这个场景, 因此,它是比其他选项更好的选择。

通过这些解析,我们最终可以说 选项D是正确答案,而选项A、B和C是不正确的。

注: 选项A中提到使用 break 语句,而选项C提到使用 continue 语句。
虽然使用 forbreak的方式也可以让循环体强制执行,但这两种方式的代码也不会很优雅。

选项A的伪代码大致如下:

for (;;) {                   // 死循环
    ......                   // 展示引导和提示信息
    String input = ...       // 读取用户输入
    if (input.equals("quit"))
        break;               // 如果是quit则退出循环
    executeCommand(input);   // 否则就执行命令...
}

但依然没有 do/while 方式直观和简洁。

而在 while 循环中使用 continue 则是标准的错误答案,你没法构造出一个可执行的解决方案。

总结

正确的选项是D。

do/while 主要就适用这种至少执行1次的场景。

当然,在某些开源代码中你会发现只执行一次的代码使用了 do{ if-break; } while(true); 的方式,这种方式的好处是可以减少if嵌套的深度,必要时进行中断。 一层一层的嵌套代码实际上是难以维护和理解的,而抽象为方法又涉及传值的问题,所以怎么方便就怎么来是比较好的选择。

相关链接

  • Java坑人面试题系列: 包装类(中级难度)
  • Java坑人面试题系列: 比对while与for循环(中级)
  • Java坑人面试题系列: 集合(高级)
  • Java坑人面试题系列: 线程/线程池(高级)
  • Java坑人面试题系列: 变量声明(中级)

原文链接: https://blogs.oracle.com/javamagazine/quiz-intermediate-loop-constructs

你可能感兴趣的:(面试笔试)