我知道这个主题有similar posts,但它们并没有完全解决我的问题。当你这样做时:
Integer a = 10;
Integer b = 10;
System.out.println("a == b: " + (a == b));
这将(显然)打印true
大部分时间,因为[-128,127]范围内的整数以某种方式被缓存。但是:
Integer a = new Integer(10);
Integer b = new Integer(10);
System.out.println("a == b: " + (a == b));
将返回false
。我理解我要求整数的新实例,但由于盒装原语在Java中是不可变的,并且机器已经在那里做“正确的事情”(如第一种情况所示),为什么会发生这种情况?
如果一个带有10的Integer的所有实例在内存中都是同一个对象,那会不会更有意义?换句话说,为什么我们没有“Integer interning”这类似于“String interning”?
更好的是,如果表示同一事物的盒装基元的实例,无论值(和类型)是同一个对象,那么它会更有意义吗?或者至少正确回应==
?
答案 0 :(得分:19)
很明显,缓存有一个不可接受的性能损失 - 每次创建一个Integer时都需要额外的if语句和内存查找。仅此一点就掩盖了任何其他原因以及其余的痛苦。
就“正确”回答==而言,OP在他的正确性假设中是错误的。整数DO通过一般Java社区对正确性的期望来正确地响应==,当然还有规范对正确性的定义。也就是说,如果两个引用指向同一个对象,则它们是==
。如果两个引用指向不同的对象,即使它们具有相同的内容,它们也不 ==
。因此,new Integer(5) == new Integer(5)
评估为false
毫不奇怪。
更有趣的问题是为什么 new Object();
每次都需要创建一个唯一的实例?一世。即为什么new Object();
不允许缓存?答案是wait(...)
和notify(...)
来电。缓存new Object()
会错误地导致线程彼此同步。
如果不是这样,那么Java实现可以用单例完全缓存new Object()
。
这应该解释为什么必须完成7次new Integer(5)
才能创建7个唯一的Integer
个对象,每个对象包含值5(因为Integer
扩展Object
)。< / p>
次要的,不那么重要的东西:这个不错的方案中的一个问题来自自动装箱和自动装箱功能。没有该功能,您无法进行比较,例如new Integer(5) == 5
。要启用这些,Java 取消装箱对象(并且不框)原语。因此new Integer(5) == 5
转换为:new Integer(5).intValue() == 5
(不 new Integer(5) == new Integer(5)
。
最后要理解的是n
的{{1}}的自动装箱是由new 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重置上限值&gt; = 127:AutoBoxCacheMax =来自命令行“
答案 1 :(得分:7)
这可能会破坏在此设计更改之前编写的代码,当每个人都认为两个新创建的实例是不同的实例时。它可以用于自动装箱,因为之前不存在自动装箱,但改变新装置的含义太危险了,可能不会带来太大的好处。短期对象的成本在Java中并不大,甚至可能低于维护长期对象缓存的成本。
答案 2 :(得分:4)
如果您查看来源,请看:
/**
* Returns an Integer instance representing the specified int value. If a new
* Integer instance is not required, this method should generally be used in
* preference to the constructor Integer(int), as this method is likely to
* yield significantly better space and time performance by caching frequently
* requested values.
*
* @Parameters: i an int value.
* @Returns: an Integer instance representing i.
* @Since: 1.5
*/
public static Integer valueOf(int i) {
final int offset = 128;
if (i >= -128 && i <= 127) { // must cache
return IntegerCache.cache[i + offset];
}
return new Integer(i);
}
来源:link
这是性能原因==
返回布尔值为真的整数 - 它完全是一个黑客。如果您想比较值,那么您可以使用compareto
或equals
方法。
在其他语言中,例如,你可以使用==
来比较字符串,这基本上是相同的原因,它被称为java语言最大的不幸之一。
int
是一种原始类型,由语言预定义并由保留关键字命名。作为原语,它不包含类或任何类关联信息。 Integer
是一个不可变的基本类,它通过一个包私有的本机机制加载并转换为Class - 这提供了自动装箱,并在JDK1.5中引入。事先JDK1.5 int
和Integer
两个非常不同的东西。
答案 3 :(得分:2)
在Java中,每次调用new
运算符时,都会分配新内存并创建新对象。这是标准的语言行为,据我所知,没有办法绕过这种行为。即使是标准班级也必须遵守这一规则。
答案 4 :(得分:2)
据我了解,new
将创建一个新对象,无论如何。这里的操作顺序是你首先调用new
,它实例化一个新对象,然后调用构造函数。 JVM无法进行干预并将new
转换为“根据传递给构造函数的值获取缓存的Integer对象”。
Integer.valueOf
吗?这很有效。
答案 5 :(得分:2)
如果一个带有10的Integer的所有实例在内存中都是同一个对象,那会不会更有意义?换句话说,为什么我们没有“Integer interning”这类似于“String interning”?
因为它太糟糕了!
首先,此代码会抛出OutOfMemoryError
:
for (int i = 0; i <= Integer.MAX_VALUE; i++) {
System.out.printf("%d\n", i);
}
大多数Integer对象可能都是短暂的。
其次,您将如何维护这样一组规范的Integer对象?用某种表格或地图。你会如何仲裁对该地图的访问?有某种锁定。因此,突然自动装箱将成为线程代码的性能损失同步噩梦。
答案 6 :(得分:1)
新实例是一个新实例,因此它们的值相等,但它们不等于对象。
因此a == b
无法返回true
。
如果他们是1个对象,正如您所要求的那样:a+=2;
会将2 添加到所有 int = 10
- 这将是非常糟糕的。
答案 7 :(得分:1)
你的第一个例子是规范的副产品,要求在大约0的某个范围内创建flyweights。永远不应该依赖它。
至于为什么Integer
不起作用String
?我会想象避免一个已经很慢的过程的开销。你可以使用原语的原因是因为它们明显更快并占用更少的内存。
现在更改它可能会破坏现有代码,因为您正在更改==
运算符的功能。
答案 8 :(得分:1)
new
表示new
。
new Object()
并非轻浮。
答案 9 :(得分:1)
BTW,如果你这样做
Integer a = 234345;
Integer b = 234345;
if (a == b) {}
这可能是真的。
这是因为你没有使用新的Integer(),如果它认为合适的话,允许JVM(不是类代码)缓存它自己的整数副本。现在你不应该根据这个来编写代码,但是当你说新的Integer(234345)时,规则保证你肯定会有不同的对象。
答案 10 :(得分:1)
让我通过链接到JLS的相关部分,稍微扩展ChrisJ和EboMike的答案。
new
是Java中的关键字,允许在类实例创建表达式(Section 15.9 of the JLS)中使用。这与C ++不同,其中new
是一个运算符,可以重载。
表达式始终尝试分配内存,并在每次评估时生成一个新对象(Section 15.9.4)。因此,此时缓存查找已经太晚了。
答案 11 :(得分:0)
对于Integer
个对象,请使用a.equals(b)
条件进行比较。
比较时,编译器不会为您进行拆箱,除非您将值指定给基本类型。
答案 12 :(得分:0)
假设您准确地描述了代码的行为,听起来像autoboxing不能在'gets'(=)operatior上工作,而是听起来像整数x = 10;给对象xa的内存指针'10'而不是10的值。因此((a == b)== true)(将评估为true,因为对象上的==对你分配给10的内存地址进行操作
那么什么时候应该使用自动装箱和拆箱?仅在引用类型和基元之间存在“阻抗不匹配”时才使用它们,例如,当您必须将数值放入集合时。将自动装箱和拆箱用于科学计算或其他对性能敏感的数字代码是不合适的。 Integer不能替代int; autoboxing和unboxing模糊了原始类型和引用类型之间的区别,但它们并没有消除它。
What oracle has to say on the subject.
请注意,文档不提供带有'='运算符的任何示例。
答案 13 :(得分:0)
请注意,Java 1.5中的缓存范围是-128到127,但是Java 1.6以上是默认范围,即您可以通过传递 -XX:AutoBoxCacheMax = new_limit 来设置上限值&gt; = 127 strong>来自命令行
答案 14 :(得分:-1)
这是因为您使用new
语句来构造对象。
Integer a = Integer.valueOf(10);
Integer b = Integer.valueOf(10);
System.out.println("a == b: " + (a == b));
这将打印出true
。很奇怪,但是Java。