参考来源:《深入理解Java虚拟机》 周志明 著
上一章:java语法糖–泛型和类型擦除
自动装箱拆箱和遍历循环也是我们在java里面使用的最多的。
让我们先从例子出发吧
public class Test {
public static void main(String[] args) {
List<Integer> list = Arrays.asList(1,2,3,4,5);
int sum = 0;
for (int i:list){
sum+=i;
}
System.out.println(sum);
}
}
看一下javap -c
解析出来的结果,如果你和我一样不熟悉java字节码字节码指令,没事,看注释就好,这里我联合idea它本身的反编译插件反编译出来的结果以及书中的编译结果来作对比分析(笔者懒得去找一款真正算得上标准的反编译器了)
书中反编译结果
public static void main(String[] args){
List list=Arrrays.asList(new Integer[]{
Integer.valueOf(1),
Integer.valueOf(2),
Integer.valueOf(3),
Integer.valueOf(4),
Integer.valueOf(5)
});
int sum = 0;
for(Iterator localItrerator=list.iterator();localIterator.haoNext();){
int i = ((Integer)localIterator.next()).intValue();
sum+=i;
}
System.out.println(sum);
}
这段代码的反编译结果中涉及到了泛型、自动装箱、自动拆箱、遍历循环、以及变长参数。泛型的类型擦除我在这里就不去详细描述了,那么我在这里简单的说一下自动装箱和自动拆箱:
自动装箱:
从上面的代码里面我们可以看到,装箱无非就是把一个基本的数据类型包装成一个对象(基本数据类型 如 byte,short,char,int,long,float,double等对应的包装类Byte,Short,Character,Integer,Long,Float,Double),这样就能在使用对象的地方使用它们,这个过程不是我们认为的操作,而是底层操作,如本例中调用Integer.valueOf方法,故称为 自动装箱。
自动拆箱:
既然有装箱,那就会有拆箱,拆箱的操作与装箱是相反的,也就是需要用到基本数据类型的操作时,相对于的包装类对象就会拆箱,也就是褪去自己对象的属性,变成一个基本数据类型,这也是底层操作,如本例中自己调用Integer.intValue方法,故称为自动拆箱
那么也许我们都会思考,它们会在什么时候发生呢?
一般会发生在:需要参数为对象的方法,你传了一个基本的数据类型,那么底层将会帮你自动装箱,或需要参数为基本数据类型,你传了一个是某个基本的数据类型的包装类对象,那么它也会发生自动拆箱操作。
关于变长参数和遍历循环:
变长参数的实现是通过传递数组对象来实现的,如例子中的Integer[]
遍历循环就是转换成迭代器的实现,所以一般要使用就必须实现Iterable接口。
上面的几个知识都是相对简单的,很容易就能够理解它的意思,感觉是没有任何我们值得注意的地方
不过,这里有个坑
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = 3;
Integer d = 3;
Integer e = 321;
Integer f = 321;
Long g = 3L;
System.out.println(c == d);
System.out.println(e == f);
System.out.println(c == (a + b));
System.out.println(c.equals(a + b));
System.out.println(g == (a + b));
System.out.println(g.equals(a + b));
}
结果是这样的
true
false
true
true
true
false
首先看第一对
true false
显然表示的是一个指向的对象是同一个,一个是不同的对象,这是为什么呢?
看一下自动装箱的valueOf的源码:
那么这里的low和high分别是多少呢?
就是-128,127,在这个范围内是指向IntegerCache中已经存在对象的引用,而超过就是会new一个Integer,故才会有上面的结果
后面的几个我就一并说吧
注意点:包装类的“==”运算在不遇到算术运算的情况下不会自动拆箱(言外之意就是只有在遇到算术运算的情况才会拆箱),以及它们的equals方法并不处理数据转型的问题
也就是说,“==”如果不拆箱,那么比较的是是否是同一对象,拆箱之后比较的就是值了,理解这里就好办了。
我们可以看一下equals的源码:
public boolean equals(Object obj) {
if (obj instanceof Long) {
return value == ((Long)obj).longValue();
}
return false;
}
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
故在euals会先里面的算术运算先开始拆箱,运算完后传参又自动装箱,又因为里面会比较对象,故第一个equals返回的是true(c是Integer,a+b装箱之后也是Integer)
第二个equals返回的则是false(g是Long)
所以,你懂了吗?
后面给你们抛个反编译的字节码执行指令吧
Compiled from "Test.java"
public class kexie.test.Test {
public kexie.test.Test();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: iconst_1
1: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
4: astore_1
5: iconst_2
6: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
9: astore_2
10: iconst_3
11: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
14: astore_3
15: iconst_3
16: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
19: astore 4
21: sipush 321
24: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
27: astore 5
29: sipush 321
32: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
35: astore 6
37: ldc2_w #3 // long 3l
40: invokestatic #5 // Method java/lang/Long.valueOf:(J)Ljava/lang/Long;
43: astore 7
45: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
48: aload_3
49: aload 4
51: if_acmpne 58
54: iconst_1
55: goto 59
58: iconst_0
59: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
62: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
65: aload 5
67: aload 6
69: if_acmpne 76
72: iconst_1
73: goto 77
76: iconst_0
77: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
80: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
83: aload_3
84: invokevirtual #8 // Method java/lang/Integer.intValue:()I
87: aload_1
88: invokevirtual #8 // Method java/lang/Integer.intValue:()I
91: aload_2
92: invokevirtual #8 // Method java/lang/Integer.intValue:()I
95: iadd
96: if_icmpne 103
99: iconst_1
100: goto 104
103: iconst_0
104: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
107: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
110: aload_3
111: aload_1
112: invokevirtual #8 // Method java/lang/Integer.intValue:()I
115: aload_2
116: invokevirtual #8 // Method java/lang/Integer.intValue:()I
119: iadd
120: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
123: invokevirtual #9 // Method java/lang/Integer.equals:(Ljava/lang/Object;)Z
126: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
129: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
132: aload 7
134: invokevirtual #10 // Method java/lang/Long.longValue:()J
137: aload_1
138: invokevirtual #8 // Method java/lang/Integer.intValue:()I
141: aload_2
142: invokevirtual #8 // Method java/lang/Integer.intValue:()I
145: iadd
146: i2l
147: lcmp
148: ifne 155
151: iconst_1
152: goto 156
155: iconst_0
156: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
159: getstatic #6 // Field java/lang/System.out:Ljava/io/PrintStream;
162: aload 7
164: aload_1
165: invokevirtual #8 // Method java/lang/Integer.intValue:()I
168: aload_2
169: invokevirtual #8 // Method java/lang/Integer.intValue:()I
172: iadd
173: invokestatic #2 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
176: invokevirtual #11 // Method java/lang/Long.equals:(Ljava/lang/Object;)Z
179: invokevirtual #7 // Method java/io/PrintStream.println:(Z)V
182: return
}