前言:

读这本书第1条规则的时候就感觉到这是一本非常好的书。可以把我们的Java功底提升一个档次,我还是比較推荐的。这里我主要就关于覆盖equals、hashCode和toString方法来做一个笔记总结。希望可以与君共勉。

概述:

这一章主要是说明一些对于全部对象都通用的方法。我们知道Java的多态是其特色之中的一个,而多态的体现方式中就有一种方式叫做“重写”。这些概念性的东西我想在大学我们学习Java的初期。老师就会如数家珍一样地灌输给我们。只是。在那个时候有多少人真的了解了什么是重载,什么是重写,什么是多态呢?

而对于如今的一些开发人员而言,了解并使用它们是家常便饭。理所应当。可是。你真的是已经够了解吗?

相关内容:

1.覆盖equals时请遵守通用约定

我们知道Java中假设须要比較两个对象是否相等的时候,就会用到equals。对于刚開始学习的人。可能遇到很多其它的是equals与"=="的差别,可能一開始大家都是一头雾水,傻傻分不清楚。这可能是由于你还没有地址和值的概念。

关于equals与"=="的差别,大家能够看看这篇博客——Java中equals和==的差别

假设你还不是非常清楚equals和"=="的差别,那么,你能够花几分钟看看上面的博客,以便你能够明确。我们为什么要覆盖equals方法。

假设你已完全了解,那么便没有什么东西能够阻止你继续往下看。

我们知道equals要实现的是逻辑上的等。站在数学的角度来看。两个事物相等的条件,有例如以下几个:

1.自反性:对于不论什么非null的引用值x,x.equals(x)必须返回true.

2.对称性:对于非空的引用值x,y,当且仅当x.equals(y)返回true时,y.equals(x)必须返回true.

3.传递性:对于不论什么非null的引用值x,y,z,假设x.equals(y)=true。y.equals(z)=true,那么x.equals(z)也必须返回true。

4.一致性:对于不论什么非null的引用值x,y。仅仅要equals的比較操作在对象中所用的信息没有被改动,多次调用x.equals(y)就会一致地返回true。或一致地返回false.

5.对于非null的引用值x,x.equals(null)必须返回false.

看完上面的这些数学式的规则,你是不是有一种哪要这么麻烦的事的感觉呢?从直观上来说,上面的这些规则的确是有一些麻烦,但你却不能忽视它们,不然麻烦的可就是你了。

以下我会通过一些实例的学习,来说明这些规则。

1.自反性:

<span style="font-family:Courier New;font-size:18px;">public static void equalsOppositeSelf() {String s = "ABC";Object o = new Object();System.out.println(s.equals(s));</span>

结果:

<span style="font-family:Courier New;font-size:18px;">true
true</span>

2.对称性:

对于对称性,可能你会感觉理所当然。

这是由于在你看来,我们要比較的两者必然是同一类型,这个必然太过理想化了。假设我们比較的两个对象不是同一种类型呢?以下能够看看这个样例。

<span style="font-family:Courier New;font-size:18px;">public final class CaseInsensitiveString {private final String s;public CaseInsensitiveString(String s) {if (s == null) {throw new NullPointerException();}this.s = s;}public boolean equals(Object o) {if (o instanceof CaseInsensitiveString) {return s.equalsIgnoreCase(((CaseInsensitiveString)o).s);}if (o instanceof String) {return s.equalsIgnoreCase((String)o);}return false;}
}</span>

上面equals方法的代码实现了忽略大写和小写来比較字符串。

我们先不考虑同类型的两个对象比較,对于不同类型的两个对象,从上面的代码中我们能够看出。假设被比較的对象是一个String类型的。那么我们就能够去忽视大写和小写进行比較。答案也是在情理之中。以下看看例证:

比較方法:

<span style="font-family:Courier New;font-size:18px;">public static void equalsSymmetric() {CaseInsensitiveString s1 = new CaseInsensitiveString("abc");String s2 = "abc";System.out.println("s1 == s2 ?

" + s1.equals(s2)); System.out.println("s2 == s1 ?

" + s2.equals(s1)); }</span>

比較结果:

<span style="font-family:Courier New;font-size:18px;">s1 == s2 ? true
s2 == s1 ? false</span>

这是为什么?不是说equals要满足对称性的吗?怎么这里又行不通了呢?

细致推敲一番就能够发现了,我们在进行s1.equals(s2)的时候,是由于s1是CaseInsensitiveString类型的,它会运行到上面的代码,而s2是String类型的。s2.equals(s1)的比較自然是String中的equals方法。

那你又会问。既然这样我们总不能去改动String类中的代码吧。假设你这样想,那我就无言以对了。我们知道一件事,两个不同类型的对象我就让它不同样去吧。也就是说,我们要有一个推断告诉程序。假设被比較的对象不是CaseInsensitiveString类型,那我们就不用客气直接返回false即可了。改动后的代码例如以下:

<span style="font-family:Courier New;font-size:18px;">    public boolean equals(Object o) {return o instanceof CaseInsensitiveString && ((CaseInsensitiveString) o).s.equalsIgnoreCase(s);}</span>

3.传递性

传递性的推断是x = y, y = z。那么就能够推断x = z了。

如今如果我们有一个类Point和一个Point的子类ColorPoint分别例如以下:

Point

<span style="font-family:Courier New;font-size:18px;">public class Point {private final int x;private final int y;public Point(int x, int y) {this.x = x;this.y = y;}public boolean equals(Object o) {if (!(o instanceof Point)) {return false;}Point p = (Point) o;return p.x == x && p.y == y;}
}</span>

ColorPoint

<span style="font-family:Courier New;font-size:18px;">public class ColorPoint extends Point {private final Color color;public ColorPoint(int x, int y, Color color) {super(x, y);this.color = color;}
}</span>

能够看到ColorPoint继承于Point,只是比Point类多一个颜色属性。当我们把ColorPoint与Point和Point与ColorPoint进行比較,例如以下:

<span style="font-family:Courier New;font-size:18px;">public static void equalsTransitivity() {Point p1 = new Point(1, 2);ColorPoint cp1 = new ColorPoint(1, 2, Color.BLACK);System.out.println("p1 == cp1 ?

" + p1.equals(cp1)); System.out.println("cp1 == p1 ? " + cp1.equals(p1)); }</span>

会得到例如以下结果:

<span style="font-family:Courier New;font-size:18px;">p1 == cp1 ?

true cp1 == p1 ? true</span>

为什么两个都true呢?明明两个不同类型啊。假设真的要去考虑父类与子类的关系,也应该是一个true一个false啊。由于这里我们的ColorPoint本身没有重写Point的equals,它使用的是Point的equals,这时不管哪一次的比較中。都是去比較x和y,与color无关。

这样就会导致一个问题,假设我的两个比較对象都是ColorPoint呢?这样一来假设我的两个ColorPoint的x和y全都一样,仅仅是color不同,那么不管怎么比較,其结果值都会是true.这里不会去检查color。

那你可能就会说,那我们就重写ColorPoint的equals啊。

这里我们使用一条建议:复合优于继承(这一点在设计模式中也有体现)。

实例示范:

<span style="font-family:Courier New;font-size:18px;">public static void equalsTransitivity() {Point p1 = new Point(1, 2);ColorPoint cp1 = new ColorPoint(1, 2, Color.BLACK);ColorPoint cp2 = new ColorPoint(1, 2, Color.BLUE);ColorPointNew cpn1 = new ColorPointNew(1, 2, Color.BLACK);ColorPointNew cpn2 = new ColorPointNew(1, 2, Color.BLUE);System.out.println("p1 == cp1 ? " + p1.equals(cp1));System.out.println("cp1 == p1 ?

" + cp1.equals(p1)); System.out.println("cp1 == cp2 ? " + cp1.equals(cp2)); System.out.println("cpn1 == cpn2 ? " + cpn1.equals(cpn2)); System.out.println("cpn1 == cp1 ? " + cpn1.equals(cp1)); System.out.println("cp1 == cpn1 ?

" + cp1.equals(cpn1)); }</span>

结果:

<span style="font-family:Courier New;font-size:18px;">p1 == cp1 ? true
cp1 == p1 ?

true cp1 == cp2 ? true cpn1 == cpn2 ? false cpn1 == cp1 ? false cp1 == cpn1 ? false</span>

上面的代码看上去非常简洁。

4.一致性

一致性的要求是,假设两个对象相等,它们就必须始终保持相等,除非它们中有一个对象被改动了。

2.覆盖equals时总要覆盖hashCode

为什么要说覆盖equals时总要覆盖hashCode呢?前面我们说的那些不都好好的么?一些equals必需的数学规则不是都已经满足了么?我们不是已经做得差点儿相同了么?是的。的确是差点儿相同了。只是我们还是要去覆盖hashCode方法。

这是由于我们假设把我们的对象与HashMap之类的Hash值联系起来,有此时候可能会感到困惑,甚至大失所望。

以下,我们就来列举一个样例,依据样例来说明再合适只是了。

我们有这样一个PhoneNumber类:

package com.java.effective.samples;public final class PhoneNumber {private final short areaCode;private final short prefix;private final short lineNumber;public PhoneNumber(int areaCode, int prefix, int lineNumber) {rangeCheck(areaCode, 999,  "area code");rangeCheck(prefix, 999,  "prefix");rangeCheck(lineNumber, 9999,  "line number");this.areaCode = (short)areaCode;this.prefix = (short)prefix;this.lineNumber = (short)lineNumber;}private static void rangeCheck(int arg, int max, String name) {if (arg < 0 || arg > max) {throw new IllegalArgumentException(name + ": " + arg);}}@Overridepublic boolean equals(Object o) {if (o == this) {return true;}if (!(o instanceof PhoneNumber)) {return false;}PhoneNumber pNumber = (PhoneNumber)o;return (pNumber.lineNumber == lineNumber) && (pNumber.prefix == prefix) && (pNumber.areaCode == areaCode);}
}

上面的代码对equals的处理全然OK。只是假设我们把PhoneNumber和HashMap放在一起使用,结果会怎样?以下是我们的測试用例:

public static void hashCodePhoneNumber() {Map<PhoneNumber, String> map = new HashMap<PhoneNumber, String>();PhoneNumber phoneNumber = new PhoneNumber(707, 867, 9876);map.put(phoneNumber, "Jenny");System.out.println(map.get(new PhoneNumber(707, 867, 9876)));System.out.println(map.get(phoneNumber));}

结果:

null
Jenny

我们能够这样来理解上面的map.put()。假设我们不去覆盖hashCode,那么当我们使用map.put时。我们是把这些PhoneNumber对象放在各个不同的盒子里。而我们去map.get()的时候。仅仅是去某一个盒子里去找(当然,假设map.get()和map.put()中的对象是同一个的话,当然能够找到)。

而假设我们覆盖了hashCode方法。这时,假设通过hashCode计算出来的值是相等的,就会放在同一个盒子里。这样。仅仅要我们对象中保存的值是全然一致的,就会找到这个key所相应的value。

不知道你发现没有,这个hashCode有点类似于分类,这样在数据量比較大的情况下就会大大提高效率。

我们能够通过下面两种方法来覆盖hashCode方法:

方法一:

@Overridepublic int hashCode() {return 42;}

方法二:

@Overridepublic int hashCode() {int result = 17;result = 31 * result + areaCode;result = 31 * result + prefix;result = 31 * result + lineNumber;return result;}

首先两种方法都能够。通过上面的分析,从效率的角度来考虑,当然是另外一种方法更为恰当。

所以在覆盖了equlas的同一时候,别忘了去覆盖hashCode.

3.始终要覆盖toString

承上,就拿PhoneNumber类来说,假设我们不去覆盖类的toString()方法,后果就是当我们须要去打印这个类的对象时。会有一些并不是是我们想要的那种。类似这种:com.java.effective.samples.PhoneNumber@12a7e3

有时我们不希望打印出这种对象,那我们就要去覆盖它们的toString方法了。

在这种方法里。我们能够依照我们自己的意愿来给类加入toString方法。对于PhoneNumber。我们能够这样来写:

@Overridepublic String toString() {String result = "";result += (areaCode + "-");result += (prefix + "-");result += (lineNumber);return result;}

打印结果:

707-867-9876

总结:

在我们优化代码的时候最好还是考虑一下去合理地覆盖这些方法。能够让我们的代码更加健壮。

转载于:https://www.cnblogs.com/gccbuaa/p/6878875.html

Effective Java:对于全部对象都通用的方法相关推荐

  1. Effective Java 对于所用对象都通用的方法 8.覆盖equals时请遵守通用约定.txt

    对于eclipse覆盖equals方法就是Alt+Shift+S,而AS就是Alt+Insert.覆盖很简单,可是却容易导致错误,而且后果很严重.最容易避免的方法就是不覆盖,这样类就只与他自身相等. ...

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

    前言: 读这本书第1条规则的时候就感觉到这是一本很好的书,可以把我们的Java功底提升一个档次,我还是比较推荐的.这里我主要就关于覆盖equals.hashCode和toString方法来做一个笔记总 ...

  3. 《Effective Java》 第二讲:对于所有对象都通用的方法

    前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家.点击跳转到教程. 上接<Effective Java> 第一讲:创建和销毁对象 八.覆盖 equals 时 ...

  4. 2.对于所有对象都通用的方法_EJ

    第8条: 覆盖equals时请遵守通用约定 我们在覆盖equals方法时,必须遵守它的通用约定: 1.自反性.对于任何非null的引用值x,x.equals(x)必须返回true: 2.对称性.对于任 ...

  5. Effective java -- 2 对于所有对象都通用到方法

    第八条:覆盖equals时请遵守通用约定 什么时候需要覆盖equals方法?类具有自己的逻辑相等概念,并且父类的equals方法不能满足需要. 重写equals时需要遵循一下约定: 自反性:非null ...

  6. 《Effective Java》—— 对于所有对象都通用的方法

    本节主要涉及Object中通用的一些方法,比如equals,hashCode,toString,clone,finalize等等 覆盖equals时请遵守通用约定 equals方法实现的等价关系: 自 ...

  7. java 通用对象_java中对所有对象都通用的方法

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 覆盖equals时请遵守通用约定 我们知道equals要实现的是逻辑上的相等.站在数学的角度来看,两个事物相等的条件,有如下几个: 1.自反性:对于任何非 ...

  8. java 匿名list,java创造匿名对象的两种方法

    在java中有时候需要一些匿名对象的使用.可能有些小伙伴拿还不会创造,其实我们在学习一些方法时都或多或少的接触过.本篇所要讲到的创造匿名对象总结了两种方法,分别是静态工具方法和Lambda表达式,我们 ...

  9. Effective Java读书笔记四:通用程序设计

    第45条:将局部变量的作用域最小化 在第一次使用变量时的地方声明: 几乎每个局部变量的声明都应该包含一个初始表达式: 如果在终止循环之后不需要循环变量的内容,for循环优于while循环.(for循环 ...

最新文章

  1. Oracle表空间管理
  2. 自学python当黑客-为什么黑客都用Python
  3. ads in shanghai
  4. linux命令速查手册_干货| 有了这个速查手册,还怕Linux命令记不住?
  5. 启明云端分享|ESP32-C3(ESP32­C3­MINI­1)使用的RISC与CISC有什么区别
  6. mysql left join、right join、inner join、union、union all使用以及图解
  7. 10个最受欢迎的 JavaScript 框架,它们的主要特征和功能
  8. Ubuntu16.04安装Shark-3.0.0
  9. C++中#和##的特殊使用
  10. [field:picname/]和[field:litpic/]区别
  11. Linux acpi off报告ACPI bug处理方法
  12. 域名微信拦截html代码,微信域名拦截查询网页源码——一个非常实用的微信域名检测工具实现...
  13. PayPal(大陆、美国、英国)如何提现到国内教程详解
  14. python如何爬取煎蛋图片(js)
  15. 10万微商被骗100亿,最大微商集团被爆涉嫌传销
  16. 根据交谈和3D面部表情判断抑郁症
  17. 个人网站---利用WordPress搭建个人网站
  18. 中水是什么?有什么用途呢?
  19. 新零售运营独白,如何联合线上线下
  20. Tsi721芯片驱动代码使用说明

热门文章

  1. 劣质代码评析——《写给大家看的C语言书(第2版)》附录B之21点程序(一)
  2. 《设计模式解析(第2版)》
  3. 成长必经的低谷已经来了么?
  4. PHP生成PDF文档的FPDF类
  5. javascript断点调试方法
  6. 《父亲家书》选:给初为人师的儿子
  7. 求职者提问的问题面试官不会_如何通过三个简单的问题就不会陷入求职困境
  8. ai css 线条粗细_如何训练AI将您的设计模型转换为HTML和CSS
  9. 软件测试开发:常见测试类型概念
  10. Linux 上 12 个高效的文本过滤命令