Java equals和==完全解析

参考:segmentfault.com/a/119000001864789...
今天朋友突然问到一个问题:

两个对象使用x.equals(y)判断结果为true时,两个对象的hashCode可以不同吗?

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

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

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

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

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

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

例:

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的地址是不同的,所以判断为false。

2. equals() 方法解析

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

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() 和 == 的区别:

    基本类型:对比它们的值是否相等
    引用类型:对比它们的内存地址是否相等
equals()
    基本类型:使用==进行比较
    引用类型:默认情况下,对比它们的地址是否相等;如果equals()方法被重写,则根据重写的要求来比较。
  1. 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());
    }
  2. 总结
    至此,==和equals()的区别及作用,equals()和hashCode的关系及使用已经了解清楚了。下面再总结一下:

    对于equals() 和 == 的区别:

     基本类型:对比它们的值是否相等
     引用类型:对比它们的内存地址是否相等

    equals()

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

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

    所以一定要记住equals()和hashCode()两个方法应该 同进同退。
    所以一定要记住equals()和hashCode()两个方法应该 同进同退。
    所以一定要记住equals()和hashCode()两个方法应该 同进同退。

本作品采用《CC 协议》,转载必须注明作者和本文链接
MissYou123
讨论数量: 1

1.数据类型与 == 的判断,(num1 == num2)为false

num1 address : 984213526;
num2 address : 400136488;
num1 == num2 : false

Java
可以看到num1和num2的内存地址是不同的,使用==判断为false

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!