为什么整数没有缓存在 Java 中?

2022-09-01 17:14:31

我知道有关于这个话题的类似帖子,但它们并没有完全解决我的问题。当您执行以下操作时:

Integer a = 10;
Integer b = 10;
System.out.println("a == b: " + (a == b));

这将(显然)在大多数时间进行打印,因为范围 [-128, 127] 中的整数以某种方式被缓存。但:true

Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println("a == b: " + (a == b));

会回来。我知道我要求整数的新实例,但是既然盒装原语在Java中是不可变的,并且机器已经存在以执行“正确的事情”(如第一种情况所示),为什么会发生这种情况?false

如果所有带有 10 的 Integer 实例在内存中都是相同的对象,那不是更有意义吗?换句话说,为什么我们没有类似于“字符串实习”的“整数实习”?

更好的是,如果表示相同事物的盒装基元的实例(无论值(和类型)如何)是同一个对象,那不是更有意义吗?或者至少正确响应 ?==


答案 1

应该非常清楚的是,缓存的性能受到不可接受的影响 - 每次创建整数时都会进行额外的 if 语句和内存查找。仅此一点就掩盖了任何其他原因以及这条线上的其他痛苦。

就“正确”地回答==而言,OP在正确性假设上是错误的。整数确实通过一般Java社区对正确性的期望,当然还有规范对正确性的定义来正确响应==。也就是说,如果两个引用指向同一对象,则它们是 。如果两个引用指向不同的对象,即使它们具有相同的内容,它们也不会指向不同的对象。因此,将评估结果为 应该不足为奇。====new Integer(5) == new Integer(5)false

更有趣的问题是,为什么每次都需要创建一个唯一的实例?即为什么不允许缓存?答案是和呼叫。缓存会错误地导致线程在不应该同步时彼此同步。new Object();new Object();wait(...)notify(...)new Object()

如果不是这样,那么Java实现完全可以用单例缓存s。new Object()

这应该可以解释为什么必须完成7次才能创建7个唯一对象,每个对象都包含值5(因为扩展)。new Integer(5)IntegerIntegerObject


次要的,不太重要的东西:这个不错的方案中的一个问题来自自动装箱和自动开箱功能。没有该功能,您将无法进行比较,例如.为了启用这些,Java 取消了对象的框(并且不会将基元框化)。因此转换为:(而不是new Integer(5) == 5new Integer(5) == 5new Integer(5).intValue() == 5new Integer(5) == new Integer(5)

最后要了解的一件事是 自动装箱 不是由 完成的。它是在内部通过调用 来完成的。nnew Integer(n)Integer.valueOf(n)

如果您认为自己理解并希望测试自己,请预测以下程序的输出:

public class Foo {
  public static void main (String[] args) {
    System.out.println(Integer.valueOf(5000) == Integer.valueOf(5000));
    System.out.println(Integer.valueOf(5000) == new Integer(5000));
    System.out.println(Integer.valueOf(5000) == 5000);
    System.out.println(new Integer(5000) == Integer.valueOf(5000));
    System.out.println(new Integer(5000) == new Integer(5000));
    System.out.println(new Integer(5000) == 5000);
    System.out.println(5000 == Integer.valueOf(5000));
    System.out.println(5000 == new Integer(5000));
    System.out.println(5000 == 5000);
    System.out.println("=====");
    System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
    System.out.println(Integer.valueOf(5) == new Integer(5));
    System.out.println(Integer.valueOf(5) == 5);
    System.out.println(new Integer(5) == Integer.valueOf(5));
    System.out.println(new Integer(5) == new Integer(5));
    System.out.println(new Integer(5) == 5);
    System.out.println(5 == Integer.valueOf(5));
    System.out.println(5 == new Integer(5));
    System.out.println(5 == 5);
    System.out.println("=====");
    test(5000, 5000);
    test(5, 5);
  }
  public static void test (Integer a, Integer b) {
    System.out.println(a == b);
  }
}

对于额外的信用,如果所有==.equals(...)

更新:感谢用户@sactiw的评论:“缓存的默认范围是-128到127,java 1.6以后,您可以通过从命令行传递-XX:AutoBoxCacheMax=来重置上限值>= 127”


答案 2

这可能会破坏在此设计更改之前编写的代码,因为每个人都强烈认为两个新创建的实例是不同的实例。它可以用于自动装箱,因为自动装箱以前不存在,但是改变new的含义太危险了,可能不会带来太大的收益。在Java中,短期对象的成本并不高,甚至可能低于维护长期对象缓存的成本。