一.概述
上一篇讲解了Java继承与多态的基本使用。链接:Java继承与多态
这篇讲解Object类,前面讲了Java是完全面向对象的,所以在Java的世界里任何事物都可以理解为对象,Object就是最终的类——对象类。他定义了对象最基本的固有方法。所有的类都会继承自Object类中的方法。下面说几个比较常用的方法,还有几个是线程方面的方法,后期讲到线程的时候会讲到。
二.Object类
1.equals方法
Object的equals方法用于检测一个对象是否等于另一个对象。在Object类中的这个方法,是判断两个对象是否具有相同的引用。如果两个对象的引用是相等的,它们就是相等的。但是对于我们想要比较的对象是否相等的原则并不是根据地址,往往通过对象的状态,如果两个对象的状态相同,就认为这两个对象相等。
import java.time.LocalDate;
import java.util.Objects;
/**
* 员工类
*/
public class Employee {
private String name;
private double salary;
private LocalDate hireDay;
/**
* 覆盖Object类中的equals方法
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
// 检测当前对象和obj是否引用同一个对象,实际比较的是内存地址。
if (this == obj) {
return true;
}
// 如果obj为null,对应第五条特性
if (obj == null) {
return false;
}
// 因为子类也要决定是否相等的概念所以使用getClass()方法
if (getClass() != obj.getClass()) {
return false;
}
// 进行强制类型转换调用子类的特有方法
Employee employee = (Employee)obj;
return Objects.equals(name,employee.name)
&& salary == employee.salary
&& Objects.equals(hireDay,employee.hireDay);
}
//此处省略getter和setter方法...
}
/**
* 子类覆盖父类的equals
*/
public class Manager extends Employee{
private double bonus;
/**
* 子类继承父类并覆盖父类的equals方法
* @param obj
* @return
*/
@Override
public boolean equals(Object obj) {
if (!super.equals(obj)) {
return false;
}
Manager manager = (Manager) obj;
return bonus == manager.bonus;
}
// 省略其他...
}
Java语言规范要求equals方法具有下面的特性:
(1)自反性:对于任何非空引用x,x.equals(x)应该返回true
(2)对称性:对于任何引用x和y,x.equals(y),返回true,那么y.equals(x)也应该返回true。
(3)传递性:对于任何引用x,y,z,如果x.equals(y)返回true,y.equals(z)返回true,那么x.equals(z)也应该返回true。
(4)一致性:如果x和y引用的对象没有发生变化,反复调用x.equals(y)应该返回同样的结果。
(5)对于任意非空引用x,x.equals(null)应该返回false。
但是以上特性都是针对的同一个类型而言,当类型不一样的时候又是什么样子呢??
当一个Employee类型对象和Manager类型对象具有相同的状态时,而Employee类重写的equals方法用instanceof进行了检测,则返回true,那么利用对称性则意味着Manager类调用equals方法也应该返回true。但是Manager类中有自己的实例域,无法比较特有的信息,所以即使判断了类型也无法进行特有的实例域进行比较。比如两个特有的属性不同也认为是相同的对象是不合适的。所以往往instanceof并不是完美的。
记住以下两个要点:
- 如果子类能够拥有自己的相等概念,则对称性需求将强制采用getClass()方法进行检测。
- 如果由父类决定相等的概念,那么就可以使用instanceof进行检测,这样可以在不同的子类的对象之间进行相等的比较。
2.hashCode方法
散列码(hash code)是由对象导出的一个整型值。因为此方法是在Object类中,所以每个对象都有自己的hashCode值,其值为对象的地址。
/**
* 覆盖父类的hashCode方法
* @return
*/
@Override
public int hashCode() {
// Objects.hash()方法将每个参数调用hashCode值
return Objects.hash(name,salary,hireDay);
}
/**
* 覆盖父类的hashCode方法
* @return
*/
@Override
public int hashCode() {
return super.hashCode() + 17 * new Double(bonus).hashCode();
}
3.equals和hashCode的关系
应该都听过重写equals就要重写hashcode,这是为什么呢??
如果只是比较两个对象相比较,那么重写equals方法就可以了,但是后面讲到的Hash类型的集合是不可以的,他们的判断逻辑是先判断放入集合中的对象hash值是否相等,如果相等,在进行equals方法的判断,因为每个对象获取的hashcode值不一定不同(由于hash算法的原因),所以hashCode值往往是不可靠的,我们通过重新写equals方法来进行比较。更深层次可以参考hash类型的集合源码以及hash算法。
测试结果demo:
/**
* 测试equals方法
*/
public class EqualsDemo {
public static void main(String[] args) {
// 测试equals方法
Employee alice1 = new Employee("Alice", 1000, 1999, 10, 1);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alice", 1000, 1999, 10, 1);
Employee bob = new Employee("Bob", 2000, 2000, 10, 1);
System.out.println("alice1 == alice2: " + (alice1 == alice2));
System.out.println("alice1 == alice3: " + (alice1 == alice3));
System.out.println("alice1.equals(alice3):" + alice1.equals(alice3));
System.out.println("alice1.equals(bob):" + alice1.equals(bob));
//测试hashcode方法
Manager carl = new Manager("Carl", 2000, 1999, 1, 1, 12);
Manager boss = new Manager("Carl", 2000, 1999, 1, 1, 12);
boss.setBonus(5000);
System.out.println("carl.equals(boss):" + carl.equals(boss));
System.out.println("alice1.hashCode():" + alice1.hashCode());
System.out.println("alice3.hashCode():" + alice3.hashCode());
System.out.println("bob.hashCode():" + bob.hashCode());
System.out.println("carl.hashCode():" + carl.hashCode());
}
}
三.结束语
还有原来toSting方法,也是Object类的方法,这个方法原本是用来打印对象所属的类名和散列码,这个信息对我们俩说毫无意义,随意要重写toString方法来进行显示对象的状态。
下一篇讲包装类以及枚举类的基本使用
有些可能我理解的不够深刻,大家如果觉得我说的不够详细可以参考我的推荐书,详细的看一下。欢迎大家评论。第一时间我会回复大家。谢谢!