Java 中的怪异整型装箱

我刚看到类似的代码:

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

22321 次浏览

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

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

是的,有一个奇怪的自动装箱规则,当值在一定范围内时就会生效。当您为 Object 变量赋值一个常量时,语言定义中没有任何内容表明要创建一个新的对象 必须的。它可以重用缓存中的现有对象。

实际上,JVM 通常会为此存储一个小 Integers 缓存,以及 Boolean. TRUE 和 Boolean. FALSE 等值。

true行实际上是由语言规范保证的:

如果装箱的 p 值为真, False,一个字节,范围内的一个 char U0000至 u007f,或 int 或 short 数字介于 -128和127之间,然后让 R1和 r2是任意两个的结果 对 p 的装箱转换。它总是 R1 = = r2的情况。

讨论还在继续,建议尽管您的第二行输出是有保证的,但第一行不是(参见下面引用的最后一段) :

理想情况下,将给定的原语装箱 值 p,总是会产生一个 相同的参考。在实践中,这 可能不可行 执行技术。规则 以上是务实的妥协 上述最后条款要求 某些共同的价值观总是被装箱 变成难以分辨的物体 实现可能会懒惰地缓存这些 或者是迫不及待。

对于其他值,这个公式 不允许任何关于 对象上的盒装值的标识 程序员的部分。这将允许 (但不要求)分享一些或 所有这些参考资料。

这确保了在最常见的情况下 情况下,行为将是 没有强加过分的东西 表现罚款,尤指 小型设备,内存有限 例如, 缓存所有字符和短裤,作为 以及整数和长度 范围 -32 K-+ 32 K。

在 Java 中,对于 Integer,装箱的范围在 -128和127之间。当您在这个范围内使用数字时,您可以将其与 = = 运算符进行比较。对于范围之外的 Integer 对象,必须使用 equals。

这是一个有趣的观点。 在书中,有效的爪哇建议始终为您自己的类重写 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
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);

产生第二个输出是因为当 Integer在一个范围内(从 -128到127)时,JVM尝试节省内存。在点2,没有为“ d”创建类型为 Integer 的新引用。它没有为 Integer 类型引用变量‘ d’创建新对象,而是仅分配了以前创建的由‘ c’引用的对象。所有这些都是由 JVM完成的。

这些内存节省规则不仅适用于 Integer。为了节省内存,下列包装器对象的两个实例(通过装箱创建)总是 = = ,其中它们的基元值相同-

  • 布尔型
  • 再见
  • 字符从 U0000\u007f(7f 为127位小数)
  • -128127的短整数

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

  1. 这适用于 -127到 + 127之间的整数值 (最大整数值)

  2. 此整数缓存仅在自动装箱时有效 当它们使用构造函数构建时,不会被缓存

更多细节请浏览以下链接:

整数缓存的详细信息

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

这解释了为什么从 -128(Integer.low)到127(Integer.high)范围内的 Integer对象在自动装箱期间是相同的引用对象。我们可以看到有一个类 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
}

将 int 文字直接赋值给 Integer 引用是自动装箱的一个示例,其中文字值到对象转换代码由编译器处理。

因此,在编译阶段,编译器将 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);
}

因此,如果传递的 int 文字大于 -128且小于127,则 Integer.valueOf()方法将从内部 IntegerCache返回 Integer 对象,而不是创建和返回新的整数对象。

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

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

此缓存行为不仅适用于 Integer 对象,类似于 Integer。整数缓存,我们也有 ByteCache, ShortCache, LongCache, CharacterCacheByte, Short, Long, Character分别。

你可以阅读更多关于我的文章 Java 整数缓存-为什么 Integer.valueOf (127) = = Integer.valueOf (127)为 True

Integer包含介于 -128和127之间的值缓存,这是 JLS 5.1.7拳击转换所要求的。因此,当您使用 ==检查这个范围内两个 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() {}
}

Integer Cache 是 Java 版本5中引入的一个特性,主要用于:

  1. 节省内存空间
  2. 绩效改善。
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 对象赋值时,它在后面执行 汽车促销

Integer object = 100;

实际上是调用 整数函数

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