Java Magazine上面有一个专门坑人的面试题系列: https://blogs.oracle.com/javamagazine/quiz-2。
这些问题的设计宗旨,主要是测试面试者对Java语言的了解程度,而不是为了用弯弯绕绕的手段把面试者搞蒙。
如果你看过往期的问题,就会发现每一个都不简单。
这些试题模拟了认证考试中的一些难题。 而 “中级(intermediate)” 和 “高级(advanced)” 指的是试题难度,而不是说这些知识本身很深。 一般来说,“高级”问题会稍微难一点。
请回答一个问题: 在使用循环进行遍历时,你更喜欢使用哪种循环:
for
还是while
?
问题的目标是比较Java中各种循环的语法。
假设需要编写程序,在控制台引导用户输入,并进行响应, 执行的步骤大致是这样的:
quit
, 则退出程序;如果输入其他指令,则执行对应的逻辑,完成后跳转到第1步。要实现上述功能,使用哪种语法和方式最好? 请选择:
for
循环和 break
语句;for
循环;while
循环和 continue
语句;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
循环, 目的是为了在某些场景下编程更加直观和容易理解。
常见的两种场景是:
从根本上来说, for
是 while
循环加一些额外部分形成的。
在这个场景中,这些额外部分可能会有用,但仍然会存在重复的 [提示和读取输入] 代码。
然后我们一起来考察【简写的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
语句。
虽然使用for
和break
的方式也可以让循环体强制执行,但这两种方式的代码也不会很优雅。
选项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嵌套的深度,必要时进行中断。 一层一层的嵌套代码实际上是难以维护和理解的,而抽象为方法又涉及传值的问题,所以怎么方便就怎么来是比较好的选择。
原文链接: https://blogs.oracle.com/javamagazine/quiz-intermediate-loop-constructs