《effective java》读书笔记2(对于所有对象都通用的方法)

时间:2021-04-16 16:06:41

前言:Object类的所有非final方法(equals,hashCode,toString,clone,finalize)被设计成被覆盖的,但是它们有明确的通用约定,在覆盖这些方法时需要遵守,否则会出现和其他类(HashMap,HashSet)配合使用的情况。

第8条:equals的通用约定

1.不需要覆盖equals的情况:

》类的每个实例唯一,用Object的equals方法即可
》不关心类是否逻辑相等
》从超类继承过来的equals方法也适合本身
》类是private或者public,别的地方无法访问,或者确定equals方法不会被调用的时候。

2.需要覆盖equals的清况:

》需要逻辑相等,超类中没有实现equals方法

3.equals通用约定:

》自反,对象必须等于自身。例如set集合,如果不满足自反,可能重复添加
》对称,A.equals(B)且B.equals(A)。书中实现了一个忽略大小写的CaseInsensitiveString类,让其可以与普通String类相等,然而反过来String类的equals方法并不能与CaseInsensitiveString类逻辑相等,不满足自反性
》传递,A.equals(B)且B.equals(C),那么A.equals(C)
》一致,A.equals(B)那么该句子恒成立
》参数非空,一般首先判断参数是否instanceof,才进行类型转换比较。

4.equals最佳实践

》如果equals代价昂贵,最好加上==检查是否为本身的引用
》instanceof防止类型错误或者null
》接着进行类型转换
》比较关键域

//举例
class A{
    @Override
    public boolean equals(Object o){
        if(o==this) return true;
        if(!(o instance A)) return false;
        A a = (A)o;
        ...
    }
}

5.注意事项

》覆盖equals方法时总要覆盖hashCode
》不要将上例子的equals(Object o)改为equals(A o),这样看起来减少类型检查,实际上修改后equals方法会变得很诡异,因为并没有Override到Object类的equals方法。

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

因为像HashMap,HashTable,HashSet这些基于散列的集合需要同时用到equals和hashCode的方法,一般来说,逻辑相等的两个对象要求hashCode值一样,不相等的两个对象hashCode值不要求一定不一样,但是可以提高性能。

1.如何写一个好的hashCode方法:

自定义散列函数 ,产生的散列值,使得逻辑相同的对象有相同的散列值(放入同一个桶中),逻辑不同的对象有不同的散列值(放入不同的桶中)。

结合equals方法的关键域,有如下步骤:

1.把一个非零的常数值,如17,保存在一个名叫result的int变量中;

2.对于equals方法中的关键域产生int类型散列值:

boolean类型的域(如名叫f) 计算(f?1:0)

byte,char,short,int类型的域 计算 (int)f

long类型的域 计算 (int)(f^(f>>>32))

float类型的域 计算 Float.floatToIntBits(f)

double类型的域 计算 Double.doubleToLongBits(f)

引用类型的域 使用 引用类型的hashCode方法得到的散列值

数组类型的域 把数组中的每个元素当成一个关键域,计算出散列值。

3.对于每一个关键域计算出来的散列值 (如名叫c)

result = result * 31 + c;

最后放回这个result整数 就是计算出来的当前调用hashCode方法得到的散列值。

注意不要试图从散列值的计算过程中排除一个关键域来提高散列值计算的性能。

第10条:始终覆盖toString方法

主要是打印关键信息方便调试或日志记录

第11条:谨慎的覆盖clone方法

Object类本身是没有实现Cloneable接口的,并且clone方法是一个native方法。在默认的情况下,Object类中的clone是返回对象的逐域拷贝的,当然,这必须让这个类实现了Cloneable接口,如果类不实现这个接口却调用clone接口,就会抛出CloneNotSupportedException。

所有实现了Cloneable接口的类都应该用一个公有的方法覆盖clone,此方法首先调用super.clone,然后修正任何需要修正的域,例如:

@Override  
public Stack clone() {  
    try {  
        Stack result = (Stack) super.clone();  
        result.elements = elements.clone();  
        return rsult;  
    } catch(CloneNotSupportedException e) {  
        throw new AssertionError();  
    }  

第12条:考虑实现Comparable接口

一旦实现了该接口就可以和许多泛型算法和支持该接口的集合进行协作。比如排序算法,或者排序树,这个用的多。