Java基础系列之你真的懂==与equals的区别吗?


  对于Java初学者而言,可能会对这两个比较方法比较模糊,有的人可能会觉得两个的方法使用起来结果是一样的等。如果你有这样的想法,我建议你来看看这边博客,让你充分了解这两个比较的异同,以及他们底层是如何比较的等。阅读这篇文章之前,我希望你是对Integer和String这两个类是有所了解的,否则可以参考一下博客以加深你对这两个类的理解:

  1、jdk源码阅读笔记-String

  2、jdk源码阅读笔记-Integer

  那么,下面我将使用一些例子来引入本篇博客的主题,看看你能做对多少个题吧!

public static void main(String[] args) {
int i1 = 8;
int i2 = 8;
Integer i3 = 8;
Integer i4 = new Integer(8);
Integer i41 = new Integer(8);
Integer i5 = 129;
Integer i6 = 129;
int i7 = 129;
System.out.print(i1 == i2);
System.out.print(" ");
System.out.print(i1 == i3);
System.out.print(" ");
System.out.print(i1 == i4);
System.out.print(" ");
System.out.print(i4 == i41);
System.out.print(" ");
System.out.print(i5 == i6);
System.out.print(" ");
System.out.print(i5 == i7);
System.out.print(" ");
System.out.print(i5.equals(i7));
}

  上面运行的结果为 true true true false false true true,第一个i1 == i2 结果为true ,这个可以理解,因为两个都是int基本数据类型,并且值相等;第二个比较 i1 == i3 结果也为true,i3为int基本数据类型对应的引用类型,那么它们为什么相等呢?这个我们需要怎么去验证呢?其实可以通过字节码看编译器是如何执行这些代码就可以了;通过字节码发现,在执行 == 的时候i3变量是先执行intValue()这方法,这个方法的作用就是返回Integer类型对应的int值。所以 i1 和i3的比较最终还是变成了 i1 == i2的比较。

  字节码文件:

  intValue方法的源码:

/**
     * Returns the value of this {@code Integer} as an
     * {@code int}.
     */
    public int intValue() {
        return value;
    }

  直接返回了 value这个变量,而这个变量就是int类型的全局变量:

/**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

  我们再回到上面i1 == i4 的问题,i1是基本类型,i4是引用类型,其实还是回到了第二种情况,所以他们还是true。

  i4 == i41 两个都是引用类型,并且都是用了new的关键字,我们知道只要调用new关键字那么他们的引用地址肯定不一样,他们的结果返回了false,说明引用类型使用==比较时比较的是他们的内存地址,而不是字面值。

  i5 == i6 : 两个都是引用类型,并且值相等,但是结果是false,通过看字节码可以知道,编译器在编译的是时候,这种直接赋值的操作实际上调用了Integer中的valueOf方法,不清楚这个方法的同学建议看一下上面关于Integer的博客。Integer这个类为了提高速度,缓存了-127 至 128 的数,当调用valueOf的时候,如果传进来的数在这个范围之内,那么直接返回缓存的数据,否则new一个对象出来,然后返回。这个例子中,两个值都超出返回,所以都new了一个对象。这个是在开发过程中经常遇到的坑,解决办法是使用equals方法,这个方法已经被重写了,其过程是将引用类型转成基本类型,然后使用 == 来比较大小;

  i5 == i7: 一个基本数据类型,一个引用类型,且超出缓存范围,结果为true。这个不难理解,引用类型在编译的时候会自动拆箱,所以最后比较的是基本类型,所以为true。

  i5.equals(i7): 上面也有提到Integer已经重写了equals方法,所以跟上一种情况是一致的,为true。

  在String类中也会有比较多的面试会遇到,比如说下面代码示例:

 public static void main(String[] args) {
        String str1 = "aa";
        String str2 = "aa";
        String str3 = new String("aa");
        System.out.println(str1 == str2);//true
        System.out.println(str1 == str3);//false
        System.out.println(str1.equals(str3));//true
    }

    看到这里,可能会有同学会问,String每创建一次就会生成一个新对象,所以他们的内存地址肯定不一样,但是str1 == str2 返回true,这说明明 == 比较的是字面量吗?答案是否定的,String在我们日常开发中使用的频率最高的类,频繁的创建对象势必对应用的性能有一定的影响,java官方为了提高性能,每次使用字面量声明字符串时都会先向缓存池中查找池中是否已存在该对象,如果有则直接引用该对象,如果没有则new一个对象出来,然后将该对象放入缓存池中。上面str1 和str2因为字面量是相同的,所以他们共用缓存池中的一个对象,所以他们指向的内存地址是一样的,故为true;

  str3是直接new出来的,所以他是不经过缓存池的,因此str1 和 str3是不同的。

  String 的equals方法也已经重写了object的equals方法,它首先比较两个字符串的内存地址,不一样了才对两个字符串字面量进行比较。

public boolean equals(Object anObject) {
        if (this == anObject) {
            return true;
        }
        if (anObject instanceof String) {
            String anotherString = (String)anObject;
            int n = value.length;
            if (n == anotherString.value.length) {
                char v1[] = value;
                char v2[] = anotherString.value;
                int i = 0;
                while (n-- != 0) {
                    if (v1[i] != v2[i])
                        return false;
                    i++;
                }
                return true;
            }
        }
        return false;
    }

  划重点:== 与 equals 的区别

  对于未重写Object类中的equals方法,两者的作用是一样的,因为Object的equals方法也是使用 == 来判断两个对象是否相等的。

  == 操作符对于基本类型数据则比较的是他们的字面量的值是否相等,只要有一个是基本类型,那么另一个如果的封装类型,则会自动拆箱;对于两个类型都是引用类型,则比较的是两个对象的内存地址的值是否相等。

  equals方法如果未被重写,与 == 无异。一般地,我们都会重写Object中equals方法,比如上面的Integer和String都重写的equals方法,该方法比较的是字面量的值是否相等,有别于 ==。

  欢迎大家关注公众号: 【java解忧杂货铺】,里面会不定时发布一些技术博客;关注即可免费领取大量最新,最流行的技术教学视频: