Tamsayı Değiştirilemez mi


103

Bunun muhtemelen çok aptalca olduğunu biliyorum, ancak birçok yer Java'daki Integer sınıfının değişmez olduğunu iddia ediyor, ancak şu kod:

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

(Beklenen) sonucu verirken herhangi bir sorun yaşamadan yürütür 6. Yani etkili bir şekilde a'nın değeri değişmiştir. Bu, Tamsayının değiştirilebilir olduğu anlamına gelmez mi? İkincil soru ve biraz konu dışı: "Değişmez sınıfların kopya oluşturuculara ihtiyacı yoktur". Nedenini açıklamak isteyen var mı?


13
Sınıf değişmez, ancak otomatik kutulama acayip şeylerin olmasını sağlıyor: stackoverflow.com/questions/3085332/…
wkl

Teşekkürler, boks google için ihtiyacım olan anahtar
kelimeydi

7
Değişmezliği nihai veya sabit bir değerle karıştırıyorsunuz.
Code Enthusiastic

Yanıtlar:


95

Değişmez, abaşka bir değere asla eşit olamayacağı anlamına gelmez . Örneğin String, değişmez de, ama yine de yapabilirim:

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

strdeğiştirilmedi, daha ziyade strartık tamamen yeni örneklenmiş bir nesnedir, tıpkı sizin Integerolduğu gibi. Yani değeri adeğişmedi, ancak tamamen yeni bir nesneyle değiştirildi, yani new Integer(6).


14
"Bunun nedeni, str artık tamamen yeni bir örneklenmiş nesne olmasıdır". Ya da str (bir varibale) yeni bir nesneye işaret eder. Nesnenin kendisi değiştirilebilir değildir, ancak değişken nihai olmadığı için farklı bir nesneye işaret edebilir.
Sandman

Evet, +=işlemin bir sonucu olarak somutlaştırılmış farklı bir nesneye işaret ediyor .
Travis Webb

11
Kesin konuşmak gerekirse, gerek bir olmak yeni nesnesi. Boxing kullanır Integer.valueOf(int)ve bu yöntem Integernesnelerin bir önbelleğini tutar . Dolayısıyla, +=bir Integerdeğişkenin sonucu daha önce var olan bir nesne olabilir (veya olması durumunda aynı nesne bile olabilir a += 0).
Stephen C

1
Neden JavaDoc for String açıkça değişmez olduğunu söylerken Tamsayı için JavaDoc bunu söylemiyor? İşte bu fark bu Soruyu okuyorum ...
cellepo

52

abir Tamsayı (3) için bir "referans" ise, kısaltmanız a+=bgerçekten şunu yapın:

a = new Integer(3 + 3)

Yani hayır, Tamsayılar değiştirilebilir değil, ancak onları gösteren değişkenler *.

* Değişmez değişkenlere sahip olmak mümkündür, bunlar anahtar kelime ile gösterilir, bu da finalreferansın değişmeyebileceği anlamına gelir.

final Integer a = 3;
final Integer b = 3;
a += b; // compile error, the variable `a` is immutable, too.

20

Kullanarak nesnenin değiştiğini belirleyebilirsiniz System.identityHashCode()(Daha iyi bir yol, sade kullanmaktır, ==ancak değerden ziyade referansın değiştiği açık değildir)

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)));

baskılar

before a +=3; a=3 id=70f9f9d8
after a +=3; a=6 id=2b820dda

Başvurduğu nesnenin temelindeki "id" anin değiştiğini görebilirsiniz.


1
System.identityHashCode () çok iyi bir ipucu. Bunun için teşekkürler.
Ad Infinitum

11

İlk sorulan soruya,

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

Tamsayı değişmezdir, bu nedenle yukarıda olan 'a' değer 6'nın yeni bir referansına değişmiştir. Başlangıç ​​değeri 3, bellekte referans olmadan bırakılır (değiştirilmemiştir), bu nedenle çöp toplanabilir.

Bu bir String'e gelirse, havuzda (PermGen alanında) referanslara sahip olmayı beklediğinden Tamsayılardan daha uzun süre kalacaktır.


9

Evet Tamsayı değişmezdir.

A, bir nesneye işaret eden bir referanstır. Bir + = 3'ü çalıştırdığınızda, bu, A'yı farklı bir değerle yeni bir Tamsayı nesnesine başvurmak için yeniden atar.

Orijinal nesneyi hiçbir zaman değiştirmediniz, bunun yerine referansı farklı bir nesneye yönlendirdiniz.

Burada nesneler ve referanslar arasındaki farkı okuyun .


