一:覆盖equals时请遵守通用约定
一直觉着equals挺实用也很简单,今天发现我大错特错了,包括以前的代码存在着很大的问题,以后使用equals必须要谨慎对待,因为此处带来的问题会。
1. 关于原则:
à不保证equals传入的是与自己相同的类的对象,一定要保证两个对象的equals返回相同的值,因此我们平时编程的时候才可以从不关注equals的参数是哪个。
à要保证传递性,比如子类的equals一定保证能正确的输出与父类对象比对的结果。且equal相互传递。可以考虑的实现比如抽象父类,复合优先于继承。
àequals比较不可依赖不可靠的资源。如IP地址,Android中sdcard中的配置文件,web services上的某些信息。
à通过每次操作都使用instanceof过滤保证不会equals(null)返回true。
2. 关于经验:
à使用==操作符检查“参数是否为这个对象的引用”。
à使用instanceof操作符检查“参数是否为正确类型”。
à把参数转换成正确的类型。
à对于该类中的每个“关键(significant)”域,检查参数中的域是否与该对象中对应的域相匹配。
à当你编写完一个equals方法后,应该问自己三个问题:它是否是对称的、传递的、一致的?
3. 同时还有几个告诫:
à覆盖equals方法总要覆盖hashCode;
à不要企图让equals方法过于智能;
à不要将equals声明中的Object对象替换为其他的类型(最好加上@Override来确保覆盖)。
因为这个部分跟以前的认识出入很大,这里举个例子来修改一下实现。
从csdn上找到一个例子,这里不给出链接了,并不是为了批判。博主的访问量很多,也只是为了说明equal的用法,这可能可以更好的说明现在很多认识与我一样略有小菜成分。这里如果只是简单的理解equals会遇到很多问题,这里先看代码:
public class Person{
private String name;
private int age;
public Person(String name,int age){
this.name=name;
this.age=age;
}
public boolean equals(Object obj){
Person per1=this;
Person per2=(Person) obj;
boolean flag=false;
if(per1.name.equals(per2.name)&&per1.age==per2.age){
flag=true;
}
return flag;
}
public String toString(){
return "姓名:"+name+"/t年龄:"+age;
}
}
public class TestEquals{
public static void main(String[] args){
Person p1=new Person("c++",30);
Person p2=new Person("Java",15);
System.out.println(p1);
System.out.println(p2);
String out=p1.equals("sdff") ? "p1和p2是同一个人":"p1和p2不是同一个人";
System.out.println(out);
}
}
不涉及代码效率和编码习惯的问题,通过刚才提到的很容易发现其中的问题,很明显传入不同类型的obj程序就会报错java.lang.ClassCastException;传入null报错NullPointer。如果我们要添加一个子类Worker,存在一个workid,首先直观的实现方法:
public boolean equals(Object obj){
boolean flag=false;
if(obj instanceof Worker){
if(super.equals(obj)){
Worker worker = (Worker)obj;
if(worker.workid==workid){
flag = true;
}
}
}
return flag;
}
现在这里很好的具备传递性,但存在一个基本问题,对于一个person.equals(worker)返回true时,worker.equals(person)返回false,这里就提到了刚才说过的反身性,我们在使用equals不关注参数是哪个。这里就需要解决这个问题。
依然使用最直观的思考,当传入Person时我们应该仅仅比较name和age。
public boolean equals(Object obj){
boolean flag=false;
if(obj instanceof Worker){
if(super.equals(obj)){
Worker worker = (Worker)obj;
if(worker.workid==workid)
flag = true;
}
}else if(obj instanceof Person)
flag = super.equals(obj);
}
return flag;
}
这样又无奈的引出了传递性问题,两个worker1、worker2,id为1和2,name和age都与一个person一样,那么worker1.equals(person),person.equals(worker2)都是返回true,但是wroker1.equals(worker2)返回false。你会发现这是一个很两难的问题。只针对这个例子的情景而言,在实现的时候equals其实就有了一个保证,person依照name和age可以完全确定,相当于数据库中的主键,但是这里就要保证同一个person如果他是worker就必须有一样的workerid,这样就具备了传递性,就这个场景而言这是最合理的处理方法,但如果没有意识到这个问题,不管使用了上面哪段代码实际都会遇到意想不到的问题,很有可能导致系统关键部分的逻辑错误,包括GUI或内核。针对普通的情况effective java中给了一些推荐,对一般的方式都可以考虑去使用,比如书中提到的Point类,就存在这种问题。
这里选择了一种推荐的方式来解决当前问题,即复合优先于继承。
这一步要修改Worker类,不让Worker继承自Person,而是采取复合的方式
public class Worker {
private Person person;
private int workid;
public Worker(String name, int age, int workid) {
person = new Person(name, age);
this.workid=workid;
}
public Person asPerson(){
return person;
}
public boolean equals(Object obj){
boolean flag=false;
if(obj instanceof Worker){
Worker worker = (Worker)obj;
if(worker.person.equals(this.person) && worker.workid==this.workid)
flag = true;
}
return flag;
}
}
这样自然就没有子类父类的问题,Worker仅仅是Worker,与Person不是同一种类型,Worker可能在工厂中使用,Person可能在社会中使用。如果要作为一种Person要使用Worker.asPerson来进入社会角色,这算是一种不错的折中方法。
分享到:
相关推荐
- Item8:equals方法应与`==`操作符一致,同时对所有字段进行比较。注意,不要忘记比较类的`this`引用。 - Item9:当覆盖equals时,必须同时覆盖hashcode,以保持哈希表的行为一致性。 - Item10:toString方法应...
以下是对《Effective Java》笔记中可能涉及的关键知识点的详细解读: 1. **单例模式**:书中强调了如何正确实现单例模式,推荐使用`enum`来创建线程安全且唯一的实例,避免传统双重检查锁定的潜在问题。 2. **构造...
### 第三章 对所有对象都通用的方法 这部分主要关注Java中的基本对象行为: 1. **equals方法**:正确覆写`equals`以满足等价关系,同时要遵循一致性和传递性原则。 2. **hashCode方法**:当覆写`equals`时,必须同时...
《Effective Java》是Java...以上仅是《Effective Java》一书中部分核心知识点的概述,实际的读书笔记中会更详细地解释这些概念,并给出具体的示例代码。通过深入学习和实践,开发者可以极大地提升其Java编程的水平。
通过提供getter和setter方法(访问方法),我们可以控制对对象状态的访问和修改,增加逻辑验证,甚至改变域的实现而不影响调用者。此外,访问方法还可以提供额外的功能,如缓存计算结果、记录日志等。这种设计模式也...
- **面向对象**:Java是一种纯粹的面向对象的语言,几乎所有的数据都是以对象的形式来表示。 - **平台无关性**:通过Java虚拟机(JVM),使得Java程序能够在多种平台上运行。 - **安全性**:Java具有强大的安全机制,...
### Thinking in Java 读书笔记知识点总结 #### 一、万事万物皆对象 1. **对象存储位置** - **寄存器**:程序无法直接控制。 - **栈(Stack)**:存储基本类型数据和对象引用,但对象本身不在此处。 - **堆(Heap)...
### Java自学之路——超详细含练习项目及源码 #### Java知识体系最强总结 本篇文章旨在根据提供的文件信息,深入解读Java自学之路的关键知识点,并针对其中提到的学习资源进行详细的解析,帮助初学者更好地掌握...
### Java中equals方法隐藏的陷阱 在Java编程中,正确实现`equals`方法至关重要,它不仅影响对象的比较逻辑,还直接关系到集合类(如`HashSet`、`HashMap`等)的行为。本文将深入探讨Java中`equals`方法的一些常见...
《Java程序员面试宝典——2012版》是一份针对Java程序员面试的全面指南,旨在帮助准备面试的程序员深入理解和掌握Java的核心概念和技术。以下是对该文档标题、描述及部分内容涉及的重要知识点的详细解析: ### Java...
在Java编程语言中,了解如何正确使用`==`和`equals()`方法是非常关键的,因为它们在比较对象和基本类型时有不同的行为。下面将详细解释这两个方法的工作原理、使用场景以及一些常见误区。 首先,`==`运算符主要用于...
在Java编程语言中,`hashCode()`和`equals()`方法是非常重要的概念,它们不仅对于深入理解Java内存管理至关重要,也是实现自定义类的关键部分之一。本文将详细介绍这两个方法的工作原理、使用场景以及它们之间的关系...
在Java编程语言中,`equals()` 和 `hashCode()` 方法是Object类中定义的基本方法,所有类都默认继承自Object类,因此每个Java对象都有这两个方法。这两个方法在处理集合类,尤其是Set接口的实现(如HashSet)时起着...
在 Java 中,equals 方法是一个非常重要的方法,它用于判断两个对象是否相等,而不是判断两个对象的引用是否相同。在 Object 基类中,equals 方法的实现是使用“==”操作符来比较对象的引用,但是这并不满足实际需求...
本文介绍了Java语言不直接支持关联数组,可以使用任何对象作为一个索引的数组,但在根Object类中使用 hashCode()方法明确表示期望广泛使用HashMap。理想情况下基于散列的容器提供有效插入和有效检索;直接在对象模式...
《Effective Java》是Java开发领域的一本经典著作,由Joshua Bloch撰写,书中提出了一系列编程最佳实践和设计模式,帮助开发者写出更高效、更可靠、更易于维护的Java代码。配套代码`effective-java-examples-master`...
- `equals()` 和 `==` 的区别在于: `==` 比较的是对象引用,而 `equals()` 方法通常用于比较对象的内容是否相同。 #### 二、高级主题 - **Oracle 面试题**: 包括 SQL 查询优化、索引使用、事务管理等内容。 - **...