最近在項(xiàng)目中遇到一個問題,兩個值相同的Integer型值進(jìn)行==比較時氛悬,發(fā)現(xiàn)Integer其中的一些奧秘,順便也復(fù)習(xí)一下==和equals的區(qū)別,先通過Damo代碼解釋如下:
System.out.println("<-128~127以內(nèi)的Integer值俄占,Integer x = value;的方式賦值!>");Integer i = 127;Integer j = 127;System.out.println("i=" + i + ",j =" + j);System.out.println("i == j:" + (i == j) + "<--比較-->i.equals(j):"+ i.equals(j));System.out.println("<-128~127以外的Integer值淆衷,Integer x = value;的方式賦值缸榄!>");Integer m = 128;Integer n = 128;System.out.println("m=" + m + ",n =" + n);System.out.println("m == n:" + (m == n) + "<--比較-->m.equals(n):"+ m.equals(n));System.out.println(); System.out.println("<任意Integer值,Integer x = new Integer(value);的方式賦值祝拯!>");Integer x = new Integer(299);Integer y = new Integer(299);System.out.println("x=" + x + ",y =" + y);System.out.println("x == y:" + (x == y) + "<--比較-->x.equals(y):"+ x.equals(y));
輸出結(jié)果為:
<-128~127以內(nèi)的Integer值甚带,Integer x = value;的方式賦值!>i=127,j =127i == j:true<--比較-->i.equals(j):true<-128~127以外的Integer值鹿驼,Integer x = value;的方式賦值欲低!>m=128,n =128m == n:false<--比較-->m.equals(n):true<任意Integer值,Integer x = new Integer(value);的方式賦值畜晰!>x=299,y =299x == y:false<--比較-->x.equals(y):true
通過以上代碼及輸出結(jié)果砾莱,想必大家已經(jīng)看出其中奧秘!先總結(jié)如下:
1凄鼻、以上代碼第一段和第二段旨在說明:在-128~127的Integer值并且以Integer x = value;的方式賦值的Integer值在進(jìn)行==和equals比較時腊瑟,都會返回true聚假,因?yàn)镴ava里面對處在在-128127之間的Integer值,用的是原生數(shù)據(jù)類型int闰非,會在內(nèi)存里供重用膘格,也就是說這之間的Integer值進(jìn)行==比較時只是進(jìn)行int原生數(shù)據(jù)類型的數(shù)值比較,而超出-128127的范圍财松,進(jìn)行==比較時是進(jìn)行地址及數(shù)值比較瘪贱。
2、第三段旨在說明:==和equals的區(qū)別辆毡,==是進(jìn)行地址及值比較菜秦,無法對==操作符進(jìn)行重載,而對于equals方法舶掖,Integer里面的equals方法重寫了Object的equals方法球昨,查看Integer源碼可以看出equals方法進(jìn)行的是數(shù)值比較。
續(xù)詳解:
首先看一段代碼(使用JDK 5)眨攘,如下:
public class Hello
{
public static void main(String[] args)
{
int a = 1000, b = 1000;
System.out.println(a == b);
Integer c = 1000, d = 1000;
System.out.println(c == d);
Integer e = 100, f = 100;
System.out.println(e == f);
}
}
輸出結(jié)果:
true
false
true
The Java Language Specification, 3rd Edition 寫道: view plaincopy
為了節(jié)省內(nèi)存主慰,對于下列包裝對象的兩個實(shí)例,當(dāng)它們的基本值相同時鲫售,他們總是==:
Boolean
Byte
Character, \u0000 - \u007f(7f是十進(jìn)制的127)
Integer, -128 — 127
查看jdk源碼共螺,如下:
[java] view plaincopy
/**
- Cache to support the object identity semantics of autoboxing for values between
- -128 and 127 (inclusive) as required by JLS.
- The cache is initialized on first usage. During VM initialization the
- getAndRemoveCacheProperties method may be used to get and remove any system
- properites that configure the cache size. At this time, the size of the
- cache may be controlled by the vm option -XX:AutoBoxCacheMax=<size>.
*/
// value of java.lang.Integer.IntegerCache.high property (obtained during VM init)
private static String integerCacheHighPropValue;
static void getAndRemoveCacheProperties() {
if (!sun.misc.VM.isBooted()) {
Properties props = System.getProperties();
integerCacheHighPropValue =
(String)props.remove("java.lang.Integer.IntegerCache.high");
if (integerCacheHighPropValue != null)
System.setProperties(props); // remove from system props
}
}
private static class IntegerCache {
static final int high;
static final Integer cache[];
static {
final int low = -128;
// high value may be configured by property
int h = 127;
if (integerCacheHighPropValue != null) {
// Use Long.decode here to avoid invoking methods that
// require Integer's autoboxing cache to be initialized
int i = Long.decode(integerCacheHighPropValue).intValue();
i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - -low);
}
high = h;
cache = new Integer[(high - low) + 1];
int j = low;
for(int k = 0; k < cache.length; k++) //緩存區(qū)間數(shù)據(jù)
cache[k] = new Integer(j++);
}
private IntegerCache() {}
}
/**
- Returns a <tt>Integer</tt> instance representing the specified
- <tt>int</tt> value.
- If a new <tt>Integer</tt> 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.
- @param i an <code>int</code> value.
- @return a <tt>Integer</tt> instance representing <tt>i</tt>.
- @since 1.5
*/
public static Integer valueOf(int i) {
if(i >= -128 && i <= IntegerCache.high)
return IntegerCache.cache[i + 128];
else
return new Integer(i);
}
這兒的IntegerCache有一個靜態(tài)的Integer數(shù)組,在類加載時就將-128 到 127 的Integer對象創(chuàng)建了龟虎,并保存在cache數(shù)組中璃谨,一旦程序調(diào)用valueOf 方法,如果i的值是在-128 到 127 之間就直接在cache緩存數(shù)組中去取Integer對象鲤妥。
再看其它的包裝器:
Boolean:(全部緩存)
Byte:(全部緩存)
Character(<= 127緩存)
Short(-128 — 127緩存)
Long(-128 — 127緩存)
Float(沒有緩存)
Doulbe(沒有緩存)
同樣對于垃圾回收器來說:
[java] view plaincopy
Integer i = 100;
i = null;//will not make any object available for GC at all.
這里的代碼不會有對象符合垃圾回收器的條件佳吞,這兒的i雖然被賦予null,但它之前指向的是cache中的Integer對象棉安,而cache沒有被賦null底扳,所以Integer(100)這個對象還是存在盒件。
而如果i大于127或小于-128則它所指向的對象將符合垃圾回收的條件:
[java] view plaincopy
Integer i = 10000;
i = null;//will make the newly created Integer object available for GC.
那么緩存如何修改呢见咒?
下面例子使用32位Windows上的Sun JDK 1.6.0 update 18。
在Java語言規(guī)范第三版朋凉,5.1.7 Boxing Conversion中蒲赂,
The Java Language Specification, 3rd Edition 寫道
If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.
這就是為什么符合規(guī)范的Java實(shí)現(xiàn)必須保證Integer的緩存至少要覆蓋[-128, 127]的范圍阱冶。
使用Oracle/Sun JDK 6,在server模式下滥嘴,使用-XX:AutoBoxCacheMax=NNN參數(shù)即可將Integer的自動緩存區(qū)間設(shè)置為[-128,NNN]木蹬。注意區(qū)間的下界固定在-128不可配置。
在client模式下該參數(shù)無效若皱。這個參數(shù)是server模式專有的镊叁,在c2_globals.hpp中聲明尘颓,默認(rèn)值是128;不過這個默認(rèn)值在默認(rèn)條件下不起作用晦譬,要手動設(shè)置它的值或者是開啟-XX:+AggressiveOpts參數(shù)才起作用疤苹。
在設(shè)置了-XX:+AggressiveOpts啟動參數(shù)后,AutoBoxCacheMax的默認(rèn)值會被修改為20000并且生效敛腌。參考arguments.cpp:
C++代碼
// Aggressive optimization flags -XX:+AggressiveOpts
void Arguments::set_aggressive_opts_flags() {
-
ifdef COMPILER2
if (AggressiveOpts || !FLAG_IS_DEFAULT(AutoBoxCacheMax)) {
if (FLAG_IS_DEFAULT(EliminateAutoBox)) {
FLAG_SET_DEFAULT(EliminateAutoBox, true);
}
if (FLAG_IS_DEFAULT(AutoBoxCacheMax)) {
FLAG_SET_DEFAULT(AutoBoxCacheMax, 20000);
}
// Feed the cache size setting into the JDK
char buffer[1024];
sprintf(buffer, "java.lang.Integer.IntegerCache.high=" INTX_FORMAT, AutoBoxCacheMax);
add_property(buffer);
}
// ...
-
endif
}
測試代碼:
Java代碼
// run with:
// java -server -XX:AutoBoxCacheMax=1000 TestAutoBoxCache
public class TestAutoBoxCache {
public static void main(String[] args) {
Integer a = 1000;
Integer b = 1000;
System.out.println(a == b);
Integer c = 1001;
Integer d = 1001;
System.out.println(c == d);
Integer e = 20000;
Integer f = 20000;
System.out.println(e == f);
}
}
在命令行上測試:
Command prompt代碼
D:>javac TestAutoBoxCache.java
D:>java TestAutoBoxCache
false
false
false
D:>java -server TestAutoBoxCache
false
false
false
D:>java -Djava.lang.Integer.IntegerCache.high=1000 TestAutoBoxCache
true
false
false
D:>java -server -Djava.lang.Integer.IntegerCache.high=1000 TestAutoBoxCache
true
false
false
D:>java -Djava.lang.Integer.IntegerCache.high=1001 TestAutoBoxCache
true
true
false
D:>java -server -Djava.lang.Integer.IntegerCache.high=1001 TestAutoBoxCache
true
true
false
D:>java -XX:AutoBoxCacheMax=1000 TestAutoBoxCache
Unrecognized VM option 'AutoBoxCacheMax=1000'
Could not create the Java virtual machine.
D:>java -server -XX:AutoBoxCacheMax=1000 TestAutoBoxCache
true
false
false
D:>java -server -XX:AutoBoxCacheMax=1001 TestAutoBoxCache
true
true
false
D:>java -server -XX:+AggressiveOpts TestAutoBoxCache
true
true
true