之所以写这篇博客,是因为在学习JavaSE的时候遇到了一个问题,由解决问题延伸到该类问题的总结,大概讲讲解决这个问题的思路:
首先先说说“ == ”吧,java中的“==”表示比较值。对基本数据类型来说,“==”就是比的值,这没有什么疑问;主要说的就是对于引用数据类型“==”比较的就不是对象的值了,而是对象的地址!!!
这似乎也没有什么可以说的,但是,对于引用类型中的包装类而言,由于他们具有自动装箱和自动拆箱的特点,使得初学的时候对使用“==”来比较两个包装类是否相等很是迷惑!接下来就对所有的包装类和String类做一个详细的比较和说明!先讨论的是包装类中的非new创建对象!
Byte的“==”比较
测试源代码
public class ByteBoxing { public static void main(String[] args) { //byte取值范围-128~127 Byte b = 1; Byte b1 = 1; System.out.println(b == b1);//测试结果为true Byte b2 = 120; Byte b3 = 120; System.out.println(b2 == b3);//测试结果为true } }
测试发现:b=b1,也就是说b和b1指向的是同一个对象!原因是什么呢?如下是Byte类的源码中的截图部分:
从截图看出,Byte类型在类加载的时候会自动生成一个大小是256个的Byte的常量对象,就是说这256个对象是一直不会改变的,而且其中的值是从-128到+127!这也就说明Byte类型是有缓存的!缓存是值是-128到127的256个对象!而在创建b的时候先去堆中找缓存的对象,看看是否有和b值相等的对象,如果有的话,就把这个缓存对象的地址个b。所以当使用上述代码创建对象的时候,都会先去找缓存对象,如果没有相等的话,就再在堆中开辟一个对象出来!所以只要是b的值在-128到127之间,那么就不用开辟对象了,直接把已经有的缓存对象的地址给要引用的变量就可以了。这就导致了b1操作的时候,去找缓存对象,发现已经有缓存对象了,就把该缓存对象的地址赋给b1。这就是b == b1的结果是true了!
2. Short的“==”比较
测试源码:
public class ShortBoxing { public static void main(String[] args) { Short s = 1; Short s1 = 1; System.out.println(s == s1);//执行结果为true @1 Short s2 = 150; Short s3 = 150; System.out.println(s2 == s3); //执行结果为false @2 } }
发现 @2的结果是flase。这有点迷惑啊,不用慌,我们去看看Short的源代码是怎么设计的
从图中我们看到,Short类型的源代码中也设计了256个缓存对象,值也是-128到127,所以只要Short要创建的对象的值在这个区间内,那就不必再开辟新的空间了,直接把缓存对象的地址给引用变量就可以了!所以使用“==”比较的时候,就会相等;但是值超过了缓存的值的范围,那么就只有new一个新的对象出来了,这时候(只要是new出来的对象)使用“==”就不相同了!
3. Integer类型的“==”比较
测试源码:
public class IntegerBoxing { public static void main(String[] args) { Integer i = 1; Integer i1 = 1; System.out.println(i = i1);//答案是 : true } }
从Byte和Short的解决思路上来说,我们先去看看java中的Integer的源码设计,有没有缓存,有的话,缓存大小是多大,值又是多少!下面是截图
4. Long类型的“==”比较
测试源码:
public class LongBoxing { public static void main(String[] args) { Long m = 2l; Long n = 2l; System.out.println(m == n);//结果是: true } }
一样的思路,去看Long的源代码设计,如下图
同样的,Long还是和Integer,Byte一样拥有256个缓存对象,值是-128到127!
5. Float类型,Double类型
测试源码:
public class FloatBoxing { public static void main(String[] args) { Float f = 12f; Float f1 = 12 f; System.out.println(f == f1); //!!!结果是false } } public class DoubleBoxing { public static void main(String[] args) { Double d = 1.0; Double d1 = 1.0; System.out.println(d == d1); //!!!结果是false } }
从图中看出就算float和double的值在-128到127区间中,结果也是false,难道java中Flaot和 Double的源码设计和整数型的包装类不同吗?那我们就去看看源码,到底是怎么一回事
!!!查看了Float和Double的源代码,确实里面没有缓存设计!!!这要注意和前面的区分了
6.Character类型“==”
测试源码:
public class CharacterBoxing { public static void main(String[] args) { Character c = ‘ ‘; //空字符,对应asscii码值是 0 Character c1= ‘ ‘; System.out.println(c == c1); //结果是true } }
从测试结果看出,Character是有缓存的,我们这就去看看源代码的设计
从源码看出,Character是有128个缓存的,值是0~128,(具体的符号需要对应asscii码表)
7.Boolean类型“==”
测试源码
public class BooleanBoxing { public static void main(String[] args) { Boolean b = false; Boolean b1 = false; System.out.println(b == b1);//测试结果: true } }
测试代码看出的是: Boolean也可能存在缓存,我们去看看Boolean的源代码设计中是否有缓存设计
8. String类型“==”
测试源码
public class StringBoxing { public static void main(String[] args) { String s = "hello"; String s1 = "hello"; System.out.println(s == s1);//结果是true } }
这个测试结果看出的是,String有可能是有缓存设计的,我们这就去看看String的源码
================
好了,到这儿我们就把String类和包装类的缓存设计带来的“==”问题解决完了。之所以有缓存设计是因为这些是我们经常用的数据对象,设定合理的缓存,可以大大提高我们代码执行速度(不用再开辟空间而耗时间了)和大大节约内存空间(不用重复new对象)
接着说说equals这个判断两个对象关系的方法吧。这个方法是Object类里面自带的方法,所有类都是Object类的子类,所以,每个类都有从Object那儿继承下来的equals方法。但是,很多时候,继承下来的判断方法并不适合当前类的个性化比较,所以有很多系统类都是对equals方法进行了重写的!所以我们发现使用equals方法得到的结果差人强意的时候,就一定要去看看该类不是重写了equals方法,如果重写了,重写的规则是什么!这个问题就是我要说的!
我们先去看看Object类中的equals方法的比较规则是什么:
我们看出没有重写的equals方法是比较的对象的引用地址;接着我么再看看包装类的equals方法:
我们看出8中包装类中的equals方法是被重写了的,重写规则是比较对象的值的大小,不再是比较引用地址了;接着我们一并看看String类的equals方法
我们看出String类的equals方法也是被重写了的,重写规则也是比较俩个字符串的值是否相同,相同的话,就返回true
本文出自 “11929788” 博客,请务必保留此出处http://11939788.blog.51cto.com/11929788/1952485
原文地址:http://11939788.blog.51cto.com/11929788/1952485