Pada chapter sebelumnya kita sempat melihat perilaku mencurigakan: memanggil concat dan toUpperCase tidak mengubah isi variabel. Perilaku tersebut bukan keanehan — melainkan keputusan desain yang disengaja. Objek String di Java bersifat immutable: sekali dibuat, isinya tidak pernah bisa diubah, dan setiap operasi teks menghasilkan objek String baru. Chapter ini membuktikan sifat tersebut lewat contoh, menjelaskan alasan di baliknya, lalu memperlihatkan akibat praktisnya pada konkatenasi di dalam perulangan.
Method String Mengembalikan Objek Baru #
Mari kita uji dugaan tersebut secara langsung. Program berikut memanggil dua method yang terkesan “mengubah” teks, namun membiarkan hasil kembaliannya terbuang tanpa ditampung variabel apa pun.
public class StringTakBerubah {
public static void main(String[] args) {
String bahasa = "Java";
bahasa.concat(" 25");
System.out.println("Hasil concat dibuang : " + bahasa);
bahasa.toUpperCase();
System.out.println("Hasil upper dibuang : " + bahasa);
bahasa = bahasa.concat(" 25");
System.out.println("Setelah reassign : " + bahasa);
String potongan = bahasa.substring(5);
System.out.println("substring(5) : " + potongan);
System.out.println("bahasa tetap : " + bahasa);
}
}Output yang Dihasilkan:
Hasil concat dibuang : Java
Hasil upper dibuang : Java
Setelah reassign : Java 25
substring(5) : 25
bahasa tetap : Java 25Mari kita telaah baris-baris kode di atas:
- Baris 5-6 (
bahasa.concat(" 25");): methodconcatmembuat String baru berisi"Java 25"lalu mengembalikannya — namun tidak ada variabel yang menampung, sehingga objek baru tersebut seketika menjadi kandidat pemungutan garbage collector. Variabelbahasatetap menunjuk objek lama"Java". - Baris 8-9 (
bahasa.toUpperCase();): kasus yang sama — String baru"JAVA"dibuat dan dibuang;bahasamasih"Java". Ini menjelaskan kesalahan klasik pemula: memanggiltoUpperCase()sendirian tanpa menampung hasilnya. - Baris 11-12 (
bahasa = bahasa.concat(" 25");): cara yang benar. String hasil concat ditampung kembali ke variabelbahasa, sehingga penugasan mengarahkan variabel ke objek baru"Java 25". Yang berubah adalah referensi variabel, bukan isi objek lama. - Baris 14-16 (
substring(5)):substringmengekstrak bagian teks ke objek String baru"25". Objek asal"Java 25"tidak tersentuh sama sekali — terbukti dari baris terakhir yang masih mencetakJava 25.
Mengapa Dirancang Tidak Berubah #
Ketidakberubahan ini bukan keterbatasan, melainkan fondasi bagi beberapa jaminan penting. Pertama, keamanan: teks berupa nama berkas, nama host, atau parameter keamanan tidak bisa diam-diam diubah pihak lain setelah diperiksa. Kedua, kecocokan untuk hashing: kode hash String dihitung sekali dan dijamin stabil, sehingga String aman menjadi key pada HashMap dan HashSet. Ketiga, berbagi secara bebas: string pool bisa menyatukan literal yang sama persis karena tidak ada jalur untuk mengubah isinya — bila satu bagian kode mengubah String bersama, semua pemegang referensi lain akan terkena imbasnya.
Konsekuensi nyatanya: setiap operasi “mengubah teks” adalah operasi menyalin. Method replace, trim, substring, semuanya menghasilkan objek baru sementara objek lama ditinggalkan bagi garbage collector.
Konkatenasi dalam Perulangan #
Sifat immutable paling terasa pada penggabungan berulang. Perhatikan program berikut yang merangkai angka dengan operator + di dalam loop:
public class KonkatenasiLoop {
public static void main(String[] args) {
String hasil = "";
for (int i = 1; i <= 5; i++) {
hasil = hasil + i;
System.out.println("Iterasi " + i + ": String baru berisi \"" + hasil + "\"");
}
System.out.println("Hasil akhir: " + hasil);
}
}Output yang Dihasilkan:
Iterasi 1: String baru berisi "1"
Iterasi 2: String baru berisi "12"
Iterasi 3: String baru berisi "123"
Iterasi 4: String baru berisi "1234"
Iterasi 5: String baru berisi "12345"
Hasil akhir: 12345Apa yang sebenarnya terjadi pada tiap iterasi? Karena String tidak bisa diubah isinya, JVM menyusun objek String baru yang memuat seluruh isi lama plus satu angka tambahan — terlihat pada output yang bertambah dari "1", "12", hingga "12345". Untuk lima iterasi persoalan tak terasa; namun bayangkan seribu iterasi: isi lama disalin berulang kali, sehingga total karakter yang tersalin tumbuh kuadratik terhadap jumlah iterasi. Banyak objek sementara tercipta dan segera menjadi sampah memori.
Sebelum melaju ke solusi tersebut, ada satu mekanisme menarik yang hanya masuk akal justru karena String immutable: string pool — gudang penyatu literal yang membuat dua variabel literal sama isinya bisa menunjuk objek yang persis sama.
Referensi #
- Oracle: Class String
- Java Language Specification: Lexical Structure (String Literals)
- dev.java: Learn Java
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
KonkatenasiLoop.java
Unduh berkaspublic class KonkatenasiLoop {
public static void main(String[] args) {
String hasil = "";
for (int i = 1; i <= 5; i++) {
hasil = hasil + i;
System.out.println("Iterasi " + i + ": String baru berisi \"" + hasil + "\"");
}
System.out.println("Hasil akhir: " + hasil);
}
}
StringTakBerubah.java
Unduh berkaspublic class StringTakBerubah {
public static void main(String[] args) {
String bahasa = "Java";
bahasa.concat(" 25");
System.out.println("Hasil concat dibuang : " + bahasa);
bahasa.toUpperCase();
System.out.println("Hasil upper dibuang : " + bahasa);
bahasa = bahasa.concat(" 25");
System.out.println("Setelah reassign : " + bahasa);
String potongan = bahasa.substring(5);
System.out.println("substring(5) : " + potongan);
System.out.println("bahasa tetap : " + bahasa);
}
}