最近在項目中遇到一個問題,兩個值相同的Integer型值進行==比較時,發現Integer其中的一些奧秘,順便也復習一下==和equals的區別,先通過Damo代碼解釋如下:
- System.out.println("<-128~127以內的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();
- <span style="white-space:pre"> </span>
- 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));
輸出結果為:
- <-128~127以內的Integer值,Integer x = value;的方式賦值!>
- i=127,j =127
- i == j:true<--比較-->i.equals(j):true
- <-128~127以外的Integer值,Integer x = value;的方式賦值!>
- m=128,n =128
- m == n:false<--比較-->m.equals(n):true
- <任意Integer值,Integer x = new Integer(value);的方式賦值!>
- x=299,y =299
- x == y:false<--比較-->x.equals(y):true
通過以上代碼及輸出結果,想必大家已經看出其中奧秘!先總結如下:
1、以上代碼第一段和第二段旨在說明:在-128~127的Integer值並且以Integer x = value;的方式賦值的Integer值在進行==和equals比較時,都會返回true,因為Java里面對處在在-128~127之間的Integer值,用的是原生數據類型int,會在內存里供重用,也就是說這之間的Integer值進行==比較時只是進行int原生數據類型的數值比較,而超出-128~127的范圍,進行==比較時是進行地址及數值比較。
2、第三段旨在說明:==和equals的區別,==是進行地址及值比較,無法對==操作符進行重載,而對於equals方法,Integer里面的equals方法重寫了Object的equals方法,查看Integer源碼可以看出equals方法進行的是數值比較。
續詳解:
首先看一段代碼(使用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);
- }
- }
輸出結果:
- true
- false
- true
The Java Language Specification, 3rd Edition 寫道:
- 為了節省內存,對於下列包裝對象的兩個實例,當它們的基本值相同時,他們總是==:
- Boolean
- Byte
- Character, \u0000 - \u007f(7f是十進制的127)
- Integer, -128 — 127
查看jdk源碼,如下:
- /**
- * 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++) //緩存區間數據
- 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有一個靜態的Integer數組,在類加載時就將-128 到 127 的Integer對象創建了,並保存在cache數組中,一旦程序調用valueOf 方法,如果i的值是在-128 到 127 之間就直接在cache緩存數組中去取Integer對象。
再看其它的包裝器:
- Boolean:(全部緩存)
- Byte:(全部緩存)
- Character(<= 127緩存)
- Short(-128 — 127緩存)
- Long(-128 — 127緩存)
- Float(沒有緩存)
- Doulbe(沒有緩存)
同樣對於垃圾回收器來說:
- 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則它所指向的對象將符合垃圾回收的條件:
- 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語言規范第三版,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.
這就是為什么符合規范的Java實現必須保證Integer的緩存至少要覆蓋[-128, 127]的范圍。
使用Oracle/Sun JDK 6,在server模式下,使用-XX:AutoBoxCacheMax=NNN參數即可將Integer的自動緩存區間設置為[-128,NNN]。注意區間的下界固定在-128不可配置。
在client模式下該參數無效。這個參數是server模式專有的,在 c2_globals.hpp中聲明,默認值是128;不過這個默認值在默認條件下不起作用,要手動設置它的值或者是開啟-XX:+AggressiveOpts參數才起作用。
在設置了-XX:+AggressiveOpts啟動參數后,AutoBoxCacheMax的默認值會被修改為20000並且生效。參考 arguments.cpp:
- // 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
- }
測試代碼:
- // 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);
- }
- }
在命令行上測試:
- 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
中間報Unrecognized VM option 'AutoBoxCacheMax=1000'錯誤是因為這個參數只能在HotSpot Server VM上使用,在HotSpot Client VM上不支持。
摘自:http://blog.csdn.net/chengzhezhijian/article/details/9628251