Topic 25 of 119 3. Operators in Java Interactive Tutorial

Unary Operators in Java: Positive, Negative, Increment, and More

13 mins read Last updated: 22 Aug 2026 19,325 views

Direct Answer & Core Concept

A unary operator takes only one operand, so it binds more tightly than any binary operator. Java has seven: unary plus `+`, unary minus `-`, prefix and postfix increment `++`, prefix and postfix decrement `--`, logical complement `!` and bitwise complement `~`. The key difference is what each form returns: `++x` changes `x` first and then yields the new value, while `x++` yields the old value and then changes `x`, so with `int x = 5` the statement `int y = x++;` leaves `x` at 6 and `y` at 5. That is why `i = i++;` leaves `i` unchanged at 1. Prefix and postfix also differ in what they are allowed to appear on: `++` and `--` modify a variable in place, so they need an lvalue and cannot be applied to a literal, a constant, or an expression, which is why `int z = ++5;` is rejected as *unexpected type*. `+` and `-` merely promote their operand, so `byte b = 10; b = b + 1;` fails as a lossy conversion while `b++;` compiles, because increment is a compound assignment with a built-in cast. Finally `!` works only on `boolean`, and `~` only on the integral types, giving `~0` the value -1 because it flips every one of the 32 bits.

Detailed Explanation & Mechanics

1. The Complete List of Unary Operators

A unary operator takes exactly one operand, which is why it binds more tightly than any binary operator: in -a * b the minus applies to a before the multiplication happens.

OperatorNameWorks OnMeaning
+xUnary plusNumericPromotes its operand and changes nothing
-xUnary minusNumericPromotes and negates
++xPrefix incrementVariable onlyIncrement, then yield the new value
x++Postfix incrementVariable onlyYield the old value, then increment
--xPrefix decrementVariable onlyDecrement, then yield the new value
x--Postfix decrementVariable onlyYield the old value, then decrement
!bLogical complementboolean onlyInverts true and false
~nBitwise complementIntegral types onlyFlips every bit

There is no unary "not a number" operator, and no unary version of %, *, or /. Note also that - and + in this table are the single-operand forms: a - b is binary subtraction and is a completely different operator that merely shares a symbol.

2. Prefix Versus Postfix: The One Rule That Matters

Both forms change the variable by exactly one. The only difference is **what value the expression produces**.

FormOrder of operationsExpression yieldsVariable after
++xChange x, then read itThe new valueIncremented
x++Read it, then change xThe old valueIncremented
Main.java
Run Online
int x = 5;
int y = x++;
step 1: read x -> 5(this is what y receives) step 2: x becomes 5 + 1 = 6 result: x == 6 y == 5 6(this is what q receives) result: p == 6 q == 6
Main.java
Run Online
int a = 5;
int b = ++a;
// b = 6, a = 6
int c = 5;
int d = c++;
// d = 5, c = 6
System.out.println(b + " " + a);
// 6 6
System.out.println(d + " " + c);
// 5 6

So the difference is only visible when the expression's value is **used**. On its own line, i++; and ++i; do exactly the same thing to i.

3. The Classic Trap: i = i++ Leaves i Unchanged

This is the single most common interview question on the topic, and the answer surprises almost everyone.

Main.java
Run Online
int i = 1;
i = i++;
System.out.println(i);
// 1, not 2

Why? Postfix i++ produces the value 1 and leaves i holding 2. Then the assignment i = 1 writes the 1 back over it. The increment happened and was immediately thrown away.

Main.java
Run Online
i = i++;
i = 1 RHS: i++ -> yields 1, leaves i = 2 LHS: i -> write 1 final: i == 1 the +1 was discarded

Contrast this with the compound assignment form, which behaves as you would expect, because it reads the variable and writes back the combined result.

Main.java
Run Online
int i = 1;
i += 1;
System.out.println(i);
// 2

The two operators work on different levels, which is why they disagree. i++ hands you a **copy** of the old value, and you store that copy. i += 1 reads the current value, adds one, and stores the sum.

4. Why ++ Needs a Variable, Not a Value

Increment and decrement change a variable in place, so they require something the language treats as an assignable location, called an **lvalue**. Literals, constants and arbitrary expressions are not assignable, and the compiler rejects them.

