import java.util.*;
public class OverrideObjectMethod {
public static void main(String[] args) {
Map<PhoneNumberBad, String> map = new HashMap<PhoneNumberBad, String>();
map.put(new PhoneNumberBad(110, 86, 1351), "chenzq");
//Object的hashCode实现返回对象的引用地址,所以equals相等的2个对象会返回不一样的hashCode
String result = map.get(new PhoneNumberBad(110, 86, 1351));
System.out.println(result); //null
Map<PhoneNumber, String> mp = new HashMap<PhoneNumber, String>();
mp.put(new PhoneNumber(110, 86, 1351), "chenzq");
result = mp.get(new PhoneNumber(110, 86, 1351));
System.out.println(result); //chenzq
System.out.println(new PhoneNumber(110, 86, 1351));
PhoneNumber[] arr = new PhoneNumber[5];
arr[0] = new PhoneNumber(110, 86, 1351);
arr[1] = new PhoneNumber(110, 87, 1351);
arr[2] = new PhoneNumber(110, 87, 1350);
arr[3] = new PhoneNumber(111, 86, 1352);
arr[4] = new PhoneNumber(114, 80, 1354);
Arrays.sort(arr);
for(PhoneNumber pn : arr) {
System.out.println(pn);
}
}
}
class PhoneNumber implements Comparable<PhoneNumber> {
private final int areaCode;
private final int prefix;
private final int lineNumber;
public PhoneNumber(int areaCode, int prefix, int lineNumber) {
this.areaCode = areaCode;
this.prefix = prefix;
this.lineNumber = lineNumber;
}
@Override public int compareTo(PhoneNumber o) {
// Compare area codes
int areaCodeDiff = areaCode - o.areaCode;
if (areaCodeDiff != 0)
return areaCodeDiff;
// Area codes are equal, compare prefixes
int prefixDiff = prefix - o.prefix;
if (prefixDiff != 0)
return prefixDiff;
// Area codes and prefixes are equal, compare line numbers
return lineNumber - o.lineNumber;
}
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumber))
return false;
PhoneNumber pn = (PhoneNumber) o;
return pn.lineNumber == lineNumber
&& pn.prefix == prefix
&& pn.areaCode == areaCode;
}
@Override public int hashCode() {
int result = 17;
result = 31 * result + this.areaCode;
result = 31 * result + this.prefix;
result = 31 * result + this.lineNumber;
return result;
}
@Override public String toString() {
return String.format("(%03d) %03d-%04d", this.areaCode, this.prefix, this.lineNumber);
}
}
class PhoneNumberBad {
private final int areaCode;
private final int prefix;
private final int lineNumber;
public PhoneNumberBad(int areaCode, int prefix, int lineNumber) {
this.areaCode = areaCode;
this.prefix = prefix;
this.lineNumber = lineNumber;
}
@Override public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof PhoneNumberBad))
return false;
PhoneNumberBad pn = (PhoneNumberBad) o;
return pn.lineNumber == lineNumber
&& pn.prefix == prefix
&& pn.areaCode == areaCode;
}
}
输出为:
null
chenzq
(110) 086-1351
(110) 086-1351
(110) 087-1350
(110) 087-1351
(111) 086-1352
(114) 080-1354
* 对于所有对象都通用的方法:
尽管Object是一个具体类,但设计它主要是为了扩展。它所有的非final方法
(equals/hashCode/toString/clone/finalize)都有明确的通用约定。
因为它们被设计成是要被覆盖的。任何一个类在覆盖这些方法时,都有责任遵守这些通用约定。否则
其他依赖于这些约定的类(HashMap/HashSet),就无法结合该类一起正常工作。
* 什么时候应该override equals方法:如果类具有自己特有的“逻辑相等”概念(不同于对象等同),
而且超类还没有覆盖equals以实现期望的行为时,需要覆盖equals。
***************************************************************
* 覆盖equals时请遵守通用约定:
自反型:对于任何非null的引用值x,x.equals(x)必须返回true。
对称性:对于任何非null的引用值x和y,x.equals(y)返回true时,y.equals(x)也必须返回true。
传递性:对于任何非null的引用值x、y和z,x.equals(y)返回true并且y.equals(z)也返回true时,
x.equals(z)也必须返回true。
一致性:对于任何非null的引用值x和y,只要x和y对象所用的比较信息没有修改,多次调用
x.equals(y)的结果应该一致。
非空性:对于任何非null的引用值x,x.equals(null)必须返回false。
null instanceof 任何类型都会返回false,所以不需要判断比较对象是否为null。
* 使用instanceof检查参数是否为正确的类型,并把参数转换为正确的类型。
* 对于非float和double的基本类型使用==进行比较,对象引用field可以递归调用equals比较,
float类型使用Float.compare方法,double使用Double.compare。
* 有些对象field包含null值是合法的,为了避免NullPointerException,使用:
(field == null ? o.field == null : field.equals(o.field))
* 不要企图让equals过于智能。
* 不要将equals声明中的Object替换为其他类型(使用强类型参数,只是重载了equals方法,
并没有覆盖equals方法)。@Override注解可以避免这种错误。
****************************************************************
* 覆盖equals时总要覆盖hashCode:
在每个覆盖了equals的类中也必须覆盖hashCode,否则就会违反Object.hashCode的通用约定,
从而导致该类无法结合所有基于散列的集合正常工作(HashMap/Hashtable/HashSet)。
在应用程序运行期间,只要对象equals方法比较所用到的field信息没有被修改,那么对这一对象
多次调用hashCode必须始终返回同一个整数。
* 如果两个对象调用equals比较是相等的,那么两个对象的hashCode也必须返回相同的整数。
* 如果两个对象调用equals比较是不相等的,那么两个对象的hashCode不一定要返回不同的整数。
但是给不同的对象截然不同的hashCode整数结果有可能提高散列表的性能。
***************************************************************
* 始终要覆盖toString:
toString通用约定指出,被返回的字符串应该是一个“简洁的,但信息丰富,并且易于阅读的表达形式”。
建议所有的子类覆盖toString方法。在实际应用中,toString应该返回所有值得关注的信息。
***************************************************************
* 类实现了Comparable接口,就表明它的实例具有内在的排序关系(natural ordering)。
Arrays.sort(arr) 当该对象小于、等于、大于指定对象的时候,分别返回一个负整数、零、正整数。
* compareTo方法的通用约定:
必须确保所有的x和y都满足,x.compareTo(y) == - (y.compareTo(x))。
必须确保比较关系是可传递的,x.compareTo(y) > 0 && y.compareTo(z) > 0 则 x.compareTo(z) > 0
强烈建议: (x.compareTo(y) == 0) == (x.equals(y))