Sunday, December 18, 2016

Bab 12. Java Teori dan Implementasi



Bab. 12 Penanganan Eksepsi




12.1 Introduksi

Error runtime terjadi pada saat suatu program sedang berjalan dan JVM mendeteksi suatu operasi yang mustahil dilakukan. Misalnya, jika Anda mencoba mengakses suatu array menggunakan suatu indeks di luar rentang yang tersedia, maka program akan mendapatkan suatu error runtime dengan ArrayIndexOutOfBoundsException. Untuk membaca data dari file, Anda perlu menciptakan suatu objek Scanner menggunakan new(Scanner new File (namafile)). Jika file tidak ada, program Anda akan mendapatkan suatu error runtime dengan FileNotFoundException.

Dalam JAVA, setiap error runtime diakibatkan oleh eksepsi. Eksepsi merupakan suatu objek yang merepresentasikan suatu error atau suatu kondisi yang dapat mencegah eksekusi berjalan secara normal. Jika eksepsi tidak diatasi, maka program akan berhenti secara tidak normal. Bagaimana Anda menangani eksepsi sehingga program dapat tetap berjalan atau berhenti secara normal? Inilah topik yang akan dibahas dalam bab ini.


12.2 Sekilas Tentang Penanganan Eksepsi
Untuk mendemonstrasikan bagaimana menangani eksepsi, termasuk bagaimana suatu objek eksepsi diciptakan dan dilempar, berikut disajikan suatu contoh (kode12.1) yang membaca dua integer dan menampilkan hasil pembagiannya (tanpa sisa).

Kode12.1 HasilBagi.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import java.util.Scanner;

public class HasilBagi {
  public static void main(String[] args) {
  Scanner masukan = new Scanner(System.in);

  // Meminta pengguna untuk memasukkan dua integer
  System.out.print("Masukkan dua integer: ");
  int angka1 = masukan.nextInt();
  int angka2 = masukan.nextInt();

  System.out.println(angka1 + " / " + angka2 + " adalah " +
   (angka1 / angka2));
  }
}

Keluaran

Masukkan dua integer: 5 3
5 / 3 adalah 1

Masukkan dua integer: 3 0
Exception in thread "main" java.lang.ArithmeticException: / by zero
    at HasilBagi.main(HasilBagi.java:12)

Jika Anda memasukkan 0 pada integer kedua, suatu error runtime akan terjadi, karena Anda tidak bisa membagi suatu integer oleh 0. Cara sederhana untuk membetulkan error ini adalah dengan menambahkan statemen if untuk menguji angka kedua, seperti ditampilkan pada kode13.2.

Kode12.2 HasilBagiDenganIf.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import java.util.Scanner;

public class HasilBagi {
  public static void main(String[] args) {
  Scanner masukan = new Scanner(System.in);

  // Meminta pengguna untuk memasukkan dua integer
  System.out.print("Masukkan dua integer: ");
  int angka1 = masukan.nextInt();
  int angka2 = masukan.nextInt();

  if (angka2 != 0)
    System.out.println(angka1 + " / " + angka2 + " adalah " +
     (angka1 / angka2));
  else
    System.out.println("Pembagi tidak boleh nol ");
  }
}

Keluaran:

Masukkan dua integer: 3 0
Pembagi tidak boleh nol

Untuk mendemonstrasikan konsep penanganan eksepsi, termasuk bagaimana menciptakan, melempar, menangkap, dan menangani suatu eksepsi, kode12.2 ditulis-ulang yang ditampilkan pada kode12.3.

Kode12.3 HasilBagiDenganEksepsi.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.Scanner;

public class HasilBagiDenganEksepsi {
  public static void main(String[] args) {
    Scanner masukan = new Scanner(System.in);

    // Meminta pengguna memasukkan dua integer
    System.out.print("Masukkan dua integer: ");
    int angka1 = masukan.nextInt();
    int angka2 = masukan.nextInt();

    try {
      if (angka2 == 0)
        throw new ArithmeticException("Pembagi tidak boleh nol");

      System.out.println(angka1 + " / " + angka2 + " adalah " +
      (angka1 / angka2));
    }
    catch (ArithmeticException ex) {
      System.out.println("Eksepsi: suatu integer " +
       "tidak bisa dibagi oleh nol ");
    }

    System.out.println("Eksekusi berlanjut ...");
  }
}

Keluaran:

Masukkan dua integer: 3 0
Eksepsi: suatu integer tidak bisa dibagi oleh nol
Eksekusi berlanjut ...

Masukkan dua integer: 3 5
3 / 5 adalah 0
Eksekusi berlanjut ...

Program memuat suatu blok if dan suatu blok catch. Blok try (baris 12-18) memuat kode yang dieksekusi dalam situasi normal. Blok catch (baris 19-22) memuat kode yang dieksekusi ketika angka2 bernilai 0. Pada situasi ini, program melemparkan suatu eksepsi dengan mengeksekusi

throw new ArithmeticException("Pembagi tidak boleh nol");

