0.前言
尽管Java的设计者将goto作为保留字,但实际上并没有打算在语言中使用它。通常,使用goto语句被认为是一种拙劣的程序设计风格。当然,也有一些程序员认为反对goto的呼声似乎有些过分(例如,Donald Knuth就曾编著过一篇名为《Structured Programming with goto statements》的著名文章)。这篇文章说:无限制地使用goto语句确实是导致错误的根源,但在有些情况下,偶尔使用goto跳出循环还是有益处的。Java设计者同意这种看法,甚至在Java语言中增加了一条带标签的break,以此来支持这种程序设计分格。
1. 标签语句概述
Java允许在语句前加上标签前缀,构成标签(label)语句。其一般格式为:
标识符:语句
其中标识符应是Java语言中合法的标识符;语句可以是任意Java语句,包括简单语句、复合语句(语句块)、控制转移语句、空语句。但由于在Java中,标签语句仅能与break和continue语句配合使用,所以给简单语句或空语句加标签没有意义。
含义是,为冒号“:”后的语句指定名为“标识符”的标签。如:
Outer:
{
......
}
Outer2:while(true)
{
......
}
提及标签语句,最好先介绍一下它的老搭档goto语句。goto语句可以说是程序控制结构的始祖,它在程序中与标签语句配合使用可灵活地构造任意复杂的业务逻辑。但由于它过于灵活,在编程时常被随意跳转,使程序控制结构变的复杂而且混乱。随着E. W. Dijkstra著名的“goto语句有害论”的问世和广泛争论,goto语句也逐渐失宠,并在Java中被取消。
当然,goto语句也并非一无是处,在一些情况下,如需从嵌套很深的循环中退出时,它还是组织控制流程的最佳手段。为此,Java虽取消了goto语句,但Java仍保留了goto语句的优点。通过标签语句与break、continue语句的配合,构造带标号的break语句和带标号的continue语句,允许程序从循环体内部退出到循环体外部或者从语句块内部退出至语句块外部,但不允许从外部跳转至内部。带标号的break,continue语句格式如下:
break labelIdentifier;
continue labelIdentifier;
带标号的break和continue语句,只能在标签所指的语句块或其嵌套包含的内层语句块中使用。表示从语句块内部退出到指定语句块中。如下面程序第9行的break语句用法是不允许的。
2. 与break语句配合使用
带标号的break语句,可以使程序从复合语句或循环体内部退出到指定标签所标识的外层语句块末尾,继续执行之后的语句。如可以从最里层(第n层),退出到最外层(第1层),也可退出到第n-m层(n>m)。图5-15演示了这一语义。
当带标号的break语句,从第n层退出到n-1层时,其效果与不带标号的break语句是相同的。如下例:
Scanner in = new Scanner(System.in);
int n;
read_data;
while(...) //this loop statement is tagged with the label
{
...
for(...) //this inner loop is not labeled
{
System.out.print("Enter a number >= 0: ");
n = in.nextInt();
if(n < 0) // should never happen - can't go on
break read_data
//break out of read_data loop
...
}
}
//this statement is executed immediately after the labeled break
if(n < 0) //check for bad situation
{
//deal with bad situation
}
else
{
//carry out normal processing
}
3. 与continue语句配合使用
带标号的continue语句只能在循环体内部使用,使程序终止当前循环;从循环体内部跳出到标签所指的循环控制层,再判定该循环判定表达式,以决定是否再次进入该循环,图5-16演示了这一过程。这与带标号的break语句终止该循环不同。
与带标号的break语句一样,当带标号的continue语句,从第n层退出到n-1层时,其效果与不带标号的continue语句是相同的。
Scanner in = new Scanner(System.in);
while(sum < goal)
{
System.out.print("Enter a number: ");
n = in.nextInt();
if(n < 0)continue;
sum += n; //not executed if n < 0
}
在优质的Java程序中,标签语句是比较不常用的。因为Java提供了丰富的循环语句,可适用于绝大多数场合。
4.注释
事实上,可以将标签应用到任何语句中,甚至可以应用到if语句或者块语句中,如下所示:
label:
{
...
if(condition) break label; //exits block
...
}
//jumps here when the break statement executes
因此,如果希望使用一条goto语句,并将一个标签放在想要跳转到的语句块之前,就可以使用break语句!当然,并不提倡使用这种方式。另外需要注意,只能跳出语句块,而不能跳入语句块。