本文目錄一覽:
java什麼時候觸發裝箱和拆箱
最近面試也遇到了這個問題,思考總結了一下。
裝箱:在基本類型的值賦值給包裝類型時觸發。例如:Integer a = 1;
這時二進位文件中實際上是Integer a = Integer.valueOf(1);
拆箱:
1. 在包裝類型賦值給基本類型時觸發。
例如:Integer a = 1; // a 是Integer類型
int b = a; // 將Integer類型賦值給int類型,觸發拆箱
2.在做運算符運算時觸發。
例如:
Integer a = 1;
Integer b = 2;
System.out.print(a * b); //這時a*b在二進位文件中被編譯成a.intValue() * b.intValue();
注意點:
==運算時,如果 a 和 b 都是Integer類型,則不會拆箱,因為==也可以直接比較對象,表示a和b是否指向同一對象地址。因此這裡並不是比較值是否相等了。而如果a 和 b 中有一個是int類型,另一個是Integer 類型,則會觸發拆箱,然後對兩個int值進行比較。
Integer a = 128;
Integer b = 128;
Integer c = 127;
Integer d = 127;
System.out.print(a == b); // 返回false
System.out.print(c == d); // 返回true
原因是Integer 的裝箱(Integer.valueOf())方法會自動緩存-128~127之間的值,多次裝箱同一個值實際上用的是同一個對象,因此這裡 a == b 是false,因為不是同一個對象,而 c==d 是true,因為使用緩存中的同一個對象,而不是因為值相等。
Integer a = 127;
Integer e = new Integer(127);
Integer f = Integer.intValue(127);
System.out.println(a == e); // 返回false,不是同一個對象
System.out.println(a == f); // 返回true,是緩存中同一個對象
歡迎修改補充…
java包裝類 問題
1.java中的原始類型數據,都是拷貝,不是引用。所以Integer a=10; Integer b=a; a=20; System.out.println(b); 最後b是10.
2.java中分為堆和棧,也就是常量池和對象池
String a=”aaa”; 和String a=new String(“aaa”); 前者放到常量池中,後者放到對象池中。
3.jdk1.5開始有自動裝箱的機制。 Double a=10.1; 和 Double a=new Double(10.1);其實沒什麼區別
java 什麼是拆箱和裝箱,拆箱和裝箱 嘛用啊???
詳解Java的自動裝箱與拆箱(Autoboxing and unboxing)
一、什麼是自動裝箱拆箱
很簡單,下面兩句代碼就可以看到裝箱和拆箱過程
//自動裝箱
Integer total = 99;
//自定拆箱
int totalprim = total;
簡單一點說,裝箱就是自動將基本數據類型轉換為包裝器類型;拆箱就是自動將包裝器類型轉換為基本數據類型。
下面我們來看看需要裝箱拆箱的類型有哪些:
這個過程是自動執行的,那麼我們需要看看它的執行過程:
public class Main {
public static void main(String[] args) {
//自動裝箱
Integer total = 99;
//自定拆箱
int totalprim = total;
}
}
反編譯class文件之後得到如下內容:
javap -c StringTest
Integer total = 99;
執行上面那句代碼的時候,系統為我們執行了:
Integer total = Integer.valueOf(99);
int totalprim = total;
執行上面那句代碼的時候,系統為我們執行了:
int totalprim = total.intValue();
我們現在就以Integer為例,來分析一下它的源碼:
1、首先來看看Integer.valueOf函數
public static Integer valueOf(int i) {
return i = 128 || i -128 ? new Integer(i) : SMALL_VALUES[i + 128];
}
它會首先判斷i的大小:如果i小於-128或者大於等於128,就創建一個Integer對象,否則執行SMALL_VALUES[i + 128]。
首先我們來看看Integer的構造函數:
private final int value;
public Integer(int value) {
this.value = value;
}
public Integer(String string) throws NumberFormatException {
this(parseInt(string));
}
它裡面定義了一個value變數,創建一個Integer對象,就會給這個變數初始化。第二個傳入的是一個String變數,它會先把它轉換成一個int值,然後進行初始化。
下面看看SMALL_VALUES[i + 128]是什麼東西:
private static final Integer[] SMALL_VALUES = new Integer[256];
它是一個靜態的Integer數組對象,也就是說最終valueOf返回的都是一個Integer對象。
所以我們這裡可以總結一點:裝箱的過程會創建對應的對象,這個會消耗內存,所以裝箱的過程會增加內存的消耗,影響性能。
2、接著看看intValue函數
@Override
public int intValue() {
return value;
}
這個很簡單,直接返回value值即可。
二、相關問題
上面我們看到在Integer的構造函數中,它分兩種情況:
1、i = 128 || i -128 ===== new Integer(i)
2、i 128 i = -128 ===== SMALL_VALUES[i + 128]
private static final Integer[] SMALL_VALUES = new Integer[256];
SMALL_VALUES本來已經被創建好,也就是說在i = 128 || i -128是會創建不同的對象,在i 128 i = -128會根據i的值返回已經創建好的指定的對象。
說這些可能還不是很明白,下面我們來舉個例子吧:
public class Main { public static void main(String[] args) {
Integer i1 = 100;
Integer i2 = 100;
Integer i3 = 200;
Integer i4 = 200;
System.out.println(i1==i2); //true
System.out.println(i3==i4); //false
}
}
代碼的後面,我們可以看到它們的執行結果是不一樣的,為什麼,在看看我們上面的說明。
1、i1和i2會進行自動裝箱,執行了valueOf函數,它們的值在(-128,128]這個範圍內,它們會拿到SMALL_VALUES數組裡面的同一個對象SMALL_VALUES[228],它們引用到了同一個Integer對象,所以它們肯定是相等的。
2、i3和i4也會進行自動裝箱,執行了valueOf函數,它們的值大於128,所以會執行new Integer(200),也就是說它們會分別創建兩個不同的對象,所以它們肯定不等。
下面我們來看看另外一個例子:
public class Main { public static void main(String[] args) {
Double i1 = 100.0;
Double i2 = 100.0;
Double i3 = 200.0;
Double i4 = 200.0;
System.out.println(i1==i2); //false
System.out.println(i3==i4); //false
}
}
看看上面的執行結果,跟Integer不一樣,這樣也不必奇怪,因為它們的valueOf實現不一樣,結果肯定不一樣,那為什麼它們不統一一下呢?
這個很好理解,因為對於Integer,在(-128,128]之間只有固定的256個值,所以為了避免多次創建對象,我們事先就創建好一個大小為256的Integer數組SMALL_VALUES,所以如果值在這個範圍內,就可以直接返回我們事先創建好的對象就可以了。
但是對於Double類型來說,我們就不能這樣做,因為它在這個範圍內個數是無限的。
總結一句就是:在某個範圍內的整型數值的個數是有限的,而浮點數卻不是。
所以在Double裡面的做法很直接,就是直接創建一個對象,所以每次創建的對象都不一樣。
public static Double valueOf(double d) {
return new Double(d);
}
下面我們進行一個歸類:
Integer派別:Integer、Short、Byte、Character、Long這幾個類的valueOf方法的實現是類似的。
Double派別:Double、Float的valueOf方法的實現是類似的。每次都返回不同的對象。
下面對Integer派別進行一個總結,如下圖:
下面我們來看看另外一種情況:
public class Main { public static void main(String[] args) {
Boolean i1 = false;
Boolean i2 = false;
Boolean i3 = true;
Boolean i4 = true;
System.out.println(i1==i2);//true
System.out.println(i3==i4);//true
}
}
可以看到返回的都是true,也就是它們執行valueOf返回的都是相同的對象。
public static Boolean valueOf(boolean b) {
return b ? Boolean.TRUE : Boolean.FALSE;
}
可以看到它並沒有創建對象,因為在內部已經提前創建好兩個對象,因為它只有兩種情況,這樣也是為了避免重複創建太多的對象。
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
上面把幾種情況都介紹到了,下面來進一步討論其他情況。
Integer num1 = 400;
int num2 = 400;
System.out.println(num1 == num2); //true
說明num1 == num2進行了拆箱操作
Integer num1 = 100;
int num2 = 100;
System.out.println(num1.equals(num2)); //true
我們先來看看equals源碼:
@Override
public boolean equals(Object o) {
return (o instanceof Integer) (((Integer) o).value == value);
}
我們指定equal比較的是內容本身,並且我們也可以看到equal的參數是一個Object對象,我們傳入的是一個int類型,所以首先會進行裝箱,然後比較,之所以返回true,是由於它比較的是對象裡面的value值。
Integer num1 = 100;
int num2 = 100;
Long num3 = 200l;
System.out.println(num1 + num2); //200
System.out.println(num3 == (num1 + num2)); //true
System.out.println(num3.equals(num1 + num2)); //false
1、當一個基礎數據類型與封裝類進行==、+、-、*、/運算時,會將封裝類進行拆箱,對基礎數據類型進行運算。
2、對於num3.equals(num1 + num2)為false的原因很簡單,我們還是根據代碼實現來說明:
@Override
public boolean equals(Object o) {
return (o instanceof Long) (((Long) o).value == value);
}
它必須滿足兩個條件才為true:
1、類型相同
2、內容相同
上面返回false的原因就是類型不同。
Integer num1 = 100;
Ingeger num2 = 200;
Long num3 = 300l;
System.out.println(num3 == (num1 + num2)); //true
我們來反編譯一些這個class文件:javap -c StringTest
可以看到運算的時候首先對num3進行拆箱(執行num3的longValue得到基礎類型為long的值300),然後對num1和mum2進行拆箱(分別執行了num1和num2的intValue得到基礎類型為int的值100和200),然後進行相關的基礎運算。
我們來對基礎類型進行一個測試:
int num1 = 100;
int num2 = 200;
long mum3 = 300;
System.out.println(num3 == (num1 + num2)); //true
上面就說明了為什麼最上面會返回true.
所以,當 「==」運算符的兩個操作數都是 包裝器類型的引用,則是比較指向的是否是同一個對象,而如果其中有一個操作數是表達式(即包含算術運算)則比較的是數值(即會觸發自動拆箱的過程)。
通過上面的分析我們需要知道兩點:
1、什麼時候會引發裝箱和拆箱
2、裝箱操作會創建對象,頻繁的裝箱操作會消耗許多內存,影響性能,所以可以避免裝箱的時候應該盡量避免。
我有一個微信公眾號,經常會分享一些Java技術相關的乾貨文章,還有一些學習資源。
如果你需要的話,可以用微信搜索「Java團長」或者「javatuanzhang」關注。
求java自動打包和自動拆包功能的解釋,謝謝。
就是數值的原始類型和包裝器類型可以直接自動互相轉換,在需要包裝器的上下文中,會自動將數值型轉換成包裝器,在需要原始數值型時,如果傳來的是包裝器,則自動解包成原始數值型。
原創文章,作者:IIRW,如若轉載,請註明出處:https://www.506064.com/zh-tw/n/137014.html