3.2 Unary Operators, Assignment, and Operator Precedence
Key Takeaways
- Unary operators act upon a single operand; unary plus (+) and minus (-) apply unary numeric promotion (widening byte, short, char to int), while logical complement (!) negates booleans and bitwise inversion (~) inverts bits according to ~x = -(x + 1).
- Prefix increment and decrement (++x, --x) update the variable in memory before returning the new value to the containing expression, whereas postfix operators (x++, x--) return the original value before updating memory.
- In expressions with side effects, Java evaluates operands strictly from left to right; the classic self-assignment trap x = x++ restores the original value of x, completely discarding the increment.
- Compound assignment operators (+=, -=, *=, /=, %=) automatically inject an implicit narrowing cast E1 = (T)((E1) op (E2)), enabling arithmetic on byte, short, and char without compilation errors but risking silent numeric overflow.
- Operator precedence establishes the syntactic binding of operands, with postfix operators having highest precedence, followed by unary, multiplicative, additive, relational, equality, logical, ternary, and assignment operators.
3.2 Unary Operators, Assignment, and Operator Precedence
[!NOTE] Exam Focus: The 1Z0-811 examination frequently tests candidate mastery of complex expressions containing prefix and postfix increment/decrement operators, chained assignments, compound assignments on narrow integer types (
byte,short), and operator precedence. Candidates must be able to trace operand evaluation from left to right and account for state changes at each execution step.
Java operators are distinguished not only by their mathematical purpose but by their arity (number of operands), precedence (order of binding), and associativity (direction of evaluation among operators of equal rank). Understanding unary operators, assignment expressions, and operator binding rules is critical for accurately predicting program output on the certification exam.
1. Unary Operators: +, -, !, ~
Unary operators operate on a single operand. Java defines four primary non-increment unary operators:
Unary Plus (+) and Unary Minus (-)
- Unary Plus (
+): Explicitly denotes a positive numeric value (rarely needed). Crucially, like binary arithmetic, unary plus applies unary numeric promotion (JLS §5.6.1), automatically promoting any operand narrower thanint(byte,short,char) to 32-bitint. - Unary Minus (
-): Negates the arithmetic sign of its operand, also promoting narrow integer operands toint.
byte b = 15;
// byte negB = -b; // COMPILE ERROR: -b promotes b to int; cannot assign int to byte!
int negB = -b; // Compiles cleanly: result is -15
byte castNeg = (byte)-b; // Compiles cleanly with explicit narrowing cast
Logical Complement (!)
The logical NOT operator (!) inverts the value of a boolean expression: !true evaluates to false, and !false evaluates to true. It operates strictly on boolean operands; applying ! to numeric types causes an immediate compilation failure.
boolean loggedIn = false;
boolean showLoginPrompt = !loggedIn; // Evaluates to true
Bitwise Inversion / Complement (~)
The bitwise complement operator (~) inverts every bit in an integral value's two's-complement binary representation (converting 0 bits to 1, and 1 bits to 0). The mathematical formula for bitwise inversion on any integer $x$ is:
int val = 7;
System.out.println(~val); // Outputs -8 (-(7 + 1))
int zero = 0;
System.out.println(~zero); // Outputs -1 (-(0 + 1))
int neg = -10;
System.out.println(~neg); // Outputs 9 (-(-10 + 1))
Like unary minus, the ~ operator applies unary numeric promotion, promoting byte, short, and char operands to int.
2. Prefix vs. Postfix Increment and Decrement Operators
The increment (++) and decrement (--) operators increase or decrease a variable's value by exactly 1.
x++; // Semantically equivalent to: x = x + 1;
x--; // Semantically equivalent to: x = x - 1;
[!WARNING] Variables Only: Increment and decrement operators can only be applied to variables. Applying them to numeric literals, constants, or parenthesized expressions causes a compile-time error (
unexpected type: required variable, found value):// 10++; // COMPILE ERROR: 10 is a literal, not a variable // (x + 1)++; // COMPILE ERROR: (x + 1) is a value expression
Prefix Operators (++x, --x)
In prefix position, the operator precedes the variable. The variable in memory is modified first, and the new, updated value is returned to the containing expression:
int x = 5;
int y = ++x; // Step 1: x increments from 5 to 6 in memory; Step 2: y is assigned the new value 6
System.out.println("x=" + x + ", y=" + y); // Prints: x=6, y=6
Postfix Operators (x++, x--)
In postfix position, the operator follows the variable. The variable's original, current value is captured and returned to the containing expression first; afterward, the variable is modified in memory:
int x = 5;
int y = x++; // Step 1: y is assigned the original value 5; Step 2: x increments from 5 to 6 in memory
System.out.println("x=" + x + ", y=" + y); // Prints: x=6, y=5
| Operator | Syntax | Name | Timing of Memory Mutation | Value Returned to Expression |
|---|---|---|---|---|
++x | Prefix | Prefix Increment | Increments $x$ by 1 before reading value | The new incremented value |
x++ | Postfix | Postfix Increment | Increments $x$ by 1 after reading value | The original unincremented value |
--x | Prefix | Prefix Decrement | Decrements $x$ by 1 before reading value | The new decremented value |
x-- | Postfix | Postfix Decrement | Decrements $x$ by 1 after reading value | The original undecremented value |
3. Dissecting Complex Expressions with Side Effects
On Exam 1Z0-811, you will encounter complex expressions combining prefix and postfix operators across one or more variables. To solve these reliably, always follow this rule from JLS §15.7:
In Java, operands of an operator are strictly evaluated from left to right.
Case Study 1: Mixed Prefix and Postfix on a Single Variable
int a = 5;
int b = a++ + ++a;
System.out.println("a=" + a + ", b=" + b);
Step-by-step trace of a++ + ++a:
- Evaluate left operand
a++: Postfix captures the current value5. Variableais then incremented to6in memory. - Evaluate right operand
++a: Prefix incrementsafrom6to7in memory first, then returns7. - Perform addition:
5 + 7 = 12. - Assign to
b:b = 12. Final state:a = 7,b = 12.
Case Study 2: The Notorious Self-Assignment Trap
int count = 10;
count = count++;
System.out.println(count);
What is printed? Many candidates mistakenly assume 11. The correct output is 10!
Here is the exact bytecode execution sequence:
- The right-hand expression
count++is evaluated. Because it is postfix, it yields the current value10and schedulescountfor incrementing. - Variable
countis incremented in memory to11. - The simple assignment operator
=executes, storing the yielded value10back into variablecount. - The
11in memory is completely overwritten with10. The final value ofcountremains10!
Case Study 3: Multiple Variables in Compound Arithmetic
int x = 3;
int y = 6;
int z = ++x * y-- - x--;
System.out.println("x=" + x + ", y=" + y + ", z=" + z);
Trace:
- Multiplication (
*) has higher precedence than subtraction (-), but operands evaluate strictly left-to-right: - Left operand of
*is++x:xincrements from3to4, yields4. - Right operand of
*isy--: Postfix yields currentyvalue6, then decrementsyto5. - Perform multiplication:
4 * 6 = 24. - Subtraction operator evaluates its right operand
x--: Postfix yields currentxvalue4, then decrementsxto3. - Perform subtraction:
24 - 4 = 20. - Assign to
z:z = 20. Final values:x = 3,y = 5,z = 20.
4. Simple Assignment and Chained Assignment
The simple assignment operator (=) assigns the value of the right-hand expression to the variable named on the left.
Assignment is an Expression
In Java, an assignment is not merely a statement—it is an expression that returns the value being assigned. Because assignment associates from right to left, you can chain assignments:
int a, b, c;
a = b = c = 100; // Evaluates right-to-left: (a = (b = (c = 100)))
// All three variables receive 100
The Boolean Assignment Trap in Conditions
Because assignment returns a value, writing an assignment inside a conditional statement is syntactically legal if the variable is boolean:
boolean isClosed = false;
if (isClosed = true) { // Assigns true to isClosed, and the expression evaluates to true!
System.out.println("Store is closed"); // ALWAYS EXECUTES!
}
If the variable is numeric, such as if (x = 5), the code fails compilation because the expression evaluates to integer 5, which cannot be converted to boolean.
5. Compound Assignment Operators & The Implicit Cast Rule
Java provides eleven compound assignment operators: +=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>=, and >>>=.
A compound assignment operator combines an arithmetic or bitwise operation with assignment:
x += 5; // Conceptually related to: x = x + 5;
The Implicit Narrowing Cast Rule (JLS §15.26.2)
There is a critical architectural difference between simple assignment with addition and compound assignment. The Java Language Specification formally defines that a compound assignment of the form:
is equivalent to:
where $T$ is the declared data type of variable $E_1$, with $E_1$ evaluated only once.
This means compound assignment operators automatically insert an implicit narrowing cast back to the type of the left-hand variable!
short s = 20;
// s = s + 5; // COMPILE ERROR: s + 5 promotes to int; cannot assign int to short!
s += 5; // COMPILES CLEANLY: Compiler injects (short)(s + 5)
byte b = 50;
// b = b * 2; // COMPILE ERROR: b * 2 produces int!
b *= 2; // COMPILES CLEANLY: Compiler injects (byte)(b * 2)
The Silent Numeric Overflow Danger
Because compound assignment operators inject an explicit cast without compiler warning, numeric overflow can occur silently at runtime:
byte maxByte = 127;
maxByte += 1; // Evaluates to (byte)(128) -> wraps around to -128 in two's complement!
System.out.println(maxByte); // Prints: -128 (no error, no exception)
6. Comprehensive Operator Precedence & Associativity Hierarchy
When multiple operators appear in a single expression without parentheses, Java's operator precedence determines how operands bind to operators. When two operators share the same precedence level, associativity dictates whether evaluation groups from left to right or from right to left.
| Precedence Level | Operator Category | Specific Operators | Associativity |
|---|---|---|---|
| 1 (Highest) | Postfix | expr++, expr-- | Left-to-Right |
| 2 | Unary Prefix | ++expr, --expr, +expr, -expr, ~, ! | Right-to-Left |
| 3 | Cast / Creation | (type), new | Right-to-Left |
| 4 | Multiplicative | *, /, % | Left-to-Right |
| 5 | Additive | +, - | Left-to-Right |
| 6 | Shift | <<, >>, >>> | Left-to-Right |
| 7 | Relational | <, <=, >, >=, instanceof | Left-to-Right |
| 8 | Equality | ==, != | Left-to-Right |
| 9 | Bitwise AND | & | Left-to-Right |
| 10 | Bitwise XOR | ^ | Left-to-Right |
| 11 | Bitwise OR | | | Left-to-Right |
| 12 | Conditional AND | && | Left-to-Right |
| 13 | Conditional OR | || | Left-to-Right |
| 14 | Ternary Conditional | ? : | Right-to-Left |
| 15 (Lowest) | Assignment | =, +=, -=, *=, /=, %=, etc. | Right-to-Left |
Using Parentheses to Override Precedence
Parentheses () possess the highest binding power and can be used to explicitly dictate evaluation order and eliminate ambiguity:
int result1 = 5 + 3 * 2; // 5 + 6 = 11 (* takes precedence over +)
int result2 = (5 + 3) * 2; // 8 * 2 = 16 (parentheses force addition first)
Consider the following Java code declarations and assignment statements:
Which of the following statements correctly describes how the compiler handles Statement 1 and Statement 2?short val1 = 15;
short val2 = 30;
// Statement 1:
val1 = val1 + val2;
// Statement 2:
val1 += val2;
What is the console output when the following Java code executes?
int x = 4;
int y = ++x * 2 + x++ - --x;
System.out.println("x=" + x + ", y=" + y);
What is printed to the console when the following statements are executed?
int a = 8;
int b = ~a;
int c = a - b;
System.out.println(b + " " + c);