129

我刚刚看到类似这样的代码:

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a == b);

        Integer c = 100, d = 100;
        System.out.println(c == d);
    }
}

运行时,此代码块将打印出:

false
true

我理解为什么第一个是false:因为这两个对象是单独的对象,所以==比较引用。但我想不通,为什么第二个语句返回true?当整数的值在一定范围内时,是否会出现一些奇怪的自动装箱规则?这里发生了什么?

4

12 回答 12

116

true行实际上由语言规范保证。从第 5.1.7 节

如果被装箱的值 p 是真、假、一个字节、一个在 \u0000 到 \u007f 范围内的字符,或者一个介于 -128 和 127 之间的 int 或短数字,则令 r1 和 r2 为任意两次装箱转换的结果p。r1 == r2 总是如此。

讨论继续进行,这表明尽管您的第二行输出是有保证的,但第一行不是(参见下面引用的最后一段):

理想情况下,对给定的原始值 p 进行装箱将始终产生相同的参考。在实践中,使用现有的实现技术这可能是不可行的。上述规则是一种务实的妥协。上面的最后一个条款要求某些常见的值总是被装箱到无法区分的对象中。实现可能会懒惰地或急切地缓存这些。

对于其他值,此公式不允许程序员对装箱值的身份进行任何假设。这将允许(但不要求)共享部分或全部这些引用。

这确保了在最常见的情况下,行为将是期望的行为,而不会造成过度的性能损失,尤其是在小型设备上。例如,内存限制较少的实现可能会缓存所有字符和短整数,以及 -32K - +32K 范围内的整数和长整数。

于 2010-06-28T05:57:45.547 回答
38
public class Scratch
{
   public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;  //1
        System.out.println(a == b);

        Integer c = 100, d = 100;  //2
        System.out.println(c == d);
   }
}

输出:

false
true

是的,第一个输出是为了比较参考而产生的;'a' 和 'b' - 这是两个不同的参考。在第 1 点中,实际上创建了两个引用,类似于 -

Integer a = new Integer(1000);
Integer b = new Integer(1000);

产生第二个输出是因为当落在一个范围内(从 -128 到 127)JVM时试图节省内存。Integer在第 2 点,没有为“d”创建整数类型的新引用。它没有为整数类型引用变量“d”创建新对象,而是只分配了先前创建的由“c”引用的对象。所有这些都是由JVM.

这些内存节省规则不仅适用于整数。出于节省内存的目的,以下包装器对象的两个实例(通过装箱创建时)将始终为 == ,其中它们的原始值相同 -

  • 布尔值
  • 字节
  • 字符从\u0000\u007f(7f 是十进制的 127)
  • 从-128127的短整型
于 2014-04-05T05:32:16.930 回答
10

某个范围内的整数对象(我认为可能是 -128 到 127)被缓存并重用。超出该范围的整数每次都会获得一个新对象。

于 2010-06-28T05:46:17.927 回答
6

是的,当值在一定范围内时,会启动一个奇怪的自动装箱规则。当您将常量分配给 Object 变量时,语言定义中没有任何内容表明必须创建新对象。它可以重用缓存中的现有对象。

事实上,JVM 通常会为此目的存储一个小整数的缓存,以及诸如 Boolean.TRUE 和 Boolean.FALSE 之类的值。

于 2010-06-28T05:46:54.117 回答
4

我的猜测是,Java 保留了一个已经“装箱”的小整数缓存,因为它们非常常见,并且它节省了大量时间来重用现有对象而不是创建新对象。

于 2010-06-28T05:46:26.710 回答
4

这是一个有趣的观点。在Effective Java一书中,建议始终为您自己的类覆盖 equals。此外,要检查 java 类的两个对象实例的相等性,请始终使用 equals 方法。

public class Scratch
{
    public static void main(String[] args)
    {
        Integer a = 1000, b = 1000;
        System.out.println(a.equals(b));

        Integer c = 100, d = 100;
        System.out.println(c.equals(d));
    }
}

返回:

true
true
于 2013-06-17T14:13:02.797 回答
4

将 int 文字直接分配给 Integer 引用是自动装箱的一个示例,其中文字值到对象的转换代码由编译器处理。

所以在编译阶段编译器转换Integer a = 1000, b = 1000;Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);.

所以实际上是Integer.valueOf()方法给了我们整数对象,如果我们查看Integer.valueOf()方法的源代码,我们可以清楚地看到该方法缓存了 -128 到 127(含)范围内的整数对象。

/**
 * Returns an {@code Integer} instance representing the specified
 * {@code int} value.  If a new {@code Integer} 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.
 *
 * This method will always cache values in the range -128 to 127,
 * inclusive, and may cache other values outside of this range.
 *
 * @param  i an {@code int} value.
 * @return an {@code Integer} instance representing {@code i}.
 * @since  1.5
 */
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

因此,如果传递的 int 字面量大于 -128 且小于 127 ,则该方法不会创建和返回新的整数对象,而是Integer.valueOf()从内部返回 Integer 对象。IntegerCache

Java 缓存这些整数对象,因为这个整数范围在日常编程中被大量使用,从而间接节省了一些内存。

由于静态块,当类被加载到内存中时,缓存在第一次使用时被初始化。缓存的最大范围可以由-XX:AutoBoxCacheMaxJVM 选项控制。

