吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 1554|回复: 5
收起左侧

[Java 转载] 详解Java中==和equals()的区别

[复制链接]
smallmin 发表于 2020-7-13 11:34

众所周知,在 Java 编程中,程序员通常会使用==equals()来简单的比较地址,内容是否相等。而这两者之间的使用区别,对于初学 Java 的同学来说可能会比较迷糊。我将根据下面的几段示例程序,来对这两种比较方法进行分析,供大家参考:

    private static void method1() {
        Integer n1 = new Integer(5);
        Integer n2 = new Integer(5);

        System.out.println(n1.equals(n2));// true
        System.out.println(n1 == n2);// false
    }

method1()中,第一个的输出打印结果如我所料,equals()比较两者的内容,输出结果会是true,但是第二个打印输出结果,如果是在我刚接触 Java 时,肯定会毅然决然的认为这条输出的结果应该是true。但事实是,尽管n1 和 n2 看起来内容相同,但是程序执行出来的结果却是false

翻阅《 Java 编程思想(第四版)》,在书中有这么一段话:

== 和 != 比较的就是对象的引用。

如果想比较两个对象的实际内容是否相同,必须使用所有对象都使用的特殊方法 equals() 方法。但这个方法不适用于“基本类型”,基本类型直接使用 == 或 != 即可。

通过书里的这段话我们进行分析,在method1()方法里,尽管 n1,n2 两个对象的内容是相等的,但是它们本质上是属于两个创建出来的不同的对象。因此,在method1()中,两个不同的对象它们所引用的地址当然是不相同的,故method1()的第二个输出结果为false

    private static void method2() {
        Integer n3 = 127;
        Integer n4 = 127;

        Integer n5 = 128;
        Integer n6 = 128;

        System.out.println(n3 == n4);// true
        System.out.println(n5 == n6);// false
    }

method2()中可以看到,这里的写法不是通过 new 形式来创建 Integer 对象了 ,而是通过自动装箱方式创建一个 Integer 对象。何谓自动装箱,通俗的解释就是自动将基本数据类型转换为包装器类型。记得我在method1()中写道:== 比较的就是对象的引用。在这里 n3 ,n4 属于两个不同的对象,通过 ==比较的是两个对象的引用,理应输出的结果是false。那么结果为什么是true呢?

为了便于分析,我们可以仅在代码里保留一行代码。

public class Equivalence {
    public static void main(String[] args) {
        Integer n3 = 127;
    }
}

通过编译该 java 文件后,在控制台窗口中切换到其编译后输出的 *.class字节码文件的位置,输入javap -c "文件名.class"对其进行反编译。我们来看看保留的这段代码执行编译中干了什么事情:

从打印输出的内容截图里,我们可以知道系统当使用 Integer 自动装箱时,系统会帮我们会调用Integer.valueOf()方法,我们可以查看下该方法的源码。

public final class Integer extends Number implements Comparable<Integer> {

        // ...
        public static Integer valueOf(int i) {
            // 对传进来的数值进行比较
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }
        /**
        * Integer的内部类,用于缓存 -128 ~ 127的数值
        */
        private static class IntegerCache {
        static final int low = -128;
        static final int high;
        static final Integer cache[];

        static {
            // high value may be configured by property
            int h = 127;
            String integerCacheHighPropValue =
                sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
            if (integerCacheHighPropValue != null) {
                try {
                    int i = parseInt(integerCacheHighPropValue);
                    i = Math.max(i, 127);
                    // Maximum array size is Integer.MAX_VALUE
                    h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
                } catch( NumberFormatException nfe) {
                    // If the property cannot be parsed into an int, ignore it.
                }
            }
            high = h;

            cache = new Integer[(high - low) + 1];
            int j = low;
            for(int k = 0; k < cache.length; k++)
                cache[k] = new Integer(j++);

            // range [-128, 127] must be interned (JLS7 5.1.7)
            assert IntegerCache.high >= 127;
        }

