Fundamentals 13 min read

7 Common Java String Pitfalls and How to Avoid Them

This article walks through frequent Java string‑related mistakes—including misuse of replace vs replaceAll, Integer equality pitfalls, BigDecimal precision issues, StringBuilder versus String, isEmpty vs isBlank, MyBatis mapper null checks, and indexOf logic—providing clear explanations, code examples, and best‑practice recommendations.

Su San Talks Tech
Su San Talks Tech
Su San Talks Tech
7 Common Java String Pitfalls and How to Avoid Them

Preface

During a recent Sonar static analysis combined with manual code review, many code issues were discovered beyond typical bugs and security flaws, especially several confusing method usages that sparked my interest.

1. Does replace replace all characters?

When we want to replace characters like A with B in a string such as ATYSDFA*Y, the first thought is to use replace. To replace every A, replaceAll is more intuitive because its name suggests a global replacement.

The JDK documentation confirms that replace replaces each matching character, but it behaves differently from replaceAll.

replace documentation
replace documentation

Key differences: replace has two overloads: one takes char oldChar, char newChar for character replacement, and the other takes CharSequence target, CharSequence replacement for string replacement.

source.replace('A', 'B')
source.replace("A", "B")
replaceAll

takes a regular‑expression pattern and a replacement string, e.g. source.replaceAll("A", "B") For special characters like *, you can use: source.replaceAll("\\*", "C") or simply source.replace("*", "C") but avoid double‑escaping, which prevents replacement.

To replace only the first occurrence, use replaceFirst:

source.replaceFirst("A", "B")

2. Can Integer be compared with == ?

Comparing two new Integer(1) objects with == yields false because they are distinct objects. The Integer constructor does not use the internal cache (‑128 to 127). The cache is used by Integer.valueOf and valueOf on String values, which can return true for the same value.

Therefore, prefer equals for value comparison:

Integer a = new Integer(1);
Integer b = new Integer(1);
System.out.println(a.equals(b)); // true

3. Does BigDecimal avoid precision loss?

Creating BigDecimal with the double constructor can still lose precision, e.g. new BigDecimal(0.02) yields a long decimal representation. The JDK documentation warns that this constructor may produce unexpected results.

To preserve exact values, use BigDecimal.valueOf or the Double.toString approach:

BigDecimal a = BigDecimal.valueOf(0.02);
BigDecimal b = BigDecimal.valueOf(0.03);
System.out.println(b.subtract(a)); // 0.01

4. Should string concatenation use String ?

Because String is immutable, repeated concatenation creates many intermediate objects. StringBuilder (or StringBuffer) should be used for efficient mutable concatenation.

StringBuilder sb = new StringBuilder();
sb.append(a).append(b);
System.out.println(sb);
StringBuilder

is not synchronized, making it faster than StringBuffer in single‑threaded scenarios. Since Java 5, the compiler optimizes the + operator on String to use StringBuilder under the hood.

5. Difference between isEmpty and isBlank

Various utility libraries provide these methods. isEmpty returns true only for null or "", while isBlank also treats strings containing only whitespace as empty.

StringUtils.isEmpty(null)      // true
StringUtils.isEmpty("")        // true
StringUtils.isEmpty(" ")       // false

StringUtils.isBlank(null)      // true
StringUtils.isBlank("")        // true
StringUtils.isBlank(" ")       // true

6. Do MyBatis mapper results need null checks?

MyBatis’s DefaultResultSetHandler.handleResultSets always creates a non‑null List (named multipleResults), so the returned collection is never null. Therefore, explicit null checks before streaming are unnecessary.

List<User> list = userMapper.query(search);
List<Long> ids = list.stream().map(User::getId).collect(Collectors.toList());

7. Correct usage of indexOf

indexOf

returns the position of the substring starting at 0, or -1 if not found. Checking > 0 misses matches at the beginning of the string.

String source = "#ATYSDFA*Y";
if (source.indexOf("#") > -1) {
    System.out.println("do something");
}

Alternatively, use contains for readability.

if (source.contains("#")) {
    System.out.println("do something");
}
Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Code reviewMyBatisStringBigDecimalstringbuilderreplaceisEmpty
Su San Talks Tech
Written by

Su San Talks Tech

Su San, former staff at several leading tech companies, is a top creator on Juejin and a premium creator on CSDN, and runs the free coding practice site www.susan.net.cn.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.