詳解Java 自動(dòng)裝箱與拆箱的實(shí)現(xiàn)原理
什么是自動(dòng)裝箱和拆箱
自動(dòng)裝箱就是Java自動(dòng)將原始類(lèi)型值轉(zhuǎn)換成對(duì)應(yīng)的對(duì)象,比如將int的變量轉(zhuǎn)換成Integer對(duì)象,這個(gè)過(guò)程叫做裝箱,反之將Integer對(duì)象轉(zhuǎn)換成int類(lèi)型值,這個(gè)過(guò)程叫做拆箱。因?yàn)檫@里的裝箱和拆箱是自動(dòng)進(jìn)行的非人為轉(zhuǎn)換,所以就稱(chēng)作為自動(dòng)裝箱和拆箱。原始類(lèi)型byte, short, char, int, long, float, double 和 boolean 對(duì)應(yīng)的封裝類(lèi)為Byte, Short, Character, Integer, Long, Float, Double, Boolean。
下面例子是自動(dòng)裝箱和拆箱帶來(lái)的疑惑
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ì)這樣?帶著疑問(wèn)繼續(xù)往下看。
自動(dòng)裝箱和拆箱的原理
自動(dòng)裝箱時(shí)編譯器調(diào)用valueOf將原始類(lèi)型值轉(zhuǎn)換成對(duì)象,同時(shí)自動(dòng)拆箱時(shí),編譯器通過(guò)調(diào)用類(lèi)似intValue(),doubleValue()這類(lèi)的方法將對(duì)象轉(zhuǎn)換成原始類(lèi)型值。
明白自動(dòng)裝箱和拆箱的原理后,我們帶著上面的疑問(wèn)進(jìn)行分析下Integer的自動(dòng)裝箱的實(shí)現(xiàn)源碼。如下:
public static Integer valueOf(int i) {
//判斷i是否在-128和127之間,如果不在此范圍,則從IntegerCache中獲取包裝類(lèi)的實(shí)例。否則new一個(gè)新實(shí)例
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
//使用亨元模式,來(lá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)方法,類(lè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ù)字的包裝類(lèi);不存在則new出一個(gè)新的包裝類(lèi)。
IntegerCache內(nèi)部實(shí)現(xiàn)了一個(gè)Integer的靜態(tài)常量數(shù)組,在類(lèi)加載的時(shí)候,執(zhí)行static靜態(tài)塊進(jìn)行初始化-128到127之間的Integer對(duì)象,存放到cache數(shù)組中。cache屬于常量,存放在java的方法區(qū)中。
接著看下面是java8種基本類(lèi)型的自動(dòng)裝箱代碼實(shí)現(xiàn)。如下:
//boolean原生類(lèi)型自動(dòng)裝箱成Boolean
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
//byte原生類(lèi)型自動(dòng)裝箱成Byte
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
//byte原生類(lèi)型自動(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原生類(lèi)型自動(dòng)裝箱成Character
public static Character valueOf(char c) {
if (c <= 127) { // must cache
return CharacterCache.cache[(int)c];
}
return new Character(c);
}
//int原生類(lèi)型自動(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原生類(lèi)型自動(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原生類(lèi)型自動(dòng)裝箱成Double
public static Double valueOf(double d) {
return new Double(d);
}
//float原生類(lèi)型自動(dòng)裝箱成Float
public static Float valueOf(float f) {
return new Float(f);
}
通過(guò)分析源碼發(fā)現(xiàn),只有double和float的自動(dòng)裝箱代碼沒(méi)有使用緩存,每次都是new 新的對(duì)象,其它的6種基本類(lèi)型都使用了緩存策略。
使用緩存策略是因?yàn)?,緩存的這些對(duì)象都是經(jīng)常使用到的(如字符、-128至127之間的數(shù)字),防止每次自動(dòng)裝箱都創(chuàng)建一此對(duì)象的實(shí)例。
而double、float是浮點(diǎn)型的,沒(méi)有特別的熱的(經(jīng)常使用到的)數(shù)據(jù)的,緩存效果沒(méi)有其它幾種類(lèi)型使用效率高。
下面在看下裝箱和拆箱問(wèn)題解惑。
//1、這個(gè)沒(méi)解釋的就是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條解釋?zhuān)鹣渥黾臃ㄟ\(yùn)算,對(duì)比的是數(shù)字,所以為true
System.out.println("i4=i5+i6\t" + (i4 == i5 + i6));//true
//double的裝箱操作沒(méi)有使用緩存,每次都是new Double,所以false
System.out.println("d1=d2\t" + (d1==d2));//false
相信你看到這就應(yīng)該能明白上面的程序輸出的結(jié)果為什么是true,false了,只要掌握原理,類(lèi)似的問(wèn)題就迎刃而解了,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
使用MapStruct進(jìn)行Java Bean映射的方式
MapStruct是一個(gè)用于JavaBean映射的注解處理器,它通過(guò)注解生成類(lèi)型安全且性能優(yōu)異的映射代碼,避免手動(dòng)編寫(xiě)重復(fù)的樣板代碼,主要特性包括類(lèi)型安全、高性能、簡(jiǎn)潔和可定制性,使用步驟包括定義映射接口、創(chuàng)建源類(lèi)和目標(biāo)類(lèi)、生成映射代碼并調(diào)用映射方法2025-02-02
Java中關(guān)于優(yōu)先隊(duì)列PriorityQueue的使用及相關(guān)方法
這篇文章主要介紹了Java中關(guān)于優(yōu)先隊(duì)列PriorityQueue的使用及相關(guān)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
SpringBoot項(xiàng)目使用mybatis-plus代碼生成的實(shí)例詳解
mybatis-plus是mybatis的增強(qiáng),不對(duì)mybatis做任何改變,涵蓋了代碼生成,自定義ID生成器,快速實(shí)現(xiàn)CRUD,自動(dòng)分頁(yè),邏輯刪除等功能。本文就來(lái)講講SpringBoot項(xiàng)目如何使用mybatis-plus實(shí)現(xiàn)代碼生成,需要的可以了解一下2022-10-10
SpringBoot如何對(duì)LocalDateTime進(jìn)行格式化并解析
這篇文章主要介紹了SpringBoot如何對(duì)LocalDateTime進(jìn)行格式化方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07
SpringBoot集成Kaptcha驗(yàn)證碼的詳細(xì)過(guò)程
Kaptcha是一個(gè)強(qiáng)大而靈活的Java驗(yàn)證碼生成庫(kù),通過(guò)合理的配置和使用,它可以有效地提高web應(yīng)用的安全性,防止自動(dòng)化程序的濫用,這篇文章主要介紹了SpringBoot集成Kaptcha驗(yàn)證碼,需要的朋友可以參考下2024-07-07
java實(shí)現(xiàn)分布式項(xiàng)目搭建的方法
這篇文章主要介紹了java實(shí)現(xiàn)分布式項(xiàng)目搭建的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-04-04
在安卓系統(tǒng)中插入表情到光標(biāo)位置的代碼詳解
這篇文章主要介紹了在安卓系統(tǒng)中插入表情到光標(biāo)位置的代碼詳解,利用Java代碼在EditText控件中實(shí)現(xiàn),需要的朋友可以參考下2015-07-07

