论坛首页 入门讨论版

java编程时要遵循的一些原则

浏览 965 次
精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
作者 正文
时间:2007-07-07 关键字: java语法
最近看了Effective Java这本书,感觉受益非浅。我把一些原则写了下来,希望能给大家一些帮助.
1.在改写equals的时候请遵守通用约定
在讨论这个问题的时候,先要确定下在什么时候要改写Object.equals.
当一个类有自己特有的“逻辑相等”概念,而且超类也没有改写equals以实现期望的行为,这时我们就
需要改写equals方法了。但在改写equals方法时,必须要遵守对equals的通用约定,否则,我们改写的类
将无法与其他类进行正常的工作,比较List,Map等集合方法,而且这种问题很难排查。
这些通用约定如下
1).自反性:对于任意的引用值x,x.equals(x)一定为true
2).对称性:对于任意的引用值x和y,当且仅当x.equals(y)返回true时,y.equals(x)也一定要返回true
3).传递性:对于任意的引用值x,y和z,如果x.equals(y)返回true,并且y.equals(z)也返回true,那么
x.equals(z)也一定返回true
4).一致性:对于任意的引用值x和y,如果用于equals比较的对象信息没有被修改的话,那么,多次调用x.equals(y)
要么一致地返回true,要么一致地返回false
5).对于任意的非空引用值x,x.equals(null)一定返回false.
这些约定来自于java.lang.Object规范,看了这些规范,是不是感觉到写equals都这么麻烦,还得要逐个看看是否
符合了这些规范,其实,这也并不难解决,只要下在的过程去实现equals方法,这些规范一般都会自然满足.
1)使用==操作符检查“实参是否为指向对象的一个引用”
2)使用instanceof操作符检查“实参是否为正确的类型”
3)把实参转换到正确的类型
4)对于该类中的每一个“关键”域,检查实参中的域与当前对象中对应的域值是否匹配
5)当你改写equals的时候,总是要改写hashCode (这条我会在后面讲到)
6)不要企图让equals方法过于聪明
7)不要使equals方法依赖于不可靠的资源
8)不要将equals声明中的Object对象替换为其他类型,也就是不要将equals(Object o)改为equals(MyClass c)
   
时间:2007-07-07
2.在改写equals的时候总是要改写hashCode (不知道有多少人知道hashCode的作用)
在每个改写了equals方法的类中,必须也要改写hashCode方法。如果不这样的话,就会违反Object.hashCode的通用约定,
这个类就会在所有基于散列值的集合类中无法正常工作,包括HashMap,HashSet和Hashtable
举个例子。
public class MyClass {
private int x;
private int y;
public MyClass() {
}
public MyClass(int x,int y) {
this.x=x;
this.y=y;
}
public boolean equals(Object x) {
if (x==null) {
return false;
}
if (!(x instanceof MyClass)) {
return false;
}
MyClass m = (MyClass)x;
return m.x==this.x && m.y==this.y;
}
}
Map p = new HashMap();
p.put(new MyClass(33,44),"Test");
在这个p中,你想通过p.get(new MyClass(33,44))去得到"Test",但实际上返回的是null,因为这个类没有重载hashCode方法,
虽然他们使用equals方法会得到true,但这两个实现是放在不同的散列桶里的。
如果给这个类重载hashCode方法,则会得到我们想要的结果,代码如下:
public int hashCode() {
return 1;
}
上面的hashCode方法是合法的,但在实际中,是永远也不会使用的,因为这个方法使所有的对象都得到相同的散列值,每个对象
都会被映射到同一个散列桶中,这个散列表就退化为了链表。那如何能够做一个比较好的散列值呢。按以下的方法做,一般都可

以生成比较好的散列值
1.把某个非零常数值,比如17,保存在一个变量里,比如:int temp=17;
2.对于对象中的每个关键值,完成以下步骤
a.计算每个值的散列码c
1)如果该值是boolean类型,则计算(f?0:1)
2)如果是byte,char,short或者int,则计算(int)f;
3)如果是long,则计算(int)(f^(f>>>32))
4)如果是float,则计算Float.floatToIntBits(f);
5)如果是一个对象引用,则同样递归调用对象的hashCode.
6)如果是一个数组,则处理每个元素。
b.按照下面公式,把步骤a中计算到的散列码c组合到结果中
temp = 37*temp+c;


所以上面的类实现hashCode方法如下
public int hashCode() {
int temp=17;
temp = 37*temp+x;
temp = 37*temp+y;
return temp;
}

但在实际中,一些对象的关键值非常多,如果每次调用hashCode()都要经过计算,肯定会费很多资源,如果这样,则可以定义

一个属性值,把计算结果放到这个属性值就可以了。代码如下:
private int hashCode = 0;
public int hashCode() {
if (hashCode==0) {
int temp=17;
temp = 37*temp+x;
temp = 37*temp+y;
hashCode = temp;
}
return hashCode;
}
   
0 请登录后投票
论坛首页 入门讨论版

跳转论坛:
JavaEye推荐