equals()

equals()方法只能用来判断两个对象是否相等,不能用于判断基本数据类型的变量。equals()存在于 Object类中且它是所有类的父类,所以所有类都有equals方法。

1
2
3
4
5
6
7
/**
* Object 类中的 equals()
*/

public boolean equals(Object obj) {
return (this == obj);
}

根据类是否重写了equals()方法,有两种情况:

  • 未重写:使用的默认是Object类的equals(),等价于通过==比较。
  • 重写:如果两个对象的每个属性都相等,则返回true,否则返回false
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    /**
    * 调用 equals() 的两种情况
    */

    class Person {
    public String name;

    public Person(String name) {
    this.name = name;
    }
    }

    Person person1 = new Person("bill");
    Person person2 = new Person("frank");
    String string1 = new String("ab");
    String string2 = new String("ab");

    // String 类重写了 equals 方法,返回 true
    System.out.println(string1.equals(string2));
    // Person 类没有重写 equals 方法,返回 false
    System.out.println( person1.equals(person2));

== 和 equals()

区别:

  • equals()是方法,而==是操作符。
  • 对于基本类型,==是比较值的大小是否相同;而对于引用数据类型,==比较的是对象的内存地址是否相同。

    因为 Java 只有值传递,所以对于==来说,不管是比较基本数据类型还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量的值是内存地址。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
/**
* == 判等
*/

Integer i1 = 288;
Integer i2 = 288;

int i3 = 288;
int i4 = 288;

// 比较引用数据类型,返回 false
System.out.println(i1 == i2);
// 比较基本数据类型,返回 true
System.out.println(i3 == i4);

重写 equals()

思路:

  1. 如果是同一个对象的引用,直接返回true

  2. 如果比较对象为空或者不为同一个类型,直接返回false

  3. Object对象进行转型后,判断每个属性或关键域是否相等;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
/**
* 重写 equals()
*/
public class EqualsExample {
private int x;
private int y;
private int z;

public EqualsExample(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}

@Override
public boolean equals(Object obj) {
// 检查是否是同一个对象的引用
if (this == obj) return ture;
// 检查是否为同一个类型
if (obj == null || obj.getClass() != this.getClass()) return false;

// 将 Object 对象进行转型
EqualsExample that = (EqualsExample) obj;

// 依次判断每个关键域是否相等
if (x != that.x) return false;
if (y != that.y) return false;
return z == that.z;
}
}

hashCode()

  • 因为hashCode()定义在Object类中,所以任何类都包含hashCode()
  • 因为计算哈希值具有随机性,所以两个值不同的对象可能计算出相同的哈希值。
    1
    2
    3
    4
    5
    6
    /**
    * Object 的 hashCode()
    * native 方法,用于返回对象的哈希码,主要使用在哈希表中,比如 JDK 中的 HashMap。
    */

    public native int hashCode()

    ObjecthashCode()是本地方法,是通过 C 或 C++ 实现的,通常用来将对象的内存地址转换为整数。

hashCode() 和 equals()

hashCode()equals()的关系:

  • 两个对象的哈希值相等,equals()不一定返回true(哈希碰撞)。
  • 两个对象的哈希值不相等,equals()一定返回false
  • equals()返回true时,两个对象的哈希值一定相等。

    重写 equals 方法时也需要重写 hashCode 方法,这是因为:必须保证 equals 方法判断两个对象相等时,hash 值也相等。

重写 hashCode()

重写hashCode,可以使用Objects.hash()方法实现,通过类的所有属性生成最终的哈希值。

1
2
3
4
5
6
7
8
/** 
* 重写 hashCode()
*/

@Override
public int hashCode() {
return Objects.hash(x, y, z);
}

Objects.hash()中调用了Arrays.hashCode(),通过循环将每一个属性的哈希值都计算在内:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Objects.hash()
public static int hash(Object... values) {
return Arrays.hashCode(values);
}

// Arrays.hashCode()
public static int hashCode(Object a[]) {
if (a == null)
return 0;

int result = 1;

for (Object element : a)
result = 31 * result + (element == null ? 0 : element.hashCode());

return result;
}

toString()

返回类的名字实例的哈希值的 16 进制字符串,所有的子类最好重写 toString 方法。

1
2
3
4
5
/**
* Object 类的 toString()
*/

public String toString()

clone()

1
2
3
4
5
/** 
* Object 中的 clone()
*/

protected native Object clone() throws CloneNotSupportedException

重写 clone()

clone()Object中的权限修饰符为protected,所以如果一个类不显式重写clone(),那新建类时就不能调用该类实例的clone()方法。

1
2
3
4
5
6
7
8
9
public class CloneExample {
private int a;
private int b;
}

CloneExample e1 = new CloneExample();
CloneExample e2 = e1.clone();

// 未重写 clone() 将报错:'clone()' has protected access in 'java.lang.Object'

类在重写clone()时还需要实现Cloneable接口,不然会抛出CloneNotSupportedException异常。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public class CloneExample {
private int a;
private int b;

@Override
public CloneExample clone() throws CloneNotSupportedException {
return (CloneExample)super.clone();
}
}

CloneExample e1 = new CloneExample();
try {
CloneExample e2 = e1.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
// 类不实现 Cloneable 接口将报错
// out: java.lang.CloneNotSupportedException: CloneExample

参考

  1. CS-Note
  2. javacore,判等问题
  3. javaguide,object 类