C 語言的高手都有一個能力,就是看到 C 代碼,能腦補彙編代碼。看到一段代碼會裏面知道這段代碼生成的代碼好不好。那麼,作爲 Java 高手,腦補字節碼也是必備能力之一,而通過本文,你離這一目標更近了一步。
最自動裝箱和拆箱的瞭解,絕大多數程序員都知道會進行自動的裝箱和拆箱,裝箱和拆箱會影響性能。但是具體什麼場景下會裝箱,什麼場景下會拆箱?裝箱和拆箱的底層原理是啥?裝箱和拆箱在什麼哪個階段進行?
問題
- 裝箱和拆箱的原理是啥?
- 自動裝箱和拆箱在什麼哪個階段進行?編譯時還是運行時?
自動裝箱拆箱
原始類型:boolean,byte,char,short,int,long,float,double
封裝類型:Boolean,Byte,Character,Short,Integer,Long,Float,Double
裝箱:將 Java 原始類型轉換爲封裝類型
拆箱:將對象轉換爲原始類型
Integer
void testInt() {
int a = 10;
int b = 128;
Integer A = a;
Integer B = b;
Integer C = new Integer(10);
Integer D = new Integer(128);
int c = C;
int d = D;
}
對應字節碼
// int a = 10
0 bipush 10
2 istore_1
// int b = 128
3 sipush 128
6 istore_2
// Integer A = Integer.valueOf(a)
7 iload_1
8 invokestatic #2 <java/lang/Integer.valueOf> // Integer.valueOf(a)
11 astore_3 // A = Integer.valueOf(a)
// Integer B = Integer.valueOf(b)
12 iload_2
13 invokestatic #2 <java/lang/Integer.valueOf> // Integer.valueOf(b)
16 astore 4 // B = Integer.valueOf(b)
// Integer C = new Integer(10)
18 new #3 <java/lang/Integer> // 在java堆上爲 Integer 對象分配內存空間,並將地址壓入操作數棧頂;
21 dup //複製操作數棧頂值,並將其壓入棧頂
22 bipush 10
24 invokespecial #4 <java/lang/Integer.<init>> // new Integer(10)
27 astore 5 // C = new Integer(10)
// Integer D = new Integer(128);
29 new #3 <java/lang/Integer>
32 dup
33 sipush 128
36 invokespecial #4 <java/lang/Integer.<init>> // new Integer(128)
39 astore 6 // D = new Integer(128)
// int c = Integer.intValue(C);
41 aload 5 // 加載 C
43 invokevirtual #5 <java/lang/Integer.intValue> // Integer.intValue(C);
46 istore 7 // c = C
// int d = Integer.intValue(D);
48 aload 6 // 加載 D
50 invokevirtual #5 <java/lang/Integer.intValue>
53 istore 8
55 return
注:對於每一個new指令來說一般編譯器都會在其下面生成一個dup指令,這是因爲實例的初始化方法肯定需要用到一次,然後第二個留給程序員使用,例如給變量賦值,拋出異常等,如果我們不用,那編譯器也會生成dup指令,在初始化方法調用完成後再從棧頂pop出來。
結論
- 自動裝箱:valueOf
- 自動拆箱:intValue
Short
void testShort() {
short a = 10;
short b = 128;
Short A = a;
Short B = b;
Short C = new Short((short)10);
Short D = new Short((short)128);
short c = C;
short d = D;
}
對應字節碼
// a = 10
0 bipush 10
2 istore_1
// b = 128
3 sipush 128
6 istore_2
// A = Short.valueOf(a);
7 iload_1
8 invokestatic #6 <java/lang/Short.valueOf>
11 astore_3
// B = Short.valueOf(b);
12 iload_2
13 invokestatic #6 <java/lang/Short.valueOf>
16 astore 4
// C = new Short(10);
18 new #7 <java/lang/Short>
21 dup
22 bipush 10
24 invokespecial #8 <java/lang/Short.<init>>
27 astore 5
// D = new Short(128);
29 new #7 <java/lang/Short>
32 dup
33 sipush 128
36 invokespecial #8 <java/lang/Short.<init>>
39 astore 6
// c = Short.shortValue(C)
41 aload 5
43 invokevirtual #9 <java/lang/Short.shortValue>
46 istore 7
// d = Short.shortValue(D)
48 aload 6
50 invokevirtual #9 <java/lang/Short.shortValue>
53 istore 8
55 return
結論
- 自動裝箱:valueOf
- 自動拆箱:shortValue
參數和返回值
void test() {
// Integer.intValue(test1(Integer.valueOf(1)))
int i1 = test1(1);
// Integer.intValue(test2(1))
int i2 = test2(1);
// test3(Integer.valueOf(1))
int i3 = test3(1);
// test4(1)
int i4 = test4(1);
// Integer.intValue(test1(new Integer(1)))
int i5 = test1(new Integer(1));
// Integer.intValue(test2((new Integer(1)).intValue()))
int i6 = test2(new Integer(1));
// test3(new Integer(1))
int i7 = test3(new Integer(1));
// test4((new Integer(1)).intValue())
int i8 = test4(new Integer(1));
// test1(Integer.valueOf(1))
Integer u1 = test1(1);
// test2(1)
Integer u2 = test2(1);
// Integer.valueOf(test3(Integer.valueOf(1)))
Integer u3 = test3(1);
// Integer.valueOf(test4(1))
Integer u4 = test4(1);
// test1(new Integer(1))
Integer u5 = test1(new Integer(1));
// test2((new Integer(1)).intValue())
Integer u6 = test2(new Integer(1));
// Integer.valueOf(test3((new Integer(1)))
Integer u7 = test3(new Integer(1));
// Integer.valueOf(test4((new Integer(1)).intValue()))
Integer u8 = test4(new Integer(1));
}
Integer test1(Integer param) {
return param;
}
Integer test2(int param) {
return param;
}
int test3(Integer param) {
return param;
}
int test4(int param) {
return param;
}
對應字節碼
// Integer.intValue(test1(Integer.valueOf(1)))
0 aload_0
1 iconst_1
2 invokestatic #2 <java/lang/Integer.valueOf>
5 invokevirtual #10 <com/wenxueliu/leetcode/Test1.test1>
8 invokevirtual #5 <java/lang/Integer.intValue>
11 istore_1
// Integer.intValue(test2(1))
12 aload_0
13 iconst_1
14 invokevirtual #11 <com/wenxueliu/leetcode/Test1.test2>
17 invokevirtual #5 <java/lang/Integer.intValue>
20 istore_2
// test3(Integer.valueOf(1))
21 aload_0
22 iconst_1
23 invokestatic #2 <java/lang/Integer.valueOf>
26 invokevirtual #12 <com/wenxueliu/leetcode/Test1.test3>
29 istore_3
// test4(1)
30 aload_0
31 iconst_1
32 invokevirtual #13 <com/wenxueliu/leetcode/Test1.test4>
35 istore 4
// Integer.intValue(test1(new Integer(1)))
37 aload_0
38 new #3 <java/lang/Integer>
41 dup
42 iconst_1
43 invokespecial #4 <java/lang/Integer.<init>>
46 invokevirtual #10 <com/wenxueliu/leetcode/Test1.test1>
49 invokevirtual #5 <java/lang/Integer.intValue>
52 istore 5
// Integer.intValue(test2((new Integer(1)).intValue()))
54 aload_0
55 new #3 <java/lang/Integer>
58 dup
59 iconst_1
60 invokespecial #4 <java/lang/Integer.<init>>
63 invokevirtual #5 <java/lang/Integer.intValue>
66 invokevirtual #11 <com/wenxueliu/leetcode/Test1.test2>
69 invokevirtual #5 <java/lang/Integer.intValue>
72 istore 6
// test3(new Integer(1))
74 aload_0
75 new #3 <java/lang/Integer>
78 dup
79 iconst_1
80 invokespecial #4 <java/lang/Integer.<init>>
83 invokevirtual #12 <com/wenxueliu/leetcode/Test1.test3>
86 istore 7
// test4((new Integer(1)).intValue()))
88 aload_0
89 new #3 <java/lang/Integer>
92 dup
93 iconst_1
94 invokespecial #4 <java/lang/Integer.<init>>
97 invokevirtual #5 <java/lang/Integer.intValue>
100 invokevirtual #13 <com/wenxueliu/leetcode/Test1.test4>
103 istore 8
// test1(Integer.valueOf(1))
105 aload_0
106 iconst_1
107 invokestatic #2 <java/lang/Integer.valueOf>
110 invokevirtual #10 <com/wenxueliu/leetcode/Test1.test1>
113 astore 9
// test2(1)
115 aload_0
116 iconst_1
117 invokevirtual #11 <com/wenxueliu/leetcode/Test1.test2>
120 astore 10
// Integer.valueOf(test3(Integer.valueOf(1)))
122 aload_0
123 iconst_1
124 invokestatic #2 <java/lang/Integer.valueOf>
127 invokevirtual #12 <com/wenxueliu/leetcode/Test1.test3>
130 invokestatic #2 <java/lang/Integer.valueOf>
133 astore 11
// Integer.valueOf(test4(1))
135 aload_0
136 iconst_1
137 invokevirtual #13 <com/wenxueliu/leetcode/Test1.test4>
140 invokestatic #2 <java/lang/Integer.valueOf>
143 astore 12
// test1(new Integer(1))
145 aload_0
146 new #3 <java/lang/Integer>
149 dup
150 iconst_1
151 invokespecial #4 <java/lang/Integer.<init>>
154 invokevirtual #10 <com/wenxueliu/leetcode/Test1.test1>
157 astore 13
// test2((new Integer(1)).intValue())
159 aload_0
160 new #3 <java/lang/Integer>
163 dup
164 iconst_1
165 invokespecial #4 <java/lang/Integer.<init>>
168 invokevirtual #5 <java/lang/Integer.intValue>
171 invokevirtual #11 <com/wenxueliu/leetcode/Test1.test2>
174 astore 14
// Integer.valueOf(test3(new Integer(1)))
176 aload_0
177 new #3 <java/lang/Integer>
180 dup
181 iconst_1
182 invokespecial #4 <java/lang/Integer.<init>>
185 invokevirtual #12 <com/wenxueliu/leetcode/Test1.test3>
188 invokestatic #2 <java/lang/Integer.valueOf>
191 astore 15
// Integer.valueOf(test3((new Integer(1)).intValue()))
193 aload_0
194 new #3 <java/lang/Integer>
197 dup
198 iconst_1
199 invokespecial #4 <java/lang/Integer.<init>>
202 invokevirtual #5 <java/lang/Integer.intValue>
205 invokevirtual #13 <com/wenxueliu/leetcode/Test1.test4>
208 invokestatic #2 <java/lang/Integer.valueOf>
211 astore 16
213 return
// 直接返回
0 aload_1
1 areturn
// Integer.valueOf(param)
0 iload_1
1 invokestatic #2 <java/lang/Integer.valueOf>
4 areturn
// param.intValue()
0 aload_1
1 invokevirtual #5 <java/lang/Integer.intValue>
4 ireturn
// 直接返回
0 iload_1
1 ireturn
總結
- 形參和實參類型不一致會進行裝箱或拆箱
- 接受類型和返回類型不一致會進行裝箱或拆箱
字符串
void testStr() {
// "123"
String s1 = "123";
// new String("123")
String s2 = new String("123");
// new String("123").intern();
String s3 = new String("123").intern();
// new StringBuilder(s1).append("345").toString()
String s4 = s1 + "345";
// new StringBuilder(s1).append(345).toString()
String s5 = s1 + 345;
// new StringBuilder(s1).append(s2).toString()
String s6 = s1 + s2;
// new StringBuilder(s1).append(s3).toString()
String s7 = s1 + s3;
// "123456"
String s8 = "123" + "456";
// new StringBuilder("123456").append(s1).toString()
String s9 = "123" + "456" + s1;
// new StringBuilder("123456").append(s2).toString()
String s10 = "123" + "456" + s2;
// new StringBuilder("123456").append(s3).toString()
String s11 = "123" + "456" + s3;
// 123789456
String s12 = "123" + 789 + "456";
// new StringBuilder("123").append(s1).append("456").toString()
String s13 = "123" + s1 + "456";
// new StringBuilder("123").append(s2).append("456").toString()
String s14 = "123" + s2 + "456";
// new StringBuilder("123").append(s3).append("456").toString()
String s15 = "123" + s3 + "456";
String s16 = "123" + 789 + "456";
}
生成的字節碼
// String s1 = "123"
0 ldc #14 <123>
2 astore_1
// String s2 = new String("123");
3 new #15 <java/lang/String>
6 dup
7 ldc #14 <123>
9 invokespecial #16 <java/lang/String.<init>>
12 astore_2
// String s3 = new String("123").intern();
13 new #15 <java/lang/String>
16 dup
17 ldc #14 <123>
19 invokespecial #16 <java/lang/String.<init>>
22 invokevirtual #17 <java/lang/String.intern>
25 astore_3
// String s4 = s1 + "345";
26 new #18 <java/lang/StringBuilder>
29 dup
30 invokespecial #19 <java/lang/StringBuilder.<init>>
33 aload_1
34 invokevirtual #20 <java/lang/StringBuilder.append>
37 ldc #21 <345>
39 invokevirtual #20 <java/lang/StringBuilder.append>
42 invokevirtual #22 <java/lang/StringBuilder.toString>
45 astore 4
// String s5 = s1 + 345;
47 new #18 <java/lang/StringBuilder>
50 dup
51 invokespecial #19 <java/lang/StringBuilder.<init>>
54 aload_1
55 invokevirtual #20 <java/lang/StringBuilder.append>
58 sipush 345
61 invokevirtual #23 <java/lang/StringBuilder.append>
64 invokevirtual #22 <java/lang/StringBuilder.toString>
67 astore 5
// String s6 = s1 + s2;
69 new #18 <java/lang/StringBuilder>
72 dup
73 invokespecial #19 <java/lang/StringBuilder.<init>>
76 aload_1
77 invokevirtual #20 <java/lang/StringBuilder.append>
80 aload_2
81 invokevirtual #20 <java/lang/StringBuilder.append>
84 invokevirtual #22 <java/lang/StringBuilder.toString>
87 astore 6
// String s7 = s1 + s3;
89 new #18 <java/lang/StringBuilder>
92 dup
93 invokespecial #19 <java/lang/StringBuilder.<init>>
96 aload_1
97 invokevirtual #20 <java/lang/StringBuilder.append>
100 aload_3
101 invokevirtual #20 <java/lang/StringBuilder.append>
104 invokevirtual #22 <java/lang/StringBuilder.toString>
107 astore 7
// String s8 = "123" + "456";
109 ldc #24 <123456>
111 astore 8
// String s9 = "123" + "456" + s1;
113 new #18 <java/lang/StringBuilder>
116 dup
117 invokespecial #19 <java/lang/StringBuilder.<init>>
120 ldc #24 <123456>
122 invokevirtual #20 <java/lang/StringBuilder.append>
125 aload_1
126 invokevirtual #20 <java/lang/StringBuilder.append>
129 invokevirtual #22 <java/lang/StringBuilder.toString>
132 astore 9
// String s9 = "123" + "456" + s2;
134 new #18 <java/lang/StringBuilder>
137 dup
138 invokespecial #19 <java/lang/StringBuilder.<init>>
141 ldc #24 <123456>
143 invokevirtual #20 <java/lang/StringBuilder.append>
146 aload_2
147 invokevirtual #20 <java/lang/StringBuilder.append>
150 invokevirtual #22 <java/lang/StringBuilder.toString>
153 astore 10
// String s11 = "123" + "456" + s3;
155 new #18 <java/lang/StringBuilder>
158 dup
159 invokespecial #19 <java/lang/StringBuilder.<init>>
162 ldc #24 <123456>
164 invokevirtual #20 <java/lang/StringBuilder.append>
167 aload_3
168 invokevirtual #20 <java/lang/StringBuilder.append>
171 invokevirtual #22 <java/lang/StringBuilder.toString>
174 astore 11
// String s12 = "123" + 789 + "456";
176 ldc #25 <123789456>
178 astore 12
// String s13 = "123" + s1 + "456";
180 new #18 <java/lang/StringBuilder>
183 dup
184 invokespecial #19 <java/lang/StringBuilder.<init>>
187 ldc #14 <123>
189 invokevirtual #20 <java/lang/StringBuilder.append>
192 aload_1
193 invokevirtual #20 <java/lang/StringBuilder.append>
196 ldc #26 <456>
198 invokevirtual #20 <java/lang/StringBuilder.append>
201 invokevirtual #22 <java/lang/StringBuilder.toString>
204 astore 13
// String s13 = "123" + s2 + "456";
206 new #18 <java/lang/StringBuilder>
209 dup
210 invokespecial #19 <java/lang/StringBuilder.<init>>
213 ldc #14 <123>
215 invokevirtual #20 <java/lang/StringBuilder.append>
218 aload_2
219 invokevirtual #20 <java/lang/StringBuilder.append>
222 ldc #26 <456>
224 invokevirtual #20 <java/lang/StringBuilder.append>
227 invokevirtual #22 <java/lang/StringBuilder.toString>
230 astore 14
//String s13 = "123" + s3 + "456";
232 new #18 <java/lang/StringBuilder>
235 dup
236 invokespecial #19 <java/lang/StringBuilder.<init>>
239 ldc #14 <123>
241 invokevirtual #20 <java/lang/StringBuilder.append>
244 aload_3
245 invokevirtual #20 <java/lang/StringBuilder.append>
248 ldc #26 <456>
250 invokevirtual #20 <java/lang/StringBuilder.append>
253 invokevirtual #22 <java/lang/StringBuilder.toString>
256 astore 15
// 123789456
258 ldc #25 <123789456>
260 astore 16
262 return
結論
- String 已經非常智能,絕大多數不需要 StringBuilder 了
對運算的影響
public static void testAdd() {
int i0 = 40;
int i1 = 20;
int i2 = i0 + i1;
Integer i3 = i0 + i1;
Integer i4 = new Integer(40);
Integer i5 = new Integer(50);
// Integer.valueOf(Integer.intValue(i4) + Integer.intValue(i5))
Integer i6 = i4 + i5;
// Integer.intValue(i4) + Integer.intValue(i5)
int i7 = i4 + i5;
Double d1 = 1.0;
Double d2 = 2.0;
// Double.valueOf(Double.doubleValue(d1) + Double.doubleValue(d2))
Double d3 = d1 + d2;
Double d4 = new Double("2.0");
Double d5 = new Double("2.0");
// Double.valueOf(Double.doubleValue(d4) + Double.doubleValue(d5))
Double d6 = d4 + d5;
}
輸出字節碼
0 bipush 40
2 istore_0
3 bipush 20
5 istore_1
// int i2 = i0 + i1;
6 iload_0
7 iload_1
8 iadd
9 istore_2
// Integer.valueOf(i0 + i1)
10 iload_0
11 iload_1
12 iadd
13 invokestatic #2 <java/lang/Integer.valueOf>
16 astore_3
// Integer i4 = new Integer(40);
17 new #3 <java/lang/Integer>
20 dup
21 bipush 40
23 invokespecial #4 <java/lang/Integer.<init>>
26 astore 4
// Integer i5 = new Integer(50);
28 new #3 <java/lang/Integer>
31 dup
32 bipush 50
34 invokespecial #4 <java/lang/Integer.<init>>
37 astore 5
// Integer i6 = i4 + i5;
39 aload 4
41 invokevirtual #5 <java/lang/Integer.intValue>
44 aload 5
46 invokevirtual #5 <java/lang/Integer.intValue>
49 iadd
50 invokestatic #2 <java/lang/Integer.valueOf>
53 astore 6
// int i7 = i4 + i5;
55 aload 4
57 invokevirtual #5 <java/lang/Integer.intValue>
60 aload 5
62 invokevirtual #5 <java/lang/Integer.intValue>
65 iadd
66 istore 7
// Double d1 = 1.0; 1.0 當做 1 處理
68 dconst_1
69 invokestatic #27 <java/lang/Double.valueOf>
72 astore 8
// Double d1 = 2.0;
74 ldc2_w #28 <2.0>
77 invokestatic #27 <java/lang/Double.valueOf>
80 astore 9
// Double d3 = d1 + d2;
82 aload 8
84 invokevirtual #30 <java/lang/Double.doubleValue>
87 aload 9
89 invokevirtual #30 <java/lang/Double.doubleValue>
92 dadd
93 invokestatic #27 <java/lang/Double.valueOf>
96 astore 10
98 new #31 <java/lang/Double>
101 dup
102 ldc #32 <2.0>
104 invokespecial #33 <java/lang/Double.<init>>
107 astore 11
109 new #31 <java/lang/Double>
112 dup
113 ldc #32 <2.0>
115 invokespecial #33 <java/lang/Double.<init>>
118 astore 12
// Double d6 = d4 + d5;
120 aload 11
122 invokevirtual #30 <java/lang/Double.doubleValue>
125 aload 12
127 invokevirtual #30 <java/lang/Double.doubleValue>
130 dadd
131 invokestatic #27 <java/lang/Double.valueOf>
134 astore 13
149 return
結論
-
做運算的時候,進行拆箱操作
-
結果根據變量類型進行裝箱或拆箱
總結
至此,對自動裝箱和拆箱的理解應該很到位了。
- 賦值會將等號右邊的類型轉換爲等號左邊的類型,裝箱還是拆箱,以等號左邊類型爲準,左邊爲基本類型,右邊爲封裝類型,進行拆箱操作。左邊是封裝類型,右邊爲基本類型,進行裝箱操作。
- String 已經足夠智能
- 進行運算的時候,會進行拆箱操作