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:
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:
Budi bergaji Rp5000000
Siti bergaji Rp6000000
Total gaji bulanan: 11000000Mari kita telaah inti kode di atas:
- Baris 3-4 (
Karyawan budi = new Karyawan(...)): membuat objekKaryawanlewat kata kuncinew, lalu menyimpan referensinya ke variabelbudi. Constructor di baris 16-19 menerima dua nilai awal. - Baris 7-8 (
budi.ringkas()): memanggil method milik objek — methodringkasdi baris 21-23 merangkai nama dan gaji menjadi satu string. - Baris 11-23 (
class Karyawan ...): class kedua pada berkas yang sama. Berbeda dengan classpublicyang namanya wajib sama dengan nama berkas, class tanpa modifierpublicbebas diberi nama apa pun.
Sekarang giliran Anda menjalankan siklus kompilasi di terminal. Simpan berkas di atas sebagai HitungGaji.java, lalu jalankan:
javac HitungGaji.java # mengompilasi sumber menjadi bytecode
ls *.class # memeriksa berkas hasil kompilasi
java HitungGaji # mengeksekusi bytecode di JVMPerintah 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:
| Opsi | Fungsi |
|---|---|
-d <direktori> | Menempatkan berkas .class ke direktori khusus |
-cp <jalur> atau -classpath | Menambahkan lokasi class lain yang dirujuk |
-encoding UTF-8 | Memastikan teks sumber dibaca dengan charset UTF-8 |
-Xlint:all | Mengaktifkan seluruh peringatan (warning) statis |
Contoh pemakaian -d menjaga direktori kerja tetap bersih:
javac -d bin HitungGaji.java
java -cp bin HitungGajiOpsi -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:
javap Karyawan # kerangka class: field dan method
javap -c HitungGaji # instruksi bytecode tiap methodPada 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:
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:
SalahTipe.java:3: error: incompatible types: String cannot be converted to int
int umur = "tujuh belas";
^
1 errorPesan 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 berkaspublic 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 berkaspublic class SalahTipe {
public static void main(String[] args) {
int umur = "tujuh belas";
System.out.println(umur);
}
}