`
zhaohaolin
  • 浏览: 1011303 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Java:所有的equals方法实现都是错误的【转】

    博客分类:
  • JAVA
阅读更多

【51CTO快译】本文介绍了一种改写(override)equals 方法的技巧。使用该技巧,即使在实体类的子类添加了新的域(field)时,仍然能够满足 equals 方法的约定。

在《Effective Java》一书的第 8 条目中,Josh Bloch 将子类化时满足 equals 约定这一困难描述为:面向对象语言中等值关系的最根本问题。Bloch 这样写道:

不存在一种方式,能够在扩展非实例类并添加值组件的同时,仍然满足equals的约定。除非你愿意放弃面向对象的抽象性这一优点。

《Programming in Scala》一书中的第 28 章提供了一种方法,子类可以对非实例类进行扩展,添加值组件,而同时满足 equals 约定。虽然书中提供的那种技巧是用于定义 Scala 类,但一样适用于 Java 中的 类定义。在本文中,为了讲解这种方法,我将使用《Programming in Scala》中相关章节,改编相关的文本,并将原书中的 Scala 示例代码转换为了 Java 代码。

常见的等值陷阱

Class java.lang.Object 定义了一个 equals 方法,其中的子类可以进行改写(override)。不幸的是,最终的结果表明,在面向对象语言中,编写正确的等值方法相当困难。事实上,在对 Java 代码的大量正文进行研究之后,几位作者在 2007 年的一份论文中作出如下结论:几乎所有 equals 方法的实现都是错误的。

这是一个严重的问题,因为等值方法是很多代码的根本。其一,对于类型 C,一个错误的等值方法可能意味着,你不能可靠地将一个类型 C 的对象放入集合中。你可能有两个等值的类型 C 元素 elem1、elem2,即“em1.equals(elem2)”输出 true。然而,在下面的示例中,equals 方法的实现就是一种常见的错误:

  1. Set< C> hashSet =  new  java.util.HashSet< C>();  
  2. hashSet.add(elem1);  
  3. hashSet.contains(elem2);  // 返回 false!  

存在四种常见的陷阱,它们都会在改写equals时导致非一致性的行为:

◆使用错误的原型对equals进行定义。

◆更改equals而未同时更改 hashCode。

◆对equals进行定义时涉及可变域(field)。

◆未能成功地将equals定义为等值关系。

这四种陷阱将在下文中具体讲述。

陷阱 1:使用错误的原型对equals进行定义

在下面的代码中,我们将为普通点的类添加一个等值方法:

  1. public   class  Point {  
  2.  
  3.   private   final   int  x;  
  4.   private   final   int  y;  
  5.  
  6.   public  Point( int  x,  int  y) {  
  7.   this .x = x;  
  8.   this .y = y;  
  9.  }  
  10.  
  11.   public   int  getX() {  
  12.   return  x;  
  13.  }  
  14.  
  15.   public   int  getY() {  
  16.   return  y;  
  17.  }  
  18.  
  19.   // ...  

一个显而易见的错误定义如下:

  1. // 一个完全错误的 equals 定义  
  2. public   boolean  equals(Point other) {  
  3.   return  ( this .getX() == other.getX() &&  this .getY() == other.getY());  

这种方法的错误之处是什么?初一看,它可以正常运行:

  1. Point p1 =  new  Point( 1 2 );  
  2. Point p2 =  new  Point( 1 2 );  
  3.  
  4. Point q =  new  Point( 2 3 );  
  5.  
  6. System.out.println(p1.equals(p2));  // prints true  
  7.  
  8. System.out.println(p1.equals(q));  // 输出 false  

然而,一旦你将point放入集合中,问题就出来了:

  1. import  java.util.HashSet;  
  2.  
  3. HashSet< Point> coll =  new  HashSet< Point>();  
  4. coll.add(p1);  
  5.  
  6. System.out.println(coll.contains(p2));  // 输出 false  

coll 怎么可能不包含 p2 呢?你已经将 p1 添加到其中,而 p1 等于 p2。在下面的互操作中,进行比较的点的具体类型被隐藏,这时,导致问题的原因将清晰可见。将 p2a 定义为 p2 的别名,但使用的是 Object 类型而不是 Point:

  1. Object p2a = p2; 

现在,如果你重复第一个比较,使用别名 p2a 而不是 p2,结果是:

  1. System.out.println(p1.equals(p2a));  // 输出 false  

哪里出错了呢?事实上,由于类型不同,之前指定的 equals 版本并没有改写标准方法 equals。下面是在根类 Object 中定义的 equals 方法:

  1. public   boolean  equals(Object other) 

由于 Point 中的 equals 方法使用 Point 而不是 Object 作为参数,因此,它并未对 Object 中的 equals 进行改写。相反,它只是一种重载的替代方法。Java 中重载由参数的静态类型解析,而不是运行时(run-time)类型。因此,只要参数的静态类型是 Point,就调用 Point 中的 equals 方法。同样,如果静态参数是 Object 类型,就调用 Object 中的 equals 方法。该方法没有被改写,因此在对 object 参数进行比较时,仍使用该方法。这就是“p1.equals(p2a)”输出 false 的原因,即使点 p1 和 p2a 具有相同的 x 和 y 值。这也是为什么在 HashSet 中 contains 方法返回 false 的原因。该方法是针对对常规集合进行操作,因此它会调用 Object 中的常规 equals 方法,而不是 Point 中重载的方法变种。

下面的代码定义了一个更好的equals方法:

  1. // 一个更好的定义,但仍不是完美的  
  2. @Override   public   boolean  equals(Object other) {  
  3.   boolean  result =  false ;  
  4.   if  (other  instanceof  Point) {  
  5.  Point that = (Point) other;  
  6.  result = ( this .getX() == that.getX() &&  this .getY() == that.getY());  
  7.  }  
  8.   return  result;  

现在,equals 具有了正确的类型。它将 Object 类型的值作为参数并输出一个 boolean 结果。该方法的实现使用了 instanceof 和 cast(类型转换)。它首先检测其他(other)对象是否为 Point 类型。如果是,它将对这 2 个点的坐标进行比较,然后返回结果。否则,输出为 false。

陷阱2 :更改equals而未同时更改hashCode

如果你使用 Point 的最新定义,再次对 p1和 p2a 进行比较,将会得到期望中的结果:true。但是,如果你重复 HashSet.contains 测试,结果仍可能是 false:

  1. Point p1 =  new  Point( 1 2 );  
  2. Point p2 =  new  Point( 1 2 );  
  3.  
  4. HashSet< Point> coll =  new  HashSet< Point>();  
  5. coll.add(p1);  
  6.  
  7. System.out.println(coll.contains(p2));  // (很可能)输出 false  

事实上,输出结果不是百分百确定。你也可能从测试中得到true值。如果得到的结果是true,你可以试试另外一些坐标为1和2的点。最终,你将会找到一个未包含在集合中的点。这里出现错误的原因是,Point重定义了equals而没有对hashCode进行重定义。

请注意,上述实例中的集合为HashSet。这表示,集合中元素被放在由相应的散列码决定的哈希桶(hash bucket)中。在contains测试中,它首先查找散列桶,然后对哈希桶中的所有元素和指定元素进行比较。现在,Point类的最新版本确实对 equals进行了重定义,但它没有同时对hashCode进行重定义。所以 hashCode 仍然保持 Object 类中其版本的值:分配对象地址的某种变化格式。p1 和 p2 的散列码几乎肯定是不同,即使这两个点的域(field)是相同的。不同的散列码意味着集合中散列桶具有较高概率的非重复性。contains 测试将根据 p2 的散列码在相应的散列桶中查找匹配的元素。大多数情况下,点 p1 会位于另一个散列桶中,因此绝不会找到它。p1 和 p2 有可能很偶然地位于同一散列桶中。对于这种情况,测试将返回ture 值。

问题在于,Point 的上次实现违法了Object 类中定义的hashCode约定:

如果两个对象根据equals(Object) 方法是等值的,那么对两个对象中任何一个调用 hashCode 方法都必须得到相同的整型结果。

事实上,在Java中,通常应同时对 hashCode 和equals进行重定义,这一事实是广为人知的。此外,hashCode 可能仅依赖equals所依赖的域。对于 Point 类,以下将是一个合适的 hashCode 定义:

  1. public   class  Point {  
  2.  
  3.   private   final   int  x;  
  4.   private   final   int  y;  
  5.  
  6.   public  Point( int  x,  int  y) {  
  7.   this .x = x;  
  8.   this .y = y;  
  9.  }  
  10.  
  11.   public   int  getX() {  
  12.   return  x;  
  13.  }  
  14.  
  15.   public   int  getY() {  
  16.   return  y;  
  17.  }  
  18.  
  19.   @Override   public   boolean  equals(Object other) {  
  20.   boolean  result =  false ;  
  21.   if  (other  instanceof  Point) {  
  22.  Point that = (Point) other;  
  23.  result = ( this .getX() == that.getX() &&  this .getY() == that.getY());  
  24.  }  
  25.   return  result;  
  26.  }  
  27.  
  28.   @Override   public   int  hashCode() {  
  29.   return  ( 41  * ( 41  + getX()) + getY());  
  30.  }  

这只是 hashCode 多种可能的实现中的一种。将常量 41 加到一个整型域 x 上,所得结果再乘以素数 41,然后在加上另一个整型域 y。这样就可以提供合理分布的散列码,而运行时间和代码大小也会降低。

在定义与 Point 相似的类时,添加 hashCode 解决了等值的问题。但是,还有其他的问题需要注意。

陷阱 3 :对equals进行定义时涉及可变域

以下对 Point 类进行一项细微的修改:

  1. public   class  Point {   
  2.  
  3.   private   int  x;  
  4.   private   int  y;  
  5.  
  6.   public  Point( int  x,  int  y) {  
  7.   this .x = x;  
  8.   this .y = y;  
  9.  }  
  10.  
  11.   public   int  getX() {  
  12.   return  x;  
  13.  }  
  14.  
  15.   public   int  getY() {  
  16.   return  y;  
  17.  }  
  18.  
  19.   public   void  setX( int  x) {  
  20.   this .x = x;  
  21.  }  
  22.  
  23.   public   void  setY( int  y) {  
  24.   this .y = y;  
  25.  }  
  26.  
  27.   @Override   public   boolean  equals(Object other) {  
  28.   boolean  result =  false ;  
  29.   if  (other  instanceof  Point) {  
  30.  Point that = (Point) other;  
  31.  result = ( this .getX() == that.getX() &&  this .getY() == that.getY());  
  32.  }  
  33.   return  result;  
  34.  }  
  35.  
  36.   @Override   public   int  hashCode() {  
  37.   return  ( 41  * ( 41  + getX()) + getY());  
  38.  }  

唯一的不同之处是域 x 和 y 不再是 final 类型,同时添加了两个集合方法,允许用户更改 x 和 y 值。现在,equals和 hashCode 方法的定义涉及了这些可变域,因此域更改时它们的结果也将改变。一旦你将点放入集合中,这会带来很奇怪的效果:

  1. Point p =  new  Point( 1 2 );  
  2.  
  3. HashSet< Point> coll =  new  HashSet< Point>();  
  4. coll.add(p);  
  5.  
  6. System.out.println(coll.contains(p));  // 输出 true  

现在,如果更改点 p 中的域,集合还将包含该点吗? 我们来试试下面的代码:

  1. p.setX(p.getX() +  1 );  
  2.  
  3. System.out.println(coll.contains(p));  // (很可能)输出 false   

这看起来很奇怪。p 到哪里去了?如果你对集合的 iterator 是否包含 p 进行测试,会得到各位奇怪的结果:

  1. Iterator< Point> it = coll.iterator();  
  2. boolean  containedP =  false ;  
  3. while  (it.hasNext()) {  
  4.  Point nextP = it.next();  
  5.   if  (nextP.equals(p)) {  
  6.  containedP =  true ;  
  7.   break ;  
  8.  }  
  9. }  
  10.  
  11. System.out.println(containedP);  // 输出 true  

此处的集合不包含 p,但 p 却在该集合的元素之中!发生了什么事呢?在更改 x 域之后,点 p 最后被放在了该集合 coll 下错误的散列桶中。也就是,其初始散列桶与散列码的新值已不再对应。在某种意义上可以说,点 p 在集合 coll 中消失了,即使它仍然是集合中元素。

从这个示例得出的教训就是,当equals和 hashCode 取决于可变状态时,可能会为用户带来问题。如果他们将这种对象放入集合中,必须小心,不要修改决定性的状态。而这是很棘手的。如果你现在需要进行一个比 较,要考虑到对象的当前状态,通常不应直接使用 equals,而是使用其他命名。 对于 Point 的上一个定义,更为可取的是省略 hashCode 的重定义,并且命名比较方法 equalContents,或者使用其他不同于equals的命名。 这样,Point 将能够继承equals和 hashCode 的缺省实现。

陷阱 4:未能成功地将equals定义为等值关系

Object 中equals的约定指出 equals 必须实现非空对象的等值关系:

◆自反性 :对于如何非空值 x,表达式 x.equals(x) 应返回true。

◆对称性 :对于任何非空值:x 和 y,x.equals(y) 应返回true,当且仅当 y.equals(x) 返回 true。

◆传递性 :对于任何非空值 x、y、z,如果 x.equals(y)返回 true 并且 y.equals(z) 返回 true,那么x.equals(z) 应返回 true。

◆一致性 :对于任何非空值:x 和 y,多次调用 x.equals(y)应始终返回 true 或始终返回 false,如果对象的equals比较中所用信息未被修改。

◆对于任何非空值 x,x.equals(null) 应返回 false。

目前,对于 Point 类所使用的equals定义满足了equals的约定。然而,一旦涉及子类,事情将变得更加复杂。比如说,Point 有一个子类 ColoredPoint,其中添加了一个 Color 类型的域 color。假定将 Color 定义为枚举类型:

  1. public   enum  Color {  
  2.  RED, ORANGE, YELLOW, GREEN, BLUE, INDIGO, VIOLET;  

ColoredPoint 改写 equals,并添加新的 color 域:

  1. public   class  ColoredPoint  extends  Point {  // 问题:equals 不对称  
  2.  
  3.   private   final  Color color;  
  4.  
  5.   public  ColoredPoint( int  x,  int  y, Color color) {  
  6.   super (x, y);  
  7.   this .color = color;  
  8.  }  
  9.  
  10.   @Override   public   boolean  equals(Object other) {  
  11.   boolean  result =  false ;  
  12.   if  (other  instanceof  ColoredPoint) {  
  13.  ColoredPoint that = (ColoredPoint) other;  
  14.  result = ( this .color.equals(that.color) &&  super .equals(that));  
  15.  }  
  16.   return  result;  
  17.  }  

很多程序员都可能这样编写代码。请注意,在这种情况下,类 ColoredPoint 不必改写 hashCode。因为 ColoredPoint 的equals的新定义比 Point 中被改写的定义更为严格(意味着等值的对象更少),hashCode 的约定仍然有效。 如果两个颜色点是相等的,它们必须具有相同的坐标,因此也已保证它们的散列码是相等的。

以类 ColoredPoint 本身为例,equals的定义看起来没问题。但,一旦普通点和颜色点混合着一起时,equals的约定就将被破坏。 例如:

  1. Point p =  new  Point( 1 2 );  
  2.  
  3. ColoredPoint cp =  new  ColoredPoint( 1 2 , Color.RED);  
  4.  
  5. System.out.println(p.equals(cp));  // 输出 true  
  6.  
  7. System.out.println(cp.equals(p));  // 输出 false  

相等比较“pequalscp”将调用 p 的equals方法,已在类 Point 中定义。该方法仅考虑两个点的坐标。因此,相等比较输出 true 值。而另一方面,相等比较“cp equals p”调用 cp 的 equals 方法,其中类 ColoredPoint 中已定义。该方法返回 false,因为 p 不是 ColoredPoint。该方法返回 false,因为 p 不是 ColoredPoint。 因此,equals定义的关系不是对称的。

对称的缺失将为集合造成意想不到的后果。下面为一个示例:

  1. Set< Point> hashSet1 =  new  java.util.HashSet< Point>();  
  2. hashSet1.add(p);  
  3. System.out.println(hashSet1.contains(cp));  // 输出 false  
  4.  
  5. Set< Point> hashSet2 =  new  java.util.HashSet< Point>();  
  6. hashSet2.add(cp);  
  7. System.out.println(hashSet2.contains(p));  // 输出 true  

因此,即使 p 和 cp 是相等的,一个 contains 测试成功,而另一个却失败。

如何更改equals的 定义可以让它变为对称?基本上有两种方式。您可以使关系更一般或更严格。使它更加一般意味着对两个对象,a 和 b 被认为是相等的,如果比较 a 和 b 或 b 和 a 输出 true。以下为完成该功能的代码:

  1. public   class  ColoredPoint  extends  Point {  // 有问题:equals 不具有传递性  
  2.  
  3.   private   final  Color color;  
  4.  
  5.   public  ColoredPoint( int  x,  int  y, Color color) {  
  6.   super (x, y);  
  7.   this .color = color;  
  8.  }  
  9.  
  10.   @Override   public   boolean  equals(Object other) {  
  11.   boolean  result =  false ;  
  12.   if  (other  instanceof  ColoredPoint) {  
  13.  ColoredPoint that = (ColoredPoint) other;  
  14.  result = ( this .color.equals(that.color) &&  super .equals(that));  
  15.  }  
  16.   else   if  (other  instanceof  Point) {  
  17.  Point that = (Point) other;  
  18.  result = that.equals( this );  
  19.  }  
  20.   return  result;  
  21.  }  

在 ColoredPoint 中,equals的新定义比旧版本多了一种情况的检查:如果其他对象是 Point 而不是 ColoredPoint,该方法将使用 Point 的equals方法。这样就可以取得预期的效果,使equals具有对称性。现在,“cp.equals(p)”和“p.equals(cp)”都返回 true。 然而,equals的约定还是被打破了。现在的问题是,新的关系不再具有传递性!为了演示这个问题,下面进行一系列的声明。定义一个点和两个不同色的颜色 点,所有点在同一位置:

  1. ColoredPoint redP =  new  ColoredPoint( 1 2 , Color.RED);  
  2. ColoredPoint blueP =  new  ColoredPoint( 1 2 , Color.BLUE); 

单独来看,redp 等于 p 并且 p 等于 bluep:

  1. System.out.println(redP.equals(p));  // 输出 true  
  2.  
  3. System.out.println(p.equals(blueP));  // 输出 true  

然而,比较 redP 和 blueP,输出 false:

  1. System.out.println(redP.equals(blueP));  // 输出 false  

因此,这违反了equals约定中的传递性子条款。

让equals关系更一般看来是死路一条。下面我们试试让它更严格。使equals更严格的一个方法是:不同类的对象,不同地对待。通过修改类 Point 和 ColoredPoint 中的equals方法来实现。 在类 Point 中,可以添加一个额外的比较,用于检查其他点的运行时类是否与这个点的类相同,代码如下:

  1. // 技术上有效,但仍不能令人满意的 equals 方法  
  2. public   class  Point {  
  3.  
  4.   private   final   int  x;  
  5.   private   final   int  y;  
  6.  
  7.   public  Point( int  x,  int  y) {  
  8.   this .x = x;  
  9.   this .y = y;  
  10.  }  
  11.  
  12.   public   int  getX() {  
  13.   return  x;  
  14.  }  
  15.  
  16.   public   int  getY() {  
  17.   return  y;  
  18.  }  
  19.  
  20.   @Override   public   boolean  equals(Object other) {  
  21.   boolean  result =  false ;  
  22.   if  (other  instanceof  Point) {  
  23.  Point that = (Point) other;  
  24.  result = ( this .getX() == that.getX() &&  this .getY() == that.getY()  
  25.  &&  this .getClass().equals(that.getClass()));  
  26.  }  
  27.   return  result;  
  28.  }  
  29.  
  30.   @Override   public   int  hashCode() {  
  31.   return  ( 41  * ( 41  + getX()) + getY());  
  32.  }  

然后,你就可以将类 ColoredPoint 的实现恢复为之前违反了对称性要求的版本:

  1. public   class  ColoredPoint  extends  Point {  // 不再违反平衡性要求  
  2.  
  3.   private   final  Color color;  
  4.  
  5.   public  ColoredPoint( int  x,  int  y, Color color) {  
  6.   super (x, y);  
  7.   this .color = color;  
  8.  }  
  9.  
  10.   @Override   public   boolean  equals(Object other) {  
  11.   boolean  result =  false ;  
  12.   if  (other  instanceof  ColoredPoint) {  
  13.  ColoredPoint that = (ColoredPoint) other;  
  14.  result = ( this .color.equals(that.color) &&  super .equals(that));  
  15.  }  
  16.   return  result;  
  17.  }  

在这里,类 Point 的实例被认为与系统类的其他实例是相等的,仅当对象具有相同的坐标,并且具有相同的运行时类,即每个对象 .getClass() 返回相同的值。 新的定义可以满足对象性和传递性的要求,因为现在对不同类之间的每次对比都将返回 false。因此,颜色点永远不会与普通点相等。这种约定看起来是合理的,当有人会指出新的定义太过严格了。

下面使用稍微有点绕的方式定义位于坐标(1, 2)上的点:

  1. Point pAnon =  new  Point( 1 1 ) {  
  2.   @Override   public   int  getY() {  
  3.   return   2 ;  
  4.  }  
  5. }; 

pAnon 等于 p 吗?答案是否定的,因为与 p 和 pAnon 关联的 java.lang.Class 对象是不同的。对于 p 是 Point 类,而对于 pAnon,它是 Point 的一个匿名子类。 但显然,pAnon 只是位于坐标(1, 2)上的另一个点。 认为它与 p 不同,看起来并不合理。

明智的方式:canEqual 方法

从以上各种情况,看起来我们进退两难。是否存在一种明智的方式,在类层次结构的多个分层中对等值比较进行重定义,而同时满足其约定?事实上,有这样 一种方式,但它需要另一个方法来重定义equals和 hashCode。这个想法是,只要类重定义 equals(和 hashCode),它就应该同时显式地声明,该类的所有对象与使用不同等值方法的超类中的对象,绝对不会相等。通过对重定义equals的每个类添加方 法 canEqual 就可以实现。以下为该方法的原型:

  1. public   boolean  canEqual(Object other) 

当其他(other)对象是(重)定义了 canEqual 的类的实例时,该方法应返回 true,或者返回 false。它从equals中调用,以确保这些对象使用2种方式都是可比较的。下面是类 Point 新的也是最后的一个实现:

  1. public   class  Point {  
  2.  
  3.   private   final   int  x;  
  4.   private   final   int  y;  
  5.  
  6.   public  Point( int  x,  int  y) {  
  7.   this .x = x;  
  8.   this .y = y;  
  9.  }  
  10.  
  11.   public   int  getX() {  
  12.   return  x;  
  13.  }  
  14.  
  15.   public   int  getY() {  
  16.   return  y;  
  17.  }  
  18.  
  19.   @Override   public   boolean  equals(Object other) {  
  20.   boolean  result =  false ;  
  21.   if  (other  instanceof  Point) {  
  22.  Point that = (Point) other;  
  23.  result = (that.canEqual( this ) &&  this .getX() == that.getX() &&  this .getY() == that.getY());  
  24.  }  
  25.   return
    分享到:
    评论

相关推荐

    Java中equals方法隐藏的陷阱

    在Java编程中,正确实现`equals`方法至关重要,它不仅影响对象的比较逻辑,还直接关系到集合类(如`HashSet`、`HashMap`等)的行为。本文将深入探讨Java中`equals`方法的一些常见陷阱,并提供相应的解决方案。 ####...

    java中hashcode()和equals()方法详解

    在Java编程语言中,`hashCode()`和`equals()`方法是非常重要的概念,它们不仅对于深入理解Java内存管理至关重要,也是实现自定义类的关键部分之一。本文将详细介绍这两个方法的工作原理、使用场景以及它们之间的关系...

    Java中的==和equals区别

    `equals`方法是`Object`类的一个成员方法,用于比较两个对象的内容是否相等。默认情况下,`equals`方法的行为与`==`类似,即比较的是对象的引用。然而,在许多情况下,特别是对于自定义类或Java标准类库中的类(如`...

    Java编程中避免equals方法的隐藏陷阱介绍

    事实上,研究表明,几乎所有的equals方法的实现都是错误的!这个问题是因为等价是和很多其他的事物相关联。例如,一个类型C的错误等价方法可能意味着你无法将这个类型C的对象可信赖的放入到容器中。 equals方法的...

    java中equals()函数的用法 equals和==的区别

    而`equals()`方法是Object类中的一个成员方法,它的默认行为也是比较两个对象的引用是否相等,与`==`一致。但是,很多类(如String、Integer等)都重写了`equals()`方法,以便进行更深层次的内容比较。例如,String...

    浅谈java 重写equals方法的种种坑

    Java 中的 equals 方法是一种用于比较对象是否相等的方法,它是 Object 类中的一个方法。然而,重写 equals 方法并不是一件简单的事情,因为它需要遵守一些约定,否则可能会出现一些不可预期的行为。在这篇文章中,...

    Java-JDK、数据库系统开发、Web开发学习笔记

    1. Object类的所有方法及功能:Object类是Java中的顶层类,所有类实际上都继承了Object类的所有方法。Object类提供了以下方法: * protected Object clone():用于获得此对象的一个拷贝 * public Boolean equals...

    java集合——Java中的equals和hashCode方法详解

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是Object类中定义的基本方法,所有类都默认继承自Object类,因此每个Java对象都有这两个方法。这两个方法在处理集合类,尤其是Set接口的实现(如HashSet)时起着...

    重写equals和hashcode方法_equals_重写equals和hashcode方法_

    在Java编程语言中,`equals()` 和 `hashCode()` 方法是Object类中的两个核心方法,所有类都默认继承自Object类。这两个方法在处理对象比较和集合操作时起着至关重要的作用。当我们创建自定义类并需要对对象进行精确...

    关于Object中equals方法和hashCode方法判断的分析

    equals 方法是用于比较两个对象是否相同的。它的默认实现是比较两个对象的引用是否相同,如果是同一个对象,那么 equals 方法将返回 true,否则返回 false。但是,如果我们想要比较两个对象的实际内容是否相同,那么...

    java中hashcode()和equals()的详解.docx

    在Java编程语言中,`hashCode()`和`equals()`方法是非常重要的概念,它们主要用于处理对象的唯一标识和对象之间的相等性判断。正确地实现这两个方法对于确保程序的高效运行至关重要。 #### `hashCode()`方法解析 `...

    java equals函数用法详解

    在Java编程语言中,`equals()` 方法是一个关键的成员函数,它主要用于比较对象的值是否相等。这个方法源自于 `Object` 类,是所有其他类的基类。默认情况下,`equals()` 方法的行为与 `==` 运算符相同,即比较两个...

    java笔记整理(超详细) java笔记整理(超详细)

    - 接口只定义方法签名,不允许有方法实现(Java 8后可以有默认方法),支持多继承。 - 抽象类可以有方法实现,只能单继承,提供部分实现,作为子类的基础。 9. **变量类型**: - 成员变量属于类,存在于堆内存,...

    java易犯错误.pdf

    然而,在Java中,并非所有的方法都可以被覆盖,特别是静态方法。 **1.1 方法覆盖的概念** 方法覆盖是指子类重写父类中的方法,以便提供不同的实现细节。在Java中,只有实例方法可以被覆盖,而静态方法则不行。这是...

    超详细_解释java_equals()与hashCode().pdf

    总之,理解并正确实现`equals()`和`hashCode()`方法是Java编程中的基础技能,特别是在设计和使用自定义对象时,这关系到对象的正确比较和哈希表的高效操作。遵循Java的约定并确保这两个方法的一致性,可以确保代码的...

    Java十大低级错误.doc

    此外,正确实现equals和hashCode方法对于满足Java集合框架的期望至关重要。 9. **转换异常处理**:将字符串转换为数字时,应处理可能的NumberFormatException,确保代码在遇到无效输入时能优雅地处理。 10. **日志...

    Java中equals与==的用法和区别

    equals() 方法是 Java 中的一个方法,它用于比较两个对象的内容是否相等。equals() 方法是 Object 类中的一个方法,该方法的初始行为是比较对象的内存地址,但是可以被子类重写以比较对象的成员变量值是否相同。 ...

    java中equals和==的区别.pdf

    首先,`equals()`方法是`java.lang.Object`类的一个成员方法。它的默认实现是基于对象的引用比较,也就是说,它会检查两个对象在内存中的地址是否相同,这与`==`运算符的行为一致。然而,许多类,特别是像`String`...

    Java中Equals使用方法汇总

    在Java编程中,`equals`方法是用于比较两个对象是否相等的重要方法。通常,`equals`方法在类中被重写以实现特定的比较逻辑,因为默认的`equals`行为(即`Object`类中的实现)仅仅基于引用的相等性,即比较两个对象...

Global site tag (gtag.js) - Google Analytics