Effective Java(第三版) 学习笔记 - 第三章 对于所有对象都通用的方法 Rule10~14

Effective Java(第三版) 学习笔记 - 第三章 对于所有对象都通用的方法 Rule10~14

目录

Effective Java(第三版) 学习笔记 - 第三章 对于所有对象都通用的方法 Rule10~14

Rule10 覆盖equals时请遵守通用约定

equals需要遵守的约定

什么情况下不需要重新定义equals

Rule11 覆盖equals时总要覆盖hashCode

Rule12 始终要覆盖toString

Rule13 谨慎地覆盖clone

Rule14 考虑实现Comparable接口


Rule10 覆盖equals时请遵守通用约定

我们都知道值相等和地址相等是有区分的,如果不重写equals方法的话,那么最终还是Object类中定义的 return (this == obj); 地址对比。如果遇到了我们需要自我实现equals方法,从而覆盖Object的equals时,需要满足以下几个约定:

equals需要遵守的约定

  • 自反性:对于任何非null的引用值x,x.equals(x)必须返回true。
  • 对称性:对于任何非null的引用值x和y,当且仅当y.equals(x)返回true时,x.equals(y)必须返回true。
  • 传递性:对于任何非null的引用值x、y和z,如果x.equals(y)返回true,并且y.equals(x)也返回true,那么x.equals(z)也必须返回true。
  • 一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true,或者一致的返回false。
  • 非空型:对于任何非null的引用值x,x.equals(null)必须返回false。

针对这些要求拥有以下诀窍

  1. 优先比较引用值是否为null。如果是、则可以直接返回false。
  2. 使用==操作符检查“参数是否为这个对象的引用”。如果是、则可以直接返回true。
  3. 使用instanceof操作符检查“参数是否为正确的类型”。如果不是、则可以直接返回false。
  4. 把参数转换成正确的类型,然后对于该类中的每个关键域,检查参数中的域是否与该对象中对应的域相匹配。
  5. 编写完equals后,应该问自己是否满足了对称性、传递性、一致性。

什么情况下不需要重新定义equals

  • 满足一下任意一点,那么我们就不需要重写Object中的equals方法:
  • 类的每个实例本质上都是唯一的。
  • 类没有必要提供“逻辑相等”功能。
  • 超类(父类)已经覆盖了equals,超类的行为对于这个类也是合适的。
  • 类是私有的,或者是包级私有的,可以确定它的equals方法永远不会被调用。

如果非要实现equals,建议听从以下的告诫

  • 覆盖equals时总要覆盖hashCode(不然会对HashMap等类,使用时key的hash值判定出问题)。
  • 不要企图让equals方法过于智能。
  • 不要将equals申明中的Object对象替换成其他的类型(不然就不是重写Object中的equals方法)。
  • 如果有可能,建议用Lombok的@EqualsAndHashCode(callSuper=true)直接实现。

 

Rule11 覆盖equals时总要覆盖hashCode

为什么要求重写hashCode方法,因为在涉及HashMap、HashTable、HashSet时,优先会比对key值的散列码(hashCode)。

如果不涉及到HashMap、HashTable、HashSet时,不重写hashCode方法不会有什么影响。但是建议在重写了equals之后,还是实现了hashCode,因为你根本无法知道未来是不是会出现涉及到HashMap的场景。

但是如果涉及到散列表操作,需要记住以下两点

  • equals相等的两个对象,它们的hashCode也必须一定相等。
  • hashCode相等的两个对象,他们并不一定真的相等。因为涉及到哈希冲突。

 

Rule12 始终要覆盖toString

    public String toString() {
        return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }

这是定义在Object中的toString实现,如果我们不重写toString方法的话,特别是针对POJO类,如果查看输出的打印日志,往往看见的不是我们想知道的具体属性值,而是类名+哈希码的十六进制字符串。

 

Rule13 谨慎地覆盖clone

Cloneable接口是一个空接口,感觉更像是一个标识定义。真正的clone方法是在Object中的native本地方法。

一般来讲不推荐实现clone方法,如果必须这么做,请记住:必须确保它不会影响到原始的对象,并确保正确的创建被clone对象中的约束条件。如果被clone的对象中包含引用对象,请确保该引用对象也实现了clone方法。

相比而言,提供一个拷贝构造器或者拷贝方法可能更加合适。

public Yum(Yum yum) {...};
public static Yum newInstance(Yum yum) {...};

 

Rule14 考虑实现Comparable接口

和clone方法相反,compareTo方法并不在Object中定义,而是定义在Comparable接口中。往往实现compareTo方法都是为了特定的排序关系。

实现compareTo最好遵守:

compareTo的类型必须相同

如果x.compareTo(y)为z,那么y.compareTo(x)必须是z的相反值

如果x.compareTo(y)>0 && y.compareTo(z)>0,那么必须x.compareTo(z)>0如果x.

如果x.compareTo(y)==0 && y.compareTo(z)==0,那么必须x.compareTo(z)==0

强烈建议(x.compareTo(y)==0) == (x.equals(y))

建议不要使用<>关系符来比较,尽可能的用Double.compare、Float.compare这种装箱类型去比较基本类型(注意:这是在第三版中特意提出与前两版不一样的地方)。或者使用Java8提供的比较器构造方法来实现。

    // Multiple-field Comparable with primitive fields (page 69)
    public int compareTo(PhoneNumber pn) {
        int result = Short.compare(areaCode, pn.areaCode);
        if (result == 0)  {
            result = Short.compare(prefix, pn.prefix);
            if (result == 0)
                result = Short.compare(lineNum, pn.lineNum);
        }
        return result;
    }

    // Comparable with comparator construction methods (page 70)
    private static final Comparator<PhoneNumber> COMPARATOR =
            comparingInt((PhoneNumber pn) -> pn.areaCode)
                    .thenComparingInt(pn -> pn.prefix)
                    .thenComparingInt(pn -> pn.lineNum);

    public int compareTo(PhoneNumber pn) {
        return COMPARATOR.compare(this, pn);
    }

不推荐利用数值相减的结果与0进行判断,因为十分有可能造成整数溢出。建议使用下面推荐的两种方法

// 不推荐,容易造成整数溢出
static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1, Object o2) {
        return o1.hashCode() - o2.hashCode();
    }
}
// 推荐1
static Comparator<Object> hashCodeOrder = new Comparator<>() {
    public int compare(Object o1, Object o2) {
        return Integer.compare(o1.hashCode(), o2.hashCode());
    }
}
// 推荐2
static Comparator<Object> hashCodeOrder = Comparator.comparingInt(o -> o.hashCode());

 

本文技术菜鸟个人学习使用,如有不正欢迎指出修正。xuweijsnj

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值