Object超类
在Java中,如果一个类没有明确地指出超类,那么Object就是这个类的超类。实际上,Object类是所有类超类,这个类定义了一些重要的方法。
equals #
equals
方法用来比较两个对象是否相等。不过,在Object类中,这是判断两个对象是否具有相同的引用。
当对象引用a
和b
指向同一个对象即认为a
等于b
,看起来,这似乎合乎情理,但是在很多情况下,需要比较对象的状态的相等性,所以,Object类的equals
方法往往是没有什么用处的。
Java语言规范要求equals
方法具有以下特性:
自反性
:对于任何非空引用x
,x.equals(x)
为true
;对称性
:对于任何引用x
、y
,当且仅当y.equals(x)
为true
时,x.equals(y)
才为true
;传递性
:对于任何引用x
、y
、z
,若x.equals(y)
为true
,y.equals(z)
为true
,那么x.equals(z)
也应该为true
;一致性
:对于任何引用x
、y
,若对象引用和equals
方法未发生变化,那么多次调用应返回一致的结果;- 对于任何非空引用
x
,x.equals(null)
为false
。
参考之前
Employee类的equals
方法:
1class Employee {
2 // skip...
3 @Override
4 public boolean equals(Object o) {
5 if (this == o)
6 return true;
7 if (o == null || getClass() != o.getClass())
8 return false;
9 Employee employee2 = (Employee) o;
10 return Objects.equals(name, employee2.name)
11 && salary == employee2.salary;
12 }
这是一个典型的覆盖equals
方法的策略:
- 检测
o
和this
是否同一引用,若是,则返回true
- 检测
o
是否为空,若为空,则返回false
- 检测
o
和this
是否是同一类型,若否,则返回false
- 将
o
转换为this
类 - 比较
o
和this
域的相等性
在导出类中调用equals方法时,要先调用基类的equals方法,基类的方法通过之后,再比较导出类的相关域的相等性。
参考如下例子:
1class ExpClass extends Employee{
2 // skip...
3 @Override
4 public boolean equals(Object o) {
5 if(!super.equals(o)) return false;
6
7 // super.equals checked that o and this belong to same class
8 ExpClass m = (ExpClass)o;
9 return bonus == m.bonus;
10 }
11}
以上的2个equals
方法说明的是若导出类拥有自己的相等概念,那么在第3步类型判断中必须使用getClass
,这样,基类和导出类(或者不同导出类)之前必然是不等的。
考虑一种情况:若想在基类和导出类之间(或不同导出类之间)进行相等比较,那么只需要比较基类共有的域即可,即是否相等由基类判断,该如何处理呢?
参考如下例子:
1public class EqualsT {
2
3 public static void main(String[] args) {
4 Stu a = new Stu("ali", 18, "190410");
5 StuM b = new StuM("ali", 18, "190410", "班长");
6 System.out.println(a.equals(b));
7 System.out.println(b.equals(a));
8 }
9}
10
11class Stu {
12 protected String name;
13 protected int age;
14 protected String code;
15
16 public Stu(String name, int age, String code) {
17 this.name = name;
18 this.age = age;
19 this.code = code;
20 }
21
22 @Override
23 public final boolean equals(Object o) {
24 if (this == o)
25 return true;
26 if (o == null)
27 return false;
28 if (!(o instanceof Stu))
29 return false;
30
31 Stu stu = (Stu) o;
32
33 if (age != stu.age)
34 return false;
35 if (name != null ? !name.equals(stu.name) : stu.name != null)
36 return false;
37 return code != null ? code.equals(stu.code) : stu.code == null;
38 }
39}
40
41class StuM extends Stu {
42 private String resp;
43
44 public StuM(String name, int age, String code, String resp) {
45 super(name, age, code);
46 this.age = age;
47 this.code = code;
48 this.name = name;
49 this.resp = resp;
50 }
51
52 // 导出类无法覆盖equals()方法
53}
54
55/*
56 * output:
57 * true
58 * true
59 */// :~
这个equals方法有几处变化:
- 这个方法是
final
的 - 判断类型使用的是
instanceof
而非getClass
将方法声明为final
即保证了基类对相等概念的控制权(导出类无法覆盖equals方法),这还不够,使用instanceof
保证了基类和导出类(或不同导出类)之间域的相等性比较的可能
应该谨慎使用
instanceof
判断操作符号。
hashCode #
hash code (散列码)是由对象导出的一个整型值
Java语言对hashCode方法有如下规范:
- 在同一次Java程序运行过程中,无论调用多少次对象的hashCode方法,返回的应该是同一个整型值;而在不同程序运行过程中则无此要求;
- 对于任何对象
a
、b
,若a.equals(b)
为true
,那么a
和b
的hashCode返回值应该相等; - 对于任何对象
a
、b
,若a.equals(b)
为false
,a
和b
的hashCode返回值没有必要一定不等;需要指出的是,给不同对象分配不同de hashCode值有利于提升哈希表的性能;
基于第2点规范,若重新定义了equals
方法,那么必须重新定义hashCode
方法。
1public class HashT {
2 public static void main(String[] args) {
3 String s = "s";
4 String t = new String("s");
5 StringBuilder sb = new StringBuilder(s);
6 StringBuilder tb = new StringBuilder(t);
7 System.out.println(s.hashCode() + " : " +sb.hashCode());
8 System.out.println(t.hashCode() + " : " +tb.hashCode());
9 }
10}
11
12/* output
13115 : 1956725890
14115 : 356573597
15*///:~
注意,s
和t
哈希值相同时因为String类覆盖了hashCode方法,其值是由字符串字面量值计算来的,而StringBuilder没有覆盖hashCode方法,其值是Object默认的hashCode方法导出的对象存储地址。
参考: 再论Object超类