字符串用==比较的是内存地址而非内容,只有字面量赋值、intern()调用或同一引用时才可能为true;equals()逐字符比较更安全但需防null,推荐Objects.equals()或字面量调用。

字符串用 == 比较时,到底在比什么
它只比内存地址,不是比内容。哪怕两个字符串字面值完全一样,只要不是同一个对象实例,== 就返回 false。比如:new String("abc") == new String("abc") 是 false,因为堆里新建了两个独立对象。
只有以下几种情况 == 才可能为 true:
- 字面量直接赋值:
String a = "abc"; String b = "abc"; a == b→true(共享字符串常量池) - 显式调用
intern():new String("abc").intern() == "abc"→true - 同一个引用变量赋值:
String a = "abc"; String b = a;→a == b一定为true
为什么 equals() 更安全,但仍有陷阱
equals() 是逐字符比较内容,语义上更符合“字符串相等”的直觉。但它不是万能的——最常见坑是 null 引用调用会抛 NullPointerException:str.equals("test") 在 str 为 null 时直接崩。
推荐写法(按优先级):
- 用
Objects.equals(str1, str2):自动判空,安全且简洁 - 用字面量调用:
"abc".equals(str),因为字面量不可能为null - 避免
str.equals("abc")这种写法,除非你 100% 确保str非空
contentEquals() 和 equalsIgnoreCase() 的适用场景
contentEquals() 不是 equals() 的替代品,而是扩展:它支持和任意 CharSequence(如 StringBuilder、StringBuffer)比较内容,不强制要求对方是 String 类型。例如:"abc".contentEquals(new StringBuilder("abc")) 返回 true。
equalsIgnoreCase() 用于忽略大小写的比较,但注意它只按 Unicode 码点做简单映射,不处理 locale 敏感规则(比如土耳其语的 i/I)。需要 locale 感知时,得用 String.regionMatches() 或 Collator。
包装类和字符串池混淆导致的误判
有人误以为 Integer.valueOf(127) == Integer.valueOf(127) 为 true,就类推到字符串——这是错的。整数缓存是 JVM 规范明确限定在 [-128, 127] 区间,而字符串常量池行为由 intern() 控制,且无固定范围。不要依赖“看起来一样就 == 得到 true”这种经验。
真正要判断字符串值是否相同,唯一可靠路径是走内容比较;== 只应在极少数明确需要判断“是不是同一对象”的场景下使用,比如锁对象、状态标识或性能敏感的内部逻辑。

















