Several definitions from jls explain this (chapter 14):
Blocks are expressions
As stated here , a Block is a StatementWithoutTrailingSubstatement , which in turn is a StatementNoShortIf , which is a Statement . Thus, when any of them is required, we can insert Block .
If clause
Although this also applies to for and while -loops, I will use if -statements. These rules are almost the same. The syntax for if-statements can be found here .
IfThenStatement: if ( Expression ) Statement IfThenElseStatement: if ( Expression ) StatementNoShortIf else Statement IfThenElseStatementNoShortIf: if ( Expression ) StatementNoShortIf else StatementNoShortIf
So, we can use our block here.
But what is the reason?
; defined as EmptyStatement ( reference ), which is also equal to StatementNoShortIf . Thus, in conditional code fragments, such as if-statement and loops, we can replace Block with EmptyStatement if StatementNoShortIf or Statement is required.
Thus, if(Expression)EmptyStatement works.
Why does this not give an error?
Pretty simple: java gives an error if it finds invalid syntax. But if(Expression)EmptyStatement is a perfectly valid syntax. Instead, javac issues a warning if it starts with the appropriate parameters. A complete list of warnings that can be turned off contains the empty warning name for this purpose. Thus, compiling with -Xlint:all or -Xlint:empty generates a warning about this.
Your IDE should also be able to enable this kind of alert. For eclipse see @nullptr answer . In IntelliJ, you can press Ctrl + Shift + A , enter empty body in the search field and enable the warning (marked on the image)

What is it even used for?
Honestly, it is of little use from a minimalist point of view. There is usually a way to succeed without the “do nothing” command. It is rather a matter of personal preference, do you prefer to use
if( a() && b() );
or
if( a() ) b();
and this applies to other cases in which EmptyStatement is used. An important point to consider on this topic is code readability. There are times when the code becomes more readable using no-op. On the other hand, there are cases when the code becomes much more difficult to understand using EmptyStatement - the above example will count on a later IMO.