60

我正在阅读 Kathy Sierra 和 Bert Bates 的 SCJP Java 6,这本书让我非常困惑。在第 245 页上,他们声明下面的代码。

Integer i1 = 1000;
Integer i2 = 1000;
if(i1 != i2)
System.out.println("different objects");

//Prints output
different objects

然后在下一页他们有以下代码

Integer i3 = 10;
Integer i4 = 10;
if(i3 == i4)
System.out.println("same objects");

//Prints output
same objects

我很混乱!当我自己尝试时,您似乎无法使用 == 来比较使用 equals() 方法的相同方式。即使整数变量设置为相同的值(即 10),使用 == 总是会给我“假”。我对么?使用 == 比较相同的 Integer 对象(具有相同的值)将始终导致 'false'

4

8 回答 8

70

答案的关键被称为对象实习。Java 实习生人数很少(小于 128),因此实习生范围内的所有Integer(n)with实例n都是相同的。大于或等于 128 的数字不会被保留,因此Integer(1000)对象彼此不相等。

于 2012-04-14T00:32:28.203 回答
20

如果您查看源代码,Integer您会发现Integer.valueOf(int) 化所有值 -128 到 127。原因是经常使用小的 Integer 值,因此值得被池化/缓存。

直接取自Integer.java

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

请注意,此池是特定于实现的,并且不能保证池范围。

关于实习的答案在概念上是正确的,但在术语上是错误的。Java 中的实习通常意味着 Java 运行时正在执行池化(例如 String 的实习生)。在 Integer 的情况下,是类本身进行池化。不涉及 JVM 魔法。

于 2012-04-14T00:57:37.503 回答
8

上面关于实习的答案是正确的。如果你这样做,需要考虑的事情:

Integer i3 = new Integer(10);
Integer i4 = new Integer(10);

由于您已明确创建新对象,因此您将不会拥有新对象。如果您按如下方式编写代码,它将被实习:

Integer i3 = Integer.valueOf(10);
Integer i4 = Integer.valueOf(10);

它们现在将再次成为同一个对象。如果您查看 src.zip 文件中 Integer.java 类中的 valueOf 方法,您可以看到它在哪里检查 int 的值是否在 -128 到 127 之外,否则它会调用新的 Integer 类它从缓存中加载它。

于 2012-04-14T00:43:37.997 回答
3
Integer i1 = 1000;
Integer i2 = 1000;

编译器将 int 1000“装箱”为 Integer 对象。为此,它将源转换为以下内容:

Integer i1 = Integer.valueOf(1000);
Integer i2 = Integer.valueOf(1000);

现在valueOf可能是一个简单的调用,new Integer(1000)但是每次int装箱时创建一个新的 Integer 对象会花费时间和空间。为了避免这种情况,Integer 类为有限范围的 int 值保留了一个 Integer 对象数组。

if(value> maxRange || value< minRange){
     //not in pool return new Integer
     return new Integer(value);
}else{
     //return pooled Integer object
     //for the value, pool contains all Integer
     //values from minRange to maxRange
     return integerPool[value-minRange];
}

可以通过在程序启动时使用 jvm 参数设置范围(afaik 默认为 -127 到 128)来调整获得的速度与丢失的内存。

于 2012-04-16T19:22:51.273 回答
0

当 Java == 运算符用于比较原始类型以外的任何内容时,它会检查引用相等;即使被比较的东西是包装的原语,这也适用。此外,该valueOf方法和编译器生成的自动装箱语句通常可以自由地任意返回一个新对象,该对象将不与任何其他先前存在的引用相等,或者返回对现有对象的引用(当然,与标识同一对象的任何预先存在的引用相同)。实现需要Integer为值 -128 到 127 维护一个实例“池”,以便所有调用Integer.valueOf在该范围内的任何特定数字上都将返回对同一对象的引用,但除此之外,实现可以自由地做类似的事情

static Integer [] intPool = new Integer[256];

public Integer valueOf(int n)
{
  int hash = (n*0x18675309) >>> 24;
  Integer instance = intPool[n];
  if (instance == null && instance.value != n)
  {
    instance = new Integer(n);
    intPool[hash] = instance ;
  }
  return instance;
}

我并不特别期望 Java 实现会做这样的事情,因为在许多情况下,“缓存命中率”可能接近 0%,而在缓存中查找实例所花费的额外时间将被浪费掉。尽管如此,永远不能保证返回的引用instanceOf不会匹配该方法返回的某个先前引用(即使它不匹配该方法返回的最后一个引用,某些缓存算法可能会导致它返回一个较早的引用,特别是如果池由多个线程共享而没有锁定。缺少锁定将永远不会导致代码返回对具有正确值的整数的引用以外的任何内容,但可能会导致返回的引用比较相等的不可预测的变化)。只有对Integer直接使用构造函数创建的对象的引用才能new Integer(n)保证是唯一的;期望返回的任何引用valueOf不匹配返回的任何引用的代码valueOf,而实际上没有观察到它不匹配,应该被认为是损坏的。

于 2013-11-23T18:06:10.023 回答
0

使用 == 和 != 的字符串比较和整数比较给出的布尔结果与我们预期的不同。所以要小心并确保可能的未知结果不会影响软件的性能、可靠性和准确性。

于 2014-01-07T17:11:52.457 回答
0

“==”总是比较值的内存位置或对象引用。equals 方法总是比较值。但 equals 也间接使用“==”运算符来比较值。Integer 使用 Integer 缓存来存储从 -128 到 +127 的值。如果使用 == 运算符检查 -128 到 127 之间的任何值,则返回 true。如果 -128 到 127 之间的任何值作为

Integer i1 = -128; 
Integer i2 = -128; 
System.out.println(i1 == i2); // returns true

除了上述范围之外,它返回 false

Integer i1 = 1000;
Integer i2 = 1000;
System.out.println(i1 == i2); // returns false

请参阅链接以获取更多信息

于 2016-08-01T17:12:12.790 回答
0

根据jls-5.1.7

If the value p being boxed is true, false, a byte, or a char in the range \u0000 to \u007f,   
or an int or short number between -128 and 127 (inclusive), then let r1 and r2 
be the results of any two boxing conversions of p. It is always the case that r1 == r2.

因此,-128127之间的任何数字都被 Interger 类缓存。
请记住,比较两个对象时总是使用equals方法。

缓存代码写在作为IntegerCache类成员的Integer类中。

这是代码片段:

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

参考

于 2018-06-12T10:18:32.660 回答