☕ Java
Unary Operators
Unary operators take a single operand. Java has five: unary plus, unary minus, increment, decrement, and logical NOT. The increment and decrement operators have prefix and postfix forms that behave differently — a distinction that trips up developers at every level.
The Five Unary Operators
Java has five unary operators — each takes exactly one operand:
Java
+ // unary plus — explicitly marks a value as positive (rarely used)
- // unary minus — negates a value
++ // increment — adds 1 (prefix and postfix forms)
-- // decrement — subtracts 1 (prefix and postfix forms)
! // logical NOT — inverts a boolean+ and - Unary Operators
Unary minus negates a numeric value. Unary plus is syntactically valid but rarely used — it doesn't change the value, but it does trigger type promotion to int for byte and short.
Java
// Unary minus — negates:
int a = 5;
int b = -a; // -5
System.out.println(-10); // -10
System.out.println(-(-5)); // 5 — double negation
// Negating variables:
int temperature = 30;
int belowZero = -temperature; // -30
double d = 3.14;
double neg = -d; // -3.14
// Unary minus on expressions:
int x = 3, y = 4;
int result = -(x + y); // -(7) = -7
// Unary plus — promotes byte/short to int, otherwise no effect:
byte b = 42;
// byte promoted = +b; // COMPILE ERROR — +b is int, not byte
int promoted = +b; // 42 — type promoted to int
int i = 5;
int same = +i; // 5 — no change for int
// Practical use of unary minus:
int[] temps = {-10, 5, -3, 8, -15};
for (int t : temps) {
if (t < 0) System.out.println("Below zero: " + (-t) + " degrees cold");
}++ and -- Prefix vs Postfix
Increment and decrement operators exist in two forms. The position of the operator — before or after the operand — determines when the update happens relative to the value being used.
Java
// Prefix (++x) — increment FIRST, then use the new value:
int a = 5;
int b = ++a;
System.out.println(a); // 6 — incremented
System.out.println(b); // 6 — received the incremented value
// Postfix (x++) — use the current value FIRST, then increment:
int x = 5;
int y = x++;
System.out.println(x); // 6 — incremented
System.out.println(y); // 5 — received the ORIGINAL value
// Same with decrement:
int m = 10;
int n = --m; // prefix: m=9, n=9
System.out.println(m + " " + n); // 9 9
int p = 10;
int q = p--; // postfix: q=10, then p=9
System.out.println(p + " " + q); // 9 10
// In isolation — no difference:
int count = 0;
count++; // same result as ++count when not in an expression
++count; // both just add 1
// In expressions — significant difference:
int i = 3;
System.out.println(i++ * 2); // 6 — uses 3, then i becomes 4
System.out.println(++i * 2); // 10 — i becomes 5 first, then 5*2
// Classic postfix trap:
int val = 5;
val = val++; // val is STILL 5!
// Sequence: temp=val(5), val=val+1(6), val=temp(5) — postfix returns original
System.out.println(val); // 5 — not 6!
// The correct way to increment and keep:
val = val + 1; // or: val++; (as standalone statement, not expression)++ and -- in Loops
Increment and decrement are most commonly seen in for loops. In the loop update clause, prefix and postfix produce identical results — there's no expression context, so the timing difference doesn't matter.
Java
// In for loop update — i++ and ++i are identical in effect:
for (int i = 0; i < 5; i++) {
System.out.print(i + " "); // 0 1 2 3 4
}
for (int i = 0; i < 5; ++i) {
System.out.print(i + " "); // 0 1 2 3 4 — identical output
}
// Counting down with --:
for (int i = 5; i > 0; i--) {
System.out.print(i + " "); // 5 4 3 2 1
}
// While loop:
int n = 10;
while (n-- > 0) {
System.out.print(n + " "); // 9 8 7 6 5 4 3 2 1 0
}
// Note: n-- uses n (10) for the > 0 check, then decrements
// Loop runs while n was > 0, but prints already-decremented n
// Contrast with prefix:
n = 10;
while (--n > 0) {
System.out.print(n + " "); // 9 8 7 6 5 4 3 2 1
}
// --n decrements first (10→9), then checks 9 > 0
// Stops when --n produces 0 (which is not > 0)! — Logical NOT
! inverts a boolean value. It's unary, prefix-only, and has high precedence — be careful with complex expressions.
Java
// Basic NOT:
System.out.println(!true); // false
System.out.println(!false); // true
// NOT with boolean variables:
boolean isLoggedIn = false;
if (!isLoggedIn) {
System.out.println("Please log in");
}
// NOT with comparisons — parentheses control precedence:
int x = 5;
System.out.println(!(x > 3)); // false — NOT(true) = false
System.out.println(!x > 3); // COMPILE ERROR — !x is invalid (!int)
// De Morgan's law — simplify negated conditions:
// !(A && B) == (!A || !B)
// !(A || B) == (!A && !B)
// Apply De Morgan to simplify:
if (!(age < 18 || score < 50)) { } // hard to read
if (age >= 18 && score >= 50) { } // cleaner equivalent
if (!(isAdmin && isVerified)) { } // readable but complex
if (!isAdmin || !isVerified) { } // De Morgan applied
// NOT with method return values:
List<String> list = List.of("a", "b", "c");
if (!list.isEmpty()) {
System.out.println("List has " + list.size() + " items");
}
if (!str.contains("@")) {
throw new IllegalArgumentException("Not a valid email");
}~ — Bitwise NOT (Complement)
~ is the bitwise complement operator — it inverts every bit of an integer. It's sometimes categorized with unary operators because it takes one operand.
Java
// ~ inverts all bits — result is -(n+1):
System.out.println(~0); // -1
System.out.println(~1); // -2
System.out.println(~5); // -6
System.out.println(~(-1)); // 0
// Formula: ~n == -(n + 1)
// Practical uses:
// 1. Check indexOf results — indexOf returns -1 if not found:
String text = "Hello World";
if (~text.indexOf("World") != 0) { // ~(-1) == 0, so != 0 means found
System.out.println("Found");
}
// More readable alternative:
if (text.indexOf("World") != -1) {
System.out.println("Found");
}
// Or best:
if (text.contains("World")) {
System.out.println("Found");
}
// 2. Creating inverse bitmasks:
int mask = 0x0F; // 0000 1111
int inverse = ~mask; // 1111 0000
// 3. Clearing bits with ~:
int flags = 0xFF;
int WRITE_FLAG = 0b00000010;
flags &= ~WRITE_FLAG; // clear the write bitRelated Topics in Java Basics
Variables in Java
A variable is just a named box in memory that holds a value. Java is strict about what goes in each box — you tell it the type upfront. Once you get this, the rest of Java clicks into place.
Data Types in Java
Java needs to know exactly what kind of data it's dealing with before it can store or process it. Integers, decimals, characters, true/false — each has its own type. Knowing which to use (and why) makes your programs efficient and bug-free.
Primitive Data Types
Java has eight primitive data types — the most basic building blocks for storing data. Unlike objects, primitives are stored directly in memory, making them fast and efficient. Understanding each type, its size, range, and when to use it is fundamental to writing correct Java programs.
Non-Primitive Data Types
Non-primitive data types — also called reference types — are everything beyond Java's eight primitives. Strings, arrays, classes, interfaces, enums, and records all fall into this category. They're more powerful than primitives, but they work differently in memory, comparison, and nullability. Understanding the distinction is essential for writing correct Java.