Java面試題之包裝類快取機制
Java包裝類快取機制
引言
在我人生中第一次找工作時(大四實習),曾經出現了這樣一道筆試題,求其結果。
Integer a = 100;
Integer b = 100;
Integer c = new Integer(100);
Integer d = new Integer(100);
Integer e = 1000;
Integer f = 1000;
System.out.println(a==b);
System.out.println(a==c);
System.out.println(c==d);
System.out.println(e==f);
我記得我當時答案是:false,false,false,false。哈哈,全false,當時腦子想的是好像有個快取,valueOf相關,這兒沒有用,都是物件,全錯。
然後…………光榮OUT。
正確答案是:true,false,false,false
一言不合,先反編譯
Integer a = Integer.valueOf(100);
Integer b = Integer.valueOf(100);
Integer c = new Integer(100);
Integer d = new Integer(100);
Integer e = Integer.valueOf(1000);
Integer f = Integer.valueOf(1000);
System.out.println(a == b);
System.out.println(a == c);
System.out.println(c == d);
System.out.println(e == f);
對,你沒有看錯,Integer a = 100 ,變成了Integer a = Integer.valueOf(100)
這涉及到了Java中的裝箱轉換。
在《Java語言規範》第三版中5.1.7節有一下一段話。
裝箱轉換將把基本型別的值轉換為相應的引用型別的值
……
……
如果被裝箱的值是true、false, —個byte、一個在\u0000~\u007f之間的char。
或者一個在-128~127之間的int或short數字,設r1和是r2的任何兩個裝箱轉換的結果,則始終有r1=r2
話是這樣說,怎麼實現呢,我們通過Integer程式碼說明(我使用的是JDK9)
Integer快取機制實現
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
上面這段話的意思就是,如果傳入的值在[IntegerCache.low,IntegerCache.high]之間,則返回IntegerCache中的cache陣列中對應的值。(比如 i = 100,low = -128,high = 127,則返回cache[228])
那這個IntegerCache又是什麼東西呢,它是Integer中的一個私有靜態內部類
private static class IntegerCache {
static final int low = -128;//##cache的最低值為-128
static final int high;//##最大值未定義,此時預設為0
static final Integer cache[];//##定義一個Integer陣列,存放Integers
static {
// high value may be configured by property
int h = 127;
String integerCacheHighPropValue =
VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
i = Math.max(i, 127);//##獲取預設值和設定的值中的最大值
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);//##最小值-127不變,h就是i和Integer.MAX_VALUE-128中的最小值。總不能超過int能表示的數
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
}
}
high = h;
cache = new Integer[(high - low) + 1];//初始化陣列
int j = low;
for(int k = 0; k < cache.length; k++)//##遍歷賦值從-128,-127...-->cache.length
cache[k] = new Integer(j++);
// range [-128, 127] must be interned (JLS7 5.1.7)
//##根據Java語言規範 -128, 127之間必須被快取 當最大值小於127時程式將會異常終止java.lang.AssertError
assert IntegerCache.high >= 127;
}
private IntegerCache() {}
}
上述程式碼中//##是我所備註註釋。
設定IntegerCache.high
-Djava.lang.Integer.IntegerCache.high=xxx
xxx為你想要設定的數字大小 [-128,xxx]範圍之內將會被快取
比如:下圖所示 [-128,300]範圍之內將會被快取
包裝類快取機制原因
一個工程中,像這種數字物件,時不時就要建立一個,如果有大量物件時,有可能會造成記憶體溢位,我們把其中共同的部分抽象出來,如果有相同的業務請求,直接返回在記憶體中已有的物件,避免重新建立。
於是,我們的Java採用了享元模式來設計這些包裝類(String也是)。
享元模式(Flyweight Pattern)
主要用於減少建立物件的數量,以減少記憶體佔用和提高效能。這種型別的設計模式屬於結構型模式,它提供了減少物件數量從而改善應用所需的物件結構的方式。
引用自菜鳥教程,它對其進行了詳細的介紹。
其他包裝類快取機制原始碼
Boolean快取機制實現
public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
Byte快取機制實現
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));
}
}
public static Byte valueOf(byte b) {
final int offset = 128;
return ByteCache.cache[(int)b + offset];
}
Short快取機制實現
private static class ShortCache {
private ShortCache(){}
static final Short cache[] = new Short[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Short((short)(i - 128));
}
}
public static Short valueOf(short s) {
final int offset = 128;
int sAsInt = s;//##先轉為int
if (sAsInt >= -128 && sAsInt <= 127) { // must cache
return ShortCache.cache[sAsInt + offset];
}
return new Short(s);
}
Long快取機制實現
private static class LongCache {
private LongCache(){}
static final Long cache[] = new Long[-(-128) + 127 + 1];
static {
for(int i = 0; i < cache.length; i++)
cache[i] = new Long(i - 128);
}
}
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);
}
Float和Double未使用快取
public static Float valueOf(float f) {
return new Float(f);
}
public static Double valueOf(double d) {
return new Double(d);
}
既然說了自動裝箱,就可以提一下自動拆箱
#自動拆箱
《Java語言規範》第三版中5.1.8節
拆箱轉換把引用型別的值轉換成相應的基本型別的值。
Integer a = 100;
int b = a;
反編譯之後。
Integer a = Integer.valueOf(100);;
int b = a.intValue();
其他包裝類一樣,都是通過intValue()獲取被包裝的值。
#總結
可以看的出,這些包裝類中(除了浮點數和雙精度數)都使用了快取機制,他確確實實減少建立物件的數量,減少了記憶體的佔用和提高了效能。但是開發者有時不會注意到這些細節,可能會引起一些程式邏輯上的問題,所以一定要注意這些使用了快取的包裝類之間的對比。
相關文章
- 再學Java 之 Integer 包裝類快取Java快取
- 【Java面試題】之類載入:從面試題分析Java類載入機制Java面試題
- Java面試題之Java類載入機制詳解!Java面試題
- Java類載入機制詳解【java面試題】Java面試題
- JVM面試問題系列:Java類載入機制之雙親委派模型JVM面試Java模型
- [js]一道快取類面試題JS快取面試題
- 包裝類的定義,API的使用和快取問題API快取
- 簡事二三 之 http快取機制HTTP快取
- Java坑人面試題系列: 包裝類(中級難度)Java面試題
- 【Java】基本資料型別包裝類面試題之一Java資料型別面試題
- 執行緒同步機制-包裝類執行緒
- 阿里面試題,深入理解Java類載入機制阿里面試題Java
- 兩道面試題帶你解析 Java 類載入機制面試題Java
- Web 快取機制 與 快取策略Web快取
- HTTP快取機制HTTP快取
- Mybatis快取機制MyBatis快取
- LRU快取機制快取
- 前端快取機制前端快取
- web快取機制Web快取
- client快取機制client快取
- 建立快取記憶體機制-java版快取記憶體Java
- Java 日誌快取機制的實現Java快取
- [玩轉MySQL之四]MySQL快取機制MySql快取
- Volley 原始碼解析之快取機制原始碼快取
- MyBatis快取機制(一級快取,二級快取)MyBatis快取
- java學習之基本包裝類Java
- 常見面試題之作業系統中的LRU快取機制實現面試題作業系統快取
- mybatis的快取機制MyBatis快取
- Redis 快取失效機制Redis快取
- mysql的快取機制MySql快取
- MyBatis 的快取機制MyBatis快取
- Java中常用快取Cache機制的實現Java快取
- Java 包裝類Java
- 從WebView快取聊到Http 的快取機制WebView快取HTTP
- 聊一聊Integer的快取機制問題快取
- Java 異常 隨機數 包裝類Java隨機
- Java 技術之類載入機制Java
- [JAVA] Java物件導向之包裝類,拆箱、裝箱Java物件