底层原理_自动装箱与拆箱底层原理
1.自動裝箱與拆箱
??? Java中的數據類型分為兩大類,基本數據類型與引用數據類型。Java中共提供了八種基本數據類型,同時提供了這八種基本數據類型對應的引用數據類型。
????自動裝箱:基本數據類型的數據自動轉化為對應的引用數據類型的數據
Integer integerVal = 0;????自動拆箱:引用數據類型的數據自動轉化為對應的基本數據類型的數據
int intVal = new Integer(0);????基本數據類型變量中存儲的就是數據值,但是引用數據類型的變量實際上指向的是此引用對象的地址值,那么按照這種思想,賦值的操作應該是將對應的數據值作為引用地址或者是引用類型對象的地址值賦值給基本數據類型才對,為什么自動裝箱與拆箱的操作就能將其轉化成對應的數據呢?
????下面就來說一下這種裝箱與拆箱的機制到底底層是如何實現的,如何能實現基本數據類型與對應引用數據類型間的自動轉化。
2.底層原理
????以下剖析原理以int與Integer類型為例
????自動裝箱的底層原理:自動裝箱實際上調用的是Integer中的靜態方法valueOf(),將基本數據類型的int數值包裝成了一個Integer對象
Integer integerVal = 0;//等效于Integer?integerVal?=?Integer.valueOf(0);????自動拆箱的底層原理:自動拆箱的底層實際上調用的是Integer對象的intValue(),得到對象內的int變量的數值,然后給賦值給變量
int?intVal?=?new?Integer(0);//等效于int?intVal?=?new?Integer(0).intValue();? ? 說到這里,可能很多人知道自動裝箱與自動拆箱是與這兩個方法有關,但是空說無憑,下面從字節碼層面來看一下,為什么大家都說自動裝箱與自動拆箱與這兩個方法有關,在哪里能證實這兩個方法就是自動裝箱與拆箱的本質。
3.字節碼原理探究
(1)字節碼文件
? ? 剛開始學Java的時候,大家應該都做過一件事件,那就是編寫.java源文件,然后使用JDK中提供的javac.exe編譯器將.java源文件編譯成.class文件,class后綴的文件也就是Java中的字節碼文件,然后使用java.exe工具來執行字節碼文件。
????也就是說JVM中執行的實際上是.class后綴的字節碼文件,以下就來解析字節碼內容,查看裝箱與拆箱的本質(不懂JVM的朋友也沒有關系,可以一起看一下從字節碼層面來看,自動裝箱與拆箱到底字節碼中是如何表述的)
(2)自動裝箱
?? ?由于字節碼文件是以二進制來存儲信息的,所以直接打開是看不到內容的,JDK中提供的javap.exe工具來將字節碼文件解析成文本
//源碼public?class?MainClass?{ public static void main(String[] args) { Integer integerVal = 0;????}}//javac編譯得到字節碼javac MainClass.java//javap解析字節碼內容(-v?-p參數有興趣的可以自行研究一下)javap -v -p MainClass.clas????使用javap工具解析后得到解析的內容:
????這里只關注public static void main()方法內的內容,Code中內部的一行行代碼就稱之為字節碼指令
????可以看到在字節碼層面調用了Integer.valueOf()方法。
????看到這里就會發現,其實編譯過后,從字節碼層面所看的行為與源碼的層面還是存在著一定的差異的,這也就是Java為開發人員屏蔽了底層細節,讓開發人員更加關注上層的語法,提高開發效率
????從這里也就可以看出,自動裝箱的機制確實是調用了Integer.valueOf()方法,將基本數據類型轉化為對應的引用數據類型
(3)自動拆箱
????自動拆箱的驗證過程與自動裝箱的過程一樣,需要使用javap.exe工具將字節碼內容解析出來
//源碼public class MainClass { public static void main(String[] args) { Integer integer = new Integer(0); int intVal = integer; }}//javac編譯得到字節碼javac MainClass.java//javap解析字節碼內容(-v -p參數有興趣的可以自行研究一下)javap -v -p MainClass.clas? ? 使用javap工具解析后得到解析的內容:
????前面的字節碼可以不用看,是創建Integer部分,在標記10的位置,調用了Integer對象的intValue()方法,這也就是自動拆箱的原理,從基本數據類型自動轉換到對應的引用數據類型
????以上所說明的是int與Integer類型的自動裝箱與拆箱原理,其他的幾種基本數據類型也是一樣的,裝箱與拆箱分別調用的都是XXX.valueOf()與xxxValue()方法,這里就不一一列舉的,具體的查看細節同上
????知道了自動裝箱與拆箱的原理,大家應該也就能知道下面列舉的代碼的執行結果了:
Integer integerVal = null;int val = integerVal;4.數據緩存池
(1)面試題
????既然聊了基本數據類型的自動裝箱與拆箱這一塊,就順帶來聊一聊什么是數據緩存池
????以下是以前看到過的一則面試題:
Integer val1 = 127;Integer val2 = 127;Integer val3 = 128;Integer val4 = 128;System.out.println(val1 == val2);System.out.println(val3 == val4);????剛剛已經聊過了自動裝箱問題,也就知道這里實際上是發生了四次裝箱,得到了四個Integer對象。
????既然Integer是引用數據類型,那么==號比較的就是這兩個引用對象的地址是否相等,但是這里并沒有任何的引用之間相互賦值,按理說應該是四個不相同的對象,但是結果卻出人意料
????根據結果可以推斷出val1與val2實際上引用地址是一樣的,但是val3與val4又是不一樣的對象
????這里的Integer對象都是由Integer的靜態方法valueOf()來創建的,所以就需要看一看valueOf()方法里面到底做了什么,是不是單純地創建對象那么簡單。
(2)數據緩存池? ?
//Integer.valueOf()源碼public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i);}????可以看到,在valueOf()方法內并不是單純地創建對象那么簡單,而是先判斷需要轉換的數據是否在low~high范圍內,如果在范圍內,直接從一個cache數組中取出對應槽位中的Integer對象;如果在此范圍內的話,就直接new一個Integer對象返回。
????下面再來看一下IntegerCache的cache數組到底裝的是什么:
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static {????????int?h?=?127;????????//...... high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++)????????????cache[k]?=?new?Integer(j++);????????//...... } //......}??? IntegerCache是Integer中的一個靜態內部類,上述省略了部分代碼,只留下了關鍵性的代碼。
????可以看到在此類的靜態代碼塊中會創建cache數組,長度也就是127-(-128)=255,后續接著就是一個循環,也就是初始化輸出,數據為-128~127的Integer對象。
????由于static代碼塊是在程序啟動的時候就會運行的,所以也就是說,在程序啟動的時候,Integer中的IntegerCache中的cache數組就被初始化為了-128~127之間,一共255個Integer對象。
????再回到valueOf()方法中來看,判斷數值是否為-128~127之間,如果是的話,就直接返回cache數組中對應的Integer對象,如果在范圍內,才是new一個Integer對象并返回。
????讀到這里,也就能說明為什么val1與val2是同一個對象,但是val3與val4并不是同一個對象了:因為val1與val2都是調用valueOf()方法,參數都是127,127屬于-128~127之間,所以是直接從IntegerCache.cache數組中取出創建好的Integer對象,并且都是從同一個槽位中取的,所以val1與val2是同一個對象;但是128超出了-128~127的范圍,所以不在范圍內而直接new了Integer對象,所以val3與val4都是new的不同的Integer對象,故不是同一個對象
????這里的IntegerCache也就是所謂的數據緩存池,起到一定范圍內緩存對象的目的,避免項目中重復創建相同的Integer對象,浪費空間又損耗性能
其他數據數據類型的緩存池:
????除了Integer中存在數據緩存池外,其他的還有Byte、Character、Short、Long
????此外Boolean中的數據緩存池比較特殊,由于boolean只有true與false,所以直接定義了兩個常量TRUE與FALSE,也就是說,所有使用到的Boolean對象都是TRUE與FALSE兩個對象。
5.總結
自動裝箱指的是基本數據類型自動轉化為對應的引用數據類型
自動拆箱指的是引用數據類型自動轉化為對應的基本數據類型
從字節碼層面發現,自動裝箱實際調用的是XXX.valueOf()方法將對應的數據包裝成對應的對象;自動拆箱是實際調用的是對象的xxxValue()方法返回對象內的基本數據類型數據
自動裝箱的時候,存在一個數據緩存池問題,數據在一定范圍內使用的是緩存池中在程序啟動的時候創建好的對象,超出范圍才是直接new的對象
此外,除了基本數據類型使用“==”號判斷相等外,引用數據類型一定要使用equals()方法來判斷相等(除了特殊情況下需要比對是否為同一個對象的情況下)
????最后,極力推薦有時間的朋友可以去學一下JVM(推薦尚硅谷宋紅康老師講的JVM視頻),可能學習了JVM你看不到太大實質性地改變,但是對更多的底層原理有了一定的理解,碰到問題的時候也就不需要死記硬背了,從原理出發更好地解決問題
總結
以上是生活随笔為你收集整理的底层原理_自动装箱与拆箱底层原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: [云炬创业基础笔记]第十章企业的利润计划
- 下一篇: [云炬创业基础笔记]第十章企业的利润计划