Main.java
Run Online
int x = ++5;
// error: unexpected type
int y = ++(3 + 2);
// error: unexpected type
final int K = 1;
int z = ++K;
// error: cannot assign a value to final variable K

Unary minus and plus have no such restriction, because they only read their operand and produce a new value.

Main.java
Run Online
int a = -5;
// perfectly legal
int b = -(3 + 2);
// also legal, evaluates to -5

The same distinction explains a pair of compile errors that look inconsistent at first glance but are not.

Main.java
Run Online
byte b = 10;
b = b + 1;
// error: incompatible types: possible lossy conversion from int to byte
b++;
// perfectly legal, and b becomes 11

b + 1 is binary arithmetic, so it promotes b to int and produces an int, which cannot be assigned back to a byte without a cast. b++ is treated as b = (byte)(b + 1), because increment is a compound assignment and the language inserts the narrowing cast for you. Both are covered in detail in the assignment topic.

Increment works on byte, short, char, int, long, float and double, but not on boolean, which is a compile error.

Main.java
Run Online
byte b = 10;
b++;
// 11
short s = 10;
s++;
// 11
char c = 'A';
c++;
// 'B', since 65 becomes 66
double d = 1.5;
d++;
// 2.5
boolean flag = false;
flag++;
// error: bad operand type boolean for unary operator '++'

5. Increment Silently Overflows at the Limits

An int that is incremented at Integer.MAX_VALUE wraps around to Integer.MIN_VALUE, with no exception. The same is true for long, and for byte and short, which wrap within their own range.

Main.java
Run Online
int big = Integer.MAX_VALUE;
big++;
System.out.println(big);
// -2147483648
int small = Integer.MIN_VALUE;
small--;
System.out.println(small);
// 2147483647
byte b = Byte.MAX_VALUE;
b++;
System.out.println(b);
// -128, wrapped within the byte range

The narrower types are worth noting. byte wraps at 127 back to -128, and short wraps at 32767 back to -32768, because those are their own ranges and not versions of the int range.

6. Increment Works on Array Elements

Because an array element is a genuine variable, arr[i]++ is legal and it writes back to the array. This is one place where postfix increment does something useful rather than merely handing back a stale copy.

Main.java
Run Online
int[] arr = {
    10, 20, 30
};
int v = arr[1]++;
System.out.println(v);
// 20, the old value
System.out.println(arr[0] + "," + arr[1] + "," + arr[2]);
// 10,21,30

The same expression can be dangerous when the index depends on the element being incremented.

Main.java
Run Online
int[] arr = {
    10, 20, 30
};
int v = arr[arr[1]++];
// uses arr[1] == 20 as the index
// throws ArrayIndexOutOfBoundsException: Index 20 out of bounds for length 3

The value 20 is read as the index and the array is still written back, but the index is far past the end of a three-element array. It is a rare trap precisely because the first half of the expression looks completely normal.

7. Expressions With Two Increments: Compiles, but Do Not Rely On It

Java permits an expression that increments the same variable twice, and it does produce a value. It is legal, and the result is deterministic on a given run, but the language specification says the two modifications are not sequenced relative to each other, so depending on it is a bad idea.

Main.java
Run Online
int i = 5;
System.out.println(i++ + i++);
// 11
System.out.println(i);
// 7, both increments did apply
int j = 5;
System.out.println(j++ + j);
// 11, j reads as 6 by then
int k = 1;
System.out.println(k++ + ++k);
// 4

The practical advice is to never write two of these in one expression. Keep each one as its own statement and the question disappears.

Main.java
Run Online
int i = 5;
int sum = i++;
sum = sum + i++;

8. Unary Plus and Minus

Unary minus negates its operand and unary plus leaves the value alone, but both still perform numeric promotion. This is the detail that surprises people: + is not quite a no-op.

Main.java
Run Online
int a = +7;
System.out.println(a);
// 7
char c = 'A';
System.out.println(-c);
// -65, not a character
System.out.println((char)(-c));
// a non-ASCII character
System.out.println((int)+c);
// 65, plus promoted the char to int

Because both promote, unary minus on a char gives an int, and assigning it back to a char needs a cast, just like binary arithmetic does.

