常量缓存与integer比较_Integer缓存范围到底是多少?
本文主要大致思路為:
不管從工作中還是面試,這篇文章都應該好好看完,本人認為是非常有用的。
案例
Integer是基本類型int的封裝類。平時不管是入坑多年的小伙伴還在入坑路上的小伙伴,都應該知道的使用頻率是相當高。
下面模仿訂單支付,做了一個訂單支付狀態枚舉類PayStatusEnum
public class IntegerDemo {
public static void main(String[] args) {
Integer a = new Integer(8);
Integer b = Integer.valueOf(8);
Integer c = 8;
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(b.equals(c));
System.out.println(a == b);
System.out.println(a == c);
System.out.println(b == c);
}
}
結果輸出什么?
把上面代碼中的8改成128后,又輸出什么?
public class IntegerDemo {
public static void main(String[] args) {
Integer a = new Integer(128);
Integer b = Integer.valueOf(128);
Integer c = 128;
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(b.equals(c));
System.out.println(a == b);
System.out.println(a == c);
System.out.println(b == c);
}
}
答案慢慢道來。
解析案例
Integer整體閱覽
構造方法
private final int value;
public Integer(int value) {
this.value = value;
}
太簡單了,沒什么可講的。
valueOf()方法
public static Integer valueOf(String s) throws NumberFormatException {
return Integer.valueOf(parseInt(s, 10));
}
//@HotSpotIntrinsicCandidate 這個注解是JDK9才引入的
//HotSpot 虛擬機將對標注了@HotSpotIntrinsicCandidate注解的方法的調用,
//替換為直接使用基于特定 CPU 指令的高效實現。這些方法我們便稱之為 intrinsic。
public static Integer valueOf(int i) {
//如果i在low和high之間就使用緩存
if (i >= IntegerCache.low && i <= IntegerCache.high){
return IntegerCache.cache[i + (-IntegerCache.low)];
}
return new Integer(i);
}
上面valueOf()方法中用到了IntegerCache,下面我們來聊聊。
IntegerCache
下面是IntegerCache源碼和部分注釋:
/**
* Cache to support the object identity semantics of autoboxing for values between
* -128 and 127 (inclusive) as required by JLS.
* JLS協議要求緩存在-128到127之間(包含邊界值)
*
* The cache is initialized on first usage.
* 程序第一次使用Integer的時候
* The size of the cache may be controlled by the {@code -XX:AutoBoxCacheMax=} option.
* JVM 的啟動參數 -XX:AutoBoxCacheMax=size 修改最大值
* During VM initialization, java.lang.Integer.IntegerCache.high property
* may be set and saved in the private system properties in the
* sun.misc.VM class.
* 可以通過系統屬性來獲得:-Djava.lang.Integer.IntegerCache.high=
*/
private static class IntegerCache {
static final int low = -128;
static final int high;
//使用數組來緩存常量池
static final Integer cache[];
static {
// high value may be configured by property
//最大值是可以配置的
int h = 127;
String integerCacheHighPropValue =
sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
//如果有配置-XX:AutoBoxCacheMax=
if (integerCacheHighPropValue != null) {
try {
int i = parseInt(integerCacheHighPropValue);
//和127進行比較,誰大用誰
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
//再比較,獲取最小值
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
} catch( NumberFormatException nfe) {
// If the property cannot be parsed into an int, ignore it.
// 如果該值配置錯誤則忽略該參數配置的值,使用默認范圍-128~127
}
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
// 緩存通過for循環來實現,創建范圍內的整數對象并存儲到cache數組中
// 程序第一次使用Integer的時候需要一定的額外時間來初始化該緩存
for(int k = 0; k < cache.length; k++){
cache[k] = new Integer(j++);
}
//無論如何,緩存的最大值肯定是大于等于127
assert IntegerCache.high >= 127;
}
//私有的構造方法,因為所有的屬性均屬于類常量
private IntegerCache() {}
}
整個靜態塊流程:
那么,如何設置java.lang.Integer.IntegerCache.high的值呢?
The size of the cache may be controlled by the {@code -XX:AutoBoxCacheMax=} option.
注釋中已經說清楚,可以使用-XX:AutoBoxCacheMax=設置。
寫個demo來debug看看
public class IntegerDemo {
public static void main(String[] args) {
Integer a = 8;
Integer b =Integer.valueOf(8);
System.out.println(a.equals(b));
System.out.println(a == b);
}
}
設置-XX:AutoBoxCacheMax=100
開始debug
看看high的值
是127,那就對了,因為上面
設置-XX:AutoBoxCacheMax=130
開啟debug模式
注意:low=-128是不會變的,整個緩存初始化過程并沒有對low進行修改,再說low是常量。
-XX:AutoBoxCacheMax最大能設置成多大?
因為Integer的最大值是2147483647 ,所以我們這里使用這個值試試,
開始debug,直接報OOM了
為什么會OOM呢?
如果-XX:AutoBoxCacheMax沒有設置值,那么對應數組是這樣的。
equals()方法
上面的案例中有equals方法,這里把這個方法也拿出來聊聊
private final int value;
public boolean equals(Object obj) {
if (obj instanceof Integer) {
//這里比較的是兩個int類型的值
return value == ((Integer)obj).intValue();
}
//obj不是Integer類型直接返回false
return false;
}
回到上面的案例中
當我們使用equals方法比較兩個對象是否相等的時候其實就是比較他們的value值。
所以不管是128還是8,equals后肯定都是true。
當引用類型使用==進行比較的時候,此時比較的是兩個引用的對象的地址,是不是同一個。
public class IntegerDemo {
public static void main(String[] args) {
Integer a = new Integer(8);
Integer b = Integer.valueOf(8);
Integer c = 8;
System.out.println(a.equals(b));
System.out.println(a.equals(c));
System.out.println(b.equals(c));
System.out.println(a == b);
System.out.println(a == c);
System.out.println(b == c);
}
}
我們看看器class文件中的字節碼;
本地變量表
每個本地變量賦值的過程
這里我們可以得出一個結論:
Integer c =8;就是Integer c = Integer.valueOf(8);
上面Integer b = Integer.valueOf(8);,那就說明變量b和c都是使用Integer.valueOf()獲取到的。
valueOf方法中
-XX:AutoBoxCacheMax不設置
上面關于IntegerCache的low和high已經進行了說明,low永遠是-128,所以當我們沒有設置
-XX:AutoBoxCacheMax 的值的時候,這時候 high=127。
當Integer.valueOf(8);的時候,就會進入上面代碼中的if中。然后從IntegerCache中的數組cache中獲取。
但是IntegerCache中的cache數組是個常量數組。
言外之意,就是一旦給這個數組cache賦值后,就不會變了。
Integer b = Integer.valueOf(8);和Integer c=8;
b和c不就是都指向同一個引用地址嗎?
所以 b==c為true;
但是Integer b=Integer.valueOf(128);
此時就不進入if中,而是直接new一個Integer對象
所以此時
Integer b=Innteger.valueOf(128) ;和Integer c = 128;
都會各自new 一個Integer對象,
此時的b==c為false。
這里也就是網上很多文章也就說到這里,就是比較當前int i 這個i是不是在-128到127范圍之內。
-XX:AutoBoxCacheMax設置為130
如果我們把-XX:AutoBoxCacheMax設置為130。那么上面
Integer b=Innteger.valueOf(128) ;和Integer c = 128;
也會進入if條件中。
最后b==c為true。
如何避坑
Integer是基本類型int的封裝類,那么在平常使用的時候需要注意幾點:
1,如果使用Integer,注意Integer的默認是null,容易引起空指針異常NullPointerException。
2,如果使用int類型,注意int類型的初始值是0,很多設計某某狀態時,很喜歡用0作為某個狀態,這里要小心使用。
3,另外從內存使用層面來講,int是基本數據類型,只占用4個字節,Integer是一個對象,當表示一個值時Integer占用的內存空間要高于int類型,從節省內存空間考慮,建議使用int類型(建議了解一下Java對象內存布局)。
4,Integer使用的時候,直接賦值,Integer c = 8,不要new Integer(8)。因為直接賦值就是Integer.valueOf方法使用緩存,沒必要每次都new一個新對象,有效提高內存使用。
5,如果系統中大量重復的使用比127大的數,建議JVM啟動的時候為-XX:AutoBoxCacheMax=size 適當的大小,提升內存使用效率(但是也不能太大,上面我們已經演示了可能出現OOM)。
面試題
面試題1
Integer num1 = new Integer(10);
Integer num2 = new Integer(10);
System.out.println(num1.equals(num2));
System.out.println(num1 == num2);
面試題2
Integer num3 = 100;
Integer num4 = 100;
System.out.println(num3.equals(num4));
System.out.println(num3 == num4);
面試題3
Integer num5 = 1000;
Integer num6 = 1000;
System.out.println(num5.equals(num6));
System.out.println(num5 == num6);
把上面看完了,在回頭來看看這種面試題,還難嗎?
如果在面試中遇到面試題3,可以適當反問一下面試官是否有對緩存范圍進行調整,或許某些面試官都會懵逼。
赤裸裸的吊打面試官。
總結
1.引用類型的==是比較對應的引用地址。
2.Integer中使用的默認緩存是-128到127。但是可以在JVM啟動的時候進行設置。
3.Integer b=Integer.valueOf(8);和Integer b=8;是一樣的效果。
4.Integer.valueOf()方式,比較是否在緩存范圍之內,在就直接從緩存中獲取,不在new一個Integer對象。
5.每次使用new來創建Integer對象,是用不到IntegerCache緩存的。
6.Integer中的使用緩存的方式也可以理解為享元模式。
歡迎關注我的公眾號:java后端技術全棧
專業做Java相關技術分享的的公眾號。一個純粹的Java技術分享達人。
總結
以上是生活随笔為你收集整理的常量缓存与integer比较_Integer缓存范围到底是多少?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: MySQL8怎么设置时区为东八区_mys
- 下一篇: Embedding改进CBOW