Pada tingkat paling dasar, setiap nilai di memori hanyalah barisan bit — angka 0 dan 1. Operator bitwise bekerja langsung pada representasi biner tersebut: & (AND), | (OR), ^ (XOR), dan ~ (NOT). Teknik ini dipakai untuk menyimpan banyak penanda dalam satu angka, membaca format berkas, memproses protokol jaringan, hingga operasi kriptografi ringan. Pada Chapter ini kita pelajari keempat operator tersebut pada tipe int, membaca hasilnya melalui Integer.toBinaryString, lalu menerapkannya pada pola bitmask hak akses yang banyak dipakai di kode nyata.

Empat Operator Bitwise #

Operator & membandingkan bit demi bit dan menghasilkan 1 hanya bila kedua bit bernilai 1. Operator | menghasilkan 1 bila salah satu bit bernilai 1, sedangkan ^ menghasilkan 1 bila kedua bit berbeda. Operator ~ adalah operator unary yang membalik setiap bit — semua 0 menjadi 1 dan sebaliknya. Java memungkinkan kita menulis angka dalam literal biner dengan awalan 0b, sehingga contoh berikut dapat dibaca dengan jelas bit demi bit.

java OperasiBit.java
public class OperasiBit {
    public static void main(String[] args) {
        int a = 0b1100;   // 12
        int b = 0b1010;   // 10

        System.out.println("a     = " + Integer.toBinaryString(a) + " (" + a + ")");
        System.out.println("b     = " + Integer.toBinaryString(b) + " (" + b + ")");
        System.out.println("a & b = " + Integer.toBinaryString(a & b) + " (" + (a & b) + ")");
        System.out.println("a | b = " + Integer.toBinaryString(a | b) + " (" + (a | b) + ")");
        System.out.println("a ^ b = " + Integer.toBinaryString(a ^ b) + " (" + (a ^ b) + ")");
        System.out.println("~a    = " + Integer.toBinaryString(~a) + " (" + ~a + ")");
    }
}

Output yang Dihasilkan:

text
a     = 1100 (12)
b     = 1010 (10)
a & b = 1000 (8)
a | b = 1110 (14)
a ^ b = 110 (6)
~a    = 11111111111111111111111111110011 (-13)

Mari kita telaah baris-baris kode di atas:

  1. Baris 3-4 (int a = 0b1100; dan int b = 0b1010;): dua operand dalam literal biner — 0b1100 bernilai desimal 12 dan 0b1010 bernilai 10.
  2. Baris 6-7 (Integer.toBinaryString(...)): method dari class Integer yang mencetak representasi biner tanpa nol di depan, sehingga 0b0110 tampil sebagai 110.
  3. Baris 8 (a & b): bit ke-empat bernilai 1 pada kedua operand hanya di posisi paling kanan kedua — hasilnya 1000 atau desimal 8.
  4. Baris 9-10 (a | b dan a ^ b): OR menghasilkan 1110 (14) karena menggabungkan semua bit yang aktif; XOR menghasilkan 110 (6) karena hanya bit yang berbeda yang bertahan.
  5. Baris 11 (~a): seluruh 32 bit dibalik sehingga hasilnya negatif — kita bedah alasannya di callout berikut.

Bitmask untuk Flag Hak Akses #

Pola pemakaian bitwise yang paling lazim adalah bitmask: setiap bit mewakili satu penanda, dan satu variabel int sanggup menampung 32 penanda sekaligus. Menggabungkan penanda memakai |, menguji satu penanda memakai & lalu membandingkan hasilnya dengan 0, menambah penanda memakai |= compound assignment, dan mencabut penanda memakai &= bersama ~. Contoh berikut menerapkan pola tersebut pada hak akses sederhana.

public class BitmaskHakAkses {
    public static void main(String[] args) {
        final int BACA = 0b0001;   // 1
        final int TULIS = 0b0010;  // 2
        final int HAPUS = 0b0100;  // 4

        int hakBudi = BACA | TULIS;             // gabung flag
        int hakSiti = BACA | TULIS | HAPUS;

        System.out.println("hakBudi = " + Integer.toBinaryString(hakBudi));
        System.out.println("hakSiti = " + Integer.toBinaryString(hakSiti));

        System.out.println("Budi bisa menulis? " + ((hakBudi & TULIS) != 0));
        System.out.println("Budi bisa menghapus? " + ((hakBudi & HAPUS) != 0));
        System.out.println("Siti bisa menghapus? " + ((hakSiti & HAPUS) != 0));

        hakBudi |= HAPUS;   // tambah flag HAPUS
        System.out.println("Budi bisa menghapus sekarang? " + ((hakBudi & HAPUS) != 0));

        hakSiti &= ~HAPUS;  // cabut flag HAPUS
        System.out.println("Siti masih bisa menghapus? " + ((hakSiti & HAPUS) != 0));
    }
}

