Java的自動拆/裝箱


作者:Alvin

關鍵字:語法糖  類  對象

參考

Java 中的語法糖

語法糖--這一篇全了解

淺談 Integer 類

什么是Java中的自動拆裝箱

深入剖析Java中的裝箱和拆箱

前言

我們知道,Java有8中基本數據類型,分別是byte,short,int,long,char,float,double,boolean,但是定義的這些基本數據類型的值只是一個字面量,而字面量的補碼是的的確確存儲在內存上的一個量,這個量不具有其他的方法屬性。但是我們在編程開發中有把一個整型int轉換成字符串等的需求,再如當我們需要把數據放到集合中時,我們的基本數據類型是不允許被放入的,而Java中的中心思想就是對象,所以Java將它們封裝成對象Byte、Short、Integer、Long、 Character、Float、Double、Boolean,並給出相應的方法。這樣當我們有需求的時候我們就可以通過相對應的對象進行調用方法來解決。

一、案例引入

public static void main(String[] args) {
    Integer i = 10;
    Integer j = 10;
    System.out.println(i == j);
      
    Integer a = 128;
    Integer b = 128;
    System.out.println(a == b);
     
    int k = 10;
    System.out.println(k == i);
    int kk = 128;
    System.out.println(kk == a);
      
    Integer m = new Integer(10);
    Integer n = new Integer(10);
    System.out.println(m == n);
}

執行結果

true
false
true
true
false

二、寫法分析

在進行分析之前我們先來了解一個概念:語法糖

語法糖(Syntactic sugar),也譯為糖衣語法,是由英國計算機科學家彼得·約翰·蘭達(Peter J. Landin)發明的一個術語,指計算機語言中添加的某種語法,這種語法對語言的功能並沒有影響,但是更方便程序員使用。通常來說使用語法糖能夠增加程序的可讀性,從而減少程序代碼出錯的機會。

  Java中的語法糖也是新增了一些語法,使得程序員使用更加方便。但是計算機底層還是使用基本語法來實現的。下面我們討論幾個經常使用到的語法糖,可變參數自動裝箱/拆箱增強for循環

我們以Integer類為例,在我們用如下代碼編寫程序后,當使用Javac.exe工具操作之后,編譯器會把我們的這個程序重寫按照DRY(Don't repeat yourself)原則進行編譯

public class Test {

    public static void main(String[] args) {
        Integer num1 = 123;//將一個基本數據類型賦給Integer對象
        int num2 = num1;//將一個Integer對象賦給整形變量
    }
}

對以上代碼編譯后的Test.class反編譯結果如下

public class Test{
  public static void main(String[] args) {
    Integer num1 = Integer.valueOf(123);//裝箱操作
    int num2 = num1.intValue();//拆箱操作
  }
}

Java語言中,javac命令可以將后綴名為.java的源文件編譯為后綴名為.class的可以運行於Java虛擬機的字節碼。如果你去看com.sun.tools.javac.main.JavaCompiler的源碼,你會發現在compile()中有一個步驟就是調用desugar(),這個方法就是負責解語法糖的實現的。

可以看到,在Java編譯器進行編譯的時候會自動把我們的代碼重整成符合DRY原則代碼並編譯成.class文件。在我們定義一個Integer類型的變量后,當我們把整型的值賦值給該Integer類型,實際上在編譯后的操作時執行了Integer.valueOf(123);操作,只是我們采用了語法糖寫法而簡寫了方法調用的過程,同理,當把一個Integer的對象用語法糖寫法編寫的時候,該對象也調用了它的intValue方法進行轉換類型。所以我們語法糖寫法只是在編寫的時候簡化了代碼,而類型的轉換操作在編譯器運行的時候會把調用的方法重新補充上。其他的八個基本數據類型各自對應的情況和Integer相同,不再贅述。

其他類似的語法糖寫法還有增強for循環(底層還是循環),可變參數,switch對String和枚舉的支持等

三、結果分析

了解了上面代碼的轉換過程,我們從反編譯的結果得知:再Java語言中永遠遵守不同類型之間不可以進行賦值的規則。置於我們在代碼中的編寫形式有時只是進行了簡寫,而最后的執行還要依賴於編譯器的解析結果。

四、實現過程

上方就是基本數據類型的自動拆/裝箱,他們的裝箱遵循以下規則

自動裝箱規范要求 byte<= 127、char<=127、-128<=short <=127、-128<=int <=127都被包裝到固定的對象中(緩存)。

也就是說在裝箱過程中執行valueOf(參數)方法后,如果滿足以上條件就會被封裝成Integer對象中。valueOf函數如下

從函數可以看出,當在-128到127范圍內,會生成同一個對象,在范圍之外,會執行new Integer();我們都知道在Java語言中,new一個對象是存儲在堆里的,我們通過棧中的引用來使用這些對象;所以,對象本身來說是比較消耗資源的。

五、總結

 包裝類的應用

1、Java中的集合類只能接收對象類型,而Java通過包裝類實現了把基本數據類型放入集合操作的目的。並且在放入集合的時候這種封裝時自動完成的。

2、包裝類與基本數據類型進行比較運算,是先將包裝類進行拆箱成基本數據類型,然后進行比較的。

3、兩個包裝類型之間的運算,會被自動拆箱成基本類型進行。

4、三目運算符flag ? i : j;片段中,三目運算符的語法規范:當第二,第三位操作數分別為基本類型和對象時,其中的對象就會拆箱為基本類型進行操作。如果這個時候i的值為null,那么久會發生NPE。(自動拆箱導致空指針異常

5、函數參數與返回值

6、Integer中的緩存機制有關。在Java 5中,在Integer的操作上引入了一個新功能來節省內存和提高性能。整型對象通過使用相同的對象引用實現了緩存和重用。適用於整數值區間-128 至 +127。只適用於自動裝箱。使用構造函數創建對象不適用。當需要進行自動裝箱時,如果數字在-128至127之間時,會直接使用緩存中的對象,而不是重新創建一個對象。

另外javadoc詳細的說明了緩存支持-128到127之間的自動裝箱過程。最大值127可以通過-XX:AutoBoxCacheMax=size修改。實際上這個功能在Java 5中引入的時候,范圍是固定的-128 至 +127。后來在Java 6中,可以通過java.lang.Integer.IntegerCache.high設置最大值。

六、注意事項

包裝類的使用簡化了代碼,方便了編程者對數據的操作,實現了面向對象思想,但是也引入了一些麻煩,我們要盡量避免,以防對以后的編程中出現負擔。

1、包裝對象的數值比較,不能簡單的使用==,雖然-128到127之間的數字可以,但是這個范圍之外還是需要使用equals比較。

2、如果一個for循環中有大量拆裝箱操作,會浪費很多資源。

3、有些場景會進行自動拆裝箱,同時也說過,由於自動拆箱,如果包裝類對象為null,那么自動拆箱時就有可能拋出NPE。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM