Effective Java:对于所有对象都通用的方法

8.覆盖equals通用约定

概述

Object的设计是为了扩展,其所有的非final方法都有明确的通用约定,复写时需要遵循通用约定,以保证和其他类能协调使用.

不复写 equals(即直接用Object#equals)

  • 类的每个实例本质上都是唯一的(针对代表活动实体而不是值的类)
  • 不关心是否提供了逻辑相等的功能
  • 超类覆盖的equals,通过继承过来的行为对子类也是合适的.
  • 类是 私有包级私有的,可以确定其equals永远不会被调用,可通过如下方式强化约束.
  @Override public boolean equals(Object o) {
    throw new AssertionError();
  }

当类具有自己特有的逻辑相等概念时,就需要重写equals方法.

单例类的逻辑相同对象等同是一回事.

equals通用约定

  • 自反性
    对于非null的引用值x,x.equals(x)必须返回true
  • 对称性
    对于非null的引用值x,y,当且仅当 y.equal(x)==true时,x.equals(y)==true
  • 传递性
    对于任何非null的引用值x,y,z,如果x.equals(y)== truey.equals(z)==true,则x.equals(z) == true.在扩展子类的时候尤其需要注意.
  • 一致性
    对于非null的引用值x,y,只要equals比较操作在对象中所用的信息没有被修改,则多次调用equals方法返回值也应一致.
  • 非空性
    对于任何非null的引用值x,x.equals(null) == false

告诫

  • 复写 equals时总要复写hashcode
  • 不要企图让equals过于只能(即考虑所有值域情况)
  • 不要将声明中的Object对象替换为其他对象.

9.覆盖equals总要覆盖hashcode

概述

复写 equals 的类也必须复写hashcode,否则无法结合基于散列的集合一起正常工作.

通用约定

  • 只要对象equals 所用到的信息没有被修改.对于同一对象多次调用hashcode必须返回同一个整数
  • 如果俩个对象的equals比较相等.则两个对象的hashcode产生同样的整数结果.
  • 如果equals不相等,则两个对象的hashcode不一定产生不同的整数结果.

建议与须知

  • 如果hashcode返回一个常量,则每个对象具有相同的散列吗,,每个对象被映射到同一个散列桶中,散列桶会退化为散列表结构.
  • 必须排除 equals 计算中没有用到的任何域.
  • 不要试图从 散列码计算中,除掉一个对象的关键部分来提高性能.
  • 31 有个很好的特性,即31*i == (i<<5)-i,现代的VM可以自动完成这种优化.
  • 如果一个类是不可变的,且计算hashcode开销比较大,建议将散列表缓存在对象内部.
@Override
    public int hashCode() {
        int result = 17;
        result = 31 * result + areaCode;
        result = 31 * result + prefix;
        result = 31 * result + lineNumber;
        return result;
    }

10.始终覆盖toString

  1. 提供好的toString使类用起来更加舒服
  2. toString应该包含所有值得关注的信息.
  3. 无论是否指定格式,都应该为toString返回值中包含的所有信息,提供一种编程式的访问路径.

11.谨慎覆盖clone

概述

如果一个类实现了Cloneable,Objectclone()方法就会返回该对象的逐级拷贝,否则抛出异常.

clone 通用协议

对于任何对象 x:

  • x.clone() != x
  • x.clone().getClass() == x.getClass(),但这不是绝对要求
  • x.clone().equals(x),也不是一个绝对要求

提醒:

  • 如果覆盖了 非final 类中的clone方法,则应该返回一个通过调用super.clone而得到的对象.
@Override protected Object clone() {
    try {
      return (PhoneNumber)super.clone();//不要让客户做转换
    } catch (CloneNotSupportedException _e) {
      throw new AssertionError();
    }
  }
  • 如果对象中的域引用了可变的对象.仅仅super.clone,新clone的对象的引用域将引用原来的可变对象.破坏了对象克隆的约束条件.
  @Override public Stack clone() {//将protect--> public
    try {
      Stack result = (Stack) super.clone();
      result.elements = elements.clone();//不伤害原始对象
      return result;
    } catch (CloneNotSupportedException _e) {
      throw new AssertionError();
    }
  }
  • 如果elements是final的将会导致失败,因为是被禁止给fianl域重新赋值
  • 要实现clone方法的类,都应该实现Cloneable接口,同时把clone方法可见性设为public
  • 同构造器一样,clone方法不能在构造过程中调用新对象的非final方法,防止子类修改其状态.

小结

  1. 所有实现了Cloneable接口的类,都应该用一个公有的方法 覆盖 clone
  2. 此方法首先调用 super.clone
  3. 最好提供其他的途径来代替对象拷贝,或者干脆不提供这样的功能.
  4. 另一种实现对象拷贝的方法是提供一个拷贝构造函数,或者拷贝工厂方法,而且此种方法更加推荐.
  5. 设计用来被继承的类时,如果不实现一个正确高效的clone重写,其子类也将无法实现正确高效的clone功能

12.考虑使用Comparable接口

概述

当需要对象自然有序时,实现Comparable接口

通用协议

  1. sgn(x.compareTo(y)) == - sgn(y.compareTo(x));当类型不对时,应该抛出ClassCastException
  2. x.compareTo(y) > 0 && y.compareTo(z) > 0 ,则x.compareTo(z) > 0
  3. x.compareTo(y) == 0,则sgn(x.compareTo(z)) == sgn(y.compareTo(z))
  4. 建议与equals保持一致,但非必须.即 x.compareTo(y) == 0, 则x.equals(y).

依赖于比较关系的类TreeSet, TreeMap,Arrays等,而HashSet, HashMap则是通过equals + hashCode保存

实现细节

  • 从最关键的域开始,逐步进行到所有的重要域.
  • 谨慎使用返回差值的方式,有可能会溢出(最大值和最小值之差 小于等于Integer.MAX_VALUE)

示例 :PhoneNumber.java

关于equals()与hashCode()的总结

equals()方法

实践表明,当equals()方法被override时,hashCode()也要被override。按照一般hashCode()方法的实现来说,相等的对象,它们的hash code一定相等。

hashcode()方法

实践表明,由Object类定义的hashCode()方法对于不同的对象返回不同的integer。

hashcode与Java集合

  1. 当集合要添加新的元素时,先调用这个元素的hashCode方法,就一下子能定位到它应该放置的物理位置上。
  2. 如果这个位置上没有元素,它就可以直接存储在这个位置上,不用再进行任何比较了;
  3. 如果这个位置上已经有元素了(哈希冲突),就调用它的equals方法与新元素进行比较,相同的话就不存了,不相同就散列其它的地址。

1、equals相同,则hashcode一定相同。
2、equals不同,hashcode可能相同。
3、hashCode相同,equals可能相同。
4、hashcode不同,equals一定不同。

扩展阅读:

Java提高篇——equals()与hashCode()方法详解

Effective Java中关于equals与hashcode的总结

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值