Is Integer Immutable

102

Saya tahu ini mungkin sangat bodoh, tetapi banyak tempat mengklaim bahwa kelas Integer di Java tidak dapat diubah, namun kode berikut:

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

Melaksanakan tanpa kesulitan memberikan hasil (yang diharapkan) 6. Jadi secara efektif nilai a telah berubah. Bukankah itu berarti Integer bisa berubah? Pertanyaan sekunder dan sedikit offtopic: "Kelas yang tidak berubah tidak membutuhkan konstruktor salinan". Ada yang mau menjelaskan kenapa?

K.Steff
sumber
12
Kelas tidak dapat diubah, tetapi autoboxing membuat hal-hal yang funky terjadi: stackoverflow.com/questions/3085332/…
wkl
Terima kasih, tinju adalah kata kunci yang saya butuhkan untuk google :)
K.Steff
7
Anda membingungkan tidak dapat diubah dengan nilai akhir atau konstan.
Code Enthusiastic

Jawaban:

95

Kekekalan tidak berarti bahwa atidak pernah bisa menyamai nilai lain. Misalnya, Stringtidak dapat diubah juga, tetapi saya masih bisa melakukan ini:

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

strtidak diubah, melainkan strsekarang menjadi objek yang sama sekali baru dibuat, sama seperti milik Anda Integer. Jadi nilai atidak bermutasi, tetapi diganti dengan objek yang sama sekali baru, yaitu new Integer(6).

Travis Webb
sumber
14
"Ini karena str sekarang adalah objek yang sama sekali baru dibuat". Atau, lebih tepatnya, str (a varibale) menunjuk ke objek baru. Objek itu sendiri tidak bisa berubah, tetapi karena variabel belum final, itu bisa menunjuk ke objek yang berbeda.
Sandman
Ya, ini menunjuk ke objek berbeda yang dipakai sebagai hasil dari +=operasi.
Travis Webb
11
Sebenarnya, itu tidak perlu menjadi objek baru . Tinju menggunakan Integer.valueOf(int)dan metode itu memelihara cache Integerobjek. Jadi hasil dari +=sebuah Integervariabel bisa jadi objek yang sebelumnya ada (atau bahkan bisa jadi objek yang sama ... dalam kasus a += 0).
Stephen C
1
Mengapa JavaDoc untuk String secara eksplisit mengatakan itu tidak dapat diubah, tetapi JavaDoc untuk Integer tidak? Perbedaan itulah mengapa saya membaca Pertanyaan ini ...
cellepo
52

aadalah "referensi" ke beberapa Integer (3), singkatan Anda a+=bbenar-benar berarti melakukan ini:

a = new Integer(3 + 3)

Jadi tidak, bilangan bulat tidak bisa diubah, tetapi variabel yang mengarah padanya adalah *.

* Ada kemungkinan untuk memiliki variabel yang tidak dapat diubah, ini dilambangkan dengan kata kunci final, yang berarti bahwa referensi tidak dapat berubah.

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

Anda dapat menentukan bahwa objek telah berubah menggunakan System.identityHashCode()(Cara yang lebih baik adalah menggunakan biasa ==namun tidak begitu jelas bahwa referensi daripada nilainya telah berubah)

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

cetakan

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

Anda dapat melihat "id" yang mendasari objek yang adirujuk telah berubah.

Peter Lawrey
sumber
1
System.identityHashCode () adalah tip yang sangat bagus. Terima kasih untuk ini.
Ad Infinitum
11

Untuk pertanyaan awal yang diajukan,

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

Integer tidak dapat diubah, jadi yang terjadi di atas adalah 'a' telah berubah menjadi referensi baru dengan nilai 6. Nilai awal 3 dibiarkan tanpa referensi di memori (belum diubah), sehingga dapat dikumpulkan sampahnya.

Jika ini terjadi pada String, String akan disimpan di pool (dalam ruang PermGen) untuk jangka waktu yang lebih lama daripada Integer karena diharapkan memiliki referensi.

Harsha
sumber
8

Ya, Integer tidak dapat diubah.

A adalah referensi yang menunjuk ke suatu objek. Ketika Anda menjalankan + = 3, itu menetapkan kembali A untuk mereferensikan objek Integer baru, dengan nilai yang berbeda.

Anda tidak pernah memodifikasi objek aslinya, melainkan Anda mengarahkan referensi ke objek lain.

Baca tentang perbedaan antara objek dan referensi di sini .

Andy
sumber
Sederhana dan mudah diucapkan dalam bahasa awam, dengan semua penjelasan rumit lainnya di luar sana :)
Roshan Fernando
5

