惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The Last Watchdog
The Last Watchdog
博客园_首页
Martin Fowler
Martin Fowler
S
SegmentFault 最新的问题
美团技术团队
小众软件
小众软件
V
V2EX
博客园 - Franky
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
The GitHub Blog
The GitHub Blog
Microsoft Security Blog
Microsoft Security Blog
Attack and Defense Labs
Attack and Defense Labs
S
Security Affairs
Simon Willison's Weblog
Simon Willison's Weblog
I
Intezer
T
The Exploit Database - CXSecurity.com
有赞技术团队
有赞技术团队
S
Schneier on Security
人人都是产品经理
人人都是产品经理
Security Archives - TechRepublic
Security Archives - TechRepublic
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
K
Kaspersky official blog
PCI Perspectives
PCI Perspectives
AI
AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
罗磊的独立博客
O
OpenAI News
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
The Register - Security
The Register - Security
V
Vulnerabilities – Threatpost
GbyAI
GbyAI
博客园 - 【当耐特】
C
Cisco Blogs
大猫的无限游戏
大猫的无限游戏
Help Net Security
Help Net Security
Google DeepMind News
Google DeepMind News
S
Securelist
Application and Cybersecurity Blog
Application and Cybersecurity Blog
P
Proofpoint News Feed
博客园 - 三生石上(FineUI控件)
雷峰网
雷峰网
L
LangChain Blog
SecWiki News
SecWiki News
博客园 - 叶小钗
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
V2EX - 技术
V2EX - 技术
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
J
Java Code Geeks
L
LINUX DO - 热门话题
Cisco Talos Blog
Cisco Talos Blog

卡瓦邦噶!

服务器高性能网络调优 | 卡瓦邦噶! 为何写作 | 卡瓦邦噶! 读《金阁寺》 | 卡瓦邦噶! 雨季又来 | 卡瓦邦噶! MTU Probe 引起的初始延迟 | 卡瓦邦噶! 3.5 秒的固定延迟问题 | 卡瓦邦噶! 学习网络的一点经验 | 卡瓦邦噶! ARP 问题诊断 | 卡瓦邦噶! 网络断断续续…… | 卡瓦邦噶! Piccolo P2P 镜像分发 | 卡瓦邦噶! 一起看电影 | 卡瓦邦噶! 《征服C指针》 | 卡瓦邦噶! 我的姥姥 | 卡瓦邦噶! WordPress网站迁移方案 Python的哲学 Python 3.5的新特性 学校不教的计算机课 垃圾回收(GC)的三种基本方式 在编程中体验纯粹的快乐 从《美丽新世界》谈自由 在快钱实习 迷人的嗓音和迷人的故事——《Sleepyhead》 Python 的十个自然语言处理工具 记一个愚蠢的bug 一年炉石传说的游戏体验 《以撒的结合:重生》网页版图鉴 分清 C++的指针、引用和数组 笑话三则 自由比皇帝更伟大——《悲惨世界》笔记 Git 10 周年访谈:Linus 讲述背后故事 用 0x3f3f3f3f 设定最大int值的优点 Joel给计算机系学生的建议 一个词法分析器的简单实现 怎样才算健康的生活方式 MacVim 配置攻略 学习培训课程的视频效果好吗? 语言的控制 可悲的大多数 CSS样式思维导图 2014年终总结 奥巴马成为首位写程序的美国总统 青岛老城区的下水道好在哪里? 做优秀 UI 的七个建议(第二部分) 选择爱情的骑士 Git简明教程 Java集合总览 读 《1984》 java问答:终极父类(六)——等待/唤醒和接口 Java 问答:终极父类(五)——toString() Hyperlapse快速视频背后的技术细节 平庸之恶 Java程序员须知的七个日志管理工具 苏州 逛书摊随想 关于考试作弊 你的工作不仅仅是编程 推荐在线学习Java的英文资源 关注女性命运——《千禧年三部曲》 用好你的幻灯片——《演说之禅》 使用ReentrantLock和Lambda表达式让同步更纯净 新手学编程,从哪里开始? 程序员都是工程师吗? 不要学习代码,要学会思考 Java 问答:终极父类(四)——hashCode() Java 问答:终极父类(三)——finalize()和 getClass() 程序员职业之路的选择 我是一名摄影家 写给何小树的城市指南 为什么一些语言会比别的快? Java的常见误区与细节 跟朋友在一起玩游戏 死神永生——读《三体》 五种类型的程序员 创业圣经——读《黑客与画家》 纪念加西亚·马尔克斯 Junit中处理异常的另一种方式:catch-exception Java8采用Martin Fowler的方法创建内部DSL Linux HotSopt虚拟机GC线程的CPU占用率 J2EE概念介绍 如何成为一名黑客 为什么我喜欢Java Java 问答:终极父类(一)——clone()方法 七个改变世界的Java项目 java中默认类型转换的小问题 传统与创新 欢迎来到互联网 莫言和马尔克斯——读《生死疲劳》 位运算的妙用 读《人为什么活着》 写博客教会我的事情 中国特色操作系统 2013年总结 《永不妥协》影评 恨不相逢未嫁时——《廊桥遗梦》影评 如何优雅地使用PPT 给明年依然年轻的我们 天才与柱子 黑客守则和黑客精神 Looking for Freedom——《被解救的姜戈》 简洁之道
Java 问答:终极父类(二)——equals()方法
laixintao · 2014-03-27 · via 卡瓦邦噶!

