Is Integer Immutable

102

Wiem, że jest to prawdopodobnie bardzo głupie, ale wiele miejsc twierdzi, że klasa Integer w Javie jest niezmienna, ale następujący kod:

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

Wykonuje się bez żadnych problemów dając (oczekiwany) wynik 6. W efekcie wartość a uległa zmianie. Czy to nie znaczy, że liczba całkowita jest zmienna? Drugie pytanie i trochę offtopic: „Niezmienne klasy nie potrzebują konstruktorów kopiujących”. Czy ktoś chce wyjaśnić, dlaczego?

K.Steff
źródło
12
Klasa jest niezmienna, ale autoboxing sprawia, że ​​dzieją się dziwne rzeczy: stackoverflow.com/questions/3085332/ ...
wkl
Dzięki, boks to słowo kluczowe, którego potrzebowałem w Google :)
K.Steff,
7
Mylisz wartość niezmienną z wartością końcową lub stałą.
Code Enthusiastic

Odpowiedzi:

95

Niezmienność nie oznacza, że anigdy nie może równać się innej wartości. Na przykład Stringjest niezmienna, ale nadal mogę to zrobić:

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

strnie został zmieniony, strjest teraz całkowicie nowo utworzonym obiektem, tak jak Twój Integer. Tak więc wartość anie uległa mutacji, ale została zastąpiona przez zupełnie nowy obiekt tj new Integer(6).

Travis Webb
źródło
14
„Dzieje się tak, ponieważ str jest teraz całkowicie nowo utworzonym obiektem”. Lub raczej str (a varibale) wskazuje na nowy obiekt. Sam obiekt nie jest zmienny, ale ponieważ zmienna nie jest ostateczna, może wskazywać na inny obiekt.
Sandman,
Tak, wskazuje na inny obiekt, którego wystąpienie zostało utworzone w wyniku +=operacji.
Travis Webb,
11
Ściśle mówiąc, nie musi to być nowy przedmiot. Używa boksu Integer.valueOf(int)i ta metoda utrzymuje pamięć podręczną Integerobiektów. Więc wynik +=na Integerzmiennej może być obiektem, który wcześniej istniał (albo może to być nawet ten sam obiekt ... w przypadku a += 0).
Stephen C
1
Dlaczego JavaDoc for String wyraźnie podaje, że jest niezmienny, a JavaDoc for Integer nie? Ta różnica jest powodem, dla którego czytam to Pytanie ...
cellepo
52

ajest "odniesieniem" do jakiejś liczby całkowitej (3), twój skrót a+=bnaprawdę oznacza robienie tego:

a = new Integer(3 + 3)

Więc nie, liczby całkowite nie są zmienne, ale zmienne, które na nie wskazują, to *.

* Można mieć niezmienne zmienne, są one oznaczone słowem kluczowym final, co oznacza, że ​​odniesienie nie może się zmienić.

final Integer a = 3;
final Integer b = 3;
a += b; // compile error, the variable `a` is immutable, too.
Mark Elliot
źródło
20

Możesz określić, że obiekt zmienił się za pomocą System.identityHashCode()(lepszym sposobem jest użycie zwykłego, ==ale nie jest tak oczywiste, że zmieniło się odniesienie, a nie wartość)

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

wydruki

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

Możesz zobaczyć, że podstawowy „id” obiektu, ado którego odwołuje się, zmienił się.

Peter Lawrey
źródło
1
System.identityHashCode () to bardzo dobra wskazówka. Dzięki za to.
Ad Infinitum
11

Na zadane pierwsze pytanie:

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

Liczba całkowita jest niezmienna, więc to, co wydarzyło się powyżej, to „a” zmieniło się na nowe odniesienie o wartości 6. Wartość początkowa 3 pozostaje bez odniesienia w pamięci (nie została zmieniona), więc może zostać usunięta.

Jeśli tak się stanie z ciągiem, pozostanie on w puli (w przestrzeni PermGen) przez dłuższy okres niż liczby całkowite, ponieważ oczekuje, że będą miały odniesienia.

Harsha
źródło
8

Tak Liczba całkowita jest niezmienna.

A to odniesienie, które wskazuje na obiekt. Po uruchomieniu + = 3, to ponownie przypisuje A do nowego obiektu Integer z inną wartością.

Nigdy nie modyfikowałeś oryginalnego obiektu, a raczej wskazałeś odniesienie do innego obiektu.

Przeczytaj o różnicy między obiektami a odniesieniami tutaj .