Bir meslekten olmayan kişinin dilinde, diğer tüm karmaşık açıklamalarla birlikte basit ve kolay bir şekilde söylenebilir :)
Roshan Fernando

5

Değişmez, bir değişkenin değerini değiştiremeyeceğiniz anlamına gelmez. Bu, herhangi bir yeni atamanın yeni bir nesne oluşturduğu (ona yeni bir bellek konumu atadığı) ve ardından değerin ona atandığı anlamına gelir.

Bunu kendiniz için anlamak için, bir döngüde Tamsayı ataması yapın (tamsayı döngü dışında bildirilmiş olarak) ve bellekteki canlı nesnelere bakın.

Değişmez nesneler için kopya yapıcıya ihtiyaç duyulmamasının nedeni basit sağduyu. Her ödev yeni bir nesne oluşturduğundan, dil teknik olarak zaten bir kopya oluşturur, böylece başka bir kopya oluşturmanız gerekmez.


2

"Değişmez sınıfların kopya oluşturuculara ihtiyacı yoktur". Nedenini açıklamak isteyen var mı?

Nedeni, nadiren olması değişmez bir sınıfın bir örneğini kopyalamaya (hatta kopyalamada herhangi bir noktaya) ihtiyaç duyulmasıdır. Nesnenin kopyası orijinali ile "aynı" olmalı ve aynıysa onu yaratmaya gerek olmamalıdır.

Yine de bazı temel varsayımlar var:

  • Uygulamanızın, sınıfın örneklerinin nesne kimliğine herhangi bir anlam yüklemediğini varsayar.

  • Bu yöntemlere göre, sınıfın aşırı yüklendiğini equalsve hashCodeböylece bir örneğin kopyasının orijinal ile "aynı" olacağını varsayar .

Ya ya da o varsayımların her ikisi olabilir yanlış olabilir ve bu bir kopya kurucu eklenmesini garanti olabilir.


1

Ben değişmezi böyle anlıyorum

int a=3;    
int b=a;
b=b+5;
System.out.println(a); //this returns 3
System.out.println(b); //this returns 8

Eğer int mutasyona uğrayabilseydi, "a" 8 basardı ama değişmez olduğu için değil, bu yüzden 3. Örneğiniz sadece yeni bir atama.


0

Integer'ın (ve Float, Short gibi diğer inancının) basit bir örnek kodla değiştirilemez olduğunu netleştirebilirim:

Basit kod

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");
    }

}

Gerçek sonuç

Sonuç beklenen sonuç yerine Hi There 100'e gelir (hem sb hem de i değişken nesneler olması durumunda) Hi There 1000

Bu, temelde i tarafından oluşturulan nesnenin değiştirilmediğini, sb'nin değiştirildiğini gösterir.

Dolayısıyla StringBuilder değişebilir davranış gösterdi ancak Tamsayı göstermedi.

Yani Tamsayı Değişmezdir. Dolayısıyla Kanıtlandı

Yalnızca Tamsayı olmayan başka bir kod:

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;
    }


}

İki farklı şey yapıyorsunuz - tamsayıyı yeniden atamaya çalışmak ve dizge oluşturucuda bir yöntemi çağırmak. Eğer yaparsanız, private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }o sbzaman değişmez.
MT0

Tamsayı'yı değiştirilebilir başka bir nesneyle yan yana koymak için StringBuilder (değiştirilebilir) ekledim. İsterseniz StringBuilder ile ilgili tüm kodları silebilir ve sadece 100'ü görmek için i çıktısını alabilirsiniz.
Ashutosh Nigam

Bu değişmezliği kanıtlamaz - yaptığınız tek şey, Java'nın değerle geçiş kullandığını (ve nesneler için iletilen değerlerin işaretçiler olduğunu) gösteren bu örneği yeniden hashing yapmaktır .
MT0

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

@ MT0 Değere göre geçtiğinizde, StringBuilder hala aynı nesneyi işaret ediyor, ancak Tamsayı aynı nesneye başvurmadan yeni kopya geçiriyor. DoInteger ile çıktı alırsanız, ana işlevin değil işlevin sahip olduğu kopyayı görüntülemiş olursunuz. Temelde i ile gösterilen nesnenin aynı olup olmadığını görmek istiyoruz. Umarım kavramları temizler :) Ayrıca StringBuilder'ın değişmez versiyonu String'dir. Bunun için bir örnek paylaşmamı isterseniz bana bildirin.
Ashutosh Nigam

0
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
}

Çıktı:

Merhaba Bye 1000 5000 1000 5000 d a

Öyleyse char değiştirilebilir, String Integer ve int değişmez.


1
Bu cevap diğerlerinden herhangi bir bilgi sağlamaz.
Giulio Caccin
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.