Java中的自動拆裝箱、基本類型的轉換、包裝類的緩存詳解
數(shù)據類型的拆裝箱
1. 拆箱、裝箱
拆箱
把包裝類轉換為基本數(shù)據類型就是拆箱。拆箱通過包裝類的xxValue方法實現(xiàn)。
裝箱
把基本類型轉換為包裝類的過程就是裝箱。裝箱通過包裝類的valueOf方法實現(xiàn)。
2. 自動拆箱
將包裝類自動轉化成對應的基本數(shù)據類型(通過包裝類的xxValue方法)。
public static void main(String[] args) { int int1 = new Integer(100); long long1 = new Long(100); double double1 = new Double(100.0); }
實際等于
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. 自動裝箱
將基本數(shù)據類型自動轉化為對應的包裝類(通過包裝類的valueOf方法)。
public static void main(String[] args) { Integer int2 = 100; Long long2 = 100L; Double double2 = 100.0; }
實際等于
public static void main(String[] args) { Integer int2 = Integer.valueOf(100); Long long2 = Long.valueOf(100L); Double double2 = Double.valueOf(100.0); }
4. 自動拆裝箱使用場景
將基本類型放入集合類(自動裝箱)
集合類只支持包裝類型(不支持基本數(shù)據類型),但是我們add(基本數(shù)據類型)也不會報錯,是因為Java給我們做了自動裝箱。
實際等于
public static void main(String[] args) { List<Integer> list = new ArrayList<>(); list.add(Integer.valueOf(1)); list.add(Integer.valueOf(2)); }
包裝類型和基本類型比較大?。ㄗ詣硬鹣洌?/strong>
包裝類與基本數(shù)據類型進行比較運算,其實是先將包裝類進行拆箱成基本數(shù)據類型,然后比較。
public static void main(String[] args) { boolean b = new Integer(2) > 1; }
實際等于
public static void main(String[] args) { boolean b = new Integer(2).intValue() > 1; }
包裝類型的運算(自動拆箱)
運算中包含包裝類型時,會將包裝類型自動拆箱為基本類型再進行運算
public static void main(String[] args) { int a = new Integer(1) + 1; }
實際等于
public static void main(String[] args) { int a = new Integer(1).intValue() + 1; }
三目運算(自動拆箱)
value = flag ? value1 : value2
三目運算中,如果value1和value2中一個是基本數(shù)據類型、另一個是包裝類型。那么無論value是基本數(shù)據類型還是包裝類型,只要根據判斷結果從value1和value2選擇的那個值是包裝類型時,都會先把包裝類型拆箱為基本數(shù)據類型,再根據value的類型來決定是否再對值進行拆封箱處理。
public static void main(String[] args) { Integer a = null; int v1 = true ? 0 : a;// 正常 int v2 = false ? 0 : a;// 報錯 Integer v3 = true ? 0 : a;// 正常 Integer v4 = false ? 0 : a;// 報錯 }
實際等于
public static void main(String[] args) { Integer a = null; int v1 = true ? 0 : a;// 正常 int v2 = false ? 0 : a.intValue();// 報錯 Integer v3 = true ? 0 : a;// 正常 Integer v4 = false ? 0 : Integer.valueOf(a.intValue());// 報錯 }
方法的返回值(自動拆裝箱)
方法的返回值類型與實際返回值類型不同時,會自動拆裝箱,變成方法返回值相同的類型。
//自動拆箱 public int getNum1() { return new Integer(1); } //自動裝箱 public Integer getNum2() { return 1; }
實際等于
//自動拆箱 public int getNum1() { return new Integer(1).byteValue(); } //自動裝箱 public Integer getNum2() { return Integer.valueOf(1); }
5. 自動拆裝箱的觸發(fā)時機
在編譯期,Java文件編譯稱Class(字節(jié)碼)文件的過程中觸發(fā)自動拆裝箱的動作。
通過查看字節(jié)碼文件可以予以證明。兩種查看字節(jié)碼信息的方式:
1.通過javac和javap查看
先通過javac將.java代碼編譯成.class字節(jié)碼,然后通過javap分析字節(jié)碼。
javac:編譯成class(字節(jié)碼)
javac DemoTest.java
javap:分析字節(jié)碼
javap -verbose DemoTest.class
若出現(xiàn)編碼問題,可指定編碼
javac -encoding utf-8 DemoTest.java javap -encoding utf-8 -verbose DemoTest.class
2.IDEA使用ASM Bytecode Outline插件查看字節(jié)碼
類文件上右鍵->選擇: Show Bytecode outline
面板上的三個選項:
- Bytecode 表示對應的class字節(jié)碼文件
- ASMified 表示利用ASM框架生成字節(jié)碼對應的代碼
- Groovified 對應的是class字節(jié)碼指令
6. 自動拆裝箱帶來的問題
- 包裝對象之間的數(shù)值比較不能簡單的使用==,除了特殊有緩存的情況(如Integer的-128~127),其他比較都需要使用equals比較。
- 如果包裝類對象為NULL,那么自動拆箱就可能會拋出空指針異常
- 如果一個for循環(huán)中有大量拆裝箱操作,會浪費很多資源
基本數(shù)據類型的轉換
1. 容量大小排序
基本數(shù)據類型容量大小排序(不含布爾類型、容量由小到大):
- byte<short/char<int<long<float<double
- byte<<int<long<float<double
2. 轉換規(guī)則
- 8種基本數(shù)據類型中,除了布爾類型之外,其他的8中數(shù)據類型之間都可以相互轉換
- 任何浮點類型不管占用多少個字符,都比整數(shù)型容量大
- char和short可表示的種類數(shù)量相同,但是char可以取更大的整數(shù)
- 整數(shù)的默認類型為int,小數(shù)的默認類型為double
- 小容量向大容量轉換,稱為自動類型轉換(又稱隱式類型轉換),不會丟失精度
- 大容量轉換成小容量,叫做強制類型轉換(又稱顯式類型轉換),可能會會丟失精度,需要加強制類型轉換符。
- 當整數(shù)字面值沒有超出byte,short,char的取值范圍,可以直接賦值給byte,short,char類型的變量
- 多種數(shù)據類型混合運算,先轉換成容量大的那種類型再做運算(byte,short,char混合運算的時候,各自先轉換成int類型再做運算)
3. 自動類型轉換
小容量向大容量轉換,稱為自動類型轉換(又稱隱式類型轉換)。
規(guī)則
- 小容量的類型轉化為大容量的類型自動使用自動類型轉換
- 整數(shù)類型可以自動轉化為浮點類型,可能會產生舍入誤差
- 字符可以自動提升為整數(shù)
4. 強制類型轉換
大容量轉換成小容量,叫做強制類型轉換(又稱顯式類型轉換)。
強制類型轉換需要在要強制類型轉換的前面加上括號,然后在括號里面加上你要轉換的類型。
規(guī)則
- 大容量的類型轉化為大容量的類型必須使用強制類型轉換
- 強制類型轉換可能導致溢出或損失精度
- 浮點數(shù)到整數(shù)的轉換是通過舍棄小數(shù)得到,而不是四舍五入
- 不能把對象類型轉換為不相干的類型
包裝類的緩存
Java包裝類的緩存機制,是在Java 5中引入的一個有助于節(jié)省內存、提高性能的功能。
包裝類的緩存只在裝箱(通過包裝類的valueOf方法來實現(xiàn))時有效。
想必大家一定遇到過這樣的問題:兩個數(shù)值相等的包裝類型對象通過==進行比較時,得到的結果有時是true,有時又是false。
這可能就是包裝類的緩存在搗亂:當包裝類對象經歷過裝箱操作,得到的對象可能是緩存好的對象,也可能是新創(chuàng)建的對象。
1. 包裝類型的緩存值范圍
基本類型 | 大小(bit) | 默認值 | 取值范圍 | 包裝類 | 包裝類緩存范圍 |
---|---|---|---|---|---|
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(長整數(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 | 無 |
double(雙精度小數(shù)) | 64 | 0.0 | [-2^63,2^63-1] [1.797693e+308,4.9000000e-324] | Double | 無 |
boolean(布爾值) | 8 | false | true,false | Boolean | true,false |
boolean類型的底層是轉換為1,0存儲的,所以大小只有一個字節(jié)(8位)
- valueOf方法的邏輯是先從判斷值是否在緩存值范圍中。如果在,直接返回緩存中的對象;如果不在,創(chuàng)建新的對象。
- 在 jdk 1.8 所有的數(shù)值類緩沖池中,Integer 的緩沖池 IntegerCache 很特殊,這個緩沖池的下界是 - 128,上界默認是 127,但是這個上界是可調的。
- 在啟動 jvm 的時候,通過 -XX:AutoBoxCacheMax=來指定這個緩沖池的大小,該選項在 JVM 初始化的時候會設定一個名為 java.lang.IntegerCache.high 系統(tǒng)屬性,然后IntegerCache 初始化的時候就會讀取該系統(tǒng)屬性來決定上界。
總結
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
SpringBoot Shiro授權實現(xiàn)過程解析
這篇文章主要介紹了SpringBoot Shiro授權實現(xiàn)過程解析,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友可以參考下2019-11-11Springboot下RedisTemplate的兩種序列化方式實例詳解
這篇文章主要介紹了Springboot下RedisTemplate的兩種序列化方式,通過定義一個配置類,自定義RedisTemplate的序列化方式,結合實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2022-09-09