Main.java
Run Online
char c = 'A';
c -> 'A'(a 16 bit char, value 65) -c -> promote to int -> 65 negate -> -65 an int, not a char(char)(-c) -> narrow back down -> some non-ASCII character so -'A' == -65 , and -'A' != '-' or 'A' in any sense

Unary minus can also be applied twice to undo itself, and it overflows on the most negative value.

Main.java
Run Online
int n = 5;
System.out.println(-(-n));
// 5
int m = Integer.MIN_VALUE;
System.out.println(-m);
// still MIN_VALUE, it overflows

-Integer.MIN_VALUE is itself Integer.MIN_VALUE, because 2147483648 has no int representation and wraps. There is no positive counterpart to MIN_VALUE in the signed 32-bit range.

9. The Logical Complement Operator !

! inverts a boolean and only a boolean. It does not work on numbers, which is where it differs from the bitwise operators in C and C++.

ExpressionResultNotes
!truefalseInverts the value
!falsetrueInverts the value
!!flagsame as flagDouble negation restores the original
!(5 > 3)falseParentheses matter
!5compile errorbad operand type int for unary operator '!'
!0compile errorZero is not false in Java
Main.java
Run Online
boolean flag = false;
System.out.println(!flag);
// true
System.out.println(!!flag);
// false, back where it started
System.out.println(!(5 > 3));
// false
System.out.println(!5 > 3);
// compiles as (!5) > 3, and !5 does not compile

The last line is worth pausing on. Because ! binds very tightly, !5 > 3 is parsed as (!5) > 3, not as !(5 > 3), so it fails to compile. Parentheses are not optional there.

10. The Bitwise Complement Operator ~

~ flips every bit of an integral value. In two's complement, which is what Java uses for all its signed integers, flipping all the bits is the same as negating and then subtracting one: ~n equals -(n + 1).

ExpressionResultReason
~0-1All 32 bits were zero, now all are one
~1-2Equals -(1 + 1)
~5-6Equals -(5 + 1)
~~55~ is its own inverse
~'A'-66'A' is 65, so -(65 + 1)
~1.0compile errorbad operand type double for unary operator '~'

Here is the bit pattern that makes it obvious. The compiler prints it for us with toBinaryString.

Main.java
Run Online
System.out.println(Integer.toBinaryString(5));
// 101
System.out.println(Integer.toBinaryString(~5));
// 11111111111111111111111111111010
System.out.println(Integer.toBinaryString(-6));
// 11111111111111111111111111111010
System.out.println(~5 == -(5 + 1));
// true
Main.java
Run Online
5 in 32 bit two's complement

       00000000 00000000 00000000 00000101
       |
       |  flip every single bit
       v
     ~5

       11111111 11111111 11111111 11111010

     and that is exactly -6, because -6 is stored as its complement plus one,
     which is 5 + 1 =
6.  This is why ~n always equals -(n + 1).

Since ~ is its own inverse, applying it twice gives the original back, which is a handy way to describe it.

Main.java
Run Online
System.out.println(~~5 == 5);
// true
System.out.println(~~Integer.MIN_VALUE == Integer.MIN_VALUE);
// true

Note that ~Integer.MIN_VALUE is Integer.MAX_VALUE, not MIN_VALUE. It is the one value in the range that is its own negative, so negating it overflows and returns it unchanged, but complementing it produces the largest positive value.

Main.java
Run Online
System.out.println(~Integer.MIN_VALUE);
// 2147483647
System.out.println(-Integer.MIN_VALUE);
// -2147483648, unchanged by overflow

11. The Compound Operators Are Unary in Origin

++ and -- are close relatives of += and -=, and the pair behaves in a revealingly different way, which is worth seeing side by side.

StatementCompileValue of i afterwardsWhy
i = i + 1error on a byten/aBinary arithmetic returns an int
i += 1legal11Compound assignment inserts a cast
i++legal11Increment inserts the same cast
++ilegal11Identical effect, returns the new value
Main.java
Run Online
byte i = 10;
i = i + 1;
// error: incompatible types: possible lossy conversion from int to byte
i += 1;
// legal, i is 11
i++;
// legal, i is 12

This is the practical reason to reach for ++ rather than writing out += 1: it is shorter, and it also compiles on the narrow types where the expanded form does not.

