什么是自動(dòng)裝箱,拆箱
先拋出定義,java中基礎(chǔ)數(shù)據(jù)類型與它們的包裝類進(jìn)行運(yùn)算時(shí),編譯器會(huì)自動(dòng)幫我們進(jìn)行轉(zhuǎn)換,轉(zhuǎn)換過(guò)程對(duì)程序員是透明的,這就是裝箱和拆箱,裝箱和拆箱可以讓我們的代碼更簡(jiǎn)潔易懂
耗時(shí)問(wèn)題
在說(shuō) java 的自動(dòng)裝箱和自動(dòng)拆箱之前,我們先看一個(gè)例子。
這個(gè)錯(cuò)誤我在項(xiàng)目中犯過(guò)(尷尬),拿出來(lái)共勉!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
private static long getcounterresult() { long sum = 0l; final int length = integer.max_value; for ( int i = 0 ; i < length; i++) { sum += i; } return sum; } public static void main(string[] args) { long startcounttime = system.currenttimemillis(); long result = getcounterresult(); long endcounttime = system.currenttimemillis(); system.out.println( "result = " + result + ", and take up time : " + (endcounttime - startcounttime) / 1000 + "s" ); } |
在我的電腦(macos 64位系統(tǒng),配置較高),打印結(jié)果如下:
result = 2305843005992468481, and take up time : 12s
居然使用了 12s,是可忍叔不可忍,再正常不過(guò)的代碼怎么會(huì)耗時(shí)這么久呢?如果在配置差一點(diǎn)的電腦上運(yùn)行耗時(shí)會(huì)更久(驚呆了.jpg)。
我們不妨先閱讀下面的內(nèi)容,再來(lái)分析、解決上述耗時(shí)的問(wèn)題。
基本概念
自從 jdk1.5 之后就有了自動(dòng)裝箱(autoboxing)和自動(dòng)拆箱(autounboxing)。
自動(dòng)裝箱,就是 java 自動(dòng)將原始(基本)類型轉(zhuǎn)換成對(duì)應(yīng)的封裝器(對(duì)象)類型的過(guò)程,比如將 int 的變量轉(zhuǎn)換成 integer 對(duì)象,這個(gè)過(guò)程叫做裝箱。
自動(dòng)拆箱,就是 java 自動(dòng)將封裝器(對(duì)象)類型轉(zhuǎn)換成基本類型的過(guò)程,如將 integer 對(duì)象轉(zhuǎn)換成 int 類型值,這個(gè)過(guò)程叫做拆箱。
之所以稱之為自動(dòng)裝箱和拆箱,是因?yàn)檫@些操作并非人工(程序猿)操作的,而是 java 自帶的一個(gè)特性。
下表是 java 中的基本類型和對(duì)應(yīng)的封裝類型的對(duì)應(yīng)表:
基本類型 | 封裝器類 |
---|---|
int | integer |
byte | byte |
long | long |
float | float |
double | double |
char | character |
boolean | boolean |
自動(dòng)裝箱示例:
1
2
|
int a = 3 ; integer b = a; |
自動(dòng)拆箱示例:
1
2
|
integer b = new integer( 7 ); int a = b; |
integer/int 自動(dòng)拆箱和裝箱
下面這段代碼是 integer 的源碼中 valueof 方法。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/** * returns an {@code integer} instance representing the specified * {@code int} value. if a new {@code integer} instance is not * required, this method should generally be used in preference to * the constructor {@link #integer(int)}, as this method is likely * to yield significantly better space and time performance by * caching frequently requested values. * * this method will always cache values in the range -128 to 127, * inclusive, and may cache other values outside of this range. * * @param i an {@code int} value. * @return an {@code integer} instance representing {@code i}. * @since 1.5 */ public static integer valueof( int i) { // 如果i的值大于-128小于127則返回一個(gè)緩沖區(qū)中的一個(gè)integer對(duì)象 if (i >= integercache.low && i <= integercache.high) return integercache.cache[i + (-integercache.low)]; // 否則返回 new 一個(gè)integer 對(duì)象 return new integer(i); } |
我們?cè)趫?zhí)行下面的這句代碼,如下:
1
|
integer i = 100 ; |
上面的代碼等同于下面的代碼:
1
|
integer i = integer.valueof( 100 ); |
結(jié)合上面的源碼可以看出來(lái),如果數(shù)值在 [-128,127] 之間(雙閉區(qū)間),不會(huì)重新創(chuàng)建 integer 對(duì)象,而是從緩存中(常量池)直接獲取,從常量池中獲取而不是堆棧操作,讀取數(shù)據(jù)要快很多。
我們?cè)賮?lái)看一下常見(jiàn)的基礎(chǔ)面試題(請(qǐng)給出打印結(jié)果),如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
public static void main(string[] args) { // ? integer a = new integer( 121 ); integer b = new integer( 121 ); system.out.println(a == b); // ? integer c = 121 ; integer d = 121 ; system.out.println(c == d); // ? integer e = 129 ; integer f = 129 ; system.out.println(e == f); // ? int g = 50 ; integer h = new integer( 50 ); system.out.println(g == h); } |
分析結(jié)果:
?: false, 兩個(gè)對(duì)象進(jìn)行比較分別指向了不同堆內(nèi)存
?: true, 自動(dòng)裝箱且數(shù)值在 [-128,127] 之間(雙閉區(qū)間)
?: false, 自動(dòng)裝箱且數(shù)值不在 [-128,127] 之間(雙閉區(qū)間)
?: true, 自動(dòng)拆箱且數(shù)值在 [-128,127] 之間(雙閉區(qū)間)
解析耗時(shí)問(wèn)題
類 long 對(duì)應(yīng)的也有一個(gè) valueof 方法,源碼如下:
1
2
3
4
5
6
7
|
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); } |
這個(gè)和 integer 的很像,道理上面說(shuō)過(guò),這里不再贅述。
在開(kāi)篇的例子中,getcounterresult 方法有下面這句代碼,如下:
1
|
long sum = 0l; |
很明顯我們聲明了一個(gè) long 的對(duì)象 sum,由于自動(dòng)裝箱,這句代碼并沒(méi)有語(yǔ)法上面的錯(cuò)誤,編譯器當(dāng)然也不會(huì)報(bào)錯(cuò)。上面代碼等同于如下代碼:
1
|
long sum = long .valueof( 0 ); |
在 for 循環(huán)中,超過(guò) [-128,127] 就會(huì)創(chuàng)建新的對(duì)象,這樣不斷的創(chuàng)建對(duì)象,不停的申請(qǐng)堆內(nèi)存,程序執(zhí)行自然也就比較耗時(shí)了。
修改一下代碼,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
private static long getcounterresult() { // 修改為普通的基本類型數(shù)據(jù) long sum = 0l; final int length = integer.max_value; for ( int i = 0 ; i < length; i++) { sum += i; } return sum; } public static void main(string[] args) { long startcounttime = system.currenttimemillis(); long result = getcounterresult(); long endcounttime = system.currenttimemillis(); system.out.println( "result = " + result + ", and take up time : " + (endcounttime - startcounttime) / 1000 + "s" ); } |
執(zhí)行時(shí)間大大縮短。
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,謝謝大家對(duì)服務(wù)器之家的支持。
原文鏈接:http://www.veryitman.com/2019/04/07/Java-自動(dòng)裝箱、拆箱引起的耗時(shí)/