Nilai yang dilemparkan, pada kasus ini new ArithmeticException("Pembagi tidak boleh nol"), disebut dengan eksepsi. Eksekusi atau suatu statemen throw disebut dengan pelemparan suatu eksepsi. Eksepsi merupakan suatu objek yang diciptakan dari suatu kelas eksepsi. Pada kasus ini, kelas eksepsinya adalah java.lang.ArithmeticException.

Ketika suatu eksepsi dilempar, aliran eksekusi normal diinterupsi. Eksepsi kemudian ditangkap oleh blok catch. Kode di dalam blok catch dieksekusi untuk menangani eksepsi. Setelah itu, statemen (baris 24) setelah blok catch dieksekusi.

Statemen throw mirip dengan suatu pemanggilan metode, namun perbedaannya adalah statemen throw tidak memanggil suatu metode, melainkan memanggil suatu blok catch. Pada kasus ini, blok catch seperti suatu definisi metode dengan suatu parameter yang cocok dengan tipe nilai yang sedang dilempar. Tidak seperti metode, setelah blok catch dieksekusi, kendali program tidak kembali kepada statemen throw, melainkan kepada statemen setelah blok catch.

Pengenal ex di dalam header blok catch

catch (ArithmeticException ex)

berperan menyerupai parameter di dalam suatu metode. Jadi, parameter ini disebut dengan parameter block catch. Tipe (dalam hal ini, ArithmeticException) sebelum pengenal ex menspesifikasi jenis eksepsi yang dapat ditangkap oleh blok catch. Setelah suatu eksepsi ditangkap oleh catch, Anda bisa mengakses nilai yang dilempar dari parameter ex dalam tubuh suatu blok catch.

Kesimpulannya, template untuk suatu blok try-throw-catch adalah seperti ini:

try {
   Kode untuk try;
   Melempar suatu eksepsi dengan suatu statemen throw atau
   dari metode jika perlu;
   Kode lain untuk try;
}

catch (tipe ex) {
   Kode untuk memproses eksepsi;
}


12.3 Kelebihan Penanganan Eksepsi
Anda telah melihat pada kode12.3 bagaimana suatu eksepsi diciptakan, dilempar, ditangkap, dan diatasi. Anda mungkin bertanya-tanya tentang keuntungannya. Untuk melihat keuntungannya, kode12.3 ditulis-ulang untuk menghitung suatu hasil bagi (tanpa sisa) menggunakan suatu metode, seperti yang ditunjukkan pada kode12.4 sebagai berikut:

Kode12.4 HasilBagiDenganEksepsi.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import java.util.Scanner;

public class HasilBagiDenganMetode {
  public static int hasilBagi(int angka1, int angka2) {
    if (angka2 == 0)
      throw new ArithmeticException("Pembagi tidak boleh nol");

    return angka1 / angka2;
  }

  public static void main(String[] args) {
    Scanner masukan = new Scanner(System.in);

    // Meminta pengguna memasukkan dua integer
    System.out.print("Masukkan dua integer: ");
    int angka1 = masukan.nextInt();
    int angka2 = masukan.nextInt();

    try {
      int hasil = hasilBagi(angka1, angka2);
      System.out.println(angka1 + " / " + angka2 + " adalah "
       + hasil);
    }
    catch (ArithmeticException ex) {
      System.out.println("Eksepsi: suatu integer " +
       "tidak dapat dibagi oleh nol ");
    }

    System.out.println("Eksekusi berlanjut ...");
  }
}

Keluaran:

Masukkan dua integer: 5 3
5 / 3 adalah 1
Eksekusi berlanjut ...

Masukkan dua integer: 3 0
Eksepsi: suatu integer tidak dapat dibagi oleh nol
Eksekusi berlanjut ...

Metode hasilBagi (baris 4-9) menghasilkan hasil bagi (tanpa sisa) atau dua integer. Jika angka2 bernilai 0, maka metode tidak tersebut tidak bisa menghasilkan nilai balik. Jadi, eksepsi dilempar pada baris 6.

Metode utama memanggil hasilBagi (baris 20). Jika metode hasilBagi dieksekusi secara normal, maka terdapat hasil balik yang dikembalikan kepada pemanggil. Jika metode hasilBagi mengalami eksepsi, maka eksepsi dilempar kembali kepada pemanggil. Blok catch dari pemanggil akan menangani eksepsi tersebut.

Sekarang Anda melihat apa keuntungan penanganan eksepsi. Hal ini memampukan suatu metode untuk melempar suatu eksepsi kepada pemanggil. Pemanggil yang akan menangani eksepsi tersebut. Tanpa kapabilitas ini, metode yang dipanggil itu sendiri yang menangani eksepsi atau harus keluar dari program. Seringkali metode yang dipanggil tidak tahu harus bagaimana ketika error terjadi. Hal ini umum terjadi pada metode-metode pustaka. Metode pustaka dapat mendeteksi error, tetapi hanya pemanggillah yang tahu apa yang perlu dilakukan ketika suatu error terjadi. Keuntungan yang paling penting dalam penanganan eksepsi adalah pemisahan antara pendeteksian error (dilakukan di dalam suatu metode yang dipanggil) dengan penanganan error (dilakukan di dalam metode pemanggil).