12. Unary Operators Cheat Sheet

QuestionAnswer
Unary operators in Java+ - ++ -- ! ~
++x returnsThe new value
x++ returnsThe old value
Does the variable still changeYes, in both cases, by one
int i = 1; i = i++;i is 1, unchanged
Do prefix and postfix differ on their own lineNo, only when the value is used
int z = ++5;Compile error, not an lvalue
int z = ++finalVar;Compile error, cannot assign to final
byte b; b = b + 1;Compile error, lossy conversion
byte b; b++;Legal, the cast is implicit
Increment on a booleanCompile error, bad operand type
Increment past MAX_VALUESilent overflow, no exception
arr[i]++Legal, writes back to the array
i++ + i++Compiles, but never depend on it
+77, but it still promotes the operand
-'A'-65, an int not a char
-Integer.MIN_VALUEMIN_VALUE again, it overflows
!5Compile error, booleans only
!5 > 3Parsed as (!5) > 3, so it does not compile
~0-1
~n equals-(n + 1)
Is ~ its own inverseYes, ~~n == n
~1.0Compile error, integral types only
PrecedenceHigher than every binary operator

The whole topic reduces to one distinction. ++x and --x change the variable and then hand you the new value, while x++ and x-- hand you the old value and change the variable afterwards. Everything else follows from that: why i = i++ is a no-op, why ++ needs a real variable while - does not, and why the increment forms compile on a byte where the written-out version does not.

Runnable Code Example: Unary Operators in Java

Java 17+
Main.java
Run Online
public class UnaryDemo {
    public static void main(String[] args) {
        // ---------- prefix: change first, then hand back the NEW value ----------
        int a = 5;
        int b = ++a;
        // a becomes 6 first, b receives 6
        System.out.println("Prefix:   b=" + b + ", a=" + a);
        // ---------- postfix: hand back the OLD value, then change ----------
        int c = 5;
        int d = c++;
        // d receives 5, then c becomes 6
        System.out.println("Postfix:  d=" + d + ", c=" + c);
        // ---------- the classic trap ----------
        int i = 1;
        i = i++;
        // takes 1, stores it back, then increments
        System.out.println("i = i++:  i=" + i + "   (unchanged)");
        // ---------- unary plus and minus ----------
        System.out.println("Unary minus: -7 = " +(-7));
        System.out.println("Unary plus:  +7 = " +(+7));
        System.out.println("'-A' is     : " +(-'A') + "   (promotes to int)");
        // ---------- logical complement: booleans only ----------
        boolean flag = false;
        System.out.println("!false     = " +(!flag));
        // ---------- bitwise complement: integral types only ----------
        System.out.println("~0         = " + ~0 + "   (flips all 32 bits)");
        System.out.println("~5         = " + ~5 + "   and -(5+1) is " +(-(5 + 1)));
        // ---------- increment overflows silently ----------
        int big = Integer.MAX_VALUE;
        big++;
        System.out.println("MAX_VALUE++ = " + big);
    }
}

Common Java Technical Interview Questions & Answers

Interview Prep
Q1

What is the difference between prefix and postfix increment in Java?

Both change the variable by one; the difference is what the expression yields. ++x changes x first and then produces the new value, while x++ produces the old value and then changes x. So with int x = 5, int y = x++ gives y = 5 and x = 6, whereas int q = ++p gives q = 6 and p = 6. On a line of their own the two forms are indistinguishable, because the value is discarded.
Q2

What does int i = 1; i = i++; print, and why?

It prints 1, so i is unchanged. The postfix i++ yields the current value 1 and leaves i holding 2, then the assignment writes that 1 back over i. The increment is performed and then immediately discarded. The compound form i += 1 does not share this problem because it reads the current value, adds one, and stores the sum.
Q3

Why does byte b = 10; b++; compile when b = b + 1 does not?

b + 1 is binary arithmetic, so it promotes the byte to int and produces an int, which cannot be narrowed back without a cast. Increment is a compound assignment, so the compiler inserts the narrowing cast implicitly, exactly as it does for b += 1. That is why the shorter form is also the more permissive one on the narrow types.
Q4

Why can you write int a = -5 but not int b = ++5?

