Source: https://book.huihoo.com/the-java-language-specification/first-edition/15.doc.html
Timestamp: 2019-04-20 20:16:49+00:00

Document:
Much of the work in a Java program is done by evaluating expressions, either for their side effects, such as assignments to variables, or for their values, which can be used as arguments or operands in larger expressions, or to affect the execution sequence in statements, or both.
This chapter specifies the meanings of Java expressions and the rules for their evaluation.
Evaluation of an expression can also produce side effects, because expressions may contain embedded assignments, increment operators, decrement operators, and method invocations.
An expression denotes nothing if and only if it is a method invocation (§15.11) that invokes a method that does not return a value, that is, a method declared void (§8.4). Such an expression can be used only as an expression statement (§14.7), because every other context in which an expression can appear requires the expression to denote something. An expression statement that is a method invocation may also invoke a method that produces a result; in this case the value returned by the method is quietly discarded.
Each expression occurs in the declaration of some (class or interface) type that is being declared: in a field initializer, in a static initializer, in a constructor declaration, or in the code for a method.
If an expression denotes a variable, and a value is required for use in further evaluation, then the value of that variable is used. In this context, if the expression denotes a variable or a value, we may speak simply of the value of the expression.
If an expression denotes a variable or a value, then the expression has a type known at compile time. The rules for determining the type of an expression are explained separately below for each kind of expression.
The value of an expression is always assignment compatible (§5.2) with the type of the expression, just as the value stored in a variable is always compatible with the type of the variable. In other words, the value of an expression whose type is T is always suitable for assignment to a variable of type T.
Note that an expression whose type is a class type F that is declared final is guaranteed to have a value that is either a null reference or an object whose class is F itself, because final types have no subclasses.
The instanceof operator (§15.19.2). An expression whose type is a reference type may be tested using instanceof to find out whether the class of the object referenced by the run-time value of the expression is assignment compatible (§5.2) with some other reference type.
Casting (§5.4, §15.15). The class of the object referenced by the run-time value of the operand expression might not be compatible with the type specified by the cast. For reference types, this may require a run-time check that throws an error if the class of the referenced object, as determined at run time, is not assignment compatible (§5.2) with the target type.
Assignment to an array component of reference type (§10.10, §15.12, §15.25.1). The type-checking rules allow the array type S to be treated as a subtype of T if S is a subtype of T, but this requires a run-time check for assignment to an army component, similar to the check performed for a cast.
Exception handling (§14.18). An exception is caught by a catch clause only if the class of the thrown exception object is an instanceof the type of the formal parameter of the catch clause.
In a cast, when the actual class of the object referenced by the value of the operand expression is not compatible with the target type specified by the cast operator (§5.4, §15.15); in this case a ClassCastException is thrown.
In an assignment to an array component of reference type, when the actual class of the object referenced by the value to be assigned is not compatible with the actual run-time component type of the array (§10.10, §15.12, §15.25.1); in this case an ArrayStoreException is thrown.
When an exception is not caught by any catch handler (§11.3); in this case the thread of control that encountered the exception first invokes the method uncaughtException (§20.21.31) for its thread group and then terminates.
Every expression has a normal mode of evaluation in which certain computational steps are carried out. The following sections describe the normal mode of evaluation for each kind of expression. If all the steps are carried out without an exception being thrown, the expression is said to complete normally.
If, however, evaluation of an expression throws an exception, then the expression is said to complete abruptly. An abrupt completion always has an associated reason, which is always a throw with a given value.
A class instance creation expression (§15.8), array creation expression (§15.9), or string concatenation operatior expression (§15.17.1) throws an OutOfMemoryError if there is insufficient memory available.
An array creation expression throws an ArrayNegativeSizeException if the value of any dimension expression is less than zero (§15.9).
A field access (§15.10) throws a NullPointerException if the value of the object reference expression is null.
A method invocation expression (§15.11) that invokes an instance method throws a NullPointerException if the target reference is null.
An array access (§15.12) throws a NullPointerException if the value of the array reference expression is null.
An array access (§15.12) throws an IndexOutOfBoundsException if the value of the array index expression is negative or greater than or equal to the length of the array.
A cast (§15.15) throws a ClassCastException if a cast is found to be impermissible at run time.
An integer division (§15.16.2) or integer remainder (§15.16.3) operator throws an ArithmeticException if the value of the right-hand operand expression is zero.
An assignment to an array component of reference type (§15.25.1) throws an ArrayStoreException when the value to be assigned is not compatible with the component type of the array.
A method invocation expression can also result in an exception being thrown if an exception occurs that causes execution of the method body to complete abruptly. A class instance creation expression can also result in an exception being thrown if an exception occurs that causes execution of the constructor to complete abruptly. Various linkage and virtual machine errors may also occur during the evaluation of an expression. By their nature, such errors are difficult to predict and difficult to handle.
If an exception occurs, then evaluation of one or more expressions may be terminated before all steps of their normal mode of evaluation are complete; such expressions are said to complete abruptly. The terms "complete normally" and "complete abruptly" are also applied to the execution of statements (§14.1). A statement may complete abruptly for a variety of reasons, not just because an exception is thrown.
If evaluation of an expression requires evaluation of a subexpression, abrupt completion of the subexpression always causes the immediate abrupt completion of the expression itself, with the same reason, and all succeeding steps in the normal mode of evaluation are not performed.
Java guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
It is recommended that Java code not rely crucially on this specification. Code is usually clearer when each expression contains at most one side effect, as its outermost operation, and when code does not depend on exactly which exception arises as a consequence of the left-to-right evaluation of expressions.
The left-hand operand of a binary operator appears to be fully evaluated before any part of the right-hand operand is evaluated. For example, if the left-hand operand contains an assignment to a variable and the right-hand operand contains a reference to that same variable, then the value produced by the reference will reflect the fact that the assignment occurred first.
It is not permitted for it to print 6 instead of 9.
because the two assignment statements both fetch and remember the value of the left-hand operand, which is 9, before the right-hand operand of the addition is evaluated, thereby setting the variable to 3. It is not permitted for either example to produce the result 6. Note that both of these examples have unspecified behavior in C, according to the ANSI/ISO standard.
If evaluation of the left-hand operand of a binary operator completes abruptly, no part of the right-hand operand appears to have been evaluated.
because the left-hand operand forgetIt() of the operator / throws an exception before the right-hand operand and its embedded assignment of 2 to j occurs.
Java also guarantees that every operand of an operator (except the conditional operators &&, ||, and ? :) appears to be fully evaluated before any part of the operation itself is performed.
If the binary operator is an integer division / (§15.16.2) or integer remainder % (§15.16.3), then its execution may raise an ArithmeticException, but this exception is thrown only after both operands of the binary operator have been evaluated and only if these evaluations completed normally.
java.lang.Exception: Shuffle off to Buffalo!
since no part of the division operation, including signaling of a divide-by-zero exception, may appear to occur before the invocation of loseBig completes, even though the implementation may be able to detect or infer that the division operation would certainly result in a divide-by-zero exception.
Java implementations must respect the order of evaluation as indicated explicitly by parentheses and implicitly by operator precedence. An implementation may not take advantage of algebraic identities such as the associative law to rewrite expressions into a more convenient computational order unless it can be proven that the replacement expression is equivalent in value and in its observable side effects, even in the presence of multiple threads of execution (using the thread execution model in §17), for all possible computational values that might be involved.
In the case of floating-point calculations, this rule applies also for infinity and not-a-number (NaN) values. For example, !(x<y) may not be rewritten as x>=y, because these expressions have different values if either x or y is NaN.
Specifically, floating-point calculations that appear to be mathematically associative are unlikely to be computationally associative. Such computations must not be naively reordered. For example, it is not correct for a Java compiler to rewrite 4.0*x*0.5 as 2.0*x; while roundoff happens not to be an issue here, there are large values of x for which the first expression produces infinity (because of overflow) but the second expression produces a finite result.
because the first expression overflows and the second does not.
In contrast, integer addition and multiplication are provably associative in Java; for example a+b+c, where a, b, and c are local variables (this simplifying assumption avoids issues involving multiple threads and volatile variables), will always produce the same answer whether evaluated as (a+b)+c or a+(b+c); if the expression b+c occurs nearby in the code, a smart compiler may be able to use this common subexpression.
In a method or constructor invocation or class instance creation expression, argument expressions may appear within the parentheses, separated by commas. Each argument expression appears to be fully evaluated before any part of any argument expression to its right.
because the assignment of the string "gone" to s occurs after the first two arguments to print3 have been evaluated.
If evaluation of an argument expression completes abruptly, no part of any argument expression to its right appears to have been evaluated.
because the assignment of 3 to id is not executed.
Primary expressions include most of the simplest kinds of expressions, from which all others are constructed: literals, field accesses, method invocations, and array accesses. A parenthesized expression is also treated syntactically as a primary expression.
As programming language grammars go, this part of the Java grammar is unusual, in two ways. First, one might expect simple names, such as names of local variables and method parameters, to be primary expressions. For technical reasons, names are lumped together with primary expressions a little later when postfix expressions are introduced (§15.13).
A literal (§3.10) denotes a fixed, unchanging value.
The type of an integer literal that ends with L or l is long; the type of any other integer literal is int.
The type of a floating-point literal that ends with F or f is float; the type of any other floating-point literal is double.
The type of a boolean literal is boolean.
The type of a character literal is char.
The type of a string literal is String.
The type of the null literal null is the null type; its value is the null reference.
Evaluation of a literal always completes normally.
The keyword this may be used only in the body of an instance method or constructor, or in the initializer of an instance variable of a class. If it appears anywhere else, a compile-time error occurs.
When used as a primary expression, the keyword this denotes a value, that is a reference to the object for which the instance method was invoked (§15.11), or to the object being constructed. The type of this is the class C within which the keyword this occurs. At run time, the class of the actual object referred to may be the class C or any subclass of C.
the class IntVector implements a method equals, which compares two vectors. If the other vector is the same vector object as the one for which the equals method was invoked, then the check can skip the length and value comparisons. The equals method implements this check by comparing the reference to the other object to this.
The keyword this is also used in a special explicit constructor invocation statement, which can appear at the beginning of a constructor body (§8.6.5).
A parenthesized expression is a primary expression whose type is the type of the contained expression and whose value at run time is the value of the contained expression.
A class instance creation expression is used to create new objects that are instances of classes.
In a class instance creation expression, the ClassType must name a class that is not abstract. This class type is the type of the creation expression.
The arguments in the argument list, if any, are used to select a constructor declared in the body of the named class type, using the same matching rules as for method invocations (§15.11). As in method invocations, a compile-time method matching error results if there is no unique constructor that is both applicable to the provided arguments and the most specific of all the applicable constructors.
At run time, evaluation of a class instance creation expression is as follows.
First, space is allocated for the new class instance. If there is insufficient space to allocate the object, evaluation of the class instance creation expression completes abruptly by throwing an OutOfMemoryError (§15.8.2).
The new object contains new instances of all the fields declared in the specified class type and all its superclasses. As each new field instance is created, it is initialized to its standard default value (§4.5.4).
Next, the argument list is evaluated, left-to-right. If any of the argument evaluations completes abruptly, any argument expressions to its right are not evaluated, and the class instance creation expression completes abruptly for the same reason.
Next, the selected constructor of the specified class type is invoked. This results in invoking at least one constructor for each superclass of the class type. This process can be directed by explicit constructor invocation statements (§8.6) and is described in detail in §12.5.
The value of a class instance creation expression is a reference to the newly created object of the specified class. Every time the expression is evaluated, a fresh object is created.
If evaluation of a class instance creation expression finds there is insufficient memory to perform the creation operation, then an OutOfMemoryError is thrown. This check occurs before any argument expressions are evaluated.
because the out-or-memory condition is detected before the argument expression oldid = id is evaluated.
Compare this to the treatment of array creation expressions (§15.9), for which the out-of-memory condition is detected after evaluation of the dimension expressions (§15.9.3).
An array instance creation expression is used to create new arrays (§10).
An array creation expression creates an object that is a new array whose elements are of the type specified by the PrimitiveType or TypeName. The TypeName may name any reference type, even an abstract class type (§8.1.2.1) or an interface type (§9).
The type of each dimension expression DimExpr must be an integral type, or a compile-time error occurs. Each expression undergoes unary numeric promotion (§5.6.1). The promoted type must be int, or a compile-time error occurs; this means, specifically, that the type of a dimension expression must not be long.
At run time, evaluation of an array creation expression behaves as follows.
First, the dimension expressions are evaluated, left-to-right. If any of the expression evaluations completes abruptly, the expressions to the right of it are not evaluated.
Next, the values of the dimension expressions are checked. If the value of any DimExpr expression is less than zero, then an NegativeArraySizeException is thrown.
Next, space is allocated for the new array. If there is insufficient space to allocate the array, evaluation of the array creation expression completes abruptly by throwing an OutOfMemoryError.
Then, if a single DimExpr appears, a single-dimensional array is created of the specified length, and each component of the array is initialized to its standard default value (§4.5.4).
There is, however, no way to get this effect with a single creation expression.
In an array creation expression (§15.9), there may be one or more dimension expressions, each within brackets. Each dimension expression is fully evaluated before any part of any dimension expression to its right.
because the first dimension is calculated as 4 before the second dimension expression sets i to 3.
because the embedded assignment that sets i to 1 is never executed.
If evaluation of an array creation expression finds there is insufficient memory to perform the creation operation, then an OutOfMemoryError is thrown. This check occurs only after evaluation of all dimension expressions has completed normally.
because the out-of-memory condition is detected after the argument expression oldlen = len is evaluated.
Compare this to class instance creation expressions (§15.8), which detect the out-of-memory condition before evaluating argument expressions (§15.8.2).
The meaning of a field access expression is determined using the same rules as for qualified names (§6.6), but limited by the fact that an expression cannot denote a package, class type, or interface type.
If the identifier names several accessible member fields of type T, then the field access is ambiguous and a compile-time error occurs.
If the identifier does not name an accessible member field of type T, then the field access is undefined and a compile-time error occurs.
If the field is final, then the result is the value of the specified class variable in the class or interface that is the type of the Primary expression.
If the field is not final, then the result is a variable, namely, the specified class variable in the class that is the type of the Primary expression.
If the value of the Primary is null, then a NullPointerException is thrown.
If the field is final, then the result is the value of the specified instance variable in the object referenced by the value of the Primary.
If the field is not final, then the result is a variable, namely, the specified instance variable in the object referenced by the value of the Primary.
Note, specifically, that only the type of the Primary expression, not the class of the actual object referred to at run time, is used in determining which field to use.
return " when " + name + " holds a "
t.x=1 when t holds a class T at run time.
s.x=0 when s holds a class S at run time.
s.x=0 when s holds a class T at run time.
The last line shows that, indeed, the field that is accessed does not depend on the run-time class of the referenced object; even if s holds a reference to an object of class T, the expression s.x refers to the x field of class S, because the type of the expression s is S. Objects of class T contain two fields named x, one for class T and one for its superclass S.
t.z()=1 when t holds a class T at run time.
s.z()=0 when s holds a class S at run time.
s.z()=1 when s holds a class T at run time.
The last line shows that, indeed, the method that is accessed does depend on the run-time class of referenced object; when s holds a reference to an object of class T, the expression s.z() refers to the z method of class T, despite the fact that the type of the expression s is S. Method z of class T overrides method z of class S.
Even though the result of favorite() is null, a NullPointerException is not thrown. That "Mount " is printed demonstrates that the Primary expression is indeed fully evaluated at run time, despite the fact that only its type, not its value, is used to determine which field to access (because the field mountain is static).
The special form using the keyword super is valid only in an instance method or constructor, or in the initializer of an instance variable of a class; these are exactly the same situations in which the keyword this may be used (§15.7.2). The form involving super may not be used anywhere in the class Object, since Object has no superclass; if super appears in class Object, then a compile-time error results.
Suppose that a field access expression super.name appears within class C, and the immediate superclass of C is class S. Then super.name is treated exactly as if it had been the expression ((S)this).name; thus, it refers to the field named name of the current object, but with the current object viewed as an instance of the superclass. Thus it can access the field named name that is visible in class S, even if that field is hidden by a declaration of a field named name in class C.
A method invocation expression is used to invoke a class or instance method.
Resolving a method name at compile time is more complicated than resolving a field name because of the possibility of method overloading. Invoking a method at run time is also more complicated than accessing a field because of the possibility of instance method overriding.
Determining the method that will be invoked by a method invocation expression involves several steps. The following three sections describe the compile-time processing of a method invocation; the determination of the type of the method invocation expression is described in §15.11.3.
If it is a simple name, that is, just an Identifier, then the name of the method is the Identifier and the class or interface to search is the one whose declaration contains the method invocation.
If it is a qualified name of the form TypeName . Identifier, then the name of the method is the Identifier and the class to search is the one named by the TypeName. If TypeName is the name of an interface rather than a class, then a compile-time error occurs, because this form can invoke only static methods and interfaces have no static methods.
In all other cases, the qualified name has the form FieldName . Identifier; then the name of the method is the Identifier and the class or interface to search is the declared type of the field named by the FieldName.
If the form is Primary . Identifier, then the name of the method is the Identifier and the class or interface to be searched is the type of the Primary expression.
If the form is super . Identifier, then the name of the method is the Identifier and the class to be searched is the superclass of the class whose declaration contains the method invocation. A compile-time error occurs if such a method invocation occurs in an interface, or in the class Object, or in a static method, a static initializer, or the initializer for a static variable. It follows that a method invocation of this form may appear only in a class other than Object, and only in the body of an instance method, the body of a constructor, or an initializer for an instance variable.
The second step searches the class or interface determined in the previous step for method declarations. This step uses the name of the method and the types of the argument expressions to locate method declarations that are both applicable and accessible, that is, declarations that can be correctly invoked on the given arguments. There may be more than one such method declaration, in which case the most specific one is chosen. The descriptor (signature plus return type) of the most specific method declaration is one used at run time to do the method dispatch.
The number of parameters in the method declaration equals the number of argument expressions in the method invocation.
The type of each actual argument can be converted by method invocation conversion (§5.3) to the type of the corresponding parameter. Method invocation conversion is the same as assignment conversion (§5.2), except that constants of type int are never implicitly narrowed to byte, short, or char.
The class or interface determined by the process described in §15.11.1 is searched for all method declarations applicable to this method invocation; method definitions inherited from superclasses and superinterfaces are included in this search.
Whether a method declaration is accessible to a method invocation depends on the access modifier (public, none, protected, or private) in the method declaration and on where the method invocation appears.
If the class or interface has no method declaration that is both applicable and accessible, then a compile-time error occurs.
for the method invocation two(1) within class Doubler, there are two accessible methods named two, but only the second one is applicable, and so that is the one invoked at run time. For the method invocation two(3) within class Test, there are two applicable methods, but only the one in class Test is accessible, and so that is the one to be invoked at run time (the argument 3 is converted to type long). For the method invocation Doubler.two(3), the class Doubler, not class Test, is searched for methods named two; the only applicable method is not accessible, and so this method invocation causes a compile-time error.
Here, a compile-time error occurs for the second invocation of setColor, because no applicable method can be found at compile time. The type of the literal 37 is int, and int cannot be converted to byte by method invocation conversion. Assignment conversion, which is used in the initialization of the variable color, performs an implicit conversion of the constant from type int to byte, which is permitted because the value 37 is small enough to be represented in type byte; but such a conversion is not allowed for method invocation conversion.
If more than one method is both accessible and applicable to a method invocation, it is necessary to choose one to provide the descriptor for the run-time method dispatch. Java uses the rule that the most specific method is chosen.
The informal intuition is that one method declaration is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
T can be converted to U by method invocation conversion.
Tj can be converted to Uj by method invocation conversion, for all j from 1 to n.
A method is said to be maximally specific for a method invocation if it is applicable and accessible and there is no other applicable and accessible method that is more specific.
If there is exactly one maximally specific method, then it is in fact the most specific method; it is necessarily more specific than any other method that is applicable and accessible. It is then subjected to some further compile-time checks as described in §15.11.3.
It is possible that no method is the most specific, because there are two or more maximally specific method declarations. In this case, we say that the method invocation is ambiguous, and a compile-time error occurs.
This example produces an error at compile time. The problem is that there are two declarations of test that are applicable and accessible, and neither is more specific than the other. Therefore, the method invocation is ambiguous.
then it would be more specific than the other two, and the method invocation would no longer be ambiguous.
Here the most specific declaration of method test is the one taking a parameter of type ColoredPoint. Because the result type of the method is int, a compile- time error occurs because an int cannot be converted to a String by assignment conversion. This example shows that, in Java, the result types of methods do not participate in resolving overloaded methods, so that the second test method, which returns a String, is not chosen, even though it has a result type that would allow the example program to compile without error.
The most applicable method is chosen at compile time; its descriptor determines what method is actually executed at run time. If a new method is added to a class, then Java code that was compiled with the old definition of the class might not use the new method, even if a recompilation would cause this method to be chosen.
The application programmer who coded class Test has expected to see the word green, because the actual argument, a ColoredPoint, has a color field, and color would seem to be a "relevant field" (of course, the documentation for the package Points ought to have been much more precise!).
Notice, by the way, that the most specific method (indeed, the only applicable method) for the method invocation of adopt has a signature that indicates a method of one parameter, and the parameter is of type Point. This signature becomes part of the binary representation of class Test produced by the compiler and is used by the method invocation at run time.
Ideally, Java code should be recompiled whenever code that it depends on is changed. However, in an environment where different Java classes are maintained by different organizations, this is not always feasible. Defensive programming with careful attention to the problems of class evolution can make upgraded code much more robust. See §13 for a detailed discussion of binary compatibility and type evolution.
15.11.3 Compile-Time Step 3: Is the Chosen Method Appropriate?
The name of the method.
The class or interface that contains the compile-time declaration.
The number of parameters and the types of the parameters, in order.
The result type, or void, as declared in the compile-time declaration.
If the compile-time declaration has the static modifier, then the invocation mode is static.
Otherwise, if the compile-time declaration has the private modifier, then the invocation mode is nonvirtual.
Otherwise, if the part of the method invocation before the left parenthesis is of the form super . Identifier, then the invocation mode is super.
Otherwise, if the compile-time declaration is in an interface, then the invocation mode is interface.
Otherwise, the invocation mode is virtual.
If the compile-time declaration for the method invocation is not void, then the type of the method invocation expression is the result type specified in the compile-time declaration.
At run time, method invocation requires five steps. First, a target reference may be computed. Second, the argument expressions are evaluated. Third, the accessibility of the method to be invoked is checked. Fourth, the actual code for the method to be executed is located. Fifth, a new activation frame is created, synchronization is performed if necessary, and control is transferred to the method code.
If the invocation mode is static, then there is no target reference.
Otherwise, the target reference is the value of this.
If the MethodName is a qualified name of the form TypeName . Identifier, then there is no target reference.
Otherwise, the target reference is the value of the expression FieldName.
If the invocation mode is static, then there is no target reference. The expression Primary is evaluated, but the result is then discarded.
Otherwise, the expression Primary is evaluated and the result is used as the target reference.
In either case, if the evaluation of the Primary expression completes abruptly, then no part of any argument expression appears to have been evaluated, and the method invocation completes abruptly for the same reason.
If the third production for MethodInvocation, which includes the keyword super, is involved, then the target reference is the value of this.
The argument expressions are evaluated in order, from left to right. If the evaluation of any argument expression completes abruptly, then no part of any argument expression to its right appears to have been evaluated, and the method invocation completes abruptly for the same reason.
Let C be the class containing the method invocation, and let T be the class or interface that contained the method being invoked, and m be the name of the method, as determined at compile time (§15.11.3).
A Java Virtual Machine must insure, as part of linkage, that the method m still exists in the type T. If this is not true, then a NoSuchMethodError (which is a subclass of IncompatibleClassChangeError) occurs. If the invocation mode is interface, then the virtual machine must also check that the target reference type still implements the specified interface. If the target reference type does not still implement the interface, then an IncompatibleClassChangeError occurs.
If T is in the same package as C, then T is accessible.
If T is in a different package than C, and T is public, then T is accessible.
If m is public, then m is accessible. (All members of interfaces are public (§9.2)).
If m is protected, then m is accessible if and only if either T is in the same package as C, or C is T or a subclass of T.
If m has default (package) access, then m is accessible if and only if T is in the same package as C.
If m is private, then m is accessible if and only if and C is T.
If either T or m is not accessible, then an IllegalAccessError occurs (§12.3).
The strategy for method lookup depends on the invocation mode.
If the invocation mode is static, no target reference is needed and overriding is not allowed. Method m of class T is the one to be invoked.
Otherwise, an instance method is to be invoked and there is a target reference. If the target reference is null, a NullPointerException is thrown at this point. Otherwise, the target reference is said to refer to a target object and will be used as the value of the keyword this in the invoked method. The other four possibilities for the invocation mode are then considered.
If the invocation mode is nonvirtual, overriding is not allowed. Method m of class T is the one to be invoked.
If the invocation mode is super, then S is initially the superclass of the class C that contains the method invocation.
The dynamic method lookup uses the following procedure to search class S, and then the superclasses of class S, as necessary, for method m.
Otherwise, if S is not T, this same lookup procedure is performed using the superclass of S; whatever it comes up with is the result of this lookup.
This procedure will find a suitable method when it reaches class T, because otherwise an IllegalAccessError would have been thrown by the checks of the previous section §15.11.4.3.
We note that the dynamic lookup process, while described here explicitly, will often be implemented implicitly, for example as a side-effect of the construction and use of per-class method dispatch tables, or the construction of other per-class structures used for efficient dispatch.
A method m in some class S has been identified as the one to be invoked.
Now a new activation frame is created, containing the target reference (if any) and the argument values (if any), as well as enough space for the local variables and stack for the method to be invoked and any other bookkeeping information that may be required by the implementation (stack pointer, program counter, reference to previous activation frame, and the like). If there is not sufficient memory available to create such an activation frame, an OutOfMemoryError is thrown.
The newly created activation frame becomes the current activation frame. The effect of this is to assign the argument values to corresponding freshly created parameter variables of the method, and to make the target reference available as this, if there is a target reference.
If the method m is a native method but the necessary native, implementation-dependent binary code has not been loaded (§20.16.14, §20.16.13) or otherwise cannot be dynamically linked, then an UnsatisfiedLinkError is thrown.
If the method m is not synchronized, control is transferred to the body of the method m to be invoked.
If the method m is synchronized, then an object must be locked before the transfer of control. No further progress can be made until the current thread can obtain the lock. If there is a target reference, then the target must be locked; otherwise the Class object for class S, the class of the method m, must be locked. Control is then transferred to the body of the method m to be invoked. The object is automatically unlocked when execution of the body of the method has completed, whether normally or abruptly. The locking and unlocking behavior is exactly as if the body of the method were embedded in a synchronized statement (§14.17).
Class C and class S have the same class loader (§20.14) and class S is not SecurityManager or a subclass of SecurityManager.
Class S has no class loader (this fact indicates that it is a system class); class S is not SecurityManager or a subclass of SecurityManager; and method m is known not to call, directly or indirectly, any method of SecurityManager (§20.17) or any of its subclasses.
Here favorite returns null, yet no NullPointerException is thrown.
As part of an instance method invocation (§15.11), there is an expression that denotes the object to be invoked. This expression appears to be fully evaluated before any part of any argument expression to the method invocation is evaluated.
the occurrence of s before ".startsWith" is evaluated first, before the argument expression s="two". Therefore, a reference to the string "one" is remembered as the target reference before the local variable s is changed to refer to the string "two". As a result, the startsWith method (§20.12.20) is invoked for target object "one" with argument "two", so the result of the invocation is false, as the string "one" does not start with "two". It follows that the test program does not print "oops".
the subclass ColoredPoint extends the clear abstraction defined by its superclass Point. It does so by overriding the clear method with its own method, which invokes the clear method of its superclass, using the form super.clear.
Overriding is sometimes called "late-bound self-reference"; in this example it means that the reference to clear in the body of Point.move (which is really syntactic shorthand for this.clear) invokes a method chosen "late" (at run time, based on the run-time class of the object referenced by this) rather than a method chosen "early" (at compile time, based only on the type of this). This provides the Java programmer a powerful way of extending abstractions and is a key idea in object-oriented programming.
An overridden instance method of a superclass may be accessed by using the keyword super to access the members of the immediate superclass, bypassing any overriding declaration in the class that contains the method invocation.
The casts to types T1 and T2 do not change the method that is invoked, because the instance method to be invoked is chosen according to the run-time class of the object referred to be this. A cast does not change the class of an object; it only checks that the class is compatible with the specified type.
An array access expression refers to a variable that is a component of an array.
An array access expression contains two subexpressions, the array reference expression (before the left bracket) and the index expression (within the brackets). Note that the array reference expression may be a name or any primary expression that is not an array creation expression (§15.9).
The type of the array reference expression must be an array type (call it T, an array whose components are of type T) or a compile-time error results. Then the type of the array access expression is T.
The index expression undergoes unary numeric promotion (§5.6.1); the promoted type must be int.
The result of an array reference is a variable of type T, namely the variable within the array selected by the value of the index expression. This resulting variable, which is a component of the array, is never considered final, even if the array reference was obtained from a final variable.
First, the array reference expression is evaluated. If this evaluation completes abruptly, then the array access completes abruptly for the same reason and the index expression is not evaluated.
Otherwise, the index expression is evaluated. If this evaluation completes abruptly, then the array access completes abruptly for the same reason.
Otherwise, if the value of the array reference expression is null, then a NullPointerException is thrown.
Otherwise, the value of the array reference expression indeed refers to an array. If the value of the index expression is less than zero, or greater than or equal to the array's length, then an IndexOutOfBoundsException is thrown.
In an array access, the expression to the left of the brackets appears to be fully evaluated before any part of the expression within the brackets is evaluated. For example, in the (admittedly monstrous) expression a[(a=b)], the expression a is fully evaluated before the expression (a=b); this means that the original value of a is fetched and remembered while the expression (a=b) is evaluated. This array referenced by the original value of a is then subscripted by a value that is element 3 of another array (possibly the same array) that was referenced by b and is now also referenced by a.
because the monstrous expression's value is equivalent to a[b] or a or 14.
because the embedded assignment of 2 to index never occurs.
A NullPointerException never occurs, because the index expression must be completely evaluated before any part of the indexing operation occurs, and that includes the check as to whether the value of the left-hand operand is null.
Postfix expressions include uses of the postfix ++ and -- operators. Also, as discussed in §15.7, names are not considered to be primary expressions, but are handled separately in the grammar to avoid certain ambiguities. They become interchangeable only here, at the level of precedence of postfix expressions.
If the Identifier occurs within the scope of a parameter or local variable named by that same Identifier, then the type of the ExpressionName is the declared type of the parameter or local variable; moreover, the value of the ExpressionName is a variable, namely, the parameter or local variable itself.
containing the keyword this (§15.7.2).
Otherwise, if it is a qualified name of the form PackageName . Identifier, then a compile-time error occurs.
Otherwise, if it is a qualified name of the form TypeName . Identifier, then it is refers to a static field of the class or interface named by the TypeName. A compile-time error occurs if TypeName does not name a class or interface. A compile-time error occurs if the class or interface named by TypeName does not contain an accessible static field named by the Identifier. The type of the ExpressionName is the declared type of the static field. The value of the ExpressionName is a variable, namely, the static field itself.
containing a parenthesized expression (§15.7.3).
A postfix expression followed by a ++ operator is a postfix increment expression. The result of the postfix expression must be a variable of a numeric type, or a compile-time error occurs. The type of the postfix increment expression is the type of the variable. The result of the postfix increment expression is not a variable, but a value.
At run time, if evaluation of the operand expression completes abruptly, then the postfix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. Before the addition, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion (§5.1.3) to the type of the variable before it is stored. The value of the postfix increment expression is the value of the variable before the new value is stored.
A variable that is declared final cannot be incremented, because when an access of a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a postfix increment operator.
A postfix expression followed by a -- operator is a postfix decrement expression. The result of the postfix expression must be a variable of a numeric type, or a compile-time error occurs. The type of the postfix decrement expression is the type of the variable. The result of the postfix decrement expression is not a variable, but a value.
At run time, if evaluation of the operand expression completes abruptly, then the postfix decrement expression completes abruptly for the same reason and no decrementation occurs. Otherwise, the value 1 is subtracted from the value of the variable and the difference is stored back into the variable. Before the subtraction, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the difference is narrowed by a narrowing primitive conversion (§5.1.3) to the type of the variable before it is stored. The value of the postfix decrement expression is the value of the variable before the new value is stored.
A variable that is declared final cannot be decremented, because when an access of a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a postfix decrement operator.
The unary operators include +, -, ++, --, ~, !, and cast operators. Expressions with unary operators group right-to-left, so that -~x means the same as -(~x).
This portion of the Java grammar contains some tricks to avoid two potential syntactic ambiguities.
The first potential ambiguity would arise in expressions such as (p)+q, which looks, to a C or C++ programmer, as though it could be either be a cast to type p of a unary + operating on q, or a binary addition of two quantities p and q. In C and C++, the parser handles this problem by performing a limited amount of semantic analysis as it parses, so that it knows whether p is the name of a type or the name of a variable.
Java takes a different approach. The result of the + operator must be numeric, and all type names involved in casts on numeric values are known keywords. Thus, if p is a keyword naming a primitive type, then (p)+q can make sense only as a cast of a unary expression. However, if p is not a keyword naming a primitive type, then (p)+q can make sense only as a binary arithmetic operation. Similar remarks apply to the - operator. The grammar shown above splits CastExpression into two cases to make this distinction. The nonterminal UnaryExpression includes all unary operator, but the nonterminal UnaryExpressionNotPlusMinus excludes uses of all unary operators that could also be binary operators, which in Java are + and -.
The second potential ambiguity is that the expression (p)++ could, to a C or C++ programmer, appear to be either a postfix increment of a parenthesized expression or the beginning of a cast, for example, in (p)++q. As before, parsers for C and C++ know whether p is the name of a type or the name of a variable. But a parser using only one-token lookahead and no semantic analysis during the parse would not be able to tell, when ++ is the lookahead token, whether (p) should be considered a Primary expression or left alone for later consideration as part of a CastExpression.
In Java, the result of the ++ operator must be numeric, and all type names involved in casts on numeric values are known keywords. Thus, if p is a keyword naming a primitive type, then (p)++ can make sense only as a cast of a prefix increment expression, and there had better be an operand such as q following the ++. However, if p is not a keyword naming a primitive type, then (p)++ can make sense only as a postfix increment of p. Similar remarks apply to the -- operator. The nonterminal UnaryExpressionNotPlusMinus therefore also excludes uses of the prefix operators ++ and --.
A unary expression preceded by a ++ operator is a prefix increment expression. The result of the unary expression must be a variable of a numeric type, or a compile-time error occurs. The type of the prefix increment expression is the type of the variable. The result of the prefix increment expression is not a variable, but a value.
At run time, if evaluation of the operand expression completes abruptly, then the prefix increment expression completes abruptly for the same reason and no incrementation occurs. Otherwise, the value 1 is added to the value of the variable and the sum is stored back into the variable. Before the addition, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the sum is narrowed by a narrowing primitive conversion (§5.1.3) to the type of the variable before it is stored. The value of the prefix increment expression is the value of the variable after the new value is stored.
A variable that is declared final cannot be incremented, because when an access of a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a prefix increment operator.
A unary expression preceded by a -- operator is a prefix decrement expression. The result of the unary expression must be a variable of a numeric type, or a compile-time error occurs. The type of the prefix decrement expression is the type of the variable. The result of the prefix decrement expression is not a variable, but a value.
At run time, if evaluation of the operand expression completes abruptly, then the prefix decrement expression completes abruptly for the same reason and no decrementation occurs. Otherwise, the value 1 is subtracted from the value of the variable and the difference is stored back into the variable. Before the subtraction, binary numeric promotion (§5.6.2) is performed on the value 1 and the value of the variable. If necessary, the difference is narrowed by a narrowing primitive conversion (§5.1.3) to the type of the variable before it is stored. The value of the prefix decrement expression is the value of the variable after the new value is stored.
A variable that is declared final cannot be decremented, because when an access of a final variable is used as an expression, the result is a value, not a variable. Thus, it cannot be used as the operand of a prefix decrement operator.
The type of the operand expression of the unary + operator must be a primitive numeric type, or a compile-time error occurs. Unary numeric promotion (§5.6.1) is performed on the operand. The type of the unary plus expression is the promoted type of the operand. The result of the unary plus expression is not a variable, but a value, even if the result of the operand expression is a variable.
At run time, the value of the unary plus expression is the promoted value of the operand.
The type of the operand expression of the unary - operator must be a primitive numeric type, or a compile-time error occurs. Unary numeric promotion (§5.6.1) is performed on the operand. The type of the unary minus expression is the promoted type of the operand.
At run time, the value of the unary plus expression is the arithmetic negation of the promoted value of the operand.
For integer values, negation is the same as subtraction from zero. Java uses two's-complement representation for integers, and the range of two's-complement values is not symmetric, so negation of the maximum negative int or long results in that same maximum negative number. Overflow occurs in this case, but no exception is thrown. For all integer values x, -x equals (~x)+1.
If the operand is NaN, the result is NaN (recall that NaN has no sign).
If the operand is an infinity, the result is the infinity of opposite sign.
If the operand is a zero, the result is the zero of opposite sign.
The type of the operand expression of the unary ~ operator must be a primitive integral type, or a compile-time error occurs. Unary numeric promotion (§5.6.1) is performed on the operand. The type of the unary bitwise complement expression is the promoted type of the operand.
At run time, the value of the unary bitwise complement expression is the bitwise complement of the promoted value of the operand; note that, in all cases, ~x equals (-x)-1.
15.14.6 Logical Complement Operator !
The type of the operand expression of the unary ! operator must be boolean, or a compile-time error occurs. The type of the unary logical complement expression is boolean.
At run time, the value of the unary logical complement expression is true if the operand value is false and false if the operand value is true.
A cast expression converts, at run time, a value of one numeric type to a similar value of another numeric type; or confirms, at compile time, that the type of an expression is boolean; or checks, at run time, that a reference value refers to an object whose class is compatible with a specified reference type.
See §15.14 for a discussion of the distinction between UnaryExpression and UnaryExpressionNotPlusMinus.
The type of a cast expression is the type whose name appears within the parentheses. (The parentheses and the type they contain are sometimes called the cast operator.) The result of a cast expression is not a variable, but a value, even if the result of the operand expression is a variable.
At run time, the operand value is converted by casting conversion (§5.4) to the type specified by the cast operator.
Not all casts are permitted by the Java language. Some casts result in an error at compile time. For example, a primitive value may not be cast to a reference type. Some casts can be proven, at compile time, always to be correct at run time. For example, it is always correct to convert a value of a class type to the type of its superclass; such a cast should require no special action at run time. Finally, some casts cannot be proven to be either always correct or always incorrect at compile time. Such casts require a test at run time. A ClassCastException is thrown if a cast is found at run time to be impermissible.
The operators *, /, and % are called the multiplicative operators. They have the same precedence and are syntactically left-associative (they group left-to-right).
The type of each of the operands of a multiplicative operator must be a primitive numeric type, or a compile-time error occurs. Binary numeric promotion is performed on the operands (§5.6.2). The type of a multiplicative expression is the promoted type of its operands. If this promoted type is int or long, then integer arithmetic is performed; if this promoted type is float or double, then floating-point arithmetic is performed.
The binary * operator performs multiplication, producing the product of its operands. Multiplication is a commutative operation if the operand expressions have no side effects. While integer multiplication is associative when the operands are all of the same type, floating-point multiplication is not associative.
If an integer multiplication overflows, then the result is the low-order bits of the mathematical product as represented in some sufficiently large two's-complement format. As a result, if overflow occurs, then the sign of the result may not be the same as the sign of the mathematical product of the two operand values.
If either operand is NaN, the result is NaN.
If the result is not NaN, the sign of the result is positive if both operands have the same sign, and negative if the operands have different signs.
Multiplication of an infinity by a zero results in NaN.
Multiplication of an infinity by a finite value results in a signed infinity. The sign is determined by the rule stated above.
In the remaining cases, where neither an infinity or NaN is involved, the product is computed. If the magnitude of the product is too large to represent, we say the operation overflows. The result is then an infinity of appropriate sign. If the magnitude is too small to represent, we say the operation underflows; the result is then a zero of appropriate sign. Otherwise, the product is rounded to the nearest representable value using IEEE 754 round-to-nearest mode. The Java language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
Despite the fact that overflow, underflow, or loss of information may occur, evaluation of a multiplication operator * never throws a run-time exception.
The binary / operator performs division, producing the quotient of its operands. The left-hand operand is the dividend and the right-hand operand is the divisor.
Integer division rounds toward 0. That is, the quotient produced for operands n and d that are integers after binary numeric promotion (§5.6.2) is an integer value q whose magnitude is as large as possible while satisfying ; moreover, q is positive when and n and d have the same sign, but q is negative when and n and d have opposite signs. There is one special case that does not satisfy this rule: if the dividend is the negative integer of largest possible magnitude for its type, and the divisor is -1, then integer overflow occurs and the result is equal to the dividend. Despite the overflow, no exception is thrown in this case. On the other hand, if the value of the divisor in an integer division is 0, then an ArithmeticException is thrown.
If the result is not NaN, the sign of the result is positive if both operands have the same sign, negative if the operands have different signs.
Division of an infinity by an infinity results in NaN.
Division of an infinity by a finite value results in a signed infinity. The sign is determined by the rule stated above.
Division of a finite value by an infinity results in a signed zero. The sign is determined by the rule stated above.
Division of a zero by a zero results in NaN; division of zero by any other finite value results in a signed zero. The sign is determined by the rule stated above.
Division of a nonzero finite value by a zero results in a signed infinity. The sign is determined by the rule stated above.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, the quotient is computed. If the magnitude of the quotient is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign. If the magnitude is too small to represent, we say the operation underflows and the result is then a zero of appropriate sign. Otherwise, the quotient is rounded to the nearest representable value using IEEE 754 round-to-nearest mode. The Java language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
Despite the fact that overflow, underflow, division by zero, or loss of information may occur, evaluation of a floating-point division operator / never throws a run-time exception.
The binary % operator is said to yield the remainder of its operands from an implied division; the left-hand operand is the dividend and the right-hand operand is the divisor.
In C and C++, the remainder operator accepts only integral operands, but in Java, it also accepts floating-point operands.
The remainder operation for operands that are integers after binary numeric promotion (§5.6.2) produces a result value such that (a/b)*b+(a%b) is equal to a. This identity holds even in the special case that the dividend is the negative integer of largest possible magnitude for its type and the divisor is -1 (the remainder is 0). It follows from this rule that the result of the remainder operation can be negative only if the dividend is negative, and can be positive only if the dividend is positive; moreover, the magnitude of the result is always less than the magnitude of the divisor. If the value of the divisor for an integer remainder operator is 0, then an ArithmeticException is thrown.
The result of a floating-point remainder operation as computed by the % operator is not the same as that produced by the remainder operation defined by IEEE 754. The IEEE 754 remainder operation computes the remainder from a rounding division, not a truncating division, and so its behavior is not analogous to that of the usual integer remainder operator. Instead, the Java language defines % on floating-point operations to behave in a manner analogous to that of the Java integer remainder operator; this may be compared with the C library function fmod. The IEEE 754 remainder operation may be computed by the Java library routine Math.IEEEremainder (§20.11.14).
If the result is not NaN, the sign of the result equals the sign of the dividend.
If the dividend is an infinity, or the divisor is a zero, or both, the result is NaN.
If the dividend is finite and the divisor is an infinity, the result equals the dividend.
If the dividend is a zero and the divisor is finite, the result equals the dividend.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, the floating-point remainder r from the division of a dividend n by a divisor d is defined by the mathematical relation where q is an integer that is negative only if is negative and positive only if is positive, and whose magnitude is as large as possible without exceeding the magnitude of the true mathematical quotient of n and d.
Evaluation of a floating-point remainder operator % never throws a run-time exception, even if the right-hand operand is zero. Overflow, underflow, or loss of precision cannot occur.
The operators + and - are called the additive operators. They have the same precedence and are syntactically left-associative (they group left-to-right).
If the type of either operand of a + operator is String, then the operation is string concatenation.
Otherwise, the type of each of the operands of the + operator must be a primitive numeric type, or a compile-time error occurs.
In every case, the type of each of the operands of the binary - operator must be a primitive numeric type, or a compile-time error occurs.
If only one operand expression is of type String, then string conversion is performed on the other operand to produce a string at run time. The result is a reference to a newly created String object that is the concatenation of the two operand strings. The characters of the left-hand operand precede the characters of the right-hand operand in the newly created string.
Any type may be converted to type String by string conversion.
If T is boolean, then use new Boolean(x) (§20.4).
If T is char, then use new Character(x) (§20.5).
If T is byte, short, or int, then use new Integer(x) (§20.7).
If T is long, then use new Long(x) (§20.8).
If T is float, then use new Float(x) (§20.9).
If T is double, then use new Double(x) (§20.10).
This reference value is then converted to type String by string conversion.
Now only reference values need to be considered. If the reference is null, it is converted to the string "null" (four ASCII characters n, u, l, l). Otherwise, the conversion is performed as if by an invocation of the toString method of the referenced object with no arguments; but if the result of invoking the toString method is null, then the string "null" is used instead. The toString method (§20.1.2) is defined by the primordial class Object (§20.1); many classes override it, notably Boolean, Character, Integer, Long, Float, Double, and String.
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class (§20.13) or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
For primitive objects, an implementation may also optimize away the creation of a wrapper object by converting directly from a primitive type to a string.
"The square root of 2 is 1.4142135623730952"
1 + 2 + " fiddlers"
System.out.println("You take one down "
2 bottles of slime on the wall!
1 bottle of slime on the wall!
No bottles of slime on the wall!
"You take one down and pass it around:"
into two pieces to avoid an inconveniently long line in the source code.
The binary + operator performs addition when applied to two operands of numeric type, producing the sum of the operands. The binary - operator performs subtraction, producing the difference of two numeric operands.
Binary numeric promotion is performed on the operands (§5.6.2). The type of an additive expression on numeric operands is the promoted type of its operands. If this promoted type is int or long, then integer arithmetic is performed; if this promoted type is float or double, then floating-point arithmetic is performed.
Addition is a commutative operation if the operand expressions have no side effects. Integer addition is associative when the operands are all of the same type, but floating-point addition is not associative.
If an integer addition overflows, then the result is the low-order bits of the mathematical sum as represented in some sufficiently large two's-complement format. If overflow occurs, then the sign of the result is not the same as the sign of the mathematical sum of the two operand values.
The sum of two infinities of opposite sign is NaN.
The sum of two infinities of the same sign is the infinity of that sign.
The sum of an infinity and a finite value is equal to the infinite operand.
The sum of two zeros of opposite sign is positive zero.
The sum of two zeros of the same sign is the zero of that sign.
The sum of a zero and a nonzero finite value is equal to the nonzero operand.
The sum of two nonzero finite values of the same magnitude and opposite sign is positive zero.
In the remaining cases, where neither an infinity, nor a zero, nor NaN is involved, and the operands have the same sign or have different magnitudes, the sum is computed. If the magnitude of the sum is too large to represent, we say the operation overflows; the result is then an infinity of appropriate sign. If the magnitude is too small to represent, we say the operation underflows; the result is then a zero of appropriate sign. Otherwise, the sum is rounded to the nearest representable value using IEEE 754 round-to-nearest mode. The Java language requires support of gradual underflow as defined by IEEE 754 (§4.2.4).
The binary - operator performs subtraction when applied to two operands of numeric type producing the difference of its operands; the left-hand operand is the minuend and the right-hand operand is the subtrahend. For both integer and floating-point subtraction, it is always the case that a-b produces the same result as a+(-b). Note that, for integer values, subtraction from zero is the same as negation. However, for floating-point operands, subtraction from zero is not the same as negation, because if x is +0.0, then 0.0-x equals +0.0, but -x equals -0.0.
Despite the fact that overflow, underflow, or loss of information may occur, evaluation of a numeric additive operator never throws a run-time exception.
The shift operators include left shift <<, signed right shift >>, and unsigned right shift >>>; they are syntactically left-associative (they group left-to-right). The left- hand operand of a shift operator is the value to be shifted; the right-hand operand specifies the shift distance.
The type of each of the operands of a shift operator must be a primitive integral type, or a compile-time error occurs. Binary numeric promotion (§5.6.2) is not performed on the operands; rather, unary numeric promotion (§5.6.1) is performed on each operand separately. The type of the shift expression is the promoted type of the left-hand operand.
If the promoted type of the left-hand operand is int, only the five lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.21.1) with the mask value 0x1f. The shift distance actually used is therefore always in the range 0 to 31, inclusive.
If the promoted type of the left-hand operand is long, then only the six lowest-order bits of the right-hand operand are used as the shift distance. It is as if the right-hand operand were subjected to a bitwise logical AND operator & (§15.21.1) with the mask value 0x3f. The shift distance actually used is therefore always in the range 0 to 63, inclusive.
At run time, shift operations are performed on the two's complement integer representation of the value of the left operand.
The value of n<<s is n left-shifted s bit positions; this is equivalent (even if overflow occurs) to multiplication by two to the power s.
The value of n>>s is n right-shifted s bit positions with sign-extension. The resulting value is . For nonnegative values of n, this is equivalent to truncating integer division, as computed by the integer division operator /, by two to the power s.
The relational operators are syntactically left-associative (they group left-to- right), but this fact is not useful; for example, a<b<c parses as (a<b)<c, which is always a compile-time error, because the type of a<b is always boolean and < is not an operator on boolean values.
The type of a relational expression is always boolean.
The type of each of the operands of a numerical comparison operator must be a primitive numeric type, or a compile-time error occurs. Binary numeric promotion is performed on the operands (§5.6.2). If the promoted type of the operands is int or long, then signed integer comparison is performed; if this promoted type is float or double, then floating-point comparison is performed.
If either operand is NaN, then the result is false.
All values other than NaN are ordered, with negative infinity less than all finite values, and positive infinity greater than all finite values.
The value produced by the < operator is true if the value of the left-hand operand is less than the value of the right-hand operand, and otherwise is false.
The value produced by the <= operator is true if the value of the left-hand operand is less than or equal to the value of the right-hand operand, and otherwise is false.
The value produced by the > operator is true if the value of the left-hand operand is greater than the value of the right-hand operand, and otherwise is false.
The value produced by the >= operator is true if the value of the left-hand operand is greater than or equal to the value of the right-hand operand, and otherwise is false.
The type of a RelationalExpression operand of the instanceof operator must be a reference type or the null type; otherwise, a compile-time error occurs. The ReferenceType mentioned after the instanceof operator must denote a reference type; otherwise, a compile-time error occurs.
At run time, the result of the instanceof operator is true if the value of the RelationalExpression is not null and the reference could be cast (§15.15) to the ReferenceType without raising a ClassCastException. Otherwise the result is false.
If a cast of the RelationalExpression to the ReferenceType would be rejected as a compile-time error, then the instanceof relational expression likewise produces a compile-time error. In such a situation, the result of the instanceof expression could never be true.
then the cast would be possible, though it would require a run-time check, and the instanceof expression would then be sensible and valid. The cast (Point)e would never raise an exception because it would not be executed if the value of e could not correctly be cast to type Point.
The equality operators are syntactically left-associative (they group left-to-right), but this fact is essentially never useful; for example, a==b==c parses as (a==b)==c. The result type of a==b is always boolean, and c must therefore be of type boolean or a compile-time error occurs. Thus, a==b==c does not test to see whether a, b, and c are all equal.
The == (equal to) and the != (not equal to) operators are analogous to the relational operators except for their lower precedence. Thus, a<b==c<d is true whenever a<b and c<d have the same truth value.
The equality operators may be used to compare two operands of numeric type, or two operands of type boolean, or two operands that are each of either reference type or the null type. All other cases result in a compile-time error. The type of an equality expression is always boolean.
In all cases, a!=b produces the same result as !(a==b). The equality operators are commutative if the operand expressions have no side effects.
If the operands of an equality operator are both of primitive numeric type, binary numeric promotion is performed on the operands (§5.6.2). If the promoted type of the operands is int or long, then an integer equality test is performed; if the promoted type is float or double, then a floating-point equality test is performed.
Positive zero and negative zero are considered equal. Therefore, -0.0==0.0 is true, for example.
Otherwise, two distinct floating-point values are considered unequal by the equality operators. In particular, there is one value representing positive infinity and one value representing negative infinity; each compares equal only to itself, and each compares unequal to all other values.
The value produced by the == operator is true if the value of the left-hand operand is equal to the value of the right-hand operand; otherwise, the result is false.
The value produced by the != operator is true if the value of the left-hand operand is not equal to the value of the right-hand operand; otherwise, the result is false.
If the operands of an equality operator are both of type boolean, then the operation is boolean equality. The boolean equality operators are associative.
The result of == is true if the operands are both true or both false; otherwise, the result is false.
The result of != is false if the operands are both true or both false; otherwise, the result is true. Thus != behaves the same as ^ (§15.21.2) when applied to boolean operands.
If the operands of an equality operator are both of either reference type or the null type, then the operation is object equality.
A compile-time error occurs if it is impossible to convert the type of either operand to the type of the other by a casting conversion (§5.4). The run-time values of the two operands would necessarily be unequal.
At run time, the result of == is true if the operand values are both null or both refer to the same object or array; otherwise, the result is false.
The result of != is false if the operand values are both null or both refer to the same object or array; otherwise, the result is true.
While == may be used to compare references of type String, such an equality test determines whether or not the two operands refer to the same String object. The result is false if the operands are distinct String objects, even if they contain the same sequence of characters. The contents of two strings s and t can be tested for equality by the method invocation s.equals(t) (§20.12.9). See also §3.10.5 and §20.12.47.
The bitwise operators and logical operators include the AND operator &, exclusive OR operator ^, and inclusive OR operator |. These operators have different precedence, with & having the highest precedence and | the lowest precedence. Each of these operators is syntactically left-associative (each groups left-to-right). Each operator is commutative if the operand expressions have no side effects. Each operator is associative.
The bitwise and logical operators may be used to compare two operands of numeric type or two operands of type boolean. All other cases result in a compile-time error.
When both operands of an operator &, ^, or | are of primitive integral type, binary numeric promotion is first performed on the operands (§5.6.2). The type of the bitwise operator expression is the promoted type of the operands.
For &, the result value is the bitwise AND of the operand values.
For ^, the result value is the bitwise exclusive OR of the operand values.
For |, the result value is the bitwise inclusive OR of the operand values.
For example, the result of the expression 0xff00 & 0xf0f0 is 0xf000. The result of 0xff00 ^ 0xf0f0 is 0x0ff0.The result of 0xff00 | 0xf0f0 is 0xfff0.
When both operands of a &, ^, or | operator are of type boolean, then the type of the bitwise operator expression is boolean.
For &, the result value is true if both operand values are true; otherwise, the result is false.
For ^, the result value is true if the operand values are different; otherwise, the result is false.
For |, the result value is false if both operand values are false; otherwise, the result is true.
The && operator is like & (§15.21.2), but evaluates its right-hand operand only if the value of its left-hand operand is true. It is syntactically left-associative (it groups left-to-right). It is fully associative with respect to both side effects and result value; that is, for any expressions a, b, and c, evaluation of the expression ((a)&&(b))&&(c) produces the same result, with the same side effects occurring in the same order, as evaluation of the expression (a)&&((b)&&(c)).
Each operand of && must be of type boolean, or a compile-time error occurs. The type of a conditional-and expression is always boolean.
At run time, the left-hand operand expression is evaluated first; if its value is false, the value of the conditional-and expression is false and the right-hand operand expression is not evaluated. If the value of the left-hand operand is true, then the right-hand expression is evaluated and its value becomes the value of the conditional-and expression. Thus, && computes the same result as & on boolean operands. It differs only in that the right-hand operand expression is evaluated conditionally rather than always.
The || operator is like | (§15.21.2), but evaluates its right-hand operand only if the value of its left-hand operand is false. It is syntactically left-associative (it groups left-to-right). It is fully associative with respect to both side effects and result value; that is, for any expressions a, b, and c, evaluation of the expression ((a)||(b))||(c) produces the same result, with the same side effects occurring in the same order, as evaluation of the expression (a)||((b)||(c)).
Each operand of || must be of type boolean, or a compile-time error occurs. The type of a conditional-or expression is always boolean.
At run time, the left-hand operand expression is evaluated first; if its value is true, the value of the conditional-or expression is true and the right-hand operand expression is not evaluated. If the value of the left-hand operand is false, then the right-hand expression is evaluated and its value becomes the value of the conditional-or expression. Thus, || computes the same result as | on boolean operands. It differs only in that the right-hand operand expression is evaluated conditionally rather than always.
The conditional operator ? : uses the boolean value of one expression to decide which of two other expressions should be evaluated.
The conditional operator is syntactically right-associative (it groups right-to-left), so that a?b:c?d:e?f:g means the same as a?b:(c?d:(e?f:g)).
The conditional operator has three operand expressions; ? appears between the first and second expressions, and : appears between the second and third expressions.
The first expression must be of type boolean, or a compile-time error occurs.
The conditional operator may be used to choose between second and third operands of numeric type, or second and third operands of type boolean, or second and third operands that are each of either reference type or the null type. All other cases result in a compile-time error.
Note that it is not permitted for either the second or the third operand expression to be an invocation of a void method. In fact, it is not permitted for a conditional expression to appear in any context where an invocation of a void method could appear (§14.7).
If the second and third operands have the same type (which may be the null type), then that is the type of the conditional expression.
If one of the operands is of type byte and the other is of type short, then the type of the conditional expression is short.
If one of the operands is of type T where T is byte, short, or char, and the other operand is a constant expression of type int whose value is representable in type T, then the type of the conditional expression is T.
Otherwise, binary numeric promotion (§5.6.2) is applied to the operand types, and the type of the conditional expression is the promoted type of the second and third operands.
If one of the second and third operands is of the null type and the type of the other is a reference type, then the type of the conditional expression is that reference type.
If the second and third operands are of different reference types, then it must be possible to convert one of the types to the other type (call this latter type T) by assignment conversion (§5.2); the type of the conditional expression is T. It is a compile-time error if neither type is assignment compatible with the other type.
If the value of the first operand is true, then the second operand expression is chosen.
If the value of the first operand is false, then the third operand expression is chosen.
The chosen operand expression is then evaluated and the resulting value is converted to the type of the conditional expression as determined by the rules stated above. The operand expression not chosen is not evaluated for that particular evaluation of the conditional expression.
There are 12 assignment operators; all are syntactically right-associative (they group right-to-left). Thus, a=b=c means a=(b=c), which assigns the value of c to b and then assigns the value of b to a.
The result of the first operand of an assignment operator must be a variable, or a compile-time error occurs. This operand may be a named variable, such as a local variable or a field of the current object or class, or it may be a computed variable, as can result from a field access (§15.10) or an array access (§15.12). The type of the assignment expression is the type of the variable.
At run time, the result of the assignment expression is the value of the variable after the assignment has occurred. The result of an assignment expression is not itself a variable.
A variable that is declared final cannot be assigned to, because when an access of a final variable is used as an expression, the result is a value, not a variable, and so it cannot be used as the operand of an assignment operator.
A compile-time error occurs if the type of the right-hand operand cannot be converted to the type of the variable by assignment conversion (§5.2).
First, the left-hand operand is evaluated to produce a variable. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the right-hand operand is not evaluated and no assignment occurs.
Otherwise, the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the value of the right-hand operand is converted to the type of the left-hand variable and the result of the conversion is stored into the variable.
First, the array reference subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason; the index subexpression (of the left-hand operand array access expression) and the right-hand operand are not evaluated and no assignment occurs.
Otherwise, the index subexpression of the left-hand operand array access expression is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and the right-hand operand is not evaluated and no assignment occurs.
Otherwise, if the value of the array reference subexpression is null, then no assignment occurs and a NullPointerException is thrown.
Otherwise, the value of the array reference subexpression indeed refers to an array. If the value of the index subexpression is less than zero, or greater than or equal to the length of the array, then no assignment occurs and an IndexOutOfBoundsException is thrown.
Otherwise, the value of the index subexpression is used to select a component of the array referred to by the value of the array reference subexpression. This component is a variable; call its type SC. Also, let TC be the type of the left-hand operand of the assignment operator as determined at compile time.
If TC is a primitive type, then SC is necessarily the same as TC. The value of the right-hand operand is converted to a value of type TC and stored into the selected array component.
If T is a reference type, then SC may not be the same as T, but rather a type that extends or implements TC. Let RC be the class of the object referred to by the value of the right-hand operand at run time.
If class RC is not assignable to type SC, then no assignment occurs and an ArrayStoreException is thrown.
Otherwise, the reference value of the right-hand operand is stored into the selected array component.
which indicates that the attempt to store a reference to a StringBuffer into an array whose components are of type Thread throws an ArrayStoreException. The code is type-correct at compile time: the assignment has a left-hand side of type Object and a right-hand side of type Object. At run time, the first actual argument to method testFour is a reference to an instance of "array of Thread" and the third actual argument is a reference to an instance of class StringBuffer.
All compound assignment operators require both operands to be of primitive type, except for +=, which allows the right-hand operand to be of any type if the left- hand operand is of type String.
Otherwise, the value of the left-hand operand is saved and then the right-hand operand is evaluated. If this evaluation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the saved value of the left-hand variable and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator. If this operation completes abruptly (the only possibility is an integer division by zero-see §15.16.2), then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the result of the binary operation is converted to the type of the left-hand variable and the result of the conversion is stored into the variable.
Otherwise, consider the array component selected in the previous step, whose value was saved. This component is a variable; call its type S. Also, let T be the type of the left-hand operand of the assignment operator as determined at compile time.
If T is a primitive type, then S is necessarily the same as T.
The saved value of the array component and the value of the right-hand operand are used to perform the binary operation indicated by the compound assignment operator. If this operation completes abruptly (the only possibility is an integer division by zero-see §15.16.2), then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the result of the binary operation is converted to the type of the array component and the result of the conversion is stored into the array component.
If T is a reference type, then it must be String. Because class String is a final class, S must also be String. Therefore the run-time check that is sometimes required for the simple assignment operator is never required for a compound assignment operator.
The saved value of the array component and the value of the right-hand operand are used to perform the binary operation (string concatenation) indicated by the compound assignment operator (which is necessarily +=). If this operation completes abruptly, then the assignment expression completes abruptly for the same reason and no assignment occurs.
Otherwise, the String result of the binary operation is stored into the array component.
They are the cases where a right-hand side that throws an exception actually gets to throw the exception; moreover, they are the only such cases in the lot. This demonstrates that the evaluation of the right-hand operand indeed occurs after the checks for a null array reference value and an out-of-bounds index value.
Unlike C and C++, the Java language has no comma operator.
Compile-time constant expressions are used in case labels in switch statements (§14.9) and have a special significance for assignment conversion (§5.2).
"The integer " + Long.MAX_VALUE + " is mighty big."

References: §15
 §15
 §15
 §15
 §15
 §15
 §17
 §12
 §15
 §15
 §15
 §13
 §15
 §20
 §15
 §15
 §3
 §20
 §15
 §15