JAVA_23
https://www.tutorialspoint.com/compile_java_online.php
Тоже хороший компилятор.
https://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.22.2
Table of Contents
&&||? :Much of the work in a 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 expressions and the rules for their evaluation.
When an expression in a program is evaluated (executed), the result denotes one of three things:
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.12) 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.8), 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.
Value set conversion (§5.1.13) is applied to the result of every expression that produces a value.
Each expression occurs in either:
The declaration of some (class or interface) type that is being declared: in a field initializer, in a static initializer, in an instance initializer, in a constructor declaration, in an annotation, or in the code for a method.
An annotation (§9.7) of a package or of a top level type declaration.
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 the value of a variable of type float or double is used in this manner, then value set conversion (§5.1.13) is applied to the value of the variable.
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 assignment compatible (§5.2) with the type of the expression, unless heap pollution (§4.12.2) occurs.
Likewise, the value stored in a variable is always compatible with the type of the variable, unless heap pollution occurs.
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.
If the type of an expression is float or double, then there is a question as to what value set (§4.2.3) the value of the expression is drawn from. This is governed by the rules of value set conversion (§5.1.13); these rules in turn depend on whether or not the expression is FP-strict.
Every compile-time constant expression (§15.28) is FP-strict.
If an expression is not a compile-time constant expression, then consider all the class declarations, interface declarations, and method declarations that contain the expression. If any such declaration bears the strictfp modifier (§8.1.1.3, §8.4.3.5, §9.1.1.2), then the expression is FP-strict.
If a class, interface, or method, X, is declared strictfp, then X and any class, interface, method, constructor, instance initializer, static initializer or variable initializer within X is said to be FP-strict.
Note that an annotation (§9.7) element value (§9.6) is always FP-strict, because it is always a compile-time constant expression.
It follows that an expression is not FP-strict if and only if it is not a compile-time constant expression and it does not appear within any declaration that has the strictfp modifier.
Within an FP-strict expression, all intermediate values must be elements of the float value set or the double value set, implying that the results of all FP-strict expressions must be those predicted by IEEE 754 arithmetic on operands represented using single and double formats.
Within an expression that is not FP-strict, some leeway is granted for an implementation to use an extended exponent range to represent intermediate results; the net effect, roughly speaking, is that a calculation might produce "the correct answer" in situations where exclusive use of the float value set or double value set might result in overflow or underflow.
If the type of an expression is a primitive type, then the value of the expression is of that same primitive type.
If the type of an expression is a reference type, then the class of the referenced object, or even whether the value is a reference to an object rather than null, is not necessarily known at compile time. There are a few places in the Java programming language where the actual class of a referenced object affects program execution in a manner that cannot be deduced from the type of the expression. They are as follows:
Method invocation (§15.12). The particular method used for an invocation o.m(...) is chosen based on the methods that are part of the class or interface that is the type of o. For instance methods, the class of the object referenced by the run-time value of o participates because a subclass may override a specific method already declared in a parent class so that this overriding method is invoked. (The overriding method may or may not choose to further invoke the original overridden m method.)
The instanceof operator (§15.20.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.5, §15.16). 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 exception 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.5, §15.13, §15.26.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 array component, similar to the check performed for a cast.
Exception handling (§14.20). 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.
Situations where the class of an object is not statically known may lead to run-time type errors.
In addition, there are situations where the statically known type may not be accurate at run time. Such situations can arise in a program that gives rise to compile-time unchecked warnings. Such warnings are given in response to operations that cannot be statically guaranteed to be safe, and cannot immediately be subjected to dynamic checking because they involve non-reifiable (§4.7) types. As a result, dynamic checks later in the course of program execution may detect inconsistencies and result in run-time type errors.
A run-time type error can occur only in these situations:
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.5, §15.16); in this case a ClassCastException is thrown.
In an automatically generated cast introduced to ensure the validity of an operation on a non-reifiable type (§4.7).
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.5, §15.13, §15.26.1); in this case an ArrayStoreException is thrown.
When an exception is not caught by any catch clause of a try statement (§14.20); in this case the thread of control that encountered the exception first attempts to invoke an uncaught exception handler (§11.3) 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.
Run-time exceptions are thrown by the predefined operators as follows:
A class instance creation expression (§15.9), array creation expression (§15.10), array initializer expression (§10.6), or string concatenation operator expression (§15.18.1) throws an OutOfMemoryError if there is insufficient memory available.
An array creation expression (§15.10) throws a NegativeArraySizeException if the value of any dimension expression is less than zero.
A field access expression (§15.11) throws a NullPointerException if the value of the object reference expression is null.
A method invocation expression (§15.12) that invokes an instance method throws a NullPointerException if the target reference is null.
An array access expression (§15.13) throws a NullPointerException if the value of the array reference expression is null.
An array access expression (§15.13) throws an ArrayIndexOutOfBoundsException if the value of the array index expression is negative or greater than or equal to the length of the array.
A cast expression (§15.16) throws a ClassCastException if a cast is found to be impermissible at run time.
An integer division (§15.17.2) or integer remainder (§15.17.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.26.1), a method invocation expression (§15.12), or a prefix or postfix increment (§15.14.2, §15.15.1) or decrement operator (§15.14.3, §15.15.2) may all throw an OutOfMemoryError as a result of boxing conversion (§5.1.7).
An assignment to an array component of reference type (§15.26.1) throws an ArrayStoreException when the value to be assigned is not compatible with the component type of the array (§10.5).
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.
If evaluation of an expression requires evaluation of a subexpression, then 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.
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.
The Java programming language guarantees that the operands of operators appear to be evaluated in a specific evaluation order, namely, from left to right.
It is recommended that 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.
If the operator is a compound-assignment operator (§15.26.2), then evaluation of the left-hand operand includes both remembering the variable that the left-hand operand denotes and fetching and saving that variable's value for use in the implied binary operation.
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.
Example 15.7.1-1. Left-Hand Operand Is Evaluated First
In the following program, the * operator has a left-hand operand that contains an assignment to a variable and a right-hand operand that contains a reference to the same variable. The value produced by the reference will reflect the fact that the assignment occurred first.
class Test1 {
public static void main(String[] args) {
int i = 2;
int j = (i=3) * i;
System.out.println(j);
}
}
This program produces the output:
9
It is not permitted for evaluation of the * operator to produce 6 instead of 9.
Example 15.7.1-2. Implicit Left-Hand Operand In Operator Of Compound Assigment
In the following program, 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 operator is evaluated, at which point the variable is set to 3.
class Test2 {
public static void main(String[] args) {
int a = 9;
a += (a = 3); // first example
System.out.println(a);
int b = 9;
b = b + (b = 3); // second example
System.out.println(b);
}
}
This program produces the output:
12 12
It is not permitted for either assignment (compound for a, simple for b) to produce the result 6.
See also the example in §15.26.2.
Example 15.7.1-3. Abrupt Completion of Evaluation of the Left-Hand Operand
class Test3 {
public static void main(String[] args) {
int j = 1;
try {
int i = forgetIt() / (j = 2);
} catch (Exception e) {
System.out.println(e);
System.out.println("Now j = " + j);
}
}
static int forgetIt() throws Exception {
throw new Exception("I'm outta here!");
}
}
This program produces the output:
java.lang.Exception: I'm outta here! Now j = 1
That is, the left-hand operand forgetIt() of the operator / throws an exception before the right-hand operand is evaluated and its embedded assignment of 2 to j occurs.
The Java programming language 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.17.2) or integer remainder % (§15.17.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.
Example 15.7.2-1. Evaluation of Operands Before Operation
class Test {
public static void main(String[] args) {
int divisor = 0;
try {
int i = 1 / (divisor * loseBig());
} catch (Exception e) {
System.out.println(e);
}
}
static int loseBig() throws Exception {
throw new Exception("Shuffle off to Buffalo!");
}
}
This program produces the output:
java.lang.Exception: Shuffle off to Buffalo!
and not:
java.lang.ArithmeticException: / by zero
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.
The Java programming language respects the order of evaluation indicated explicitly by parentheses and implicitly by operator precedence.
An implementation of the Java programming language 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 or both are 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.
So, for example, the test program:
strictfp class Test {
public static void main(String[] args) {
double d = 8e+307;
System.out.println(4.0 * d * 0.5);
System.out.println(2.0 * d);
}
}
prints:
Infinity 1.6e+308
because the first expression overflows and the second does not.
In contrast, integer addition and multiplication are provably associative in the Java programming language.
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 Java 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.
If evaluation of an argument expression completes abruptly, no part of any argument expression to its right appears to have been evaluated.
Example 15.7.4-1. Evaluation Order At Method Invocation
class Test1 {
public static void main(String[] args) {
String s = "going, ";
print3(s, s, s = "gone");
}
static void print3(String a, String b, String c) {
System.out.println(a + b + c);
}
}
This program produces the output:
going, going, gone
because the assignment of the string "gone" to s occurs after the first two arguments to print3 have been evaluated.
Example 15.7.4-2. Abrupt Completion of Argument Expression
class Test2 {
static int id;
public static void main(String[] args) {
try {
test(id = 1, oops(), id = 3);
} catch (Exception e) {
System.out.println(e + ", id=" + id);
}
}
static int test(int a, int b, int c) {
return a + b + c;
}
static int oops() throws Exception {
throw new Exception("oops");
}
}
This program produces the output:
java.lang.Exception: oops, id=1
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, class literals, field accesses, method invocations, and array accesses. A parenthesized expression is also treated syntactically as a primary expression.
Primary:
PrimaryNoNewArray
ArrayCreationExpression
PrimaryNoNewArray:
Literal
Type . class
void . class
this
ClassName . this
( Expression )
ClassInstanceCreationExpression
FieldAccess
MethodInvocation
ArrayAccess
This part of the grammar of the Java programming language 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 grouped together with primary expressions a little later when postfix expressions are introduced (