String sudah dipakai sejak contoh pertama, tetapi mekanisme di baliknya baru terlihat ketika teks dibandingkan, disambung, dan diolah dalam jumlah besar. Topik ini mendalami tiga hal itu, yaitu sifat imutabel (immutable) yang membuat objek String tak pernah berubah, string pool yang berbagi objek literal dengan isi sama, serta alasan operator == gagal membandingkan isi teks. Karena itu, pembandingan isi teks wajib lewat metode equals. Rangkaian metode penting ditutup dengan contoh yang bisa dijalankan.
String itu imutabel#
Objek String bersifat imutabel, artinya isi teksnya tidak dapat diubah setelah objek itu dibuat. Setiap metode yang terkesan memodifikasi teks, seperti toUpperCase, replace, atau concat, sebenarnya membangun objek String baru dan mengembalikannya, sementara objek lama tetap utuh di tempatnya.
public class BuktiImutabel {
public static void main(String[] args) {
String kata = "java";
String hasil = kata.toUpperCase();
System.out.println("hasil panggilan : " + hasil);
System.out.println("kata setelahnya : " + kata);
kata = kata + " script";
System.out.println("kata yang baru : " + kata);
}
}hasil panggilan : JAVA
kata setelahnya : java
kata yang baru : java scriptPemanggilan kata.toUpperCase() menghasilkan objek baru berisi JAVA, sedangkan kata masih menunjuk objek lama berisi java. Baris kata = kata + " script" pun tidak mengubah objek lama, melainkan membuat objek ketiga lalu memindahkan tanda panah variabel kata ke objek baru itu. Desain imutabel ini bukan kebetulan, karena sifatnya membuat teks aman dibagikan ke banyak bagian program sekaligus, layak dijadikan kunci pada struktur data seperti HashMap, dan bebas dari perubahan mendadak oleh thread lain.
String pool dan objek yang berbagi#
String pool adalah area khusus di memori yang dikelola JVM untuk menyimpan objek String literal secara unik. Ketika dua literal ditulis dengan isi yang sama, keduanya merujuk satu objek yang sama di pool, sehingga memori tidak terbuang untuk teks yang berulang. Pembuatan objek dengan new String("...") memaksa objek baru di heap, meskipun isinya persis sama dengan literal yang sudah ada di pool.
Perilaku ini menjelaskan hasil program pada bagian berikutnya, yaitu a dan b yang ditulis sebagai literal sama berakhir menunjuk objek yang sama, sedangkan c yang dibangun dengan new berdiri sendiri di heap. JVM juga menyediakan metode intern() yang menarik isi string apa pun kembali ke pool, meskipun pemakaiannya jarang diperlukan pada program biasa.
== gagal, equals benar#
Operator == membandingkan isi dua variabel primitif secara langsung, tetapi pada tipe referensi ia membandingkan alamat objeknya, bukan isinya. Dua objek String dengan teks identik bisa saja berada di alamat berbeda, dan di situ == melaporkan false padahal teksnya sama. Metode equals membandingkan isi karakter demi karakter, sehingga inilah alat yang benar untuk pembandingan teks.
public class UjiStringPool {
public static void main(String[] args) {
String a = "dwi";
String b = "dwi";
String c = new String("dwi");
System.out.println("a == b : " + (a == b));
System.out.println("a == c : " + (a == c));
System.out.println("a.equals(b) : " + a.equals(b));
System.out.println("a.equals(c) : " + a.equals(c));
}
}a == b : true
a == c : false
a.equals(b) : true
a.equals(c) : trueBaris pertama sering menipu pemula. a == b bernilai true bukan karena Java membandingkan isi, melainkan karena string pool membuat kedua literal itu berbagi satu objek. Begitu ada satu saja string yang dibangun saat program berjalan, jebakan ini terbongkar, sebagaimana dibuktikan program berikut.
public class UjiPembandingTeks {
public static void main(String[] args) {
String bagian1 = "ja";
String bagian2 = "va";
String tersusun = bagian1 + bagian2;
System.out.println("isi tersusun : " + tersusun);
System.out.println("tersusun == \"java\" : " + (tersusun == "java"));
System.out.println("tersusun.equals(\"java\") : " + tersusun.equals("java"));
}
}isi tersusun : java
tersusun == "java" : false
tersusun.equals("java") : trueVariabel tersusun berisi java, tetapi tersusun == "java" bernilai false karena penyambungan saat program berjalan membangun objek baru di heap, di luar pool. Isinya sama, alamatnya beda. Karena tidak ada jaminan tentang kapan teks berasal dari pool dan kapan tidak, kesimpulannya tegas, yaitu pembandingan isi teks selalu memakai equals dan == disisihkan untuk kasus primitif.
Metode penting String#
Kelas String menyediakan puluhan metode, dan sebagian kecilnya menutup hampir semua kebutuhan harian. Program berikut mempraktikkan beberapa di antaranya dalam satu rangkaian.
public class DemoMetodeString {
public static void main(String[] args) {
String kalimat = " Latihan Java dimulai ";
String rapi = kalimat.strip();
System.out.println("panjang awal : " + kalimat.length());
System.out.println("panjang rapi : " + rapi.length());
System.out.println("huruf besar : " + rapi.toUpperCase());
System.out.println("ganti kata : " + rapi.replace("dimulai", "selesai"));
System.out.println("kata kedua : " + rapi.split(" ")[1]);
System.out.println("berisi Java : " + rapi.contains("Java"));
System.out.println("awalan Latih : " + rapi.startsWith("Latih"));
System.out.println("huruf ke-4 : " + rapi.charAt(3));
}
}panjang awal : 24
panjang rapi : 20
huruf besar : LATIHAN JAVA DIMULAI
ganti kata : Latihan Java selesai
kata kedua : Java
berisi Java : true
awalan Latih : true
huruf ke-4 : iMetode lain yang sering dipakai dirangkum pada tabel berikut, semuanya mengikuti pola yang sama, yaitu menerima argumen lalu mengembalikan hasil tanpa menyentuh string asalnya.
| Metode | Tugas |
|---|---|
substring(awal, akhir) | memotong teks dari indeks awal sampai sebelum akhir, batas akhirnya eksklusif |
indexOf(teks) | mencari posisi pertama teks, mengembalikan -1 bila tidak ditemukan |
equalsIgnoreCase(teks) | membandingkan isi tanpa memandang besar kecil hurufnya |
isEmpty(), isBlank() | menguji string kosong, atau kosong dan hanya berisi spasi |
strip(), trim() | membuang spasi di awal dan akhir |
repeat(n) | mengulang teks sebanyak n kali |
toCharArray() | memecah teks menjadi array char |
Kesalahan umum#
- Membandingkan isi teks dengan
==. Hasilnya betul pada literal dan salah pada string hasil perhitungan. Gunakanequalsuntuk isi dan simpan==untuk primitif. - Memanggil metode tanpa menampung hasilnya. Baris
kata.toUpperCase();berdiri sendiri tidak mengubah apa pun karena hasilnya objek baru yang langsung dibuang. Tuliskata = kata.toUpperCase();bila variabelnya memang perlu diperbarui. - Salah memahami batas
substring. Pemanggilansubstring(0, 4)mengambil indeks 0 sampai 3, karena batas akhirnya tidak ikut. Satu karakter terpotong atau kelebihan satu karakter adalah kesalahan yang paling sering muncul di sini. - Memanggil metode pada variabel
Stringyang bernilainull, yang melemparNullPointerException. Bila nilai null mungkin terjadi, bandingkan dari arah literal, misalnya"java".equals(nilai), sehingga pemanggilan aman meskinilaikosong. - Melupakan bahwa pola pada
splitberupa regular expression. Pemanggilansplit(".")memecah menjadi array kosong karena titik berarti karakter apa pun, sehingga titik literal harus ditulissplit("\\.").
Ringkasan#
- Objek
Stringimutabel, sehingga setiap metode olah teks menghasilkan objek baru dan objek lama tetap utuh. - String pool menyimpan literal secara unik, dua literal berisi sama berbagi satu objek, sedangkan
new Stringmembuat objek terpisah di heap. ==membandingkan alamat objek sehingga bisafalsepadahal isi sama, maka pembandingan isi teks selalu memakaiequals.- Metode
Stringsepertistrip,replace,split, dansubstringbekerja mengembalikan hasil baru, dansubstringmemiliki batas akhir yang eksklusif.
Referensi#
- Class String — Oracle Java SE 21
— Dokumentasi kelas
String, termasuk sifat imutabel dan seluruh metodenya. - String Literals — JLS Java SE 21 — Aturan spesifikasi bahwa literal yang sama direpresentasikan oleh satu objek String yang dibagi.
- Strings (The Java Tutorials) — Oracle — Tutorial resmi mengolah string dan pembandingannya.