Tidak dapat diubah tidak berarti bahwa Anda tidak dapat mengubah nilai variabel. Ini hanya berarti bahwa setiap tugas baru membuat objek baru (menugaskannya ke lokasi memori baru) dan kemudian nilainya ditetapkan ke sana.

Untuk memahami ini sendiri, lakukan tugas Integer dalam satu loop (dengan integer dideklarasikan di luar loop) dan lihat objek langsung dalam memori.

Alasan mengapa konstruktor salinan tidak diperlukan untuk objek yang tidak dapat diubah adalah akal sehat yang sederhana. Karena setiap tugas membuat objek baru, bahasa secara teknis sudah membuat salinan, jadi Anda tidak perlu membuat salinan lain.

uncaught_exceptions
sumber
2

"Kelas yang tidak dapat diubah tidak memerlukan pembuat salinan". Ada yang mau menjelaskan kenapa?

Alasannya adalah bahwa jarang ada kebutuhan untuk menyalin (atau bahkan tujuan apa pun dalam menyalin) sebuah instance dari kelas yang tidak dapat diubah. Salinan objek harus "sama dengan" aslinya, dan jika sama, tidak perlu membuatnya.

Ada beberapa asumsi yang mendasari:

  • Ini mengasumsikan bahwa aplikasi Anda tidak memberi arti apa pun pada identitas objek instance kelas.

  • Ini mengasumsikan bahwa kelas telah kelebihan beban equalsdan hashCodesalinan instance akan menjadi "sama dengan" aslinya ... menurut metode ini.

Salah satu atau kedua asumsi tersebut bisa salah, dan itu mungkin memerlukan penambahan konstruktor salinan.

Stephen C
sumber
1

Inilah cara saya memahami kekekalan

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

Jika int bisa bermutasi, "a" akan mencetak 8 tetapi tidak karena itu tidak berubah, itulah mengapa itu 3. Contoh Anda hanyalah sebuah tugas baru.

Ndheti
sumber
0

Saya dapat menjelaskan bahwa Integer (dan kredo lainnya seperti Float, Short dll) tidak dapat diubah dengan kode contoh sederhana:

Kode sampel

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

}

Hasil Aktual

Hasilnya datang ke he Hi There 100 bukannya hasil yang diharapkan (dalam kasus sb dan saya menjadi objek yang bisa berubah) Hi There 1000

Ini menunjukkan bahwa objek yang dibuat oleh i di main tidak dimodifikasi, sedangkan sb dimodifikasi.

Jadi StringBuilder mendemonstrasikan perilaku yang bisa berubah tetapi bukan Integer.

Jadi Integer Tidak Berubah. Oleh karena itu Terbukti

Kode lain tanpa hanya 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;
    }


}
Ashutosh Nigam
sumber
Anda melakukan dua hal berbeda - mencoba menetapkan ulang integer dan memanggil metode pada stringbuilder. Jika Anda melakukannya private void doStringBuilder(StringBuilder sb){ sb = new StringBuilder(); }maka sbtidak berubah.
MT0
Saya menambahkan StringBuilder (yang bisa berubah) untuk hanya menyandingkan Integer dengan objek lain yang bisa berubah. Jika mau, Anda dapat menghapus semua kode terkait StringBuilder dan hanya mencetak i untuk melihat 100.
Ashutosh Nigam
Ini tidak membuktikan keabadian - yang Anda lakukan hanyalah melakukan hashing ulang pada contoh ini yang menunjukkan bahwa Java menggunakan pass-by-value (dan bahwa nilai yang diteruskan untuk objek adalah pointer).
MT0
Cobaprivate void doInteger(Integer i){ System.out.println( i == 100 ); i=1000; System.out.println( i == 100 ); }
MT0
@ MT0 Ketika Anda melewati nilai StringBuilder masih menunjuk ke objek yang sama tetapi Integer mengirimkan salinan baru yang bukan referensi ke objek yang sama. Jika Anda mencetak di doInteger, Anda menampilkan salinan yang dimiliki oleh fungsi tersebut bukan fungsi utama. Kami ingin melihat apakah objek yang ditunjukkan oleh i di main adalah sama atau tidak. Semoga ini bisa membersihkan konsepnya :) Juga versi tetap dari StringBuilder adalah String. Beri tahu saya jika Anda ingin saya membagikan sampelnya.
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
}

Outputnya adalah:

Hai Bye 1000 5000 1000 5000 d a

Jadi char bisa berubah, String Integer dan int tidak bisa diubah.

özgür arslan
sumber
1
Jawaban ini tidak memberikan informasi apapun dari yang lain.
Giulio Caccin