在成长中学习,在学习中成长
分享下Java基础,感谢文章作者 http://blog.csdn.net/chengzhezhijian/article/details/9628251
内容如下:
版权声明:本文为博主原创文章,未经博主允许不得转载。
最近在项目中遇到一个问题,两个值相同的Integer型值进行==比较时,发现Integer其中的一些奥秘,顺便也复习一下==和equals的区别,先通过Damo代码解释如下:
[java] view plain copy 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));
输出结果为:
[java] view plain copy <-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),如下:
[html] view plain copy 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); } }输出结果:
[html] view plain copy true false trueThe Java Language Specification, 3rd Edition 写道:
[html] view plain copy 为了节省内存,对于下列包装对象的两个实例,当它们的基本值相同时,他们总是==: Boolean Byte Character, \u0000 - \u007f(7f是十进制的127) Integer, -128 — 127查看jdk源码,如下:
[java] view plain copy /** * 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(没有缓存)
同样对于垃圾回收器来说:
[java] view plain copy 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 plain copy 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 : 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 中间报 Unrecognized VM option 'AutoBoxCacheMax=1000' 错误是因为这个参数只能在HotSpot Server VM上使用,在HotSpot Client VM上不支持。