jshell memanjakan kita: kode diketik, hasil langsung muncul. Namun di balik layar, setiap potongan kode tersebut tetap melewati compiler yang sama — javac — sebelum dijalankan JVM. Memahami siklus kompilasi ini adalah kunci memahami Java: di sinilah kesalahan tipe tertangkap dan berkas .class berisi bytecode dihasilkan. Pada Chapter ini kita akan menjalankan siklus javac → java secara utuh, memeriksa keluaran compilernya, dan melihat apa yang terjadi ketika compiler menolak kode kita.

Peran javac dalam Siklus Java #

Compiler javac menerima berkas sumber .java dan menghasilkan berkas .class berisi bytecode — instruksi untuk JVM, bukan untuk prosesor fisik. Prosesnya berlangsung dalam beberapa tahap: javac membaca teks sumber, memeriksa aturan bahasa dan kecocokan tipe, lalu menulis bytecode yang sudah terverifikasi. Inilah alasan Java disebut statically typed: seluruh pemeriksaan tipe terjadi sebelum program berjalan, bukan saat program sedang dieksekusi.

Alur lengkapnya dapat digambarkan begini:

Setiap class pada berkas sumber menjadi satu berkas .class tersendiri — detail kecil yang sering mengejutkan pemula saat pertama kali melihat isi direktori setelah kompilasi.

Siklus Lengkap: Kompilasi lalu Eksekusi #

Mari kita kerjakan siklus penuh pada satu contoh. Program berikut mendefinisikan class HitungGaji berisi main, ditambah class pembantu Karyawan pada berkas yang sama:

java HitungGaji.java
public class HitungGaji {
    public static void main(String[] args) {
        Karyawan budi = new Karyawan("Budi", 5000000);
        Karyawan siti = new Karyawan("Siti", 6000000);

        System.out.println(budi.ringkas());
        System.out.println(siti.ringkas());
        System.out.println("Total gaji bulanan: " + (budi.gaji + siti.gaji));
    }
}

class Karyawan {
    String nama;
    int gaji;

    Karyawan(String nama, int gaji) {
        this.nama = nama;
        this.gaji = gaji;
    }

    String ringkas() {
        return nama + " bergaji Rp" + gaji;
    }
}

Output yang Dihasilkan:

text
Budi bergaji Rp5000000
Siti bergaji Rp6000000
Total gaji bulanan: 11000000

Mari kita telaah inti kode di atas:

  1. Baris 3-4 (Karyawan budi = new Karyawan(...)): membuat objek Karyawan lewat kata kunci new, lalu menyimpan referensinya ke variabel budi. Constructor di baris 16-19 menerima dua nilai awal.
  2. Baris 7-8 (budi.ringkas()): memanggil method milik objek — method ringkas di baris 21-23 merangkai nama dan gaji menjadi satu string.
  3. Baris 11-23 (class Karyawan ...): class kedua pada berkas yang sama. Berbeda dengan class public yang namanya wajib sama dengan nama berkas, class tanpa modifier public bebas diberi nama apa pun.

Sekarang giliran Anda menjalankan siklus kompilasi di terminal. Simpan berkas di atas sebagai HitungGaji.java, lalu jalankan:

bash
javac HitungGaji.java   # mengompilasi sumber menjadi bytecode
ls *.class              # memeriksa berkas hasil kompilasi
java HitungGaji         # mengeksekusi bytecode di JVM

Perintah ls *.class akan menampilkan dua berkas: HitungGaji.class dan Karyawan.class — satu untuk tiap class pada berkas sumber. Perintah java HitungGaji memuat HitungGaji.class ke JVM dan memulai eksekusi dari method main; class Karyawan dimuat otomatis ketika dibutuhkan. Output program persis seperti blok “Output yang Dihasilkan” di atas.

Opsi javac yang Sering Dipakai #

javac memiliki banyak opsi; empat berikut paling sering terpakai sehari-hari:

