Apakah benda yang tidak dapat diubah itu?
Objek String tidak dapat diubah, tetapi itu berarti Anda tidak dapat mengubah nilainya dengan memanggil metode publiknya.
Seperti yang kita ketahui bersama, di Java, kelas String tidak dapat diubah. Jadi, apa sebenarnya objek yang tidak dapat diubah itu? Anda dapat memikirkannya seperti ini: Jika suatu objek tidak dapat mengubah keadaannya setelah dibuat, maka objek tersebut tidak dapat diubah. Status tidak dapat diubah, artinya variabel anggota dalam objek tidak dapat diubah, termasuk nilai tipe data dasar. Variabel tipe referensi tidak dapat menunjuk ke objek lain, dan status objek yang ditunjuk oleh tipe referensi tidak dapat diubah diubah.
Membedakan antara objek dan referensi objek
Untuk pemula Java, selalu ada keraguan tentang String sebagai objek yang tidak dapat diubah. Lihatlah kode di bawah ini:
String s = "ABCabc";System.out.println("s = " + s);s = "123456";System.out.println("s = " + s);Hasil cetakannya adalah:
s = ABCabc s = 123456
Pertama buat objek String s, lalu biarkan nilai s menjadi "ABCabc", lalu biarkan nilai s menjadi "123456". Terlihat dari hasil cetakannya, nilai s memang mengalami perubahan. Jadi mengapa Anda masih mengatakan bahwa objek String tidak dapat diubah? Faktanya, ada kesalahpahaman di sini: s hanyalah referensi ke objek String, bukan objek itu sendiri. Objek adalah area memori di dalam memori. Semakin banyak variabel anggota, semakin besar ruang yang ditempati area memori tersebut. Referensi hanyalah data 4-byte yang menyimpan alamat objek yang ditunjuknya. Objek tersebut dapat diakses melalui alamat ini.
Dengan kata lain, s hanyalah referensi, yang menunjuk ke objek tertentu. Ketika s="123456"; setelah kode ini dijalankan, objek baru "123456" dibuat, dan referensi s menunjuk ke objek hati ini lagi , objek asli "ABCabc" masih ada di memori dan tidak berubah. Struktur memori ditunjukkan pada gambar di bawah ini:
Salah satu perbedaan antara Java dan C++ adalah di Java tidak mungkin mengoperasikan objek itu sendiri secara langsung. Semua objek diarahkan oleh referensi. Anda harus menggunakan referensi ini untuk mengakses objek itu sendiri, termasuk mendapatkan nilai variabel anggota dan mengubahnya variabel anggota objek. Panggil metode objek, dll. Di C++, ada tiga hal: referensi, objek, dan pointer, yang semuanya dapat mengakses objek. Faktanya, referensi di Java dan pointer di C++ secara konseptual serupa, keduanya adalah nilai alamat dari objek yang disimpan di memori. Namun, di Java, referensi kehilangan beberapa fleksibilitas. Misalnya, referensi di Java tidak dapat digunakan seperti Penjumlahan dan Pengurangan dilakukan seperti pointer di C++.
Mengapa objek String tidak dapat diubah?
Untuk memahami kekekalan String, pertama-tama lihat variabel anggota di kelas String. Di JDK1.6, variabel anggota String meliputi:
public final class String mengimplementasikan java.io.Serializable, Comparable<String>, CharSequence{ /** Nilai digunakan untuk penyimpanan karakter. */ private final char value[]; yang digunakan. */ private final int offset; /** Hitungan adalah jumlah karakter dalam String. */ private int count /** Simpan kode hash untuk string */ private int hash; Bawaan ke 0Di JDK1.7, kelas String telah membuat beberapa perubahan, terutama mengubah perilaku ketika metode substring dijalankan, yang tidak terkait dengan topik artikel ini. Hanya ada dua variabel anggota utama kelas String di JDK1.7:
public final class String mengimplementasikan java.io.Serializable, Comparable<String>, CharSequence { /** Nilai digunakan untuk penyimpanan karakter. */ private final char value[]; hash int pribadi; // Defaultnya adalah 0Seperti dapat dilihat dari kode di atas, kelas String di Java sebenarnya merupakan enkapsulasi dari array karakter. Di JDK6, nilai adalah larik yang dienkapsulasi oleh String, offset adalah posisi awal String dalam larik nilai, dan hitungan adalah jumlah karakter yang ditempati oleh String. Di JDK7, hanya ada satu variabel nilai, yaitu semua karakter yang bernilai milik objek String. Perubahan ini tidak mempengaruhi pembahasan artikel ini. Selain itu, terdapat variabel anggota hash yang merupakan cache dari nilai hash objek String. Variabel anggota ini juga tidak relevan dengan pembahasan artikel ini. Di Java, array juga merupakan objek (lihat artikel saya sebelumnya Ciri-ciri Array di Java). Jadi nilai hanyalah referensi, yang menunjuk ke objek array nyata. Faktanya, setelah mengeksekusi kode String s = “ABCabc”;, tata letak memori sebenarnya akan seperti ini:
Ketiga variabel value, offset dan count semuanya bersifat pribadi, dan tidak ada metode publik seperti setValue, setOffset dan setCount yang disediakan untuk mengubah nilai-nilai ini, sehingga String tidak dapat dimodifikasi di luar kelas String. Artinya, setelah diinisialisasi, ia tidak dapat diubah, dan ketiga anggota ini tidak dapat diakses di luar kelas String. Selain itu, ketiga variabel value, offset, dan count semuanya bersifat final, artinya di dalam kelas String, setelah ketiga nilai ini diinisialisasi, ketiga nilai tersebut tidak dapat diubah. Jadi objek String dapat dianggap tidak dapat diubah.
Jadi di dalam String, jelas ada beberapa metode, dan memanggilnya bisa mendapatkan nilai yang diubah. Metode ini termasuk substring, replace, replaceAll, toLowerCase, dll. Misalnya kode berikut:
String a = "ABCabc"; Sistem.keluar.println("a = " + a); a = a.ganti('A', 'a');Hasil cetakannya adalah:
a = ABCabca = aBCabc
Kemudian nilai a sepertinya sudah berubah, namun nyatanya kesalahpahaman yang sama. Sekali lagi, a hanyalah referensi, bukan objek string sebenarnya. Saat memanggil a.replace('A', 'a'), metode ini secara internal akan membuat objek String baru dan menugaskan ulang objek baru tersebut ke Cited a. Kode sumber metode penggantian di String dapat mengilustrasikan masalahnya:
Pembaca dapat memeriksa sendiri metode lain, semuanya membuat ulang objek String baru di dalam metode dan mengembalikan objek baru ini tidak akan diubah. Inilah sebabnya mengapa metode seperti replace, substring, toLowerCase, dll. semuanya memiliki nilai kembalian. Ini juga mengapa pemanggilan seperti berikut tidak mengubah nilai objek:
String ss = "123456";System.out.println("ss = " + ss);ss.replace('1', '0');System.out.println("ss = " + ss);Cetak hasilnya:
ss = 123456 ss = 123456
Apakah objek String benar-benar tidak dapat diubah?
Seperti dapat dilihat di atas, variabel anggota String bersifat final pribadi, artinya tidak dapat diubah setelah inisialisasi. Di antara anggota-anggota ini, nilai bersifat istimewa karena merupakan variabel referensi, bukan objek nyata. Nilai diubah oleh final, yang berarti final tidak dapat lagi menunjuk ke objek array lain, jadi bisakah saya mengubah array yang ditunjuk oleh nilai? Misalnya, ubah karakter pada posisi tertentu dalam array menjadi garis bawah "_". Setidaknya kita tidak bisa melakukannya dengan kode biasa yang kita tulis sendiri, karena kita tidak bisa mengakses referensi nilai ini sama sekali, apalagi memodifikasi array melalui referensi ini.
Jadi bagaimana kita bisa mengakses anggota pribadi? Benar sekali, dengan menggunakan refleksi, Anda dapat merefleksikan atribut value pada objek String, dan kemudian mengubah struktur array melalui referensi nilai yang diperoleh. Berikut ini contoh kodenya:
public static void testReflection() throws Exception { //Buat string "Hello World" dan tetapkan ke referensi s String s = "Hello World"; System.out.println("s = " + s); Dunia //Dapatkan bidang nilai di kelas String Bidang valueFieldOfString = String.class.getDeclaredField("value"); //Ubah izin akses atribut nilai valueFieldOfString.setAccessible(true); //Dapatkan nilai atribut value pada objek s char[] value = (char[]) valueFieldOfString.get(s); //Ubah karakter kelima dalam array yang direferensikan oleh value value[5] = '_' ; Sistem.keluar.println("s = " + s); //Halo_Dunia }Hasil cetakannya adalah:
s = Halo Dunia s = Halo_Dunia
Dalam proses ini, s selalu mengacu pada objek String yang sama, tetapi sebelum dan sesudah refleksi, objek String berubah. Dengan kata lain, apa yang disebut objek "tidak dapat diubah" dapat dimodifikasi melalui refleksi. Namun secara umum kami tidak melakukan hal ini. Contoh refleksi ini juga dapat menggambarkan suatu masalah: jika keadaan suatu objek dan objek lain yang menyusunnya dapat berubah, maka objek tersebut kemungkinan besar bukanlah objek yang tidak dapat diubah. Misalnya, objek Mobil digabungkan dengan objek Roda. Meskipun objek Roda dinyatakan sebagai final pribadi, keadaan internal objek Roda dapat berubah, sehingga objek Mobil tidak dapat dijamin tidak dapat diubah.