2.4. Expressions and Operators
So far in this chapter, we've learned about the primitive types that Java programs can manipulate and seen how to include primitive values as
literals
in a Java program. We've also used
An
expression
is the
1.7 // A floating-point literal true // A boolean literal sum // A variable When the Java interpreter evaluates a literal expression, the resulting value is the literal itself. When the interpreter evaluates a variable expression, the resulting value is the value stored in the variable. Primary expressions are not very interesting. More complex expressions are made by using operators to combine primary expressions. For example, the following expression uses the assignment operator to combine two primary expressionsa variable and a floating-point literalinto an assignment expression: sum = 1.7 But operators are used not only with primary expressions; they can also be used with expressions at any level of complexity. The following are all legal expressions: sum = 1 + 2 + 3*1.2 + (4 + 8)/3.0 sum/Math.sqrt(3.0 * 1.234) (int)(sum + 33) 2.4.1. Operator Summary
The kinds of expressions you can write in a programming language depend entirely on the set of operators available to you. Table 2-4 summarizes the operators available in Java. The P and A
Table 2-4. Java operators
2.4.1.1 PrecedenceThe P column of Table 2-4 specifies the precedence of each operator. Precedence specifies the order in which operations are performed. Consider this expression: a + b * c The multiplication operator has higher precedence than the addition operator, so a is added to the product of b and c . Operator precedence can be thought of as a measure of how tightly operators bind to their operands. The higher the number, the more tightly they bind.
Default operator precedence can be overridden through the use of parentheses that explicitly specify the order of operations. The previous expression can be rewritten as
(a + b) * c
The default operator precedence in Java was
// Class cast combined with member access
((Integer) o).intValue( );
// Assignment combined with comparison
while((line = in.readLine( )) != null) { ... }
// Bitwise operators combined with comparison
if ((flags & (PUBLIC PROTECTED)) != 0) { ... }
2.4.1.2 Associativity
When an expression involves several operators that have the same precedence, the operator associativity governs the order in which the operations are performed. Most operators are
The additive operators are all left-to-right associative, so the expression a+b-c is evaluated from left to right: (a+b)-c . Unary operators and assignment operators are evaluated from right to left. Consider this complex expression: a = b += c = -~d This is evaluated as follows: a = (b += (c = -(~d))) As with operator precedence, operator associativity establishes a default order of evaluation for an expression. This default order can be overridden through the use of parentheses. However, the default operator associativity in Java has been chosen to yield a natural expression syntax, and you rarely need to alter it. 2.4.1.3 Operand number and type
The fourth column of Table 2-4 specifies the number and type of the operands expected by each operator. Some operators
-n // The unary minus operator Most operators, however, are binary operators that operate on two operand values. The - operator actually comes in both forms: a - b // The subtraction operator is a binary operator
Java also defines one ternary operator, often called the conditional operator. It is like an
if
statement inside an expression. Its three operands are separated by a question mark and a
x > y ? x : y // Ternary expression; evaluates to the larger of x and y In addition to expecting a certain number of operands, each operator also expects particular types of operands. Column four of the table lists the operand types. Some of the codes used in that column require further explanation:
2.4.1.4 Return type
Just as every operator expects its operands to be of specific types, each operator produces a value of a specific type. The arithmetic, increment and decrement, bitwise, and shift operators return a
double
if at least one of the operands is a
double
. They return a
float
if at least one of the operands is a
float
. They return a
long
if at least one of the operands is a
long
.
The comparison, equality, and boolean operators always return boolean values. Each assignment operator returns whatever value it assigned, which is of a type compatible with the variable on the left side of the expression. The conditional operator returns the value of its second or third argument (which must both be of the same type). 2.4.1.5 Side effects
Every operator computes a value based on one or more operand values. Some operators, however, have
side effects
in addition to their basic evaluation. If an expression contains side effects, evaluating it changes the state of a Java program in such a way that evaluating the expression again may yield a different result. For example, the
++
increment operator has the side effect of incrementing a variable. The expression
++a
2.4.1.6 Order of evaluation
When the Java interpreter evaluates an expression, it
int a = 2; int v = ++a + ++a * ++a; Although the multiplication is performed before the addition, the operands of the + operator are evaluated first. Thus, the expression evaluates to 3+4*5, or 23. 2.4.2. Arithmetic Operators
Since most programs operate primarily on numbers, the most commonly used operators are often those that perform arithmetic operations. The arithmetic operators can be used with integers, floating-point
System.out.println("Total: " + 3 + 4); // Prints "Total: 34", not 7!
7/3 // Evaluates to 2 7/3.0f // Evaluates to 2.333333f 7/0 // Throws an ArithmeticException 7/0.0 // Evaluates to positive infinity 0.0/0.0 // Evaluates to NaN
2.4.3. String Concatenation Operator
In addition to adding numbers, the
+
operator (and the related
+=
operator) also
System.out.println("Quotient: " + 7/3.0f); // Prints "Quotient: 2.3333333"
As a result, you must be careful to put any addition expressions in parentheses when combining them with string concatenation. If you do not, the addition operator is interpreted as a concatenation operator.
The Java interpreter has built-in string conversions for all primitive types. An object is converted to a string by invoking its
toString( )
method. Some classes define custom
toString( )
methods so that objects of that class can easily be converted to strings in this way. An array is converted to a string by invoking the built-in
toString( )
method, which,
2.4.4. Increment and Decrement OperatorsThe ++ operator increments its single operand, which must be a variable, an element of an array, or a field of an object, by one. The behavior of this operator depends on its position relative to the operand. When used before the operand, where it is known as the pre-increment operator, it increments the operand and evaluates to the incremented value of that operand. When used after the operand, where it is known as the post-increment operator, it increments its operand, but evaluates to the value of that operand before it was incremented. For example, the following code sets both i and j to 2: i = 1; j = ++i; But these lines set i to 2 and j to 1: i = 1; j = i++; Similarly, the -- operator decrements its single numeric operand, which must be a variable, an element of an array, or a field of an object, by one. Like the ++ operator, the behavior of -- depends on its position relative to the operand. When used before the operand, it decrements the operand and returns the decremented value. When used after the operand, it decrements the operand, but returns the undecremented value. The expressions x++ and x-- are equivalent to x=x+1 and x=x-1 , respectively, except that when using the increment and decrement operators, x is only evaluated once. If x is itself an expression with side effects, this makes a big difference. For example, these two expressions are not equivalent: a[i++]++; // Increments an element of an array a[i++] = a[i++] + 1; // Adds one to an array element and stores it in another These operators, in both prefix and postfix forms, are most commonly used to increment or decrement the counter that controls a loop. 2.4.5. Comparison Operators
The comparison operators consist of the equality operators that test values for equality or inequality and the relational operators used with ordered types (numbers and characters) to test for greater than and less than relationships. Both types of operators yield a
boolean
result, so they are typically used with
if
statements and
while
and
for
if (o != null) ...; // The not equals operator while(i < a.length) ...; // The less than operator Java provides the following equality operators:
The relational operators can be used with numbers and characters, but not with boolean values, objects, or arrays because those types are not ordered. Java provides the following relational operators:
2.4.6. Boolean OperatorsAs we've just seen, the comparison operators compare their operands and yield a boolean result, which is often used in branching and looping statements. In order to make branching and looping decisions based on conditions more interesting than a single comparison, you can use the boolean (or logical) operators to combine multiple comparison expressions into a single, more complex expression. The boolean operators require their operands to be boolean values and they evaluate to boolean values. The operators are:
if (x < 10 && y > 3) ... // If both comparisons are true
This operator (and all the boolean operators except the unary
!
operator) have a lower precedence than the comparison operators. Thus, it is
if ((x < 10) && (y > 3)) ... You should use whichever style you find easier to read.
This operator is called a conditional AND because it conditionally evaluates its second operand. If the first operand evaluates to
false
, the value of the expression is
false
, regardless of the value of the second operand. Therefore, to increase efficiency, the Java interpreter takes a shortcut and skips the second operand. Since the second operand is not
if (data != null && i < data.length && data[i] != -1)
...
The second and third comparisons in this expression would cause errors if the first or second comparisons evaluated to false . Fortunately, we don't have to worry about this because of the conditional behavior of the && operator.
2.4.7. Bitwise and Shift Operators
The bitwise and shift operators are low-level operators that manipulate the individual bits that make up an integer value. The bitwise operators are most commonly used for testing and setting individual flag bits in a value. In order to understand their behavior, you must understand binary (base-2) numbers and the
If either of the arguments to a bitwise operator is a long , the result is a long . Otherwise, the result is an int . If the left operand of a shift operator is a long , the result is a long ; otherwise, the result is an int . The operators are:
byte b = ~12; // ~00001100 = => 11110011 or -13 decimal flags = flags & ~f; // Clear flag f in a set of flags
10 & 7 // 00001010 & 00000111 = => 00000010 or 2 if ((flags & f) != 0) // Test whether flag f is set When used with boolean operands, & is the infrequently used boolean AND operator described earlier.
10 7 // 00001010 00000111 = => 00001111 or 15 flags = flags f; // Set flag f When used with boolean operands, is the infrequently used boolean OR operator described earlier.
10 ^ 7 // 00001010 ^ 00000111 = => 00001101 or 13 When used with boolean operands, ^ is the infrequently used boolean XOR operator.
10 << 1 // 00001010 << 1 = 00010100 = 20 = 10*2 7 << 3 // 00000111 << 3 = 00111000 = 56 = 7*8 -1 << 2 // 0xFFFFFFFF << 2 = 0xFFFFFFFC = -4 = -1*4 If the left operand is a long , the right operand should be between 0 and 63. Otherwise, the left operand is taken to be an int , and the right operand should be between and 31.
10 >> 1 // 00001010 >> 1 = 00000101 = 5 = 10/2 27 >> 3 // 00011011 >> 3 = 00000011 = 3 = 27/8 -50 >> 2 // 11001110 >> 2 = 11110011 = -13 != -50/4 If the left operand is positive and the right operand is n , the >> operator is the same as integer division by 2 n .
0xff >>> 4 // 11111111 >>> 4 = 00001111 = 15 = 255/16 -50 >>> 2 // 0xFFFFFFCE >>> 2 = 0x3FFFFFF3 = 1073741811 2.4.8. Assignment OperatorsThe assignment operators store, or assign, a value into some kind of variable. The left operand must evaluate to an appropriate local variable, array element, or object field. The right side can be any value of a type compatible with the variable. An assignment expression evaluates to the value that is assigned to the variable. More importantly, however, the expression has the side effect of actually performing the assignment. Unlike all other binary operators, the assignment operators are right-associative, which means that the assignments in a=b=c are performed right-to-left, as follows: a=(b=c) . The basic assignment operator is = . Do not confuse it with the equality operator, = = . In order to keep these two operators distinct, I recommend that you read = as "is assigned the value." In addition to this simple assignment operator, Java also defines 11 other operators that combine assignment with the 5 arithmetic operators and the 6 bitwise and shift operators. For example, the += operator reads the value of the left variable, adds the value of the right operand to it, stores the sum back into the left variable as a side effect, and returns the sum as the value of the expression. Thus, the expression x+=2 is almost the same as x=x+2 . The difference between these two expressions is that when you use the += operator, the left operand is evaluated only once. This makes a difference when that operand has a side effect. Consider the following two expressions, which are not equivalent: a[i++] += 2; a[i++] = a[i++] + 2; The general form of these combination assignment operators is: var op = value This is equivalent (unless there are side effects in var ) to: var = var op value The available operators are: += -= *= /= %= // Arithmetic operators plus assignment &= = ^= // Bitwise operators plus assignment <<= >>= >>>= // Shift operators plus assignment The most commonly used operators are += and -= , although &= and = can also be useful when working with boolean flags. For example: i += 2; // Increment a loop counter by 2 c -= 5; // Decrement a counter by 5 flags = f; // Set a flag f in an integer set of flags flags & ~f; // Clear a flag f in an integer set of flags 2.4.9. The Conditional OperatorThe conditional operator ? : is a somewhat obscure ternary (three-operand) operator inherited from C. It allows you to embed a conditional within an expression. You can think of it as the operator version of the if/else statement. The first and second operands of the conditional operator are separated by a question mark ( ? ) while the second and third operands are separated by a colon (:). The first operand must evaluate to a boolean value. The second and third operands can be of any type, but they must be convertible to the same type. The conditional operator starts by evaluating its first operand. If it is TRue , the operator evaluates its second operand and uses that as the value of the expression. On the other hand, if the first operand is false , the conditional operator evaluates and returns its third operand. The conditional operator never evaluates both its second and third operand, so be careful when using expressions with side effects with this operator. Examples of this operator are: int max = (x > y) ? x : y; String name = (name != null) ? name : "unknown";
Note that the
?
: operator has lower precedence than all other operators except the assignment operators, so parentheses are not usually necessary around the operands of this operator. Many programmers find conditional expressions easier to read if the first operand is placed within parentheses, however. This is
2.4.10. The instanceof OperatorThe instanceof operator requires an object or array value as its left operand and the name of a reference type as its right operand. It evaluates to true if the object or array is an instance of the specified type; it returns false otherwise. If the left operand is null , instanceof always evaluates to false . If an instanceof expression evaluates to true , it means that you can safely cast and assign the left operand to a variable of the type of the right operand. The instanceof operator can be used only with reference types and objects, not primitive types and values. Examples of instanceof are:
"string" instanceof String // True: all strings are instances of String
"" instanceof Object // True: strings are also instances of Object
null instanceof String // False: null is never an instance of anything
Object o = new int[] {1,2,3};
o instanceof int[] // True: the array value is an int array
o instanceof byte[] // False: the array value is not a byte array
o instanceof Object // True: all arrays are instances of Object
// Use instanceof to make sure that it is safe to cast an object
if (object instanceof Point) {
Point p = (Point) object;
}
2.4.11. Special Operators
Java has five language constructs that are sometimes considered operators and sometimes
new ArrayList( ); new Point(1,2)
(byte) 28 // An integer literal cast to a byte type (int) (x + 3.14f) // A floating-point sum value cast to an integer value (String)h.get(k) // A generic object cast to a more specific string type |