OpsiFungsi
-d <direktori>Menempatkan berkas .class ke direktori khusus
-cp <jalur> atau -classpathMenambahkan lokasi class lain yang dirujuk
-encoding UTF-8Memastikan teks sumber dibaca dengan charset UTF-8
-Xlint:allMengaktifkan seluruh peringatan (warning) statis

Contoh pemakaian -d menjaga direktori kerja tetap bersih:

bash
javac -d bin HitungGaji.java
java -cp bin HitungGaji

Opsi -d bin memerintahkan javac membuat direktori bin lalu menaruh seluruh berkas .class di dalamnya; opsi -cp bin memberi tahu JVM bahwa bytecode berada di direktori tersebut. Pola direktori-terpisah inilah cikal bakal struktur proyek yang kita bahas pada Chapter Struktur Proyek Java.

Melihat Isi .class dengan javap #

Berkas .class berisi bytecode biner, tetapi alat javap dapat menerjemahkannya kembali ke bentuk yang dapat dibaca. Perintah javap Karyawan menampilkan kerangka class — nama field dan tanda tangan method — sementara javap -c HitungGaji menampilkan instruksi bytecode-nya:

bash
javap Karyawan          # kerangka class: field dan method
javap -c HitungGaji     # instruksi bytecode tiap method

Pada keluaran javap -c, kita akan menjumpai instruksi seperti new (mengalokasikan objek), ldc (memuat konstanta), invokespecial (memanggil constructor), dan invokevirtual (memanggil method milik objek). Kita tidak perlu menulis bytecode secara manual — tetapi mengetahui bahwa lapisan ini ada menjelaskan mengapa program Java dapat berjalan di sistem operasi mana pun: semua platform menjalankan bytecode yang sama, hanya JVM-nya yang berbeda.

Ketika Compiler Menolak Kode Kita #

Momen paling mendidik dari siklus kompilasi justru terjadi saat javac menolak kode. Coba program kecil berikut:

java SalahTipe.java
public class SalahTipe {
    public static void main(String[] args) {
        int umur = "tujuh belas";
        System.out.println(umur);
    }
}

Program tersebut gagal dikompilasi — JVM tidak pernah sempat berjalan. Compiler javac melaporkan:

text
SalahTipe.java:3: error: incompatible types: String cannot be converted to int
        int umur = "tujuh belas";
                   ^
1 error

Pesan tersebut memuat tiga informasi penting: lokasi masalah (SalahTipe.java, baris 3, ditandai tanda sisip ^), jenis masalah (incompatible types — tipe tidak cocok), dan solusinya tersirat jelas: variabel int tidak dapat menampung nilai String. Inilah compile-time error — kelas kesalahan yang tertangkap sebelum program berjalan, lawan dari runtime error yang baru muncul saat eksekusi. Membaca pesan seperti ini dengan tenang adalah keterampilan yang akan kita asah terus sepanjang Course.

Dengan siklus kompilasi kini kita kuasai, pertanyaan berikutnya wajar muncul: apakah setiap program kecil harus melalui dua perintah itu? Chapter Menjalankan Program Java menjawabnya dengan mengenalkan tiga cara menjalankan program — termasuk mode satu berkas yang melompati ritual javac manual.

Referensi #

Berkas Sumber

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

HitungGaji.java

Unduh berkas
java
public class HitungGaji {
    public static void main(String[] args) {
        Karyawan budi = new Karyawan("Budi", 5000000);
        Karyawan siti = new Karyawan("Siti", 6000000);

        System.out.println(budi.ringkas());
        System.out.println(siti.ringkas());
        System.out.println("Total gaji bulanan: " + (budi.gaji + siti.gaji));
    }
}

class Karyawan {
    String nama;
    int gaji;

    Karyawan(String nama, int gaji) {
        this.nama = nama;
        this.gaji = gaji;
    }

    String ringkas() {
        return nama + " bergaji Rp" + gaji;
    }
}

SalahTipe.java

Unduh berkas
java
public class SalahTipe {
    public static void main(String[] args) {
        int umur = "tujuh belas";
        System.out.println(umur);
    }
}