這篇文章給大家介紹深入淺析Java中拆箱與自動(dòng)裝箱的原理,內(nèi)容非常詳細(xì),感興趣的小伙伴們可以參考借鑒,希望對(duì)大家能有所幫助。
成都創(chuàng)新互聯(lián)網(wǎng)站建設(shè)公司一直秉承“誠信做人,踏實(shí)做事”的原則,不欺瞞客戶,是我們最起碼的底線! 以服務(wù)為基礎(chǔ),以質(zhì)量求生存,以技術(shù)求發(fā)展,成交一個(gè)客戶多一個(gè)朋友!專注中小微企業(yè)官網(wǎng)定制,做網(wǎng)站、網(wǎng)站建設(shè),塑造企業(yè)網(wǎng)絡(luò)形象打造互聯(lián)網(wǎng)企業(yè)效應(yīng)。
什么是自動(dòng)裝箱和拆箱
自動(dòng)裝箱就是Java自動(dòng)將原始類型值轉(zhuǎn)換成對(duì)應(yīng)的對(duì)象,比如將int的變量轉(zhuǎn)換成Integer對(duì)象,這個(gè)過程叫做裝箱,反之將Integer對(duì)象轉(zhuǎn)換成int類型值,這個(gè)過程叫做拆箱。因?yàn)檫@里的裝箱和拆箱是自動(dòng)進(jìn)行的非人為轉(zhuǎn)換,所以就稱作為自動(dòng)裝箱和拆箱。原始類型byte, short, char, int, long, float, double 和 boolean 對(duì)應(yīng)的封裝類為Byte, Short, Character, Integer, Long, Float, Double, Boolean。
下面例子是自動(dòng)裝箱和拆箱帶來的疑惑
public class Test { public static void main(String[] args) { test(); } public static void test() { int i = 40; int i0 = 40; Integer i1 = 40; Integer i2 = 40; Integer i3 = 0; Integer i4 = new Integer(40); Integer i5 = new Integer(40); Integer i6 = new Integer(0); Double d1=1.0; Double d2=1.0; System.out.println("i=i0\t" + (i == i0)); System.out.println("i1=i2\t" + (i1 == i2)); System.out.println("i1=i2+i3\t" + (i1 == i2 + i3)); System.out.println("i4=i5\t" + (i4 == i5)); System.out.println("i4=i5+i6\t" + (i4 == i5 + i6)); System.out.println("d1=d2\t" + (d1==d2)); System.out.println(); } }
請(qǐng)看下面的輸出結(jié)果跟你預(yù)期的一樣嗎?
輸出的結(jié)果:
i=i0 true
i1=i2 true
i1=i2+i3 true
i4=i5 false
i4=i5+i6 true
d1=d2 false
為什么會(huì)這樣?帶著疑問繼續(xù)往下看。
自動(dòng)裝箱和拆箱的原理
自動(dòng)裝箱時(shí)編譯器調(diào)用valueOf將原始類型值轉(zhuǎn)換成對(duì)象,同時(shí)自動(dòng)拆箱時(shí),編譯器通過調(diào)用類似intValue(),doubleValue()這類的方法將對(duì)象轉(zhuǎn)換成原始類型值。
明白自動(dòng)裝箱和拆箱的原理后,我們帶著上面的疑問進(jìn)行分析下Integer的自動(dòng)裝箱的實(shí)現(xiàn)源碼。如下:
public static Integer valueOf(int i) { //判斷i是否在-128和127之間,如果不在此范圍,則從IntegerCache中獲取包裝類的實(shí)例。否則new一個(gè)新實(shí)例 if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } //使用亨元模式,來減少對(duì)象的創(chuàng)建(亨元設(shè)計(jì)模式大家有必要了解一下,我認(rèn)為是最簡(jiǎn)單的設(shè)計(jì)模式,也許大家經(jīng)常在項(xiàng)目中使用,不知道他的名字而已) private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; //靜態(tài)方法,類加載的時(shí)候進(jìn)行初始化cache[],靜態(tài)變量存放在常量池中 static { // high value may be configured by property int 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_VALUE h = 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() {} }
Integer i1 = 40; 自動(dòng)裝箱,相當(dāng)于調(diào)用了Integer.valueOf(40);方法。
首先判斷i值是否在-128和127之間,如果在-128和127之間則直接從IntegerCache.cache緩存中獲取指定數(shù)字的包裝類;不存在則new出一個(gè)新的包裝類。
IntegerCache內(nèi)部實(shí)現(xiàn)了一個(gè)Integer的靜態(tài)常量數(shù)組,在類加載的時(shí)候,執(zhí)行static靜態(tài)塊進(jìn)行初始化-128到127之間的Integer對(duì)象,存放到cache數(shù)組中。cache屬于常量,存放在java的方法區(qū)中。
接著看下面是java8種基本類型的自動(dòng)裝箱代碼實(shí)現(xiàn)。如下:
//boolean原生類型自動(dòng)裝箱成Boolean public static Boolean valueOf(boolean b) { return (b ? TRUE : FALSE); } //byte原生類型自動(dòng)裝箱成Byte public static Byte valueOf(byte b) { final int offset = 128; return ByteCache.cache[(int)b + offset]; } //byte原生類型自動(dòng)裝箱成Byte public static Short valueOf(short s) { final int offset = 128; int sAsInt = s; if (sAsInt >= -128 && sAsInt <= 127) { // must cache return ShortCache.cache[sAsInt + offset]; } return new Short(s); } //char原生類型自動(dòng)裝箱成Character public static Character valueOf(char c) { if (c <= 127) { // must cache return CharacterCache.cache[(int)c]; } return new Character(c); } //int原生類型自動(dòng)裝箱成Integer public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } //int原生類型自動(dòng)裝箱成Long public static Long valueOf(long l) { final int offset = 128; if (l >= -128 && l <= 127) { // will cache return LongCache.cache[(int)l + offset]; } return new Long(l); } //double原生類型自動(dòng)裝箱成Double public static Double valueOf(double d) { return new Double(d); } //float原生類型自動(dòng)裝箱成Float public static Float valueOf(float f) { return new Float(f); }
通過分析源碼發(fā)現(xiàn),只有double和float的自動(dòng)裝箱代碼沒有使用緩存,每次都是new 新的對(duì)象,其它的6種基本類型都使用了緩存策略。
使用緩存策略是因?yàn)?,緩存的這些對(duì)象都是經(jīng)常使用到的(如字符、-128至127之間的數(shù)字),防止每次自動(dòng)裝箱都創(chuàng)建一此對(duì)象的實(shí)例。
而double、float是浮點(diǎn)型的,沒有特別的熱的(經(jīng)常使用到的)數(shù)據(jù)的,緩存效果沒有其它幾種類型使用效率高。
下面在看下裝箱和拆箱問題解惑。
//1、這個(gè)沒解釋的就是true System.out.println("i=i0\t" + (i == i0)); //true //2、int值只要在-128和127之間的自動(dòng)裝箱對(duì)象都從緩存中獲取的,所以為true System.out.println("i1=i2\t" + (i1 == i2)); //true //3、涉及到數(shù)字的計(jì)算,就必須先拆箱成int再做加法運(yùn)算,所以不管他們的值是否在-128和127之間,只要數(shù)字一樣就為true System.out.println("i1=i2+i3\t" + (i1 == i2 + i3));//true //比較的是對(duì)象內(nèi)存地址,所以為false System.out.println("i4=i5\t" + (i4 == i5)); //false //5、同第3條解釋,拆箱做加法運(yùn)算,對(duì)比的是數(shù)字,所以為true System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));//true //double的裝箱操作沒有使用緩存,每次都是new Double,所以false System.out.println("d1=d2\t" + (d1==d2));//false
關(guān)于深入淺析Java中拆箱與自動(dòng)裝箱的原理就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺得文章不錯(cuò),可以把它分享出去讓更多的人看到。