Expressions
An expression combines operators and/or operands into a unit of work. Those operands are expressions in their own right. Expressions range from the simplest literal and variable name to a complicated combination of operators and operands. Java uses expressions to create objects and arrays, to determine whether a loop statement should terminate or a decision statement should execute other statements, to pass values to and call methods, to assign values to variables, and so on. Understanding how to use expressions is one of the more important aspects in a study of non-object-oriented language features.
Note
Expressions involve identifiers, types, literals, variables, separators, and operators. That is the reason those other topics were explored before expressions.
At the Java Virtual Machine (JVM) level, an expression is evaluated by executing bytecode instructions that represent the various parts of the expression. The result of the evaluation is either a variable, a value, or nothing (indicated by the void keyword).
The following code fragment demonstrates those three kinds of evaluation results:
int j; j = 1; // j = 1 is an expression which assigns one to variable j. This variable // is the result of the expression. int x = 2; System.out.println (x + 1); // x + 1 is an expression which evaluates sub-expression x (by // retrieving its value), evaluates sub-expression 1 (by retrieving the // binary representation of this literal), adds this 1 to the value of x // and returns the result. This result (a value) is then passed to the // System.out.println () method call. void print (String s) { System.out.println (s); } print ("abc"); // print ("abc"); is an expression which calls a method named print() // with an "abc" string literal argument. This expression returns // nothing because the method call does not return anything (because it // is marked void).
In an expression, Java guarantees that each operator's operands are evaluated in a left-to-right evaluation order. The left-hand operand is evaluated before the right-hand operand.
The following code fragment demonstrates a consequence of evaluating the left-hand operand before the right-hand operand:
int x = 5; int y = (x = 2) * x; System.out.println (y);
What do you think gets printed? Give up? The answer is: 4. In the int y = (x = 2) * x; expression, x = 2 is evaluated first because that subexpression is contained in parentheses. That subexpression assigns 2 to x (a side effect of the overall expression). Now that its left-hand operandthe expression (x = 2)has been evaluated, * evaluates its right-hand operandthe expression x which contains 2. Therefore, 2 (the left-hand value) multiplied by 2 (the right-hand value) yields 4, which is assigned (by simple assignment operator =) to y.
An expression is evaluated in such a manner that certain operators evaluate their operands before other operators. That is known as precedence. Consider the previous example. If the parentheses were removed from x = 2, how would the resulting y = x = 2 * x; expression evaluate? Because the multiplicative operator (*) has higher precedence than the simple assignment operator (=), * would first evaluate its left operand (2) and then its right operand (xwhich contains value 5). Those values would be multiplied to produce 10. Now, the rightmost simple assignment operator would evaluate its leftmost operand (x) by remembering that variable and then evaluate its rightmost operand (the value 10), which would subsequently assign to x. Finally, the leftmost simple assignment operator would evaluate its left operand (y) by remembering that variable and then evaluate its right operand (xwhich contains 10) by retrieving the 10 stored in x. The value 10 would then assign to y.
4 For a quick summary of Java's operators and their natural precedence settings, see
"Operator Precedence," p. 789.
Natural precedence can be circumvented (as in the previous example) by using the parentheses separator. Java is designed so that it always evaluates an expression beginning with the subexpression in the innermost set of parenthesesin a left-to-right evaluation order (only).
The following code fragment demonstrates evaluating an expression using natural precedence and also using a parentheses override:
int j = 8 + 3 * 2; // 8 + 3 * 2 is naturally evaluated as if it was 8 + (3 * 2) because // multiplication has higher precedence. The result is 14, which is // assigned to j. int k = (8 + 3) * 2; // (8 + 3) is evaluated first. Then, the result value is multiplied // by 2. The final result is 22, which is assigned to k.
Tip
There is a lot more that could be said about expressionsat least another 10 pages. You are strongly encouraged to read up on expressions in the JLS. You don't have to do that right now, but learning what the JLS has to say about expressions will most definitely help you improve as a Java developer.
What's Next?
This chapter focused on literals, variables, separators/operators, and expressions in a continued exploration of Java's non-object-oriented language features. Although the source code to a couple of applications was presented, that source code was quite limited. To liven things up, source code needs to include statements. The next chapter focuses on the statement feature and presents more complex source code. By the way, does Java consider a semicolon character to be a statement? To find out, turn to the next chapter.