Banyak metode pustakan melempar eksepsi. Kode12.5 menyajikan suatu contoh yang menangani FileNotFoundException dalam memanggil konstruktor Scanner(File file).

Kode12.5 DemoFileNotFoundException.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import java.util.Scanner;
import java.io.*;

public class DemoFileNotFoundException {
  public static void main(String[] args) {
    Scanner masukanDariKonsol = new Scanner(System.in);
    // Meminta pengguna memasukkan suatu nama file
    System.out.print("Masukkan suatu nama file: ");
    String namafile = masukanDariKonsol.nextLine();

    try {
      Scanner masukanDariFile = new Scanner(new File(namafile));
      System.out.println("File " + namafile + " ada ");
      // Memproses file...
    }
    catch (FileNotFoundException ex) {
      System.out.println("Eksepsi: " + namafile + " tidak ada");
    }
  }
}

Keluaran:

Masukkan suatu nama file: E:\Cuaca.java
File E:\Cuaca.java ada

Masukkan suatu nama file: E:\buku.java
Eksepsi: E:\buku.java tidak ada

Program menciptakan suatu Scanner untuk suatu file (baris 12). Jika file tidak ada, konstruktor melempar suatu FileNotFoundException, yang ditangkap dalam blok catch.

Kode12.6 memberikan suatu contoh yang menangani suatu eksepsi InputMismatchException.

Kode12.6 DemoInputMismatchException.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.util.*;

public class DemoInputMismatchException {
  public static void main(String[] args) {
    Scanner masukan = new Scanner(System.in);
    boolean masukanLanjut = true;

    do {
      try {
        System.out.print("Masukkan suatu integer: ");
        int angka = masukan.nextInt();

        // Menampilkan hasil
        System.out.println(
         "Angka yang dimasukkan adalah " + angka);

        masukanLanjut = false;
      }
      catch (InputMismatchException ex) {
        System.out.println("Coba lagi. (" +
         "Masukan tidak tepat: yang dibutuhkan adalah suatu integer)");
        masukan.nextLine(); // Buang masukan
      }
    } while (masukanLanjut);
  }
}     

Keluaran:

Masukkan suatu integer: 3.6
Coba lagi. (Masukan tidak tepat: yang dibutuhkan adalah suatu integer)
Masukkan suatu integer: ab
Coba lagi. (Masukan tidak tepat: yang dibutuhkan adalah suatu integer)
Masukkan suatu integer: 5
Angka yang dimasukkan adalah 5

Ketika mengeksekusi masukan.nextInt() (baris 11), suatu InputMismatchException terjadi jika masukan yang diberikan bukan suatu integer. Misalnya, 3.6 yang dimasukkan. suatu InputMismatchException terjadi dan kendali dipindahkan kepada blok catch. Statemen-statemen yang ada dalam blok catch sekarang dieksekusi. Statemen masukan.nextLine() pada baris 22 membuang baris masukan saat ini sehingga pengguna dapat memasukkan suatu baris masukan baru. Variabel masukanLanjut mengendalikan loop. Nilai awalnya adalah true (baris 6), dan diubah menjadi false (baris 17) ketika masukan valid.


12.4 Tipe - Tipe Eksepsi
Beberapa tipe eksepsi yang telah digunakan dalam bagian-bagian terdahulu adalah ArithmeticException, FileNotFoundException, dan InputMismatchException. Apakah ada tipe eksepsi yang lain? Ya, ada. Terdapat banyak kelas eksepsi yang terdefinisi di dalam JAVA API. Gambar 12.1 menunjukkan beberapa diantaranya.


Gambar 12.1 Eksepsi-eksepsi yang dilempat merupakan instans-instans kelas yang ditunjukkan dalam diagram ini, atau sub-subkelas dari salah satu kelas ini.


Kelas Throwable merupakan akar dari semua kelas eksepsi. Semua kelas eksepsi JAVA mewarisi secara langsung atau tidak langsung dari Throwable. Anda bisa menciptakan kelas eksepsi sendiri dengan cara mewarisi Exception atau sub-subkelas Exception.

Kelas-kelas eksepsi dapat diklasifikasikan menjadi tiga tipe utama: error sistem, eksepsi, dan eksepsi runtime.
·         Error sistem dilemparkan oleh JVM dan direpresentasikan oleh kelas Error. Kelas Error mendeskripsikan error internal. Error semacam ini jarang terjadi. Jika terjadi, sangat terbatas yang dapat Anda lakukan kecuali hanya memberitahu pengguna dan mencoba mengentikan program secara normal. Beberapa contoh sub-subkelas Error dicantumkan pada Tabel 12.1.

Tabel 12.1 Dua contoh sub-subkelas Error
Kelas
Alasan yang mungkin terjadinya eksepsi
LinkageError




VirtualMachineError
Suatu kelas memiliki beberapa ketergantungan pada kelas lain, tetapi kelas lain ini telah diubah tanpa menjaga kompatibilitasnya setelah kompilasi dilakukan terhadap kelas pertama.

JVM telah kehabisan sumber-daya yang dibutuhkan untuk melanjutkan operasi.