Equality

问:euqals()函数是用来做什么的?

答:equals()函数可以用来检查一个对象与调用这个equals()的这个对象是否相等。

问:为什么不用“==”运算符来判断两个对象是否相等呢?

答:虽然“==”运算符可以比较两个数据是否相等,但是要来比较对象的话,恐怕达不到预期的结果。就是说,“==”通过是否引用了同一个对象来判断两个对象是否相等,这被称为“引用相等”。这个运算符不能通过比较两个对象的内容来判断它们是不是逻辑上的相等。

问:使用Object类的equals()方法可以用来做什么样的对比?

答:Object类默认的eqauls()函数进行比较的依据是:调用它的对象和传入的对象的引用是否相等。也就是说,默认的equals()进行的是引用比较。如果两个引用是相同的,equals()函数返回true;否则,返回false.

问:覆盖equals()函数的时候要遵守那些规则?

答:覆盖equals()函数的时候需要遵守的规则在Oracle官方的文档中都有申明:

  • 自反性:对于任意非空的引用值x,x.equals(x)返回值为真。
  • 对称性:对于任意非空的引用值x和y,x.equals(y)必须和y.equals(x)返回相同的结果。
  • 传递性:对于任意的非空引用值x,y和z,如果x.equals(y)返回真,y.equals(z)返回真,那么x.equals(z)也必须返回真。
  • 一致性:对于任意非空的引用值x和y,无论调用x.equals(y)多少次,都要返回相同的结果。在比较的过程中,对象中的数据不能被修改。
  • 对于任意的非空引用值x,x.equals(null)必须返回假。

问:能提供一个正确覆盖equals()的示例吗?

答:当然,请看代码清单8。

代码清单8:对两个对象进行逻辑比较

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

class Employee

{

   private String name;

   private int age;

   Employee(String name, int age)

   {

      this.name = name;

      this.age = age;

   }

   @Override

   public boolean equals(Object o)

   {

      if (!(o instanceof Employee))

         return false;

      Employee e = (Employee) o;

      return e.getName().equals(name) && e.getAge() == age;

   }

   String getName()

   {

      return name;

   }

   int getAge()

   {

      return age;

   }

}

public class EqualityDemo

{

   public static void main(String[] args)