此缓存行为仅适用于 Integer 对象,类似于我们也分别拥有ByteCache, ShortCache, LongCache, CharacterCache的Integer.IntegerCache Byte, Short, Long, Character

您可以阅读我的文章Java Integer Cache - Why Integer.valueOf(127) == Integer.valueOf(127) Is True了解更多信息。

于 2018-11-26T05:19:15.203 回答
4

整数缓存是 Java 版本 5 中引入的一项功能,主要用于:

  1. 节省内存空间
  2. 性能的改进。
Integer number1 = 127;
Integer number2 = 127;

System.out.println("number1 == number2" + (number1 == number2); 

输出: True


Integer number1 = 128;
Integer number2 = 128;

System.out.println("number1 == number2" + (number1 == number2);

输出: False

如何?

实际上,当我们为 Integer 对象赋值时,它会在后台自动提升。

Integer object = 100;

实际上是调用Integer.valueOf()函数

Integer object = Integer.valueOf(100);

细枝末节的细节valueOf(int)

    public static Integer valueOf(int i) {
        if (i >= IntegerCache.low && i <= IntegerCache.high)
            return IntegerCache.cache[i + (-IntegerCache.low)];
        return new Integer(i);
    }

描述:

此方法将始终缓存 -128 到 127(含)范围内的值,并且可能缓存此范围之外的其他值。

当需要 -128 到 127 范围内的值时,它每次都会返回一个常量内存位置。但是,当我们需要一个大于 127 的值时

return new Integer(i);

每次我们启动一个对象时都会返回一个新的引用。


此外,==Java 中的运算符用于比较两个内存引用而不是值。

Object1位于 1000 并包含值 6。
Object2位于 1020 并包含值 6。

Object1 == Object2False因为它们具有不同的内存位置,但包含相同的值。

于 2020-11-14T18:19:34.297 回答
3

在 Java 中,对于整数,装箱在 -128 到 127 之间的范围内起作用。当您使用此范围内的数字时,您可以将其与 == 运算符进行比较。对于范围之外的整数对象,您必须使用等于。

于 2010-06-28T06:19:41.163 回答
1

如果我们查看Integer类的源代码,我们可以找到valueOf方法的源代码,如下所示:

public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}

这解释了为什么Integer在 -128 ( Integer.low) 到 127 ( Integer.high) 范围内的对象在自动装箱期间是相同的引用对象。我们可以看到有一个类IntegerCache负责Integer缓存数组,它是该类的私有静态内部Integer类。

还有另一个有趣的例子可能有助于理解这种奇怪的情况:

public static void main(String[] args) throws ReflectiveOperationException {
    Class cache = Integer.class.getDeclaredClasses()[0];
    Field myCache = cache.getDeclaredField("cache");
    myCache.setAccessible(true);

    Integer[] newCache = (Integer[]) myCache.get(cache);
    newCache[132] = newCache[133];

    Integer a = 2;
    Integer b = a + a;
    System.out.printf("%d + %d = %d", a, a, b); // The output is: 2 + 2 = 5
}
于 2016-11-17T06:11:22.290 回答
0

在 Java 5 中,引入了一项新功能来节省内存并提高整数类型对象处理的性能。整数对象在内部缓存并通过相同的引用对象重用。

  1. 这适用于 –127 到 +127(最大整数值)之间的整数值。

  2. 此整数缓存仅适用于自动装箱。整数对象在使用构造函数构建时不会被缓存。

有关更多详细信息,请通过以下链接:

详细的整数缓存

于 2016-06-02T14:15:48.110 回答
0

根据 JLS 5.1.7的要求,类Integer包含 -128 到 127 之间的值的缓存。拳击转换。所以当你用==来检查Integer这个范围内的两个 s 是否相等时,你会得到相同的缓存值,如果你比较Integer这个范围外的两个 s,你会得到两个不同的值。

您可以通过更改 JVM 参数来增加缓存上限:

-XX:AutoBoxCacheMax=<cache_max_value>

或者

-Djava.lang.Integer.IntegerCache.high=<cache_max_value>

见内部IntegerCache类:

/**
 * 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.  The size of the cache
 * may be controlled by the {@code -XX:AutoBoxCacheMax=<size>} option.
 * During VM initialization, java.lang.Integer.IntegerCache.high property
 * may be set and saved in the private system properties in the
 * sun.misc.VM class.
 */
private static class IntegerCache {
    static final int low = -128;
    static final int high;
    static final Integer cache[];

    static {
        // high value may be configured by property
        int h = 127;
        String integerCacheHighPropValue =
            sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high");
        if (integerCacheHighPropValue != null) {
            try {
                int i = parseInt(integerCacheHighPropValue);
                i = Math.max(i, 127);
                // Maximum array size is Integer.MAX_VALUE
                h = Math.min(i, Integer.MAX_VALUE - (-low) -1);
            } catch( NumberFormatException nfe) {
                // If the property cannot be parsed into an int, ignore it.
            }
        }
        high = h;

        cache = new Integer[(high - low) + 1];
        int j = low;
        for(int k = 0; k < cache.length; k++)
            cache[k] = new Integer(j++);

        // range [-128, 127] must be interned (JLS7 5.1.7)
        assert IntegerCache.high >= 127;
    }

    private IntegerCache() {}
}
于 2020-10-17T18:09:33.933 回答