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:

text
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"): 0

Mari kita telaah baris-baris kode di atas:

  1. Baris 5-7 (a, b, c, d): a dan b adalah literal yang sama-sama menunjuk satu objek di string pool; c dibuat new String sehingga menempati objek terpisah; d berisi teks sama namun berbeda kapitalisasi.
  2. Baris 9-10 (a == b, a == c): hasil true lalu false. a == b benar karena literal “Belajar” memang satu objek di pool; a == c salah karena c objek lain meski isinya sama — bukti bahwa nasib == bergantung pada asal-usul objek, bukan isi.
  3. Baris 11-12 (a.equals(c), a.equals(d)): equals membandingkan karakter demi karakter, sehingga a dan c dianggap sama meski objeknya beda; d dianggap beda karena B besar dan b kecil adalah karakter yang berbeda.
  4. 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.
  5. Baris 15-18 (compareTo): mengembalikan selisih bukan boolean. Nilai -32 berasal dari selisih kode karakter B (66) dan b (98); nilai -9 dari a (97) dikurangi j (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:

java KataSandi.java
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:

text
input == tersimpan     : false
input.equals(tersimpan): true
Akses diberikan

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

KataSandi.java

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