   {

      Employee e1 = new Employee("John Doe", 29);

      Employee e2 = new Employee("Jane Doe", 33);

      Employee e3 = new Employee("John Doe", 29);

      Employee e4 = new Employee("John Doe", 27+2);

      // 验证自反性。

      System.out.printf("Demonstrating reflexivity...%n%n");

      System.out.printf("e1.equals(e1): %b%n", e1.equals(e1));

      // 验证对称性。

      System.out.printf("%nDemonstrating symmetry...%n%n");

      System.out.printf("e1.equals(e2): %b%n", e1.equals(e2));

      System.out.printf("e2.equals(e1): %b%n", e2.equals(e1));

      System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));

      System.out.printf("e3.equals(e1): %b%n", e3.equals(e1));

      System.out.printf("e2.equals(e3): %b%n", e2.equals(e3));

      System.out.printf("e3.equals(e2): %b%n", e3.equals(e2));

      // 验证传递性。

      System.out.printf("%nDemonstrating transitivity...%n%n");

      System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));

      System.out.printf("e3.equals(e4): %b%n", e3.equals(e4));

      System.out.printf("e1.equals(e4): %b%n", e1.equals(e4));

      // 验证一致性。

      System.out.printf("%nDemonstrating consistency...%n%n");

      for (int i = 0; i < 5; i++)

      {

         System.out.printf("e1.equals(e2): %b%n", e1.equals(e2));

         System.out.printf("e1.equals(e3): %b%n", e1.equals(e3));

      }

      // 验证传入非空集合时,返回值为false。

      System.out.printf("%nDemonstrating null check...%n%n");

      System.out.printf("e1.equals(null): %b%n", e1.equals(null));

   }

}

代码清单8声明了一个包含名字、年龄成员变量的Employee对象。这个对象覆盖了equals()函数来对Employee对象进行适当的对比。

ps:覆盖hashCode()函数
当覆盖equals()函数的时候,就相当于覆盖了hashCode()函数,我将在下篇文章讨论hashCode()的时候详细说明。

equals()函数首先要检查传入的确实是一个Employee对象。如果不是,返回false。这个检查是靠instanceof运算来判断 的,当传入null值的时候,同样也返回false。因此,遵守了“对于任意的非空引用值x,x.equals(null)必须返回假”这个规则。

这样,我们就保证了传入的对象是Employee类型。因为之前的instanceof判断保证了传入值必须是Employee类型的对象,所以在 这里我们就不必担心抛出ClassCastException异常了。接下来,equals()方法对两个对象的name和age的值进行了比较。

编译(javac EqualityDemo.java)并运行(java EqualityDemo)代码清单8,你可以看到以下输出结果:

代码清单8运行结果

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

Demonstrating reflexivity...

e1.equals(e1): true

Demonstrating symmetry...

e1.equals(e2): false

e2.equals(e1): false

e1.equals(e3): true

e3.equals(e1): true

e2.equals(e3): false

e3.equals(e2): false

Demonstrating transitivity...

e1.equals(e3): true

e3.equals(e4): true

e1.equals(e4): true

Demonstrating consistency...

e1.equals(e2): false

e1.equals(e3): true

e1.equals(e2): false

e1.equals(e3): true

e1.equals(e2): false

e1.equals(e3): true

e1.equals(e2): false

e1.equals(e3): true

e1.equals(e2): false

e1.equals(e3): true

Demonstrating null check...

e1.equals(null): false

equals()和继承

当Employee类被继承的时候,代码清单8就存在一些问题。例如,SaleRep类继承了Employee类,这个类中也有基于字符串类型的变 量,equals()可以对其进行比较。假设你创建的Employee对象和SaleRep对象都有相同的“名字”和“年龄”。但是,SaleRep中还 是添加了一些内容。

假设你在Employee对象中调用equals()方法并且传入了一个SaleRep对象。由于SaleRep对象继承了Employee,也是一种 Employee的对象,instanceof判断会通过,并且执行equals()方法来判断名字和年龄。因为这两个对象有完全相同的名字和年龄,所以 equals()方法返回true。如果拿SaleRep对象中Employee的部分来和Employee比较的话,返回true值是正确的,但是,如 果拿整个SaleRep对象来和Employee对象比较,返回true值就不妥了。