·         Eksepsi direpresentasikan dalam kelas Exception, yang mendeskripsikan error-error yang diakibatkan oleh program Anda dan oleh lingkungan luar. Error-error ini ditangkap dan ditangani oleh program Anda. Beberapa contoh subkelas dari Exception dicantumkan pada Tabel 12.2 berikut ini:

Tabel 12.2 Dua contoh sub-subkelas Exception
Kelas
Alasan yang mungkin terjadinya eksepsi
ClassNotFoundException






IOException
Percobaan untuk menggunakan suatu kelas yang tidak ada. Eksepsi ini akan terjadi, misalnya, jika Anda mencoba menjalankan suatu kelas yang tidak ada menggunakan perintah java, atau jika program Anda terdiri-dari, katakanlah, tiga file kelas, namun hanya dua saja yang dapat ditemukan.

Berkaitan dengan operasi masukan/keluaran, seperti masukan yang tidak valid, membaca melampaui akhir suatu file, dan membuka file yang tidak ada. Beberapa sub-subkelas IOException adalah InterruptedIOException, EOFException (EOF adalah singkatan dari end of file), dan FileNotFoundException.

·         Eksepsi runtime direpresentasikan oleh kelas RuntimeException, yang mendeskripsikan kesalahan pemrograman, seperti casting yang salah, pengaksesan array di luar batas, dan kesalahan numerik. Eksepsi runtime biasanya dilempar oleh JVM. Beberapa sub-subkelas RuntimeException dicantumkan pada Tabel 12.3.

Tabel 12.3 Dua contoh sub-subkelas Exception
Kelas
Alasan yang mungkin terjadinya eksepsi
ArithmeticException

NullPointerException


IndexOutOfBoundsException

IllegalArgumentException
Pembagian suatu integer oleh nol.

Mencoba untuk mengakses suatu objek melalui suatu variabel referensi null.

Indeks array di luar rentang yang ada.

Argumen yang dilewatkan kepada suatu metode adalah ilegal atau tidak cocok.


12.5 Lebih Lanjut Tentang Penanganan Eksepsi
Beberapa bagian terdahulu telah menyajikan kepada Anda pengenalan tentang bagaimana menangani eksepsi dan tentang tipe-tipe eksepsi yang terdefinisi dalam JAVA. Bagian ini akan memberikan diskusi yang lebih dalam tentang penanganan eksepsi.

Model penanganan eksepsi dalam JAVA didasarkan pada tiga operasi: pendeklarasian suatu eksepsi, pelemparan suatu eksepsi, dan penangkapan suatu eksepsi, seperti yang ditampilkan pada Gambar 12.2 sebagai berikut:

Gambar 12.2 Penanganan eksepsi dalam JAVA didasarkan pada tiga operasi: pendeklarasian suatu eksepsi, pelemparan suatu eksepsi, dan penangkapan suatu eksepsi.


12.5.1 Mendeklarasikan Eksepsi
Dalam JAVA, statemen yang sedang dieksekusi berada dalam suatu metode. Interpreter JAVA memanggil metode main untuk memulai pengeksekusian program. Setiap metode harus menyatakan tipe eksepsi yang akan dilempar. Hal ini dikenal dengan pelemparan eksepsi. Karena error sistem dan error runtime bisa terjadi pada sembarang kode, JAVA tidak mensyaratkan Anda untuk mendeklarasikan Error dan RuntimeException secara eksplisit dalam metode. Namun, semua eksepsi lain yang akan dilemparkan oleh metode harus dideklarasikan secara eksplisit pada header metode sehingga pemanggil metode diberitahu tentang jenis eksepsi yang bakal terjadi.

Untuk mendeklarasikan suatu eksepsi di dalam suatu metode, gunakan katakunci throws pada header metode, seperti contoh ini:

public void metodeKu() throws IOException

Katakunci throws mengindikasikan bahwa metodeKu mungkin akan melempar suatu IOException. Jika metode kemungkinan akan melempar beberapa eksepsi, maka tambahkan daftar eksepsi, dipisahkan dengan koma, setelah throws:

public void metodeKu() throws Eksepsi1, Eksepsi2, ..., EksepsiN


12.5.2 Melempar Eksepsi
Suatu program yang mendeteksi suatu error dapat menciptakan suatu instans dari tipe eksepsi yang cocok dan melemparkannya. Ini dikenal dengan pelemparan eksepsi. Berikut adalah salah satu contoh. Dimisalkan program mendeteksi bahwa suatu argumen yang dilewatkan kepada metode tidak mematuhi kontrak metode (misalnya, argumen harus tidak negatif, tetapi yang dilewatkan suatu argumen negatif); maka program menciptakan suatu instans dari IllegalArgumentException dan melemparkannya, sebagai berikut:
IllegalArgumentException ex =
  new IllegalArgumentException("Argumen salah");
throw ex;

atau, Anda bisa menggunakan ini:

throw new IllegalArgumentException("Argumen salah ");


12.5.3 Menangkap Eksepsi
Anda sekarang telah mengetahui bagaimana mendeklarasikan dan melemparkan suatu eksepsi. Ketika suatu eksepsi dilempar, eksepsi itu dapat ditangkap dan ditangani di dalam suatu blok try-catch, sebagai berikut:

try {
statemen-statemen; // Statemen yang mungkin melempar eksepsi
}

catch (Eksepsi1 exVar1) {
  statemen untuk mengatasi eksepsi1;
}

catch (Eksepsi2 exVar2) {
  statemen untuk mengatasi eksepsi2;
}
...

catch (EksepsiN exVarN) {
  statemen untuk mengatasi eksepsiN;
}

Jika tidak terdapat eksepsi selama eksekusi blok try, maka blok-blok catch akan dilompati.

Jika salah satu statemen di dalam blok try melemparkan suatu eksepsi, maka JAVA melompati statemen-statemen yang tersisa di dalam blok try dan mulai mencari kode untuk menangani eksepsi tersebut. Kode yang menangani eksepsi disebut dengan exception handler; ditemukan dengan mempropagasi eksepsi ke belakang melalui rantai pemanggilan metode, mulai dari metode saat ini. Setiap blok catch akan diuji secara bergiliran, dari pertama sampai terakhir, untuk melihat apakah tipe objek eksepsi adalah instans dari kelas eksepsi dalam blok catch. Jika benar, objek eksepsi ditugaskan kepada variabel yang dideklarasikan, dan kemudian kode di dalam blok catch dieksekusi. Jika tidak ada handler ditemukan, maka JAVA keluar dari metode ini, melewatkan eksepsi kepada metode pemanggil, dan melanjutkan proses yang sama untuk menemukan suatu handler. Jika tidak ada satupun handler yang ditemukan dalam rantai metode, maka program akan berhenti dan menampilkan suatu pesan error pada konsol. Proses pencarian suatu handler disebut dengan penangkapan suatu eksepsi.

Dimisalkan metode main memanggil metode1, metode1 memanggil metode2, metode2 memanggil metode3, dan metode3 melemparkan suatu eksepsi, seperti ditunjukkan pada Gambar 12.3. Perhatikan skenario berikut ini:
·         Jika tipe eksepsi adalah Eksepsi3, maka akan ditangkap oleh blok catch untuk menangani eksepsi ex3 dalam metode2. statemen5 dilompati dan statemen6 dieksekusi.
·         Jika tipe eksepsi adalah Eksepsi2, maka metode2 dibatalkan, kendali program dikembalikan kepada metode1, dan eksepsi ditangkap oleh blok catch untuk menangani eksepsi ex2 dalam metode1. statemen3 dilompati, dan statemen4 dieksekusi.
·         Jika tipe eksepsi adalah Eksepsi1, maka metode1 dibatalkan, kendali program dikembalikan kepada main, dan eksepsi ditangkap oleh blok catch untuk menangani eksepsi ex1 dalam main. statemen1 dilompati, dan statemen2 dieksekusi.
·         Jika tipe eksepsi tidak ditangkap dalam metode1, metode2, dan main, maka program akan berhenti. statemen1 dan statemen2 tidak dieksekusi.


Gambar 12.3 Jika suatu eksepsi tidak ditangkap dalam metode saat ini, maka eksepsi tersebut dilemparkan kepada pemanggil. Proses berulang sampai eksepsi ditangkap atau dilewatkan kepada metode main.


12.5.4 Memperoleh Informasi dari Eksepsi
Suatu objek eksepsi memuat informasi berguna tentang eksepsi. Anda dapat menggunakan metode-metode instans dalam kelas java.lang.Throwable berikut ini untuk mendapatkan informasi yang berkaitan dengan eksepsi, seperti yang ditampilkan pada Gambar 12.4. Metode printStackTrace() menampilkan jejak tumpukan informasi pada konsol. Metode getStackTrace() menyediakan akses kepada jejak tumpukan yang ditampilkan oleh printStackTrace().

Gambar 12.4 Throwable merupakan kelas akar bagi semua objek eksepsi


Kode12.7 menyajikan suatu contoh yang menggunakan metode-metode di dalam kelas Throwable untuk  menampilkan informasi eksespi. Baris 4 memanggil metode jum untuk mengembalikan penjumlahan semua elemen di dalam array. Terdapat suatu error pada baris 23 yang menyebabkan ArrayIndexOutOfBoundsException, suatu subkelas dari IndexOutOfBoundsException. Eksepsi ini ditangkap dalam suatu blok try-catch. Baris 7-9 menampilkan jejak tumpukan, pesan eksepsi, dan pesan dan objek eksepsi menggunakan printStackTrace(), getMessage(), dan toString(), seperti yang ditunjukkan pada Gambar 12.5. Baris 10 membawa elemen-elemen jejak tumpukan ke dalam suatu array. Setiap elemen merepresentasikan suatu pemanggilan metode. Anda dapat memperoleh metode (baris 12), nama kelas (baris 13), dan jumlah baris eksepsi (baris 14) untuk setiap elemen.


Gambar 12.5 Anda dapat menggunakan printStackTrace(), getMessage(),toString(), dan getStackTrace() untuk mendapatkan informasi dari objek eksepsi.

Kode12.7 UjiEksepsi.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class UjiEksepsi {
  public static void main(String[] args) {
    try {
      System.out.println(jum(new int[] {1, 2, 3, 4, 5}) );
    }
    catch (Exception ex) {
      ex.printStackTrace();
      System.out.println("\n" + ex.getMessage());
      System.out.println("\n" + ex.toString());

      System.out.println("\nInfo jejak tumpukan dari getStackTrace");
      StackTraceElement[] elemenJejak = ex.getStackTrace();
      for (int i = 0; i < elemenJejak.length; i++) {
        System.out.print("metode " + elemenJejak[i].getMethodName());
        System.out.print("(" + elemenJejak[i].getClassName() + ":");
        System.out.println(elemenJejak[i].getLineNumber() + ")");
      }
    }
  }

  private static int jum(int[] list) {
    int hasil = 0;
    for (int i = 0; i <= list.length; i++)
      hasil += list[i];
    return hasil;
  }
}


12.5.5 Contoh: Mendeklarasikan, Melempar, dan Menangkap Eksepsi
Contoh ini mendemonstrasikan pendeklarasian, pelemparan, dan penangkapan eksepsi dengan memodifikasi metode tetapkanRadius dalam kelas Lingkaran pada kode8.9, Lingkaran3.java. Metode tetapkanRadius yang baru melemparkan suatu eksepsi jika radius bernilai negatif.

Kelas Lingkaran akan dinamai-ulang menjadi LingkaranDenganEksepsi yang diberikan pada kode 12.8. Kelas ini sama dengan kelas Lingkaran kecuali bahwa metode tetapkanRadius(double radiusBaru) melemparkan suatu IllegalArgumentException jika argumen radiusBaru bernilai negatif.

Kode12.8 LingkaranDenganEksepsi.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
public class LingkaranDenganEksepsi {
  /** Radius lingkaran */
  private double radius = 1;

  /** Jumlah objek yang diciptakan */
  private static int jumlahObjek = 0;

  /** Menciptakan suatu lingkaran dengan radius 1 */
  public LingkaranDenganEksepsi() {
    this(1.0);
  }

  /** Menciptakan suatu lingkaran dengan radius tertentu */
  public LingkaranDenganEksepsi(double radiusBaru) {
    tetapkanRadius(radiusBaru);
    jumlahObjek++;
  }

  /** Memberikan nilai balik radius */
  public double dapatRadius(){
    return radius;
  }

  /** Menetapkan nilai radius baru */
  public void tetapkanRadius(double radiusBaru)
      throws IllegalArgumentException {
    if (radiusBaru >= 0)
      radius = radiusBaru;
    else
       throw new IllegalArgumentException(
       "Radius tidak boleh negatif");

 }

  /** Mengembalikan jumlahObjek */
  public static int dapatJumlahObjek(){
    return jumlahObjek;
  }

  /** Mengembalikan luas lingkaran */
  public double dapatLuas() {
    return radius * radius * Math.PI;
  }
}

Suatu program uji yang menggunakan kelas Lingkaran baru ini diberikan pada kode12.9.

Kode12.9 UjiLingkaranDenganEksepsi.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class UjiLingkaranDenganEksepsi {
  public static void main(String[] args) {
    try {
      LingkaranDenganEksepsi c1 = new LingkaranDenganEksepsi(5);
      LingkaranDenganEksepsi c2 = new LingkaranDenganEksepsi(-5);
      LingkaranDenganEksepsi c3 = new LingkaranDenganEksepsi(0);
    }
    catch (IllegalArgumentException ex) {
      System.out.println(ex);
    }

    System.out.println("Jumlah objek yang diciptakan: " +
    LingkaranDenganEksepsi.dapatJumlahObjek());
  }
}

Keluaran:

java.lang.IllegalArgumentException: Radius tidak boleh negatif
Jumlah objek yang diciptakan: 1

Kelas Lingkaran yang asli tetap utuh kecuali bahwa nama kelas diubah menjadi LingkaranDenganEksepsi, suatu konstruktor baru LingkaranDenganEksepsi(radiusBaru) diciptakan, dan metode tetapkanRadius sekarang mendeklarasikan suatu eksepsi dan melemparkannya jika radius bernilai negatif.

Metode tetapkanRadius melempar IllegalArgumentException pada header metode (baris 25-32 dalam LingkaranDenganEksepsi.java). Kelas LingkaranDenganEksepsi sekarang masih dapat dikompilasi meski jika klausa IllegalArgumentException dihilangkan dari deklarasi metode, karena merupakan subkelas dari RuntimeException dan setiap metode dapat melempar RuntimeException meskipun tidak dideklarasikan pada header metode.

Program uji menciptakan tiga objek LingkaranDenganEksepsi, c1, c2, dan c3, untuk menguji bagaimana program menangani eksepsi. Pemanggilan new LingkaranDenganEksepsi(-5) (baris 5 pada kode12.9) menyebabkan metode tetapkanRadius dipanggil, yang melemparkan IllegalArgumentException, karena radius bernilai negatid. Pada blok catch, tipe objek ex adalah IllegalArgumentException, yang sesuai dengan objek eksepsi yang dilemparkan oleh metode tetapkanRadius. Jadi, eksepsi ini berhasil ditangkap oleh blok catch.

