我今天刚刚了解到以下Java代码完全合法:
myBlock: {
/* ... code ... */
if (doneExecutingThisBlock())
break myBlock;
/* ... more code ... */
}
请注意myBlock
不是循环 - 它只是我用花括号分隔的代码块。
这似乎是一个相当奇怪的特征。这意味着您可以使用命名的break
来突破if
语句或匿名块,但在这些上下文中通常不能使用break
语句。
我的问题是:是否有充分的理由做出此设计决定?也就是说,为什么要这样做,以便您只能使用标记的break
s来突破某些封闭语句但不是常规的break
?为什么要允许这种行为呢?鉴于(相对)精心设计的Java如何作为一种语言,我认为这是有原因的,但老实说,我想不出一个。
答案 0 :(得分:10)
这样做似乎是为了简单起见。如果最初标记的中断只能破坏循环语句,那么语言设计者应该立即明白限制是不必要的,语义对所有语句都是一样的。对于语言规范的经济性,编译器的简单实现,或者只是出于普遍性的习惯,为任何语句定义标记的break,而不仅仅是循环语句。
现在我们可以回顾并判断这个选择。通过赋予程序员额外的表达能力,它是否有益于程序员?似乎很少,该功能很少使用。程序员是否需要学习和理解?似乎是这样,正如这次讨论所证明的那样。
如果你能回去改变它,是吗?我不能说我愿意。我们对一般性有一种迷恋。
如果在并行Universe中它仅限于循环语句,那么有人可能会有更小的机会在stackoverflow上发布问题:为什么它不能用于任意语句?
答案 1 :(得分:2)
将其视为从块中而不是从整个函数返回的return
语句。您应用于break
对象散布在任何地方的相同推理也可以应用于return
被允许的任何地方,除非在函数末尾。
答案 2 :(得分:2)
goto的问题在于它可以向前跳过代码。带标签的休息不能那样做(它只能倒退)。 IIRC C ++必须处理goto跳过代码(自从我关心它以来已经超过17年了,但我不确定我是否记得这一点)。
Java旨在供C / C ++程序员使用,因此做了很多工作以使这些开发人员熟悉它。可以从C / C ++到Java进行合理的翻译(虽然有些事情并不简单)。
有理由认为他们将这些语言融入语言中,以便为C / C ++开发人员提供一个安全的转到(在代码中只能向后转),以使某些程序员转换更加舒适。
我从未见过使用它,在16年多的Java编程中,我很少看到标记符号。
你无法突破:
public class Test
{
public static void main(final String[] argv)
{
int val = 1;
X:
{
if(argv.length == 0)
{
break X;
}
if(argv.length == 1)
{
break Y; <--- forward break will not compile
}
}
val = 0;
Y:
{
Sysytem.out.println(val); <-- if forward breaks were allowed this would
print out 1 not 0.
}
}
}
答案 3 :(得分:1)
为什么要这样做,你只能使用标记的中断而不是常规中断来突破某些封闭的语句
考虑:
while (true) {
if (condition) {
break;
}
}
如果break
按照您的建议执行,则此代码会意外执行。休息将变得更加难以使用。
为什么要允许这种行为?
我没有使用它,但它是一个功能,并允许某些独特的控制流构造。我问你,为什么不允许它?
答案 4 :(得分:1)
这个设计决定有充分的理由吗?
是。因为它有效。
在带标签的中断情况下,您不需要在循环或开关内部这一事实可以让您表达难以用其他方式表达的内容。 (不可否认,人们很少会以这种方式使用带标签的断点......但这不是语言设计的错误。)
在未标记的中断情况下,行为是打破最内层的封闭循环或开关。如果它是打破最内层的封闭语句,那么很多事情将难以表达,许多人可能会要求标记的块。例如:
while (...) {
/* ... */
if (something) break;
/* ... */
}
如果break
突破了最里面的封闭语句,那么它就不会突破循环。
答案 5 :(得分:0)
它是“结构化的”等同于goto,在某些情况下很有用。
我经常在一个方法中使用这样的标签创建命名子块来严格限制变量的范围,或者简单地标记一个不适合突破到单独函数的代码块。也就是说,我用它来标记一个块,以便保留大括号周围的代码结构。这是C中用于JNI调用的示例,我在Java中也这样做:
JNIEXPORT void JNICALL Java_xxx_SystemCall_jniChangePassword(JNIEnv *jep, jobject thsObj,
jlong handle, jbyteArray rndkey, jbyteArray usrprf, jbyteArray curpwd, jbyteArray newpwd, jint pwdccs, jint tmosec) {
Message rqs,rpy;
thsObj=thsObj;
SetupRequest: {
memset(&rqs,0,sizeof(rqs));
setOpcode(&rqs,"CHGPWD");
if(!setField(mFldAndLen(rqs.rnd ),null ,jep,rndkey,"Random Key")) {
return;
}
if(!setField(mFldAndLen(rqs.dta.chgpwd.user ),&rqs.dta.chgpwd.userLen ,jep,usrprf,"User Profile")) {
return;
}
if(!setField(mFldAndLen(rqs.dta.chgpwd.curPass),&rqs.dta.chgpwd.curPassLen,jep,curpwd,"Cur Password")) {
return;
}
if(!setField(mFldAndLen(rqs.dta.chgpwd.newPass),&rqs.dta.chgpwd.newPassLen,jep,newpwd,"New Password")) {
return;
}
rqs.dta.chgpwd.ccsid=pwdccs;
}
...
答案 6 :(得分:0)
break语句终止带标签的语句;它不会将控制流转移到标签上。控制流程在标记(终止)语句之后立即转移到语句。
退出嵌套循环似乎很有用。见http://download.oracle.com/javase/tutorial/java/nutsandbolts/branch.html
它在语义上与is there a equivalent of Java's labelled break in C# or a workaround
相同答案 7 :(得分:0)
添加到Stephen C的答案,if (something)
你不能打破嵌套循环。这些情况确实发生在数值算法中。这里有一个简单的例子 - 你不能在没有命名的情况下突破i循环。希望这会有所帮助。
public class JBreak {
private int brj;
public JBreak (String arg) {
brj = Integer.parseInt (arg);
}
public void print () {
jbreak:
for (int i = 1 ; i < 3 ; i++) {
for (int j = 0 ; j < 5 ; j++) {
if ((i*j) == brj)
break jbreak;
System.out.println ("i,j: " + i + "," + j);
}}}
public static void main (String[] args) {
new JBreak(args[0]).print();
}}
答案 8 :(得分:0)
在Java之前,已经有几种编程语言实现了保留语句序列的功能。两个例子:
Algol-68有退出来终止最小的封闭条款的执行(从广义上讲,开始 ... 结束顺序)。
BLISS用BEAVE语句标记了BEGIN…END块,以终止执行。
带有标签的实现(如Java中)更加灵活,因为它们可以退出嵌套的块(或复合语句,或您选择的语言中所称的名称);没有标签,则仅限于退出单个“级别”。
回答直接的问题“为什么”-因为人们发现它是其他先前语言的有用构造。