76

今天找了半个小时的bug,发现可以在if语句后面加分号,而不是代码,像这样:

if(a == b);
// Do stuff

a这基本上意味着无论是否相等,这些东西都会完成b,并且该if声明没有任何意义。为什么Java不给我一个错误?在任何情况下这会有用吗?

4

18 回答 18

90

为什么会这样?

Java 语言规范说:

空的声明

空语句什么都不做。

EmptyStatement:
    ;

空语句的执行总是正常完成

它本质上意味着如果 a==b 要执行空语句

if(a == b);

你该怎么办:

这个问题有两个主要的解决方案:

  1. 您可以通过使用代码格式化程序和周围的东西来避免空语句的if问题{and }。通过这样做,您的空语句将更具可读性。

    if(a == b){
      ;
    }
    
  2. 您还可以检查用于静态代码分析的工具,例如:

    他们可以立即突出显示诸如此类的问题。

我建议将这两种解决方案结合起来。

于 2013-01-01T17:25:02.710 回答
29

在任何情况下这会有用吗?

有用?就像“让你的代码更干净、更清晰、更快、更易于维护”一样?一点也不。这很可能是糟糕的、令人困惑的代码

但不一定是良性的。由于导致副作用的方法,这样的语句可以执行动作和/或更改状态,并且由于运算符的短路而可选地评估这些方法。

if( a() && b() );

在这里,a()orb()可能会做一些事情,并且只有在为 trueb()时才会执行。a()

至于为什么,我认为答案很简单,即偏离已定义的、预期的行为(例如,类似while(reader.read());的语句)比开发人员编写糟糕的代码更糟糕。

编写糟糕的代码总是可能的。重申一下,这几乎在任何情况下都是糟糕的代码。

于 2013-01-01T17:25:38.990 回答
19

一个可能的用例:

if (a==b);
else {
  // Do something
}

不好,但可能。

不过,我确实认为 Java 规范应该不允许空的if.

于 2013-01-01T17:30:25.350 回答
11

如果你使用的是 Eclipse,你可以让它警告你这些语句:

Java->编译器->错误/警告

于 2013-05-07T22:09:53.550 回答
8

如果使用if语句,则if如果条件为真,将执行后面的第一条语句。如果您在if(带有花括号)之后有一个块,则它对整个块都有效。如果没有块,它只计算一个语句。单个分号是一个空语句。您还可以像这样编写示例中的代码:

if(a==b) {
    ;
}
于 2013-01-01T17:27:15.437 回答
7

这是从有更多语法糖来区分表达式和语句的时代的遗留物。

基本上,逗号用作列表项分隔符,因此分号用作“语句列表”分隔符。缺点是处理列表中的空项和块中的空语句。

在项目列表中,Java 使用显式关键字null,但“空语句”只是一个空行。允许空行的存在是继承自 C 的传统的保留。

为什么这样做?特别是if当您知道没有执行任何语句时使用语句:因为某些 if 语句有副作用:

 int c;
 if ((c = in.read()) != -1);

是的,这不是最好的例子,但基本上它说从流中读取一个字节并且什么都不做。在某些极端情况下可能有用,但即使这个例子不是最好的,它也说明了意图。我们希望在不意外执行任何语句的情况下感受表达式的副作用。

于 2013-01-01T22:13:40.497 回答
5

我想不出它有用的场合。它对于像这样的循环很有用

 while(do something);

或者

 for(init; do something; something else);

如果您经常在 IDE 中使用代码格式,那么这些错误就会变得很明显。一些 IDE 也将此突出显示为可能的错误。

于 2013-01-01T17:25:25.630 回答
4

我同意你的观点,这对人类没有任何用处。我怀疑它在那里是因为它简化了语言定义;例如,这意味着ifa 之后的事物与 a 之后的事物相同while

于 2013-01-01T17:27:41.060 回答
3

为什么?这是因为它对编译器编写者来说更容易。您不必特殊情况下检查分号之后if(cond),并且有一个额外的用法是允许

if (cond && maybeFunc())
    ;// Code here I want to ignore

即使允许这样做实际上是一个糟糕的主意。允许然后添加一个案例来检查这一点更容易。

于 2013-01-01T23:57:55.227 回答
2

Java 允许在允许语句块的任何地方使用空块。我确信将此作为所有块的一般规则可以简化编译器。

我同意这主要是导致非常难以找到的错误的原因。我总是在块周围使用大括号,即使只有一条语句,但是 Java 允许您在任何时候使用大括号创建一个块,因此使用大括号无法使您摆脱这种命运。例如,我曾经浪费了 4 个小时试图找到这样的东西:

while (condition);
{
    statement;
    statement;
}

第一行末尾的分号是一个错字,不小心使 while 循环的语句块为空。因为语法是有效的程序编译和运行良好,只是不是我想要的方式。真的很难找到。

我可以想到一种情况,允许你有空块是非常好的,这是这样的:

if (condition1) {
    do_action_1();
}
else if (condition2) {
    //nothing really to do in this case
}
else if (condition3) {
    do_action2();
}
else {
    do_action3();
}

在上面的示例中,您希望能够分离出各种条件。请记住,这些条件可能会重叠,因此并不总是可以重新排列顺序。如果其中一个条件真的不需要做任何事情,那么 Java 允许你有一个空块是很好的。否则,当您真的不想做任何事情时,该语言将需要某种形式的“noop”方法来使用。

我个人更喜欢显式的“noop”语句——但这不是 Java 的定义方式。

