Tampilan
Kenapa penting
Developer backend menghabiskan banyak waktu bukan untuk menulis kode baru, melainkan untuk mencari tahu kenapa kode tidak berjalan seperti seharusnya — test yang merah, pesanan yang totalnya salah, atau aplikasi yang berhenti di tengah malam. Developer junior sering menebak-nebak: mengubah kode secara acak sambil berharap bug hilang. Developer yang efektif membaca pesan error dengan teliti dan memakai debugger untuk melihat isi variabel saat program berjalan. Kemampuan ini juga sering diuji saat interview teknis ("ini ada bug, coba cari"). Bab ini menutup M01 dengan bekal paling praktis: membaca error dan men-debug dengan sistematis.
Konsep inti
Tiga jenis error
| Jenis | Kapan ketahuan | Contoh | Siapa yang memberi tahu |
|---|---|---|---|
| Kompilasi | saat javac/IDE memeriksa | lupa titik koma, salah tipe, typo nama | compiler (garis merah IDE) |
| Runtime | saat program berjalan | indeks array di luar batas, bagi nol | JVM lewat exception & stack trace |
| Logika | saat hasilnya dicek | diskon selalu 0, total kurang satu item | hanya test atau pengguna |
Error kompilasi paling mudah: compiler menunjuk baris dan alasannya. Error logika paling sulit: program berjalan mulus tapi hasilnya salah. Di sinilah test (bab Setup JDK, latihan di exercises/) dan debugger berperan.
Pesan javac yang paling sering muncul
Setiap pesan berbentuk NamaFile.java:baris: error: pesan, diikuti baris kode dan tanda ^ di posisi masalahnya. Pesan-pesan di bawah ini adalah output javac asli:
| Pesan | Artinya & cara memperbaiki |
|---|---|
';' expected | statement belum diakhiri titik koma — cek akhir baris yang ditunjuk |
cannot find symbol + symbol: variable totl | nama tidak dikenal: salah ketik, belum dideklarasikan, atau di luar scope |
incompatible types: String cannot be converted to int | tipe nilai tidak cocok; ubah dulu dengan Integer.parseInt bila perlu |
incompatible types: possible lossy conversion from double to int | narrowing tanpa cast (bab Tipe Data) — pakai cast atau tipe yang lebih lebar |
missing return statement | ada jalur di method non-void yang tidak mengembalikan nilai |
variable total might not have been initialized | variabel lokal dibaca sebelum pasti diberi nilai |
method price in class K cannot be applied to given types; | jumlah/tipe argumen tidak cocok — baca baris required: dan found: |
reached end of file while parsing | kurung kurawal } kurang — biasanya di akhir file |
unreachable statement | ada kode setelah return/break yang tidak mungkin dijalankan |
Dua kebiasaan penting: perbaiki error pertama dulu (satu kesalahan bisa memicu banyak error lanjutan), dan baca seluruh pesannya, termasuk baris tambahan seperti required: long,int dan found: int.
Error runtime dan stack trace
Error runtime berupa exception yang menghentikan program. JVM mencetak jenisnya, pesannya, dan jejak pemanggilan (stack trace):
java
public class OrderReport {
static int averageItems(int[] itemsPerOrder) {
int total = 0;
for (int i = 0; i <= itemsPerOrder.length; i++) {
total += itemsPerOrder[i];
}
return total / itemsPerOrder.length;
}
public static void main(String[] args) {
int[] itemsPerOrder = {2, 5, 1};
System.out.println("Rata-rata item: " + averageItems(itemsPerOrder));
}
}text
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
at OrderReport.averageItems(OrderReport.java:5)
at OrderReport.main(OrderReport.java:12)Baca dari atas: jenis + pesan (indeks 3 untuk array berisi 3 elemen), lalu baris paling atas yang merupakan kodemu (OrderReport.java:5, di dalam averageItems), lalu ke bawah untuk melihat siapa yang memanggilnya (main baris 12). Pesannya langsung mengarah ke kondisi <= yang seharusnya <. Cara membaca stack trace yang lebih panjang — termasuk Caused by: — dibahas lengkap di M03.
Alur debugging yang sistematis
- Reproduksi — pastikan kamu bisa memunculkan bug dengan input tertentu. Idealnya tulis test kecil yang gagal.
- Isolasi — persempit lokasinya. Teknik bagi dua: cek nilai di tengah alur; bila sudah salah di sana, bug ada di separuh awal, bila masih benar, di separuh akhir. Ulangi.
- Hipotesis — rumuskan dugaan yang spesifik ("diskon 0 karena pembagian bulat"), bukan "mungkin ada yang salah".
- Verifikasi — buktikan dugaan dengan debugger atau test, perbaiki, lalu jalankan semua test lagi.
Teknik sederhana yang sering ampuh: rubber duck debugging — jelaskan kodemu baris per baris (ke rekan, atau bahkan bebek mainan). Saat menjelaskan, kamu sering menyadari asumsi yang keliru.
Debugger IDE
Debugger menjalankan program sambil memberimu kendali: berhenti di baris tertentu, melihat isi variabel, lalu maju langkah demi langkah. Shortcut di bawah adalah default IntelliJ IDEA dan VS Code di Windows/Linux (macOS berbeda).
| Aksi | Fungsi | IntelliJ IDEA | VS Code |
|---|---|---|---|
| Breakpoint | tandai baris tempat program berhenti (klik di samping nomor baris) | Ctrl+F8 | F9 |
| Mulai debug | jalankan program/test dalam mode debug | Shift+F9 | F5 |
| Step Over | jalankan baris ini, berhenti di baris berikutnya | F8 | F10 |
| Step Into | masuk ke dalam method yang dipanggil di baris ini | F7 | F11 |
| Step Out | selesaikan method ini, kembali ke pemanggilnya | Shift+F8 | Shift+F11 |
| Resume | lanjut sampai breakpoint berikutnya | F9 | F5 |
| Evaluate expression | hitung ekspresi apa pun dengan nilai variabel saat ini | Alt+F8 | panel Debug Console |
Saat program berhenti, perhatikan tiga panel:
- Variables — semua variabel lokal dan nilainya saat ini. Nilai yang berubah biasanya disorot.
- Watches — ekspresi yang kamu pantau terus, misalnya
total * discountPercent / 100. - Call stack / Frames — urutan method yang sedang berjalan; klik baris lain untuk melihat variabel pemanggil.
Conditional breakpoint (klik kanan breakpoint → isi kondisi, mis. i == 9_999 atau orderId.equals("INV-7781")) membuat debugger hanya berhenti saat kondisinya true — sangat berguna di loop besar. Kamu juga bisa men-debug test: klik ikon ▶ di samping method test lalu pilih Debug.
Print debugging, debugger, atau logging?
System.out.printlncepat untuk program kecil, tapi harus ditambah-hapus manual dan mudah lupa dibersihkan.- Debugger paling kuat untuk memahami alur dan nilai tanpa mengubah kode.
- Logging (SLF4J, dibahas di M05) dipakai di aplikasi yang sudah berjalan di server, di mana debugger tidak bisa dipasang. Log yang baik adalah "debugger" untuk production.
Contoh di dunia backend
Tim QA melaporkan: keranjang berisi 2 × Rp25.000, 1 × Rp18.000, dan 4 × Rp7.500 dengan diskon 10% seharusnya Rp88.200, tetapi sistem menagih Rp48.000. Langkah pertama: reproduksi dengan program kecil.
java
public class CartBug {
// Total keranjang setelah diskon persen. Harga dalam rupiah.
static long cartTotal(long[] prices, int[] quantities, int discountPercent) {
long total = 0;
for (int i = 1; i < prices.length; i++) {
total += prices[i] * quantities[i];
}
long discount = total * (discountPercent / 100);
return total - discount;
}
public static void main(String[] args) {
long[] prices = {25_000, 18_000, 7_500};
int[] quantities = {2, 1, 4};
System.out.println("Total: " + cartTotal(prices, quantities, 10) + " (seharusnya 88200)");
}
}Output: Total: 48000 (seharusnya 88200). Sesi debugging-nya:
- Pasang breakpoint di baris
long discount = ..., lalu jalankan dalam mode debug. - Panel Variables menunjukkan
total = 48000, padahal subtotal yang benar 98.000. Selisihnya 50.000 — persis 2 × Rp25.000, yaitu item pertama. Hipotesis: item pertama terlewat. Pasang breakpoint di dalam loop dan lihatipada putaran pertama: nilainya1, bukan0. Bug 1 ditemukan. - Kembali ke baris diskon, evaluate expression
discountPercent / 100→ hasilnya0. Pembagian bulat dari bab Operator. Bug 2 ditemukan.
Setelah diperbaiki:
java
public class CartFixed {
// Total keranjang setelah diskon persen. Harga dalam rupiah.
static long cartTotal(long[] prices, int[] quantities, int discountPercent) {
long total = 0;
for (int i = 0; i < prices.length; i++) { // perbaikan 1: indeks mulai dari 0
total += prices[i] * quantities[i];
}
long discount = total * discountPercent / 100; // perbaikan 2: kalikan dulu, baru bagi
return total - discount;
}
public static void main(String[] args) {
long[] prices = {25_000, 18_000, 7_500};
int[] quantities = {2, 1, 4};
System.out.println("Total: " + cartTotal(prices, quantities, 10));
}
}Output: Total: 88200. Di tim sungguhan, langkah terakhirnya adalah menambah test untuk kasus ini agar bug yang sama tidak kembali (regression test) — persis seperti latihan bab ini, di mana test sudah disiapkan dan tugasmu menemukan bug-nya.
Pertanyaan interview
1. Bagaimana langkahmu saat menemukan bug yang belum jelas penyebabnya?
Reproduksi dulu dengan input yang pasti (idealnya test yang gagal), persempit lokasinya (bagi dua alur, cek nilai di titik tengah), rumuskan hipotesis spesifik, verifikasi dengan debugger atau log, lalu perbaiki akar masalahnya dan tambahkan regression test. Hindari mengubah kode secara acak.
2. Apa beda error kompilasi, runtime, dan logika?
Error kompilasi ditemukan compiler sebelum program jalan (sintaks, tipe). Error runtime terjadi saat program berjalan dan muncul sebagai exception dengan stack trace. Error logika tidak menghentikan program tapi hasilnya salah — hanya ketahuan lewat test, debugging, atau laporan pengguna.
3. Apa beda Step Over, Step Into, dan Step Out?
Step Over menjalankan baris saat ini seluruhnya lalu berhenti di baris berikutnya. Step Into masuk ke method yang dipanggil di baris itu. Step Out menyelesaikan method saat ini dan berhenti di pemanggilnya. Kombinasikan dengan conditional breakpoint untuk loop besar.
Jebakan umum
- Memperbaiki error kompilasi dari bawah. Error pertama sering menyebabkan error lain; mulai dari yang paling atas.
- Menebak tanpa melihat data. Lihat nilai variabel sebenarnya di debugger sebelum mengubah kode.
- Lupa menghapus
System.out.printlndebug sebelum commit — log jadi penuh sampah. - Mengubah test agar lulus. Test adalah spesifikasi; yang salah biasanya kodenya.
- Berhenti setelah gejala hilang. Pastikan akar masalahnya yang diperbaiki, lalu jalankan semua test, bukan satu saja.
Ringkasan
- Error terbagi tiga: kompilasi (compiler), runtime (exception + stack trace), dan logika (hasil salah).
- Baca pesan
javacdari error pertama, termasuk barissymbol:,required:, danfound:. - Stack trace dibaca dari atas: jenis & pesan, lalu baris pertama milik kodemu.
- Debugging sistematis: reproduksi → isolasi → hipotesis → verifikasi, lalu tambah regression test.
- Debugger: breakpoint (termasuk conditional), step over/into/out, variables, watch, dan evaluate expression.