Java equals 和 == 彻底解析

今天朋友忽然问到一个问题:html

两个对象使用x.equals(y)判断结果为true时,两个对象的hashCode能够不一样吗?java

在Java编程中,判断两个对象是否相等经常使用equals()或是==,可是其中的区别和原理可能不少人并不彻底清楚。今天就借着上面这个问题来看看equals()==的区别和原理。编程

1. 数据类型与==的含义

Java中的数据类型分为基本数据类型和引用数据类型:数组

  1. 基本类型:编程语言中内置的最小粒度的数据类型。它包括四大类八种类型bash

    • 4种整数类型:byte、short、int、long
    • 2种浮点数类型:float、double
    • 1种字符类型:char
    • 1种布尔类型:boolean
  2. 引用类型:引用也叫句柄,引用类型,是编程语言中定义的在句柄中存放着实际内容所在地址的地址值的一种数据形式编程语言

    • 接口
    • 数组
  • 对于基本类型来讲,== 比较的是它们的值
  • 对于引用类型来讲,== 比较的是它们在内存中存放的地址(堆内存地址)

例:ide

public void test(){
    int num1 = 100;
    int num2 = 100;

    String str1 = "James";
    String str2 = "James";

    String str3 = new String("James");
    String str4 = new String("James");

    System.out.println("num1 == num2 : " + (num1 == num2));
    System.out.println("str1 address : " + System.identityHashCode(str1) + ";\nstr2 address : " + System.identityHashCode(str1) + ";\nstr1 == str2 : " + (str1 == str2));
    System.out.println("str3 address : " + System.identityHashCode(str3) + ";\nstr4 address : " + System.identityHashCode(str4) + ";\nstr3 == str4 : " + (str3 == str4));
}
复制代码

运行上面的代码,能够获得如下结果:测试

num1 == num2 : true

str1 address : 1174290147;
str2 address : 1174290147;
str1 == str2 : true

str3 address : 1289696681;
str4 address : 1285044316;
str3 == str4 : false
复制代码

能够看到str1和str2的内存地址都是1174290147,因此使用==判断为true,可是str3和str4的地址是不一样的,因此判断为falseui

2. equals() 方法解析

在Java语言中,全部类都是继承于Object这个超类的,在这个类中也有一个equals()方法,那么咱们先来看一下这个方法。this

public boolean equals(Object obj) {
    return (this == obj);
}
复制代码

