欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《Effect Java》第三章"对于所有对象都通用的方法”笔记

程序员文章站 2022-06-11 11:58:17
...

第八条:覆盖equals时请遵循通用约定

在覆盖equals时,你必须遵守它的通用约定。下面是约定内容,来自Object的规范【JAVASE6】

  自反性:对于任何非null的引用值x,x.equals(x)必须返回true。也就是说一个类的实例一定是与它本身相等的,不管你怎么实现它的逻辑判断,但它的“本”不能忘。

  对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。这条比较好理解,x=1,y=1,你不能y=x而x!=y吧。

  传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么x.equals(z)也必须返回true。显而易见。

  一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中的所用的信息没有被修改,多次调用x.equals(y)就会一致地返回true,或者一致地返回false。这条显然,你不能多调用判断几次它的结果就产生变化了吧。

可以借鉴String中的equals()方法。

第9条:覆盖equals时总要覆盖hashCode

如果不这么做,就会违反Object.hashCode()的通用约定,从而导致该类无法结合所有基于散列的集合一起正常运作。例如hashMap,hashSet,hashTable。下面是Object规范

1.只要对象的equals方法操作所得到的信息没有被修改,那么hashCode方法也必须都始终如一的返回同一个整数。

2.如果两个对象equals产生的结果相同,那么hashCode产生的结果也必须相同。

3.如果两个对象equals产生不同的结果,那么hashCode可以相同也可以不同,但不同的结果有助于提高散列的性能。

实际上,要了解为什么需要重写hashCode方法,需要理解hashMap的原理,就需要去看下源码。

public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
}

首先put数据进去的时候,会通过hash算法和key作为参数计算出一个值,这个值我们暂定叫做hash值,我们继续看。

V putVal(int hash, K key, V value, boolean onlyIfAbsent,
                   boolean evict) {
        //....

        if ((p = tab[i = (n - 1) & hash]) == null)
            tab[i] = newNode(hash, key, value, null);

        //...
}

多余代码我已经省略了,可以看到数组下标是根据hash值来生成的(所以也能根据hash值来直接定位到数组的位置),然后会指向一个Node的内部类。这个数组其实放的是一个链表的内部类。因为hashCode有可能存在相同的(相同就会在进去链表),所以hashCode的散列程度直接影响其性能。

第十条:始终要覆盖toString

覆盖toString方法的好处在于测试的时候更直观清晰。并且toString的约定也指出:“建议所有的子类都覆盖这个方法”。

第十一条:谨慎的覆盖clone

 书中是不建议自定义重写clone方法的,如果非要重写书中总结为一句话:clone方法就是一个构造器,你必须确保它不会伤害到原始的对象,并确保正确地创建被克隆对象中的约束条件。

  再说一个与本条目无关的点,查看Cloneable接口实际上可以发现里面什么方法都没有,clone方法却来自Object类,继承了Cloneable接口为什么就能重写clone方法了呢?原因在于clone方法在Object类中的修饰符是protected,而Cloneable接口和Object处于同一个包下,熟悉修饰符的都知道protected的权限限定在同一个包下或者其子类。Cloneable和Object同属于一个包,Cloneable自然能继承clone方法,继承了Cloneable接口的成为了它的子类同样也就继承了clone方法。

 --------------------本条摘抄于其他博客------------------

第十二条:考虑实现comparable接口

 

相关标签: effect