Common Java Pitfalls: Equality, Objects.equals, BigDecimal, Streams, Autoboxing, and String Replacement
This article explains six frequent Java pitfalls—including misuse of the == operator with Integer, subtle bugs in Objects.equals, precision loss with BigDecimal constructors, reference sharing in Stream.filter, NullPointerExceptions from autounboxing, and differences between replace, replaceAll, and replaceFirst—providing code examples and best‑practice recommendations.
As a Java developer you may have encountered mysterious bugs caused by seemingly simple code; this article collects six common pitfalls and shows how to avoid them.
1. Using == with Integer
Comparing two Integer objects with == checks reference equality, not value equality, so the result is false even when both objects contain the same value.
Integer orderStatus1 = new Integer(1);
Integer orderStatus2 = new Integer(1);
System.out.println(orderStatus1 == orderStatus2); // falseThe Integer cache (‑128 to 127) is used only by valueOf , not by the constructor. Use equals or Integer.valueOf for reliable comparison.
2. Objects.equals Pitfall
When a user ID is stored as Integer but compared with a Long literal (e.g., 888L ), Objects.equals returns false because the types differ.
if (Objects.equals(userInfo.getId(), 888L)) {
sendEmail(userInfo);
}The underlying Integer.equals method only returns true when the argument is also an Integer . Ensure both sides have the same type or convert explicitly.
3. BigDecimal Precision Issues
Creating a BigDecimal with the double constructor can introduce binary‑floating‑point errors:
BigDecimal amount1 = new BigDecimal(0.02);
BigDecimal amount2 = new BigDecimal(0.03);
System.out.println(amount2.subtract(amount1)); // 0.009999999999999998...Use BigDecimal.valueOf or the String constructor to avoid loss of precision:
BigDecimal amount1 = BigDecimal.valueOf(0.02);
BigDecimal amount2 = BigDecimal.valueOf(0.03);
System.out.println(amount2.subtract(amount1)); // 0.014. Java 8 Stream filter Pitfall
Filtering a list with stream().filter(...) returns a view that holds references to the original objects. Modifying an element in the filtered list also changes the element in the original list because both lists share the same object instances.
List
filtered = users.stream()
.filter(u -> u.getId() > 1000 && u.getAge() > 18)
.collect(Collectors.toList());
for (User u : filtered) {
u.setName(u.getName() + "Test");
}
// The names in the original 'users' list are also changed.5. Autoboxing/Unboxing Pitfalls
Automatic unboxing of a null Integer to a primitive int throws a NullPointerException :
Integer a = null;
int sum = a + 5; // NPESimilarly, passing a null Integer to a method that expects primitive int parameters causes the same error because the JVM invokes intValue() on a null reference.
6. String.replace vs replaceAll
String.replace has two overloads: one for char arguments and one for CharSequence . It replaces every occurrence without interpreting the arguments as regular expressions.
String s = "A*B";
System.out.println(s.replace('A', 'C')); // C*B
System.out.println(s.replace("A", "C")); // C*BreplaceAll treats the first argument as a regex, so special characters must be escaped:
String s = "A*B";
System.out.println(s.replaceAll("\\*", "C")); // ACBTo replace only the first match, use replaceFirst with a regex.
These seemingly simple constructs can easily become sources of bugs if their nuances are not understood.
IT Services Circle
Delivering cutting-edge internet insights and practical learning resources. We're a passionate and principled IT media platform.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.