java自动装箱、拆箱、循环遍历与自动装箱的陷阱
1. 自动装箱、拆箱、循环遍历
- 编译之前的代码
package com.zhu.part10;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* 自动装箱,自动拆箱,循环遍历
* @author xiaozhu
* @date 2022年07月18日 23:20
*/
public class Demo10_11 {
public static void main(String[] args) {
//自动装箱
List<Integer> list = Arrays.asList(1, 2, 3, 4);
int sum = 0;
//自动拆箱,循环遍历
for (int i : list) {
sum += i;
}
System.out.println(sum);
}
}
- jdk8 编译之后的class文件中的代码(idea又自动使用Fernflower进行了反编译)
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.zhu.part10;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
public class Demo10_11 {
public Demo10_11() {
}
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1, 2, 3, 4);
int sum = 0;
int i;
for(Iterator var3 = list.iterator(); var3.hasNext(); sum += i) {
i = (Integer)var3.next();
}
System.out.println(sum);
}
}
- 《深入理解java虚拟机》作者环境编译后的class文件
package com.zhu.part10;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
/**
* 自动装箱、拆箱与遍历循环之后
* @author xiaozhu
* @date 2022年07月18日 23:24 $
*/
public class Demo10_12 {
public static void main(String[] args) {
//转化成了对应的包装和还原方法:Integer.valueOf(),Integer.intValue()
//变长参数,调用的时候使用了一个数组类型的参数
List list = Arrays.asList(new Integer[] {
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3),
Integer.valueOf(4)
});
int sum = 0;
//遍历循环则是还原成了迭代器去实现
for (Iterator localIterator = list.iterator(); localIterator.hasNext();) {
sum += ((Integer)localIterator.next()).intValue();
}
System.out.println(sum);
}
}
在泛型中,自动装箱、拆箱转化成对应的包装和还原方法,比如自动装箱Integer.valueOf() 和自动拆箱 Integer.intValue()
我们可以重点查看自动装箱方法的源码,Integer.valueOf()
//这个源码是个坑
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,在自动装箱的时候,如果数字范围在-128到127,则直接返回缓存中已有的对象,不在这个范围之内则new一个新的对象。
这也就以下代码打印false的原因
Integer e = 321;
Integer f = 321;
System.out.println(e == f);
//打印false
2. 自动拆箱的陷阱
package com.zhu.part10;
/**
* 自动装箱的陷阱
* @author xiaozhu
* @date 2022年07月20日 10:43 $
*/
public class Demo10_13 {
public static void main(String[] args) {
//Integer 是 int的包装类
//Long 是 long的包装类
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
//1、包装类的 "==" 运算在不遇到算数运算的时候不会自动拆箱,以及它们的equals()方法不处理数据类型转换
System.out.println(c == d); //true
//2、安照1中的思路应该就是true,但是此处也是一个小坑,上文中有详细解释
System.out.println(e == f); //false
//3、true 自动拆箱后的值和类型相等
System.out.println(c == (a + b));
//4、true 值和类型都相等
System.out.println(c.equals(a + b));
//包装类的 "==" 运算在不遇到算数运算的时候不会自动拆箱
//5、a + b是算数运算 ,所以包装类拆箱,拆箱后两边数值相等,返回true
System.out.println(g == (a + b));//true
//6、包装类的equals()方法不处理数据类型转换,g和a+b的类型不同,所以返回false
System.out.println(g.equals(a + b));
}
}
一些方法技巧:
- 我们可以在idea中查看java文件编译之后的class文件
- 遇到问题我们可以通过查看源码的方式进一步发现和理解问题的所在
参考书籍:《深入理解java虚拟机》
如有错误和冒犯,欢迎指正。