关于数据类、字符类的(==)、equals()方法和valueOf()方法的区别比较

2 篇文章 0 订阅
1 篇文章 0 订阅

今天遇到一个很简单String类型对比问题:String类型的equals()方法是怎么对比的?
当时眼前一热,把String类型当成普通类,普通类的equals()方法实质是(==),比较两个对象的地址是否相同。
回过神来,这肯定是不对的啊。


好了,回归整体,今天我们就来对比一下这些特殊类型
首先就从数据类型来进行分析,下面是网上一道经典的对基本数据类型int的拆箱、装箱的应用:

Integer i1 = 59; //自动装箱,等于i3的Integer.valueOf(59)
int i2 = 59;
Integer i3 = Integer.valueOf(59);
Integer i4 = new Integer(59); //创建一个新的Integer对象
System.out.println(i1 == i2);
System.out.println(i1 == i3);
System.out.println(i3 == i4);
System.out.println(i2 == i4);

如我注释里分析的一样,当包装类Integer初始对象i1,右边赋值为数字时,数字会自动装箱,那么,是如何装箱的呢?我们不妨在第一行代码中打断点,看看源码是怎么实现的:

public static Integer valueOf(int i) {
    assert IntegerCache.high >= 127;
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

如上代码,断点直接进入了Integer类的valuOf()方法内,下面将介绍一个被大家很容易忽视的重要知识点。
代码第一段有一个变量IntegerCache这是Integer类型自带的一个静态缓存,包含-128到127之间的Integer对象,所以每次Integer.valueOf()方法的参数是-128到127之间的整数,则得到的是静态缓存里的对象引用地址,换而言之,不在这之间时,才会新实例一个Integer对象返回。
所以,i1一定等于i3,因为它们其实是一个对象;而与基本数字类型i2进行对比时,都会被拆箱成数字进行比较。
下面是Integer类等于比较的结果和分析:

等于比较结果分析
i1==i2trueInteger对象遇int基本类型,自动拆箱比较数据
i1==i3true对象地址相同,为同一个对象
i1==i4falsei4是新实例的对象,两个为不同的对象
i2==i3trueInteger对象遇int基本类型,自动拆箱比较数据
i2==i4trueInteger对象遇int基本类型,自动拆箱比较数据
i3==i4false两个为不同的对象

温馨提示:使用valueOf()时,一定要注意数字是否在缓存对象中。
分析完Integer类的(==)比较,相信你还会想到类的equals()方法吧,现在是不是被弄得有点模糊了,其实Integer类的equals()方法就是比较值是否相同,但我们还是来冷静分析一波。
Integer类的equals()方法的源代码如下:

public boolean equals(Object obj) {
    if (obj instanceof Integer) {
        return value == ((Integer)obj).intValue();
    }
    return false;
}

首先equals里的参数是一个对象,当对象是属于Integer类型时,再比较两个对象的数值。所以,i1、i3、i4对象的value值都是相同。
那么,equals内传入的是基本数据类型i2呢?
代码i1.equals(i2)断点首先进入的是Integer类的valueOf()方法,所以i2先被equals()方法自动装箱了,再进行数值比较。


好的,我们再来拓展一下,那么对于其他包装类呢,是否也是与Integer类一样呢?

public static Long valueOf(long l) {
    final int offset = 128;
    if (l >= -128 && l <= 127) { // will cache
        return LongCache.cache[(int)l + offset];
    }
    return new Long(l);
}

博主分别查看他们的源代码,结论是浮点类型的包装类没有静态缓存,而Long、Short、Byte类都有-128到127的对应对象的静态缓存
需要注意的是,Short类的基本数据类型是8位,最大值是127,最小值是-128,所以Short类只要不是new实例的对象,得到的都是Short类的静态缓存对象引用的结果。


接下来回到本篇博客的第一个问题,我们来介绍下String类的对比,看看下面的代码:

String s1 = "good";
String s2 = String.valueOf("good");
String s3 = new String("good");
System.out.println(s1 == s2);
System.out.println(s1 == s3);
System.out.println(s2 == s3);
System.out.println(s1.equals(s2));
System.out.println(s1.equals(s3));
System.out.println(s2.equals(s3));

我们都应该了解,String类的(==)与普通的Object类一样,就是比较对象的地址,所以让我们看看上面实例的三个String对象是不是在同一个地址。
s1没有new一个对象,是直接赋值字符串的,那么在分析之前,我们得明白String直接赋值与使用new的区别
JVM里有一个字符串常量池,每个字符串对象最终指向的都是这个池内的字符串对象。当String类直接赋值时,如果常量池内存在这个字符串,则s1直接指向常量池的地址,若没有,则先在常量池内创建这个字符串对象,s1直接指向常量池这个字符串的内存地址;
当String类使用new实例对象时,首先在堆里创建这个对象,若是常量池内没这个字符串,则也创建一个,然后堆里的对象的value指向常量池内的字符串。
所以s1是一个引用地址,先在常量池内新建一个对象,s1直接指向常量池;
s2采用String的valueOf()方法,所以我们看看它的源代码:

public static String valueOf(Object obj) {
    return (obj == null) ? "null" : obj.toString();
}

可以了解,方法的参数是一个对象,所以字符串在进入方法时已经完成了和s1一样的操作,所以s2也是和s1一样,共同指向常量池内相同的地址;
s3很明显,直接new实例的,对象地址肯定在堆里。
所以输出的结果如下表:

等于对比结果
s1==s2true
s1==s3false
s2==s3false

了解完这些,大家接下来思考那么String类的equals()方法是比较什么呢,是对比字符,还是对比字符存在的地址呢?我们来看看源代码:

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

从代码分析,若对象的指向地址相同,无须比较,直接返回true;若不相同,则再循环比较对象value的每一个字符是否相等。
其实对于地址相同,无须比较字符串的理论,我们若懂了上面说的String赋值的区别就能懂,无论String对象是存在堆还是存在常量池,最终都是指向常量池的对象地址;所以一开始对象地址相同,可能是在堆中同一个对象或者都是直接指向常量池的对象,也就无须再比较字符串了。
因此,上文三个equals对比,因字符串相同,都是true结果。

好了,今天就总结这么多,小编还会继续探究String类的一些特殊点和intern()方法的用法,期待一下。
要是博友喜欢这篇文章,记得点赞收藏哈~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值