Before a program can choose between two paths, it has to ask a question that has exactly one of two answers. In Java that answer is a boolean — the value true or the value false, and nothing else. Unlike some languages, Java will not accept 0 or 1 or an empty String in place of a boolean, and that strictness is a gift: it means a condition that compiles is a condition that genuinely asks a yes-or-no question.
| Operator | Reads as | Example | Result |
|---|---|---|---|
== | is equal to | 5 == 5 | true |
!= | is not equal to | 5 != 5 | false |
< | is less than | 3 < 7 | true |
> | is greater than | 3 > 7 | false |
<= | is less than or equal to | 7 <= 7 | true |
>= | is greater than or equal to | 6 >= 7 | false |
Single comparisons are combined with the three logical operators. && is AND, and it is true only when both sides are true. || is OR, and it is true when at least one side is true — note that this is an inclusive or, so it is still true when both sides are true. ! is NOT, and it flips a boolean to its opposite. Their precedence runs ! first, then &&, then ||. Only && and || sit below the relational operators, which is why a < b && c > d needs no brackets at all. ! is different: it is a unary operator and binds tighter than the comparisons, so !age >= 13 does not mean what it looks like — Java reads it as (!age) >= 13, which will not even compile. Whenever you negate a comparison, bracket it: !(age >= 13).
int age = 16;
double mark = 83.5;
boolean teenager = age >= 13 && age <= 19; // true
boolean weekend = false;
boolean canGo = teenager && weekend; // false — both must hold
boolean interesting = mark >= 80 || weekend; // true — one side is enough
boolean notYet = !teenager; // false
System.out.println(canGo + " " + interesting); // prints: false trueShort-circuit evaluation
Java evaluates && and || lazily, from left to right, and stops the instant the answer is certain. With &&, if the left side is false the whole thing must be false, so the right side is never evaluated at all. With ||, if the left side is true the right side is skipped. This is called short-circuit evaluation, and it is not merely an optimization — it is how you write a guard that protects the test that follows it.
int total = 0;
int count = 0;
// Safe: count != 0 is false, so the division is never attempted.
if (count != 0 && total / count > 5) {
System.out.println("High average");
}
// Unsafe order: the division runs first and throws ArithmeticException.
// if (total / count > 5 && count != 0) { ... }De Morgan's laws
Sometimes the clearest way to express a condition is to negate a complicated one, and De Morgan's laws tell you how to push that negation inside. Negating an AND turns it into an OR of the negations, and negating an OR turns it into an AND of the negations. Both halves flip, and the connective flips too — forgetting the second half is the usual mistake.
!(age >= 13 && age <= 19)is the same condition asage < 13 || age > 19.!(isFull || isClosed)is the same condition as!isFull && !isClosed.- Notice that each comparison also flips: the opposite of
>=is<, not<=. - Use whichever form reads more naturally out loud — they run identically.
The two traps that catch everyone
int x = 5;
// if (x = 5) { } // compile error: int cannot be converted to boolean
boolean done = false;
if (done = true) { // COMPILES — and is a bug. It assigns, then tests true.
System.out.println("This always runs, and done is now true.");
}
String a = "cat";
String b = "cat";
String c = new String("cat");
System.out.println(a == b); // true — both point at the same pooled literal
System.out.println(a == c); // false — a different object in memory
System.out.println(a.equals(c)); // true — same characters, which is what we meant