于 2013-01-01T23:02:28.767 回答
2

只是关于可用性的仅供参考,如果有这样的声明,它会产生什么影响或可以产生什么影响

考虑如下一段代码。

int a = 10;
if ((a = 50) == 50);

System.out.println("Value of a = " + a);

显然,在这种情况下,该if语句确实改变了输出。因此,这样的声明可以产生影响。

在这种情况下,这可能是有用的,或者说对程序有影响。

于 2013-01-02T10:44:42.683 回答
1
if(a==b)
    println("a equals b");

{}如果只有一行要执行,你可以使用 IF 语句,所以通过使用if(a==b);你说如果它们相等,执行和清空语句......所以它什么也不做,然后返回到你的正常循环,外面IF块。

于 2013-01-01T17:26:43.887 回答
1

jls 的一些定义解释了这一点(第 14 章):

块是语句

如此处所述, aBlock是 a StatementWithoutTrailingSubstatement,而后者又是 a StatementNoShortIf,即 a Statement。因此,无论何时需要这些,我们都可以插入一个Block.

if 子句

尽管 forfor和-loop 也是如此while,但我将使用if-statements。这些规则几乎相同。的语法描述if-statements可以在这里找到。

IfThenStatement:
    if ( Expression ) Statement

IfThenElseStatement:
    if ( Expression ) StatementNoShortIf else Statement

IfThenElseStatementNoShortIf:
    if ( Expression ) StatementNoShortIf else StatementNoShortIf

所以我们可以在这里使用我们的块。

但为什么它适用于 ; ?

;被定义为EmptyStatement( link ),它也是一个StatementNoShortIf. 所以在有条件的代码片段中,比如if-statement和循环,我们可以用 a 替换Blocka EmptyStatement,如果 a StatementNoShortIforStatement是必需的。

因此if(Expression)EmptyStatement有效。

为什么这不会给出错误?

很简单:如果 java 发现无效的语法,它会报错。但是if(Expression)EmptyStatement是完全有效的语法。如果使用正确的参数启动,则会javac发出警告。可以禁用/启用的完整警告列表empty为此目的列出了警告名称。-Xlint:all所以用or编译-Xlint:empty会产生一个警告。

您的 IDE 也应该有一个选项来启用这种警告。对于日食,请参阅@nullptr 的答案。在 IntelliJ 中,您可以按Ctrl + Shift + A,进入empty body搜索字段并启用警告(在图像中标记)

IntelliJ 启用空体警告

这甚至是用来做什么的?

老实说,从极简主义的角度来看,它并没有多大用处。通常有一种方法可以在没有“什么都不做”命令的情况下完成任务。这是个人喜好的问题,您是否愿意使用

if( a() && b() );

或者

if( a() ) b();

同样适用于使用 的其他情况EmptyStatement。关于这个主题要考虑的一个重要点是代码的可读性。在某些情况下,通过使用 no-op,代码变得更具可读性。另一方面,在某些情况下,使用该代码变得非常难以理解EmptyStatement- 上面的示例将计入后来的 IMO。

于 2016-05-12T15:57:44.187 回答
0

我可以想到一个需要空语句的场景(不是 forif条件,而是 forwhile循环)。

当程序只需要用户的明确确认以继续时。当用户确认后的工作取决于其他一些事情并且用户想要控制何时继续时,这可能是必需的。

    System.out.println("Enter Y to proceed. Waiting...");
    System.out.println("");

    while(!(new Scanner(System.in).next().equalsIgnoreCase("Y")));

    System.out.println("Proceeding...");
    // do the work here
于 2013-01-02T08:31:42.340 回答
0

看看这个:

int a,b,c = 0;
if(a == b){
   c =1;
} 
System.out.print(c);//1

所以,你可以这样写:

if (a == b)c=1;

但是,如果这段代码是这样的:

int a,b,c=0;
if (a != b){
}
if (a == b ){
  c =1;
}

你可以这样写:

if(a != b);
if(a == b )c=1;

所以,你会知道 if(a != b);注意

于 2019-03-10T02:06:29.380 回答
0

if 中的分号表示 if 条件的终止,因为在 java;中被视为语句的结尾,因此 if 之后的语句被执行。

于 2019-04-17T20:52:42.903 回答
-1

分号结尾,
if(a==b); 只需在单行中完成语句,即忽略条件结果并从下一行继续执行
此代码很有用,另一方面有时会在程序中引入错误,例如

case 1.

a = 5;
b = 3;
如果(a == b);
prinf("a 和 b 相等");

情况 2.

a = 5;
b = 5;
如果(a == b);
prinf("a 和 b 相等");
将在屏幕上打印相同的输出...

于 2013-01-01T23:40:36.150 回答
-2

在为班级进行编程作业时,我正在使用 N×N 网格的装饰物并将随机装饰物的特征与上方、下方、左侧和右侧的特征进行比较,我发现一个很好的用途是防止嵌套语句和潜在的边界异常。我的目标是最小化代码并避免嵌套 if 语句。

if (row == 0); 
else (method (grid[row][col], grid[row-1][col]));
if (row == N-1);
else (method (grid[row][col], grid[row+1][col]));
if (col == 0);
else (method (grid[row][col], grid[row][col-1]));
if (col == N-1);<br>
else (method (grid[row][col], grid[row][col+1]));

method(Doodad a, Doodad b)a和b之间的一些操作在哪里。

或者,您可以使用异常处理来避免这种语法,但它适用于我的应用程序并且效果很好。

于 2017-06-27T02:28:41.250 回答