在日常Java开发中,NullPointerException(NPE)是我们最常遇到的运行时异常之一。特别是字符串操作时,一不小心就会掉进NPE的陷阱。今天就来彻底剖析这些隐藏的坑点,让你的代码更加健壮!

🔥 最经典的陷阱:a.equals(b)

String a = null;
String b = "hello";

// 这是NPE的经典场景!
a.equals(b); // 抛出 NullPointerException

真相揭秘

  • a 可以为null,但调用方法时会抛出NPE

  • b 可以为null,通常返回 false(前提是a不为null)

安全写法

// 方法1:让常量在前(推荐)
"hello".equals(a); // 安全,a为null时返回false

// 方法2:使用Objects.equals(最安全)
Objects.equals(a, b); // 两者都为null时返回true

// 方法3:显式null检查
if (a != null) {
    a.equals(b);
}

💥 更大的陷阱:a.contains(b)

你以为equals已经很坑了?contains更是坑中之王!

String a = null;
String b = "hello";

a.contains(b); // NPE!

String c = "hello";
String d = null;

c.contains(d); // 还是NPE!

残酷真相

  • a 不能为null → NPE

  • b 不能为null → NPE

  • 两个参数都不能为null!

安全方案

// 自定义安全方法
public static boolean safeContains(String str, String searchStr) {
    return str != null && searchStr != null && str.contains(searchStr);
}

// 使用工具类
StringUtils.contains(a, b); // Apache Commons Lang

🚨 字符串操作中的NPE地雷阵

下面这些方法,只要调用者为null,统统NPE!

比较相关

String str = null;
str.compareTo("abc");       // 💥 NPE
str.compareToIgnoreCase("abc"); // 💥 NPE
str.contentEquals("abc");   // 💥 NPE

查找相关

str.indexOf("a");           // 💥 NPE
str.lastIndexOf("a");       // 💥 NPE  
str.startsWith("a");        // 💥 NPE
str.endsWith("a");          // 💥 NPE

操作相关

str.substring(1);           // 💥 NPE
str.charAt(0);              // 💥 NPE
str.length();               // 💥 NPE
str.trim();                 // 💥 NPE
str.toUpperCase();          // 💥 NPE

正则相关

str.matches("regex");       // 💥 NPE
str.replaceAll("a", "b");   // 💥 NPE
str.split(",");             // 💥 NPE

空检查(讽刺吧?)

str.isEmpty();              // 💥 NPE
str.isBlank();              // 💥 NPE

🛡️ 全方位防御方案

方案1:Objects工具类(JDK原生)

import java.util.Objects;

Objects.equals(a, b);              // 比较安全
Objects.toString(str, "default");  // 转字符串安全

方案2:Apache Commons Lang

import org.apache.commons.lang3.StringUtils;

StringUtils.equals(a, b);          // 比较安全
StringUtils.contains(a, b);        // 包含检查安全
StringUtils.isEmpty(a);            // 空检查安全
StringUtils.isBlank(a);            // 空白检查安全

方案3:防御性编程模式

// 三元运算符
String safeStr = (str != null) ? str : "";
boolean result = safeStr.contains("abc");

// Optional优雅处理
Optional.ofNullable(str)
    .map(s -> s.contains("abc"))
    .orElse(false);

// 提前返回
if (str == null) {
    return false; // 或抛出业务异常
}
return str.contains("abc");

方案4:静态代码分析工具

  • SonarQube:检测潜在的NPE风险

  • SpotBugs:静态分析找出NPE问题

  • IDEA Inspections:IDE自动提示

📊 最佳实践总结

场景危险写法安全写法
字符串比较a.equals(b)Objects.equals(a, b)
包含检查a.contains(b)StringUtils.contains(a, b)
空检查a.isEmpty()StringUtils.isEmpty(a)
方法调用a.length()Optional.ofNullable(a).map(String::length)

💡 终极建议

  1. 养成习惯:总是考虑参数为null的情况

  2. 使用工具:优先选择null安全的工具类

  3. 代码审查:将NPE风险纳入代码审查清单

  4. 单元测试:覆盖null边界情况的测试用例

记住:在Java世界里,唯一不会抛出NPE的字符串方法,就是你没有在null上调用的那个方法!


思考题:你的项目中还有哪些隐藏的NPE陷阱?欢迎在评论区分享交流!