《Java核心技术》第10版读书笔记之Chap5(3)——Object类及其equals、hashCode与toString方法

来源:互联网 发布:can数据帧格式 编辑:程序博客网 时间:2024/05/16 06:02

在许多类库中,都会设定一个类作为所有类的基类,这样在该类上实现的特性和功能就默认被所有继承至该类的子类所拥有了,如MFC类库中的CObject、VCL框架的TObject以及QT框架中的QObject等等。在Java附带的类库中,也有这么一个“老祖宗”,它的名字叫Object。

万佛朝宗

在Java中,所有没有父类的类都默认隐式继承了Object类。而所有的类要么是没有父类的类,要么就是某个类的子类,所以,所有类都直接或间接的继承了Object类。

  • 无需显示地注明一个类继承至Object类,只要该类没有其他父类,则Object就自动成为其父类。

  • 可以用Object类型引用任何对象实例:

Object obj = new Employee(参数XXX);//需要昭雪平反时,再正本清源Employee e = (Employee) obj;
  • 除了基本数据类型不是对象外,其余所有类型都是对象,包括数组类型。

Object三板斧

如果暂时不考虑Object类中多线程相关的方法,

equals方法

这是一个见名知意的方法,用于判定两个对象是否相等。相信使用过Java中String类型的人对这个方法都有深刻的印象吧。实际上这个方法是在Object类中定义的,而String类覆写了该方法。为什么要覆写该方法呢?显然是因为Object类中的equals方法不能满足String类型实际的需求呀。

在Object类中,equals方法比较的是传入的两个对象是否是对同一个类实例的引用。然而,通常人们希望实现的equals方法返回true当且仅当两个对象有相同的状态(如字符串相等、或公司的雇员类中员工的编号、姓名等等信息能完全对的上)

通常大家希望equals方法有如下性质:

  • 自反性:对于任意的非空引用x,x.equals(x)值为true应该恒成立。
  • 对称性:对任意的引用x和y,x.equals(y) 与 y.equals(x)的结果应该相同。
  • 传递性:对于任意的引用x、y和z,若x.equals(y)成立且y.equals(z)成立,则x.equals(z)也应成立。
  • 稳定性:若x,y引用的对象实例没有发生变化,则x.equals(y)的结果不随时间的变化而变化。
  • 强制规定:若x为非空引用,则x.equals(null)必须返回false。

在《Core Java》中,给出了一个基类实现equals方法的通用套路:

public equals(Object otherObject) {    // 1.若是相同的引用,返回true    if (this == otherObject) {        return true;    }    // 2.若传入的为null,返回false    if (otherObject == null) {        return false;    }    // 3.用getClass比较两者是否是同一个类(用类对象比较),不是则返回false    if (getClass() != otherObject.getClass()) {        return false;    }    // 4.进行到此处说明otherObject确实是本类的实例,可转换类型后按实际情况进行比较    XXX other = (XXX)otherObject;    // 5.比较this和otherObject的具体字段    // 对于原始数据类型,直接用==判断是否相等    // 对于类对象,用其equals成员方法加以比较    // 如果该成员对象可能为空,可用Objects.equals(x,y)比较,其语义如下:    //  A. 若x与y同为null,返回true,    //  B. 仅一个为null,返回false,    //  C. 都不为null,返回x.equals(y)}

这样,在子类中如需覆写equals方法,需首先调用基类的equals方法,若其返回false,则无需再进行比较,直接返回false即可。若其返回true,则至少已经能确定otherObject不为null且与this同属于一类。

equals方法中的getClass()与instanceof的PK

有时在上述第三步时,可能用instanceof代替getClass。但instanceof可能破坏equals的对称性,因为

boolean b1 = child instanceof CBase; //为真boolean b2 = base instanceof CChild; //为假

因此,一般不建议使用instanceof在equals方法中判定是否应该转为同类型比较具体字段,除非以下情况同时满足:

  • 基类是抽象类,即不可能有真正基类的对象产生
  • 判定相等的逻辑在基类中就已经完备了,比如无论何种Shape(为基类),只按其成员变量id为标准比较是否相等。

hashCode方法

当需要计算当前实例的哈希时(最常见的就是将实例存放到哈希类容器中时),该方法就会被调用,返回一个32位的哈希值。在Object类中定义了该方法,默认返回的是类对象实例的地址。需要指出的是,由于该值范围固定(32位长度),而理论上来说类对象实例千变万化,是将一个无线域映射到有限域上,所以碰撞是必然会发生的。该值需要有如下特性:

  • 稳定性:当类对象实例未被改动时,在任意时刻调用其hashCode方法得到的值应该相同。
  • 若x.equals(y)值为true,则x.hashCode()就必须与y.hashCode()有相同的值。
  • 其值应该尽可能均匀地散布,减少碰撞的产生。

在计算类对象实例的哈希值时,如果需要计算成员变量的哈希值,有如下原则要注意:

  • 如果该字段为基本数据类型,使用对应Wrapper类的hashCode方法。如Double.hashCode(salary)
  • 如果该字段为类类型,要考虑到其为null的可能,这时应采用Objects.hashCode(member)
public int hashCode() {    //在此计算hash值}

toString方法

该方法用于将该对象实例转换为字符串。我们经常使用System.out.println()方法来打印各式各样的对象实例,其实就是这些对象定义的toString方法发挥了作用。通常一个设计良好的类的toString方法遵循这样的格式:类的名字,随后是一对方括号括起来的成员变量的值。

public String toString() {    return getClass().getName()      + "[name=" + name      + ",salary=" + salary      + ",hireDay=" + hireDay      + "]";}

这样,子类的toString方法中只需要先调用父类的toString方法,然后再输出其自有的成员变量即可。

public class Manager extends Employee    public String toString() {        return super.toString()          + "[bonus=" + bonus          + "]";    }}

建议为编写的类都加上该方法,方便自己和使用该类的其他程序员观察、调试以及日志记录。

需要注意以下两点:

  1. 对于类类型,可以调用其toString()方法。但考虑到与原始数据类型相兼容,可以采用”” + x的方式。
  2. 数组类继承了Object类的toString方法,若要输出其中的元素值,则应调用Arrays.toString(a)方法。对于多维数组,则应调用其Arrays.deepToString方法。
阅读全文
0 0
原创粉丝点击