一文帶你深入了解Java的自動(dòng)拆裝箱
什么是自動(dòng)拆裝箱
JDK 1.5開(kāi)始增加了自動(dòng)拆裝箱機(jī)制,Java保留了一些原始的基本數(shù)據(jù)類(lèi)型,但由于Java是強(qiáng)面向?qū)ο笳Z(yǔ)言,數(shù)據(jù)類(lèi)型當(dāng)然也應(yīng)該設(shè)置成對(duì)象才是,所以Java也推出了對(duì)于基本數(shù)據(jù)類(lèi)型的對(duì)應(yīng)的對(duì)象,將基本數(shù)據(jù)類(lèi)型轉(zhuǎn)換為對(duì)象就稱(chēng)為裝箱,反之則是拆箱
八種基本數(shù)據(jù)類(lèi)型
| 基本數(shù)據(jù)類(lèi)型 | 對(duì)象類(lèi)型 |
|---|---|
| byte | Byte |
| short | Short |
| int | Integer |
| long | Long |
| float | Float |
| double | Double |
| char | Character |
| boolean | Boolean |
Java的大部分字節(jié)碼指令都沒(méi)有支持類(lèi)型byte、char和short,甚至沒(méi)有任何指令支持boolean類(lèi)型。
那么Java底層是如何實(shí)現(xiàn)這些數(shù)據(jù)類(lèi)型的?
事實(shí)上編譯器會(huì)在編譯期或運(yùn)行期搞事
- 將byte和short類(lèi)型的數(shù)據(jù)帶符號(hào)擴(kuò)展(Sign-Extend)為相應(yīng)的int類(lèi)型數(shù)據(jù)。
- 將boolean和char類(lèi)型數(shù)據(jù)零位擴(kuò)展(Zero-Extend)為相應(yīng)的int類(lèi)型數(shù)據(jù)。
在處理boolean、byte、short和char類(lèi)型的數(shù)組時(shí),也會(huì)轉(zhuǎn)換為使用對(duì)應(yīng)的int類(lèi)型的字節(jié)碼指令來(lái)處理。因此,大多數(shù)對(duì)于boolean、byte、short和char類(lèi)型數(shù)據(jù)的操作,實(shí)際上都是使用相應(yīng)的對(duì)int類(lèi)型作為運(yùn)算類(lèi)型來(lái)進(jìn)行的
例如,下面代碼每一行代碼上面的注釋為其部分字節(jié)碼指令
public class Test {
/*
* 以下均為整型入棧指令
* iconst_1 (-1到5的數(shù)據(jù)使用iconst指令,-1使用iconst_m1指令)
* bipush 100 (-128到127[一個(gè)字節(jié)]的數(shù)據(jù)使用bipush指令)
* sipush 1000 (-32768到32767[二個(gè)字節(jié)]的數(shù)據(jù)使用sipush指令)
* ldc 40000 (-2147483648到2147483647[四個(gè)字節(jié)]的數(shù)據(jù)使用ldc指令)
*/
public static void main(String[] args) {
// bipush 100【將整型數(shù)據(jù)100壓入?!?
byte b1 = 100;
// bipush 101【將整型數(shù)據(jù)101壓入?!?
short s1 = 101;
// bipush 49【將整型數(shù)據(jù)49壓入棧】
char c1 = '1';
// iconst_1【將整型數(shù)據(jù)1壓入?!?
boolean bl1 = false;
}
}
自動(dòng)拆裝箱原理
byte與Byte
public class Test {
public static void main(String[] args) {
Byte num = 1;
byte num3 = num;
}
}
編譯后代碼
public class Test {
public static void main(String[] args) {
Byte num = Byte.valueOf(1);
byte num3 = num.byteValue();
}
}
從編譯后代碼可以看出,這兩者的轉(zhuǎn)換用了Byte類(lèi)中的方法,看一下這兩個(gè)方法
// 靜態(tài)內(nèi)部類(lèi)
private static class ByteCache {
private ByteCache(){}
static final Byte cache[] = new Byte[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Byte((byte)(i - 128));
}
}
private final byte value;
public static Byte valueOf(byte b) {
// 數(shù)組是不存在負(fù)的下標(biāo)的,所以加上偏移量
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
public Byte(byte value) {
this.value = value;
}
public byte byteValue() {
return value;
}
Byte類(lèi)在使用的時(shí)候就會(huì)將[-128,127]范圍的Byte對(duì)象緩存進(jìn)類(lèi)中,這是用了享元模式的思想存取常用的熱點(diǎn)Byte對(duì)象,重復(fù)利用該范圍類(lèi)的Byte對(duì)象,也就是說(shuō)在這個(gè)數(shù)值范圍的Byte對(duì)象一直是同一個(gè)對(duì)象
short與Short、long與Long、char與Character的轉(zhuǎn)換原理和這個(gè)相同
(char保存的是ASCII碼,所以只緩存[0,127]的對(duì)象)
int與Integer
public class Test {
public static void main(String[] args) {
Integer num = 1;
Integer num2 = 200;
int num3 = num;
}
}
編譯后代碼
public class Test {
public static void main(String[] args) {
Integer num = Integer.valueOf(1);
Integer num2 = Integer.valueOf(200);
int num3 = num.intValue();
}
}
我們可以看出自動(dòng)拆裝箱事實(shí)上是調(diào)用了Integer類(lèi)中的方法,來(lái)看一下這兩個(gè)方法,事實(shí)上和byte類(lèi)似,默認(rèn)[-128,127]的Integer對(duì)象被緩存了,但是IntegerCache.high是可能被人為配置過(guò)的,如果配置的緩存上限值大于127就會(huì)緩存[-128,配置的上限值],在此范圍內(nèi)直接返回緩存的對(duì)象,否則就new一個(gè)新的Integer對(duì)象
public static Integer valueOf(int i) {
// IntegerCache.low = -128,IntegerCache.high = 127(默認(rèn))
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
boolean與Boolean
同樣是這兩個(gè)方法,由于布爾型只有兩個(gè)值,所以直接緩存兩個(gè)對(duì)象
public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
private final boolean value;
public Boolean(boolean value) {
this.value = value;
}
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
public boolean booleanValue() {
return value;
}
float與Float以及double與Double
由于浮點(diǎn)數(shù)整數(shù)位相同時(shí)小數(shù)位可以有多種情況,比如整數(shù)位為1的有1.321332,1.543635,..................,數(shù)量太多所以沒(méi)有某一個(gè)浮點(diǎn)數(shù)會(huì)被反復(fù)使用,所以沒(méi)必要緩存,直接返回一個(gè)Float新對(duì)象
public static Float valueOf(float f) {
return new Float(f);
}
public static Double valueOf(double d) {
return new Double(d);
}
測(cè)試
我們測(cè)試一下是否返回的是同一個(gè)對(duì)象
public class Test {
public static void main(String[] args) {
Integer b1 = 10;
Integer b2 = 10;
Integer b3 = new Integer(10);
Integer b4 = new Integer(10);
System.out.println(b1 == b2); // true
System.out.println(b2 == b3); // false
System.out.println(b3 == b4); // false
}
}
引用類(lèi)型使用等于比較,比較的是對(duì)象是否相同(基本數(shù)據(jù)類(lèi)型使用 = 進(jìn)行比較比較的是數(shù)值)
那么b1等于b2說(shuō)明兩個(gè)裝箱類(lèi)型返回的b1和b2兩個(gè)Integer對(duì)象是同一個(gè)對(duì)象,10這個(gè)數(shù)值在緩存范圍內(nèi)
而使用構(gòu)造方法返回的對(duì)象是兩個(gè)不同的對(duì)象,所以不相同結(jié)果為false
public class Test {
public static void main(String[] args) {
Integer b1 = 1000;
Integer b2 = 1000;
System.out.println(b1 == b2);// false
}
}
而1000這個(gè)數(shù)值已經(jīng)沒(méi)有緩存了,所以返回的是兩個(gè)對(duì)象
public class Test {
public static void main(String[] args) {
Double b1 = 10.0;
Double b2 = 10.0;
System.out.println(b1 == b2);// false
}
}
由于Double是沒(méi)有用緩存的,所以?xún)蓚€(gè)對(duì)象一定是不同的
到此這篇關(guān)于一文帶你深入了解Java的自動(dòng)拆裝箱的文章就介紹到這了,更多相關(guān)Java自動(dòng)拆裝箱內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
淺談利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問(wèn)題
本篇文章主要介紹了淺談利用Spring的AbstractRoutingDataSource解決多數(shù)據(jù)源的問(wèn)題,具有一定的參考價(jià)值,有需要的可以了解一下2017-08-08
Spring學(xué)習(xí)之開(kāi)發(fā)環(huán)境搭建的詳細(xì)步驟
本篇文章主要介紹了Spring學(xué)習(xí)之開(kāi)發(fā)環(huán)境搭建的詳細(xì)步驟,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07
springcloud整合seata的實(shí)現(xiàn)代碼
這篇文章主要介紹了springcloud整合seata的實(shí)現(xiàn)方法,整合步驟通過(guò)引入spring-cloud-starter-alibaba-seata?jar包,文中結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),需要的朋友可以參考下2022-05-05
Kafka中的producer攔截器與consumer攔截器詳解
這篇文章主要介紹了Kafka中的producer攔截器與consumer攔截器詳解,Producer 的Interceptor使得用戶(hù)在消息發(fā)送前以及Producer回調(diào)邏輯前有機(jī)會(huì)對(duì)消息做 一些定制化需求,比如修改消息等,需要的朋友可以參考下2023-12-12
SpringBoot中引入MyBatisPlus的常規(guī)操作
這篇文章主要介紹了SpringBoot中引入MyBatisPlus的常規(guī)操作,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
SpringBoot?分模塊開(kāi)發(fā)的操作方法
這篇文章主要介紹了SpringBoot?分模塊開(kāi)發(fā)的操作方法,通過(guò)在原項(xiàng)目新增一個(gè)maven模塊,本文通過(guò)示例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-04-04
Java詳解HashMap實(shí)現(xiàn)原理和源碼分析
這篇文章主要介紹了Java關(guān)于HashMap的實(shí)現(xiàn)原理并進(jìn)行源碼分析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-09-09
Java8實(shí)現(xiàn)對(duì)List<Integer>的求和
這篇文章主要介紹了Java8實(shí)現(xiàn)對(duì)List<Integer>的求和方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-05-05

