128陷阱
- 128陷阱的概念
- 包裝器類
- 自動裝箱
- 自動拆箱
- 128陷阱
- Intager源碼
- equals
128陷阱的概念
首先想要清楚什么是128陷阱,需要了解一些概念
包裝器類
包裝器類(Wrapper classes)是Java中的一組類,它們允許將基本數據類型(如int、char、boolean等)封裝為對象。這些包裝器類提供了許多有用的方法,用于在基本數據類型和對象之間進行轉換和操作。這里給出兩個例子
基本類型 | 包裝器類 |
---|---|
int | Integer |
double | Double |
自動裝箱
自動裝箱(Autoboxing)是Java中的一種特性,它允許在基本數據類型和對應的包裝器類之間進行自動的轉換。當你使用基本數據類型的值賦給一個對應的包裝器類對象時,編譯器會自動將其轉換為包裝器類對象。
當把一個int類型的值賦給一個Integer對象時,自動裝箱就會發生:
int number = 42; // 基本數據類型
Integer wrappedNumber = number; // 自動裝箱
System.out.println("Wrapped Number: " + wrappedNumber);
上述代碼將int類型的值42賦給一個Integer對象wrappedNumber,編譯器會自動將其轉換為Integer對象。這樣,我們就可以像操作對象一樣操作基本數據類型
自動拆箱
自動拆箱(Unboxing)是Java中的另一個特性,它與自動裝箱相反。自動拆箱允許將包裝器類對象自動轉換為對應的基本數據類型。
當你將一個包裝器類對象賦給一個基本數據類型變量時,編譯器會自動進行拆箱操作。
Integer wrappedNumber = Integer.valueOf(42); // 創建一個Integer對象
int unwrappedNumber = wrappedNumber; // 自動拆箱
System.out.println("Unwrapped Number: " + unwrappedNumber);
128陷阱
在Java中,對于范圍在-128到127之間的整數值,自動裝箱后的包裝器類對象會被緩存起來以提高性能。這意味著對于這個范圍內的整數值,每次裝箱得到的包裝器類對象都是同一個對象。
然而,當我們超過這個范圍時,就會遇到所謂的"128陷阱"。這是因為超過范圍的整數值會導致自動裝箱時創建新的包裝器類對象,而不是使用緩存中的對象。
Integer num1 = 100;
Integer num2 = 100;
System.out.println(num1 == num2); // 輸出: trueInteger num3 = 200;
Integer num4 = 200;
System.out.println(num3 == num4); // 輸出: false
Intager源碼
我們從源碼的角度來分析一下為什么會產生128陷阱
private static class IntegerCache {static final int low = -128;static final int high;static final Integer cache[];static {// high value may be configured by propertyint h = 127;String integerCacheHighPropValue =sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");if (integerCacheHighPropValue != null) {try {int i = parseInt(integerCacheHighPropValue);i = Math.max(i, 127);// Maximum array size is Integer.MAX_VALUEh = Math.min(i, Integer.MAX_VALUE - (-low) -1);} catch( NumberFormatException nfe) {// If the property cannot be parsed into an int, ignore it.}}high = h;cache = new Integer[(high - low) + 1];int j = low;for(int k = 0; k < cache.length; k++)cache[k] = new Integer(j++);// range [-128, 127] must be interned (JLS7 5.1.7)assert IntegerCache.high >= 127;}private IntegerCache() {}}
首先,這個類定義了兩個靜態常量low和high,low的值為-128,high的值在靜態初始化塊中被設置為127。這兩個常量定義了緩存的整數值的范圍,即[-128, 127]。
靜態初始化塊中的代碼首先嘗試通過讀取系統 java.lang.Integer.IntegerCache.high來獲取用戶配置的緩存上限值。如果該屬性存在且能夠被解析為一個整數值,將該值與127進行比較,并確保不超過整數數組的最大容量。接下來,代碼創建了一個大小為(high - low) + 1的整數數組cache,用于存儲緩存的整數對象。使用low的初始值,從low遞增到high的范圍,通過循環創建了緩存數組中的每個整數對象。最后,通過斷言(assert)語句,確保high的值至少為127,以滿足Java語言規范(JLS 7 5.1.7)對范圍[-128, 127]的整數值必須被緩存的要求。
當使用一個超過范圍的整數值進行自動裝箱時,循環創建緩存數組cache的過程會創建一個新的整數對象,而不是使用緩存中的對象。這就是為什么超過范圍的整數值會導致自動裝箱后產生新的對象的原因。
equals
當我們調用equals()方法時,它會在包裝器類中執行特定的邏輯來比較兩個對象的值是否相等。為了實現這個邏輯,包裝器類會重寫equals()方法。
在Java中,equals()方法的默認實現是比較對象的引用,即使用==運算符。但是,包裝器類(如Integer、Double等)對equals()方法進行了重寫,以便比較它們所包裝的值是否相等。
當我們調用equals()方法時,它會首先檢查傳入的對象是否為同一類型的包裝器類對象。如果不是,它會立即返回false,因為不同類型的對象不可能具有相等的值。
如果傳入的對象是同一類型的包裝器類對象,equals()方法會進一步比較兩個對象所包裝的值。這種比較是按照數值的邏輯進行的,而不是比較引用。
例如,當我們調用num3.equals(num4)時,equals()方法會比較num3和num4所包裝的整數值。如果這兩個值相等,equals()方法將返回true;否則,它將返回false。
這樣,通過重寫equals()方法,包裝器類可以實現對值的比較,從而正確判斷兩個對象是否相等。