【问题标题】:Why is 128==128 false but 127==127 is true when comparing Integer wrappers in Java?为什么在比较 Java 中的整数包装器时 128==128 为假但 127==127 为真?
【发布时间】:2021-09-09 16:45:43
【问题描述】:
class D {
    public static void main(String args[]) {
        Integer b2=128;
        Integer b3=128;
        System.out.println(b2==b3);
    }
}

输出:

false

class D {
    public static void main(String args[]) {
        Integer b2=127;
        Integer b3=127;
        System.out.println(b2==b3);
    }
}

输出:

true

注意:-128 到 127 之间的数字为真。

【问题讨论】:

    标签: java integer comparison pass-by-reference pass-by-value


    【解决方案1】:

    当您在 Java 中编译数字文字并将其分配给整数(大写 I)时,编译器会发出:

    Integer b2 =Integer.valueOf(127)
    

    这行代码也是在你使用自动装箱的时候生成的。

    valueOf 的实现使得某些数字被“池化”,它为小于 128 的值返回相同的实例。

    来自 java 1.6 源代码,第 621 行:

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

    high 的值可以通过系统属性配置为另一个值。

    -Djava.lang.Integer.IntegerCache.high=999

    如果您使用该系统属性运行程序,它将输出 true!

    显而易见的结论:永远不要依赖两个引用相同,始终将它们与.equals() 方法进行比较。

    所以b2.equals(b3) 将为 b2,b3 的所有逻辑相等值打印 true。

    注意Integer缓存不是出于性能原因,而是为了符合JLS, section 5.1.7;必须为值 -128 到 127(含)提供对象标识。

    Integer#valueOf(int) 也记录了这种行为:

    通过缓存频繁请求的值,此方法可能会显着提高空间和时间性能。此方法将始终缓存 -128 到 127(含)范围内的值,并可能缓存此范围之外的其他值。

    【讨论】:

    • 请注意,小于 127 的值将被 java 忽略,大于 Integer.MAX_VALUE-128 的值将被封顶。
    • 整数在 Java 5 及更高版本中被缓存为字节值,使得 new Integer(1) == new Integer(1)。但是,在 Java 1.4 或更低版本中并非如此,因此如果您最终必须降级到该环境,请小心。
    • 不,这是错误的。无论jvm如何,new Integer(1) == new Integer(1) 都是假的。 AFAIK 没有编译器会在“new”关键字上作弊。它必须总是实例化一个新对象。
    • @Holger 有趣的点。但是从技术上讲,用自定义的 impl 替换 JDK 中的 Integer 类是可行的......(不要问为什么有人会那么疯狂) - 然后它可能会产生不允许优化的副作用
    • @AndreasPetersson 当然。 “编译器”是指 JIT 编译器,它确实知道实际的实现类,并且只有在构造函数没有副作用的情况下才可以优化。或者优化表达式只重现副作用,然后使用false。实际上,这可能在今天已经发生,作为应用逃逸分析和标量替换的副作用。
    【解决方案2】:

    自动装箱缓存 -128 到 127。这是在 JLS (5.1.7) 中指定的。

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

    处理对象时要记住的一个简单规则是 - 如果要检查两个对象是否“相等”,请使用 .equals,如果要查看它们是否指向同一个实例,请使用 ==

    【讨论】:

    • 注意:Java 9 中的 JLS 发生了变化。现在只保证 编译时常量表达式;查看已接受答案的更新。
    【解决方案3】:

    使用原始数据类型,整数,在这两种情况下都会产生预期的输出。

    但是,由于您使用的是 Integer 对象,因此 == 运算符具有不同的含义。

    在对象的上下文中,== 检查变量是否引用相同的对象引用。

    要比较对象的值,您应该使用 equals() 方法 例如

     b2.equals(b1)
    

    这将指示 b2 是否小于 b1、大于或等于(查看 API 了解详细信息)

    【讨论】:

      【解决方案4】:

      是Java相关的内存优化。

      为了节省内存,Java 会“重用”所有其值的包装对象 属于以下范围:

      所有布尔值(真假)

      所有字节值

      从 \u0000 到 \u007f 的所有字符值(即十进制的 0 到 127)

      从 -128 到 127 的所有 Short 和 Integer 值。

      【讨论】:

        【解决方案5】:

        看一下Integer.java,如果值在-128到127之间,会使用缓存池,所以(Integer) 1 == (Integer) 1(Integer) 222 != (Integer) 222

         /**
         * 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) {
            assert IntegerCache.high >= 127;
            if (i >= IntegerCache.low && i <= IntegerCache.high)
                return IntegerCache.cache[i + (-IntegerCache.low)];
            return new Integer(i);
        }       
        

        【讨论】:

          【解决方案6】:

          其他答案描述了为什么可以观察到观察到的效果,但这对程序员来说真的是无关紧要的(当然很有趣,但在编写实际代码时应该忘记这一切)。

          要比较 Integer 对象是否相等,请使用 equals 方法。

          不要尝试使用标识运算符== 比较整数对象的相等性。

          可能会发生一些相等的值是相同的对象,但这不是通常应该依赖的。

          【讨论】:

            【解决方案7】:

            如果该值介于 -128 和 127 之间,它将使用缓存池,并且仅在自动装箱时才适用。 因此,您将拥有以下内容:

                public static void main(String[] args) {
                    Integer a  = new Integer(100);
                    Integer b = new Integer(100);
                    System.out.println(a == b);         // false. == compare two instances, they are difference
                    System.out.println(a.equals(b));    // true. equals compares the value
            
                    Integer a2 = 100;
                    Integer b2 = 100;
                    System.out.println(a2 == b2);       // true. auto-boxing uses cached pool between -128/127
                    System.out.println(a2.equals(b2));  // true. equals compares the value
            
                    Integer a3 = 129;
                    Integer b3 = 129;
                    System.out.println(a3 == b3);       // false. not using cached pool
                    System.out.println(a3.equals(b3));  // true. equals compares the value
                }
            }
            

            【讨论】:

              【解决方案8】:

              我写了以下内容,因为这个问题不仅仅针对 Integer。我的结论是,如果您不正确地使用 API,您通常会看到不正确的行为。正确使用它,您应该会看到正确的行为:

              public static void main (String[] args) {
                  Byte b1=127;
                  Byte b2=127;
              
                  Short s1=127; //incorrect should use Byte
                  Short s2=127; //incorrect should use Byte
                  Short s3=128;
                  Short s4=128;
              
                  Integer i1=127; //incorrect should use Byte
                  Integer i2=127; //incorrect should use Byte
                  Integer i3=128;
                  Integer i4=128;
              
                  Integer i5=32767; //incorrect should use Short
                  Integer i6=32767; //incorrect should use Short
              
                  Long l1=127L;           //incorrect should use Byte
                  Long l2=127L;           //incorrect should use Byte
                  Long l3=13267L;         //incorrect should use Short
                  Long l4=32767L;         //incorrect should use Short
                  Long l5=2147483647L;    //incorrect should use Integer 
                  Long l6=2147483647L;    //incorrect should use Integer
                  Long l7=2147483648L;
                  Long l8=2147483648L;
              
                  System.out.print(b1==b2); //true  (incorrect) Used API correctly
                  System.out.print(s1==s2); //true  (incorrect) Used API incorrectly
                  System.out.print(i1==i2); //true  (incorrect) Used API incorrectly
                  System.out.print(l1==l2); //true  (incorrect) Used API incorrectly
              
                  System.out.print(s3==s4); //false (correct) Used API correctly
                  System.out.print(i3==i4); //false (correct) Used API correctly
                  System.out.print(i5==i6); //false (correct) Used API correctly
                  System.out.print(l3==l4); //false (correct) Used API correctly
                  System.out.print(l7==l8); //false (correct) Used API correctly
                  System.out.print(l5==l6); //false (correct) Used API incorrectly
              
              }
              

              【讨论】:

                猜你喜欢
                • 2015-03-11
                • 1970-01-01
                • 1970-01-01
                • 2014-01-20
                • 2011-08-21
                • 2014-01-19
                相关资源
                最近更新 更多