面试官:'==' 与 'equals()',你真的懂吗?

抛砖引玉

首先,让我们通过一段简单的Java代码来进行思考和猜测。请大家看以下示例代码,并猜测每个输出语句会打印出什么结果。

public static void main(String[] args) {

    int i1 = 1;
    int i2 = 1;

    String s1 = "Hello World";
    String s2 = new String("Hello World");
    String s3 = s2;

    // 两个int类型变量比较
    System.out.println("i1 == i2:" + (i1 == i2));
    
    // 三个字符串两两用“==”比较
    System.out.println("s1 == s2:" + (s1 == s2));
    System.out.println("s1 == s3:" + (s1 == s3));
    System.out.println("s2 == s3:" + (s2 == s3));
    
    // 三个字符串两两用“equals”方法比较
    System.out.println("s1 equals s2:" + s1.equals(s2));
    System.out.println("s1 equals s3:" + s1.equals(s3));
    System.out.println("s2 equals s3:" + s2.equals(s3));
}

现在,让我们一起运行main方法,查看实际的输出结果:

i1 == i2:true
s1 == s2:false
s1 == s3:false
s2 == s3:true
s1 equals s2:true
s1 equals s3:true
s2 equals s3:true

你猜对了吗?接下来,我们将深入分析这段代码,解释为什么会出现这样的结果。

庖丁解牛

在上述代码中,涉及了两种数据类型:基本数据类型和引用类型。我们首先来了解它们在堆栈内存中的存放方式,如下图所示。

面试官:'==' 与 'equals()',你真的懂吗?


基本数据类型直接分配在栈内存中,如 int i1 = 1;int i2 = 1;

引用类型创建的对象在堆内存中分配,而栈内存中的变量存储对对象的引用地址。例如,String s2 = new String("Hello World"); 中,s2 在栈中存储对字符串对象的引用地址xf002

有了这个基础知识,我们继续分析代码。

基础数据类型比较

首先,对于基本数据类型,使用 == 运算符比较的是它们在栈内存中的值。因此,int i1 == i2 返回 true,因为它们的值相等。

引用类型比较

接下来,我们关注引用类型的比较。细心的同学可能已经注意到了关于String对象的创建其实是采用了两种方式:一种是直接赋常量值,一种是通过new关键字进行创建,如下所示。

// 创建了一个字符串常量,存储在常量池中,而 s1 存储了对该常量的引用。
String s1 = "Hello World";
// 使用 new 关键字创建了一个字符串对象,存储在堆内存中,s2 存储了对堆中对象的引用。
String s2 = new String("Hello World");
// 将 s2 赋值给 s3,使它们共享相同的引用。
String s3 = s2;

引用类型 == 比较

使用==运算符比较两个引用时,比较的是它们在栈内存中存储的引用地址。因此,s1 == s2s1 == s3返回false,因为它们分别指向常量池和堆内存中的不同对象;s2 == s3返回true,因为它们指向相同的对象。

引用类型 equals 比较

在Java中Object类中之王,所有类都默认继承自该类。它自身有默认的equals方法,源码如下所示。

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

可以看到,Object类的equals方法其实就是采用的==方式来比较两个对象是否相等,所以默认情况下equals==没啥区别。

既然这样的话,那么三个字符串之间采用equals方法跟采用==不应该是一样的吗,为啥用equals比较全部返回true呢。

因为String类重写了Object类的equals方法,具体源码实现如下所示。

/**
 * 将当前字符串与另一个对象进行比较。
 * 如果两者相等(内容相同),则返回 true,否则返回 false。
 *
 * @param anObject 要比较的对象
 * @return 如果当前字符串与指定对象相等,返回 true,否则返回 false
 */

public boolean equals(Object anObject) {
    // 判断是否是同一个对象引用
    if (this == anObject) {
        return true;
    }

    // 判断传入的对象是否是一个字符串
    if (anObject instanceof String) {
        // 将传入的对象转换为字符串
        String anotherString = (String)anObject;
        
        // 获取当前字符串和另一个字符串的字符数组
        char v1[] = value;
        char v2[] = anotherString.value;

        // 获取当前字符串和另一个字符串的长度
        int n = value.length;

        // 如果两个字符串的长度相等,进行字符逐一比较
        if (n == anotherString.value.length) {
            int i = 0;
            while (n-- != 0) {
                // 如果有任何字符不相等,则返回 false
                if (v1[i] != v2[i])
                    return false;
                i++;
            }
            // 所有字符都相等,返回 true
            return true;
        }
    }

    // 传入的对象不是字符串,或者长度不相等,返回 false
    return false;
}

String类重写了Object类的equals方法,实现了逐字符比较字符串内容的逻辑。因此,使用equals方法比较字符串对象时,比较的是它们的内容而不是引用地址。因此,s1.equals(s2)s1.equals(s3)s2.equals(s3)全部返回true,因为它们的内容都是相同的。

总结

  • 使用 == 比较基本数据类型时,比较的是它们的值。
  • 使用 == 比较引用类型时,比较的是它们在栈内存中存储的引用地址。
  • equals 方法通常被重写用于比较对象的内容,而不是引用地址。在 String 类中,equals 方法比较的是字符串的字符内容。

end

面试官:'==' 与 'equals()',你真的懂吗?

原文始发于微信公众号(Java浩窍门):面试官:'==' 与 'equals()',你真的懂吗?

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/244542.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!