奇怪的整数装箱

ego6inou  于 2021-06-29  发布在  Java
关注(0)|答案(12)|浏览(273)

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

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

qnzebej0

qnzebej01#

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

b4lqfgs4

b4lqfgs42#

如果我们查一下 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
}
mnowg1ta

mnowg1ta3#

这是一个有趣的观点。在书中,effectivejava建议总是为自己的类重写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
kiz8lqtg

kiz8lqtg4#

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);

产生第二个输出是因为 JVM 尝试保存内存,当 Integer 落在范围内(从-128到127)。在点2,没有为“d”创建integer类型的新引用。它没有为整型引用变量“d”创建新对象,而是只分配了“c”引用的先前创建的对象。所有这些都是由 JVM .
这些内存节省规则不仅适用于整数。为了节省内存,以下 Package 器对象的两个示例(通过装箱创建时)将始终为==其中它们的原语值相同-
布尔值
字节
字符从\u0000到 \u007f (7f是十进制的127)
从-128到127的短整数

yqyhoc1h

yqyhoc1h5#

将int文本直接赋值给整数引用是自动装箱的一个示例,其中文本值到对象的转换代码由编译器处理。
所以在编译阶段编译器会转换 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);
}

因此,与其创建并返回新的整数对象, Integer.valueOf() 该方法从内部 IntegerCache 如果传递的int文本大于-128小于127。
java缓存这些整数对象是因为这些整数在日常编程中被大量使用,这间接地节省了一些内存。
当类由于静态块而被加载到内存中时,缓存在第一次使用时被初始化。缓存的最大范围可以由 -XX:AutoBoxCacheMax jvm选项。
这种缓存行为不适用于integer对象,类似于integer.integercache ByteCache, ShortCache, LongCache, CharacterCache 为了 Byte, Short, Long, Character 分别。
您可以阅读更多关于我的文章java integer cache-为什么integer.valueof(127)==integer.valueof(127)是真的。

col17t5w

col17t5w6#

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

cu6pst1q

cu6pst1q7#

班级 Integer 根据jls 5.1.7的要求,包含介于-128和127之间的值缓存。拳击转换。所以当你使用 == 检查二的相等性 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() {}
}
hgqdbh6s

hgqdbh6s8#

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

jtw3ybtb

jtw3ybtb9#

整数缓存是java版本5中引入的一项功能,主要用于:
节省内存空间
性能改进。

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

tyg4sfes

tyg4sfes10#

在Java5中,引入了一个新特性来节省内存并提高整数类型对象处理的性能。整数对象在内部缓存,并通过相同的引用对象重用。
这适用于-127到+127(最大整数值)之间的整数值。
此整数缓存仅适用于自动装箱。使用构造函数生成整数对象时,不会缓存它们。
更多详情请点击以下链接:
整数缓存详细信息

uajslkp6

uajslkp611#

是的,有一个奇怪的自动装箱规则,当值在某个范围内时就会生效。为对象变量指定常量时,语言定义中没有说明必须创建新对象。它可以重用缓存中的现有对象。
实际上,jvm通常会为此存储一个小整数的缓存,以及boolean.true和boolean.false等值。

piok6c0g

piok6c0g12#

这个 true 行实际上是由语言规范保证的。根据第5.1.7节:
如果装箱的值p为true、false、一个字节、一个介于\u0000到\u007f之间的字符或一个介于-128到127之间的int或short数字,则让r1和r2为p的任意两个装箱转换的结果。通常情况下r1==r2。
讨论继续进行,建议虽然你的第二行输出是有保证的,但第一行不是(见下面引用的最后一段):
理想情况下,装箱一个给定的原语值p,总是会产生一个相同的引用。在实践中,使用现有的实现技术可能不可行。上述规则是一种务实的妥协。上面的最后一句要求某些公共值总是被装箱到不可区分的对象中。实现可以懒洋洋地或迫不及待地缓存这些。
对于其他值,此公式不允许程序员对装箱值的标识进行任何假设。这将允许(但不要求)共享部分或所有这些引用。
这确保了在大多数常见情况下,行为将是所需的行为,而不会造成不适当的性能损失,特别是在小型设备上。例如,内存有限的实现可能会缓存-32k-+32k范围内的所有字符和短字符,以及整数和长字符。

相关问题