Bug paling lama di dunia Java pemula berbunyi begini: program login menolak kata sandi yang benar, padahal teks yang diketik pengguna terlihat sama persis dengan yang tersimpan. Akar persoalannya hampir selalu sama — pembandingan String memakai operator ==, yang membandingkan alamat objek, bukan isi teks. Chapter ini membahas tiga alat pembanding dengan perannya masing-masing: equals dan equalsIgnoreCase untuk kesamaan isi, compareTo untuk urutan, serta == yang peran sahnya sangat terbatas. Ditutup contoh kasus login yang menunjukkan perbedaannya secara nyata.
equals versus Operator == #
Operator == pada dua variabel String bertanya: “apakah kedua variabel menunjuk objek yang sama?” Method equals bertanya: “apakah urutan karakter di dalam kedua objek sama?” Dua pertanyaan tersebut berbeda — dua objek berbeda bisa berisi teks identik, dan jawaban yang benar untuk “sama isi” adalah equals. Perbandingan == hanya bernilai true bila kedua sisi kebetulan menunjuk objek yang sama, misalnya berdua literal dari string pool.
public class BandingString {
public static void main(String[] args) {
String a = "Belajar";
String b = "Belajar";
String c = new String("Belajar");
String d = "belajar";
System.out.println("a == b : " + (a == b));
System.out.println("a == c : " + (a == c));
System.out.println("a.equals(c) : " + a.equals(c));
System.out.println("a.equals(d) : " + a.equals(d));
System.out.println("a.equalsIgnoreCase(d) : " + a.equalsIgnoreCase(d));
System.out.println("a.compareTo(d) : " + a.compareTo(d));
System.out.println("\"apel\".compareTo(\"jeruk\") : " + "apel".compareTo("jeruk"));
System.out.println("\"jeruk\".compareTo(\"apel\") : " + "jeruk".compareTo("apel"));
System.out.println("\"jeruk\".compareTo(\"jeruk\"): " + "jeruk".compareTo("jeruk"));
}
}Output yang Dihasilkan:
a == b : true
a == c : false
a.equals(c) : true
a.equals(d) : false
a.equalsIgnoreCase(d) : true
a.compareTo(d) : -32
"apel".compareTo("jeruk") : -9
"jeruk".compareTo("apel") : 9
"jeruk".compareTo("jeruk"): 0Mari kita telaah baris-baris kode di atas:
- Baris 5-7 (
a,b,c,d):adanbadalah literal yang sama-sama menunjuk satu objek di string pool;cdibuatnew Stringsehingga menempati objek terpisah;dberisi teks sama namun berbeda kapitalisasi. - Baris 9-10 (
a == b,a == c): hasiltruelalufalse.a == bbenar karena literal “Belajar” memang satu objek di pool;a == csalah karenacobjek lain meski isinya sama — bukti bahwa nasib==bergantung pada asal-usul objek, bukan isi. - Baris 11-12 (
a.equals(c),a.equals(d)):equalsmembandingkan karakter demi karakter, sehinggaadancdianggap sama meski objeknya beda;ddianggap beda karenaBbesar danbkecil adalah karakter yang berbeda. - Baris 13 (
equalsIgnoreCase): mengabaikan besar-kecil huruf, sehingga"Belajar"dan"belajar"dianggap sama. Cocok untuk data seperti alamat surel atau kode promo yang tidak sensitif kapital. - Baris 15-18 (
compareTo): mengembalikan selisih bukan boolean. Nilai-32berasal dari selisih kode karakterB(66) danb(98); nilai-9daria(97) dikurangij(106). Negatif berarti “lebih dulu dalam urutan kamus”, nol berarti identik, positif berarti sesudah.
Kasus Login dan String dari Runtime #
Bahaya == paling nyata pada teks yang dibuat saat runtime — hasil pembacaan input pengguna, isi berkas, atau data database. Objek seperti itu tidak datang dari string pool, sehingga == terhadap literal hampir pasti bernilai false meski isinya cocok. Program berikut menerima kata sandi sebagai argumen baris perintah lalu membandingkannya dua cara:
public class KataSandi {
public static void main(String[] args) {
String input = args[0];
String tersimpan = "rahasia";
System.out.println("input == tersimpan : " + (input == tersimpan));
System.out.println("input.equals(tersimpan): " + input.equals(tersimpan));
if (input.equals(tersimpan)) {
System.out.println("Akses diberikan");
} else {
System.out.println("Akses ditolak");
}
}
}Output yang Dihasilkan:
input == tersimpan : false
input.equals(tersimpan): true
Akses diberikanBedah singkatnya: JVM membuat objek String baru untuk setiap argumen program — argumen rahasia di atas dikirim saat menjalankan contoh — sehingga input tidak pernah menunjuk objek literal "rahasia" di pool, dan hasil input == tersimpan adalah false. Sebaliknya input.equals(tersimpan) membandingkan isinya karakter demi karakter dan menghasilkan true, mengantarkan program ke cabang “Akses diberikan”. Bila program ini memakai == pada kondisi if, pengguna bernasib selalu ditolak walau mengetik kata sandi yang benar.
Dengan tiga alat pembanding di tangan, kita beranjak ke gudang method pengolah String yang paling sering dipakai sehari-hari: memotong, mencari, mengganti, memecah, lalu merakit kembali.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
BandingString.java
Unduh berkaspublic class BandingString {
public static void main(String[] args) {
String a = "Belajar";
String b = "Belajar";
String c = new String("Belajar");
String d = "belajar";
System.out.println("a == b : " + (a == b));
System.out.println("a == c : " + (a == c));
System.out.println("a.equals(c) : " + a.equals(c));
System.out.println("a.equals(d) : " + a.equals(d));
System.out.println("a.equalsIgnoreCase(d) : " + a.equalsIgnoreCase(d));
System.out.println("a.compareTo(d) : " + a.compareTo(d));
System.out.println("\"apel\".compareTo(\"jeruk\") : " + "apel".compareTo("jeruk"));
System.out.println("\"jeruk\".compareTo(\"apel\") : " + "jeruk".compareTo("apel"));
System.out.println("\"jeruk\".compareTo(\"jeruk\"): " + "jeruk".compareTo("jeruk"));
}
}
KataSandi.java
Unduh berkaspublic class KataSandi {
public static void main(String[] args) {
String input = args[0];
String tersimpan = "rahasia";
System.out.println("input == tersimpan : " + (input == tersimpan));
System.out.println("input.equals(tersimpan): " + input.equals(tersimpan));
if (input.equals(tersimpan)) {
System.out.println("Akses diberikan");
} else {
System.out.println("Akses ditolak");
}
}
}