Java踩坑笔记:基本类型与包装类之间的自动转换
示例如下:
Integer x = 400; Integer y = x; x++; System.out.println(x == y);//false
这里可以“解yan释shi”为x和y都是基本类型,具有值语义(雾),x自增后y不变。
Integer x = 400; Integer y = x; System.out.println(x == y);//true
这个例子没毛病,x和y“貌似”都是基本类型。
Integer x = 400; Integer y = 400; System.out.println(x == y);//false
开始有点懵逼了,x和y不相等应该是因为触发了自动装箱(基本类型自动转换到包装类)以及Integer的缓存机制(默认-128至127)。
Integer x = 2; Integer y = 2; System.out.println(x == y);//true
这个例子也没毛病,因为Integer的缓存机制,数字2的包装类实例被事先被缓存了。
那么问题来了,为什么x和y有时候是值语义有时候是引用语义呢?再做一个实验:
Integer x = 400; Integer y = 400; System.out.println(x.equals(y));//true
得到了想要的结果,这时候想起来Integer之间的比较和String之间的比较是相似的。
或者说,这里的核心原理是“==”运算符比较的是两个实例的指针(引用),相等即表示内存地址相同。
还有一点:
int x = 400; int y = 400; System.out.println(x == y);//true
这里就没有被自动装箱。
从课本上引用一段话:“如果一个基本类型值出现在需要对象的环境中,编译器会将基本类型值进行自动装箱;如果一个对象出现在需要基本类型值的环境中,编译器将对象进行自动开箱。”
由此可见,自动开装箱与否,是由编译器决定的,这是一个从jdk1.5开始引入的语法糖。
对了,第一个例子和第二个例子的原理就需要重新解释了:
Integer x = 400;//触发自动装箱,等同于Integer x = new Integer(400); Integer y = x;//没有触发自动转换 System.out.println(x == y);//y的引用和x相同,true了 x++;//触发自动开箱和自动装箱,等同于x = x + 1;(这时候x的引用已经更新了!我之前也没注意到。。) System.out.println(x == y);//理所当然地false了
更进一步的思考,自动装/开箱的条件,即所谓的需要的环境是怎样的呢?我手头暂时没有更权威的资料,我猜测,赋值的时候根据声明类型来决定,运算的时候根据是否有必要转换来决定。
那么问题都被解决了,由于声明变量的时候声明类型是包装类,所以编译器帮我们把int类型自动装箱成包装类类型;“==”运算符支持对象与对象之间的运算,于是“==”运算符就跑去比较对象了;“=”运算符支持对象引用的赋值,于是“=”运算符就跑去赋引用值了。
得出来的实践经验的话,就是以后要尽量小心Integer,不能随便用来声明整型,毕竟碰上“==”运算符,再加上Integer的缓存机制。这三种东西混在一起,简直要爆炸。。XD
最无脑方便万无一失的方法,统统用equals比较是否相等,哈哈。
彩蛋:
Integer x = 400; Integer y = 401; System.out.println(x < y);//true
Integer x = 400; Integer y = 401; System.out.println(x > y);//false
(“<”“>”运算符不支持对象的运算,触发了自动开箱)