Java equals,hashCode,toString方法

equals方法

  Object类中的equals方法用于检测对象是否相等,Object 类中equals方法:

1
2
3
public boolean equals(Object obj) {
return (this == obj);
}

  可以看到这里是比较两个对象是否有相同的引用,当然两个对象具有相同的引用,它们一定相等。然而对于大多数情况,这种判断没有太多意义,或者说过于严格。我们可以看下书中Employee类的equals方法:

equals01

  getClass方法返回对象所属类,注意到这里如果两个对象所属与的类不一样就返回false,但是如果对于由同一个抽象类继承出的类,比如AbstractSet类中的equals方法,TreeSet和HashSet都实现了Abstract类,但是如果AbstractSet中用getClass检测,就可能不相等。我们看下AbastractSet源码中饰如何实现的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof Set))
return false;
Collection c = (Collection) o;
if (c.size() != size())
return false;
try {
return containsAll(c);
} catch (ClassCastException unused) {
return false;
} catch (NullPointerException unused) {
return false;
}
}

  这里使用了instanceof检测是否属于Set类及其子类,当然instanceof也存在一定问题,就是没有解决子类问题。因此总结一下,equals中使用getClass还是instanceof判断的问题:

  1. 如果子类有自己相等的概念,就使用getClass检测
  2. 如果超类决定相等概念使用instanceof,因为要兼容子类对象比较

  因此本质上就是看子类是否有特定的equals语义来决定用哪一个。可以再来看下Object类中的hashCode方法,是个native方法,原因可以参考stackoverflow的回答

hashCode方法

  先来看看String类中的hashCode方法实现:

1
2
3
4
5
6
7
8
9
10
11
12
public int hashCode() {
int h = hash;
if (h == 0 && value.length > 0) {
char val[] = value;
for (int i = 0; i < value.length; i++) {
h = 31 * h + val[i];
}
hash = h;
}
return h;
}

  可以看出String类的hashCode基于字符内容计算的,可以再看看equals方法也是基于字符比较的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String) anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}

  因为equals方法和hashCode方法定义必须已知,也就是equals返回true,hashCode方法就必须有相同的值,因此如果重新定义equals方法,就必须重新定义hashCode方法。

toString方法

  toString方法用于返回表示对象值的字符串,绝大多数的toString方法遵循这样格式:类的名字,随后是一对方括号扩起来的域值。记住只要对象与一个字符串通过操作符+连接起来,就会自动调用地toString方法,或者通过调用System.out.println(obj)