Unary minus only reads its operand and produces a new value, so any expression is acceptable. Increment modifies a variable in place, so it requires an lvalue, and a literal is not assignable. The compiler reports "unexpected type" for ++5. A constant or final variable is rejected too, with "cannot assign a value to final variable".
Q5

What does the ~ operator do, and what is ~0?

It flips every bit of an integral value. Java uses two's complement for all signed integers, so ~n is equivalent to -(n + 1), which makes ~0 equal to -1 and ~5 equal to -6. Because it flips every bit, ~ is its own inverse, so ~~n is always equal to n.
Q6

Why does -Integer.MIN_VALUE still return Integer.MIN_VALUE?

Integer.MIN_VALUE is -2147483648, and its positive counterpart 2147483648 cannot be represented as an int, so negating it overflows and wraps back to the minimum value. There is no positive counterpart to MIN_VALUE in the signed 32-bit range. Note that ~Integer.MIN_VALUE is different: it gives Integer.MAX_VALUE, because all the bits are one apart from the MIN_VALUE pattern.
Q7

Does the logical complement ! work on numbers?

No. It accepts only boolean and rejects anything else with "bad operand type int for unary operator '!'". In Java zero is not false, so there is no truthiness for ! to exploit. The bitwise operators from C and C++, where !0 is true, behave differently in Java.
Q8

What does the ! operator bind to in !5 > 3?

It binds very tightly, so the expression parses as (!5) > 3 rather than !(5 > 3), and it fails to compile because ! needs a boolean. Parentheses are required to express the negation of a comparison, as in !(5 > 3). This is a good illustration of why the highest-precedence unary operators can still surprise people.
Q9

Can you increment a boolean?

No. Increment and decrement work on byte, short, char, int, long, float and double, but a boolean produces "bad operand type boolean for unary operator '++'". The same restriction explains why byte and char can be incremented: they are numeric types with well-defined arithmetic.
Q10

What happens when you increment past Integer.MAX_VALUE?

It silently overflows and wraps to Integer.MIN_VALUE, with no exception thrown. The same happens at the other end and for long. The narrower types wrap within their own range, so a byte at Byte.MAX_VALUE goes to -128 and a short at Short.MAX_VALUE goes to -32768.
Q11

Is arr[i]++ legal, and does it update the array?

Yes. An array element is a genuine variable rather than a temporary, so arr[i]++ is a valid lvalue and writes the incremented value back into the array. The expression itself still yields the old value, so int v = arr[1]++ gives v the previous element and leaves the array incremented. This is where postfix increment genuinely modifies something rather than just handing back a stale copy.
Q12

Is int i = 5; int sum = i++ + i++; acceptable code?

It compiles and produces a deterministic result on a given run, but the language specification does not sequence the two modifications relative to each other, so the result is not something to rely on. Write each increment as its own statement. The single-increment case i = i++ is different, since there the behaviour is fully specified and simply leaves i unchanged.

Key Takeaways, Best Practices & Clean Code Rules

Java Best Practice & Production Rules

Treat ++ and -- as statements rather than as value-producing expressions. Prefix and postfix only differ in the value the expression yields, so on a line of their own they are identical, and the only reason to prefer one over the other is a for loop index where ++i is conventional. Never let the value of an increment feed into a larger calculation, because i++ hands back a copy of the old value and i = i++ silently discards the increment entirely; write the steps out as separate statements instead. It is also worth knowing that byte b = b + 1 fails to compile while b++ succeeds, so do not read the short form as a style shortcut on the narrow types. Prefer explicit arithmetic such as total = total + price, or the += form, over ++ in business logic, since a named increment states the intent far better than a terse symbol and leaves no room for the order-of-evaluation traps described above. When a loop bound might already be at the limit, check for overflow before incrementing, or use Math.incrementExact, because Java wraps silently at Integer.MAX_VALUE and will hand you a negative number with no exception. Finally, remember that increment needs a variable: it cannot be applied to a literal, a constant, or the result of a calculation, so reach for ordinary addition when there is nothing to modify in place.
We value your privacy & transparency

OperateTools uses cookies and local storage to ensure basic tool functionality, preserve preferences, and analyze aggregated traffic. We never sell your personal data or index your document files. Learn more in our Privacy Policy.