Handler eksepsi menampilkan suatu pesan singkat, ex.toString() (baris 9), tentang eksepsi, menggunakan System.out.println(ex).

Perhatikan bahwa eksekusi tetap berlanjut meskipun eksepsi terjadi. Jika handler tidak bisa menangkap eksepsi, maka program akan berhenti secara mendadak.

Program uji masih tetap dapat dikompilasi meski bila statemen try tidak digunakan, karena metode melemparkan suatu instans dari IllegalArgumentException, yang merupakan subkelas dari RuntimeException. Jika suatu metode melemparkan selain RuntimeException dan Error, maka metode harus dipanggil di dalam blok try-catch.


12.6 Klausa finally
Kadang-kala, Anda menginginkan kode untuk dieksekusi tanpa mempedulikan apakah suatu eksepsi ditangkap atau tidak. JAVA memiliki suatu klausa finally yang dapat digunakan untuk mencapai tujuan ini. Sintaks klausa finally adalah seperti ini:

try {
  statemen-statemen;
}

catch (Eksepsi ex) {
  penanganan ex;
}

finally {
  statemen-statemen akhir;
}

Kode di dalam blok finally dieksekusi pada situasi apapun, tanpa memandang apakah eksekusi terjadi di dalam blok try ditangkap atau tidak. Perhatikan tiga kemungkinan kasus berikut ini:
·         Jika tidak ada eksepsi yang di dalam blok try, maka statemen-statemen akhir dieksekusi, dan statemen selanjutnya setelah statemen try dieksekusi.
·         Jika suatu statemen menyebabkan eksepsi di dalam blok try yang ditangkap dalam blok catch, maka sisa statemen dalam di dalam blok try dilompati, blok catch dieksekusi, klausa finally dieksekusi. Statemen berikutnya setelah statemen try dieksekusi.
·         Jika salah satu statemen menyebabkan eksepsi di dalam blok try yang tidak ditangkap dalam sembarang blok catch, maka statemen-statemen lain di dalam blok try dilompati, klausa finally dieksekusi, dan eksepsi dilewatkan kepada metode pemanggil.

Blok finally dieksekusi meskipun jika terdapat statemen return sebelum mencapai blok finally. Penggunaan finally yang umum dilakukan adalah dalam pemrograman I/O. Untuk menjamin bahwa suatu file ditutup, maka Anda bisa menempatkan statemen penutupan file di dalam blok finally, seperti yang ditunjukkan pada kode12.10.

Kode12.10 DemoFinally.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public class DemoFinally {
  public static void main(String[] args) {
    java.io.PrintWriter keluaran = null;

    try {
      // Menciptakan suatu file
      keluaran = new java.io.PrintWriter("teks.txt");

      // Menulis keluaran terformat pada file
      keluaran.println("JAVA itu Tangguh!");
    }
    catch (java.io.IOException ex) {
      ex.printStackTrace();
    }
    finally {
      // Menutup
      if (keluaran != null) keluaran.close();
    }

    System.out.println("Akhir program");
  }
}

Statemen pada baris 7 dan 10 bisa jadi melemparkan suatu IOException, sehingga keduanya ditempatkan dalam blok suatu try. Statemen keluaran.close() menutup objek printWriter


12.7 Kapan Menggunakan Eksepsi
Suatu eksepsi terjadi di dalam metode. Jika Anda ingin eksepsi diproses oleh pemanggilnya belakangan, maka Anda harus menciptakan suatu objek eksepsi dan melemparkannya. Jika Anda menangani eksepsi di dalam metode, maka tidak ada kebutuhan akan penggunaan dan pelemparan eksepsi.

Kapan Anda seharusnya menggunakan suatu blok try-catch di dalam kode? Gunakan ketika Anda harus menghadapi kondisi-kondisi error yang tak terduga. Jangan gunakan suatu blok try-catch untuk mengatasi situasi-situasi yang sederhana dan bisa diduga. Sebagai contoh, kode berikut ini

try {
  System.out.println(varRef.toString());
}

catch (NullPointerException ex) {
  System.out.println("varRef adalah null");
}

lebih baik diganti dengan

if (varRef!= null)
  System.out.println(varRef.toString());

else
  System.out.println("varRef is null");

Mana situasi yang luar biasa dan mana situasi yang terduga kadang-kala sangat susah untuk diputuskan. Intinya adalah jangan memaksakan untuk menggunakan eksepsi hanya untuk menguji logika sederhana saja.


12.8 Melempar - Ulang Eksepsi
JAVA mengijinkan handler eksepsi untuk melempar-ulang eksepsi jika handler tidak dapat memprosesnya atau sekadar menginginkan pemanggilnya untuk diberitahu akan adanya eksepsi. Sintaks pelemparan-ulang eksepsi adalah seperti ini:

try {
  statemen-statemen;
}

catch (Eksepsi ex) {
  melakukan operasi-operasi sebelum keluar;
  throw ex;
}

