《Java解惑》系列——02字符谜题——谜题13:动物庄园(字符串的==和equals)

知识点:

字符串常量的==和equals


问题:

下面程序会打印什么??

public class AnimalFarm{ 
    public static void main(String[] args){ 
        final String pig = "length: 10"; 
        final String dog = "length: " + pig.length(); 
        System.out. println("Animals are equal: " 
                            + pig == dog); 
    } 
}

// 期望结果:Animals are equal: true
// 实际结果:false

结果是不是出乎大家的意料呢?的确如此。
产生这个结果的原因:

对该程序的表面分析可能会认为它应该打印出Animal are equal: true。毕竟,
pig和dog都是final的string类型变量,它们都被初始化为字符序列“length:
10”。换句话说,被 pig和 dog引用的字符串是且永远是彼此相等的。然而,==
操作符测试的是这两个对象引用是否正好引用到了相同的对象上。在本例中,它
们并非引用到了相同的对象上。  
你可能知道String类型的编译期常量是内存限定的。换句话说,任何两个String
类型的常量表达式,如果标明的是相同的字符序列,那么它们就用相同的对象引
用来表示。如果用常量表达式来初始化pig和 dog,那么它们确实会指向相同的
对象,但是 dog并不是用常量表达式初始化的。既然语言已经对在常量表达式中
允许出现的操作作出了限制,而方法调用又不在其中,那么,这个程序就应该打
印Animal are equal: false,对吗? 
嗯,实际上不对。如果你运行该程序,你就会发现它打印的只是 false,并没有
其它的任何东西。它没有打印Animal are equal: 。它怎么会不打印这个字符
串字面常量呢?毕竟打印它才是正确的呀!谜题 11 的解谜方案包含了一条暗示:
+ 操作符,不论是用作加法还是字符串连接操作,它都比 == 操作符的优先级高。
因此,println 方法的参数是按照下面的方式计算的:  
System.out.println(("Animals are equal: " + pig) == dog);
这个布尔表达式的值当然是 false,它正是该程序的所打印的输出。 
有一个肯定能够避免此类窘境的方法:在使用字符串连接操作符时,总是将非平
凡的操作数用括号括起来。更一般地讲,当你不能确定你是否需要括号时,应该
选择稳妥地做法,将它们括起来。如果你在 println语句中像下面这样把比较部
分括起来,它将产生所期望的输出Animals are equal: false :  
System.out.println("Animals are equal: " + (pig == dog));



解决方法:

在比较对象引用时,你应该优先使用 equals 方法而不是 == 操作符,除非你需
要比较的是对象的标识而不是对象的值。通过把这个教训应用到我们的程序中,
我们给出了下面的 println语句,这才是它应该具有的模样。很明显,在用这种
方式订正了该程序之后,它将打印出true:  
System.out.println("Animals are equal: " + pig.equals(dog));

总结:

•  字符串连接的优先级不应该和加法一样。这意味着重载 + 操作符来执行
字符串连接是有问题的,就像在谜题 11 中提到的一样。  
•  还有就是,对于不可修改的类型,例如String,其引用的等价性比值的
等价性更加让人感到迷惑。也许 == 操作符在被应用于不可修改的类型时
应该执行值比较。要实现这一点,一种方法是将 == 操作符作为 equals
方法的简便写法,并提供一个单独的类似于System.identityHashCode
的方法来执行引用标识的比较。 




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值