题
我刚刚看到与此类似的代码:
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
?当整数的价值在一定范围内时,是否有一些奇怪的自动氧化规则?这里发生了什么?
解决方案
这 true
实际上,语言规范可以保证行。从 第5.1.7节:
如果被装箱的值p是正确的,则为false,一个字节,范围 u0000至 u007f的字符,或-128和127之间的int或短数字,然后让R1和R2为任何两个拳击转换的结果p。 R1 == R2总是如此。
讨论继续进行,表明尽管您的第二行产出可以保证,但第一个不是(请参阅下面引用的最后一段):
理想情况下,给定的原始值P拳击始终会产生相同的参考。实际上,使用现有的实施技术可能是不可行的。上面的规则是务实的妥协。上面的最终条款要求将某些共同值始终被包装到难以区分的对象中。实施可能会懒洋洋或热切地缓存。
对于其他值,此公式将不允许对程序员部分上盒装值的身份进行任何假设。这将允许(但不需要)共享某些或全部这些参考。
这样可以确保在大多数常见的情况下,行为将是所需的行为,而不会施加不适当的性能处罚,尤其是在小型设备上。较少的内存限制实现可能会例如缓存所有字符和短裤,以及整数和longs -32k- +32k的范围。
其他提示
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”创建类型整数的新引用。它不是为整数类型引用变量“ d”创建新对象,而仅以“ C”引用的先前创建的对象分配。所有这些都是由 JVM
.
这些内存保存规则不仅适用于整数。为了保存内存的目的,以下包装对象的两个实例(在通过拳击创建)中始终为==,其中其原始值相同 -
- 布尔
- 字节
- 角色来自 u0000 到
\u007f
(7F为127个小数) - 短而整数 -128 到 127
整数范围内的对象(我认为可能是-128至127)被缓存和重复使用。该范围之外的整数每次都会收到一个新对象。
是的,当值在一定范围内时,有一个奇怪的自动氧化规则。当您将常数分配给对象变量时,语言定义中的任何内容都没有说明新对象 必须 被创建。它可以从缓存重复使用现有对象。
实际上,JVM通常为此目的存储一个小整数的缓存,以及诸如boolean.true和boolean.false之类的值。
这是一个有趣的观点。在书里 有效的Java 建议始终覆盖自己的课程。同样,要检查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
我的猜测是,Java保留了已经被“盒装”的小整数的缓存,因为它们非常普遍,并且节省了很多时间来重新使用现有对象,而不是创建新对象。
在Java中,拳击在-128至127之间,用于整数。当您使用此范围内的数字时,您可以将其与==运算符进行比较。对于超出范围之外的整数对象,您必须使用平等对象。
直接分配INT字面的文字为整数参考是自动盒的一个示例,其中编译器对对象转换代码进行了文字值。
因此,在编译阶段编译器中转换 Integer a = 1000, b = 1000;
到 Integer a = Integer.valueOf(1000), b = Integer.valueOf(1000);
.
就是这样 Integer.valueOf()
实际上给我们整数对象的方法,如果我们查看 Integer.valueOf()
方法我们可以清楚地看到-128至127(包含)范围内的方法缓存整数。
/**
*
* 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.integercache我们也有 ByteCache, ShortCache, LongCache, CharacterCache
为了 Byte, Short, Long, Character
分别。
您可以在我的文章中阅读更多 Java Integer Cache-为什么Integer.valueof(127)== Integer.valueof(127)为true.
在Java 5中,引入了一项新功能,以节省内存并提高整数类型对象处理的性能。整数对象在内部缓存,并通过相同的引用对象重复使用。
这适用于–127至+127(最大整数值)之间的整数值。
此整数缓存仅适用于自动氧化。使用构造函数构建整数对象将不会缓存。
有关更多详细信息,请通过以下链接:
如果我们检查 Integer
obeject,我们将找到 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 NoSuchFieldException, IllegalAccessException {
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
}