Object特性
- 所有类的超类
- 可以引用任何对象
equals方法
Object
类中的equals
方法判断两对象是否有相同的引用
equals的重写
在实际coding中,一般我们都会对equals
方法进行重写,例:
对于超类
Employee
:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22class Employee{
...
public boolean equals(Object otherObject){
//如果otherObject与this是同一个引用
if(this == therObject) return true;
//如果therObject为空
if(therObject == null) return false;
//如果类型不同
if(getClass() != otherObject.getClass()) return false;
//现在我们已经确认了otherObject是一个非空的Employee对象
//为了比较实例域,我们将otherObject转换为Employee类型
Employee other = (Employee) otherObject
//检测实例域是否相等
return name.equals(other.name)
&& salary == other.salary
&& hireDay.equals(other.hireDay);
}
}为了防止
this.name
或this.hireDay
可能为null
的情况,将最后一句改为:1
2
3return Obejcts.equals(name,other.name)
&& salary == other.salary
&& Objects.equals(hireDay,other.hireDay);Obejcts.equals(a,b)
运行过程:- 如果
a
和b
都为null
,则返回true - 如果其中一个为
null
,返回false - 如果都不为
null
,则调用a.equals(b)
- 如果
对于
Employee
的子类Manager
在子类中定义euqals
方法时,要首先调用超类的euqals,然后比较子类中的实例域。
1
2
3
4
5
6
7
8
9
class Manager extends Employee{
...
public boolean equals(Object otherObject){
//先调用超类的equals方法,检验this与otherObject是否属于同一class
if(!super.equals(otherObject)) return false;
Manager other = (Manager) otherObejct;
return bonus == other.bonus
}
}
equals方法的特性
- 自反性:若
x!=null
,则x.equals(x)
应返回true
- 对称性:
y.equals(x)
与x.euqals(y)
应返回同样结果 - 传递性:若
x.equals(y)
返回true
,且y.equals(z)
返回true
,则x.equals(z)
也必须返回true
- 一致性:若x与y的引用对象没有发生变化,则反复调用
x.equals(y)
应返回同样结果 - 对任意非空引用x,则
x.equals(null)
应该返回false
相等测试
问题 如果隐式(this)和显式(传入的)的参数不属于同一类,equals方法如何处理?
解决方法一 在上例中,我们用到了getClass()
:
//如果类型不同
if(getClass() != otherObject.getClass()) return false;
解决方法二 有的程序员喜欢用instanceof
检测: (instanceof 运算符是用来在运行时指出对象是否是特定类的一个实例)
if(!(otherObeject instanceof Employee)) return false;
方法一存在的问题 若e
是一个Employee
对象,m
是一个Manager
对象,且两者具有相同姓名、薪水和雇用日期。
如果Manager
没有重新实现equals
方法,那么当m1.equals(m2)
比较时,就会使用到e.getClass() != m2.getClass()
来进行比较,显然结果返回false
,因此这不是正确的比较。
方法二存在的问题 那么对于e.equals(m)
来说,instanceof
返回true
.(Manager
是Employee
的一个实例) 但对于m.equals(e)
来说,instanceof
返回false
!!!!!违反了对称性!
总结
- 如果子类拥有自己的相等概念,例如若两个
Manager
对象的姓名、薪水和雇用日期(父类Employee的域)均相等,而奖金(子类Manager的域)不相等,就认为两个Manager
不相等。此时可以用getClass
检测 - 如果使用雇员ID(父类Employee的域)来作为相等检测标准,并这个标准适合所有的子类,就可以用
instanceof
检测,并将Employee
的equals
申明为final
完美的equals:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public boolean equals(Object otherObject){
//检测this与otherObject是否引用同一对象
if(this==otherObject)return true;
//检测otherObject是否为null
if(otherObject == null) return false;
//比较this与Object是否属于同一个类,有以下两种方案:
//如果equals的语义在每个子类中都有改变,就用getClass:
if(getClass() != otherObject.getClass()) return false;
//如果所有子类都有统一equals语义,则用instanceof检测:
if(!(otherObject instanceof ClassName)) return false;
//将otherObject转换为相应的类类型
ClassName other = (ClassName) otherObject
//对所需要的域进行比较
return field1 == other.field1
&& field2 == other.field2
&& Objects.equals(field3,other field3)
&& ...;
}
注意:如果在子类中重新定义equals
,则要在其中包含调用super.equals(other)
hashCode方法
hashCode : 散列码
使用:
1
2
3
4
int hash = 0
for ( int i = 0 ; i < lenth() ; i++ ){
hash = 31 * hash + charAt(i)
}
注意
- 每个对象都有一个默认的散列码,值为对象的存储地址
- 字符串的散列码是由内容导出的,因此
s
和t
有相同hashcode - 如果重新定义了equals方法,就必须重新定义hashCode方法,且通过equal测试的两个对象的hashCode应该也是相等的,以便用户可以将对象插入到散列表中
- hashCode方法应该返回一个整型数值,并使得不同对象的hashCode更均匀。
例如:
1
2
3
4
5
6
7
class Employee{
public int hashCode(){
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
}
更方便的方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public int hashCode(){
return Objects.hash(name,salary,hireDay);
}```
# toString方法
用途:返回表示对象值的字符串
例如:
```Java
public String toString(){
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+"]";
}
特别地:
- 只要对象与一个字符串通过操作符“+”连接起来,或使用System.out.println(x),Java编译就会自动调用toString方法。
- Object类定义了toString方法,用来打印输出对象所属的类名和散列码。
- 有趣的是,数组继承了object类的toString方法。修正的方法是采用Array.toString.例如
String s = Array.toString(luckyNumbers)
。多维数组需要调用Array.deepToString
方法。