对于所有对象都通用的方法
No.8 覆盖equals时请遵守通用约定
类具有自己特有的”逻辑相等”概念时应该覆盖Object.equals方法
覆盖equals时,必须要遵守它的通用约定:
- 自反性(reflexive)对于任何非null的引用值x,x.equals(x)必须为true
- 对称性(symmetric)对于任何非null的引用值x和y,当且仅当x.equals(y)为true时,y.equals(x)必须返回true
- 传递性(transitive)对于任何非null的引用值x、y和z,如果x.equals(y)为true,并且y.equals(z)为true,那么x.equals(z)也必须为true
- 一致性(consistent)只要操作对象信息未被修改,多次调用equals结果必须一致
- 非空性(Non-nullity)对于任何非null的引用值x,x.equals(null)必须为false
实现高质量equals方法的诀窍:
- 使用
==
检查“参数是否为这个对象的引用” - 使用
instanceof
检查”参数是否为正确类型” - 把参数转换成正确类型
- 检查参数中的关键(significant)域与该对象中对应的域是否相匹配
- 检查是否达到了对称性、传递性、一致性的要求
- 覆盖equals时总要覆盖hashCode
- 不要企图让equals方法过于智能
- 不要将equals声明中的Object对象替换成其他的类型
- 使用
@Override
注解
No.9 覆盖equals时总要覆盖hashCode
每个覆盖了equals方法的类中,也必须覆盖hashCode方法
约定:
- 应用程序的一次执行中,对同一个对象的调用多次,hashCode方法必须始终如一的返回同一个整数。在同一个程序的多次执行过程中,每次执行锁返回的整数可以不一致
- 如果两个对象的根据equals方法比较是相等的,那么调用它们的hashCode方法的整数结果也必须相等
- 如果两个对象的根据equals方法比较是不相等的,那么调用它们的hashCode方法的整数结果不一定要不相等
推荐散列算法:
int result = 17;
- 为所有的域分别计算
int
类型的散列码(若是对象则调用其hashCode()
方法):boolean
类型:f ? 1 : 0
byte
、char
、short
、int
类型:(int) f
long
类型:(int) (f ^ (f >>> 32))
float
类型:Float.floatToIntBits(f)
double
类型:Double.doubleToLongBits(f)
后按long
类型处理- 引用类型(对象):调用其
hashCode()
方法(若域为null
则为0) - 数组类型:对每个元素当做单独的域处理或者使用
Arrays.hashCode()
方法
- 将每个域计算出的散列码合并:
result = 31 * result + c;
- 返回
result
- 验证该散列算法是否符合约定,“相等的实例是否都具有相等的散列码”
总结:
- 相等的对象具有相等的散列码
- 不相等的对象的散列码不一定不相等
- 不要试图从散列码计算中排除掉一个对象的关键部分来提高性能
- 一个比较好的散列算法:
31*i
,因为31*i == (i << 5) - 1
No.10 始终要覆盖toString
toString 方法旨在返回一个”简洁的,但信息丰富,并且易于阅读的表达形式”,建议所有的子类都覆盖这个方法
- toString 方法应该返回对象中包含的所有值得关注的信息
- 当对象被传递给println、printf、字符串级联操作符(+)、assert、调试器打印等情况时,toString方法会被自动调用
No.11 谨慎地覆盖clone
- 所有实现了Cloneable接口的类都应该用一个公有的方法覆盖clone,此公有方法1首先应该调用
super.clone()
,然后修正任何需要修正的域。 - 其他接口都不应该扩展(extends)这个接口(Cloneable)
- 为了继承而设计的类也不应该实现(implement)这个接口(Cloneable)
No.12 考虑实现Comparable接口
- 对实现了Comparable接口的对象数组a进行排序只需要
Arrays.sort(a);
- 如果在编写一个值类,它具有非常明显的内在排序关系,那么应该坚决考虑实现这个接口