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))