Output yang Dihasilkan:

text
hakBudi = 11
hakSiti = 111
Budi bisa menulis? true
Budi bisa menghapus? false
Siti bisa menghapus? true
Budi bisa menghapus sekarang? true
Siti masih bisa menghapus? false

Perhatikan mekanisme di balik output tersebut:

  1. Baris 3-5: tiga konstanta final menempatkan setiap hak pada posisi bit yang berbeda — nilai 1, 2, dan 4 adalah bilangan berpangkat dua.
  2. Baris 7-8: BACA | TULIS menghasilkan 0b0011 (tampil 11) dan BACA | TULIS | HAPUS menghasilkan 0b0111 (tampil 111) — tiga penanda hidup dalam satu angka.
  3. Baris 13-15: hakBudi & TULIS menghasilkan 2 (bukan 0) sehingga Budi dinyatakan boleh menulis; hakBudi & HAPUS menghasilkan 0 sehingga Budi belum boleh menghapus.
  4. Baris 17 (hakBudi |= HAPUS;): bit HAPUS dinyalakan tanpa mengganggu bit lain — jawaban berubah menjadi true.
  5. Baris 20 (hakSiti &= ~HAPUS;): ~HAPUS adalah ...11111011; operasi AND menjaga semua bit lain tetap utuh sambil mematikan bit HAPUS.

Satu keluarga operator bit masih belum kita sentuh: operator shift yang menggeser posisi bit ke kiri dan ke kanan. Menggeser bit ternyata cara tercepat mengalikan atau membagi dengan pangkat dua, dan pada bilangan negatif perilakunya menyimpan kejutan. Chapter berikutnya mengupas <<, >>, dan >>> sampai ke contoh angka negatif.

Referensi #

Berkas Sumber

Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.

BitmaskHakAkses.java

Unduh berkas
java
public class BitmaskHakAkses {
    public static void main(String[] args) {
        final int BACA = 0b0001;   // 1
        final int TULIS = 0b0010;  // 2
        final int HAPUS = 0b0100;  // 4

        int hakBudi = BACA | TULIS;             // gabung flag
        int hakSiti = BACA | TULIS | HAPUS;

        System.out.println("hakBudi = " + Integer.toBinaryString(hakBudi));
        System.out.println("hakSiti = " + Integer.toBinaryString(hakSiti));

        System.out.println("Budi bisa menulis? " + ((hakBudi & TULIS) != 0));
        System.out.println("Budi bisa menghapus? " + ((hakBudi & HAPUS) != 0));
        System.out.println("Siti bisa menghapus? " + ((hakSiti & HAPUS) != 0));

        hakBudi |= HAPUS;   // tambah flag HAPUS
        System.out.println("Budi bisa menghapus sekarang? " + ((hakBudi & HAPUS) != 0));

        hakSiti &= ~HAPUS;  // cabut flag HAPUS
        System.out.println("Siti masih bisa menghapus? " + ((hakSiti & HAPUS) != 0));
    }
}

OperasiBit.java

Unduh berkas
java
public class OperasiBit {
    public static void main(String[] args) {
        int a = 0b1100;   // 12
        int b = 0b1010;   // 10

        System.out.println("a     = " + Integer.toBinaryString(a) + " (" + a + ")");
        System.out.println("b     = " + Integer.toBinaryString(b) + " (" + b + ")");
        System.out.println("a & b = " + Integer.toBinaryString(a & b) + " (" + (a & b) + ")");
        System.out.println("a | b = " + Integer.toBinaryString(a | b) + " (" + (a | b) + ")");
        System.out.println("a ^ b = " + Integer.toBinaryString(a ^ b) + " (" + (a ^ b) + ")");
        System.out.println("~a    = " + Integer.toBinaryString(~a) + " (" + ~a + ")");
    }
}