        private IntegerCache() {}
    }
}

通过查阅源码注释(本文未展示),这个方法是在JDK 1.5时加入的。当执行Integer.valueOf()方法时,会将传进来的数值与IntegerCache.lowIntegerCache.high进行比较。为了避免多次创建浪费资源,通过IntegerCache这个 Integer 的缓存内部类,将-128 ~ 127的数值缓存起来。

也就是说,当我们使用 Integer 进行自动装箱操作时,会将装箱的数值与缓存的数值进行比较,如果在该缓存范围内,则会返回一个已经创建好的 Integer 对象,反之将会重新创建一个 Integer 对象。这么一来,也就可以解释的通:为什么 n3 、n4数值相等,n4、n5数值也同样相等,输出的结果却是不同的原因了。

上述内容,我们仅是通过 Integer 包装类的源码对==equals()两种比较方式进行分析。实际上,不同的包装类,所执行出的效果也不尽相同。

接下来我们再通过代码示例,对equals()进行分析。

public class Equivalence {
    public static void main(String[] args) {
        method3();
    }
    private static void method3() {
        Value v1 = new Value(100);
        Value v2 = new Value(100);
        String s1 = new String("smallmin");
        String s2 = new String("smallmin");
        System.out.println(v1.equals(v2));// false
        System.out.println(s1.equals(s2));// true
    }

}
class Value {
    int i;

    public Value(int i) {
        super();
        this.i = i;
    }

}

记得我们在method1()中讲过,比较两个对象的实际内容是否相同,必须使用所有对象都使用的特殊方法 equals() 方法。那么上面示例代码中,为什么v1 与 v2 比较输出结果会是false,而 s1 与 s2 比较出的结果却是true呢?

method1()中我们 Integer 对象之间通过equals()比较以及本例中,String对象间通过equals()输出的比较结果都是true(对象内容相等的前提下)的原因是,Integer 和 String 等类中,都对equals()进行了重写,因文章篇幅原因故不再逐一附上源码。

而v1 与 v2 是我们自己创建的对象,没有重写equals()方法,当调用该方法时执行的是父类Objectequals()方法。而Objectequals()方法通过查看源码可得知,其实它是比较两者之间的引用地址。v1 和 v2 尽管对象的内容相同,但是它们分别来自不同的对象,引用地址当然不同,故两者比较的结果是false

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

总结:

  • 基本数据类型之间比较,可直接使用==进行比较。
  • 当比较两个对象引用地址是否为同个地址时,使用==进行比较。
  • 在重写了equals()的前提下,比较对象内容或者其他自定义的比较方法时,使用equals()比较。

免费评分

参与人数 4吾爱币 +9 热心值 +4 收起 理由
keith_yang + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
尾叶 + 1 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!
靓仔小黄 + 1 + 1 谢谢@Thanks!
苏紫方璇 + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

靓仔小黄 发表于 2020-7-13 15:41
枯了前天考试就考到n1==n2为false,应该是正确的,我以为是true,答错了早点看到这篇帖子就好了
 楼主| smallmin 发表于 2020-7-13 16:42
靓仔小黄 发表于 2020-7-13 15:41
枯了前天考试就考到n1==n2为false,应该是正确的,我以为是true,答错了早点看到这 ...

哈哈,我的错。我其实早写了,只是没发出来。这次错了可能会印象更深点了,加油。
木偶的丧歌 发表于 2020-7-14 13:46
 楼主| smallmin 发表于 2020-7-14 16:39
木偶的丧歌 发表于 2020-7-14 13:46
用== equals 来分析 堆 栈 常量池更好

确实不错的提议,感谢指点,抽空我把文章再完善下
Tangyaya 发表于 2020-7-15 13:21
java中基本类型的包装类的大部分都实现了常量池技术,
即Byte,Short,Integer,Long,Character,Boolean;
Float,Double并没有实现常量池技术。
String 也有使用常量池技术。
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-11-26 03:28

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表