Thursday, March 22, 2018

13.Operasi I/O Bagian Satu



Bab ini akan mengintroduksi salah satu paket penting dalam Java: io. Paket io mendukung sistem masukan/keluaran (I/O, Input/Output), termasuk I/O file. Bab ini akan mendiskusikan pondasi dari sistem ini sehingga Anda dapat melihat bagaimana ia diintegrasikan ke dalam bahasa Java dan bagaimana ia diterapkan pada konteks lebih besar dari pemrograman Java. Bab ini juga akan mendiskusikan statemen try lebih jauh lagi dan beberapa katakunci Java lain: transient, volatile, dan instanceof. 


Dasar-Dasar I/O
Seperti yang telah Anda baca pada bab-bab sebelumnya, tidak banyak penggunaan I/O pada program-program contoh. Pada dasarnya, selain print() dan println(), tidak ada metode I/O yang telah digunakan secara signifikan. Alasannya sederhana: hampir semua aplikasi riil dari Java tidak berbasis teks atau berbasis konsol. Tetapi, umumnya aplikasi Java merupakan program berorientasik grafikal menggunakan framework antarmuka pengguna grafikal (GUI, graphical user interface), seperti Swing, AWT, JavaFX untuk interaksi, atau aplikasi Web. 

Meskipun berbasis teks, program konsol merupakan hal paling penting dalam pembelajaran Java. Memang, dukungan I/O konsol sangat terbatas dan cukup melelahkan untuk digunakan, meski pada program sederhana. Konsol berbasis teks tidak banyak digunakan pada pemrograman Java di dunia nyata.


Aliran
Program Java melakukan proses I/O melalui apa yang dinamakan dengan aliran. Aliran adalah sebuah abstraksi, yang menjelaskan tentang produksi informasi atau konsumsi informasi. Aliran selalu dikaitkan dengan divais fisikal oleh sistem I/O Java. Semua aliran berperilaku sama, meskipun divais fisikal aktualnya berbeda. Jadi, kelas-kelas dan metode-metode  I/O yang sama dapat diterapkan pada pelbagai divais berbeda. Ini berarti bahwa sebuah aliran masukan dapat dipakai untuk menangani pelbagai jenis masukan: dari konsol, keyboard, atau soket jaringan. Sama halnya dengan aliran keluaran. Java mengimplementasikan aliran-aliran di dalam hierarki kelas yang didefinisikan pada paket java.io.


Aliran Byte dan Aliran Karakter
Java mendefinisikan dua jenis aliran: byte dan karakter. Aliran byte menyediakan cara mudah untuk menangani masukan dan keluaran byte. Aliran byte dipakai, misalnya, ketika membaca atau menuliskan data biner. Aliran karakter menyediakan cara untuk menangani masukan dan keluaran karakter. Aliran ini menggunakan himpunan kode karakter Unicode, yang dapat diinternasionalisasi. Dalam sejumlah kasus, aliran karakter lebih efisien daripada aliran byte.

Versi awal dari Java, JDK 1.0, tidak memiliki aliran karakter, karena semua sistem I/O berorientasi byte. Aliran karakter ditambahkan pada JDK 1.1, dan sejumlah kelas dan metode berorientasi byte didepresiasi atau direkomendasikan untuk tidak digunakan. Meskipun kode lama yang tidak menggunakan aliran karakter menjadi semakin jarang digunakan, hal itu ternyata direkomendasikan oleh sejumlah programer. Satu hal penting lain adalah pada level terendah, semua I/O masih berorientasi byte. Aliran berbasis karaketer hanya diperuntukkan untuk menangani karakter.


Kelas-Kelas Aliran Byte
Aliran byte didefinisikan menggunakan dua hierarki pewarisan kelas. Di bagian atas hierarki tersebut terdapat dua kelas abstrak: InputStream dan OutputStream. Setiap kelas abstrak ini memiliki sejumlah subkelas konkrit yang menangani perbedaan di antara pelbagai divais, seperti file disk, koneksi jaringan, atau bahkan penyangga memori. Kelas-kelas aliran byte pada paket java.io ditunjukkan pada Tabel di bawah ini. Beberapa kelas di antaranya akan didiskusikan pada bab ini. Anda hanya perlu mengingat bahwa untuk menggunakan kelas-kelas aliran ini, Anda perlu mengimpornya dengan java.io.

Kelas Aliran
Penjelasan
BufferedInputStream
Aliran masukan penyangga
BufferedOutputStream
Aliran keluaran penyangga
ByteArrayInputStream
Aliran masukan yang membaca dari sebuah array byte
ByteArrayOutputStream
Aliran keluaran yang menuliskan atau mengisi sebuah array byte
DataInputStream
Aliran masukan yang memuat metode-metode untuk membaca tipe-tipe masukan data standar Java
DataOutputStream
Aliran keluaran yang memuat metode-metode untuk menuliskan tipe-tipe keluaran data standar Java
FileInputStream
Aliran masukan yang membaca dari sebuah file
FileOutputStream
Aliran keluaran yang menuliskan ke sebuah file
FilterInputStream
Mengimplementasikan InputStream
FilterOutputStream
Mengimplementasikan OutputStream
InputStream
Kelas abstrak yang menjelaskan masukan aliran
OutputStream
Kelas abstrak yang menjelaskan keluaran aliran
ObjectInputStream
Aliran masukan untuk objek-objek
ObjectOutputStream
Aliran keluaran untuk objek-objek
PipedInputStream
Pipa masukan
PipedOutputStream
Pipa keluaran
PrintStream
Aliran keluaran yang memuat print() dan println()
PushbackInputStream
Aliran masukan yang mendukung keluaran satu-byte pada aliran masukan
SequenceInputStream
Aliran masukan yang merupakan sebuah kombinasi dari dua atau lebih aliran masukan yang akan dibaca secara sekuensial, satu demi satu

Kelas abstrak InputStream dan OuputStream mendefinisikan beberapa metode kunci yang diimplementasikan kelas-kelas aliran lain. Dua yang paling penting adalah read() dan write(), yang membaca dan menuliskan byte-byte data.  Tiap metode ini memiliki bentuk abstrak dan harus didefinisikan ulang (override) oleh kelas-kelas aliran yang menderivasinya.


Kelas-Kelas Aliran Karakter
Aliran karakter didefinisikan menggunakan dua hierarki pewarisan kelas. Di bagian atas hierarki terdapat dua kelas abstrak: Reader dan Writer. Kedua kelas abstrak ini menangani aliran-aliran karakter Unicode. Java memiliki sejumlah subkelas konkrit untuk tiap kelas abstrak ini. Kelas-kelas aliran karakter pada java.io ditampilkan pada tabel di bawah ini.

Kelas Aliran
Penjelasan
BufferedReader
Aliran karakter masukan penyangga
BufferedWriter
Aliran karakter keluaran penyangga
CharArrayReader
Aliran masukan yang membaca dari sebuah karakter array
CharArrayWriter
Aliran keluaran menuliskan atau mengisi sebuah karakter array
FileReader
Aliran masukan yang membaca dari sebuah file
FileWriter
Aliran keluaran yang menuliskan ke sebuah file
FilterReader
Pembacaan aliran karakter yang ditapis
FilterWriter
Penulisan aliran karakter yang tertapis
InputStreamReader
Aliran masukan yang menerjemahkan dari byte menjadi karakter
LineNumberReader
Aliran masukan yang mencacah (menghitung) baris
OutputStreamWriter
Aliran keluaran yang menerjemahkan dari karakter menjadi byte
PipedReader
Pipa masukan
PipedWriter
Pipa keluaran
PrintWriter
Aliran keluaran yang memuat metode print() dan println()
PushbackReader
Aliran masukan yang menempatkan karakter-karakter ke aliran masukan
Reader
Kelas abstrak yang menjelaskan masukan aliran karakter
StringReader
Aliran masukan yang membaca dari sebuah string
StringWriter
Aliran keluaran yang menuliskan ke sebuah string
Writer
Kelas abstrak yang menjelaskan keluaran aliran karakter

Kelas abstrak Reader dan Writer mendefinisikan sejumlah metode kunci yang diimplementasikan oleh sub-subkelas yang mewarisinya. Dua metode paling penting adalah read() dan write(), yang membaca dan menulis karakter data.


Aliran-Aliran Pustaka
Seperti Anda ketahui, semua program Java secara otomatis mengimpor paket java.lang. Paket ini mendefinisikan sebuah kelas dengan nama System, yang mengenkapsulasi beberapa aspek pada lingkungan run-time Java. Sebagai contoh, dengan menggunakan metode-metode dari kelas System ini, Anda bisa memperoleh waktu terkini dan melakukan pengaturan berbagai properti terkait dengan sistem. Kelas System juga memuat tiga variabel aliran pustaka: in, out, dan err. Ini berarti bahwa baik variabel maupun metode dari kelas ini dapat digunakan pada tiap program Anda tanpa perlu mereferensi ke objek System tertentu.

System.out merujuk ke aliran keluaran standar. Secara default, ini merupakan konsol. System.out merujuk ke masukan standar, yang merupakan keyboard secara default. System.err merujsuk ke aliran error standar, yang juga merupakan konsol secara default. Semua aliran ini bisa diredireksi ke sembarang divais I/O yang kompatibel.

System.in merupakan sebuah objek bertipe InputStream, sedangkan System.out dan System.err merupakan objek bertipe PrintStream. Ketiganya merupakan aliran byte, meskipun umumnya digunakan untuk membaca dan menuliskan karakter dari dan kepada konsol. Seperti yang akan Anda lihat, And dapat membungkusnya ke dalam aliran-aliran berbasis karakter, jika Anda inginkan.


Masukan Konsol Pembacaan
Pada Java 1.0, satu-satunya cara untuk melakukan masukan konsol adalah menggunakan aliran byte. Sekarang, penggunaan aliran byte untuk membaca masukan konsol masih bisa diterima. Namun, untuk aplikasi-aplikasi komersial, metode yang lebih banyak digunakan untuk membaca masukan konsol adalah menggunakan aliran berorientasi karakter. Ini mempermudah program Anda untuk menginternasionalisasi.

Dalam Java, masukan konsol dilakukan dengan membaca System.in. Untuk mendapat sebuah aliran berbasis karakter yang melekat pada konsol, Anda bisa membungkus System.in di dalam sebuah objek BufferedReader. Kelas BufferedReader mendukung aliran masukan penyangga. Konstruktor yang umum digunakan ditunjukkan di sini:

BufferedReader(Reader pembacaMasukan)

Di sini, pembacaMasukan adalah aliran yang terhubung ke objek dari BufferedReader yang sedang diciptakan. Reader merupakan sebuah kelas abstrak. Salah satu dari subkelas konkritnya adalah InputStreamReader, yang mengkonversi byte menjadi karakter. Untuk memperoleh sebuah objek InputStreamReader yang terhubung ke System.in, Anda menggunakan konstruktor berikut:

BufferedReader br = new BufferReader(new InputStreamReader(System.in));

Setelah statemen ini dieksekusi, br menjadi sebuah aliran berbasis karakter yang terhubung ke konsol melalui System.in.


Membaca Karakter
Untuk membaca sebuah karakter dari objek BufferedReader, Anda menggunakan read(). Versi dari read() yang akan digunakan adalah

int read() throws IOException

Setiap kali read() dipanggil, ia membaca sebuah karakter dari aliran masukan dan menjadikannya nilai balik sebagai sebuah nilai integer. Metode ini menghasilkan -1 ketika akhir aliran dijumpai. Seperti yang dapat Anda lihat, metode ini melemparkan eksepsi IOException.

Program berikut mendemonstrasikan read() dengan membaca karakter-karakter dari konsol sampai pengguna mengetikkan “q”. Perhatikan bahwa setiap eksepsi I/O yang akan dihasilkan dilakukan melalui main(). Pendekatan semacam ini umum dilakukan ketika membaca dari konsol pada program-program contoh sederhana, tetapi pada aplikasi yang lebih kompleks, Anda dapat menangani eksepsi secara eksplisit:

// Menggunakan sebuah objek BufferedReader untuk membaca
// karakter dari konsol.
import java.io.*;

public class DemoBacaKarakter {
   public static void main(String args[]) throws IOException
   {
      char c;
      BufferedReader br = new
        BufferedReader(new InputStreamReader(System.in));
	
      System.out.println("Masukkan karakter-karakter, 'q' untuk keluar.");
	
      // membaca karakter-karakter
      do {
         c = (char) br.read();
         System.out.println(c);
      } while(c != 'q');
   }
}

Jika dijalankan, program ini menghasilkan contoh keluaran berikut:

Masukkan karakter-karakter, 'q' untuk keluar.
1234abcd
1
2
3
4
a
b
c
d
q

Keluaran ini tampak sedikit berbeda dari apa yang Anda harapkan karena System.in merupakan penyangga baris, secara default. Ini berarti bahwa tidak ada masukan yang secara aktual dilewatkan kepada program sampai Anda menekan ENTER.


Membaca String
Untuk membaca sebuah string dari keyboard, gunakan versi readLine() yang merupakan sebuah anggota dari kelas BufferedReader. Bentuk umumnya adalah berikut:

String readLine() throws IOException

Seperti yang dapat Anda lihat, metode ini menghasilkan sebuah objek String.

Program berikut mendemonstrasikan BufferedReader dan metode readLine(); program membaca dan menampilkan baris teks sampai Anda memasukkan kata “berhenti”:

// Membaca string dari konsol menggunakan objek BufferedReader.
import java.io.*;

public class DemoBacaString {
   public static void main(String args[]) throws IOException
   {
      // menciptakan sebuah objek BufferedReader menggunakan System.in
      BufferedReader br = new BufferedReader(new
         InputStreamReader(System.in));
	
      String str;

      System.out.println("Masukkan sebaris teks.");
      System.out.println("Masukkan 'berhenti' untuk keluar.");
	
      do {
         str = br.readLine();
         System.out.println(str);
      } while(!str.equals("berhenti"));
   }
}

Jika dijalankan, program ini menghasilkan contoh keluaran berikut:

Masukkan sebaris teks.
Masukkan 'berhenti' untuk keluar.
satu
satu
dua
dua
tiga
tiga
berhenti
berhenti

Contoh selanjutnya menciptakan sebuah editor teks kecil, yang menciptakan sebuah array yang memuat objek-objek String dan kemudian membaca baris-baris teks dan menyimpan tiap baris tersebut ke dalam array. Program akan membaca sampai 100 baris atau sampai Anda memasukkan “berhenti”. Program menggunakan BufferedReader untuk membaca dari konsol.

// Editor teks kecil.
import java.io.*;

public class EditorKecil {
   public static void main(String args[]) throws IOException
   {
      // Menciptakan objek BufferedReader menggunakan System.in
      BufferedReader br = new BufferedReader(new
         InputStreamReader(System.in));
	
      String str[] = new String[100];
	
      System.out.println("Masukkan sebaris teks.");
      System.out.println("Masukkan 'berhenti' untuk keluar.");
	
      for(int i=0; i<100; i++) {
         str[i] = br.readLine();
         if(str[i].equals("berhenti")) break;
      }
	
      System.out.println("\nBerikut adalah file Anda:");
	
      // Menampilkan baris-baris string
      for(int i=0; i<100; i++) {
         if(str[i].equals("berhenti")) break;
         System.out.println(str[i]);
      }
   }
}

Jika dijalankan, program ini menghasilkan contoh keluaran berikut:

Masukkan sebaris teks.
Masukkan 'berhenti' untuk keluar.
satu
dua
tiga
empat
berhenti

Berikut adalah file Anda:
satu
dua
tiga
empat


Menulis Keluaran Konsol
Keluaran konsol paling mudah dilakukan dengan print() dan println(), yang telah dijelaskan sebelumnya, yang dipakai pada kebanyakan contoh pada buku ini. Kedua metode ini didefinisikan oleh kelas PrintStream (yang merupakan tipe data objek yang direferensi oleh Stream.out). Meskipun System.out merupakan sebuah aliran byte, penggunaannya pada keluaran contoh sederhana masih bisa diterima.

Karena PrintStream merupakan sebuah aliran keluaran yang diderivasi dari OutputStream, kelas itu juga mengimplementasikan metode level-renda write(). Jadi, write() dapat dipakai menuliskan data pada konsol. Bentuk paling sederhana dari write() yang didefinisikan oleh PrintStream ditunjukkan di sini:

void write(int nilaibyte)

Metode ini menuliskan byte yang ditetapkan oleh nilaibyte. Meskipun nilaibyte dideklarasikan sebagai integer, hanya delapan bit orde-rendah yang akan dituliskan. Berikut adalah sebuah contoh pendek yang menggunakan write() untuk menampilkan karakter “A” yang diikuti oleh baris baru di layar:

// Mendemonstrasikan System.out.write().
public class DemoWrite {
   public static void main(String args[]) {
      int b;
      b = 'A';
		
      System.out.write(b);
      System.out.write('\n');
   }
}


Kelas PrintWriter
Meskipun penggunaan System.out untuk menuliskan data pada konsol dapat diterima, penggunaan tersebut hanya diperuntukkan untuk program sederhana seperti pada buku ini. Pada program kompleks, metode yang direkomendasikan untuk menuliskan data pada konsol ketika menggunakan Java adalah melalui aliran PrintWriter. Kelas ini merupakan salah satu kelas berbasis karakter. Dengan menggunakan kelas berbasis karakter, proses internasionalisasi program menjadi lebih mudah.

Kelas PrintWriter mendefinisikan beberapa konstruktor. Salah satunya akan digunakan di sini:

PrintWriter(OutpurStream aliranKeluaran, boolean kendaliAliran)

Di sini, aliranKeluaran merupakan sebuah objek bertipe OutputStream, dan kendaliAliran mengendalikan apakah Java menyemburkan atau mengalirkan aliran keluaran setiap kali metode println() dipangil. Jika kendaliAliran bernilai true, maka pengaliran data terjadi secara otomatis. Jika false, pengaliran data tidak otomatis.

Kelas PrintWriter mendukung metode print() dan println(). Jadi, Anda bisa menggunakan kedua metode ini dengan cara sama seperti Anda menggunakannya dengan System.out. Jika argumen bukan tipe data sederhana, maka metode-metode dari PrintWriter akan memanggil metode dari objek toString() dan kemudian menampilkan hasilnya.

Untuk menampilkan atau menuliskan data pada konsol menggunakan PrintWriter, baris kode ini menciptakan sebuah objek PrintWriter yang terkoneksi ke keluaran konsol:

PrintWriter = pw = new PrintWriter(System.out, true);

Aplikasi berikut mengilustrasikan penggunaan objek PrintWriter untuk menangani keluaran konsol:

Aplikasi berikut mengilustrasikan penggunaan objek PrintWriter untuk menangani keluaran konsol:

// Mendemonstrasikan PrintWriter
import java.io.*;

public class DemoPrintWriter {
   public static void main(String args[]) {
      PrintWriter pw = new PrintWriter(System.out, true);

      pw.println("Ini adalah sebuah string");
      
      int i = -7;
      pw.println(i);
      
      double d = 4.5e-7;	
      pw.println(d);
   }
}

Jika dijalankan, program ini menghasilkan contoh keluaran berikut:

Ini adalah sebuah string
-7
4.5E-7

Ingat, tidak ada salahnya menggunakan System.out untuk menampilkan teks sederhana pada konsol ketika Anda sedang belajar Java atau mendebug program Anda. Namun, penggunaan PrintWriter membuat aplikasi Anda lebih mudah untuk diinternasionalisasi. Karena tidak banyak keuntungan yang didapatkan dalam menggunakan PrintWriter untuk program-program sederhana pada buku ini, penggunaan System.out akan dilanjutkan pada buku ini.


Membaca dan Menulis File
Java menyediakan sejumlah kelas dan metode yang bisa dipakai untuk membaca dari dan menuliskan ke dalam file. Tujuannya di sini adalah untuk mengenalkan teknik-teknik dasar dalam membaca dari dan menuliskan ke dalam file. Meskipun aliran byte yang digunakan, teknik-teknik di sini dapat diadaptasi menjadi aliran berbasis karakter.

Dua kelas aliran yang paling umum digunakan adalah FileInputStream dan FileOutputStream, yang menciptakan aliran byte yang terhubung ke file. Untuk membuka sebuah file, Anda hanya perlu menciptakan sebuah objek dari salah satu dari dua kelas ini, dengan menetapkan nama file sebagai argumen kepada konstruktornya. Meskipun kedua kelas mendukung keberadaan sejumlah konstruktor lain, berikut merupakan bentuk konstruktor yang akan digunakan:

FileInputStream (String namafile) throws FileNotFoundException
FileOutputStream (String namafile) throws FileNotFoundException

Di sini, namafile menyatakan nama dari file yang ingin dibuka. Ketika Anda menciptakan sebuah aliran masukan, jika file tersebut tidak ada, maka eksepsi FileNotFoundException akan dilemparkan. Untuk aliran keluaran, jika file tidak bisa dibuka atau diciptakan, maka eksepsi FileNotFoundException akan dilemparkan. Ketika sebuah file keluaran dibuka, semua file yang memiliki nama sama akan dihancurkan.

Ketika Anda selesai menggunakan file tertentu, Anda harus menutupnya. Ini dilakukan dengan memanggil metode close(), yang diimplementasikan oleh kedua kelas FileInputStream maupun FileOutputStream. Hal itu ditunjukkan berikut:

void close() throws IOException

Penutupan file akan melepaskan sumberdaya sistem yang dialokasikan kepada file, sehingga sumberdaya tersebut bisa digunakan untuk file lain. Kegagalan menutup file dapat menyebabkan “kebocoran memori” karena sumberdaya yang tak digunakan tetap dialokasikan.

Untuk membaca dari sebuah file, Anda bisa menggunakan sebuah versi dari read() yang didefinisikan di dalam FileInputStream. Versi read() yang digunakan pada buku ini:

int read() throws IOException

Setiap kali metode ini dipanggil, ia membaca satu byte dari file dan menghasilkan byte tersebut sebagai sebuah nilai integer. Metode read() menghasilkan -1 ketika akhir file ditemui. Metode ini dapat melemparkan ekspesi IOException.

Program berikut menggunakan read() untuk membaca dan menampilkan isi dari sebuah file yang memuat teks ASCII. Nama file ditetapkan sebagai argumen perintah-baris:

Nama file ditetapkan sebagai argumen perintah-baris:

/*Menampilkan sebuah file teks.
* Untuk menggunakan program ini,
* tetapkan nama file yang akan Anda lihat
* Contoh, untuk melihat sebuah file dengan
* nama UJI.TXT, gunakan perintah baris
* java TampilFile UJI.TXT
*/
import java.io.*;

public class TampilFile {
   public static void main(String args[])
   {
      int i;
      FileInputStream fin;
	
      // Pertama, memastikan bahwa nama file telah diberikan.
      if(args.length != 1) {
         System.out.println("Nama file belum diberikan");
         return;
      }
	
      // Mencoba membuka file.
      try {
         fin = new FileInputStream(args[0]);
      } catch(FileNotFoundException e) {
         System.out.println("Tidak bisa membuka file");
         return;
      }
	
      // Pada titik ini, file telah dibuka dan dapat dibaca.
      // Berikut membaca karakter-karakter sampai EOF ditemui.
      try {
         do {
            i = fin.read();
            if(i != -1) System.out.print((char) i);
         } while(i != -1);
      } catch(IOException e) {
         System.out.println("Error Pembacaan File");
      }
	
      // Menutup file.
      try {
         fin.close();
      } catch(IOException e) {
         System.out.println("Error Penutupan File");
      }
	}
}

Pada program, perhatikan blok-blok try/catch yang menangani error I/O yang bisa saja terjadi. Setiap operasi I/O memonitor eksepsi, dan jika sebuah eksepsi terjadi, ia akan ditangani.



Selanjutnya  >>>





No comments: