JavaRush /Java Blog /Random-TW /Java 中的自動裝箱與拆箱
Viacheslav
等級 3

Java 中的自動裝箱與拆箱

在 Random-TW 群組發布
<h2>簡介</h2>一門程式語言,就像人們所說的語言一樣,生活和變化,新的現像出現在其中,使語言更方便使用。眾所周知,語言應該方便地表達我們的想法。
Java 中的自動裝箱和拆箱 - 1
所以,在Java SE 5中,引入了裝箱/拆箱機制。Oracle 的一個單獨的教學專門介紹了這種表達思想的方式的特性:自動裝箱和拆箱。<h2>自動裝箱 Boxing</h2>讓我們來看一個自動裝箱 Boxing 的範例。首先,讓我們看看它是如何運作的。讓我們使用網站compilejava.net並建立一個類別:
public class App {
    public static void main(String[] args) {
        Integer portNumber = 8080;
        if (args.length != 0) {
            portNumber = Integer.valueOf(args[0]);
        }
        System.out.println("Port number is: " + portNumber);
    }
}
簡單的程式碼。我們可以指定輸入參數並更改連接埠值。正如我們所見,因為 String我們從參數中讀取連接埠值,我們Integer透過獲取它來獲取它Integer.valueOf。因此,我們被迫不將其指定為原始類型,而是指定為物件類型Integer。一方面,我們有一個物件變量,預設值是一個基元。它有效。但我們不相信魔法,不是嗎?正如他們所說,讓我們看看“幕後”。點擊“Download ZIP”從compilejava.net 下載原始程式碼。之後,將下載的存檔解壓縮到一個目錄中並轉到該目錄。現在讓我們這樣做:javap -c -p App.class其中 App.class 是您的類別的已編譯類別檔案。我們會看到這樣的內容:
Java 中的自動裝箱與拆箱 - 2
這也是臭名昭著的「字節碼」。但現在對我們來說重要的是我們所看到的。首先,8080原語被放置在方法執行堆疊上,然後執行Integer.valueOf。這就是拳擊的「魔力」。裡面的魔法看起來像這樣:
Java 中的自動裝箱和拆箱 - 3
也就是說,本質上,將根據數字的值從緩存中取出Integer或獲取一個新的Integer(緩存只不過是一個整數數組)。如此幸運的人自然Integer不只一人。有相關原始類型及其包裝器(代表 OOP 世界中的原始類型的類別)的完整清單。此清單在 Oracle 教學的最底部給出:「自動裝箱和拆箱」。值得注意的是,在不連接任何第三方函式庫的情況下,由原語組成的陣列沒有「包裝器」。那些。Arrays.asList不會讓int[]我們ListInteger的。<h2>拆箱</h2>裝箱的逆過程稱為拆箱拆箱。我們來看一個拆包的例子:
public class App {

    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("Please, enter params");
            return;
        }
      	int value = Math.abs(Integer.valueOf(args[0]));
        System.out.println("Absolute value is: " + value);
    }

}
Math.abs僅接受原語。怎麼辦?對於這種情況,包裝類別有一個特殊的方法,它會傳回一個原語。例如,這是intValueInteger方法。如果我們看一下字節碼,它是這樣的:
Java 中的自動裝箱與拆箱 - 4
顯然,沒有魔法。一切都在 Java 中。它只是“自行”工作。為了我們的方便。<h2>耙子</h2>
Java 中的自動裝箱與拆箱 - 5
任何工具,如果使用不當,都會成為對抗自身的強大武器。Java中的自動裝箱/拆箱機制也不例外。第一個明顯的比較是透過==. 我認為這已經很清楚了,但我們再看一下:
public static void main(String[] args) {
    Integer inCacheValue = 127;
    Integer inCacheValue2 = 127;
    Integer notInCache = 128; // new Integer(129)
    Integer notInCache2 = 128; // new Integer(129)
    System.out.println(inCacheValue == inCacheValue2); //true
    System.out.println(notInCache == notInCache2); //false
}
在第一種情況下,值是從Integer值快取中取得的(請參閱上面裝箱的解釋),在第二種情況下,每次都會建立一個新物件。但這裡值得預訂。此行為取決於快取上限 ( java.lang.Integer.IntegerCache.high )。此外,此限制可能會因其他設定而改變。您可以在 stackoverflow 上閱讀有關此主題的討論:Integer 快取有多大? 當然,物件需要使用 equals 進行比較: System.out.println(notInCache.equals(notInCache2)); 與相同機制相關的第二個問題是效能。Java中的任何裝箱都相當於建立一個新物件。如果該數字不包含在快取值中(即-128到127),那麼每次都會建立一個新物件。如果突然在循環中進行打包(即裝箱),這會導致不必要的物件大量增加,並消耗垃圾收集器工作的資源。因此,不要太草率了。第三種同樣痛苦的耙子也源自於相同的機制:
public static void check(Integer value) {
    if (value <= 0) {
        throw new IllegalStateException("Value is too small");
    }
}
在這段程式碼中,這個人顯然試圖不去克服這個錯誤。但沒有檢查null. 如果涉及到輸入null,那麼我們將得到一個不可理解的錯誤,而不是一個可以理解的錯誤NullPointerException。因為為了比較,Java會嘗試執行value.intValue並崩潰,因為... value將要null。<h2>結論</h2>裝箱/拆箱機制允許程式設計師編寫更少的程式碼,有時甚至不需要考慮從基元到物件的相互轉換。但這並不意味著您應該忘記它是如何運作的。否則,您可能會犯一個可能不會立即出現的錯誤。我們不應該依賴系統中不完全受我們控制的部分(例如整數邊界)。但不要忘記包裝類的所有優點(例如Integer)。通常,這些包裝類別有一組附加的靜態方法,可以讓您的生活更美好,讓您的程式碼更具表現力。這是一個追趕範例:
public static void main(String[] args) {
    int first = 1;
    int second = 5;
    System.out.println(Integer.max(first, second));
    System.out.println(Character.toLowerCase('S'));
}
一切的正確結論是,沒有魔法,有某種實現。並不是一切都會如我們所期望的。例如,沒有打包: System.out.println("The number is " + 8); 上面的例子會被編譯器最佳化成一行。也就是說,就好像你寫了「數字是 8」。在下面的範例中,也不會出現任何包裝:
public static void main(String[] args) {
    System.out.println("The number is " + Math.abs(-2));
}
println當我們將一個物件作為輸入並需要以某種方式連接線路 時,會發生什麼情況?線條……是的,這就是為什麼沒有這樣的包裝。有Integer靜態方法,但其中一些是package. 也就是說,我們不能使用它們,但在 Java 本身中它們可以被主動使用。這裡的情況正是如此。將呼叫 getChars 方法,該方法根據數字建立字元數組。再說一遍,沒有魔法,只有 Java)。因此,在任何不清楚的情況下,你只需要看看實施情況,至少有些東西會就位。#維亞切斯拉夫
留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION