“==”不等于“.equals”

本文探讨了Java中String对象的比较问题,解释了使用==与equals方法的区别,以及为何equals更适合String值的比较。
这里举出一个Java编程程序员经常碰到的问题。例如现在是凌晨3点,在你喝完第4杯咖啡后,你设法找到正确的逻辑来解决复杂的编程问题。到目前,你几乎不能思考String和Object引用,因为你已经昏昏欲睡了。然后糟糕的事情发生了……不,并不是Java溢出,而是如下所示。


String name = getName();
if (name == "Sleepy") // oops!
{
doSomething();
}

你快速编译并测试代码后,代码似乎正常运行。终于到下班回家休息的时候了!然而,一段时间后,应用程序测试发现了一个间歇性错误,并跟踪到此错误的来源恰好是这段代码。

“怎么会这样?”你可能会愤怒地说,“前几天我还试验过类似的String比较,并且能够正确运行!”。但是,你需要首先重温一下Java对象引用的概念。一个对象变量是一个指向存储在堆内存(heap memory)中实际对象的引用(指针)。当为另一个变量分配一个变量时,事实上分配的是引用而不是实际的对象(如图1-1所示):

String a, b, c, d;
a = "123";
b = a;
c = new String("123");
d = "WCJ";


图 1-1 对象引用
Java中,“==”运算符用来比较两个引用以查看它们是否指向同一个内存对象。而对于String实例,运行时状态会尽可能地确保任意两个具有相同字符信息的String字面值指向同一个内部对象。此过程称为驻留(interning),但是它并不有助于每个String的比较。一个原因是垃圾收集器线程删除了驻留值,另一个原因是String所在的位置可能被一个由String构造函数创建的新实例占用。如果是这样,“==”将总是返回false。
可以设计equals方法来比较两个对象的状态(state)或每个对象的内容。对你自己的类,必须重写此方法来使它正确操作。但是如果使用equals方法,String实例总是能够正确地比较。假定所有的String值是驻留的,下面的代码段说明了此问题:
String name1, name2, name3;
name1 = "123";
name2 = name1;
if (name1 == name2) {} // true
if (name1.equals(name2)) {} // true

name2 = "123";
if (name1 == name2) {} // usually true
if (name1.equals(name2)) {} // true

name3 = new String("123");
if (name1 == name3) {} // false
if (name1.equals(name3)) {} // true

[img]http://new.51cto.com/files/uploadimg/20071108/112525487.gif[/img]
注意:

总是使用.equals来比较两个String值,尽管使用“==”运算符看似能够正确操作。对于大多数应用程序而言,即使它能正确运行,但“==”代码事实上是错误的,而只有equals是正确的。因此告诉所有你的开发同行支持String的“equals(平等)”权吧(这很可能是本书中最差的双关语)!

补充:“==”是有时候可以 有时候不行,但是在比较类似“-1”之类的数字串时候,用“!=”就绝对比不出来了,不相信可以试试
是的,这段代码 **完全可以精简并提升可读性和维护性**。 你当前的写法存在以下问题: - ❌ 重复判断:`dpiMonthlyReportResult.getAntivirusAnalysis()` 和 `.getDefense()` 被反复调用多次 - ❌ 冗余使用 `Boolean.TRUE.equals(...)` —— 对于返回 `boolean` 的方法这是无意义的 - ✅ 目标明确:安全地从嵌套对象中提取三个开关状态并设置到 `SwitchParam` --- ### ✅ 精简目标 1. **只判空一次** 2. **避免重复调用链式 get 方法** 3. **正确处理基本类型 `boolean`** 4. **结构清晰、易于扩展** --- ### ✅ 推荐优化方案(最佳实践) ```java // 提取中间对象,避免重复调用和多次判空 AntivirusAnalysis antivirusAnalysis = dpiMonthlyReportResult != null ? dpiMonthlyReportResult.getAntivirusAnalysis() : null; Defense defense = antivirusAnalysis != null ? antivirusAnalysis.getDefense() : null; // 如果 defense 为空,默认所有功能关闭 senseSecuritySwitch = new SwitchParam(); if (defense != null) { senseSecuritySwitch.setBrowsingProtectionEnabled(defense.isBrowsingProtection()); senseSecuritySwitch.setTrackingProtectionEnabled(defense.isTrackingProtection()); senseSecuritySwitch.setSmartHomeProtectionEnabled(defense.isSmartHomeProtection()); } else { // 可选:显式设置为 false(默认也可设,构造函数可能已初始化) senseSecuritySwitch.setBrowsingProtectionEnabled(false); senseSecuritySwitch.setTrackingProtectionEnabled(false); senseSecuritySwitch.setSmartHomeProtectionEnabled(false); } ``` --- ### ✅ 更简洁写法(一行流,但可读性略低) 如果你喜欢更紧凑风格,也可以这样写: ```java Defense defense = Optional.ofNullable(dpiMonthlyReportResult) .map(DPIMonthlyReportResult::getAntivirusAnalysis) .map(AntivirusAnalysis::getDefense) .orElse(null); senseSecuritySwitch = new SwitchParam(); if (defense != null) { senseSecuritySwitch.setBrowsingProtectionEnabled(defense.isBrowsingProtection()); senseSecuritySwitch.setTrackingProtectionEnabled(defense.isTrackingProtection()); senseSecuritySwitch.setSmartHomeProtectionEnabled(defense.isSmartHomeProtection()); } ``` > ⚠️ 注意:仅当你的项目支持 Java 8+ 且启用了 desugaring(如 Android)时可用。 --- ### ❌ 为什么 `Boolean.TRUE.equals(isXxx())` 是错误的? 假设: ```java public boolean isBrowsingProtection() { ... } // 返回 primitive boolean ``` 那么: ```java Boolean.TRUE.equals(defense.isBrowsingProtection()) ``` 会发生什么? - `defense.isBrowsingProtection()` 返回 `boolean` - 自动装箱为 `Boolean` - `Boolean.TRUE.equals(Boolean)` → 没错,能运行 - 但是!这等价于直接写: ```java defense.isBrowsingProtection() ``` 因为: - `true` → `Boolean.TRUE.equals(true)` → `true` - `false` → `Boolean.TRUE.equals(false)` → `false` 👉 所以完全多此一举! ✅ 只有当你返回的是 `Boolean isXxx()`(包装类,可能为 `null`)时才需要 `Boolean.TRUE.equals(...)` --- ### ✅ 最终建议重构版本(推荐使用) ```java senseSecuritySwitch = new SwitchParam(); Defense defense = Optional.ofNullable(dpiMonthlyReportResult) .map(DPIMonthlyReportResult::getAntivirusAnalysis) .map(AntivirusAnalysis::getDefense) .orElse(null); if (defense != null) { senseSecuritySwitch.setBrowsingProtectionEnabled(defense.isBrowsingProtection()); senseSecuritySwitch.setTrackingProtectionEnabled(defense.isTrackingProtection()); senseSecuritySwitch.setSmartHomeProtectionEnabled(defense.isSmartHomeProtection()); } // 否则保持默认值(或可在此处批量设为 false) ``` 或者用 Optional: ```java senseSecuritySwitch = new SwitchParam(); Defense defense = null; if (dpiMonthlyReportResult != null && (defense = dpiMonthlyReportResult.getAntivirusAnalysis()) != null && (defense = defense.getDefense()) != null) { senseSecuritySwitch.setBrowsingProtectionEnabled(defense.isBrowsingProtection()); senseSecuritySwitch.setTrackingProtectionEnabled(defense.isTrackingProtection()); senseSecuritySwitch.setSmartHomeProtectionEnabled(defense.isSmartHomeProtection()); } ``` --- ### ✅ 总结 | 改进项 | 原始代码 | 优化后 | |--------|--------|--------| | 判空次数 | 每个字段重复 2~3 次 | 全局只判一次 | | 方法调用 | 多次重复 `getAntivirusAnalysis().getDefense()` | 抽取局部变量 | | `Boolean.TRUE.equals(...)` | 冗余 | 删除,直接使用布尔值 | | 可读性 | 差,冗长 | 清晰,结构分明 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值