Java中自動裝箱、拆箱引起的耗時詳解
什么是自動裝箱,拆箱
先拋出定義,Java中基礎(chǔ)數(shù)據(jù)類型與它們的包裝類進行運算時,編譯器會自動幫我們進行轉(zhuǎn)換,轉(zhuǎn)換過程對程序員是透明的,這就是裝箱和拆箱,裝箱和拆箱可以讓我們的代碼更簡潔易懂
耗時問題
在說 Java 的自動裝箱和自動拆箱之前,我們先看一個例子。
這個錯誤我在項目中犯過(尷尬),拿出來共勉!
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,是可忍叔不可忍,再正常不過的代碼怎么會耗時這么久呢?如果在配置差一點的電腦上運行耗時會更久(驚呆了.jpg)。
我們不妨先閱讀下面的內(nèi)容,再來分析、解決上述耗時的問題。
基本概念
自從 jdk1.5 之后就有了自動裝箱(Autoboxing)和自動拆箱(AutoUnboxing)。
自動裝箱,就是 Java 自動將原始(基本)類型轉(zhuǎn)換成對應(yīng)的封裝器(對象)類型的過程,比如將 int 的變量轉(zhuǎn)換成 Integer 對象,這個過程叫做裝箱。
自動拆箱,就是 Java 自動將封裝器(對象)類型轉(zhuǎn)換成基本類型的過程,如將 Integer 對象轉(zhuǎn)換成 int 類型值,這個過程叫做拆箱。
之所以稱之為自動裝箱和拆箱,是因為這些操作并非人工(程序猿)操作的,而是 Java 自帶的一個特性。
下表是 Java 中的基本類型和對應(yīng)的封裝類型的對應(yīng)表:
| 基本類型 | 封裝器類 |
|---|---|
| int | Integer |
| byte | Byte |
| long | Long |
| float | float |
| double | Double |
| char | Character |
| boolean | Boolean |
自動裝箱示例:
int a = 3; Integer b = a;
自動拆箱示例:
Integer b = new Integer(7); int a = b;
Integer/int 自動拆箱和裝箱
下面這段代碼是 Integer 的源碼中 valueOf 方法。
/**
* 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則返回一個緩沖區(qū)中的一個Integer對象
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
// 否則返回 new 一個Integer 對象
return new Integer(i);
}
我們在執(zhí)行下面的這句代碼,如下:
Integer i = 100;
上面的代碼等同于下面的代碼:
Integer i = Integer.valueOf(100);
結(jié)合上面的源碼可以看出來,如果數(shù)值在 [-128,127] 之間(雙閉區(qū)間),不會重新創(chuàng)建 Integer 對象,而是從緩存中(常量池)直接獲取,從常量池中獲取而不是堆棧操作,讀取數(shù)據(jù)要快很多。
我們再來看一下常見的基礎(chǔ)面試題(請給出打印結(jié)果),如下:
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, 兩個對象進行比較分別指向了不同堆內(nèi)存
⓶: true, 自動裝箱且數(shù)值在 [-128,127] 之間(雙閉區(qū)間)
⓷: false, 自動裝箱且數(shù)值不在 [-128,127] 之間(雙閉區(qū)間)
⓸: true, 自動拆箱且數(shù)值在 [-128,127] 之間(雙閉區(qū)間)
解析耗時問題
類 Long 對應(yīng)的也有一個 valueof 方法,源碼如下:
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);
}
這個和 Integer 的很像,道理上面說過,這里不再贅述。
在開篇的例子中,getCounterResult 方法有下面這句代碼,如下:
Long sum = 0L;
很明顯我們聲明了一個 Long 的對象 sum,由于自動裝箱,這句代碼并沒有語法上面的錯誤,編譯器當然也不會報錯。上面代碼等同于如下代碼:
Long sum = Long.valueof(0);
在 for 循環(huán)中,超過 [-128,127] 就會創(chuàng)建新的對象,這樣不斷的創(chuàng)建對象,不停的申請堆內(nèi)存,程序執(zhí)行自然也就比較耗時了。
修改一下代碼,如下:
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í)行時間大大縮短。
總結(jié)
以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學習或者工作具有一定的參考學習價值,謝謝大家對腳本之家的支持。
相關(guān)文章
java調(diào)用webservice接口,并解析返回參數(shù)問題
這篇文章主要介紹了java調(diào)用webservice接口,并解析返回參數(shù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-07-07
java使用Apache工具集實現(xiàn)ftp文件傳輸代碼詳解
這篇文章主要介紹了java使用Apache工具集實現(xiàn)ftp文件傳輸代碼詳解,分享了詳細連接ftp server和上傳文件,下載文件的代碼,以及結(jié)果展示,具有一定借鑒價值,需要的朋友可以參考下。2017-12-12
SpringBoot整合MybatisPlus實現(xiàn)增刪改查功能
MybatisPlus是國產(chǎn)的第三方插件,?它封裝了許多常用的CURDapi,免去了我們寫mapper.xml的重復勞動。本文將整合MybatisPlus實現(xiàn)增刪改查功能,感興趣的可以了解一下2022-05-05
關(guān)于Spring中一級緩存、二級緩存和三級緩存的那些事
Spring解決循環(huán)依賴的核心思想在于提前曝,下面這篇文章主要給大家介紹了關(guān)于Spring中一級緩存、二級緩存和三級緩存的那些事,文中通過實例代碼介紹的非常詳細,需要的朋友可以參考下2022-02-02
數(shù)組實現(xiàn)Java 自定義Queue隊列及應(yīng)用操作
這篇文章主要介紹了數(shù)組實現(xiàn)Java 自定義Queue隊列及應(yīng)用操作,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-06-06
Struts 2 數(shù)據(jù)校驗功能及校驗問題的解決方案
這篇文章主要介紹了Struts 2 數(shù)據(jù)校驗功能及校驗問題的解決方案的相關(guān)資料,需要的朋友可以參考下2016-09-09

