本文目录导读:

Java案例深度解析:如何精准判断字符串为空?避开99%开发者的常见陷阱
目录导读
- 引言:空字符串的“死亡陷阱”
真实案例:一次空指针导致的线上事故
- Java中“空”的三种形态解析
null、空字符串""、空白字符串的区别
- 四大主流判断方法实测对比
- str == null
- str.isEmpty()
- str.equals("")
- Apache Commons Lang3 / Spring StringUtils
- 企业级最佳实践:三段式判断法
代码模板与注释
- 高频问答:面试/实战终极解答
- Q1:str.length() == 0 比 isEmpty() 快吗?
- Q2:如何判断空白字符串如" "?
- Q3:Optional 能否替代传统判断?
- 写出无人能坑的健壮代码
引言:空字符串的“死亡陷阱”
2019年某金融系统上线当夜崩溃,原因竟是用户输入了一个不可见字符(Unicode \u200B零宽空格),Java开发中,“字符串为空”这一简单需求,实际包含3种截然不同的语义:null(未初始化)、(长度为0)、空白字符串(全空格或不可见字符)。
一个典型死亡案例:
if (str != null && !str.equals("")) {
// 业务处理
}
看似正确,却忽略了 " ".trim().isEmpty() 这类场景,最终导致数据库写入异常。
本文将用真实案例 + 多维度对比,帮你构建牢不可破的字符串判空体系。
Java中“空”的三种形态解析
null:未初始化的对象
String str = null; // 无内存地址 str.length(); // 立即报NullPointerException
典型场景:数据库查询无结果、未赋值的变量。
"":长度为0的空字符串
String str = ""; System.out.println(str.length()); // 0 System.out.println(str.isEmpty()); // true
注意:isEmpty() 在JDK 6+ 才可用,低版本需用 length()==0。
空白字符串:看起来“空”的陷阱
String str = " \t\n"; // 空格、制表符、换行 str.isEmpty(); // false(长度为3)
最易忽视:JSON反序列化时,很多库会把 当作有效值保留。
四大主流判断方法实测对比
str == null
- 作用:仅判断是否为null
- 缺陷:无法处理 和空白字符串
- 适用场景:仅需过滤空指针时
str.isEmpty()
- 底层:
return value.length == 0; - 坑点:必须确保对象非null,否则抛NPE
- 速度:最快(仅比较int length)
str.equals("")
- 缺陷:若str为null,立即NPE
- 替代:
"".equals(str)可避免NPE,但不直观
工具库判空
Apache Commons Lang3:
StringUtils.isBlank(str); // 同时判断null、""、空白 StringUtils.isNotBlank(str); // 取反
Spring Framework:
StringUtils.hasText(str); // 需引入spring-core
性能对比:工具库内部会做trim(),比纯判断多2-5微秒,但安全性提升100倍。
企业级最佳实践:三段式判断法
在金融、医疗等对稳定性要求极高的场景,推荐使用以下“三层防御”模板:
public static boolean isReallyEmpty(String str) {
// 第一层:过滤null
if (str == null) {
return true;
}
// 第二层:过滤空字符串
if (str.isEmpty()) {
return true;
}
// 第三层:过滤空白/不可见字符(基于Unicode)
for (int i = 0; i < str.length(); i++) {
if (!Character.isWhitespace(str.charAt(i))) {
return false; // 发现非空白字符即为非空
}
}
return true;
}
为什么不用 str.trim().isEmpty()?
因为 trim() 只能去除 ASCII 空格(U+0020),无法处理零宽空格(U+200B)、全角空格(U+3000)等。
终极方案:使用 StringUtils.isBlank(str) 来自 Commons Lang3,它内部使用 Character.isWhitespace() 兼容所有Unicode空白。
高频问答:面试/实战终极解答
Q1:str.length() == 0 比 isEmpty() 快吗?
A:完全相同!查看JDK源码可知:
public boolean isEmpty() {
return value.length == 0;
}
只是语法糖,性能无差异,建议统一用 isEmpty() 增强可读性。
Q2:如何判断空白字符串如" "?
A:
StringUtils.isBlank(str); // 推荐 str.trim().isEmpty(); // 局限:只处理空格、tab、换行
进阶方案:用正则 str.matches("\\s*"),但性能较差(需编译Pattern)。
Q3:Optional 能否替代传统判断?
A:不能完全替代,Optional 主要用于链式调用和函数式编程,但判空仍需结合 filter() 与 isPresent():
Optional.ofNullable(str)
.filter(s -> !s.trim().isEmpty())
.ifPresent(s -> dosomething(s));
适合流式代码,但性能略低于普通if。
Q4:MyBatis/MyBatis Plus 中如何判空?
A:在XML中使用 <if test="str != null and str != ''">,但注意 和 的区别,最佳实践:
<if test="str != null and str.trim() != ''">
AND column = #{str}
</if>
或在实体类用 StringUtils.isNotBlank(str) 先处理。
写出无人能坑的健壮代码
- 永远不要只写
str != null:它忽略空白字符; - 永远不要在判空前调用
str.isEmpty():必须确保非null; - 如果项目允许,无脑用
StringUtils.isBlank():一次解决所有问题; - 如果必须手写,用
Character.isWhitespace()逐字符判断:兼容所有Unicode空白; - 线上代码必须预判不可见字符:推荐在生产环境进行空白字符正则校验。
最后记住一个原则:用户输入不可信,API返回不可信,数据库字段不可信,只有你的判空逻辑,才是最终防线。