尽管Object是一个具体类,但是设计它主要是为了扩展。它所有的非final方法(equals、hashCode、toString、clone和finalize)都有明确的通用约定,因为它们被设计成是要被覆盖的。
任何一个类,它在覆盖这些方法的时候,都有责任遵守这些通用约定;如果不能做到这一点,其他依赖于这些约定的类(例如:HashMap和HashSet)就无法结合该类一起正常工作。
有一种"值类"不需要覆盖equals方法,即实例受控确保"每个值至多只存在一个对象"的类。枚举类型就属于这种类。
对于这样的类而言,逻辑相同与对象等同是一回事,因此Object的equals方法等同于逻辑意义上的equals方法。
如果类具有自己特有的“逻辑相等”概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为,这时我们需要覆盖equals方法。
这通常属于“值类(value class)”的情形。值类仅仅是一个表示值的类,例如Integer或者Date。
程序员在利用equals方法来比较对象的引用时,希望知道它们在逻辑上是否相等,而不是想了解它们是否指向同一个对象。
为了满足程序员的要求,不仅必须重写equals方法,而且这样做也使得这个类的实例可以被用作映射表的键,或者集合的元素,使映射和集合表现出预期的行为。
什么时候应该覆盖Object.equals
如果类具有自己特有的”逻辑相等“概念(不同于对象等同的概念),而且超类还没有覆盖equals以实现期望的行为。
在覆盖equals方法的时候,你必须要遵守它的通用约定(自反性、对称性、传递性、一致性)。
为什么一定要遵守通用约定,因为其他的程序设计都认为你遵循了通用约定,例如其他程序认为:x.equals(y)跟y.equals(x)是一样的。
如果你没有遵循,其他的程序代码就会有问题。
list.contains(s);
在覆盖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(z)也返回true,那么x.equals(z)也必须返回true。
一致性:对于任何非null的引用值x和y,只要equals的比较操作在对象中所用的信息没有被修改,多次调用x.equals(y)就会一致的返回true/false。
非空性:对于任何非null的引用值x,x.equals(null)必须返回false。(抛异常也不行)
对称性违反的例子
package com.ez.impl; /** * 违反对称性 */ public class CaseIgnoreString { private final String s; public CaseIgnoreString(String s){ if(s==null) throw new NullPointerException(); this.s=s; } @Override public boolean equals(Object obj) { if(obj instanceof CaseIgnoreString){ return s.equalsIgnoreCase(((CaseIgnoreString)obj).s); }else if(obj instanceof String){ return s.equalsIgnoreCase((String)obj); } return false; } public static void main(String[] args) { CaseIgnoreString cs=new CaseIgnoreString("EZbcw"); String str="ezbcw"; System.out.println(cs.equals(str)); System.out.println(str.equals(cs)); } }
假如你把不区分大小写的字符串对象放到一个集合中,此时list.contains(s)会返回什么结果呢?没人知道,在Sun的当前实现中,它碰巧返回false。
public boolean contains(Object o) { return indexOf(o) >= 0; } public int indexOf(Object o) { if (o == null) { for (int i = 0; i < size; i++) if (elementData[i]==null) return i; } else { for (int i = 0; i < size; i++) if (o.equals(elementData[i])) return i; } return -1; }
一旦违反了equals约定,当其他对象面对你的对象时,你完全不知道这些对象的行为会怎么样。
传递性违反的例子
package com.ez.impl; /** * 长方形 */ public class Oblong { private final int width; private final int length; public Oblong(int width,int length){ this.width=width; this.length=length; } @Override public boolean equals(Object obj) { if(!(obj instanceof Oblong)){ return false; } Oblong oblong=(Oblong)obj; return oblong.width==width&&oblong.length==length; } }
/** * 长方体 */ public class Cuboid extends Oblong{ private int height; public Cuboid(int width,int length,int height){ super(width, length); this.height=height; } @Override public boolean equals(Object obj) { if(!(obj instanceof Oblong)){ return false; //不是长方形直接返回false } else if(!(obj instanceof Cuboid)){ return obj.equals(this); //长方形比较长和宽 } return super.equals(obj)&&((Cuboid)obj).height==height; //长方体比较长宽高 } }
在Java平台类库中,有一些类扩展了可实例化的类,并添加了新的值组件。
例如,java.sql.Timestamp对java.util.Date进行了扩展,并增加了nanoseconds域。
Timestamp的equals实现确实违反了对称性,如果Timestamp和Date对象被用于一个集合中,或者以其他方式被混合在一起,则会引起不正确的行为。
Timestamp类有一个免责声明,告诫程序员不要混合使用Date和Timestamp对象。
覆盖equals时总要覆盖hashCode
如果两个对象根据equals(Object)方法比较是相等的,那么调用这两个对象中任意一个对象的hashCode方法都必须产生同样的整数结果。
如果两个对象根据equals(Object)方法比较是不相等的,那么调用这两个对象中任意一个对象的hashCode方法,则不一定产生不同的整数结果。
例子:通过List的removeAll方法,重写类的equals和hashcode方法,来实现
package com.ez.test.mytests; import java.util.ArrayList; import java.util.List; /** * 逻辑上认为User的id相同,就认为是同一个人,因为一个用户可以取多个名字,但是id是唯一的。 * 重写equals为了list去重-apps.removeAll(apps1),重写equals必须同时重写hashcode。 * 使用Eclipse自动生成的hashCode */ public class User { private int id; private String name; public static void main( String[] args ) { List<User> apps=new ArrayList<User>(); User app=new User(); app.setId(1); app.setName("张山"); User app1=new User(); app1.setId(2); app1.setName("李四"); User app2=new User(); app2.setId(3); app2.setName("王五"); apps.add(app); apps.add(app1); apps.add(app2); List<User> apps1=new ArrayList<User>(); User app3=new User(); app3.setId(1); app3.setName("孙六"); User app4=new User(); app4.setId(2); app4.setName("陈七"); apps1.add(app3); apps1.add(app4); apps.removeAll(apps1); System.out.println("apps size="+apps.size()+", id="+apps.get(0).getId()+", name="+apps.get(0).getName()); } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + id; return result; } @Override public boolean equals(Object obj) { if (this == obj) { return true; } if (obj == null) { return false; } if (!(obj instanceof User)) { return false; } User other = (User) obj; if (id != other.id) { return false; } return true; } }
相关推荐
重写equals方法需要遵守通用约定,hashCode方法的实现需要保证equals相同的两个对象的hashCode结果一致。ArrayList、Vector和LinkedList是Java中三种常见的容器集合,每种集合都有其特点和应用场景。
此外,Object 类的设计是为了扩展,它提供了一些非 final 方法,如 equals、hashCode、toString、clone 和 finalize,这些方法都有通用的约定,需要在子类中被覆盖(override)。如果不遵守这些约定,依赖这些约定的...
重写`equals`方法时,应遵循以下通用约定: 1. **自反性**:对于任何对象`x`,`x.equals(x)`应返回`true`。 2. **对称性**:如果`y.equals(x)`返回`true`,那么`x.equals(y)`也应该返回`true`。 3. **传递性**:...
在重写 equals() 方法时,需要遵守通用约定,包括: 1. 自反性:对于任意的引用值 x,x.equals(x) 一定为 true。 2. 对称性:对于任意的引用值 x 和 y,当 x.equals(y) 返回 true 时,y.equals(x) 也一定返回 true...
在Java编程语言中,`equals()`方法和`==`运算符是两个经常用来比较对象的...同时,为了确保其他自定义类的`equals()`方法正确实现,应遵循通用约定,即同时重写`equals()`和`hashCode()`方法,以保持一致性和正确性。
这些方法在设计时考虑到了它们可能会被子类覆盖,因此有着明确的通用约定。正确地理解和使用这些方法对于编写高质量的Java代码至关重要。本文将重点介绍 `equals()` 方法的正确实现方式及其通用约定。 #### 二、`...
8.7.1 Object.Equals 210 8.7.2 Object.GetHashCode 212 8.7.3 Object.ToString 213 8.8 Uri 214 8.9 System.Xml的使用 216 8.10 相等性操作符 218 8.10.1 值类型的相等性操作符 218 8.10.2 引用类型...
8.7.1 Object.Equals 210 8.7.2 Object.GetHashCode 212 8.7.3 Object.ToString 213 8.8 Uri 214 8.9 System.Xml的使用 216 8.10 相等性操作符 218 8.10.1 值类型的相等性操作符 218 8.10.2 引用类型...
- 重写`equals()`方法时,应接受一个`Object`类型的参数。 - 这样的设计可以确保方法的通用性。 #### 内存管理 13. **R13:不使用终结器(finalizer)** - 终结器在某些情况下可能导致不确定的行为和性能问题。 ...
根据给定的文件信息,以下是对“core java...Object是所有Java类的根类,提供了如toString()、equals()、hashCode()等基础方法,用于支持对象的基本操作。理解并适当重写这些方法对于编写高质量的Java代码至关重要。
- 创建通用类时,应包含常用的方法:`equals()`、`hashCode()`、`toString()`、`clone()`(实现`Cloneable`)和`Serializable`接口。 3. **测试代码**: - 为每个类提供一个`main()`方法用于测试,方便检查和验证...
**Java中的Object类**:在Java中,所有类都直接或间接地继承自Object类,它提供了所有Java对象通用的行为,如toString()、equals()和hashCode()等方法。通过继承,我们可以为特定类添加额外的功能,同时保持与Java...
- 重写`equals()`和`hashCode()`方法时,确保满足对象相等性的约定。 - 尽可能使用接口而非具体类进行类型声明,提高代码的灵活性。 【开发EJB规则】 - 遵守Enterprise JavaBeans(EJB)的官方规范,如实体Bean、...
"=="比较的是两个对象的内存地址,而equals()方法用于比较两个对象的内容是否相等。如果两个对象是同一个对象,那么它们的"=="和equals()结果相同。如果equals()未被重写,它默认的行为与"=="相同。 【Java反射机制...
- **Equals()与GetHashCode()**:实现这两个方法时,要确保一致性,以支持哈希表等集合的操作。 - **Clone()**:实现克隆功能时,注意深拷贝与浅拷贝的区别。 - **默认构造器**:每个类都应至少有一个构造器,即使它...
**String类为什么复写Object类的equals方法** - `equals`方法用于比较字符串的内容是否相等。 - 示例代码展示如何使用`equals`方法比较字符串。 #### 七、IO **字节流复制文件** - 使用字节流读取文件并将其写入新...
8. **equals()与hashCode()**:当重写`equals()`方法时,务必也重写`hashCode()`,以保持对象的等价性。同时,这两个方法的行为应符合Object类中的定义。 9. **final**:在不打算修改的变量前使用`final`关键字,...
- **Object类**:所有Java类的基类,提供了toString()、equals()等通用方法。 - **内部类**:定义在另一个类内部的类,分为成员内部类、局部内部类、静态内部类和匿名内部类。 - **集合**:Java集合框架,包括List、...