企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
[TOC] ## 覆请equals遵守规则 Object中equals方法的实现仅仅是比较了两个对象的地址,对于某些类来说正是所需用的、毋需复写的 equals方法的复写需要满足以下通用约定 * 自反性:对于任何非空引用值 x,x.equals(x) 都应返回 true,就是自己和自己比较必须相等。 * 对称性:对于任何非空引用值 x 和 y,当且仅当 y.equals(x) 返回 true 时,x.equals(y) 才应返回 true,就是x若等于y,那么y也应该等于x。 * 传递递性:对于任何非空引用值 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。 如无必要不要复写equals 方法,如果复写了此方法一定要记得复写hashCode方法,因为两个对象相等,它们的hashCode也要相等,下面是equals方法的常用步骤 ~~~ @Override public boolean equals(Object o) { //判断引用是否相等 if (o == this) { return true; } //判断参数类型是否正确 如果o为null也会返回false //这里判断的是class类型,也有可能是接口类型,这样就允许实现这个接口的类之间进行比较 //AbstractSet,AbstractList,AbstracMap的equals方法这一步都是比较的接口 if (!(o instanceof PhoneNumber)) { return false; } //类型转换 // AbstractSet的类型转换 Collection<?> c = (Collection<?>) o; PhoneNumber pNum = (PhoneNumber) o; // 判断重要字段的相等,如果使用的是接口,调用接口的方法获取字段 // 对于基本类型 如果不是float或double 直接使用==比较 // float使用Float.compare(float, float), 原因参考testFloat方法 //double使用Double.compare(double, double) 同上 //Float.equals和Double的equals都设计autobox,影响性能 //引用类型继续调用其equals方法 // 上述方法也同样适用于数组元素,如果要比较整个数值,使用Arrays.equals对应的方法 //对象的某些字段能为Null,为了避免NPE,使用Objects.equals(Object, Object) return this.linNum == pNum.linNum &&this.areaCode == pNum.areaCode &&this.prefix == pNum.this.prefix; ~~~ ## 覆盖equals总是覆盖hashCode 上文说到如果复写equals方法一定要复写hashCode方法。下面说说hash值的计算 确保与equals中使用的字段一致 * 如果字段是基本类型,使用包装类计算hash值如Float.hashCode(f) * 如果字段是引用类型,并且在equals方法中递归调用去equals方法,那么这里也递归调用其hashCode方法 * 如果字段是数组类型,对其中重要元素的hash计算上述方法同样使用,如果要计算整个数组的hash值,使用Arrays.hashCode(array) * 质素31的选取是个传统,能尽量让不同对象拥有不同hash值,即分布均匀, * Objects.hash(linNum,prefix,areaCode)方法简便,但涉及可变数组的创建和拆装箱操作,性能敏感 此方法返回的值不应该有详细规范,如String的hashCode方法返回精确值就是一个失误 ## ==和equals和hashCode 基本数据类型:“==”比较的是其值 类:“==”比较数据内存地址是否相同 “equals”是Object方法,在子类未重写方法时 ~~~ public boolean equals(Object obj) { return (this == obj); } ~~~ hashCode返回的是对象在内存中地址转换成的一个int值,所以如果没有重写 hashCode()方法,任何对象的hashCode()方法都是不相等的。 ## 始终要覆盖toString 尽量复写toString方法,虽然不及equals和hashCode方法必要,但良好的类描述将能提供充分和友好的信息。 ## 谨慎覆盖clone 那么在java语言中,有几种方式可以创建对象呢?  * 使用new操作符创建一个对象。 * 使用clone方法复制一个对象。 如果一个class 实现了Cloneable接口 那么它应该 提供一个public clone方法 * 这是一个毋需构造器就能创建对象的方法 * 注意:这种方式复制对象容易出错而且复杂,难以维护 仅仅在对基本类型数组的复制是可取的 * 这个方法是个浅拷贝,也就是字段到字段的复制,如果都是基本类型,那将是一步到位的, * 但如果还有引用类型,它们指向的对象不会被拷贝,而仅仅拷贝了引用,这就会导致拷贝后的对象和被拷贝的对象不是相互独立的,这些引用指向了相同的对象,也就是任何一方的修改都在另一方得到体现 * 如果要深度拷贝,可以每个引用类型都需要实现cloneable接口和clone方法 * 在实际中要实现对象拷贝,并不建议使用clone方法,而建议采用静态工厂或构造器方式提供复制操作 ## comparable * 如果此方法返回0那么equals应该返回true,如果不是一定要说明不一致性 * HashSet依赖equals比较元素是否重复,TreeSet依赖compareTo给元素排序 * 不要使用来比较大小,对浮点有例外(基本类型的OK的),也不要使用减号,会有溢出 ## 推荐阅读 [深入理解 Java Object](https://www.jianshu.com/p/4222abe52c12) 《Effective Java》第二章