基础篇二 两个 Integer 用 == 比较结果竟然不一样?真相藏在 JVM 里
文章目录一、 到底比较的是什么二、Integer 缓存池JVM 的小聪明三、用图理解内存布局缓存范围内100缓存范围外1000四、缓存范围可以改吗五、其他包装类的缓存情况六、new Integer 绕过缓存七、正确姿势比较 Integer 值八、面试速答模板个人网站先看一段代码猜猜输出什么Integera100;Integerb100;System.out.println(ab);// ?Integerc1000;Integerd1000;System.out.println(cd);// ?运行结果第一个输出true第二个输出false。同样的写法同样是 Integer同样的比较为什么结果不一样如果你以为比较的是数值那就掉坑里了。一、 到底比较的是什么在 Java 中的行为取决于比较的是什么类型比较对象比较的是基本类型int, long…值引用类型Integer, String…内存地址Integer是引用类型所以a b比较的不是 100 和 100 这两个数值而是 a 和 b 这两个变量指向的是不是同一个对象。那为什么100 100是 true 呢因为它们指向的确实是同一个对象。这就引出了 Integer 的缓存机制。二、Integer 缓存池JVM 的小聪明当你写下Integer a 100时Java 编译器会自动装箱等价于IntegeraInteger.valueOf(100);而Integer.valueOf()的源码是这样的publicstaticIntegervalueOf(inti){if(iIntegerCache.lowiIntegerCache.high)returnIntegerCache.cache[i(-IntegerCache.low)];returnnewInteger(i);}关键就在这里——如果值在缓存范围内直接返回缓存对象超出范围才创建新对象。缓存范围是多少看IntegerCache的定义privatestaticclassIntegerCache{staticfinalintlow-128;staticfinalinthigh;// 默认 127static{// high 值可以通过 JVM 参数配置inth127;StringintegerCacheHighPropValuesun.misc.VM.getSavedProperty(java.lang.Integer.IntegerCache.high);if(integerCacheHighPropValue!null){try{intiparseInt(integerCacheHighPropValue);iMath.max(Math.max(i,127),Integer.MAX_VALUE-(-low)-1);hi;}catch(NumberFormatExceptionnfe){}}highh;cachenewInteger[(high-low)1];intjlow;for(intk0;kcache.length;k)cache[k]newInteger(j);}}默认缓存范围-128 到 127共 256 个对象在类加载时就被创建好放在数组里复用。所以Integer a 100; → valueOf(100) → 缓存池中取返回同一个对象 Integer b 100; → valueOf(100) → 还是缓存池中取还是同一个对象 a b → true ✅ 指向同一对象 Integer c 1000; → valueOf(1000) → 超出缓存范围new Integer(1000) Integer d 1000; → valueOf(1000) → 超出缓存范围new Integer(1000) c d → false ❌ 两个不同的对象三、用图理解内存布局缓存范围内100栈 堆 ┌───────┐ ┌──────────────┐ │ a ────────→ │ Integer(100) │ ←──────┐ └───────┘ └──────────────┘ │ ┌───────┐ │ │ b ───────────────────────────────────────┘ └───────┘ a b → true指向同一个对象缓存范围外1000栈 堆 ┌───────┐ ┌──────────────┐ │ c ────────→ │ Integer(1000)│ └───────┘ └──────────────┘ ┌───────┐ ┌──────────────┐ │ d ────────→ │ Integer(1000)│ └───────┘ └──────────────┘ c d → false两个不同的对象四、缓存范围可以改吗可以通过 JVM 启动参数调整上限java-XX:AutoBoxCacheMax2000MyProgram设置后缓存范围变为-128 到 2000此时1000 1000也会返回 true。下限 -128 是固定的不可修改上限可以通过参数调大但不能小于 127。这个参数在实际中有什么用一些高并发场景下如果大量使用小整数如 ID 值扩大缓存池可以减少对象创建降低 GC 压力。五、其他包装类的缓存情况不是所有包装类都有缓存整理如下包装类是否有缓存缓存范围Integer✅ 有-128 ~ 127上限可调Long✅ 有-128 ~ 127不可调Short✅ 有-128 ~ 127Byte✅ 有-128 ~ 127全覆盖Character✅ 有0 ~ 127Float❌ 无—Double❌ 无—Boolean✅ 有true / false注意Float 和 Double 没有缓存所以无论什么值比较都是 falseFloatf11.0f;Floatf21.0f;System.out.println(f1f2);// false六、new Integer 绕过缓存还有一个容易忽略的点new Integer()会直接创建新对象不走缓存池Integera100;IntegerbnewInteger(100);System.out.println(ab);// false即使值在缓存范围内new出来的对象和缓存对象也是不同的。不过new Integer()在 JDK 9 中已被标记为Deprecated推荐统一使用Integer.valueOf()。七、正确姿势比较 Integer 值方式示例推荐equals()a.equals(b)✅ 推荐intValue()a.intValue() b.intValue()✅ 可用但需防 NPEa b❌ 严禁// ✅ 正确System.out.println(a.equals(b));// ✅ 也可先拆箱为 int 比较值System.out.println(a.intValue()b.intValue());// ❌ 错误比较的是地址System.out.println(ab);最佳实践在代码规范中加入一条——所有包装类的值比较一律使用equals()。八、面试速答模板Q为什么 Integer 100 100 是 true1000 1000 是 falseA因为 Integer 有缓存池默认缓存 -128 到 127 的对象。在这个范围内Integer.valueOf()返回缓存中的同一个对象所以比较为 true超出范围则创建新对象比较为 false。本质是比较的是引用地址而非数值。QInteger 缓存池的范围能改吗A上限可以通过-XX:AutoBoxCacheMaxsize参数调整下限 -128 固定不可变。这个参数在某些高频整数场景下可以用来优化性能减少对象创建。Q怎么正确比较两个 Integer 的值A用equals()方法不要用。比较的是对象引用地址受缓存池影响结果不确定。equals()比较的是实际数值行为一致可靠。相关文章原文阅读内容有帮助点赞、收藏、关注三连评论区等你