Java SE 17 (1Z0-829) study materials
Java SE 17 (1Z0-829) study resources

Java SE 17 (1Z0-829) common mistakes

Java SE 17 (1Z0-829) · Understand the traps and how to avoid them.

Mistake 1

Assuming that a LocalDate, LocalTime, or LocalDateTime created with a method like plusDays() or withDayOfMonth() is modified in place, and then using the original variable for later checks.

These classes are immutable, but their fluent API returns new instances, so it is easy to mentally treat them as mutable. The compiler does not warn because the original variable is still valid.

What to do instead

Always assign the result: date = date.plusDays(1);. If you intended to keep the original, store it separately before the call. When reviewing code, search for calls to date/time methods whose return value is ignored.

Mistake 2

Using == to compare two String objects, or using equals() on a StringBuilder, and expecting value equality.

String literals are interned but runtime concatenation and new String() create distinct objects, so == compares references. StringBuilder does not override equals(), so equals() also compares references.

What to do instead

Use equals() for String value comparison. For StringBuilder, compare the result of toString() with equals(), or use contentEquals() on a String. Remember that switch on String uses equals() semantics.

Mistake 3

Assuming a stream pipeline can be reused after a terminal operation, or forgetting that intermediate operations are lazy and do nothing until a terminal operation is invoked.

Streams are single-use by design; reusing one throws IllegalStateException. Also, without a terminal operation, no processing occurs, so side effects inside map() or filter() never run.

What to do instead

Create a new stream for each pipeline. Ensure every pipeline ends with a terminal operation such as collect(), forEach(), count(), or reduce(). To debug laziness, add peek() before the terminal operation.

Mistake 4

Mixing up the overloads of Collectors.toMap() and groupingBy(), causing duplicate-key exceptions or unexpected Map types.

toMap() without a merge function throws IllegalStateException on duplicate keys. groupingBy() returns a Map whose values are Lists by default, and the overload with a downstream collector is easy to miss.

What to do instead

Use the three-argument toMap(keyMapper, valueMapper, mergeFunction) when duplicates are possible. For groupingBy, supply a downstream collector such as counting() or mapping() when a non-List value is needed. Check the declared return type before assigning.

Mistake 5

Writing a switch statement or expression with fall-through cases and forgetting break or yield, or mixing colon and arrow syntax.

Traditional switch cases fall through without break, and in a switch expression every non-arrow case must yield a value. Arrow labels do not fall through, which changes behavior when converting between forms.

What to do instead

Use arrow labels for no fall-through. In a switch expression, ensure every branch either uses an arrow expression or a colon block that ends with yield. Add a default branch unless the switch is exhaustive over an enum.

Mistake 6

Assuming a loop variable declared in a for-each or classic for loop is in scope after the loop, or that a variable declared in an if block is visible outside it.

Java uses block scoping. The loop variable and variables declared inside the loop body or an if block are not visible after the block ends, which causes compile errors when referenced later.

What to do instead

Declare the variable before the loop or block if it is needed afterward. For for-each, copy the value into an outer variable inside the loop. Use an IDE or compiler error as a reminder to check scope.

Mistake 7

Catching a checked exception with a broader supertype such as Exception and then calling methods that throw other checked exceptions without declaring them.

The compiler analyzes the catch parameter type, not the actual thrown exception. Catching Exception makes the catch block responsible for all checked exceptions, which can trigger unreported exception errors.

What to do instead

Catch the most specific exception type possible. If a broad catch is required, handle or wrap checked exceptions inside the block. Use multi-catch (catch (IOException | SQLException e)) to keep types precise.

Mistake 8

Using var where the compiler cannot infer a type, such as with null, a lambda without a target type, or an array initializer without a type.

Local variable type inference requires an initializer that gives a single, denotable type. null has no type, lambdas need a functional interface target, and array initializers like {1,2} need an explicit array type.

What to do instead

Provide an explicit type in those cases: String s = null;, Runnable r = () -> {};, int[] a = {1,2};. Remember var is not allowed for fields, method parameters, or return types.

Mistake 9

Expecting a lambda or method reference to be assignable to any interface, or forgetting that a functional interface must have exactly one abstract method.

Lambdas require a target type that is a functional interface. Interfaces with multiple abstract methods, or classes, cannot be lambda targets. Default and static methods do not count toward the single abstract method.

What to do instead

Check the interface for a single abstract method before using a lambda. Use @FunctionalInterface to document intent. For method references, ensure the signature matches the abstract method exactly, including parameter and return types.

Mistake 10

Assuming that overriding equals() alone is enough for correct behavior in HashSet or HashMap, or that overriding equals() without hashCode() is safe.

Hash-based collections use hashCode() to locate buckets and equals() to confirm matches. If equals() is overridden but hashCode() is not, equal objects can land in different buckets and be treated as distinct.

What to do instead

Override both equals() and hashCode() consistently, or use a record which generates them. Follow the contract: equal objects must have equal hash codes. Test with a HashSet by adding two logically equal instances and checking size().