我知道这可能很愚蠢,但很多地方声称Java中的Integer类是不可变的,然而以下代码:
Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);
执行没有问题,得到了(预期的)结果6。所以a的值实际上已经改变了。这是否意味着Integer是可变的?第二个问题有点离题:“不可变类不需要复制构造函数”。有人能解释一下为什么吗?
我知道这可能很愚蠢,但很多地方声称Java中的Integer类是不可变的,然而以下代码:
Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);
执行没有问题,得到了(预期的)结果6。所以a的值实际上已经改变了。这是否意味着Integer是可变的?第二个问题有点离题:“不可变类不需要复制构造函数”。有人能解释一下为什么吗?
Immutable并不意味着a
永远不能等于另一个值。例如,String
也是不可变的,但我仍然可以这样做:
Immutable并不代表a
永远不可能等于其他值。例如,String
也是不可变的,但我仍然可以执行以下操作:
String str = "hello";
// str equals "hello"
str = str + "world";
// now str equals "helloworld"
str
并未改变,相反str
现在是一个全新实例化的对象,就像你的Integer
一样。因此,a
的值没有发生变化,但它被替换为一个全新的对象,即new Integer(6)
。
+=
操作实例化的结果。 - Travis WebbInteger.valueOf(int)
方法并维护一个 Integer
对象的缓存。因此,在 Integer
变量上执行 +=
操作的结果可能是先前存在的对象(或者甚至可能是相同的对象……在 a += 0
的情况下)。 - Stephen Ca
是指向整数3的“引用”,你的简写 a+=b
实际上意味着执行以下操作:
a = new Integer(3 + 3)
所以,整数是不可变的,但指向它们的变量是可变的*。
*可以有不可变的变量,这些变量用关键字final
表示,这意味着引用不能更改。
final Integer a = 3;
final Integer b = 3;
a += b; // compile error, the variable `a` is immutable, too.
你可以使用 System.identityHashCode()
确定对象是否已更改(更好的方法是使用普通的 ==
,但不太明显是引用而不是值已更改)
Integer a = 3;
System.out.println("before a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));
a += 3;
System.out.println("after a +=3; a="+a+" id="+Integer.toHexString(System.identityHashCode(a)));
打印
before a +=3; a=3 id=70f9f9d8
after a +=3; a=6 id=2b820dda
您可以看到对象a
所引用的底层"id"已经发生了改变。
对于最初提出的问题,
Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);
整数是不可变的,因此上面发生的情况是'a'已经改变成了一个新的值为6的引用。初始值3在内存中没有引用(它未被更改),因此可以进行垃圾回收。
如果这种情况发生在字符串上,它将在池中保留(在PermGen空间中)的时间比整数长,因为它期望有引用。
是的,整数是不可变的。
A是一个指向对象的引用。当您运行a += 3时,它会重新分配A以引用一个新的整数对象,并且该对象具有不同的值。
您从未修改过原始对象,而是将引用指向了另一个对象。
请阅读这里关于对象和引用之间的区别。
Immutable并不意味着您不能为变量更改值。它只是意味着任何新的赋值都会创建一个新对象(分配一个新的内存位置),然后将值赋给它。
为了自己理解这一点,在循环中执行整数赋值(使用在循环外声明的整数),并查看内存中的实时对象。
为什么不需要为不可变对象编写复制构造函数是因为这是常识。由于每个赋值都创建一个新对象,语言在技术上已经创建了一个副本,因此您不必创建另一个副本。
我对不可变对象的理解如下:
int a=3;
int b=a;
b=b+5;
System.out.println(a); //this returns 3
System.out.println(b); //this returns 8
public static void main(String[] args) {
// TODO Auto-generated method stub
String s1="Hi";
String s2=s1;
s1="Bye";
System.out.println(s2); //Hi (if String was mutable output would be: Bye)
System.out.println(s1); //Bye
Integer i=1000;
Integer i2=i;
i=5000;
System.out.println(i2); // 1000
System.out.println(i); // 5000
int j=1000;
int j2=j;
j=5000;
System.out.println(j2); // 1000
System.out.println(j); // 5000
char c='a';
char b=c;
c='d';
System.out.println(c); // d
System.out.println(b); // a
}
输出结果为:
嗨 再见 1000 5000 1000 5000 d a
因此,char是可变的,而String、Integer和int是不可变的。
我可以通过简单的示例代码来说明 Integer(以及其他类似 Float、Short 等)是不可变的:
示例代码
public class Test{
public static void main(String... args){
Integer i = 100;
StringBuilder sb = new StringBuilder("Hi");
Test c = new Test();
c.doInteger(i);
c.doStringBuilder(sb);
System.out.println(sb.append(i)); //Expected result if Integer is mutable is Hi there 1000
}
private void doInteger(Integer i){
i=1000;
}
private void doStringBuilder(StringBuilder sb){
sb.append(" there");
}
}
实际结果
结果显示为Hi There 100,而不是预期结果(在sb和i都是可变对象的情况下)Hi There 1000
这表明在主函数中创建的i对象没有被修改,而sb对象被修改了。public class Test{
public static void main(String... args){
Integer i = 100;
Test c = new Test();
c.doInteger(i);
System.out.println(i); //Expected result is 1000 in case Integer is mutable
}
private void doInteger(Integer i){
i=1000;
}
}
private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }
那么 sb
不会改变。 - MT0private void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
- MT0