上善若水
          In general the OO style is to use a lot of little objects with a lot of little methods that give us a lot of plug points for overriding and variation. To do is to be -Nietzsche, To bei is to do -Kant, Do be do be do -Sinatra
          posts - 146,comments - 147,trackbacks - 0

          J2SE5.0后推出了自動(dòng)裝箱和拆箱的功能,以提高我們的開發(fā)效率,然而自動(dòng)裝箱和拆箱實(shí)際上是通過編譯器來支持的(并非語言本身,或者說虛擬機(jī)),因而這種支持也隱藏了部分內(nèi)部實(shí)質(zhì),再加上某些類的優(yōu)化(比如Integer里面的緩存等,參看關(guān)于緩存節(jié)),就更加容易在特定的環(huán)境下產(chǎn)生問題,并且如果不知道原來還無法調(diào)試。以下先是簡(jiǎn)單的介紹了編譯器對(duì)裝箱和拆箱的實(shí)現(xiàn),并根據(jù)實(shí)現(xiàn)簡(jiǎn)單介紹一下可能會(huì)遇到的幾個(gè)問題。

          裝箱和拆箱實(shí)現(xiàn)

          以下裝箱和拆箱代碼:

                 Object value = 10;
                 
          int intValue = (Integer)value;
                 Integer newIntValue 
          = new Integer(10);

           

          編譯成字節(jié)碼如下:

               0 bipush 10

               2 invokestatic java.lang.Integer.valueOf(int) : java.lang.Integer [20]

               5 astore_1 [value]

               6 aload_1 [value]

               7 checkcast java.lang.Integer [21]

              10 invokevirtual java.lang.Integer.intValue() : int [26]

              13 istore_2 [intValue]

              14 new java.lang.Integer [21]

              17 dup

              18 bipush 10

              20 invokespecial java.lang.Integer(int) [30]

              23 astore_3 [newIntValue]

          從以上字節(jié)碼可以看到10首先調(diào)用valueOf方法轉(zhuǎn)換為Integer實(shí)例,再賦值該value,而value強(qiáng)制轉(zhuǎn)換成Integer類后,會(huì)調(diào)用intValue方法,后賦值給intValue。這就是用編譯器來實(shí)現(xiàn)裝箱和拆箱。

           

          奇怪的NullPointerException

          查看以下代碼:

                 Integer value = null;
                 
          int intValue = value;

           

          可以編譯通過,但是運(yùn)行的時(shí)候卻會(huì)發(fā)生NullPointerException。這是由什么引起的呢?依然看一下字節(jié)碼就可以了:

               0 aconst_null

               1 astore_1 [value]

               2 aload_1 [value]

              3 invokevirtual java.lang.Integer.intValue() : int [20]

               6 istore_2 [intValue]

          從字節(jié)碼中可以看到,從value賦值該intValue事實(shí)上是直接在value實(shí)例上調(diào)用intValue函數(shù)。

          對(duì)當(dāng)前代碼,我們可以一眼就看出當(dāng)前valuenull的問題,但是如果這個(gè)null是在很遠(yuǎn)以外的地方賦值的呢?或者是間接賦值呢?這個(gè)時(shí)候遇到這種問題就會(huì)比較詭異了。

           

          相等與不相等問題

          查看一下代碼:

                 Integer value1 = 100;
                 Integer value2 
          = 100;
                 System.out.println(
          "value1 == value2 is " + (value1 == value2));
                
                 Integer value3 
          = 200;
                 Integer value4 
          = 200;
                 System.out.println(
          "value3 == value4 is " + (value3 == value4));

           

          這段代碼會(huì)是什么結(jié)果?

          value1 == value2 is true

          value3 == value4 is false

           

          兩段代碼就是值不一樣,其他的都一樣,竟然會(huì)有區(qū)別?這個(gè)奧妙就因?yàn)檠b箱過程中調(diào)用的是valueOf方法,而valueOf方法對(duì)值在-128127之間的數(shù)值緩存了(參見關(guān)于緩存一節(jié)),因而value1value2的引用是相同的,而value3value4的引用是不一樣的,而==比較的是引用,因而才會(huì)出現(xiàn)以上的結(jié)果。

          這確的做法應(yīng)該是:

                 Integer value1 = 100;
                 Integer value2 
          = 100;
                 System.out.println(
          "value1 == value2 is " + (value1.equals(value2)));
                
                 Integer value3 
          = 200;
                 Integer value4 
          = 200;

                 System.out.println("value3 == value4 is " + (value3.equals(value4))); 

          這樣的結(jié)果就是預(yù)料的結(jié)果了:

          value1 == value2 is true

          value3 == value4 is true

           

          所以我們要慎用“==”操作符。

           

          String中的相等與不等

          String中也有類似的情況,查看一下代碼:

                 String str1 = "abc";
                 String str2 
          = "abc";
                 System.out.println(
          "str1 == str2 is " + (str1 == str2));
                
                 String str3 
          = new String("abc");
                 String str4 
          = new String("abc");
                 System.out.println(
          "str3 == str4 is " + (str3 == str4));

           

          執(zhí)行結(jié)果:

          str1 == str2 is true

          str3 == str4 is false

           

          這是因?yàn)?/span>str1str2使用的是同一個(gè)字符串,即在字符常量中的字符串,而str3str4在使用字符常量中的字符為參數(shù)又創(chuàng)建出了兩個(gè)新的字符串對(duì)象,因而在引用比較情況下是不等的。我們可以從字節(jié)碼中得到這些信息(刪除打印的代碼):

               0 ldc <String "abc"> [20]

               2 astore_1 [str1]

               3 ldc <String "abc"> [20]

               5 astore_2 [str2]

               6 new java.lang.String [22]

               9 dup

              10 ldc <String "abc"> [20]

              12 invokespecial java.lang.String(java.lang.String) [24]

              15 astore_3 [str3]

              16 new java.lang.String [22]

              19 dup

              20 ldc <String "abc"> [20]

              22 invokespecial java.lang.String(java.lang.String) [24]

              25 astore 4 [str4]

          正確的做法還是調(diào)用equals方法,而不是使用“==”操作符。

           

          關(guān)于緩存

          據(jù)目前信息,有緩存的類有:ByteShortIntegerLong以及Boolean類。而這種緩存也只是在調(diào)用valueOf(靜態(tài))方法的時(shí)候才會(huì)存在(裝箱正是調(diào)用了valueOf方法)。對(duì)整型,緩存的值都是-128127(包括-128127)之間,其他值都不緩存,而對(duì)Boolean類型只有truefalse值。代碼如下:

          public final class Integer extends Number {
              
          public static Integer valueOf(int i) {
                  
          if(i >= -128 && i <= IntegerCache.high)
                      
          return IntegerCache.cache[i + 128];
                  
          else
                  
          return new Integer(i);
          }
          public final class Boolean {
              
          public static Boolean valueOf(boolean b) {
                  
          return (b ? TRUE : FALSE);
              }


          2011-01-05

           

           

          posted on 2011-07-20 23:09 DLevin 閱讀(5785) 評(píng)論(2)  編輯  收藏 所屬分類: Core Java

          FeedBack:
          # re: Java中的裝箱與拆箱
          2011-07-25 22:29 | AlleNny
          so many years ~  回復(fù)  更多評(píng)論
            
          # re: Java中的裝箱與拆箱
          2011-07-26 01:12 | DLevin
          It really is, but it is not so old for a new guy like me, especially for the cache strategy applied by the compiler. :)@AlleNny
            回復(fù)  更多評(píng)論
            
          主站蜘蛛池模板: 宜丰县| 河北省| 弥渡县| 陆川县| 沐川县| 阳曲县| 张家界市| 安丘市| 东乌| 胶南市| 古丈县| 睢宁县| 什邡市| 濮阳县| 苍山县| 汕尾市| 沐川县| 湘乡市| 河池市| 阜宁县| 长春市| 拜城县| 邯郸市| 仲巴县| 凤冈县| 阜宁县| 得荣县| 林甸县| 航空| 阳谷县| 崇信县| 安阳市| 库尔勒市| 曲靖市| 平山县| 依兰县| 昌乐县| 义马市| 华阴市| 新河县| 井陉县|