Java Integer常量池
在Java中我們知道String有一個常量池,維護了所有的String對象。我們寫String temp="test"的時候其實是使用String.valueOf("test")從常量池中找了一個對象返回,但是如果使用String temp=new String("temp")的話就不會從常量池中去找,而是直接創建一個新的對象。
其實對於Integer來說也有這樣一個常量池存在,並且我們都知道String常量池自從HotSpot1.8之后就從方法區移動到堆中了,而Integer常量池在哪里基本上都沒有人提到,我個人比較相信它也是在堆中的。之所以討論的少可能是因為它相對不太重要吧
https://blog.csdn.net/womenghuangliang/article/details/17377285
Integer i = 10 和 Integer j = new Integer(10) 的區別
public class IntegerClassDemo { public static void main(String[] javalatte) { Integer i = 10; Integer j = new Integer(10); Integer k = 145; Integer p = new Integer(145); } }
在編譯了上面這個類之后,我嘗試着反編譯這個類的 Class 文件。我得到了下面這個類
public class IntegerClassDemo { public static void main(String[] javalatte) { Integer i = Integer.valueOf(10); Integer j = new Integer(10); Integer k = Integer.valueOf(145); Integer p = new Integer(145); } }
所以當我們用 Integer i = 10 的方式創建一個 Integer 類時,Java 調用了方法 Integer.valueOf()。
Integer.valueOf()
讓我們來看看這個函數做了什么。JDK 文檔是這樣說的:
public static Integer valueOf(int i)
返回一個表示指定的 int 值的 Integer 實例。如果不需要新的 Integer 實例,則通常應優先使用該方法,而不是構造方法 Integer(int),因為該方法有可能通過緩存經常請求的值而顯著提高空間和時間性能。
這個方法總是緩存了 - 128 到 127 之間的值,還有,它也可以緩存這個范圍之外的值。
為了說明這個問題,讓我們來看看 Integer.valueOf() 的源代碼。
public static Integer valueOf( int i) { assert IntegerCache. high >= 127; if (i >= IntegerCache. low && i <= IntegerCache. high ) return IntegerCache. cache[i + (-IntegerCache. low)]; return new Integer(i); }
通過上面的代碼,我們可以很清楚的看到,當我們在 - 128 到 127 這個范圍內調用 valueof() 這個函數時,實際上使用的是緩存(IntegerCache)里面的值。
IntegerCache 是幫助我們緩存 Integer 的類。緩存數值的大小是由 - XX:AutoBoxCacheMax=
來控制,或者可以通過系統屬性來獲得:-Djava.lang.Integer.IntegerCache.high= 所以如果值在 - 128 到 127 之間,你使用 valueof() 將獲得相同的引用,超過這個范圍將返回 new Integer(int)。因為是相同的引用,在 - 128 到 127 范圍內你的 == 操作是有用的。
Java 緩存了 - 128 到 127 范圍內的 Integer 類。所以當你給這個范圍的封裝類賦值時,裝箱操作會調用 Integer.valueOf() 函數,反過來就會給封裝類賦上一個緩存池實例的引用。
另一方面來說,如果用超出這個范圍的值給封裝類賦值時,Integer.valueOf() 會創建一個新的 Integer 類。因此,比對超出這個范圍的 Integer 類時,會返回 false。
也就是說,Integer常量池一定緩存了-128到127之間的對象,因為這是一個字節表示的范圍內的數,也是最常用的數,緩存這些就夠了,過多的話會導致內存占用過大且沒什么用。而要使用緩存,就不能使用new Integer()的方式,它是不走常量池的。
至於為什么只緩存-128到127而字符串就是全部緩存,我感覺原因是字符串的范圍比Integer小:我們可能使用任何值的Integer對象,但是只會使用我們創建過的字符串對象,所以一個程序中字符串的范圍是比整數小的多的,而是復用的概率也高。
而對於包裝類,我們要注意==和equals的區別:
@Test public void testEquals() { int int1 = 12; int int2 = 12; Integer integer1 = new Integer(12); Integer integer2 = new Integer(12); Integer integer3 = new Integer(127); Integer a1 = 127; Integer a2 = 127; Integer a = 128; Integer b = 128; System.out.println("int1 == int2 -> " + (int1 == int2)); System.out.println("int1 == integer1 -> " + (int1 == integer1)); System.out.println("integer1 == integer2 -> " + (integer1 == integer2)); System.out.println("integer3 == a1 -> " + (integer3 == a1)); System.out.println("a1 == a2 -> " + (a1 == a2)); System.out.println("a == b -> " + (a == b)); }
答案是: 1、 int1 == int2 -> true 2、 int1 == integer1 -> true 3、 integer1 == integer2 -> false 4、 integer3 == a1 -> false 5、 a1 == a2 -> true 6、 a == b -> false
- int和int用==是比值
- Integer和int用==是比值
- Integer和Integer用==是比引用
我個人建議如果要使用包裝類Integer的話還是不要冒險,即使知道有常量池的存在也在比較中只使用equals(),否則容易出Bug