是整数不可变


100

我知道这可能很愚蠢,但是很多地方都声称Java中的Integer类是不可变的,但是下面的代码:

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

毫无困难地执行(给出预期的结果)6。因此有效地改变了a的值。这不是说Integer是可变的吗?第二个问题和一点题外话:“不变的类不需要复制构造函数”。有人在乎解释原因吗?


12
类是不可变的,但自动装箱正在时髦的事情发生了:stackoverflow.com/questions/3085332/...
WKL

谢谢,拳击是我需要谷歌关键字:)
K.Steff 2011年

7
您将不可变与最终值或常数混淆。
热情的Code

Answers:


94

不可变并不意味着a永远不能等于另一个值。例如,String也是不可变的,但我仍然可以这样做:

String str = "hello";
// str equals "hello"
str = str + "world";
// now str equals "helloworld"

str并没有改变,而是变成str了一个完全新实例化的对象,就像您一样Integer。因此,的值a没有发生变化,而是由一个全新的对象代替new Integer(6)


14
“这是因为str现在是一个完全新实例化的对象”。或者,str(一个变量)指向一个新对象。对象本身不是可变的,但是由于变量不是最终变量,因此可以指向其他对象。
桑德曼

是的,它指向另一个由于+=操作而实例化的对象。
Travis Webb

11
严格来说,它不必对象。拳击使用Integer.valueOf(int)和那个方法维护Integer对象的缓存。因此,+=on Integer变量的结果可能是先前存在的对象(或者甚至可能是同一对象…… a += 0)。
Stephen C

1
为什么JavaDoc for String明确表示其不可变,而JavaDoc for Integer却没有呢?这种差异是为什么我要阅读此问题的原因……
cellepo

50

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.

18

您可以使用确定对象是否已更改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” 已更改。


System.identityHashCode()是一个很好的技巧。谢谢你
Ad Infinitum'3

10

对于最初提出的问题,

Integer a=3;
Integer b=3;
a+=b;
System.out.println(a);

整数是不可变的,因此上面发生的是'a'已更改为值6的新引用。初始值3在内存中不带任何引用(未更改),因此可以进行垃圾回收。

如果这发生在字符串上,则它将在池中(在PermGen空间中)保留的时间长于整数,因为它希望具有引用。


8

是整数是不可变的。

A是指向对象的参考。当您运行+ = 3时,它将重新分配A来引用具有不同值的新Integer对象。

您从未修改原始对象,而是将引用指向了另一个对象。

此处阅读有关对象和引用之间的区别。


简单和轻松地用外行语言讲,还有所有其他复杂的解释:)
Roshan Fernando

5

不可变并不意味着您不能更改变量的值。这只是意味着任何新分配都会创建一个新对象(为它分配一个新的内存位置),然后将值分配给它。

要自己了解这一点,请在循环中执行Integer分配(在循环外部声明整数),然后查看内存中的活动对象。

不可变对象不需要复制构造函数的原因很简单。由于每个分配都会创建一个新对象,因此该语言在技术上已经创建了一个副本,因此您不必创建另一个副本。


2

“不变的类不需要复制构造函数”。有人在乎解释原因吗?

原因是几乎不需要复制(甚至复制中的任何点)不可变类的实例。对象的副本应与原始对象“相同”,并且如果相同,则无需创建它。

但是有一些基本的假设:

  • 它假定您的应用程序对类实例的对象标识没有任何意义。

  • 根据这些方法,它假定该类已重载equalshashCode因此实例的副本将与原始副本“相同”。

这些假设中的一个或两个都可能是错误的,并且可能需要添加复制构造函数。


1

这就是我理解不变的方式

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。您的示例只是一个新的赋值。


0

我可以通过简单的示例代码明确指出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在main中创建的对象未修改,而sb已修改。

因此,StringBuilder表现出可变的行为,但没有表现出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;
    }


}

您正在做两件事-尝试重新分配整数并在stringbuilder上调用方法。如果您这样做,private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }sb保持不变。
MT0

我添加了StringBuilder(可变的)只是将Integer与另一个可变的对象并置。如果你愿意,你可以删除所有的StringBuilder相关的代码,只是打印输出我看到100
Ashutosh说Nigam公司

这并不能证明不变性-您要做的只是重新哈希 这个例子表明Java使用传递的值(和通过了对象的值是指针)。
MT0

试试private void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0

@ MT0当您按值传递时,StringBuilder仍指向相同的对象,但Integer传递的新副本未引用相同的对象。如果在doInteger中打印输出,则显示的是该功能而非主要功能所拥有的副本。我们想看看i在main中指向的对象是否相同。希望它能清除概念:) StringBuilder的不可变版本也是String。让我知道是否要我分享它的样本。
Ashutosh Nigam

-1
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是不可变的。


1
该答案不提供其他任何信息。
朱利奥·卡钦
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.