Statemen throw ex melempar-ulang eksepsi kepada pemanggil sehingga handler yang lain di dalam pemanggil memiliki kesempatan untuk memproses eksepsi ex.


12.9 Eksepsi Berantai
Pada bagian terdahulu, blok catch dapat melempar-ulang eksepsi asli. Kadang-kala, Anda menginginkan untuk melemparkan suatu eksepsi baru (dengan informasi tambahan) bersama dengan eksepsi asli. Hal ini dikenal dengan eksepsi berantai. Kode12.11 mengilustrasikan bagaimana menciptakan dan melemparkan eksepsi berantai.

Kode12.11 DemoEksepsiBerantai.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class DemoEksepsiBerantai {
  public static void main(String[] args) {
    try {
      metode1();
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  public static void metode1() throws Exception {
    try {
      metode2();
    }
    catch (Exception ex) {
      throw new Exception("Informasi baru dari metode1", ex);
    }
  }

  public static void metode2() throws Exception {
    throw new Exception("Informasi baru dari metode2");
  }
}

Keluaran

java.lang.Exception: Informasi baru dari metode1
    at DemoEksepsiBerantai.metode1(DemoEksepsiBerantai.java:16)
    at DemoEksepsiBerantai.main(DemoEksepsiBerantai.java:4)
Caused by: java.lang.Exception: Informasi baru dari metode2
    at DemoEksepsiBerantai.metode2(DemoEksepsiBerantai.java:21)
    at DemoEksepsiBerantai.metode1(DemoEksepsiBerantai.java:13)
    ... 1 more

Metode main memanggil metode1 (baris 4), metode1 memanggil metode2 (baris 13), dan metode2 melempar suatu eksepsi (baris 21). Eksepsi ini ditangkap dalam blok catch di dalam metode1 dan kemudian dibungkus menjadi suatu eksepsi bari pada baris 16. Eksepsi baru tersebut kemudian dilempar dan ditangkap dalam blok catch di dalam metode main pada baris 4. Keluaran menunjukkan bahwa keluaran dari metode printStackTrace() pada baris 7. Eksepsi baru yang dilempar dari metode1 ditampilkan pertama kali, diikuti dengan eksepsi asli dari metode2.


12.10 Menciptakan Kelas Eksepsi Sendiri
JAVA hanya menyediakan sedikit kelas eksepsi. Anda dapat menggunakannya jika memungkinkan, daripada harus membuat kelas eksepsi sendiri. Akan tetapi, jika Anda mendapati masalah yang tidak bisa diselesaikan bila menggunakan kelas-kelas eksepsi dalam pustaka JAVA, maka Anda bisa menciptakan kelas eksepsi sendiri, yang diderivasi dari Exception atau dari suatu kelas Exception, seperti IOException.

Pada kode12.8, LingkaranDenganEksepsi.java, metode tetapkanRadius melemparkan suatu eksepsi jika radius bernilai negatif. Dimisalkan Anda ingin melewatkan radius kepada handler. Pada kasus itu, Anda bisa menciptakan kelas eksepsi sendiri, seperti ditunjukkan pada kode12.12.

Kode12.11 DemoEksepsiBerantai.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class EksepsiRadiusTakSah extends Exception{
  private double radius;

  /** Menciptakan suatu eksepsi */
  public EksepsiRadiusTakSah(double radius){
    super("Radius tidak sah " + radius);
    this.radius = radius;
  }

  /** Mengembalikan radius */
  public double dapatRadius() {
    return radius;
  }
 }

Kelas eksepsi buatan sendiri ini mewarisi java.lang.Exception (baris 1). Kelas Exception sendiri mewarisi java.lang.Throwable. Semua metode (misalnya, getMessage(), toString(), dan printStackTrace()) dalam kelas Exception diwarisi dari Throwable. Kelas Exception memiliki empat konstruktor. Dua diantaranya ditampilkan berikut ini:



Baris 6 memanggil konstruktor superkelas dengan suatu pesan. Pesan ini ditetapkan dalam objek eksepsi dan dapat diperoleh dengan memanggil metode getMessage() pada objek.

Untuk menciptakan suatu EksepsiRadiusTakSah, Anda perlu melewatkan suatu radius. Jadi, metode tetapkanRadius dalam kode12.8 dapat dimodifikasi menjadi sebagai berikut:

/** Menetapkan suatu radius baru */
public void tetapkanRadius(double radiusBaru) throws EksepsiRadiusTakSah {
  if (radiusBaru >= 0)
    radius = radiusBaru;
 
  else
    throw new EksepsiRadiusTakSah (radiusBaru);
}

Kode berikut ini menciptakan suatu objek lingkaran dan menetapkan radiusnya sebesar -5:

try {
  LingkaranDenganEksepsi1 c = new LingkaranDenganEksepsi1(4);
  c.tetapkanRadius(-5);
}

catch( ) {
  System.out.println("Radius tidak valid = " + ex.getRadius());
}

Pemanggilan tetapkanRadius(-5) akan melemparkan EksepsiRadiusTakSah, yang ditangkap oleh handler. Handler kemudian menampilkan radius dalam objek eksepsi ex.

No comments:

Post a Comment