3.14 像使用goto语句一样使用break语句
除了用于switch语句和循环外,break语句也可以用提供像goto语句一样的用法。由于goto语句是一种改变程序流的非结构化方法,因此Java并没有goto语句。使用了很多goto语句的程序一般难以理解和维护。但是,有一些使用goto语句的地方是有用且合理的。例如,在退出一组嵌套很深的循环时就是这样。为了应对这种情况,Java定义了break语句的扩展形式。例如,通过使用这种形式的break语句,可以从多层代码块退出。这些代码块不必是循环或是switch的一部分。它们可以是任意的代码块。另外,还可以精确指定执行恢复的位置,因为break的这种形式使用了标签。break语句具有goto语句的优点,还避免了goto语句的一些问题。
这种有标签的break语句的一般形式如下:
break label;
这里,label是标签名,标识一句代码或一个代码块。当这种形式的break语句执行时,控制权从标记的语句或块中转移出。标记的语句或块必须包含break语句,但是它不一定是最近的语句或块。例如,这意味着,可以用有标签的break语句退出一系列嵌套代码块。但不能使用break语句将程序控制权转移到不含break语句的块中。
为了给语句和块起个名字,可以在其开始处加标签。标签(label)是后加冒号的任意有效Java标识符。一旦为语句和块加了标签,就可以将这个标签作为break语句的目标。这样做可以执行语句或块后的语句。例如,下面的程序有三个嵌套的循环。
程序的输出如下所示:
让我们仔细看看这个程序,以理解为什么生成这样的输出。当i为1时,第一个if语句成功,使break跳转到由标签one定义的代码块的末尾。这使得程序输出“After block one”。当i为2时,第二个if语句成功,使控制权转移到由标签two标注的块的末尾。这使程序顺序输出“After block two”和“After block one”。当i为3时,第三个if语句成功,控制权转移到标记为three的语句的末尾。这时,三条消息都显示。
再看一个例子。这个例子使用break语句从一系列嵌套的for循环中跳出。当内层运行的break语句执行时,程序控制权跳转到外层for循环定义的块的末尾,这个块标记为done。这使得三重循环的剩余部分不再执行。
程序输出如下所示:
在何处精确放置标签十分重要,特别是当涉及循环时。例如,考虑下面的程序:
程序输出如下所示:
在程序中,两个嵌套的循环只有一点不同。在第一个嵌套循环中,标签在外层for语句的前面。在这种情况下,当执行break语句时,将控制权转移到整个for块的末尾,跳过外层循环迭代的其他代码。在第二个嵌套循环中,标签在外层for语句的左花括号前。于是,当执行break stop2时,控制权转移到外层for语句块的末尾,而不是循环的末尾。这引起下一次迭代的发生。
注意,不能跳转到不是为包括break语句的语句或块定义的标签。例如,下面程序是无效的,不能编译。
因为标注为one的for循环不包括在第二个for循环中的break语句,所以不可能将控制权转移到那个标签。