Common Gotchas
This chapter is a quick-reference checklist, not new material — each item below is taught in full elsewhere in this curriculum. Its job is to put the most common real mistakes side by side, since they tend to resurface long after the lesson that first covered them. Note: this list is expected to grow once real code-review and exercise practice surfaces more of the mistakes people actually make — it currently only covers what the original deck flagged.
== vs. .equals()
== compares primitives (int, double, boolean) by value, which is correct and sufficient for them. For objects — including String — == instead checks whether two variables point at the exact same object in memory, which is almost never what you actually want to ask:
String a = new String("BlueAlliance-Left");
String b = new String("BlueAlliance-Left");
System.out.println(a == b); // false — two different objects, same text
System.out.println(a.equals(b)); // true — compares the actual characters
Use .equals() — or a purpose-built comparison method like .equalsIgnoreCase(), .compareTo(), or .contains() — for any object type, including boxed numbers like Integer and Double. See Lesson 6.1 for the full explanation.
A few == special cases worth knowing by heart:
double ==. Even without boxing,0.1 + 0.2 == 0.3isfalse— floating-point math rarely lands on an exact value. Compare with a tolerance instead (Math.abs(a - b) < 0.0001), as covered in Lesson 2.4.Integer ==. Java caches small boxedIntegervalues (-128to127), soInteger x = 100, y = 100; x == yhappens to betrue— butInteger p = 1000, q = 1000; p == qisfalse. Never rely on this; always use.equals()for boxed numbers.- Enums are the exception.
==is safe (and preferred) for comparing enum constants (Lesson 11) — there's only ever one object per constant, so==and.equals()always agree.
Integer Division
Dividing two ints produces an int — the fractional part is silently discarded (truncated), not rounded:
int result = 5 / 2; // 2, not 2.5 — the .5 is thrown away, not rounded
double result2 = 5.0 / 2.0; // 2.5 — at least one operand must be a floating-point type
This bites often when averaging (total / count where both are int) — cast at least one operand to double first. See Lesson 2.4 for casting rules in full.
NullPointerException
Calling a method or accessing a field on a variable that's null (declared but never assigned a real object) throws NullPointerException — one of the most common runtime crashes in any Java program, robot code included:
Drivetrain drivetrain = null; // explicitly assigned null — no real object yet
drivetrain.stop(); // NullPointerException — nothing to call stop() on
(A local variable left truly unassigned — just Drivetrain drivetrain; with no = null — is a different, compile-time error: Java refuses to compile code that reads it before it's given a value, reporting "variable drivetrain might not have been initialized." Only a field defaults to null automatically.)
The fix is always the same shape: make sure every object is actually constructed (new Drivetrain(...)) before anything calls a method on it, and check for null explicitly at any point where a value might legitimately be missing (e.g., a HashMap.get() that found no match — see Lesson 9.3).
Off-by-One
Arrays (and ArrayList) are indexed starting at 0 — the last valid index is always length - 1 (or size() - 1), never length itself:
int[] canBusIds = new int[10]; // valid indices: 0 through 9
// ArrayIndexOutOfBoundsException — 10 is one past the end
System.out.println(canBusIds[10]);
This is the single most common loop-bound mistake — writing i <= array.length instead of i < array.length. See Lesson 9.1 (arrays) and 9.2 (ArrayList) for the full indexing rules.
Ignoring a Return Value
Methods like String.toUpperCase(), .trim(), and .replace() don't modify the object they're called on — String is immutable — they return a new value. Calling one and throwing away the result does nothing:
String teamName = "shockwave";
teamName.toUpperCase(); // return value discarded — teamName is unchanged
teamName = teamName.toUpperCase(); // correct — reassign the result
See Lesson 6.1 for the full list of String methods that work this way.
switch Fall-Through
A colon-style switch (Lesson 5.12) keeps running into the next case once one matches, unless a break stops it — forgetting one is a classic bug:
switch (day)
{
case 1:
System.out.println("Mon");
case 2: // day == 2 starts here...
System.out.println("Tue");
case 3: // ...and falls through to here too
System.out.println("Wed");
break;
default:
System.out.println("other");
}
// day == 2 prints both "Tue" and "Wed" — not just "Tue"
Arrow-style case -> (also Lesson 5.12) never falls through, which is why this course defaults to it.
Common Pitfalls
- Treating this chapter as the complete list of Java mistakes. It isn't — it's the handful the original deck happened to flag. Real practice will surface plenty more (this chapter is expected to grow).
- Fixing the symptom instead of the cause. E.g., wrapping a
NullPointerExceptionin atry/catch(Lesson 12) instead of fixing why the object was never constructed in the first place.
Key Takeaways
- Use
==for primitives,.equals()for objects (Lesson 6.1) — except enums, where==is safe. Watchdouble ==and theIntegercache. int / inttruncates; make at least one operand a floating-point type to keep the fractional part (Lesson 2.4).NullPointerExceptionmeans something was used before it was actually constructed — always initialize before use (Lessons 4.4–4.6, 9.1).- Valid indices run from
0tolength - 1(orsize() - 1) — neverlengthitself (Lessons 9.1, 9.2). - A discarded
Stringmethod return value does nothing; a colon-styleswitchwithoutbreakfalls through to the nextcase. - This is a living checklist, not a finished one — expect it to grow as more real gotchas surface through practice.
Derived from mechacoder-test: src/lessons/java-1.js, slide 13 ("Common Gotchas") — original deck content, no external citation