提问人:K.Steff 提问时间:4/6/2011 最后编辑:James P.K.Steff 更新时间:2/23/2021 访问量:82580
整数是不可变的吗
Is Integer Immutable
问:
我知道这可能非常愚蠢,但很多地方声称 Java 中的 Integer 类是不可变的,但以下代码:
Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);
毫无问题地执行,给出(预期的)结果 6.因此,实际上 a 的值已经发生了变化。这不意味着 Integer 是可变的吗? 第二个问题和一点题外话:“不可变类不需要复制构造函数”。有人愿意解释为什么吗?
答:
a
是对某个 Integer(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.
评论
不可变并不意味着不能更改变量的值。它只是意味着任何新的赋值都会创建一个新对象(为其分配新的内存位置),然后将值分配给它。
要自己理解这一点,请在循环中执行整数赋值(在循环外声明整数)并查看内存中的活动对象。
不可变对象不需要复制构造函数的原因是简单的常识。由于每个赋值都会创建一个新对象,因此从技术上讲,该语言已经创建了一个副本,因此您不必创建另一个副本。
不可变并不意味着它永远不能等于另一个值。例如,也是不可变的,但我仍然可以这样做:a
String
String str = "hello";
// str equals "hello"
str = str + "world";
// now str equals "helloworld"
str
没有被改变,而是现在是一个全新的实例化对象,就像你一样。所以 的值没有改变,而是被一个全新的对象所取代,即 .str
Integer
a
new Integer(6)
评论
+=
Integer.valueOf(int)
Integer
+=
Integer
a += 0
是的,整数是不可变的。
A 是指向对象的引用。当您运行 += 3 时,它会重新分配 A 以引用具有不同值的新 Integer 对象。
您从未修改过原始对象,而是将引用指向其他对象。
在此处了解对象和引用之间的区别。
评论
“不可变类不需要复制构造函数”。有人愿意解释为什么吗?
原因是很少需要复制(甚至复制)不可变类的实例。对象的副本应与原始对象“相同”,如果相同,则无需创建它。
不过,有一些基本假设:
它假定应用程序不会对类实例的对象标识赋予任何意义。
它假定该类已重载,因此实例的副本将与原始副本“相同”。根据这些方法。
equals
hashCode
这些假设中的任何一个或两个都可能是错误的,这可能需要添加一个复制构造函数。
您可以使用以下方法确定对象已更改(更好的方法是使用普通值,但是引用而不是值已更改并不那么明显)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)));
prints
before a +=3; a=3 id=70f9f9d8
after a +=3; a=6 id=2b820dda
You can see the underlying "id" of the object refers to has changed.a
评论
对于最初提出的问题,
Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);
整数是不可变的,所以上面发生的事情是“a”已更改为值为 6 的新引用。初始值 3 在内存中没有引用(未更改),因此可以对其进行垃圾回收。
如果这种情况发生在 String 上,它将在池中(在 PermGen 空间中)保留的时间比 Integers 长,因为它希望有引用。
我可以通过简单的示例代码明确指出 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");
}
}
实际结果
结果是他 嗨,有 100 而不是预期结果(如果 sb 和 i 都是可变对象) 嗨,有 1000
这表明 main 中 i 创建的对象未被修改,而 sb 被修改。
因此,StringBuilder 演示了可变行为,但演示了 Integer。
所以整数是不可变的。因此证明
另一个没有 Integer 的代码:
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
private void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
这就是我对不可变的理解
int a=3;
int b=a;
b=b+5;
System.out.println(a); //this returns 3
System.out.println(b); //this returns 8
如果 int 可以变异,“a”会打印 8,但它不会,因为它是不可变的,这就是为什么它是 3。你的例子只是一个新任务。
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
}
Output is :
Hi Bye 1000 5000 1000 5000 d a
So char is mutable , String Integer and int are immutable.
评论
复制并运行此代码,希望这能解答您的所有疑问
private static void wrapperClassDemo() {
//checking wrapper class immutability using valueOf method
//We can create wrapper class by using either "new" keyword or using a static method "valueOf()"
//The below Example clarifies the immutability concept of wrapper class in detail
//For better understanding just ciopy the below code to the editor and run
Integer num1 =Integer.valueOf(34); // i'm passing the 34 as the parameter to the valueOf method
System.out.println("value assigned to num1 is : "+num1);
System.out.println("Printing the hashcode assigned to store the \" num1 \"value in memory: "+System.identityHashCode(num1));
Integer num2 =Integer.valueOf(34);
System.out.println("value assigned to num2 is : "+num2);
System.out.println("Printing the hashcode assigned to store the \" num2 \"value in memory: "+System.identityHashCode(num2));
/*Now u can notice both the hashcode value of num1 and num2 are same. that is because once you created the num1 with the value 34 an object is
* created in the heap memory. And now You are passing the value same as num1 to the num2 .Now JVM Checks the same value is present in the heap Mmeomry
* If present the reference variable(in this example it is num2) will be pointed to the same address where the object num1 is stored so u get the same hashcode */
num2++; // You can use num2 = 35 as both are same;
System.out.println("\nvalue assigned to num2 is : "+num2);
System.out.println("Printing the hashcode of \" num1 \": "+System.identityHashCode(num1) + "\nPrinting the hashcode of \" num2 \": "+System.identityHashCode(num2));
System.out.println("As now you can notice the hashcode has changed for num2 ,That is because now a new object is created for num2 and it is referencing the new object");
//Again i'm incrementing num2
System.out.println("\nBefore incremeting the hashcode of \" num2 \" is: "+System.identityHashCode(num2));
num2++; // You can use num2 = 36 as both are same;
System.out.println("After incremeting the hashcode of \" num2 \" is: "+System.identityHashCode(num2));
//now the hashcode value of num2 is changed ,again new object is created for the updated value and num2 is referencing new object ,and old object will be garbage collected
System.out.println("\n Thus the CONCLUSION is Wrapper objects are immutable ,They only create new object and refernce the new object ,They won't modify the present object ");
System.out.println("This is applicable for Strings also");
需要记住的是,现在有一个整数值的缓存。它有时可以使开发人员免于在从 int 更改为 Integer 时使用 == 而不是 .equals() 的错误。但并非总是如此。当您实例化一个新的 Integer 时,将创建一个新实例。因此,整数不仅是不可变的,而且是半静态的。
Integer a = 3;
Integer b = 3;
Integer c = new Integer(3);
b = b + 1;
b = b - 1;
System.out.println("a-id: " + System.identityHashCode(a));
System.out.println("b-id: " + System.identityHashCode(b));
System.out.println("c-id: " + System.identityHashCode(c));
System.out.println("a == b: " + (a == b));
System.out.println("a == c: " + (a == c));
System.out.println("a eq c: " + (a.equals(c)));
给出打印输出:
a-id: 666988784
b-id: 666988784
c-id: 1414644648
a == b: true
a == c: false
a eq c: true
评论