代码之家  ›  专栏  ›  技术社区  ›  Joel

爪哇怪诞拳

  •  103
  • Joel  · 技术社区  · 15 年前

    我刚刚看到类似的代码:

    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 ?当一个整数的值在某个范围内时,是否有一些奇怪的自动氧化规则?这是怎么回事?

    10 回复  |  直到 6 年前
        1
  •  96
  •   Pshemo    12 年前

    这个 true 行实际上是由语言规范保证的。从 section 5.1.7 :

    如果装箱的值p为真, 假,一个字节,一个字符在范围内 u10000到u07f,或int或短 数字介于-128和127之间,然后让 R1和R2是任意两个的结果 P的装箱转换总是 R1==R2的情况。

    讨论继续,建议尽管第二行的产出是有保证的,但第一行却没有(见下面最后一段):

    理想情况下,拳击一个给定的原始人 值p,将始终产生 相同的参考。实际上,这个 使用现有的可能不可行 实现技术。规则 以上是务实的妥协。这个 上述最后条款要求

    identity of the boxed values on the programmer's part. This would allow (但不要求)共享一些或 all of these references.

    这确保了最常见的情况。 情况下,行为将是 理想的,不强加的 绩效惩罚,尤其是 小型设备。较少内存限制 例如,实现可能是 缓存所有字符和短裤,如 以及

        2
  •  24
  •   Razib    7 年前
    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);
    

    The second output is produced because the JVM tries to save memory, when the Integer 属于范围(从-128到127)。在第2点,没有为“d”创建新的整数类型引用。它没有为整型引用变量“d”创建新的对象,而是只为以前创建的对象分配由“c”引用的对象。所有这些都是由 JVM .

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

    • 布尔
    • 字节
    • 字符来自 乌万斯 \u007f (7f is 127 in decimal)
    • 短整数 - 128 一百二十七
        3
  •  8
  •   Adam Crume    15 年前

    Integer objects in some range (I think maybe -128 through 127) get cached and re-used. Integers outside that range get a new object each time.

        4
  •  4
  •   Avi    15 年前

    Yes, there is a strange autoboxing rule that kicks in when the values are in a certain range. 将常量赋给对象变量时,语言定义中没有表示新对象的内容。 必须 被创造。It may reuse an existing object from cache.

    事实上,为了实现这个目的,JVM通常会存储一个小整数的缓存,以及Boolean.true和Boolean.false等值。

        5
  •  4
  •   AmirHd    12 年前

    这是一个有趣的观点。 书中 Effective Java suggests always to override equals for your own classes. Also that, to check equality for two object instances of a java class always use the equals method.

    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
    
        6
  •  3
  •   Omnifarious    15 年前

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

        7
  •  3
  •   marvin    15 年前

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

        8
  •  1
  •   Naresh Joshi    6 年前

    将int文本直接赋值给整数引用是自动装箱的一个例子,在这个例子中,编译器处理对象转换代码的文本值。

    Integer a = 1000, b = 1000; Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);

    就是这样 Integer.valueOf() 方法,如果我们查看 整数·值() 方法我们可以清楚地看到该方法缓存范围为-128到127(包括128)的整数对象。

    /**
     *
     * 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);
     }
    

    因此,不要创建和返回新的整数对象, 整数·值() the method returns Integer objects from the internal IntegerCache

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

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

    ByteCache, ShortCache, LongCache, CharacterCache 对于 Byte, Short, Long, Character 分别。

    You can read more on my article Java Integer Cache - Why Integer.valueOf(127) == Integer.valueOf(127) Is True .

        9
  •  0
  •   Rahul Maurya    9 年前

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

    1. 这适用于在介于127到127之间的整数值。 (Max Integer值)。

    2. 这个整数缓存只在自动装箱上运行。整数对象将 当使用构造函数构建时不缓存。

    更多细节请参阅以下链接:

    Integer Cache in Detail

        10
  •  0
  •   L Joey Utkarsh Bhatt    8 年前

    如果我们检查源代码 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);
    }
    

    which can explain why 整数 对象,范围为-128( Integer.low )至127( Integer.high )是自动装箱过程中引用的对象。我们可以看到有一个班 IntegerCache 照顾好 整数 缓存数组,它是私有静态内部类 整数 班级。

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

    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
    
          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    
    
    }