Andy
źródło
Prosto i łatwo powiedziane w języku laika, ze wszystkimi innymi złożonymi wyjaśnieniami :)
Roshan Fernando
5

Niezmienność nie oznacza, że ​​nie można zmienić wartości zmiennej. Oznacza to po prostu, że każde nowe przypisanie tworzy nowy obiekt (przypisuje mu nowe miejsce w pamięci), a następnie zostaje mu przypisana wartość.

Aby samemu to zrozumieć, wykonaj przypisanie liczb całkowitych w pętli (z zadeklarowaną liczbą całkowitą poza pętlą) i spójrz na obiekty na żywo w pamięci.

Powodem, dla którego konstruktor kopiujący nie jest potrzebny dla niezmiennych obiektów, jest prosty zdrowy rozsądek. Ponieważ każde przypisanie tworzy nowy obiekt, z technicznego punktu widzenia język tworzy już kopię, więc nie musisz tworzyć kolejnej kopii.

uncaught_exceptions
źródło
2

„Niezmienne klasy nie potrzebują konstruktorów kopiujących”. Czy ktoś chce wyjaśnić, dlaczego?

Powodem jest to, że rzadko istnieje potrzeba kopiowania (lub nawet jakiegokolwiek sensu kopiowania) instancji niezmiennej klasy. Kopia obiektu powinna być „taka sama jak” oryginał, a jeśli jest taka sama, nie powinno być potrzeby jej tworzenia.

Istnieją jednak pewne podstawowe założenia:

  • Zakłada się, że aplikacja nie nadaje żadnego znaczenia tożsamości obiektów instancji klasy.

  • Zakłada, że ​​klasa została przeciążona equalsi hashCodekopia instancji byłaby „taka sama jak” oryginał… zgodnie z tymi metodami.

Jedno lub oba z tych założeń mogą być fałszywe, a to może uzasadniać dodanie konstruktora kopiującego.

Stephen C.
źródło
1

Oto jak rozumiem niezmienne

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

Gdyby int mógł się zmutować, "a" wypisałoby 8, ale nie, ponieważ jest niezmienne, dlatego jest równe 3. Twój przykład jest po prostu nowym przypisaniem.

Ndheti
źródło
0

Mogę wyjaśnić, że Integer (i inne jego wyznania, takie jak Float, Short itp.) Są niezmienne za pomocą prostego przykładowego kodu:

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

}

Aktualny rezultat

Wynik to on Hi There 100 zamiast oczekiwanego wyniku (w przypadku gdy zarówno sb, jak i i są obiektami mutowalnymi) Hi There 1000

To pokazuje, że obiekt utworzony przez i w main nie jest modyfikowany, podczas gdy sb jest modyfikowany.

Więc StringBuilder zademonstrował zmienne zachowanie, ale nie Integer.

Więc liczba całkowita jest niezmienna. Stąd udowodnione

Kolejny kod bez tylko liczby całkowitej:

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


}
Ashutosh Nigam
źródło
Robisz dwie różne rzeczy - próbujesz ponownie przypisać liczbę całkowitą i wywołać metodę w programie budującym ciągi. Jeśli tak, private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }to sbpozostaje bez zmian.
MT0
Dodałem StringBuilder (który jest zmienny), aby po prostu zestawić Integer z innym obiektem, który jest zmienny. Jeśli chcesz, możesz usunąć cały kod związany z StringBuilder i po prostu wydrukować i, aby zobaczyć 100.
Ashutosh Nigam
Nie dowodzi to niezmienności - wszystko, co robisz, to tylko ponowne haszowanie tego przykładu, pokazując, że Java używa przekazywanych wartości (i że wartości przekazywane dla obiektów są wskaźnikami).
MT0
Wypróbujprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0
@ MT0 Po przekazaniu przez wartość StringBuilder nadal wskazuje na ten sam obiekt, ale Integer przekazuje nową kopię, a nie odwołanie do tego samego obiektu. Jeśli drukujesz w doInteger, wyświetlasz kopię posiadaną przez funkcję, a nie funkcję główną. Chcemy sprawdzić, czy obiekt wskazany przez i w main jest taki sam, czy nie. Mam nadzieję, że to wyjaśnia koncepcje :) Również niezmienną wersją StringBuildera jest String. Daj mi znać, jeśli chcesz, żebym udostępnił jego próbkę.
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
}

Wynik to:

Cześć Pa 1000 5000 1000 5000 d a

Więc char jest zmienna, String Integer i int są niezmienne.

özgür arslan
źródło
1
Ta odpowiedź nie zawiera żadnych informacji od innych.
Giulio Caccin