Kamis, 08 Maret 2012

Operasi pada Aliran (Stream)

Kelas dasar I/O Reader, Writer, InputStream dan OutputStream hanya menyediakan operasi I/O sangat dasar. Misalnya, kelas InputStream memiliki metode instansi
public int read() throws IOException
untuk membaca satu byte data dari aliran input. Jika sampai pada akhir dari aliran input , metode read() akan mengembalikan nilai -1. Jika ada kesalahan yang terjadi pada saat pengambilan input, maka pengecualian IOException akan dilemparkan. Karena IOException adalah kelas pengecualian yang harus ditangani, artinya kita harus menggunakan metode read() di dalam penyataan try atau mengeset subrutin untuk throws IOException.
Kelas InputStream juga memiliki metode untuk membaca beberapa byte data dalam satu langkah ke dalam array byte. Akan tetapi InputStream tidak memiliki metode untuk membaca jenis data lain, seperti int atau double dari aliran. Ini bukan masalah karena dalam prakteknya kita tidak akan menggunakan objek bertipe InputStream secara langsung. Yang akan kita gunakan adalah kelas turunan dari InputStream yang memiliki beberapa metode input yang lebih beragam daripada InputStream itu sendiri.
Begitu juga dengan kelas OutputStream memiliki metode output primitif untuk menulis satu byte data ke aliran output, yaitu metode
public void write(int b) throws IOException
Tapi, kita hampir pasti akan menggunakan kelas turunannya yang mampu menangani operasi yang lebih kompleks.
Kelas Reader dan Writer memiliki operasi dasar yang hampir sama, yaitu read dan write, akan tetapi kelas ini berorientasi karakter (karena digunakan untuk membaca dan menulis data yang bisa dibaca manusia). Artinya operasi baca tulis akan mengambil dan menulis nilai char bukan byte. Dalam prakteknya kita akan menggunakan kelas turunan dari kelas-kelas dasar ini.

Salah satu hal menarik dari paket I/O pada Java adalah kemungkinan untuk menambah kompleksitas suatu aliran dengan membungkus aliran tersebut dalam objek aliran lain. Objek pembungkus ini juga berupa aliran, sehingga kita juga bisa melakukan baca tulis dari objek yang sama dengan tambahan kemampuan dalam objek pembungkusnya.
Misalnya, PrintWriter adalah kelas turunan dari Writer yang memiliki metode tambahan untuk menulis tipe data Java dalam karakter yang bisa dibaca manusial. Jika kita memiliki objek bertipe Writer atau turunannya, dan kita ingin menggunakan metode pada PrintWriter untuk menulis data, maka kita bisa membungkus objek Writer dalam objek PrintWriter.
Contoh jika baskomKarakter bertipe Writer, maka kita bisa membuat
PrintWriter printableBaskomKarakter = new PrintWriter(baskomKarakter);
Ketika kita menulis data ke printableBaskomKarakter dengan menggunakan metode pada PrintWriter yang lebih canggih, maka data tersebut akan ditempatkan di tempat yang sama dengan apabila kita menulis langsung pada baskomKarakter. Artinya kita hanya perlu membuat antar muka yang lebih baik untuk aliran output yang sama. Atau dengan kata lain misalnya kita bisa menggunakan PrintWriter untuk menulis file atau mengirim data pada jaringan.
Untuk lengkapnya, metode pada kelas PrintWriter memiliki metode sebagai berikut :
// Metode untuk menulis data dalam
// bentuk yang bisa dibaca manusia
public void print(String s)
public void print(char c)
public void print(int i)
public void print(long l)
public void print(float f)
public void print(double d)
public void print(boolean b)
 
// Menulis baris baru ke aliran
public void println()
 
// Metode ini sama dengan di atas
// akan tetapi keluarannya selalu
// ditambah dengan baris baru
public void println(String s)
public void println(char c)
public void println(int i)
public void println(long l)
public void println(float f)
public void println(double d
public void println(boolean b)
Catatan bahwa metode-metode di atas tidak pernah melempar pengecualian IOException. Akan tetapi, kelas PrintWriter memiliki metode
public boolean checkError()
yang akan mengembalikan true jika ada kesalahan yang terjadi ketika menulis ke dalam aliran. Kelas PrintWriter menangkap pengecualian IOException secara internal, dan mengeset nilai tertentu di dalam kelas ini jika kesalahan telah terjadi. Sehingga kita bisa menggunakan metode pada PrintWriter tanpa khawatir harus menangkap pengecualian yang mungkin terjadi. Akan tetapi, jika kita ingin membuat progam yang tangguh tentunya kita harus selalu memanggil checkError() untuk melihat apakah kesalahan telah terjadi ketika kita menggunakan salah satu metode pada PrintWriter.

Ketika kita menggunakan metode PrintWriter untuk menulis data ke aliran, data tersebut diubah menjadi rangkaian karakter yang bisa dibaca oleh manusia. Bagaimana caranya jika kita ingin membuat data dalam bentuk bahasa mesin?
Paket java.io memiliki kelas aliran byte, yaitu DataOutputStream yang bisa digunakan untuk menulis suatu data ke dalam aliran dalam format biner. DataOutputStream berhubungan erat dengan OutputStream seperti hubungan antara PrintWriter dan Writer.
Artinya, OutputStream hanya berisi metode dasar untuk menulis byte, sedangkan DataOutputStream memiliki metode writeDouble(double x) untuk menulis nilai double, writeInt(int x) untuk menulis nilai int, dan seterusnya. Dan juga kita bisa membungkus objek bertipe OutputStream atau turunannya ke dalam aliran DataOutputStream sehingga kita bisa menggunakan metode yang lebih kompleks.
Misalnya, jika baskomByte adalah variabel bertipe OutputStream, maka
DataOutputStream baskomData = new DataOutputStream(baskomByte);
untuk membungkus baskomByte dalam baskomData.
Untuk mengambil data dari aliran, java.io memiliki kelas DataInputStream. Kita bisa membungkus objek bertipe InputStream atau turunannya ke dalam objek bertipe DataInputStream. Metode di dalam DataInputStream untuk membaca data biner bisa menggunakan readDouble(), readInt() dan seterusnya. Data yang ditulis oleh DataOutputStream dijamin untuk bisa dibaca kembali oleh DataInputStream, meskipun data kita tulis pada satu komputer dan data dibaca pada komputer jenis lain dengan sistem operasi berbeda. Kompatibilitas data biner pada Java adalah salah satu keunggulan Java untuk bisa dijalakan pada beragam platform.
Salah satu fakta yang menyedihkan tentang Java adalah ternyata Java tidak memiliki kelas untuk membaca data dalam bentuk yang bisa dibaca oleh manusia. Dalam hal ini Java tidak memiliki kelas kebalikan dari PrintWriter sebagaimana DataOutputStream dan DataInputStream. Akan tetapi kita tetap bisa membuat kelas ini sendiri dan menggunakannya dengan cara yang persis sama dengan kelas-kelas di atas.

Kelas PrintWriter, DataInputStream, dan DataOutputStream memungkinkan kita untuk melakukan input dan output semua tipe data primitif pada Java. Pertanyaannya bagaimana kita melakukan baca tulis suatu objek?
Mungkin secara tradisional kita akan membuat fungsi sendiri untuk memformat objek kita menjadi bentuk tertentu, misalnya urutan tipe primitif dalam bentuk biner atau karakter kemudian disimpan dalam file atau dikirim melalui jaringan. Proses ini disebut serialisasi (serializing) objek.
Pada inputnya, kita harus bisa membaca data yang diserialisasi ini sesuai dengan format yang digunakan pada saat objek ini diserialisasi. Untuk objek kecil, pekerjaan semacam ini mungkin bukan masalah besar. Akan tetapi untuk ukuran objek yang besar, hal ini tidak mudah.
Akan tetapi Java memiliki cara untuk melakukan input dan output isi objek secara otomatis, yaitu dengan menggunakan ObjectInputStream dan ObjectOutputStream. Kelas-kelas ini adalah kelas turunan dari InputStream dan OutputStream yang bisa digunakan untuk membaca dan menulis objek yang sudah diserialisasi.
ObjectInputStream dan ObjectOutputStream adalah kelas yang bisa dibungkus oleh kelas InputStream dan OutputStream lain. Artinya kita bisa melakukan input dan output objek pada aliran byte apa saja.
Metde untuk objek I/O adalah readObject() yang tersedia pada ObjectInputStream dan writeObject(Object obj) yang tersedia dalam ObjectOutputStream. Keduanya bisa melemparkan IOException. Ingat bahwa readObject() mengembalikan nilai bertipe Object yang artinya harus di-type cast ke tipe sesungguhnya.
ObjectInputStream dan ObjectOutputStream hanya bekerja untuk objek yang mengimplementasikan interface yang bernama Serializable. Lbih jauh semua variabel instansi pada objek harus bisa diserialisasi, karena interface Serializable tidak mempunyai metode apa-apa. Interface ini ada hanya sebagai penanda untuk kompiler supaya kompiler tahu bahwa objek ini digunakan untuk baca tulis ke suatu media.
Yang perlu kita lakukan adalah menambahkan "implements Serializable" pada definisi kelas. Banyak kelas standar Java yang telah dideklarasikan untuk bisa diserialisasi, termasuk semua komponen kelas Swing dan AWT. Artinya komponen GUI pun bisa disimpan dan dibaca dari dalam perangkat I/O menggunakan ObjectInputStream dan ObjectOutputStream.

Tidak ada komentar:

Posting Komentar