【笔记】经典Integer对象赋值128问题

Integer经典面试问题:两个Integer对象都赋值为128,这两个对象比较是否相同?为什么?

回答这个问题,首先我们要知道,在Java中,当你写Integer a = 1; 实际上是调用了Java的自动装箱功能。这会将整数 1 自动装箱为Integer对象,然后将这个对象赋值给变量a。

自动装箱功能是由编译器自动插入的,实际上它相当于执行了如下的代码:

Integer a = Integer.valueOf(1);

这里的valueOf方法是Integer类的一个静态方法,它的作用就是将传入的参数(通常是基本数据类型)自动转换为对应的包装类对象。对于Integer类来说,就是将整数值转换为Integer对象。

知道这一点之后,我们看看Integer.valueOf方法的源码:

/**
 * 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实例,则通常应该优先使用此方法,而不是构造函数Integer(int),因为通过缓存频繁请求的值,该方法可能会产生更好的空间和时间性能。此方法将始终缓存-128到127(包括-128到127)范围内的值,并可能缓存此范围之外的其他值。

接下来看看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=} 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() {}
}

缓存在第一次使用时初始化。缓存的大小可以通过-XX:AutoBoxCacheMax=选项来控制。在虚拟机初始化过程中,可以设置java.lang.Integer.IntegerCache.high属性,并保存在sun.misc.VM类的私有系统属性中。

IntegerCache缓存范围的最小值固定为-128,最大值默认为127.但是最大值是可以被重新定义的,那么可定义的范围是多少呢?

i = Math.max(i, 127);
// Maximum array size is Integer.MAX_VALUE
h = Math.min(i, Integer.MAX_VALUE - (-low) -1);

上面的两行已经定义了IntegerCache缓存最大值的范围:

  1. 定义最大值范围大于等于127;

  2. 定义最大值范围小于等于Integer.MAX_VALUE - (-low) -1;

Integer.MAX_VALUE的定义:(2的23次方 - 1)

@Native public static final int   MAX_VALUE = 0x7fffffff;

IntegerCache缓存的范围定义好后,为区间范围赋值。

for(int k = 0; k < cache.length; k++)
	cache[k] = new Integer(j++);

最后通过断言检查 IntegerCache.high 是否大于等于 127。

// range [-128, 127] must be interned (JLS7 5.1.7)
assert IntegerCache.high >= 127;

IntegerCache.high 是 Java 中 Integer 缓存的一部分,这个缓存用于存储 -128 到 127 之间的整数,以提高这个范围内的整数在多次使用时的性能。

assert 是一个用于调试的语句,当 IntegerCache.high < 127 时,该语句将抛出 AssertionError

JLS7 5.1.7 是 Java Language Specification 的一个部分,这部分规定了对于字符串字面值,编译器必须将它们在编译期放入字符串池中,以避免内存浪费。

这段代码的目的是确保 IntegerCache 的范围在 [-128, 127] 内,因为这是 String 缓存这个优化策略可以应用的范围。如果 IntegerCache 的范围超出了这个范围,那么可能会导致无法进行这种优化,从而可能降低性能。

需要注意的是,assert 语句在默认情况下是关闭的,如果你想要开启它,需要在启动 JVM 时添加 -ea(或 -enableassertions)参数。

因此,当写 Integer i1 = 128;Integer i2 = 128; ,实际上在内存中创建了两个不同的对象,即使它们包含相同的值。

可以通过调用 equals() 方法来比较这两个对象的内容是否相同,而不是使用 == 运算符。这是因为 ==在比较对象时实际上是检查它们是否指向内存中的同一个对象,而不是比较它们的内容。而 equals() 方法则是比较对象的内容。

如果使用 equals() 方法来比较这两个对象,它们会被视为相等,因为它们包含相同的内容。但是,如果你使用 ==运算符来比较,它们会被视为不相等,因为它们是不同的对象。

你可能感兴趣的:(代码人生,Java,java,Integer)