Table of Contents
if Statementassert Statementswitch Statementwhile Statementdo Statementfor Statementbreak Statementcontinue Statementreturn Statementthrow Statementsynchronized Statementtry statementThe sequence of execution of a program is controlled by statements, which are executed for their effect and do not have values.
Some
statements contain other statements as part of
their structure; such other statements are substatements of the
statement. We say that
statement S immediately
contains statement U if there is no
statement T different from S
and U such that S
contains T and T
contains U. In the same manner, some statements
contain expressions (§15) as part of their
structure.
The first section of this chapter discusses the distinction between normal and abrupt completion of statements (§14.1). Most of the remaining sections explain the various kinds of statements, describing in detail both their normal behavior and any special treatment of abrupt completion.
Blocks are explained first (§14.2), followed by local class declarations (§14.3) and local variable declaration statements (§14.4).
Next a grammatical maneuver that sidesteps the familiar "dangling else" problem (§14.5) is explained.
The last section (§14.21) of this chapter addresses the requirement that every statement be reachable in a certain technical sense.
Every statement has a normal mode of execution in which certain computational steps are carried out. The following sections describe the normal mode of execution for each kind of statement.
If all the steps are carried out as described, with no indication of abrupt completion, the statement is said to complete normally. However, certain events may prevent a statement from completing normally:
The
break (§14.15), continue
(§14.16), and return
(§14.17) statements cause a transfer of
control that may prevent normal completion of statements that
contain them.
Evaluation of certain expressions may
throw exceptions from the Java Virtual Machine (§15.6). An
explicit throw (§14.18) statement also
results in an exception. An exception causes a transfer of control
that may prevent normal completion of statements.
If such an event occurs, then execution of one or more statements may be terminated before all steps of their normal mode of execution have completed; such statements are said to complete abruptly.
An abrupt completion always has an associated reason, which is one of the following:
The terms
"complete normally" and "complete abruptly" also apply to the
evaluation of expressions (§15.6). The only
reason an expression can complete abruptly is that an exception is
thrown, because of either a throw with a given value
(§14.18) or a run-time exception or error
(§11, §15.6).
If a statement evaluates an expression, abrupt completion of the expression always causes the immediate abrupt completion of the statement, with the same reason. All succeeding steps in the normal mode of execution are not performed.
Unless otherwise specified in this chapter, abrupt completion of a substatement causes the immediate abrupt completion of the statement itself, with the same reason, and all succeeding steps in the normal mode of execution of the statement are not performed.
Unless otherwise specified, a statement completes normally if all expressions it evaluates and all substatements it executes complete normally.
A block is a sequence of statements, local class declarations, and local variable declaration statements within braces.
Block:
{ BlockStatementsopt }
BlockStatements:
BlockStatement
BlockStatements BlockStatement
BlockStatement:
LocalVariableDeclarationStatement
ClassDeclaration
Statement
A block is executed by executing each of the local variable declaration statements and other statements in order from first to last (left to right). If all of these block statements complete normally, then the block completes normally. If any of these block statements complete abruptly for any reason, then the block completes abruptly for the same reason.
A local class is a nested class (§8) that is not a member of any class and that has a name (§6.2, §6.7).
All local classes are inner classes (§8.1.3).
Every local class declaration statement is immediately contained by a block (§14.2). Local class declaration statements may be intermixed freely with other kinds of statements in the block.
It is a
compile-time error if a local class declaration contains any of the
access modifiers public, protected, or private
(§6.6), or the modifier static
(§8.1.1).
The scope and shadowing of a local class declaration is specified in §6.3 and §6.4.
Example 14.3-1. Local Class Declarations
Here is an example that illustrates several aspects of the rules given above:
class Global {
class Cyclic {}
void foo() {
new Cyclic(); // create a Global.Cyclic
class Cyclic extends Cyclic {} // circular definition
{
class Local {}
{
class Local {} // compile-time error
}
class Local {} // compile-time error
class AnotherLocal {
void bar() {
class Local {} // ok
}
}
}
class Local {} // ok, not in scope of prior Local
}
}
The first statement of method foo
creates an instance of the member
class Global.Cyclic rather than an instance of the
local class Cyclic, because the local class
declaration is not yet in scope.
The fact that the scope of a local class encompasses
its own declaration (not only its body) means that the definition of
the local class Cyclic is indeed cyclic because it
extends itself rather
than Global.Cyclic. Consequently, the declaration
of the local class Cyclic will be rejected at
compile time.
Since local class names cannot be redeclared within
the same method (or constructor or initializer, as the case may be),
the second and third declarations of Local result
in compile-time errors. However, Local can be
redeclared in the context of another, more deeply nested, class such
as AnotherLocal.
The fourth and last declaration
of Local is legal, since it occurs outside the
scope of any prior declaration of Local.
A local variable declaration statement declares one or more local variable names.
LocalVariableDeclarationStatement:
LocalVariableDeclaration ;
LocalVariableDeclaration:
VariableModifiersopt Type VariableDeclarators
The following are repeated from §8.4.1 and §8.3 to make the presentation here clearer:
VariableModifiers:
VariableModifier
VariableModifiers VariableModifier
VariableModifier: one of
Annotation final
VariableDeclarators:
VariableDeclarator
VariableDeclarators , VariableDeclarator
VariableDeclarator:
VariableDeclaratorId
VariableDeclaratorId = VariableInitializer
VariableDeclaratorId:
Identifier
VariableDeclaratorId []
VariableInitializer:
Expression
ArrayInitializer
Every local variable declaration statement is immediately contained by a block. Local variable declaration statements may be intermixed freely with other kinds of statements in the block.
A local
variable declaration can also appear in the header of a for
statement (§14.14). In this case it is executed
in the same manner as if it were part of a local variable declaration
statement.
Each declarator in a local variable declaration declares one local variable, whose name is the Identifier that appears in the declarator.
If the
optional keyword final appears at the start of the declarator, the
variable being declared is a final variable
(§4.12.4).
If an
annotation a (§9.7) on a local
variable declaration corresponds to an annotation type T, and T
has a (meta-)annotation m that corresponds to
java.lang.annotation.Target, then m must have an element whose value
is java.lang.annotation.ElementType.LOCAL_VARIABLE, or a compile-time error occurs.
The declared type of a local variable is denoted by the Type that appears in the local variable declaration, followed by any bracket pairs that follow the Identifier in the declarator.
A local
variable of type float always contains a value that is an element of
the float value set (§4.2.3); similarly, a local
variable of type double always contains a value that is an element
of the double value set. It is not permitted for a local variable of
type float to contain an element of the float-extended-exponent
value set that is not also an element of the float value set, nor for
a local variable of type double to contain an element of the
double-extended-exponent value set that is not also an element of the
double value set.
The scope and shadowing of a local variable is specified in §6.3 and §6.4.
A local variable declaration statement is an executable statement. Every time it is executed, the declarators are processed in order from left to right. If a declarator has an initialization expression, the expression is evaluated and its value is assigned to the variable.
If a declarator does not have an initialization expression, then every reference to the variable must be preceded by execution of an assignment to the variable, or a compile-time error occurs by the rules of §16.
Each initialization (except the first) is executed only if evaluation of the preceding initialization expression completes normally.
Execution of the local variable declaration completes normally only if evaluation of the last initialization expression completes normally.
If the local variable declaration contains no initialization expressions, then executing it always completes normally.
There are many kinds of statements in the Java programming language. Most correspond to statements in the C and C++ languages, but some are unique.
As in C and C++, the if
statement of the Java programming language suffers from the so-called "dangling
else problem," illustrated by this misleadingly formatted
example:
if (door.isOpen())
if (resident.isVisible())
resident.greet("Hello!");
else door.bell.ring(); // A "dangling else"
The problem is that both the
outer if statement and the inner if statement might conceivably
own the else clause. In this example, one might surmise that the
programmer intended the else clause to belong to the outer if
statement.
The Java programming language, like C and C++
and many programming languages before them, arbitrarily decrees that
an else clause belongs to the innermost if to which it might
possibly belong. This rule is captured by the following
grammar:
Statement:
StatementWithoutTrailingSubstatement
LabeledStatement
IfThenStatement
IfThenElseStatement
WhileStatement
ForStatement
StatementWithoutTrailingSubstatement:
Block
EmptyStatement
ExpressionStatement
AssertStatement
SwitchStatement
DoStatement
BreakStatement
ContinueStatement
ReturnStatement
SynchronizedStatement
ThrowStatement
TryStatement
StatementNoShortIf:
StatementWithoutTrailingSubstatement
LabeledStatementNoShortIf
IfThenElseStatementNoShortIf
WhileStatementNoShortIf
ForStatementNoShortIf
The following are repeated from §14.9 to make the presentation here clearer:
IfThenStatement:
if ( Expression ) Statement
IfThenElseStatement:
if ( Expression ) StatementNoShortIf else Statement
IfThenElseStatementNoShortIf:
if ( Expression ) StatementNoShortIf else StatementNoShortIf
Statements are thus
grammatically divided into two categories: those that might end in an
if statement that has no else clause (a "short if statement")
and those that definitely do not.
Only statements that
definitely do not end in a short if statement may appear as an
immediate substatement before the keyword else in an if statement
that does have an else clause.
This simple rule prevents the
"dangling else" problem. The execution behavior of a statement with
the "no short if" restriction is identical to the execution behavior
of the same kind of statement without the "no short if" restriction;
the distinction is drawn purely to resolve the syntactic
difficulty.
Statements may have label prefixes.
LabeledStatement:
Identifier : Statement
LabeledStatementNoShortIf:
Identifier : StatementNoShortIf
The Identifier is declared to be the label of the immediately contained Statement.
Unlike C and C++, the
Java programming language has no goto statement; identifier
statement labels are used with break (§14.15)
or continue (§14.16) statements appearing
anywhere within the labeled statement.
The scope of a label of a labeled statement is the immediately contained Statement.
It is a compile-time error if the name of a label of a labeled statement (§14.7) is used within the scope of the label as a label of another labeled statement.
There is no restriction against using the same identifier as a label and as the name of a package, class, interface, method, field, parameter, or local variable. Use of an identifier to label a statement does not obscure (§6.4.2) a package, class, interface, method, field, parameter, or local variable with the same name. Use of an identifier as a class, interface, method, field, local variable or as the parameter of an exception handler (§14.20) does not obscure a statement label with the same name.
A labeled statement is executed by executing the immediately contained Statement.
If the
statement is labeled by an Identifier and the contained Statement
completes abruptly because of a break with the same Identifier,
then the labeled statement completes normally. In all other cases of
abrupt completion of the Statement, the labeled statement completes
abruptly for the same reason.
Certain kinds of expressions may be used as statements by following them with semicolons.
ExpressionStatement:
StatementExpression ;
StatementExpression:
Assignment
PreIncrementExpression
PreDecrementExpression
PostIncrementExpression
PostDecrementExpression
MethodInvocation
ClassInstanceCreationExpression
An expression statement is executed by evaluating the expression; if the expression has a value, the value is discarded.
Execution of the expression statement completes normally if and only if evaluation of the expression completes normally.
Unlike C and C++, the Java programming language allows only certain
forms of expressions to be used as expression statements. Note that
the Java programming language does not allow a "cast to void" - void is not a
type - so the traditional C trick of writing an expression statement
such as:
(void)... ; // incorrect!
does not work. On the other hand, the Java programming language
allows all the most useful kinds of expressions in expressions
statements, and it does not require a method invocation used as an
expression statement to invoke a void method, so such a trick is
almost never needed. If a trick is needed, either an assignment
statement (§15.26) or a local variable
declaration statement (§14.4) can be used
instead.
The if
statement allows conditional execution of a statement or a conditional
choice of two statements, executing one or the other but not
both.
IfThenStatement:
if ( Expression ) Statement
IfThenElseStatement:
if ( Expression ) StatementNoShortIf else Statement
IfThenElseStatementNoShortIf:
if ( Expression ) StatementNoShortIf else StatementNoShortIf
The
Expression must have type boolean or Boolean, or a compile-time
error occurs.
An if-then statement is
executed by first evaluating the Expression. If the result is of
type Boolean, it is subject to unboxing conversion
(§5.1.8).
If
evaluation of the Expression or the subsequent unboxing conversion
(if any) completes abruptly for some reason,
the if-then statement completes abruptly for the
same reason.
Otherwise, execution continues by making a choice based on the resulting value:
An if-then-else statement
is executed by first evaluating the Expression. If the result is of
type Boolean, it is subject to unboxing conversion
(§5.1.8).
If
evaluation of the Expression or the subsequent unboxing conversion
(if any) completes abruptly for some reason, then
the if-then-else statement completes abruptly for
the same reason.
Otherwise, execution continues by making a choice based on the resulting value:
If
the value is true, then the first contained Statement (the one
before the else keyword) is executed;
the if-then-else statement completes normally
if and only if execution of that statement completes
normally.
If
the value is false, then the second contained Statement (the
one after the else keyword) is executed;
the if-then-else statement completes normally
if and only if execution of that statement completes
normally.
An assertion is an
assert statement containing a boolean expression. An
assertion is either enabled
or disabled. If the assertion is enabled,
execution of the assertion causes evaluation of the boolean expression
and an error is reported if the expression evaluates to false. If
the assertion is disabled, execution of the assertion has no effect
whatsoever.
AssertStatement:
assert Expression1 ;
assert Expression1 : Expression2 ;
It is a
compile-time error if Expression1 does not have
type boolean or Boolean.
In the
second form of the assert statement, it is a compile-time error if
Expression2 is void
(§15.1).
An assert
statement that is executed after its class has completed
initialization is enabled if and only if the host system has
determined that the top level class that lexically contains the
assert statement enables assertions.
Whether or not a top level class enables assertions is determined no later than the earliest of the initialization of the top level class and the initialization of any class nested in the top level class, and cannot be changed after it has been determined.
An assert
statement that is executed before its class has completed
initialization is enabled.
This rule is motivated by a case that demands special treatment. Recall that the assertion status of a class is set no later than the time it is initialized. It is possible, though generally not desirable, to execute methods or constructors prior to initialization. This can happen when a class hierarchy contains a circularity in its static initialization, as in the following example:
public class Foo {
public static void main(String[] args) {
Baz.testAsserts();
// Will execute after Baz is initialized.
}
}
class Bar {
static {
Baz.testAsserts();
// Will execute before Baz is initialized!
}
}
class Baz extends Bar {
static void testAsserts() {
boolean enabled = false;
assert enabled = true;
System.out.println("Asserts " +
(enabled ? "enabled" : "disabled"));
}
}
Invoking Baz.testAsserts()
causes Baz to be initialized. Before this can
happen, Bar must be
initialized. Bar's static initializer again
invokes Baz.testAsserts(). Because initialization
of Baz is already in progress by the current
thread, the second invocation executes immediately,
though Baz is not initialized
(§12.4.2).
Because of the rule above, if the program above is executed without enabling assertions, it must print:
Asserts enabled Asserts disabled
A
disabled assert statement does nothing. In particular, neither
Expression1 nor Expression2
(if it is present) are evaluated. Execution of a disabled assert
statement always completes normally.
An
enabled assert statement is executed by first
evaluating Expression1. If the result is of type
Boolean, it is subject to unboxing conversion
(§5.1.8).
If
evaluation of Expression1 or the subsequent
unboxing conversion (if any) completes abruptly for some reason, the
assert statement completes abruptly for the same reason.
Otherwise, execution continues by making a choice based on the value of Expression1:
For example, after unmarshalling all of the
arguments from a data buffer, a programmer might assert that the
number of bytes of data remaining in the buffer is zero. By verifying
that the boolean expression is indeed true, the system corroborates
the programmer's knowledge of the program and increases one's
confidence that the program is free of bugs.
Typically, assertion-checking is enabled during program development and testing, and disabled for deployment, to improve performance.
Because assertions may be disabled, programs must not assume that the expressions contained in assertions will be evaluated. Thus, these boolean expressions should generally be free of side effects.
Evaluating such a boolean expression should not affect any state that is visible after the evaluation is complete. It is not illegal for a boolean expression contained in an assertion to have a side effect, but it is generally inappropriate, as it could cause program behavior to vary depending on whether assertions were enabled or disabled.
Along similar lines, assertions should not be used
for argument-checking in public methods. Argument-checking is
typically part of the contract of a method, and this contract must be
upheld whether assertions are enabled or disabled.
Another problem with using assertions for argument
checking is that erroneous arguments should result in an appropriate
run-time exception (such as IllegalArgumentException, ArrayIndexOutOfBoundsException, or
NullPointerException). An assertion failure will not throw an appropriate
exception. Again, it is not illegal to use assertions for argument
checking on public methods, but it is generally inappropriate. It is
intended that AssertionError never be caught, but it is possible to
do so, thus the rules for try statements should treat assertions
appearing in a try block similarly to the current treatment of
throw statements.
The switch statement
transfers control to one of several statements depending on the value
of an expression.
SwitchStatement:
switch ( Expression ) SwitchBlock
SwitchBlock:
{ SwitchBlockStatementGroupsopt SwitchLabelsopt }
SwitchBlockStatementGroups:
SwitchBlockStatementGroup
SwitchBlockStatementGroups SwitchBlockStatementGroup
SwitchBlockStatementGroup:
SwitchLabels BlockStatements
SwitchLabels:
SwitchLabel
SwitchLabels SwitchLabel
SwitchLabel:
case ConstantExpression :
case EnumConstantName :
default :
EnumConstantName:
Identifier
The type of the
Expression must be char, byte, short, int, Character,
Byte, Short, Integer, String, or an enum
type (§8.9), or a compile-time error
occurs.
The body
of a switch statement is known as a switch
block.
Any
statement immediately contained by the switch block may be labeled
with one or more switch labels, which are case
or default labels.
These labels
are said to be associated with the switch
statement, as are the values of the constant expressions
(§15.28) or enum constants
(§8.9.1) in the case
labels.
All of the following must be true, or a compile-time error occurs:
Every case constant expression
associated with a switch statement must be assignable
(§5.2) to the type of the switch
Expression.
No
two of the case constant expressions associated with a switch
statement may have the same value.
At
most one default label may be associated with the same switch
statement.
The prohibition against using null as a switch
label prevents one from writing code that can never be executed. If
the switch expression is of a reference type, that is, String or a
boxed primitive type or an enum type, then a run-time error will occur
if the expression evaluates to null at run time. In the judgment of
the designers of the Java programming language, this is a better
outcome than silently skipping the entire switch statement or
choosing to execute the statements (if any) after the default label
(if any).
A Java compiler is encouraged (but not required) to
provide a warning if a switch on an enum-valued expression lacks a
default label and lacks case labels for one or more of the enum
type's constants. (Such a statement will silently do nothing if the
expression evaluates to one of the missing constants.)
In C and C++ the body of a switch statement can be
a statement and statements with case labels do not have to be
immediately contained by that statement. Consider the simple
loop:
for (i = 0; i < n; ++i) foo();
where n is known to be
positive. A trick known as Duff's device can be
used in C or C++ to unroll the loop, but this is not valid code in the
Java programming language:
int q = (n+7)/8;
switch (n%8) {
case 0: do { foo(); // Great C hack, Tom,
case 7: foo(); // but it's not valid here.
case 6: foo();
case 5: foo();
case 4: foo();
case 3: foo();
case 2: foo();
case 1: foo();
} while (--q > 0);
}
Fortunately, this trick does not seem to be widely known or used. Moreover, it is less needed nowadays; this sort of code transformation is properly in the province of state-of-the-art optimizing compilers.
When the
switch statement is executed, first the Expression is
evaluated. If the Expression evaluates to null, a NullPointerException is thrown
and the entire switch statement completes abruptly for that
reason. Otherwise, if the result is of a reference type, it is subject
to unboxing conversion (§5.1.8).
If
evaluation of the Expression or the subsequent unboxing conversion
(if any) completes abruptly for some reason, the switch statement
completes abruptly for the same reason.
Otherwise, execution continues by comparing the
value of the Expression with each case constant, and there is a
choice:
If
one of the case constants is equal to the value of the
expression, then we say that the
case matches, and all statements after the
matching case label in the switch block, if any, are executed
in sequence.
If all these statements complete
normally, or if there are no statements after the matching case
label, then the entire switch statement completes
normally.
If
no case matches but there is a default label, then all
statements after the matching default label in the switch
block, if any, are executed in sequence.
If all these statements complete
normally, or if there are no statements after the default label,
then the entire switch statement completes normally.
If
no case matches and there is no default label, then no further
action is taken and the switch statement completes
normally.
If any
statement immediately contained by the Block body of the switch
statement completes abruptly, it is handled as follows:
If
execution of the Statement completes abruptly because of a
break with no label, no further action is taken and the switch
statement completes normally.
If
execution of the Statement completes abruptly for any other
reason, the switch statement completes abruptly for the same
reason.
The case of abrupt completion because of a
break with a label is handled by the general rule for labeled
statements (§14.7).
Example 14.11-1. Fall-Through in the switch Statement
As in C and C++, execution of statements in a
switch block "falls through labels."
For example, the program:
class TooMany {
static void howMany(int k) {
switch (k) {
case 1: System.out.print("one ");
case 2: System.out.print("too ");
case 3: System.out.println("many");
}
}
public static void main(String[] args) {
howMany(3);
howMany(2);
howMany(1);
}
}
contains a switch block in which the code for each
case falls through into the code for the next case. As a result,
the program prints:
many too many one too many
If code is not to fall through case to case in
this manner, then break statements should be used, as in this
example:
class TwoMany {
static void howMany(int k) {
switch (k) {
case 1: System.out.println("one");
break; // exit the switch
case 2: System.out.println("two");
break; // exit the switch
case 3: System.out.println("many");
break; // not needed, but good style
}
}
public static void main(String[] args) {
howMany(1);
howMany(2);
howMany(3);
}
}
This program prints:
one two many
The while statement
executes an Expression and a Statement repeatedly until the value
of the Expression is false.
WhileStatement:
while ( Expression ) Statement
WhileStatementNoShortIf:
while ( Expression ) StatementNoShortIf
The
Expression must have type boolean or Boolean, or a compile-time
error occurs.
A while
statement is executed by first evaluating the Expression. If the
result is of type Boolean, it is subject to unboxing conversion
(§5.1.8).
If
evaluation of the Expression or the subsequent unboxing conversion
(if any) completes abruptly for some reason, the while statement
completes abruptly for the same reason.
Otherwise, execution continues by making a choice based on the resulting value:
If
the value is true, then the contained Statement is
executed. Then there is a choice:
If execution of the Statement
completes normally, then the entire while statement is
executed again, beginning by re-evaluating the
Expression.
If execution of the Statement completes abruptly, see §14.12.1.
If
the (possibly unboxed) value of the Expression is false, no
further action is taken and the while statement completes
normally.
If the (possibly unboxed) value of the
Expression is false the first time it is evaluated, then the
Statement is not executed.
Abrupt completion of the contained Statement is handled in the following manner:
If execution of the Statement
completes abruptly because of a break with no label, no further
action is taken and the while statement completes
normally.
If execution of the Statement
completes abruptly because of a continue with no label, then the
entire while statement is executed again.
If execution of the Statement
completes abruptly because of a continue with label L, then
there is a choice:
If execution of the Statement
completes abruptly for any other reason, the while statement
completes abruptly for the same reason.
The case of abrupt completion because of a
break with a label is handled by the general rule for labeled
statements (§14.7).
The do statement executes a
Statement and an Expression repeatedly until the value of the
Expression is false.
DoStatement:
do Statement while ( Expression ) ;
The
Expression must have type boolean or Boolean, or a compile-time
error occurs.
A do
statement is executed by first executing the Statement. Then there
is a choice:
If
execution of the Statement completes normally, then the
Expression is evaluated. If the result is of type Boolean, it
is subject to unboxing conversion
(§5.1.8).
If evaluation of the Expression or
the subsequent unboxing conversion (if any) completes abruptly for
some reason, the do statement completes abruptly for the same
reason.
If execution of the Statement completes abruptly, see §14.13.1.
Executing a do statement always executes the
contained Statement at least once.
Abrupt completion of the contained Statement is handled in the following manner:
If execution of the Statement
completes abruptly because of a break with no label, then no
further action is taken and the do statement completes
normally.
If execution of the Statement
completes abruptly because of a continue with no label, then the
Expression is evaluated. Then there is a choice based on the
resulting value:
If execution of the Statement
completes abruptly because of a continue with label L, then
there is a choice:
If execution of the Statement
completes abruptly for any other reason, the do statement
completes abruptly for the same reason.
The case of abrupt completion because of a
break with a label is handled by the general rule for labeled
statements (§14.7).
Example 14.13-1. The do Statement
The following code is one possible implementation of
the toHexString method of class Integer:
public static String toHexString(int i) {
StringBuffer buf = new StringBuffer(8);
do {
buf.append(Character.forDigit(i & 0xF, 16));
i >>>= 4;
} while (i != 0);
return buf.reverse().toString();
}
Because at least one digit must be generated, the
do statement is an appropriate control structure.
The for statement has two
forms:
ForStatement:
BasicForStatement
EnhancedForStatement
The basic for statement
executes some initialization code, then executes an Expression, a
Statement, and some update code repeatedly until the value of the
Expression is false.
BasicForStatement:
for ( ForInitopt ; Expressionopt ; ForUpdateopt ) Statement
ForStatementNoShortIf:
for ( ForInitopt ; Expressionopt ; ForUpdateopt ) StatementNoShortIf
ForInit:
StatementExpressionList
LocalVariableDeclaration
ForUpdate:
StatementExpressionList
StatementExpressionList:
StatementExpression
StatementExpressionList , StatementExpression
The
Expression must have type boolean or Boolean, or a compile-time
error occurs.
The scope and shadowing of a local
variable declared in the ForInit part of a basic
for statement is specified in §6.3 and
§6.4.
A
for statement is executed by first executing
the ForInit code:
If the ForInit code is a list of statement expressions (§14.8), the expressions are evaluated in sequence from left to right; their values, if any, are discarded.
If evaluation of any expression
completes abruptly for some reason, the for statement completes
abruptly for the same reason; any ForInit
statement expressions to the right of the one that completed
abruptly are not evaluated.
If the ForInit code is a local variable declaration, it is executed as if it were a local variable declaration statement (§14.4) appearing in a block.
If execution of the local variable
declaration completes abruptly for any reason, the for statement
completes abruptly for the same reason.
Next,
a for iteration step is performed, as follows:
If the Expression is present, it is
evaluated. If the result is of type Boolean, it is subject to
unboxing conversion (§5.1.8).
If evaluation of the Expression
or the subsequent unboxing conversion (if any) completes abruptly,
the for statement completes abruptly for the same reason.
Otherwise, there is then a choice based on the presence or absence of the Expression and the resulting value if the Expression is present; see next bullet.
If the Expression is not present,
or it is present and the value resulting from its evaluation
(including any possible unboxing) is true, then the contained
Statement is executed. Then there is a choice:
If execution of the Statement completes normally, then the following two steps are performed in sequence:
First, if
the ForUpdate part is present, the
expressions are evaluated in sequence from left to right;
their values, if any, are discarded. If evaluation of any
expression completes abruptly for some reason, the for
statement completes abruptly for the same reason;
any ForUpdate statement expressions
to the right of the one that completed abruptly are not
evaluated.
If execution of the Statement completes abruptly, see §14.14.1.3.
If the Expression is present and
the value resulting from its evaluation (including any possible
unboxing) is false, no further action is taken and the for
statement completes normally.
If the (possibly unboxed) value of the
Expression is false the first time it is evaluated, then the
Statement is not executed.
If
the Expression is not present, then the only way a for statement
can complete normally is by use of a break statement.
Abrupt completion of the contained Statement is handled in the following manner:
If execution of the Statement
completes abruptly because of a break with no label, no further
action is taken and the for statement completes normally.
If execution of the Statement
completes abruptly because of a continue with no label, then the
following two steps are performed in sequence:
If execution of the Statement
completes abruptly because of a continue with
label L, then there is a choice:
If execution of the Statement
completes abruptly for any other reason, the for statement
completes abruptly for the same reason.
Note that the case of abrupt completion because
of a break with a label is handled by the general rule for
labeled statements (§14.7).
The
enhanced for statement has the form:
EnhancedForStatement:
for ( FormalParameter : Expression ) Statement
The following is repeated from §8.4.1 and §8.3 to make the presentation here clearer:
FormalParameter:
VariableModifiersopt Type VariableDeclaratorId
VariableDeclaratorId:
Identifier
VariableDeclaratorId []
The type
of the Expression must be Iterable or an array type
(§10.1), or a compile-time error occurs.
The scope and shadowing of a local
variable declared in the FormalParameter part of
an enhanced for statement is specified in §6.3
and §6.4.
The
meaning of the enhanced for statement is given by translation into a
basic for statement, as follows:
If the
type of Expression is a subtype of Iterable, then the
translation is as follows.
If the type of Expression is a
subtype of Iterable<X> for some type argument X, then
let I be the
type java.util.Iterator<X>; otherwise,
let I be the raw
type java.util.Iterator.
The enhanced for statement is
equivalent to a basic for statement of the form:
for (I #i = Expression.iterator(); #i.hasNext(); ) { VariableModifiersopt TargetType Identifier = (TargetType) #i.next(); Statement }
#i is an
automatically generated identifier that is distinct from any
other identifiers (automatically generated or otherwise) that are
in scope (§6.3) at the point where the
enhanced for statement occurs.
If
Type (in
the FormalParameter production) is a
reference type, then TargetType
is Type;
otherwise, TargetType is the upper bound of
the capture conversion of the type argument of I, or Object
if I is raw.
List<? extends Integer> l = ...
for (float i : l) ...
will be translated to:
for (Iterator<Integer> #i = l.iterator(); #i.hasNext(); ) {
float #i0 = (Integer)#i.next();
...
Otherwise, the Expression necessarily
has an array type, T[].
Let L1 ... Lm be the (possibly
empty) sequence of labels immediately preceding the enhanced for
statement.
The enhanced for statement is
equivalent to a basic for statement of the form:
T[]#a = Expression;L1:L2: ...Lm: for (int #i = 0; #i < #a.length; #i++) { VariableModifiersopt TargetType Identifier = #a[#i]; Statement }
#a
and #i are automatically generated identifiers
that are distinct from any other identifiers (automatically
generated or otherwise) that are in scope at the point where the
enhanced for statement occurs.
TargetType is the type of the loop variable as denoted by the Type that appears in the FormalParameter followed by any bracket pairs that follow the Identifier in the FormalParameter (§10.2).
Example 14.14-1. Enhanced-for And Arrays
The following program, which calculates the sum of
an integer array, shows how enhanced for works for arrays:
int sum(int[] a) {
int sum = 0;
for (int i : a) sum += i;
return sum;
}
Example 14.14-2. Enhanced-for And Unboxing Conversion
The following program combines the enhanced for
statement with auto-unboxing to translate a histogram into a frequency
table:
Map<String, Integer> histogram = ...;
double total = 0;
for (int i : histogram.values())
total += i;
for (Map.Entry<String, Integer> e : histogram.entrySet())
System.out.println(e.getKey() + " " + e.getValue() / total);
}
A break
statement transfers control out of an enclosing statement.
BreakStatement:
break Identifieropt ;
A break
statement with no label attempts to transfer control to the innermost
enclosing switch, while, do, or for statement of the
immediately enclosing method or initializer; this statement, which is
called the break target, then immediately
completes normally.
To be
precise, a break statement with no label always completes abruptly,
the reason being a break with no label.
If no
switch, while, do, or for statement in the immediately
enclosing method, constructor, or initializer contains the break
statement, a compile-time error occurs.
A break
statement with label Identifier attempts to transfer control to the
enclosing labeled statement (§14.7) that has the
same Identifier as its label; this statement, which is called
the break target, then immediately completes
normally. In this case, the break target need not be a switch,
while, do, or for statement.
To be
precise, a break statement with label Identifier always completes
abruptly, the reason being a break with label Identifier.
A break
statement must refer to a label within the immediately enclosing
method, constructor, or initializer. There are no non-local jumps. If
no labeled statement with Identifier as its label in the
immediately enclosing method, constructor, or initializer
contains the break statement, a compile-time error occurs.
It can be seen, then, that a break statement
always completes abruptly.
The preceding
descriptions say "attempts to transfer control" rather than just
"transfers control" because if there are any try statements
(§14.20) within the break target whose try
blocks or catch clauses contain the break
statement, then any finally clauses of those try statements are
executed, in order, innermost to outermost, before control is
transferred to the break target. Abrupt completion of a finally
clause can disrupt the transfer of control initiated by a break
statement.
Example 14.15-1. The break Statement
In the following example, a mathematical graph is
represented by an array of arrays. A graph consists of a set of nodes
and a set of edges; each edge is an arrow that points from some node
to some other node, or from a node to itself. In this example it is
assumed that there are no redundant edges; that is, for any two
nodes P and Q,
where Q may be the same as P,
there is at most one edge from P
to Q.
Nodes are represented by integers, and there is an
edge from node i to
node edges[
for every i][j]i and j for which the
array
reference edges[
does not throw an i][j]ArrayIndexOutOfBoundsException.
The task of the method loseEdges,
given integers i and j, is to
construct a new graph by copying a given graph but omitting the edge
from node i to node j, if any,
and the edge from node j to
node i, if any:
class Graph {
int edges[][];
public Graph(int[][] edges) { this.edges = edges; }
public Graph loseEdges(int i, int j) {
int n = edges.length;
int[][] newedges = new int[n][];
for (int k = 0; k < n; ++k) {
edgelist:
{
int z;
search:
{
if (k == i) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == j) break search;
}
} else if (k == j) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == i) break search;
}
}
// No edge to be deleted; share this list.
newedges[k] = edges[k];
break edgelist;
} //search
// Copy the list, omitting the edge at position z.
int m = edges[k].length - 1;
int ne[] = new int[m];
System.arraycopy(edges[k], 0, ne, 0, z);
System.arraycopy(edges[k], z+1, ne, z, m-z);
newedges[k] = ne;
} //edgelist
}
return new Graph(newedges);
}
}
Note the use of two statement
labels, edgelist and search, and
the use of break statements. This allows the code that copies a
list, omitting one edge, to be shared between two separate tests, the
test for an edge from node i to
node j, and the test for an edge from
node j to node i.
A
continue statement may occur only in a while, do, or for
statement; statements of these three kinds are
called iteration statements. Control passes to
the loop-continuation point of an iteration statement.
ContinueStatement:
continue Identifieropt ;
A
continue statement with no label attempts to transfer control to the
innermost enclosing while, do, or for statement of the
immediately enclosing method, constructor, or initializer; this
statement, which is called the continue target,
then immediately ends the current iteration and begins a new
one.
To be
precise, such a continue statement always completes abruptly, the
reason being a continue with no label.
If no
while, do, or for statement of the immediately enclosing method,
constructor, or initializer contains the continue statement, a
compile-time error occurs.
A
continue statement with label Identifier attempts to transfer
control to the enclosing labeled statement
(§14.7) that has the same Identifier as its
label; that statement, which is called the continue
target, then immediately ends the current iteration and
begins a new one.
To be
precise, a continue statement with label Identifier always
completes abruptly, the reason being a continue with label
Identifier.
The
continue target must be a while, do, or for statement, or a
compile-time error occurs.
A
continue statement must refer to a label within the immediately
enclosing method, constructor, or initializer. There are no non-local
jumps. If no labeled statement with Identifier as its
label in the immediately enclosing method, constructor, or
initializer contains the continue statement, a
compile-time error occurs.
It can be seen, then, that a continue statement
always completes abruptly.
See the descriptions of the while statement
(§14.12), do statement
(§14.13), and for statement
(§14.14) for a discussion of the handling of
abrupt termination because of continue.
The preceding
descriptions say "attempts to transfer control" rather than just
"transfers control" because if there are any try statements
(§14.20) within the continue target whose try
blocks or catch clauses contain the continue
statement, then any finally clauses of those try statements are
executed, in order, innermost to outermost, before control is
transferred to the continue target. Abrupt completion of a finally
clause can disrupt the transfer of control initiated by a continue
statement.
Example 14.16-1. The continue Statement
In the Graph class in
§14.15, one of the break statements is used to
finish execution of the entire body of the outermost for loop. This
break can be replaced by a continue if the for loop itself is
labeled:
class Graph {
int edges[][];
public Graph(int[][] edges) { this.edges = edges; }
public Graph loseEdges(int i, int j) {
int n = edges.length;
int[][] newedges = new int[n][];
edgelists:
for (int k = 0; k < n; ++k) {
int z;
search:
{
if (k == i) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == j) break search;
}
} else if (k == j) {
for (z = 0; z < edges[k].length; ++z) {
if (edges[k][z] == i) break search;
}
}
// No edge to be deleted; share this list.
newedges[k] = edges[k];
continue edgelists;
} //search
// Copy the list, omitting the edge at position z.
int m = edges[k].length - 1;
int ne[] = new int[m];
System.arraycopy(edges[k], 0, ne, 0, z);
System.arraycopy(edges[k], z+1, ne, z, m-z);
newedges[k] = ne;
} //edgelists
return new Graph(newedges);
}
}
Which to use, if either, is largely a matter of programming style.
A
return statement returns control to the invoker of a method
(§8.4, §15.12) or
constructor (§8.8,
§15.9).
ReturnStatement:
return Expressionopt ;
A return
statement is contained in the innermost
constructor, method, or initializer whose body encloses the return
statement.
It is a
compile-time error if a return statement is contained in an instance initializer
or a static initializer (§8.6,
§8.7).
A return
statement with no Expression must be contained in a method that is declared, using the keyword void, not to return
any value (§8.4), or in a
constructor (§8.8), or a compile-time error
occurs.
A
return statement with no Expression attempts to transfer control
to the invoker of the method or constructor that contains it. To be
precise, a return statement with no Expression always completes
abruptly, the reason being a return with no value.
A return
statement with an Expression must be contained in a method
declaration that is declared to return a value
(§8.4), or a compile-time error occurs.
The Expression must denote a variable or value of some type T, or a compile-time error occurs.
The type T must be assignable (§5.2) to the declared result type of the method, or a compile-time error occurs.
A
return statement with an Expression attempts to transfer control
to the invoker of the method that contains it; the value of the
Expression becomes the value of the method invocation. More
precisely, execution of such a return statement first evaluates the
Expression. If the evaluation of the Expression completes abruptly
for some reason, then the return statement completes abruptly for
that reason. If evaluation of the Expression completes normally,
producing a value V, then the return statement
completes abruptly, the reason being a return with
value V.
If the
expression is of type float and is not FP-strict
(§15.4), then the value may be an element of
either the float value set or the float-extended-exponent value set
(§4.2.3). If the expression is of type double
and is not FP-strict, then the value may be an element of either the
double value set or the double-extended-exponent value set.
It can be seen, then, that a return statement
always completes abruptly.
The preceding
descriptions say "attempts to transfer control" rather than just
"transfers control" because if there are any try statements
(§14.20) within the method or constructor whose
try blocks or catch clauses contain the
return statement, then any finally clauses of those try
statements will be executed, in order, innermost to outermost, before
control is transferred to the invoker of the method or
constructor. Abrupt completion of a finally clause can disrupt the
transfer of control initiated by a return statement.
A throw
statement causes an exception (§11) to be
thrown. The result is an immediate transfer of control
(§11.3) that may exit multiple statements and
multiple constructor, instance initializer, static initializer and
field initializer evaluations, and method invocations until a try
statement (§14.20) is found that catches the
thrown value. If no such try statement is found, then execution of
the thread (§17) that executed the throw is
terminated (§11.3) after invocation of
the uncaughtException method for the thread group
to which the thread belongs.
ThrowStatement:
throw Expression ;
The
Expression in a throw statement must denote either 1) a variable
or value of a reference type which is assignable
(§5.2) to the type Throwable, or 2)
the null reference, or a compile-time error
occurs.
The reference type of the Expression will always
be a class type (since no interface types are assignable to
Throwable) which is not parameterized (since a subclass of
Throwable cannot be generic (§8.1.2)).
At least one of the following three conditions must be true, or a compile-time error occurs:
The type of the Expression is an unchecked exception class (§11.1.1) or the null type (§4.1).
The
throw statement is contained in the try block of a try
statement (§14.20) and it is not the case that the try
statement can throw an exception of the type of the
Expression. (In this case we say the thrown value
is caught by the try statement.)
The
throw statement is contained in a method or constructor
declaration and the type of the Expression is assignable
(§5.2) to at least one type listed in the
throws clause (§8.4.6,
§8.8.5) of the declaration.
The
exception types that a throw statement can throw are specified in
§11.2.2.
A throw
statement first evaluates the Expression. Then:
If
evaluation of the Expression completes abruptly for some reason,
then the throw completes abruptly for that reason.
If
evaluation of the Expression completes normally, producing a
non-null value V, then the throw statement
completes abruptly, the reason being a throw with
value V.
If
evaluation of the Expression completes normally, producing a
null value, then an instance V' of class
NullPointerException is created and thrown instead of null. The throw
statement then completes abruptly, the reason being a throw with
value V'.
It can be seen, then, that a throw statement
always completes abruptly.
If there
are any enclosing try statements (§14.20) whose
try blocks contain the throw statement, then any finally clauses
of those try statements are executed as control is transferred
outward, until the thrown value is caught. Note that abrupt completion
of a finally clause can disrupt the transfer of control initiated by
a throw statement.
If a
throw statement is contained in a method declaration, but its value
is not caught by some try statement that contains it, then the
invocation of the method completes abruptly because of the
throw.
If a
throw statement is contained in a constructor declaration, but its
value is not caught by some try statement that contains it, then the
class instance creation expression that invoked the constructor will
complete abruptly because of the throw
(§15.9.4).
If a
throw statement is contained in a static initializer
(§8.7), then a compile-time check
(§11.2.3) ensures that either its value is always
an unchecked exception or its value is always caught by some try
statement that contains it. If at run time, despite this check, the
value is not caught by some try statement that contains the throw
statement, then the value is rethrown if it is an instance of class
Error or one of its subclasses; otherwise, it is wrapped in an
ExceptionInInitializerError object, which is then thrown
(§12.4.2).
If a
throw statement is contained in an instance initializer
(§8.6), then a compile-time check
(§11.2.3) ensures that either its value is always
an unchecked exception or its value is always caught by some try
statement that contains it, or the type of the thrown exception (or
one of its superclasses) occurs in the throws clause of every
constructor of the class.
By convention, user-declared throwable types should
usually be declared to be subclasses of class Exception, which is a
subclass of class Throwable (§11.1.1).
A
synchronized statement acquires a mutual-exclusion lock
(§17.1) on behalf of the executing thread,
executes a block, then releases the lock. While the executing thread
owns the lock, no other thread may acquire the lock.
SynchronizedStatement:
synchronized ( Expression ) Block
The type of Expression must be a reference type, or a compile-time error occurs.
A
synchronized statement is executed by first evaluating the
Expression. Then:
If
evaluation of the Expression completes abruptly for some reason,
then the synchronized statement completes abruptly for the same
reason.
Otherwise, if the value of the
Expression is null, a NullPointerException is thrown.
Otherwise, let the non-null value of
the Expression be V. The executing thread
locks the monitor associated
with V. Then the Block is executed, and then
there is a choice:
The locks
acquired by synchronized statements are the same as the locks that
are acquired implicitly by synchronized methods
(§8.4.3.6). A single thread
may acquire a lock more than once.
Acquiring
the lock associated with an object does not in itself prevent other
threads from accessing fields of the object or invoking
un-synchronized methods on the object. Other threads can also use
synchronized methods or the synchronized statement in a
conventional manner to achieve mutual exclusion.
Example 14.19-1. The synchronized Statement
class Test {
public static void main(String[] args) {
Test t = new Test();
synchronized(t) {
synchronized(t) {
System.out.println("made it!");
}
}
}
}
This program produces the output:
made it!
Note that this program would deadlock if a single thread were not permitted to lock a monitor more than once.
A try
statement executes a block. If a value is thrown and the try
statement has one or more catch clauses that can catch it, then
control will be transferred to the first such catch clause. If the
try statement has a finally clause, then another block of code is
executed, no matter whether the try block completes normally or
abruptly, and no matter whether a catch clause is first given
control.
TryStatement:
try Block Catches
try Block Catchesopt Finally
TryWithResourcesStatement
Catches:
CatchClause
Catches CatchClause
CatchClause:
catch ( CatchFormalParameter ) Block
CatchFormalParameter:
VariableModifiersopt CatchType VariableDeclaratorId
CatchType:
ClassType
ClassType | CatchType
Finally:
finally Block
The Block immediately after
the keyword try is called the try block of
the try statement.
The Block immediately after
the keyword finally is called the finally
block of the try statement.
A try statement may have
catch clauses, also called exception
handlers.
A catch
clause has exactly one parameter, which is called
an exception parameter.
The scope and shadowing of an exception parameter is specified in §6.3 and §6.4.
An exception
parameter may denote its type as either a single class type or a union
of two or more class types
(called alternatives). The alternatives of a
union are syntactically separated by |.
A catch clause whose exception
parameter is denoted as a single class type is called
a uni-catch clause.
A catch clause whose exception
parameter is denoted as a union of types is called
a multi-catch clause.
Each class type
used in the denotation of the type of an exception parameter must be
the class Throwable or a subclass of
Throwable, or a compile-time error occurs.
It is a compile-time error if a type variable is used in the denotation of the type of an exception parameter.
It is a compile-time error if a union of types contains two alternatives Di and Dj (i ≠ j) where Di is a subtype of Dj (§4.10.2).
The declared type of an exception parameter that denotes its type with a single class type is that class type.
The declared type
of an exception parameter that denotes its type as a union with
alternatives D1 | D2 | ... | Dn
is lub(D1, D2, ..., Dn)
(§15.12.2.7).
An exception
parameter of a multi-catch clause is implicitly declared final if
it is not explicitly declared final.
It is a
compile-time error if an exception parameter that
is implicitly or explicitly declared final is
assigned to within the body of the catch clause.
In a uni-catch
clause, an exception parameter that is not declared final
(implicitly or explicitly) is considered effectively
final if it never occurs within its scope as the left-hand
operand of an assignment operator
(§15.26).
An implicitly final exception parameter is final
by virtue of its declaration, while an effectively final exception
parameter is (as it were) final by virtue of how it is used. An
exception parameter of a multi-catch clause is implicitly final, so
will never occur as the left-hand operand of an assignment operator,
but it is not considered effectively
final.
If an exception parameter is effectively final (in a
uni-catch clause) or implicitly final (in a multi-catch clause),
then adding an explicit final modifier to its declaration will not
introduce any compile-time errors. However, if the exception parameter
of a uni-catch clause is explicitly declared
final, then removing the final modifier may introduce compile-time
errors. This is because the exception parameter, while still
effectively final, can no longer be referenced by, for example, local
classes. On the other hand, if there are no compile-time errors, it is
possible to further change the program so that the exception parameter
is re-assigned and no longer effectively final.
The
exception types that a try statement can throw are specified in
§11.2.2.
The
relationship of the exceptions thrown by the try block of a try
statement and caught by the catch clauses (if any) of the try
statement is specified in §11.2.3.
Exception
handlers are considered in left-to-right order: the earliest possible
catch clause accepts the exception, receiving as its argument the
thrown exception object, as specified in
§11.3.
A multi-catch clause can be thought of as a
sequence of uni-catch clauses. That is, a catch clause whose
exception parameter type is denoted as a union
D1|D2|...|Dn is equivalent to a sequence of
n catch clauses whose exception parameters have class types
D1, D2, ..., Dn, respectively. For example, the following
code:
try {
... throws ReflectiveOperationException ...
}
catch (ClassNotFoundException | IllegalAccessException ex) {
... body ...
}
is semantically equivalent to the following code:
try {
... throws ReflectiveOperationException ...
} catch (final ClassNotFoundException ex1) {
... body ...
} catch (final IllegalAccessException ex2) {
... body ...
}
whereby the multi-catch clause with two
alternatives has been translated into two separate catch clauses,
one for each alternative. A Java compiler is neither required nor
recommended to compile a multi-catch clause by duplicating code in
this manner, since it is possible to represent the multi-catch
clause in a class file without duplication.
A finally clause ensures
that the finally block is executed after the try block and any
catch block that might be executed, no matter how control leaves the
try block or catch block. Handling of the finally block is
rather complex, so the two cases of a try statement with and without
a finally block are described separately
(§14.20.1, §14.20.2).
A try statement is permitted to omit
catch clauses and a finally clause if it is
a try-with-resources statement
(§14.20.3).
A try
statement without a finally block is executed by first executing the
try block. Then there is a choice:
If execution of the try block
completes normally, then no further action is taken and the try
statement completes normally.
If execution of the try block
completes abruptly because of a throw of a
value V, then there is a choice:
If the run-time type
of V is assignment compatible with
(§5.2) a catchable exception class of any
catch clause of the try statement, then the first
(leftmost) such catch clause is selected. The
value V is assigned to the parameter of the
selected catch clause, and the Block of that catch
clause is executed, and then there is a choice:
If the run-time
type of V is not assignment compatible with
a catchable exception class of any catch clause of the try
statement, then the try statement completes abruptly because
of a throw of the value V.
If
execution of the try block completes abruptly for any other
reason, then the try statement completes abruptly for the same
reason.
Example 14.20.1-1. Catching An Exception
class BlewIt extends Exception {
BlewIt() { }
BlewIt(String s) { super(s); }
}
class Test {
static void blowUp() throws BlewIt { throw new BlewIt(); }
public static void main(String[] args) {
try {
blowUp();
} catch (RuntimeException r) {
System.out.println("Caught RuntimeException");
} catch (BlewIt b) {
System.out.println("Caught BlewIt");
}
}
}
Here, the exception BlewIt is
thrown by the
method blowUp. The try-catch
statement in the body of main has two catch
clauses. The run-time type of the exception
is BlewIt which is not assignable to a variable of
type RuntimeException, but is assignable to a variable of
type BlewIt, so the output of the example
is:
Caught BlewIt
A try
statement with a finally block is executed by first executing the
try block. Then there is a choice:
If execution of the try block
completes normally, then the finally block is executed, and then
there is a choice:
If
execution of the try block completes abruptly because of a
throw of a value V, then there is a
choice:
If the run-time type
of V is assignment compatible with a
catchable exception class of any catch clause of the try
statement, then the first (leftmost) such catch clause is
selected. The value V is assigned to the
parameter of the selected catch clause, and the Block of
that catch clause is executed. Then there is a
choice:
If the run-time type
of V is not assignment compatible with a
catchable exception class of any catch clause of the try
statement, then the finally block is executed. Then there is
a choice:
If
execution of the try block completes abruptly for any other
reason R, then the finally block is executed,
and then there is a choice:
Example 14.20.2-1. Handling An Uncaught Exception With finally
class BlewIt extends Exception {
BlewIt() { }
BlewIt(String s) { super(s); }
}
class Test {
static void blowUp() throws BlewIt {
throw new NullPointerException();
}
public static void main(String[] args) {
try {
blowUp();
} catch (BlewIt b) {
System.out.println("Caught BlewIt");
} finally {
System.out.println("Uncaught Exception");
}
}
}
This program produces the output:
Uncaught Exception
Exception in thread "main" java.lang.NullPointerException
at Test.blowUp(Test.java:7)
at Test.main(Test.java:11)
The NullPointerException (which is a kind of RuntimeException)
that is thrown by method blowUp is not caught by
the try statement in main, because a NullPointerException is not
assignable to a variable of type BlewIt. This
causes the finally clause to execute, after which the thread
executing main, which is the only thread of the
test program, terminates because of an uncaught exception, which
typically results in printing the exception name and a simple
backtrace. However, a backtrace is not required by this
specification.
The problem with mandating a backtrace is that an exception can be created at one point in the program and thrown at a later one. It is prohibitively expensive to store a stack trace in an exception unless it is actually thrown (in which case the trace may be generated while unwinding the stack). Hence we do not mandate a back trace in every exception.
A
try-with-resources statement is parameterized with variables (known
as resources) that are initialized before
execution of the try block and closed automatically, in the reverse
order from which they were initialized, after execution of the try
block. catch clauses and a finally clause are often unnecessary
when resources are closed automatically.
TryWithResourcesStatement:
try ResourceSpecification Block Catchesopt Finallyopt
ResourceSpecification:
( Resources ;opt )
Resources:
Resource
Resource ; Resources
Resource:
VariableModifiersopt Type VariableDeclaratorId = Expression
A ResourceSpecification
declares one or more local variables with initializer expressions to
act as resources for the try statement.
A resource
declared in a ResourceSpecification is implicitly
declared final (§4.12.4) if it is not
explicitly declared final.
The type of a
variable declared in a ResourceSpecification must
be a subtype of AutoCloseable, or a compile-time
error occurs.
The scope and shadowing of a variable declared in a ResourceSpecification is specified in §6.3 and §6.4.
It is a compile-time error for a ResourceSpecification to declare two variables with the same name.
Resources are
initialized in left-to-right order. If a resource fails to initialize
(that is, its initializer expression throws an exception), then all
resources initialized so far by the try-with-resources statement are
closed. If all resources initialize successfully, the try block
executes as normal and then all non-null resources of the
try-with-resources statement are closed.
Resources are
closed in the reverse order from that in which they were
initialized. A resource is closed only if it initialized to a non-null
value. An exception from the closing of one resource does not prevent
the closing of other resources. Such an exception
is suppressed if an exception was thrown
previously by an initializer, the try block, or the closing of a
resource.
A
try-with-resources statement with a
ResourceSpecification clause that declares
multiple resources is treated as if it were multiple
try-with-resources statements, each of which has a
ResourceSpecification clause that declares a
single Resource. When a try-with-resources
statement with n Resources (n > 1)
is translated, the result is a try-with-resources statement with
n-1 Resources. After n such
translations, there are n nested try-catch-finally
statements, and the overall translation is complete.
A try-with-resources statement with
no catch clauses or finally clause is called
a basic try-with-resources statement.
The meaning of a
basic try-with-resources statement:
try (VariableModifiersopt R Identifier = Expression ...) Block
is given by the
following translation to a local variable declaration and a
try-catch-finally statement:
{
final VariableModifiers_minus_final R Identifier = Expression;
Throwable #primaryExc = null;
try ResourceSpecification_tail
Block
catch (Throwable #t) {
#primaryExc = #t;
throw #t;
} finally {
if (Identifier != null) {
if (#primaryExc != null) {
try {
Identifier.close();
} catch (Throwable #suppressedExc) {
#primaryExc.addSuppressed(#suppressedExc);
}
} else {
Identifier.close();
}
}
}
}
VariableModifiersopt_minus_final
is defined as VariableModifiersopt without
final, if present.
#t, #primaryExc,
and #suppressedExc are automatically generated
identifiers that are distinct from any other identifiers
(automatically generated or otherwise) that are in scope at the point
where the try-with-resources statement occurs.
If the
ResourceSpecification declares one resource,
then ResourceSpecification_tail is empty (and the
try-catch-finally statement is not itself a try-with-resources
statement).
If the
ResourceSpecification declares n > 1
resources, then ResourceSpecification_tail
consists of the 2nd, 3rd, ..., n'th resources declared
in ResourceSpecification, in the same order (and
the try-catch-finally statement is itself a try-with-resources
statement).
Reachability and
definite assignment rules for the basic try-with-resources statement
are implicitly specified by the translation above.
In a basic
try-with-resources statement that manages a single resource:
If the
initialization of the resource completes abruptly because of a
throw of a value V, then the
try-with-resources statement completes abruptly because of a
throw of the value V.
If the
initialization of the resource completes normally, and the try
block completes abruptly because of a throw of a
value V, then:
If the automatic closing of
the resource completes normally, then the
try-with-resources statement completes abruptly because of
a throw of the value V.
If the automatic closing of
the resource completes abruptly because of a throw of a
value V2, then the try-with-resources
statement completes abruptly because of a throw of
value V with V2 added
to the suppressed exception list
of V.
If the
initialization of the resource completes normally, and the try
block completes normally, and the automatic closing of the
resource completes abruptly because of a throw of a
value V, then the try-with-resources
statement completes abruptly because of a throw of the
value V.
In a basic
try-with-resources statement that manages multiple resources:
If the
initialization of a resource completes abruptly because of a
throw of a value V, then:
If the automatic closings of
all successfully initialized resources (possibly zero)
complete normally, then the try-with-resources statement
completes abruptly because of a throw of the
value V.
If the automatic closings of
all successfully initialized resources (possibly zero)
complete abruptly because of throws of
values V1...Vn, then
the try-with-resources statement completes abruptly because
of a throw of the value V with any
remaining
values V1...Vn added to
the suppressed exception list of V.
If the
initialization of all resources completes normally, and the try
block completes abruptly because of a throw of a
value V, then:
If the automatic closings of
all initialized resources complete normally, then the
try-with-resources statement completes abruptly because of
a throw of the value V.
If the automatic closings of
one or more initialized resources complete abruptly because
of throws of
values V1...Vn, then
the try-with-resources statement completes abruptly because
of a throw of the value V with any
remaining
values V1...Vn added to
the suppressed exception list of V.
If the
initialization of every resource completes normally, and the
try block completes normally, then:
If one automatic closing of an
initialized resource completes abruptly because of a throw
of value V, and all other automatic
closings of initialized resources complete normally, then the
try-with-resources statement completes abruptly because of
a throw of the value V.
If more than one automatic
closing of an initialized resource completes abruptly because
of throws of
values V1...Vn, then
the try-with-resources statement completes abruptly because
of a throw of the value V1 with any
remaining
values V2...Vn added to
the suppressed exception list of V1
(where V1 is the exception from the
rightmost resource failing to close and Vn
is the exception from the leftmost resource failing to
close).
A try-with-resources statement with
at least one catch clause and/or a finally clause is called
an extended try-with-resources
statement.
The meaning of an
extended try-with-resources statement:
try ResourceSpecification Block Catchesopt Finallyopt
is given by the
following translation to a basic try-with-resources
statement
(§14.20.3.1) nested inside a try-catch or
try-finally or try-catch-finally statement:
try {
try ResourceSpecification
Block
}
Catchesopt
Finallyopt
The effect of the
translation is to put the ResourceSpecification
"inside" the try statement. This allows a catch clause of an
extended try-with-resources statement to catch an exception due to
the automatic initialization or closing of any resource.
Furthermore, all
resources will have been closed (or attempted to be closed) by the
time the finally block is executed, in keeping with the intent of
the finally keyword.
It is a compile-time error if a statement cannot be executed because it is unreachable.
This section is devoted to a precise explanation of
the word "reachable." The idea is that there must be some possible
execution path from the beginning of the constructor, method, instance
initializer, or static initializer that contains the statement to the
statement itself. The analysis takes into account the structure of
statements. Except for the special treatment of while, do, and
for statements whose condition expression has the constant value
true, the values of expressions are not taken into account in the
flow analysis.
For example, a Java compiler will accept the code:
{
int n = 5;
while (n > 7) k = 2;
}
even though the value of n is
known at compile time and in principle it can be known at compile time
that the assignment to k can never be
executed.
The rules in this section define two technical terms:
The definitions here allow a statement to complete normally only if it is reachable.
To shorten the description of the rules, the customary abbreviation "iff" is used to mean "if and only if."
A reachable break
statement exits a statement if, within the break
target, either there are no try statements whose try blocks
contain the break statement, or there are try statements whose
try blocks contain the break statement and all finally clauses
of those try statements can complete normally.
This definition is based on the logic around "attempts to transfer control" in §14.15.
A continue
statement continues a do statement if, within
the do statement, either there are no try statements whose try
blocks contain the continue statement, or there are try statements
whose try blocks contain the continue statement and all finally
clauses of those try statements can complete normally.
The block that is the body of a constructor, method, instance initializer, or static initializer is reachable.
An empty block that is not a switch block can complete normally iff it is reachable.
A non-empty block that is not a switch block can complete normally iff the last statement in it can complete normally.
The first statement in a non-empty block that is not a switch block is reachable iff the block is reachable.
Every other
statement S in a non-empty block that is not a
switch block is reachable iff the statement
preceding S can complete normally.
A local class declaration statement can complete normally iff it is reachable.
A local variable declaration statement can complete normally iff it is reachable.
An empty statement can complete normally iff it is reachable.
A labeled statement can complete normally if at least one of the following is true:
The contained statement is reachable iff the labeled statement is reachable.
An expression statement can complete normally iff it is reachable.
An if-then
statement can complete normally iff it is reachable.
The then-statement
is reachable iff the if-then statement is
reachable.
An if-then-else
statement can complete normally iff
the then-statement can complete normally or the
else-statement can complete normally.
The then-statement
is reachable iff the if-then-else statement is
reachable.
The else-statement
is reachable iff the if-then-else statement is
reachable.
This handling of an if statement, whether or
not it has an else part, is rather unusual. The rationale is
given at the end of this section.
An assert statement
can complete normally iff it is reachable.
A switch statement
can complete normally iff at least one of the following is
true:
A switch block is
reachable iff its switch statement is reachable.
A statement in a switch
block is reachable iff its switch statement is reachable and at
least one of the following is true:
A while statement can
complete normally iff at least one of the following is
true:
The while
statement is reachable and the condition expression is not a
constant expression (§15.28) with value
true.
There is a
reachable break statement that exits the while
statement.
The contained
statement is reachable iff the while statement is reachable and
the condition expression is not a constant expression whose value
is false.
A do statement can
complete normally iff at least one of the following is
true:
The contained
statement can complete normally and the condition expression
is not a constant expression (§15.28)
with value true.
The
do statement contains a reachable continue statement with
no label, and the do statement is the innermost while,
do, or for statement that contains that continue
statement, and the continue statement continues
that do statement, and the condition expression is
not a constant expression with value true.
The
do statement contains a reachable continue statement with
a label L, and the do statement has
label L, and the continue
statement continues that do statement, and the
condition expression is not a constant expression with value
true.
There is a
reachable break statement that exits the do
statement.
The contained
statement is reachable iff the do statement is
reachable.
A basic for statement
can complete normally iff at least one of the following is
true:
The for
statement is reachable, there is a condition expression, and
the condition expression is not a constant expression
(§15.28) with value true.
There is a
reachable break statement that exits the for
statement.
The contained
statement is reachable iff the for statement is reachable and
the condition expression is not a constant expression whose value
is false.
An enhanced for
statement can complete normally iff it is reachable.
A break, continue,
return, or throw statement cannot complete normally.
A synchronized
statement can complete normally iff the contained statement can
complete normally.
The contained
statement is reachable iff the synchronized statement is
reachable.
A try statement can
complete normally iff both of the following are true:
The try block is
reachable iff the try statement is reachable.
A catch
block C is reachable iff both of the following
are true:
Either the type
of C's parameter is an unchecked exception
type or Throwable; or some expression or throw
statement in the try block is reachable and can throw
a checked exception whose type is
assignable to the parameter of the catch
clause C.
An expression is reachable iff the innermost statement containing it is reachable.
See §15.6 for normal and abrupt completion of expressions.
There is no
earlier catch block A in the try
statement such that the type of C's
parameter is the same as or a subclass of the type
of A's parameter.
The Block of a catch block is reachable
iff the catch block is reachable.
If a finally block is
present, it is reachable iff the try statement is
reachable.
One might expect the if
statement to be handled in the following manner:
An if-then statement can
complete normally iff at least one of the following is
true:
The if-then statement is
reachable and the condition expression is not a constant
expression whose value is true.
The then-statement can
complete normally.
The then-statement is
reachable iff the if-then statement is
reachable and the condition expression is not a constant
expression whose value is false.
An if-then-else statement can
complete normally iff the then-statement can
complete normally or the else-statement can complete
normally.
The then-statement is
reachable iff the if-then-else statement is
reachable and the condition expression is not a constant
expression whose value is false.
The else-statement is reachable iff
the if-then-else statement is reachable and the
condition expression is not a constant expression whose value is
true.
This approach would be consistent with the treatment of other control structures. However, in order to allow the if statement to be used conveniently for "conditional compilation" purposes, the actual rules differ.
As an example, the following statement results in a compile-time error:
while (false) { x=3; }
because the statement x=3; is not
reachable; but the superficially similar case:
if (false) { x=3; }
does not result in a compile-time error. An
optimizing compiler may realize that the
statement x=3; will never be executed and may
choose to omit the code for that statement from the
generated class file, but the
statement x=3; is not regarded as "unreachable" in
the technical sense specified here.
The rationale for this differing treatment is to allow programmers to define "flag variables" such as:
static final boolean DEBUG = false;
and then write code such as:
if (DEBUG) { x=3; }
The idea is that it should be possible to change the
value of DEBUG from false to true or from
true to false and then compile the code correctly with no other
changes to the program text.
This ability to "conditionally compile" has a
significant impact on, and relationship to, binary compatibility
(§13). If a set of classes that use such a "flag"
variable are compiled and conditional code is omitted, it does not
suffice later to distribute just a new version of the class or
interface that contains the definition of the flag. A change to the
value of a flag is, therefore, not binary compatible with pre-existing
binaries (§13.4.9). (There are other reasons for
such incompatibility as well, such as the use of constants in case
labels in switch statements; see
§13.4.9.)