Java中的自動(dòng)拆裝箱、基本類型的轉(zhuǎn)換、包裝類的緩存詳解
數(shù)據(jù)類型的拆裝箱
1. 拆箱、裝箱
拆箱
把包裝類轉(zhuǎn)換為基本數(shù)據(jù)類型就是拆箱。拆箱通過(guò)包裝類的xxValue方法實(shí)現(xiàn)。
裝箱
把基本類型轉(zhuǎn)換為包裝類的過(guò)程就是裝箱。裝箱通過(guò)包裝類的valueOf方法實(shí)現(xiàn)。
2. 自動(dòng)拆箱
將包裝類自動(dòng)轉(zhuǎn)化成對(duì)應(yīng)的基本數(shù)據(jù)類型(通過(guò)包裝類的xxValue方法)。
public static void main(String[] args) { int int1 = new Integer(100); long long1 = new Long(100); double double1 = new Double(100.0); }
實(shí)際等于
public static void main(String[] args) { int int1 = new Integer(100).intValue(); long long1 = new Long(100).longValue(); double double1 = new Double(100.0).doubleValue(); }
3. 自動(dòng)裝箱
將基本數(shù)據(jù)類型自動(dòng)轉(zhuǎn)化為對(duì)應(yīng)的包裝類(通過(guò)包裝類的valueOf方法)。
public static void main(String[] args) { Integer int2 = 100; Long long2 = 100L; Double double2 = 100.0; }
實(shí)際等于
public static void main(String[] args) { Integer int2 = Integer.valueOf(100); Long long2 = Long.valueOf(100L); Double double2 = Double.valueOf(100.0); }
4. 自動(dòng)拆裝箱使用場(chǎng)景
將基本類型放入集合類(自動(dòng)裝箱)
集合類只支持包裝類型(不支持基本數(shù)據(jù)類型),但是我們add(基本數(shù)據(jù)類型)也不會(huì)報(bào)錯(cuò),是因?yàn)镴ava給我們做了自動(dòng)裝箱。
實(shí)際等于
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(Integer.valueOf(1)); list.add(Integer.valueOf(2)); }
包裝類型和基本類型比較大?。ㄗ詣?dòng)拆箱)
包裝類與基本數(shù)據(jù)類型進(jìn)行比較運(yùn)算,其實(shí)是先將包裝類進(jìn)行拆箱成基本數(shù)據(jù)類型,然后比較。
public static void main(String[] args) { boolean b = new Integer(2) > 1; }
實(shí)際等于
public static void main(String[] args) { boolean b = new Integer(2).intValue() > 1; }
包裝類型的運(yùn)算(自動(dòng)拆箱)
運(yùn)算中包含包裝類型時(shí),會(huì)將包裝類型自動(dòng)拆箱為基本類型再進(jìn)行運(yùn)算
public static void main(String[] args) { int a = new Integer(1) + 1; }
實(shí)際等于
public static void main(String[] args) { int a = new Integer(1).intValue() + 1; }
三目運(yùn)算(自動(dòng)拆箱)
value = flag ? value1 : value2
三目運(yùn)算中,如果value1和value2中一個(gè)是基本數(shù)據(jù)類型、另一個(gè)是包裝類型。那么無(wú)論value是基本數(shù)據(jù)類型還是包裝類型,只要根據(jù)判斷結(jié)果從value1和value2選擇的那個(gè)值是包裝類型時(shí),都會(huì)先把包裝類型拆箱為基本數(shù)據(jù)類型,再根據(jù)value的類型來(lái)決定是否再對(duì)值進(jìn)行拆封箱處理。
public static void main(String[] args) { Integer a = null; int v1 = true ? 0 : a;// 正常 int v2 = false ? 0 : a;// 報(bào)錯(cuò) Integer v3 = true ? 0 : a;// 正常 Integer v4 = false ? 0 : a;// 報(bào)錯(cuò) }
實(shí)際等于
public static void main(String[] args) { Integer a = null; int v1 = true ? 0 : a;// 正常 int v2 = false ? 0 : a.intValue();// 報(bào)錯(cuò) Integer v3 = true ? 0 : a;// 正常 Integer v4 = false ? 0 : Integer.valueOf(a.intValue());// 報(bào)錯(cuò) }
方法的返回值(自動(dòng)拆裝箱)
方法的返回值類型與實(shí)際返回值類型不同時(shí),會(huì)自動(dòng)拆裝箱,變成方法返回值相同的類型。
//自動(dòng)拆箱 public int getNum1() { return new Integer(1); } //自動(dòng)裝箱 public Integer getNum2() { return 1; }
實(shí)際等于
//自動(dòng)拆箱 public int getNum1() { return new Integer(1).byteValue(); } //自動(dòng)裝箱 public Integer getNum2() { return Integer.valueOf(1); }
5. 自動(dòng)拆裝箱的觸發(fā)時(shí)機(jī)
在編譯期,Java文件編譯稱Class(字節(jié)碼)文件的過(guò)程中觸發(fā)自動(dòng)拆裝箱的動(dòng)作。
通過(guò)查看字節(jié)碼文件可以予以證明。兩種查看字節(jié)碼信息的方式:
1.通過(guò)javac和javap查看
先通過(guò)javac將.java代碼編譯成.class字節(jié)碼,然后通過(guò)javap分析字節(jié)碼。
javac:編譯成class(字節(jié)碼)
javac DemoTest.java
javap:分析字節(jié)碼
javap -verbose DemoTest.class
若出現(xiàn)編碼問(wèn)題,可指定編碼
javac -encoding utf-8 DemoTest.java javap -encoding utf-8 -verbose DemoTest.class
2.IDEA使用ASM Bytecode Outline插件查看字節(jié)碼
類文件上右鍵->選擇: Show Bytecode outline
面板上的三個(gè)選項(xiàng):
- Bytecode 表示對(duì)應(yīng)的class字節(jié)碼文件
- ASMified 表示利用ASM框架生成字節(jié)碼對(duì)應(yīng)的代碼
- Groovified 對(duì)應(yīng)的是class字節(jié)碼指令
6. 自動(dòng)拆裝箱帶來(lái)的問(wèn)題
- 包裝對(duì)象之間的數(shù)值比較不能簡(jiǎn)單的使用==,除了特殊有緩存的情況(如Integer的-128~127),其他比較都需要使用equals比較。
- 如果包裝類對(duì)象為NULL,那么自動(dòng)拆箱就可能會(huì)拋出空指針異常
- 如果一個(gè)for循環(huán)中有大量拆裝箱操作,會(huì)浪費(fèi)很多資源
基本數(shù)據(jù)類型的轉(zhuǎn)換
1. 容量大小排序
基本數(shù)據(jù)類型容量大小排序(不含布爾類型、容量由小到大):
- byte<short/char<int<long<float<double
- byte<<int<long<float<double
2. 轉(zhuǎn)換規(guī)則
- 8種基本數(shù)據(jù)類型中,除了布爾類型之外,其他的8中數(shù)據(jù)類型之間都可以相互轉(zhuǎn)換
- 任何浮點(diǎn)類型不管占用多少個(gè)字符,都比整數(shù)型容量大
- char和short可表示的種類數(shù)量相同,但是char可以取更大的整數(shù)
- 整數(shù)的默認(rèn)類型為int,小數(shù)的默認(rèn)類型為double
- 小容量向大容量轉(zhuǎn)換,稱為自動(dòng)類型轉(zhuǎn)換(又稱隱式類型轉(zhuǎn)換),不會(huì)丟失精度
- 大容量轉(zhuǎn)換成小容量,叫做強(qiáng)制類型轉(zhuǎn)換(又稱顯式類型轉(zhuǎn)換),可能會(huì)會(huì)丟失精度,需要加強(qiáng)制類型轉(zhuǎn)換符。
- 當(dāng)整數(shù)字面值沒(méi)有超出byte,short,char的取值范圍,可以直接賦值給byte,short,char類型的變量
- 多種數(shù)據(jù)類型混合運(yùn)算,先轉(zhuǎn)換成容量大的那種類型再做運(yùn)算(byte,short,char混合運(yùn)算的時(shí)候,各自先轉(zhuǎn)換成int類型再做運(yùn)算)
3. 自動(dòng)類型轉(zhuǎn)換
小容量向大容量轉(zhuǎn)換,稱為自動(dòng)類型轉(zhuǎn)換(又稱隱式類型轉(zhuǎn)換)。
規(guī)則
- 小容量的類型轉(zhuǎn)化為大容量的類型自動(dòng)使用自動(dòng)類型轉(zhuǎn)換
- 整數(shù)類型可以自動(dòng)轉(zhuǎn)化為浮點(diǎn)類型,可能會(huì)產(chǎn)生舍入誤差
- 字符可以自動(dòng)提升為整數(shù)
4. 強(qiáng)制類型轉(zhuǎn)換
大容量轉(zhuǎn)換成小容量,叫做強(qiáng)制類型轉(zhuǎn)換(又稱顯式類型轉(zhuǎn)換)。
強(qiáng)制類型轉(zhuǎn)換需要在要強(qiáng)制類型轉(zhuǎn)換的前面加上括號(hào),然后在括號(hào)里面加上你要轉(zhuǎn)換的類型。
規(guī)則
- 大容量的類型轉(zhuǎn)化為大容量的類型必須使用強(qiáng)制類型轉(zhuǎn)換
- 強(qiáng)制類型轉(zhuǎn)換可能導(dǎo)致溢出或損失精度
- 浮點(diǎn)數(shù)到整數(shù)的轉(zhuǎn)換是通過(guò)舍棄小數(shù)得到,而不是四舍五入
- 不能把對(duì)象類型轉(zhuǎn)換為不相干的類型
包裝類的緩存
Java包裝類的緩存機(jī)制,是在Java 5中引入的一個(gè)有助于節(jié)省內(nèi)存、提高性能的功能。
包裝類的緩存只在裝箱(通過(guò)包裝類的valueOf方法來(lái)實(shí)現(xiàn))時(shí)有效。
想必大家一定遇到過(guò)這樣的問(wèn)題:兩個(gè)數(shù)值相等的包裝類型對(duì)象通過(guò)==進(jìn)行比較時(shí),得到的結(jié)果有時(shí)是true,有時(shí)又是false。
這可能就是包裝類的緩存在搗亂:當(dāng)包裝類對(duì)象經(jīng)歷過(guò)裝箱操作,得到的對(duì)象可能是緩存好的對(duì)象,也可能是新創(chuàng)建的對(duì)象。
1. 包裝類型的緩存值范圍
基本類型 | 大小(bit) | 默認(rèn)值 | 取值范圍 | 包裝類 | 包裝類緩存范圍 |
---|---|---|---|---|---|
byte(字節(jié)) | 8 | 0 | [-2^7,2^7-1] [-128,127] | Byte | [-128,127] |
char(字符) | 16 | 空值(\u0000) (unicode編碼) | [0,2^16-1] [0,65535] | Character | [0,127] |
short(短整數(shù)) | 16 | 0 | [-2^15,2^15-1] [-32768,32767] | Short | [-128,127] |
int(整數(shù)) | 32 | 0 | [-2^31,2^31-1] [-2147483648,2147483647] | Integer | [-128,127] |
long(長(zhǎng)整數(shù)) | 64 | 0L | [-2^63,2^63-1] [-9223372036854774808,9223372036854774807] | Long | [-128,127] |
float(單精度小數(shù)) | 32 | 0.0F | [-2^31,2^31-1] [3.402823e+38,1.401298e-45] | Float | 無(wú) |
double(雙精度小數(shù)) | 64 | 0.0 | [-2^63,2^63-1] [1.797693e+308,4.9000000e-324] | Double | 無(wú) |
boolean(布爾值) | 8 | false | true,false | Boolean | true,false |
boolean類型的底層是轉(zhuǎn)換為1,0存儲(chǔ)的,所以大小只有一個(gè)字節(jié)(8位)
- valueOf方法的邏輯是先從判斷值是否在緩存值范圍中。如果在,直接返回緩存中的對(duì)象;如果不在,創(chuàng)建新的對(duì)象。
- 在 jdk 1.8 所有的數(shù)值類緩沖池中,Integer 的緩沖池 IntegerCache 很特殊,這個(gè)緩沖池的下界是 - 128,上界默認(rèn)是 127,但是這個(gè)上界是可調(diào)的。
- 在啟動(dòng) jvm 的時(shí)候,通過(guò) -XX:AutoBoxCacheMax=來(lái)指定這個(gè)緩沖池的大小,該選項(xiàng)在 JVM 初始化的時(shí)候會(huì)設(shè)定一個(gè)名為 java.lang.IntegerCache.high 系統(tǒng)屬性,然后IntegerCache 初始化的時(shí)候就會(huì)讀取該系統(tǒng)屬性來(lái)決定上界。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
SpringBoot Shiro授權(quán)實(shí)現(xiàn)過(guò)程解析
這篇文章主要介紹了SpringBoot Shiro授權(quán)實(shí)現(xiàn)過(guò)程解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11hibernate一對(duì)多關(guān)聯(lián)映射學(xué)習(xí)小結(jié)
這篇文章主要介紹了hibernate一對(duì)多關(guān)聯(lián)映射學(xué)習(xí)小結(jié),需要的朋友可以參考下2017-09-09SpringCloud修改Feign日志記錄級(jí)別過(guò)程淺析
OpenFeign源于Netflix的Feign,是http通信的客戶端。屏蔽了網(wǎng)絡(luò)通信的細(xì)節(jié),直接面向接口的方式開(kāi)發(fā),讓開(kāi)發(fā)者感知不到網(wǎng)絡(luò)通信細(xì)節(jié)。所有遠(yuǎn)程調(diào)用,都像調(diào)用本地方法一樣完成2023-02-02Maven如何解決添加依賴之后沒(méi)有加載jar包報(bào)錯(cuò)問(wèn)題
這篇文章主要介紹了Maven如何解決添加依賴之后沒(méi)有加載jar包報(bào)錯(cuò)問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-05-05Springboot下RedisTemplate的兩種序列化方式實(shí)例詳解
這篇文章主要介紹了Springboot下RedisTemplate的兩種序列化方式,通過(guò)定義一個(gè)配置類,自定義RedisTemplate的序列化方式,結(jié)合實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-09-09mybatis中bind標(biāo)簽和concat的使用說(shuō)明
這篇文章主要介紹了mybatis中bind標(biāo)簽和concat的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-12-12Spring MVC如何設(shè)置請(qǐng)求頭和響應(yīng)頭的Header
這篇文章主要介紹了Spring MVC如何設(shè)置請(qǐng)求頭和響應(yīng)頭的Header問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03