java:Object类

Object类是java所有类的始祖,在java中每个类都是由它扩展而来的,但是并不需要这样写:

public class Employee extends Object

如果没有明确的指出超类,Object就被认为是这个类的超类。

我们可以使用Object类型的变量引用任何类型的对象:

Object obj=new Employee("ykc",300000);

当然,Object类型的变量只能用于作为各种值的通用持有者。要想对其中的内容进行具体的操作,还需要清楚对象的原始类型,并进行相应的类型转换:

Employee e=(Employee)obj; 

在java中,只有基本类型不是对象,例如:数值、字符和布尔类型的值都不是对象。

所有的数组类型,不管是对象数组还是基本类型的数组都扩展了Object类。

1.equals方法

Object类中的equals方法用于检测一个对象是否等于另一个对象,在Object类中,这个方法将判断两个对象是否具有相同的引用。如果两个对象具有相同的引用,他们一定是相等的。

列如,如果两个雇员对象的姓名、薪水和雇佣日期都一样样,就认为他们是相等的(在实际的雇员数据库中,比较ID更有意义)。

利用下面这个实例演示equals方法的实现机制:

public class Employee{
	...
	public boolean equals(Object otherObject) {
		if(this==otherObject) return true;
		
		if(otherObject==null) return false;
		
		if(getClass()!=otherObject.getClass()) return false;
		
		Employee other=(Emplyee) otherObject;
		
		return name.equals(other.name) && salary==other.salary && hireDay.equals(other.hireDay);
	}
}

getClass方法将返回一个对象所属的类(反射里边的知识),在检测中,只有在两个对象属于同一个类时,才有可能相等。

在子类中定义equals方法时,首先调用超类的equals。如果检测失败,对象就不可能相等。如果超类中的域都相等,就需要比较子类中的实例域。

public class Manager extends Employee{
	...
	public boolean equals(Object otherObject) {
		
		if(!super.equals(otherObject)) return false;
		Manager other=(Manager)otherObject;
		return bonus==other.bonus;
	}
}

抛一个问题:如果隐式参数和显式参数不属于同一个类,equals如何处理呢?

在前面的例子中,如果发现类不匹配,则equals返回false,但是许多程序员却喜欢用instanceof进行检测:

		if(!(otherObject instanceof Employee)) return false;

这样做不但没有解决otherObject是子类的情况,并且还可能招致一些问题,因此我们大多数时候不建议这样使用。java语言规范要求equals方法具有下面的特性:

1.自反性:对于任何非空引用x,x.equals(x)一定是true

2.对称性:对于任何引用x和y,当且仅当y.equals(x)返回true,x.equals(y)也应该返回true

3.传递性:对于任何引用x,y,z, 若y.equals(x) 和 y.equals(z)都返回true,则x.equals(z)也应该返回true

4.一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。

5.对于任意非空引用x,x.equals(null)应该返回false。

有一个应用AbstractSet类的equals方法的典型例子,它将检测两个集合是否有相同的元素。AbstractSet类有两个具体子类:TreeSet和HashSet,他们分别使用不同的算法实现查找集合元素的操作。无论集合采用何种方式实现,都需要拥有对任意两个集合进行比较的功能。

下面可以从两个截然不同的情况看一下这个问题:

1.如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass进行检测。

2.如果由超类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同子类的对象之间进行相等的比较。

提示:对于数组类型的域,可以使用静态的Arrays.equals方法检测相应的数组元素是否相等。

警告:下面是实现equals方法的一种常见错误:

public class Employee{
	public boolean equals(Employee other) {
		return other!=null && getClass()==other.getClass()
				&& Object.equals(name,other.name) && salary==other.salary
				&& Objects.equals(hireDay,other.hireDay);
	}
}

这个方法声明的显式参数是Employee。其结果并没有覆盖Object类的equals方法,而是定义了一个完全无关的方法。

为了避免发生类型错误,可以使用@Override对覆盖超类的方法进行标记:

	@Override public boolean equals(Object other)

如果出现了错误,并且正在定义一个新方法,编译器就会给出错误报告。

hashCode方法

 散列码(hash code)是由对象导出的一个整型值。散列码是没有规律的。如果x和y是两个不同的对象,x.hashCode()与y.hashCode()基本上不会相同。

String类使用下列算法计算散列码:

	int hash=0;
	for(int i=0;i<length();i++)
		hash=31*hash+char(i);

由于hashCode方法定义在Object类中,因此每个对象都有一个默认的散列码,其值为对象的存储地址。

请注意:若两个String类内容一样,则它们拥有相同的散列码,这是因为字符串的散列码是由内容导出的。而StringBuilder类中没有定义hashCode方法,它的散列码是由Object类的默认hashCode方法导出的对象存储地址。

如果重新定义equals方法,就必须重新定义hashCode方法,以便用户可以将对象插入到散列表中。

hashCode方法应该返回一个整型数值(也可以是负数),并合理地组合实例域的散列码,以便能够让各个不同的对象产生的散列码更加均匀。

public class Solution{
	public int hashCode() {
		return 7*name.hashCode()+11*new Double(salary).hashCode()
				+13*hireDay.hashCode();
	}
}

还有更好的做法,需要组合多个散列值时,可以调用Objects.hash并提供多个参数。这个方法会对各个参数调用Objects.hashCode,并组合这些散列值。

public class Solution{
	public int hashCode() {
		return Objects.hashCode(name,salary,hireDay);
	}
}

Equals与hashCode的定义必须一致;如果x.equals(y)返回true,那么x.hashCode()必须与y.hashCode()具有相同的值。例如:如果用定义的Employee.equals比较雇员的ID,那么hashCode方法就需要散列ID,而不是雇员的姓名或存储地址。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值