5 分鐘掌握 JDK 之自動裝箱和拆箱

C 語言的高手都有一個能力,就是看到 C 代碼,能腦補彙編代碼。看到一段代碼會裏面知道這段代碼生成的代碼好不好。那麼,作爲 Java 高手,腦補字節碼也是必備能力之一,而通過本文,你離這一目標更近了一步。

最自動裝箱和拆箱的瞭解,絕大多數程序員都知道會進行自動的裝箱和拆箱,裝箱和拆箱會影響性能。但是具體什麼場景下會裝箱,什麼場景下會拆箱?裝箱和拆箱的底層原理是啥?裝箱和拆箱在什麼哪個階段進行?

問題

  1. 裝箱和拆箱的原理是啥?
  2. 自動裝箱和拆箱在什麼哪個階段進行?編譯時還是運行時?

自動裝箱拆箱

原始類型: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出來。

結論
  1. 自動裝箱:valueOf
  2. 自動拆箱: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
結論
  1. 自動裝箱:valueOf
  2. 自動拆箱: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
總結
  1. 形參和實參類型不一致會進行裝箱或拆箱
  2. 接受類型和返回類型不一致會進行裝箱或拆箱

字符串

    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
結論
  1. 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
結論
  1. 做運算的時候,進行拆箱操作

  2. 結果根據變量類型進行裝箱或拆箱

總結

至此,對自動裝箱和拆箱的理解應該很到位了。

  1. 賦值會將等號右邊的類型轉換爲等號左邊的類型,裝箱還是拆箱,以等號左邊類型爲準,左邊爲基本類型,右邊爲封裝類型,進行拆箱操作。左邊是封裝類型,右邊爲基本類型,進行裝箱操作。
  2. String 已經足夠智能
  3. 進行運算的時候,會進行拆箱操作
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章