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