关于数据类、字符类的(==)、equals()方法和valueOf()方法的区别比较
2018-03-15 00:12
267 查看
今天遇到一个很简单String类型对比问题:String类型的equals()方法是怎么对比的?
当时眼前一热,把String类型当成普通类,普通类的equals()方法实质是(==),比较两个对象的地址是否相同。
回过神来,这肯定是不对的啊。
好了,回归整体,今天我们就来对比一下这些特殊类型。
首先就从数据类型来进行分析,下面是网上一道经典的对基本数据类型int的拆箱、装箱的应用:
如我注释里分析的一样,当包装类Integer初始对象i1,右边赋值为数字时,数字会自动装箱,那么,是如何装箱的呢?我们不妨在第一行代码中打断点,看看源码是怎么实现的:
如上代码,断点直接进入了Integer类的valuOf()方法内,下面将介绍一个被大家很容易忽视的重要知识点。
代码第一段有一个变量IntegerCache ,这是Integer类型自带的一个静态缓存,包含-128到127之间的Integer对象,所以每次Integer.valueOf()方法的参数是-128到127之间的整数,则得到的是静态缓存里的对象引用地址,换而言之,不在这之间时,才会新实例一个Integer对象返回。
所以,i1一定等于i3,因为它们其实是一个对象;而与基本数字类型i2进行对比时,都会被拆箱成数字进行比较。
下面是Integer类等于比较的结果和分析:
温馨提示:使用valueOf()时,一定要注意数字是否在缓存对象中。
分析完Integer类的(==)比较,相信你还会想到类的equals()方法吧,现在是不是被弄得有点模糊了,其实Integer类的equals()方法就是比较值是否相同,但我们还是来冷静分析一波。
Integer类的equals()方法的源代码如下:
首先equals里的参数是一个对象,当对象是属于Integer类型时,再比较两个对象的数值。所以,i1、i3、i4对象的value值都是相同。
那么,equals内传入的是基本数据类型i2呢?
代码
好的,我们再来拓展一下,那么对于其他包装类呢,是否也是与Integer类一样呢?
博主分别查看他们的源代码,结论是浮点类型的包装类没有静态缓存,而Long、Short、Byte类都有-128到127的对应对象的静态缓存。
需要注意的是,Short类的基本数据类型是8位,最大值是127,最小值是-128,所以Short类只要不是new实例的对象,得到的都是Short类的静态缓存对象引用的结果。
接下来回到本篇博客的第一个问题,我们来介绍下String类的对比,看看下面的代码:
我们都应该了解,String类的(==)与普通的Object类一样,就是比较对象的地址,所以让我们看看上面实例的三个String对象是不是在同一个地址。
s1没有new一个对象,是直接赋值字符串的,那么在分析之前,我们得明白String直接赋值与使用new的区别:
JVM里有一个字符串常量池,每个字符串对象最终指向的都是这个池内的字符串对象。当String类直接赋值时,如果常量池内存在这个字符串,则s1直接指向常量池的地址,若没有,则先在常量池内创建这个字符串对象,s1直接指向常量池这个字符串的内存地址;
当String类使用new实例对象时,首先在堆里创建这个对象,若是常量池内没这个字符串,则也创建一个,然后堆里的对象的value指向常量池内的字符串。
所以s1是一个引用地址,先在常量池内新建一个对象,s1直接指向常量池;
s2采用String的valueOf()方法,所以我们看看它的源代码:
可以了解,方法的参数是一个对象,所以字符串在进入方法时已经完成了和s1一样的操作,所以s2也是和s1一样,共同指向常量池内相同的地址;
s3很明显,直接new实例的,对象地址肯定在堆里。
所以输出的结果如下表:
了解完这些,大家接下来思考那么String类的equals()方法是比较什么呢,是对比字符,还是对比字符存在的地址呢?我们来看看源代码:
从代码分析,若对象的指向地址相同,无须比较,直接返回true;若不相同,则再循环比较对象value的每一个字符是否相等。
其实对于地址相同,无须比较字符串的理论,我们若懂了上面说的String赋值的区别就能懂,无论String对象是存在堆还是存在常量池,最终都是指向常量池的对象地址;所以一开始对象地址相同,可能是在堆中同一个对象或者都是直接指向常量池的对象,也就无须再比较字符串了。
因此,上文三个equals对比,因字符串相同,都是true结果。
好了,今天就总结这么多,小编还会继续探究String类的一些特殊点和intern()方法的用法,期待一下。
要是博友喜欢这篇文章,记得点赞收藏哈~~
当时眼前一热,把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==i2 | true | Integer对象遇int基本类型,自动拆箱比较数据 |
i1==i3 | true | 对象地址相同,为同一个对象 |
i1==i4 | false | i4是新实例的对象,两个为不同的对象 |
i2==i3 | true | Integer对象遇int基本类型,自动拆箱比较数据 |
i2==i4 | true | Integer对象遇int基本类型,自动拆箱比较数据 |
i3==i4 | false | 两个为不同的对象 |
分析完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==s2 | true |
s1==s3 | false |
s2==s3 | false |
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()方法的用法,期待一下。
要是博友喜欢这篇文章,记得点赞收藏哈~~
相关文章推荐
- 知识点干货—关于equals方法,Hashcode方法和两个对象的比较
- 关于用equals()方法去比较对象
- 关于Integer类中parseInt()和valueOf()方法的区别以及int和String类性的转换.以及String类valueOf()方法
- 【收藏】比较全的关于js获取css样式各种方法区别
- Java中关于==和equal的区别以及equals()方法重写
- java中关于“==”和“equals()”方法的区别
- ==与.equals方法的比较区别
- Java中两个对象的比较 equals()方法和==号的区别
- String使用equals方法和==分别比较的区别
- String.valueOf(l)方法引发的关于2String、String.valueOf、toString区别的思考
- 关于C#中==与Equals方法的区别总结
- Java中关于==和equal的区别 以及equals()方法重写
- "=="和"equals"两种比较方法的区别
- Java中两个对象的比较 equals()方法和==号的区别
- 【概念解析一】两种比较方法的区别:== 和 equals
- null 与 “” 的区别以及“==”和equals方法比较
- 关于equals()方法和==的区别
- 关于java中比较所用的"=="与s.equals()方法的不同
- 关于hibernate中实体中equals和hashcode方法的重写
- 关于List的add方法与addAll方法的区别