禁止码迷,布布扣,豌豆代理,码农教程,爱码网等第三方爬虫网站爬取!

目录

  • 所有类的超类
  • equals 方法
  • 同类判断
  • 非同类判断
  • equals 方法设计
  • hashCode 方法
  • toString 方法
  • 变参方法
  • 参考资料

所有类的超类

Object 类是 Java 中所有类的超类,Java 中的每个类都是 Object 类的子类。因此可以使用 object 类型的变量引用任何类型的变量,但是 Object 类型的变量只能作为各种值的泛型容器,想要操作该对象还是需要强制类型转换。

Object obj = new Employee("Alice Adams", 75000);
Employee e = (Employee) obj;

在 Java 中基本类型不是对象,而所有数组类型都是基于 Object 类实现的。这点还是很特别的,因为 C++ 没有所有类的根类,而 Python 中的所有数据类型都是对象。

equals 方法

同类判断

equals 方法可以检测一个对象是否等于另一个对象,在 Object 类中的判断方式是判断 2 个对象的引用是否相等。但是在很多情况下我们不关心特们的引用是否相等,而是关系各个状态字段是否相等。因此在基础 equals 方法时,我们经常需要针对状态来进行覆盖。
首先先看看 getClass() 方法,该方法可以返回对象所属的类。使用 getClass() 方法进行判断是可行的,因为当 2 个对象属于同一个类时才有可能相等。接着程序就需要对字段的各状态进行判断:

private String name;
private double salary;
private LocalDate hireDay;

public boolean equals(Object otherObject){
      if (this == otherObject)      //判断引用是否相等
            return true;

      if (otherObject == null)      //判空
            return false;

      if (getClass() != otherObject.getClass())      //判断是否是相同的类 
            return false;

      Employee other = (Employee) otherObject;      //强制类型转换
      return Objects.equals(name, other.name)      //比较各个字段值
            && salary == other.salary 
            && Objects.equals(hireDay, other.hireDay);
}

当子类覆盖了超类的 equals 方法时,因为超类的 equals 方法可能是私有方法,因此要先调用超类的 equals 方法。

private double bonus;

public boolean equals(Object otherObject){
      if (!super.equals(otherObject)) 
            return false;

      Manager other = (Manager) otherObject;      
      return bonus == other.bonus;
}

非同类判断

如果显式参数和隐式参数不是同一个类,equals 使用什么方案判断更为合适?首先先谈一下 instanceof 关键字,它可以测试它左边的对象是否是它右边的类的实例,返回 boolean 的数据类型。

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

写代码时要怎么选择 instanceof 关键字和 getClass() 方法呢?equals 的设计需要考虑 2 种情形:

  1. 子类可以有自己的相等性概念,使用 getClass() 方法检测;
  2. 超类决定相等性概念,选择 instanceof 关键字检测。

equals 方法设计

因此设计一个 equals 方法可以参考以下几点:

  1. 显式参数命名为 otherObject,比较时用强制类型转换为另一个 other 变量;
  2. 检测 this 和 otherObject 是否相等;
  3. 检测 otherObject 是否是 null;
  4. 比较 this 与 otherObject 的类是否相同;
  5. 将 otherObject 强制类型转换为相应的类类型变量,比较各个字段。

hashCode 方法

散列码是由对象导出的一个整数值,不同的对象的散列码不同,Object 方法会根据存储地址的不同,为每一个对象提供一个散列码。例如 String 计算散列码的代码:

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

hashCode 方法定义在 Object,可以返回对象的散列值。当 equals 方法被覆盖时,hashCode 方法也要随之修改,因为 “x.equals(y)” 判断为 true 的 2 个对象,执行 “x.hashCode() == y.hashCode()” 也要为真。当需要组合多个散列值时,使用 Object.hash() 方法是不错的选择。

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

toString 方法

toString 方法可以返回表示对象值的字符串,常见的表示是“类名+字段值”。例如:

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

当子类继承超类的 toString 方法时,可以用 super 关键字调用超类的 toString 方法。

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

当对象和一个字符串用 “+” 运算符连接时,toString 方法就会被自动调用。toString 方法可以放映类中各个状态的变化,因此很适合用于调试。

变参方法

注意到我们熟悉的 printf 方法,这个函数能够接收多个参数,它的方法定义如下所示。

public class PrintStream{
      public PrintStream printf(String fmt, Object… args){
            return format(fmt, args);
      }
}

使用 “…” 表示方法可以接受任意数量的对象,而这些对象可以被组织为一个 Object[] 数组来使用。例如这个方法可以比较多个数字的大小:

public static double max(double… values){
      double largest = values[0];
      for(double v:values){
            if(v > largest)
                  largest = v;
      }
      return largest;
}