必须用equals()比较字符串内容,==仅比较引用;Objects.equals()更安全,equalsIgnoreCase()替代toLowerCase().equals()避免性能与locale问题。

用 equals() 比较字符串内容,别用 ==
Java 里判断两个字符串“值一样”,必须用 equals();== 只比较引用是否指向同一块内存地址。新手常写 str1 == str2,结果在拼接、new String() 或从不同来源(比如 JSON 解析、IO 读取)拿到的字符串上出错——表面看着一样,却返回 false。
常见错误现象:String a = "hello"; String b = new String("hello"); System.out.println(a == b); // false,但 a.equals(b) 是 true。
-
==在字符串字面量之间偶尔“碰巧”为true(因为字符串常量池),但这不是语义保证,不能依赖 -
equals()会先判空(null安全),但调用方是null时仍会抛NullPointerException,所以更稳妥写法是Objects.equals(str1, str2) - 如果确定非空且只比内容,用
str1.equals(str2);不确定是否为空,优先用Objects.equals(str1, str2)
equals() 的大小写敏感问题与替代方案
默认 equals() 区分大小写,比如 "Hello".equals("HELLO") 返回 false。业务中常需要忽略大小写比较,但别直接用 toLowerCase() 再 equals()——它会创建新字符串,影响性能,尤其在循环或高频场景下。
- 推荐用
equalsIgnoreCase():语义清晰、不额外分配对象、JVM 还可能内联优化 - 注意:它对 Unicode 大小写处理有局限,比如土耳其语的
'i'和'I'不满足常规映射,如需国际化支持,应改用Collator - 避免
str1.toLowerCase().equals(str2.toLowerCase()),既慢又可能因 locale 导致意外行为(例如在 Turkish locale 下"i".toUpperCase()不是"I")
字符串比较中的空指针风险与防御写法
调用 null 字符串的 equals() 会立即抛 NullPointerException。很多人把判空逻辑写成 if (str != null && str.equals("abc")),虽可行但啰嗦易漏。
立即学习“Java免费学习笔记(深入)”;
- 标准解法是
Objects.equals(str1, str2):内部先判两边是否都为null,再调用equals(),安全且一行搞定 - 如果已知一个值恒非空(如常量
"OK"),可倒过来写:"OK".equals(input)——这样即使input是null也不会崩 - 别用
StringUtils.equals()(Apache Commons)除非项目已强依赖它;标准库的Objects足够轻量且无额外依赖
性能与编译器优化的现实影响
有人担心 equals() 比 == 慢很多,其实现代 JVM 对短字符串的 equals() 做了大量优化:先比长度、再比哈希码(如果已计算)、最后才逐字符比。实际差距微乎其微,远不如一次多余对象创建或正则匹配开销大。
- 真正该警惕的是反复调用
String.intern()试图让==可用——这会污染字符串常量池,GC 压力大,且 Java 7+ 后常量池移到堆内存,intern()行为更难预测 - 字符串拼接后比较(如
prefix + suffix)几乎不可能命中常量池,==必然失败,此时equals()是唯一合理选择 - 如果真在 hot path 上做百万级字符串比较,优先考虑预计算哈希或用
CharSequence接口抽象,而不是纠结==vsequals()
最常被忽略的一点:字符串比较逻辑一旦嵌套在条件分支、日志判断或配置校验里,就很容易被当成“简单操作”跳过单元测试——而 null、大小写、编码差异这些边界,往往只在特定环境才暴露。









