Java超级父类 Object 的非final方法
- equals
- hashCode
- toString
- clone
- finalize
都有着明确通用的约定 general contract , 因为他们被设计成为 override .
equals
什么情况下覆盖equals方法
- 类的每个实例本子上都是唯一
- 不关心类是否提供逻辑相等的测试功能
- 超类已经覆盖了equals方法,从超类继承过来的行为对于子类也是适用
- 类是私有或包级私有,可以去定它的equals方法永远不会被调用
覆盖equals方法的约定
来自Object 规范 JavaSE6
- 自反性 reflexive . 对象必须与本身相等 .
- 对称性 symmetric
- 传递性 transitive . 如果 ObjectA 等于 ObjectB , ObjectB 等于 ObjectC 那么 ObjectA 等于 ObjectC
- 一致性 consistent . 如果两个对象相等 , 那么始终保持相等 , 知道被修改 .
- 非空性 Non-nullity . 对象不允许为空 .
- equals 方法实现胃等价关系 equivalence relation
- 对于任何非null的引用值 x.equals(null) 必须返回false
高质量equals
- 使用==操作符检测参数是否为这个对象的引用
- 使用instancdof 操作检查参数是否为正确类型
- 把参数转换为正确的类型
- 对于该类中的每个关键域,检查参数中的域是否与该对象对应的域匹配
- 覆盖equals总要覆盖hashCode
- 不要企图然equals过于智能
- 不要将equals声明中的Object 对象替换为其他类型
hashCode
- 在程序的执行期间,只要对象equals方法的比较操作使用到的信息没有被修改,那么对这同一个对象调用多次,hashCode方法都必须始终如一地返回同一个整数.
- 如果两个对象根据equals方法比较是相等的,那么调用这两个方法中的任意一个对象的hashCode方法必须产生同样的整数结果 .
- equals方法比较是不等,那么调用任意对象hashCode,则不一定产生不同整数结果. 给不相等的对象产生截然不同的整数结果,有可能提高散列表 hash table 的性能 .
一个好的散列函数通常倾向于 为不相等的对象产生不相等的散列码(好像是废话),理解情况下,散列函数应该把集合中不相等的的实例均匀的分不到所有可能的散列值上.要是完全达到这种理想的情况是非常困难的 . 完美中的完美总是那么遥不可及 ,不过我们可以接近完美 .
@Override public int hashCode(){
int result = 17 ;
result = 31 * result + 属性1;
result = 31 * result + 属性2;
result = 31 * result + 属性...;
return result ;
}
31它是一个奇素数.如果乘数是偶数,并且乘法益处的话,信息就会丢失,因为与2相乖等价于移位运算.使用素数的好外并不很明显,但是习惯上都使用素数来计算散列结果.31有个很好的特性,即用移位和减法来代替乘法,可以等到更好的性能 31 * i == ( i << 5 ) - i , 现代VM自动完成这样的优化 .
如果一个类不可变的,并且计算散列开销比较大.就应该考虑吧散列缓存在对象内部,而不是每次请求的时候都计算hashCode. 如果觉得这种类型大多对象会被计算hashCode,就应该在建立实例的时候计算hashCode,否则可以用延迟初始化 lazily initialize hashCode , 一直到hashCode 被第一次调用时候才初始化.
lazily initialized cached hashCode
private volatile int hashCode;
@Override public int hashCode(){
int result = hashCode ;
if(result == 0){
result = 17 ;
result = 31 * result + 属性1;
result = 31 * result + 属性2;
result = 31 * result + 属性...;
}
return result ;
}
clone
Object类的clone方法
protected native Object clone() throws CloneNotSupportedException;
注意
如果对象中包含的域引用了可变的对象
public class Stack{
private Object[] elements ; //域引用了可变的对象
private int size = 0 ;
private static final int DEFAULT_INITIAL_CAPACITY = 16 ;
...
}
如果它的clone方法仅仅返回super.clone(),elements域将引用原来Statc实例相同的数组. 修改原始的实例会破坏克隆对象中的约束关系. clone方法就是另一个构造器,你必须确保它不会伤害到原始的对象,并确保正解地创建被克隆对象中的约束条件 invariant
修改后的clone
@Override public Stack clone(){
try{
Stack result = (Stack) super.clone();
result.elemnts = elements.clone();
return result ;
}catch(CloneNotSupportedException e){
throw new AssertionError();
}
}