EffectiveJava第三章:对于所有对象都通用的方法

Object类的所有非final方法(equals、hashCode、toString、clone、finalize)都有明确的通用约定general contract,因为这些方法被设计成是要被复写的override。
任何一个类,它在覆盖这些方法时,都必须遵守这些约定;如果违法的话,依赖于这些约定的类(HashMap和HashSet)就无法结合该类正常工作。

8. 覆盖equals时遵守的通用约定

  • 不需要覆盖equals方法的情况:
    1. 类的每个实例本质上都是唯一的。
      对于代表活动的实体而不是值得类,比如Thread,Object提供的equals()已然是正确的行为。
    2. 不关心类是否提供了逻辑相等(logical equality)的测试功能。
      比如工具类,用户始终不会使用到equals()方法。
    3. 父类已经覆盖了equals(),从父类继承而来的行为对于子类也是合适的。
      比如Set的实现继承自AbstractSet、List的实现继承自AbstractList,Map实现继承自AbstractMap。
    4. 类是私有的或包级私有的,可以确定它的equals()方法永远不会被调用。可以复写equals()方法并在其内抛出异常。
@override public boolean equals(Obejct o){
  throw new AssertionError();
}
  • 如果类具有自己特有的逻辑相等概念时,且父类并没有提供期望的equals()方法时,则需要覆盖equals()方法。
    希望知道它们在逻辑上是否相等,而不是是否是一个对象。

  • equals()方法实现了等价关系(equivalence relation)

    1. 自反性: 对象必须等于其自身 A == A为true
    2. 对称性: 如果A==B为true,则B == A也为true
    3. 传递性:如果A==B,B==C,那么则有A==C
    4. 一致性: 如果两个对象相等,它们就始终保持相等
    5. 非空性: 所有对象都必须不等于null。
  • 基于这些原则,实现equals()方法必须的步骤:

    • 使用==检查参数是否为这个对象的引用
    • 使用instanceof检查参数是否为正确的类型
    • 把参数转化为正确的类型
    • 对于该类中的每个关键域,检查参数中的域是否与该对象中对应的域相匹配。
  • 覆盖equals()时总要覆盖hashCode()

  • 不要将equals()声明中的Object对象替换为其他类型。

9. 覆盖equals时总要覆盖hashCode

在每个覆盖了equals方法的类中,也必须覆盖hashCode方法。这是Object.hashCode的通用约定。如果不这样做的话,就会导致该类无法和HashMap、HashSet、Hashtable一起正常工作。

  • hashCode()设计原则

    1. Whenever it is invoked on the same object more than once during an execution of a Java application, the hashCode method must consistently return the same integer。
      一致性。一个对象的hashCode()始终一样。

    2. If two objects are equal according to the equals(Object)
      method, then calling the hashCode method on each of the two objects must produce the same integer result.
      如果两个对象相同,那么他们的hashcode应该 相等。

    3. It is not required that if two objects are unequal according to the equals() method, then calling the hashCode method on each of the two objects must produce distinct integer results.
      如果两个对象不相同,他们的hashcode可能相同。

    4. As much as is reasonably practical, the hashCode method defined by class Object does return distinct integers for distinct objects. (This is typically implemented by converting the internal address of the object into an integer, but this implementation technique is not required by the JavaTM programming language.)
      最好的做法是,不同对象的hashCode()值是不一样的。这取决于JVM对内存地址进行转化的实现。

  • 复写hashcode()方法简单的通用步骤

@override public int hashCode(){
    //1. 把某个非零的常数值(17),保存在result中
    int result = 17;
    //2. 根据固定的步骤计算每个关键域的散列码c
    // ...
    // 3.  相加所有的散列码
    result = 31 * result + c1;
    result = 31 * result + c2;

    return result;
}
  • 如果一个类是不可变的,并且计算散列码的开销也比较大,可以把散列码缓存在对象内部。可以使用延迟初始化方法
private volatile int hashCode;

@override public int hashCode(){
    int result = hashCode;
    if (result == 0){
        result = 17;  
        result = 31 * result + c1;
        result = 31 * result + c2;
        hashCode = result;
    }

    return result;
}
  • 不要试图从散列码计算中排除掉一个关键域来提高性能。

  • 现代的IDE当需要时都会自动生成equals()和hashCode(),毕竟遵循的是固定的步骤。

10. 始终要覆盖toString

  • Object.toString()返回的是类的名称@散列码的无符号十六进制

  • toString方法应该返回对象中包含的所有值得关注的信息。

11. 谨慎的覆盖clone

Cloneable接口的目的是作为对象的一个标识接口,表明这样的对象允许克隆。

  • 对于Cloneable接口,它改变了父类中受保护方法的行为。这是接口的一种极端非典型用法,不值得效仿。
    如果子类实现了该接口,便可获取该对象的逐域拷贝;否则抛出CloneNotSupportedException

  • 实现了Cloneable接口,就意味着可以无需调用构造器就可以创建对象

  • clone方法的约定:

    1. x.clone() != x
    2. x.clone().getClass() == x.getClass()
    3. x.clone().equals(x)
  • 可以参考原型模式。固定写法。

12. 考虑实现Comparable接口

类实现了Comparable接口,就表明它具有内在的排序关系(natural ordering)。

  • 当该对象小于、等于或大于指定对象时,分别返回负整数、零或正整数。

  • CompareTo()和equals()的不同:

    1. Compareable接口是泛型化的,不必进行参数检查。
    2. CompareTo方法中域的比较是顺序的比较,而不是等同性比较。如果含有引用类型的对象,可以递归的调用。
  • 如果一个类有多个关监域,必须从最关键的域开始,逐步进行到所有的重要域。

  • compareTo方法并没有指定返回值的大小,只是指定了返回值得符号。但要注意相差不要超过Integer.MAX_VALUE,否则会难以调试、并且难以察觉。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

baiiu

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值