Java 的“128陷阱”:深入理解 Integer 缓存机制

什么是128陷阱?

在 Java 中,当你使用 Integer 类比较两个在 -128 到 127 范围内的整数时,== 可能返回 true,但超出此范围时却返回 false。这种现象被称为 “128陷阱”

Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true ✅

Integer c = 128;
Integer d = 128;
System.out.println(c == d); // false ❌

根源:IntegerCache 类

JDK 1.8 在 Integer 类内部维护了一个静态缓存数组,默认缓存范围是 -128 到 127。源码如下(简化版):

// JDK 1.8 Integer.java 源码片段
private static class IntegerCache {
    static final int low = -128;
    static final int high; // 默认127
    static final Integer cache[];

    static {
        int h = 127;
        // 可通过JVM参数自定义上限
        String integerCacheHighPropValue = 
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                h = Math.max(parseInt(integerCacheHighPropValue), 127);
            } catch (...) { ... }
        }
        high = h;
        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++); // 预创建缓存对象
    }
}

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)]; // 返回缓存对象
    return new Integer(i); // 新建对象
}

关键机制

  1. 自动装箱使用 valueOf()
    Integer a = 127; 实际调用 Integer.valueOf(127)
  2. 缓存范围默认 -128~127
    此范围内的值直接返回缓存对象,== 比较的是同一对象
  3. 范围外新建对象
    超出范围时 new Integer() 创建新对象,== 比较不同对象地址

为什么设计这个机制?

  • 性能优化:频繁使用的小整数避免重复创建对象
  • 减少内存开销:复用常用数字对象
  • ⚠️ 副作用:导致 == 行为不一致(看似值相等,实则对象不同)

解决方案:如何正确比较?

方法 1:使用 .equals() 方法

Integer x = 200;
Integer y = 200;
System.out.println(x.equals(y)); // true ✅(比较实际值)

方法 2:强制拆箱比较

System.out.println(x.intValue() == y.intValue()); // true ✅
// 或简写为:
System.out.println(x == y.intValue()); // true ✅

方法 3:调整缓存范围(不推荐)

通过 JVM 参数扩大缓存上限(例如设为 200):

java -Djava.lang.Integer.IntegerCache.high=200 YourProgram

其他包装类的缓存机制

包装类 缓存范围
Byte 全部值 (-128~127)
Short -128~127
Long -128~127
Character 0~127 (ASCII字符)
Boolean TRUE/FALSE(全部缓存)

⚠️ FloatDouble 没有缓存机制,始终新建对象!

总结与最佳实践

  • 比较包装类对象时,始终使用 .equals() 而非 ==
  • 理解自动装箱的底层行为(调用 valueOf()
  • 在性能敏感场景,优先使用基本类型(int 而非 Integer
  • 128陷阱是设计优化的副作用,而非 BUG

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