Java中奇怪的整数拳击

时间:2010-06-28 05:43:31

标签: java autoboxing

我刚刚看到类似的代码:

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?当Integer的值在一定范围内时,是否会出现一些奇怪的自动装箱规则?这是怎么回事?

12 个答案:

答案 0 :(得分:96)

true行实际上由语言规范保证。来自section 5.1.7

  

如果装箱的值p为真,   false,一个字节,范围内的char   \ u0000到\ u007f,或者是int或short   在-128和127之间的数字,然后让   r1和r2是任意两个的结果   p的拳击转换它始终是   r1 == r2。

的情况

讨论继续进行,建议虽然你的第二行输出是有保证的,但第一行不是(参见下面引用的最后一段):

  

理想情况下,装箱给定的原语   值p,总会产生一个   相同的参考。在实践中,这   使用现有可能不可行   实施技术。规则   以上是务实的妥协。该   上述最后条款要求   某些常见值总是被装箱   成为无法区分的对象。该   实施可能会缓慢地缓存这些   或热切地。

     

对于其他值,此配方   不允许任何关于。的假设   身上的盒装价值的身份   程序员的一部分。这样就可以了   (但不要求)分享一些或   所有这些参考文献。

     

这确保了最常见的   案件,行为将是   期望的,没有施加不适当的   性能惩罚,特别是   小型设备。内存限制较少   例如,实现可能   缓存所有字符和短片,如   以及整数和长期   范围为-32K - + 32K。

答案 1 :(得分:24)

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

是否生成第一个输出用于比较参考; &#39;一个&#39;和&#39; b&#39; - 这是两个不同的参考。在第1点中,实际创建了两个类似于 -

的引用
Integer a = new Integer(1000);
Integer b = new Integer(1000);

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

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

  • 布尔
  • 字节
  • \ u0000 \u007f的字符(7f为十进制127)
  • -128 127
  • 的短整数

答案 2 :(得分:8)

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

答案 3 :(得分:4)

是的,当值在某个范围内时,会出现一个奇怪的自动装箱规则。为Object变量赋值时,语言定义中没有任何内容表示必须创建新对象。它可以重用缓存中的现有对象。

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

答案 4 :(得分: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

答案 5 :(得分:3)

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

答案 6 :(得分:3)

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

答案 7 :(得分:1)

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

因此,在编译阶段,编译器会将Integer a = 1000, b = 1000;转换为Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);

实际上是Integer.valueOf()方法为我们提供了整数对象,如果我们查看Integer.valueOf()方法的源代码,我们可以清楚地看到该方法将-128到127(含)。

/**
 *
 * 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()会从内部IntegerCache返回Integer对象,而不是创建和返回新的整数对象。

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

由于静态块而将类加载到内存时,第一次使用时会初始化缓存。缓存的最大范围可以由-XX:AutoBoxCacheMax JVM选项控制。

与Integer类似,此缓存行为仅适用于Integer对象。IntegerCache我们分别为ByteCache, ShortCache, LongCache, CharacterCacheByte, Short, Long, Character

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

答案 8 :(得分:0)

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

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

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

  3. 有关详细信息,请参阅下面的链接:

    Integer Cache in Detail

答案 9 :(得分:0)

如果我们检查Integer obeject的源代码,我们会找到valueOf方法的来源,如下所示:

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

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

另一个有趣的例子可以帮助我们理解这种奇怪的情况:

Integer

答案 10 :(得分:0)

Integer包含-128和127之间的值的高速缓存,这是JLS 5.1.7. Boxing Conversion所要求的。因此,当您使用==检查此范围内两个Integer的相等性时,您将获得相同的缓存值,并且如果您比较此范围之外的两个Integer,则您得到两个不同的值。

您可以通过更改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() {}
}

答案 11 :(得分:0)

Integer Cache是​​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,因为它们包含相同的值,但它们具有不同的存储位置。