现在假设在SaleRep对象中调用equals()方法并将Employee传入。因为Employee不是SaleRep类型的对象(否则的话,你可 以访问Employee对象中不存在的Region域,这会导致虚拟机崩溃),无法通过instanceof判断,equals()方法返回false。 综上,equals()在一种判断中为true却在另一判断中为false,违背了“对称性原则”。

Joshua Bloch在《Effective Java Programming Language Guide》第七版中指出:我们无法扩展可被实例化的类(例如Employee)并向其中增加一个域(如Region域),而同时维持equals()方 法的对称性。尽管也有办法来维持对称性,但代价是破坏传递性。Bloch指出解决这个问题需要在继承上支持组合:不是让SaleRep来扩展 Employee,SaleRep应该引用一个私有的Employee值。获得更多信息可以参考Bloch的书。

问:可以使用equals()函数来判断两个数组是否相等吗?

答:可以调用equals()函数来比较数组的引用是否相等。但是,由于在数组对象中无法覆盖equals(),所以只能对数组的引用进行比较,因为不是很常用。参见代码清单9。

代码清单9:尝试通过equals()函数来比较两个数组

Java

1

2

3

4

5

6

7

8

9

10

public class EqualityDemo

{

   public static void main(String[] args)

   {

  int x[] = { 1, 2, 3 };

  int y[] = { 1, 2, 3 };

  System.out.printf("x.equals(x): %b%n", x.equals(x));

  System.out.printf("x.equals(y): %b%n", x.equals(y));

   }

}

代码清单9的main()函数中声明了一对类型与内容完全相等的数组。然后尝试对第一个数组和它自己、第一个数组和第二个数组分别进行比较。由于 equals()对数组来说比较的仅仅是引用,而不比较内容,所以x.equals(x)返回true(因为自反性——一个对象与它自己相等),但是 x.equals(y)返回false。

编译(javac EqualityDemo.java) 并运行(java EqualityDemo)代码清单9,你将会看到以下输出结果:

代码清单9运行结果

Java

1

2

x.equals(x): true

x.equals(y): false

如果你想要比较的是两个数组的内容,也不要绝望。 可以使用java.util.Arrays 类中声明的 static boolean deepEquals(Object[] a1, Object[] a2) 方法来实现。代码清单10演示了这个方法。

代码清单10:通过deepEquals()函数来比较两个数组

Java

1

2

3

4

5

6

7

8

9

10

11

12

13

import java.util.Arrays;

public class EqualityDemo

{

   public static void main(String[] args)

   {

  Integer x[] = { 1, 2, 3 };

  Integer y[] = { 1, 2, 3 };

  Integer z[] = { 3, 2, 1 };

  System.out.printf("x.equals(x): %b%n", Arrays.deepEquals(x, x));

  System.out.printf("x.equals(y): %b%n", Arrays.deepEquals(x, y));

  System.out.printf("x.equals(z): %b%n", Arrays.deepEquals(x, z));

   }

}

由于deepEquals()方法要求传入的数组元素必须是对象,所以之前在代码清单9中的元素类型要从int[]改为Integer[]。 Java语言的自动封装特性会把integer常量转换成Integer对象存放在数组中。接下来要将数组传入到deepEquals()就是小事一桩 了。

编译(javac EqualityDemo.java)并运行(java EqualityDemo)代码清单10,你将看到以下输出结果。

代码清单10运行结果

Java

1

2

3

x.equals(x): true

x.equals(y): true

x.equals(z): false

用deepEquals()方法比较的相等是“深度”的相等:这要求每个元素对象所包含的的成员、对象相等。成员对象如果还包含了对象,也要相等, 以此类推,才算是“相等”(另外,两个空的数组引用也是“深度”的相等,因此Arrays.deepEquals(null, null)返回true)。

下期预告:

在第二部分,我将讨论finalize()方法和析构的话题。我也会讨论getClass()方法并指出获得java.lang.Class对象的几种方式,哈希码和hashCode()方法。

英文原文由首次发表在Java World,译文由赖信涛 首次发表在Importnew