探究Java中Integer緩沖區(qū)底層原理
一. Integer底層原理探究
1. int和Integer的區(qū)別
除了要掌握Integer的用法之外,我們還要了解它的一些底層內容,因為在面試時,關于Integer的底層考察的比較多。比如一個常見的面試題是這樣的:請問int和Integer的區(qū)別有哪些?
面對這樣的一道題目,你該怎么回答?常規(guī)的答案其實很容易答出來,比如:
- int是基本數(shù)據(jù)類型,代表整型數(shù)據(jù),默認值是0;
- Integer是 int的包裝類,屬于引用類型,默認值為 null ;
- int 和 Integer 都可以表示某一個整型數(shù)值;
- Integer變量實際是對象的引用,當new一個Integer時,實際上是生成一個指針指向此對象;而int則是直接存儲數(shù)據(jù)值;
- Integer可以區(qū)分出未賦值和值為 0 的區(qū)別,而int 則無法表達出未賦值的情況;
- int 和 Integer 不能夠互用,因為他們是兩種不同的數(shù)據(jù)類型;
- int在初始化時,可以直接寫成 int=1 的形式;
- 因為Integer是包裝類型,使用時可以采用
Integer i = new Integer(1)
的形式,但因為Java中的自動裝箱和拆箱機制,使得對Integer類的賦值也可以使用Integer i= 1
的形式;- 如果我們只是進行一些加減乘除的運算 或者 作為參數(shù)進行傳遞,那么就可以直接使用int這樣的基本數(shù)據(jù)類型;但如果想按照對象來進行操作處理,那么就要使用Integer來聲明一個對象。
但是如果你只能回答出這樣的答案,你在面試官的眼里只能算合格,還算不上優(yōu)秀,我們需要對Integer了解地更多一些。
2. 被final修飾的Integer類
為了搞清楚Integer的底層,我們就不得不研究一下它的源碼,我們來追蹤一下Integer源碼,如下圖所示:
從源碼中可以看出,Integer是Number的一個子類,且被final所修飾!請大家回顧一些之前講過的final知識點。
我們知道,被final修飾的類是常量類,該類不能被繼承,里面的方法不能被重寫,創(chuàng)建出的對象也不能被修改!總之,Integer符合final類的特征。
3. IntegerCache緩沖區(qū)
我們還記得,在Integer中有一個valueOf()方法,該方法可以將int值轉為Integer對象。接下來我們來看看該方法的實現(xiàn)源碼,如下圖所示:
從上圖的源碼截圖中我們可以看到,Integer中有一個緩沖區(qū)叫做IntegerCache,這是Integer中的一個內部類,如下圖所示:
我們可以看到,low就是-128,high等于127,這是緩沖區(qū)的最低和最高邊界。那么這個緩沖區(qū)的存在到底有什么用呢?大家別著急,我們先做幾個核心實驗。
4. 幾個核心實驗
為了能夠講清楚Integer的底層邏輯,小編給大家設計了如下代碼,用于驗證Integer的底層設計。
4.1 比較new出的兩個Integer對象
我們通過new對象的方式,來創(chuàng)建兩個Integer對象i和j,并比較這兩個對象。
//通過new生成的兩個Integer變量進行比較,結果為false Integer i = new Integer(100); Integer j = new Integer(100); System.out.print(i == j); //false
從運行的結果中可以看出,通過new生成的兩個Integer對象永遠是不會相等的。這是因為new生成的是兩個對象,Integer變量實際上是對Integer對象的引用,這兩個對象的內存地址是不同的。
4.2 Integer對象和int變量進行比較
接下來我們在把一個Integer對象和int變量進行比較,如下:
Integer i = new Integer(100); int j = 100; System.out.print(i == j); //true
Integer變量和int變量進行比較時,只要兩個變量的值是相等的,結果就為true。這是因為Integer包裝類和int基本類型進行比較時,Java會進行自動拆箱操作,將Integer轉為了int,然后再進行比較,實際上就變?yōu)榱藘蓚€int變量的比較。本案例中兩者的值都是100,所以用“==”等號進行比較時自然就是相等的。
4.3 非new的Integer變量和new出的Integer變量進行比較
然后我們再把一個非new的Integer變量和new出的Integer變量進行比較,如下所示:
//非new生成的Integer變量和new Integer()生成的變量進行比較 Integer i = new Integer(100); //自動裝箱 Integer j = 100; System.out.print(i == j); //false
在這段代碼中,非new生成的Integer變量和new Integer()生成的變量進行比較時,結果卻為false ! 這是因為非new生成Integer變量時,內部會調用valueOf()方法,進行自動裝箱操作,此時會把Integer變量的值指向Java常量池中的數(shù)據(jù)。而new Integer()生成的變量,則指向的是堆中新建的對象,兩者在內存中的地址是不同的。
4.4 兩個非new生成的Integer對象進行比較
接著我們再對兩個非new生成的Integer對象進行比較,如下所示:
//兩個非new生成的Integer對象進行比較 //i與j的取值范圍是在 -128~127 之間! Integer i = 100; Integer j = 100; System.out.print(i == j); //true //x與y的取值范圍不在 -128~127 之間! Integer x = 200; Integer y = 200; System.out.print(x == y); //false
這段代碼中,兩個非new生成的Integer對象進行比較時,如果兩個變量的取值在 -128到127 之間,則比較結果為true;如果兩個變量的值不在此區(qū)間,則比較結果為false。 這又是為什么呢? 其實要想弄明白這個原因,我們只需要看看Integer類的valueOf()方法是怎么寫的就可以了。valueOf()方法源碼如下:
public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); }
我們知道,valueOf(int i)方法可以將int值自動裝箱變成對應的Integer實例。 并且從這段源碼中我們可以看到其內部有一個if判斷,根據(jù)判斷結果的不同,會有2種不同的方式得到Integer對象:當arg大于等于-128且小于等于127時,則直接從緩存中返回一個已經存在的對象;如果參數(shù)的值不在這個范圍內,則new一個Integer對象返回, 即要么new Integer,要么從int常量池中獲取 !
之前我們構建Integer對象的傳統(tǒng)方式是直接 new 一個Integer對象,內部會調用構造器。但是根據(jù)實踐,我們發(fā)現(xiàn)大部分的數(shù)據(jù)操作都是集中在有限的、較小的數(shù)值范圍內。因而在JDK 1.5中,新增了一個靜態(tài)工廠方法valueOf(int i)。當我們進行Integer i=xxx 賦值操作時,Java內部會調用執(zhí)行這個valueOf()實現(xiàn)自動裝箱。而在調用valueOf()方法時,其內部會利用緩存機制,對取值在-128~127之間的int值進行緩存操作,這是在JDK 1.5 之后進行的一個可以明顯改善性能的提升 。 而按照Javadoc文檔,該緩存機制默認會緩存在 -128 到 127 之間的值,不在該區(qū)間的值并不會進行緩存。所以,給Integer i
賦值的大小不同,比較的結果也可能會不同。
4.5 ==和equals的區(qū)別
最后我們再做一個實驗,來看看==與equals比較兩個Integer對象時有什么不同。
Integer x = 127; Integer y = 127; Integer m = 100000; Integer n = 100000; System.out.println("x == y: " + (x==y)); // true System.out.println("m == n: " + (m==n)); // false System.out.println("x.equals(y): " + x.equals(y)); // true System.out.println("m.equals(n): " + m.equals(n)); // true
從該實驗中可以看出,==比較時,較小的兩個相同的Integer會返回true,較大的兩個相同的Integer會返回false。結合上面給大家的講解,你思考一下這是為什么?
5. 結論
通過以上的幾個核心實驗,可以給大家梳理出一個結論:
當我們利用”==“等號比較兩個Integer i 和 Integer j
的值時,如果取值范圍是在-128~127之間,兩個相同的Integer值會返回true;如果不在該區(qū)間,兩個相同的Integer值會返回false。這是因為Integer是final類,編譯器把Integer i = 100; 自動變?yōu)镮nteger i = Integer.valueOf(100);
。為了節(jié)省內存, Integer.valueOf()
對于較小的數(shù),始終會返回相同的實例對象,因此,==比較的結果就是true。
那么如果我們只是為了比較兩個Integer對象的值是否相等,而不是為了比較兩個對象的地址是否相同,在開發(fā)時請盡量使用equals()方法,而不是==!
并且我們現(xiàn)在還知道,在Java中有3種方式可以構造出一個Integer對象,代碼如下:
//方法1: Integer i = new Integer(100); //方法2: Integer i = Integer.valueOf(100); //方法3: Integer i = 100;
實際上,方法2和方法3的本質是一樣的,所以開發(fā)時為了簡潔,我們一般是通過方法3來得到一個Integer對象。但是盡量不要使用方法1來構建Integer對象,這是因為方法1總是會創(chuàng)建一個新的 Integer 實例,而方法2和方法3則會盡可能地返回緩存的實例對象,以節(jié)省內存。
所以最終關于”int和Integer的區(qū)別有哪些“這道面試題的答案,如果你想拿到高分,就需要把Integer的底層原理也回答出來才行!如果你可以把以上內容都回答清楚,我相信單憑這一道題目,就足以讓面試官對你刮目相看!
二. 結語
最后我再梳理一下該問題的回答要點:
先簡單回顧Java中的數(shù)據(jù)類型及取值范圍;
然后簡介基本類型與包裝類,最后還能說明為什么需要有包裝類;
接著說一下int與Integer的基本區(qū)別;
最后再說int與Integer的深入區(qū)別,即底層的源碼和原理。
如果你可以把我總結的這4點都能回答好,就這一個問題,面試官就會對你留下深刻的影響,他就會認為你的基礎知識足夠扎實,因為大多數(shù)人只會回答int和Integer的基本區(qū)別,很少有人去回答底層的內容!而通過這個問題,面試官也會了解到,你對Java的內存分配是很熟悉的!
以上就是探究Java中Integer緩沖區(qū)底層原理的詳細內容,更多關于Java Integer底層原理的資料請關注腳本之家其它相關文章!