1Z0-771 study materials
1Z0-771 study resources

1Z0-771 common mistakes

1Z0-771 · Understand the traps and how to avoid them.

Mistake 1

Declaring a local variable inside a lambda body and then trying to reuse it, or mutating a captured local variable, and expecting it to compile.

Candidates learn that lambdas can 'capture' variables from the enclosing method and assume capture works like fields. In fact captured locals must be effectively final; any reassignment, including inside an if or loop, removes effectively-final status and produces a compile error on the lambda, not on the assignment.

What to do instead

Before writing any lambda that reads an enclosing local, confirm the variable is never reassigned after initialization. If you need an accumulator, use an array element, an AtomicInteger, or a mutable holder object, or restructure to use a stream reduction/collector. In exam questions, scan the method for hidden reassignments (loop counters, i++ in a for-loop header) - those are the intended traps.

Mistake 2

Assuming a switch expression with arrow labels falls through, or forgetting that a switch statement over an enum/string needs exhaustive handling while a switch expression must be exhaustive.

Candidates carry over old colon-label fallthrough intuition and misread arrow-label semantics, and they confuse the exhaustiveness requirement that applies to switch expressions and pattern switches with the looser rules for statement switches.

What to do instead

Memorize: arrow labels never fall through and allow no break; a switch expression must cover all values (for enums, all constants, or a default); a switch statement is not required to be exhaustive. When you see a switch used as an initializer or return value, immediately check for a default or full enum coverage.

Mistake 3

Mixing up compile-time and runtime outcomes for checked exceptions - e.g. thinking catching a superclass of a thrown checked exception is fine, or that catching an exception that cannot be thrown is always an error.

The rules are asymmetric: catching a checked exception type that the try block cannot throw is a compile error (unless the catch parameter is Exception/Throwable), but catching an unchecked type is always allowed; conversely, a method may declare throws for checked exceptions it never throws. Candidates apply one rule uniformly.

What to do instead

For each catch clause ask: is the caught type checked? If checked and not throwable from the try body (and no supertype catch precedes it), it will not compile. Remember RuntimeException and Error subtypes are exempt from the 'cannot be thrown' check, and that a more specific catch after a broader catch is a compile error for any type.

Mistake 4

Predicting the wrong overload when a lambda or method reference is passed, or expecting a lambda to be assignable to Object, and misjudging which functional interface a target type resolves to.

Lambda expressions are poly expressions: their type comes from the target type, and overload resolution uses applicability by arity and shape before the body is considered. Candidates instead pick the overload whose body 'looks right', or assume a lambda can be stored in a var/Object-typed variable.

What to do instead

First count parameters (and check return compatibility) to eliminate inapplicable overloads; if two remain applicable, the compiler reports ambiguity - that is the answer. Remember a lambda cannot be assigned to Object or var without a cast to a functional interface, and method references like String::valueOf can match many shapes, so resolve the target type first.

Mistake 5

Assuming ArrayList or HashMap iteration order, or that HashMap preserves insertion order, and predicting wrong output from printing a collection.

Candidates memorize 'HashMap is unordered' but still expect a specific printed order in exam snippets, and they conflate LinkedHashMap (insertion order) with TreeMap (sorted) and HashSet with LinkedHashSet.

What to do instead

Classify every collection by contract: ArrayList and HashMap give no guaranteed iteration order; LinkedHashMap/LinkedHashSet give insertion order (access order if constructed with accessOrder=true); TreeMap/TreeSet give sorted order and require Comparable or a Comparator. If an exam question prints a HashMap, the correct answer is that the order is unspecified - do not pick a specific sequence.

Mistake 6

Treating an interface's static and private methods as inherited or overridable, or expecting a default method to be callable via an implementing class name.

Candidates generalize 'interfaces can have default methods' to 'all interface methods are inherited'. Static interface methods are not inherited by implementers or subinterfaces and must be called on the interface name; private interface methods exist only to support other methods in the same interface.

What to do instead

For any interface method call, check the receiver: InterfaceName.staticMethod() is legal; implementingClass.staticMethod() or an instance reference to a static interface method is not. Also remember default methods can be overridden and can be invoked with InterfaceName.super.method() only from a class that directly implements that interface.

Mistake 7

Expecting records to be mutable, to have a no-arg canonical constructor, or to allow instance field declarations beyond the components.

Records are often studied as 'short classes', so candidates assume setter generation, JavaBean-style getX() accessors, or extra instance fields. In reality records are implicitly final, generate accessors named exactly like the components, have a canonical constructor taking all components, and forbid additional instance fields.

What to do instead

Recall the record contract: final class, private final fields, accessors named componentName(), equals/hashCode/toString generated, no extra instance fields, and a compact canonical constructor for validation. If a snippet declares an extra instance field or a setter, it will not compile; a no-arg constructor only exists if you write one that delegates to the canonical constructor.

Mistake 8

Believing that calling start() twice on a Thread, or calling run() directly, produces a second concurrent execution.

Candidates conflate starting a thread with invoking its task. Calling start() twice throws IllegalThreadStateException; calling run() directly executes on the current thread with no new thread of execution. Similar confusion arises with ExecutorService.submit versus execute and with shutdown semantics.

What to do instead

Memorize: start() may be called once per Thread object and schedules a new thread; run() is an ordinary method call. For executors, execute(Runnable) returns void, submit returns a Future, shutdown() stops accepting tasks but lets queued tasks finish, and shutdownNow() interrupts and drains the queue.

Mistake 9

Using Files.readAllLines or Files.readString without handling IOException, or assuming Paths.get creates a file, or misreading relative-path normalization in NIO.2.

Candidates treat NIO.2 as a convenience API and forget that most Files methods throw checked IOException, that Path is only a locator (no I/O happens until a Files method is called), and that normalize()/toAbsolutePath()/relativize() behave differently from what intuition suggests.

What to do instead

For every Path question, ask 'is this creating or locating?'. Path.of/Paths.get only parse a path; Files.createFile/createDirectory/write perform I/O and throw IOException. Practice normalize (removes . and .. lexically without touching disk), toAbsolutePath (uses working directory), and relativize (requires same root, otherwise IllegalArgumentException).

Mistake 10

Assuming a module with no requires can access java.base, or that reflection on a non-exported package works without opens, or forgetting that automatic modules read all other modules.

Candidates memorize 'modules must require what they use' but forget java.base is implicitly required by every module, and they conflate exports (compile-time and normal runtime access) with opens (deep reflection). Automatic modules are a special case: they read every other module and export all packages.

What to do instead

Check three things per module declaration: java.base is implicit, so no requires java.base is needed; reflection via setAccessible requires the package to be open (opens directive or open module), not merely exported; and a JAR on the module path without module-info becomes an automatic module that requires no explicit reads. Named modules cannot read the unnamed module.