Java Integer(-128~127)值的==和equals比较产生的思考

最近在项目中遇到一个问题,两个值相同的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();       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 =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

通过以上代码及输出结果,想必大家已经看出其中奥秘!先总结如下:

1、以上代码第一段和第二段旨在说明:在-128~127的Integer值并且以Integer x = value;的方式赋值的Integer值在进行==和equals比较时,都会返回true,因为Java里面对处在在-128127之间的Integer值,用的是原生数据类型int,会在内存里供重用,也就是说这之间的Integer值进行==比较时只是进行int原生数据类型的数值比较,而超出-128127的范围,进行==比较时是进行地址及数值比较。

2、第三段旨在说明:==和equals的区别,==是进行地址及值比较,无法对==操作符进行重载,而对于equals方法,Integer里面的equals方法重写了Object的equals方法,查看Integer源码可以看出equals方法进行的是数值比较。

续详解:

首先看一段代码(使用JDK 5),如下:

  1. public class Hello

  2. {

  3. public static void main(String[] args)

  4. {

  5. int a = 1000, b = 1000;

  6. System.out.println(a == b);

  7. Integer c = 1000, d = 1000;

  8. System.out.println(c == d);

  9. Integer e = 100, f = 100;

  10. System.out.println(e == f);

  11. }

  12. }

输出结果:

  1. true

  2. false

  3. true

The Java Language Specification, 3rd Edition 写道: view plaincopy

  1. 为了节省内存,对于下列包装对象的两个实例,当它们的基本值相同时,他们总是==:

  2. Boolean

  3. Byte

  4. Character, \u0000 - \u007f(7f是十进制的127)

  5. Integer, -128 — 127

查看jdk源码,如下:

[java] view plaincopy

  1. /**

    • 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>.
  2. */

  3. // value of java.lang.Integer.IntegerCache.high property (obtained during VM init)

  4. private static String integerCacheHighPropValue;

  5. static void getAndRemoveCacheProperties() {

  6. if (!sun.misc.VM.isBooted()) {

  7. Properties props = System.getProperties();

  8. integerCacheHighPropValue =

  9. (String)props.remove("java.lang.Integer.IntegerCache.high");

  10. if (integerCacheHighPropValue != null)

  11. System.setProperties(props); // remove from system props

  12. }

  13. }

  14. private static class IntegerCache {

  15. static final int high;

  16. static final Integer cache[];

  17. static {

  18. final int low = -128;

  19. // high value may be configured by property

  20. int h = 127;

  21. if (integerCacheHighPropValue != null) {

  22. // Use Long.decode here to avoid invoking methods that

  23. // require Integer's autoboxing cache to be initialized

  24. int i = Long.decode(integerCacheHighPropValue).intValue();

  25. i = Math.max(i, 127);

  26. // Maximum array size is Integer.MAX_VALUE

  27. h = Math.min(i, Integer.MAX_VALUE - -low);

  28. }

  29. high = h;

  30. cache = new Integer[(high - low) + 1];

  31. int j = low;

  32. for(int k = 0; k < cache.length; k++) //缓存区间数据

  33. cache[k] = new Integer(j++);

  34. }

  35. private IntegerCache() {}

  36. }

  37. /**

    • 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
  38. */

  39. public static Integer valueOf(int i) {

  40. if(i >= -128 && i <= IntegerCache.high)

  41. return IntegerCache.cache[i + 128];

  42. else

  43. return new Integer(i);

  44. }

这儿的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 plaincopy

  1. Integer i = 100;

  2. 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

  1. Integer i = 10000;

  2. 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++代码

  1. // Aggressive optimization flags -XX:+AggressiveOpts

  2. void Arguments::set_aggressive_opts_flags() {

  3. ifdef COMPILER2

  4. if (AggressiveOpts || !FLAG_IS_DEFAULT(AutoBoxCacheMax)) {

  5. if (FLAG_IS_DEFAULT(EliminateAutoBox)) {

  6. FLAG_SET_DEFAULT(EliminateAutoBox, true);

  7. }

  8. if (FLAG_IS_DEFAULT(AutoBoxCacheMax)) {

  9. FLAG_SET_DEFAULT(AutoBoxCacheMax, 20000);

  10. }

  11. // Feed the cache size setting into the JDK

  12. char buffer[1024];

  13. sprintf(buffer, "java.lang.Integer.IntegerCache.high=" INTX_FORMAT, AutoBoxCacheMax);

  14. add_property(buffer);

  15. }

  16. // ...

  17. endif

  18. }

测试代码:

Java代码

  1. // run with:

  2. // java -server -XX:AutoBoxCacheMax=1000 TestAutoBoxCache

  3. public class TestAutoBoxCache {

  4. public static void main(String[] args) {

  5. Integer a = 1000;

  6. Integer b = 1000;

  7. System.out.println(a == b);

  8. Integer c = 1001;

  9. Integer d = 1001;

  10. System.out.println(c == d);

  11. Integer e = 20000;

  12. Integer f = 20000;

  13. System.out.println(e == f);

  14. }

  15. }

在命令行上测试:

Command prompt代码

  1. D:>javac TestAutoBoxCache.java

  2. D:>java TestAutoBoxCache

  3. false

  4. false

  5. false

  6. D:>java -server TestAutoBoxCache

  7. false

  8. false

  9. false

  10. D:>java -Djava.lang.Integer.IntegerCache.high=1000 TestAutoBoxCache

  11. true

  12. false

  13. false

  14. D:>java -server -Djava.lang.Integer.IntegerCache.high=1000 TestAutoBoxCache

  15. true

  16. false

  17. false

  18. D:>java -Djava.lang.Integer.IntegerCache.high=1001 TestAutoBoxCache

  19. true

  20. true

  21. false

  22. D:>java -server -Djava.lang.Integer.IntegerCache.high=1001 TestAutoBoxCache

  23. true

  24. true

  25. false

  26. D:>java -XX:AutoBoxCacheMax=1000 TestAutoBoxCache

  27. Unrecognized VM option 'AutoBoxCacheMax=1000'

  28. Could not create the Java virtual machine.

  29. D:>java -server -XX:AutoBoxCacheMax=1000 TestAutoBoxCache

  30. true

  31. false

  32. false

  33. D:>java -server -XX:AutoBoxCacheMax=1001 TestAutoBoxCache

  34. true

  35. true

  36. false

  37. D:>java -server -XX:+AggressiveOpts TestAutoBoxCache

  38. true

  39. true

  40. true

image
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,293评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,604评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,958评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,729评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,719评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,630评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,000评论 3 397
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,665评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,909评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,646评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,726评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,400评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,986评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,959评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,996评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,481评论 2 342

推荐阅读更多精彩内容