能够看得出,这个方法很简单,就是比较对象的内存地址的。因此在对象没有重写这个方法时,默认使用此方法,即比较对象的内存地址值。可是相似于String、Integer等类均已重写了equals()。下面以String为例。

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = length();
            if (n == anotherString.length()) {
                int i = 0;
                while (n-- != 0) {
                    if (charAt(i) != anotherString.charAt(i))
                            return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }
复制代码

很明显,String的equals()方法仅仅是对比它的 数据值,而不是对象的 内存地址

String 为例测试一下。

public void test() {

    String str1 = "James";
    String str2 = "James";

    String str3 = new String("James");
    String str4 = new String("James");

    System.out.println("str1 address : " + System.identityHashCode(str1) + ";\nstr2 address : " + System.identityHashCode(str1) + ";\nstr1.equals(str2) : " + str1.equals(str2));
    System.out.println("str3 address : " + System.identityHashCode(str3) + ";\nstr4 address : " + System.identityHashCode(str4) + ";\nstr3.equals(str4) : " + str3.equals(str4));
}
复制代码

结果为:

str1 address : 1174290147;
str2 address : 1174290147;
str1.equals(str2) : true

str3 address : 1289696681;
str4 address : 1285044316;
str3.equals(str4) : true
复制代码

能够发现无论对象的内存地址是否相同并不影响其结果,因此String类型比较的是 数据值, 而不是 内存地址值

因此总结一下equals()== 的区别:

  1. ==
  • 基本类型:对比它们的值是否相等
  • 引用类型:对比它们的内存地址是否相等
  1. equals()
  • 基本类型:使用==进行比较
  • 引用类型:默认状况下,对比它们的地址是否相等;若是equals()方法被重写,则根据重写的要求来比较。

3. equals() 与 hashCode()

在详细的了解了==equals()的做用和区别后,如今来研究一下以前的那个问题:

两个对象使用x.equals(y)判断结果为true时,两个对象的hashCode能够不一样吗?

首先咱们须要知道hashCode究竟是什么?仍是从Object这个超类来看一下。

public int hashCode() {
    return identityHashCode(this); // 此处返回对象的内存地址值
}
复制代码

代码也很简单,看来默认状况下,hashCode就等于对象的 内存地址值(注:System.identityHashCode(Object obj)方法用于获取对象的内存地址,以前的样例代码中有使用)。和equals()方法同样重写以后,hashCode()方法方法也是能够被重写的,并且二者通常状况下都是成对出现。

简单测试一下String类型重写hashCode()方法以后有什么变化。

public void test() {
    String str1 = "James";
    System.out.println("str1 address : " + System.identityHashCode(str1) + "\nstr1 hashCode : " + str1.hashCode());
}
复制代码

结果为:

str1 address : 1174290147
str1 hashCode : 71338276
复制代码

很明显,hashCode 已经不是内存地址了。

那么总结一下:

  • equals():默认状况下比较的是对象的 内存地址值,被重写后按照重写要求进行比较,通常是比较对象的 数据值
  • hashCode(): 默认状况下为对象的 内存地址值,被重写后按照重写要求生成新的值。

到此对于刚开始提出的问题应该很好解决了。对于这两个对象,只要咱们重写equals()方法,就能够比较对象的 数据值,而不重写hashCode()方法,此时两个对象的 hashCode 就默认为内存地址值了,只要将两个对象指向不一样的地址便可。

验证环节,先建立一个类:

public class CustomBean {
    private String name;
    private int age;

    public CustomBean(String name, int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        CustomBean that = (CustomBean) o;
        return age == that.age &&
                Objects.equals(name, that.name);
    }

    // @Override
    // public int hashCode() {
    // return Objects.hash(name, age);
    // }
}
复制代码

建立测试方法:

@Test
public void test() {
    CustomBean x = new CustomBean("James", 18);
    CustomBean y = new CustomBean("James", 18);

    System.out.println("x.hashCode: " + x.hashCode());
    System.out.println("x address : " + System.identityHashCode(x));
    System.out.println("y.hashCode: " + y.hashCode());
    System.out.println("x address : " + System.identityHashCode(y));

    System.out.println("x and y is equals : " + x.equals(y));
}
复制代码

运行结果为:

x.hashCode: 1174290147
x address : 1174290147

y.hashCode: 1289696681
x address : 1289696681

x and y is equals : true
复制代码

很明显,这就是问题中所描述的那种状况:两个对象使用x.equals(y)判断结果为true时,两个对象的hashCode不相同。

4. 总结

至此,==equals()的区别及做用,equals()hashCode的关系及使用已经了解清楚了。下面再总结一下:

对于equals()== 的区别:

  1. ==

    • 基本类型:对比它们的值是否相等
    • 引用类型:对比它们的内存地址是否相等
  2. equals()

    • 基本类型:使用==进行比较
    • 引用类型:默认状况下,对比它们的地址是否相等;若是equals()方法被重写,则根据重写的要求来比较

对于equals()hashCode()的关系:

根据Object超类中的文档说明,equals()hashCode()两个方法应该 同进同退。上面的例子只是举例说明存在那种状况,但那并非一个很好的应用。

  • 因此必定要记住equals()hashCode()两个方法应该 同进同退
  • 因此必定要记住equals()hashCode()两个方法应该 同进同退
  • 因此必定要记住equals()hashCode()两个方法应该 同进同退

重要的事情说三遍。

欢迎您关注个人博客主页:James Blog

阅读原文

相关文章
相关标签/搜索