Skip to content

Common Java Pitfalls Cookbook

trampas comunes en Java

Common Java Pitfalls Cookbook

This document compiles common errors and subtle behaviors in Java that can lead to hard-to-detect bugs. Each entry follows the outline problem → solution → discussion so you can identify, understand, and avoid these errors in your code.


1. Integer comparison outside the cached range

Problem: The operator == returns false for integers greater than 127 (or less than -128) even if the numerical values are equal.

Solution: Use .equals() to compare values of Integer. For performance or simplicity, use int if you don't need nulls.

Discussion: Java caches the Integer in the range [-128, 127]. Outside that range, Integer.valueOf() returns distinct instances; == compare references, not content.


2. Comparing String with ==

Problem: == between chains sometimes it gives true and others false for the same text, which confuses beginners.

Solution: Compare strings with .equals() either .equalsIgnoreCase().

Discussion: He string pool You can make two literals point to the same reference, but objects created with new String() No. == compare references; .equals() compare content.


3. Modify a collection during iteration

Problem: Remove or add items to a collection while browsing with for-each lance ConcurrentModificationException.

Solution: Use a Iterator and his remove(), or collect first and modify later; alternatively, use concurrent collections.

Discussion: Many collections detect structural changes during iteration. Concurrent collections (e.g., CopyOnWriteArrayList) have different semantics and additional costs.


4. NullPointerException when unboxing

Problem: Convert a Integer null to int produces NullPointerException.

Solution: Valid null before unboxing or use OptionalInt / default values.

Discussion: Unboxing internally invokes intValue() about the reference; if it is null, fails. Avoid this wherever possible by using primitive types.


5. Arrays do not deep copy

Problem: Assigning an array to another variable copies the reference; changes in one view affect the other.

Solution: Use Arrays.copyOf() either clone() to copy the array; for arrays/objects, implements an explicit deep copy.

Discussion: Copying by reference is fast but surprising for those who expect data independence. For nested structures, it copies each level.


6. Lack of break in switch (fall-through)

Problem: Forget break runs additional cases unexpectedly.

Solution: Duck break or use switch with expressions (Java 14+) and ->, which does not do fall-through default.

Discussion: He fall-through It is useful in grouped cases, but it is often a source of bugs. Expressions switch modern technologies reduce this risk.


7. Virtual calls in constructors (incomplete initialization)

Problem: Calling overridden methods from a constructor may use uninitialized fields of the subclass.

Solution: Do not invoke overridden methods in constructors; use methods end or explicit initialization.

Discussion: The superclass constructor executes before initializing the subclass fields. The virtual call accesses an incomplete state.


8. Arrays of generic types

Problem: Arrays of specific generic types cannot be created, e.g., new ArrayList [10].

Solution: Use List [] or preferably collections, avoiding generic arrays.

Discussion: Arrays are covariant and reified; generics use type erasure. Mixing breaks type safety at runtime.


9. Mutable keys in HashMap/HashSet

Problem: Changing a key after entering it prevents it from being found again.

Solution: Use immutable keys (e.g., record or immutable classes) or do not modify the keys after using them.

Discussion: hashCode() and equals() determine the location. If they change, the map loses its reference to the entry.


10. Optional.get() without checking presence

Problem: Call to get() about a Optional empty spear NoSuchElementException.

Solution: Use orElse(), orElseGet(), orElseThrow() either ifPresent().

Discussion: Optional aims to avoid null; wear get() breaks its intent. Prefers expressive APIs.


11. Variable shadowing (field hiding)

Problem: The method parameter hides the instance field and assignments like name = name; They do nothing useful.

Solution: Use this.name = name; or change the parameter name.

Discussion: Shadowing hampers maintenance and causes silent bugs. Enabling IDE inspections helps detect it.


12. Attendance: lack of volatile (visibility)

Problem: A thread does not observe changes by another thread to a variable without synchronization.

Solution: Declare the variable as volatile or synchronize accesses (synchronized, Lock).

Discussion: The Java memory model allows for per-thread reordering and caching. volatile guarantees visibility and order of publication/reading.


13. Concurrency: Non-atomic increments

Problem: Operations such as value++ lose updates under concurrency.

Solution: Use AtomicInteger.incrementAndGet(), blocks synchronized or accumulators (LongAdder).

Discussion: value++ implies read-modify-write. Without mutual exclusion, two threads can overwrite results.


14. Calendar: zero-based month

Problem: Calendar#set(year, month, day) interprets January as 0; 10 It's November, not October.

Solution: Use constants (Calendar.OCTOBER) or migrates to java.time.

Discussion: The old date API is prone to errors. java.time (JSR-310) is clearer and more immutable.


15. LocalDate.parse() with non-ISO formats

Problem: LocalDate.parse("05/10/2025") lance DateTimeParseException.

Solution: Define a formatter: DateTimeFormatter.ofPattern("dd/MM/yyyy") and pass it to parse().

Discussion: Default, parse() wait yyyy-MM-ddFor other formats you must specify the pattern explicitly.

en_USEnglish