Bab.5 Masukan
dan Keluaran File
Tujuan Instruksional
|
|
·
Mengetahui bagaimana I/O diproses dalam JAVA.
·
Membedakan antara I/O teks dan I/O biner.
·
Menulis dan membaca byte menggunakan FileInputStream dan FileOutputStream.
·
Membaca dan menulis nilai primitif dan string
menggunakan DataInputStream/DataOutputStream.
·
Mengimplementasikan antarmuka Serializable
untuk membuat objek dapat diserialkan.
|
·
Menyimpan dan merestorasi objek menggunakan ObjectOutputStream dan ObjectInputStream, dan untuk memahami
bagaimana objek-objek diserialkan dan apa jenis objek yang bisa diserialkan.
·
Menyerialkan array.
·
Menulis dan menulis file menggunakan kelas RandomAccessFile.
|
5.1
Introduksi
Data yang disimpan dalam file teks
direpresentasikan dalam format yang dapat dibaca manusia. Data yang disimpan
dalam file biner disimpan dalam format biner. Anda tidak bisa membaca file
biner, karena hanya didesain untuk dibaca oleh program. Sebagai contoh, program
sumber JAVA disimpan dalam file teks dan dapat dibaca oleh editor teks, tetapi
kelas JAVA disimpan dalam file biner dan hanya bisa dibaca oleh JVM. Keuntungan
file biner adalah lebih efisien diproses daripada file teks.
Meskipun
secara teknis tidak presisi dan tepat, Anda bisa menganggap suatu file teks
memuat runtun karakter dan suatu file biner memuat runtun biner. Sebagai
contoh, integer desimal 199 disimpan
sebagai runtun tiga karakter ‘1’, ‘9’, ‘9’, di dalam file teks, dan integer yang sama sebagai nilai
tipe-byte C7 di dalam file biner,
karena desimal 199 sama dengan C7 dalam heksadesimal
.
JAVA
menawarkan banyak kelas untuk melaksanakan masukan dan keluaran file, yang
dapat dikategorikan sebagai kelas I/O teks dan kelas I/O biner.
5.2
Bagaimana I/O Ditangani JAVA
Ingat
bahwa objek File mengenkapsulasi
properti-properti suatu file tetapi tidak memuat metode-metode untuk
menulis/membaca data ke/dari suatu file. Untuk melaksanakan tugas I/O, Anda
perlu menciptakan objek dari kelas I/O JAVA yang sesuai. Objek tersebut memuat
metode-metode untuk menulis/membaca data ke/dari suatu file. Sebagai contoh,
untuk menulis teks ke suatu file bernama temp.txt,
Anda perlu menciptakan suatu objek menggunakan kelas PrintWriter sebagai berikut:
PrintWriter keluaran = new
PrintWriter("temp.txt");
Anda sekarang
dapat memanggil metode print dari objek tersebut untuk menulis suatu string ke
dalam file. Sebagai contoh, statemen berikut menulis “JAVA itu Tangguh!” ke dalam file:
keluaran.print("JAVA
itu Tangguh!");
Statemen
berikut ini menutup file tersebut:
keluaran.close();
Terdapat
banyak kelas I/O untuk berbagai tujuan. Pada umumnya, dapat diklasifikasikan
menjadi kelas masukan dan kelas keluaran. Kelas masukan memuat metode-metode
untuk membaca data, dan kelas keluaran mamuat metode-metode untuk menulis data.
PrintWriter merupakan suatu contoh
kelas keluaran, dan Scanner adalah
suatu contoh kelas masukan. Kode berikut menciptakan suatu objek masukan untuk
file temp.txt dan membaca data dari
file itu:
Scanner masukan =
new Scanner(new
File("temp.txt"));
System.out.println(masukan.nextLine());
Jika
file temp.txt memuat “JAVA itu Tangguh!”, maka
masukan.nextLine() menghasilkan string “JAVA
itu Tangguh!”.
Gambar
5.1 mengilustrasikan pmerograman I/O JAVA. Suatu objek masukan membaca aliran
data dari suatu file, dan suatu objek keluaran menulis aliran data ke dalam
suatu file. Objek masukan disebut dengan aliran masukan dan objek keluaran
disebut dengan aliran keluaran.
Gambar
5.1 Program menerima data dari suatu
objek masukan dan mengirimkan data melalui suatu objek keluaran
5.3
I/O Teks dan I/O Biner
Komputer
tidak membedakan file biner dan file teks. Semua file disimpan dalam format
biner, jadi semua file secara esensial adalah file biner. I/O teks dibangun
berdasarkan I/O biner untuk menyediakan suatu level abstraksi dalam pengkodean
dan pendekodean, seperti tertampil pada Gambar 5.2a. Pengkodean dan pendekodean
secara otomatis dilakukan untuk I/O teks. JVM mengubah Unicode menjadi
pengkodean berbasis-file ketika menulis suatu karakter dan mengubah pengkodean
berbasis-file menjadi Unicode ketika membaca suatu karakter. Sebagai contoh, dimisalkan
Anda menulis string “199”
menggunakan I/O teks ke dalam file. Setiap karakter ditulis ke dalam file.
Karena Unicode untuk ‘1’ adalah 0x0031, Unicode 0x0031 dikonversi menjadi suatu kode yang bergantung dari skema
pengkodean untuk file terkait. (Perhatikan bahwa 0x menandakan bilangan heksadesimal). Pengkodean yang umum dipakai
adalah ASCII. Kode ASCII untuk karakter ‘1’
adalah 49 (0x31 dalam heksadesimal) dan untuk karakter ‘9’ adalah 57 (0x39 dalam heksadesimal). Jadi, menulis
karakter-karakter “199” adalah sama dengan mengirimkan tiga byte—0x31, 0x39,
0x39— pada keluaran, seperti tertampil pada Gambar 5.2a.
I/O
biner tidak memerlukan konversi. Jika Anda menulis suatu nilai numerik ke dalam
file menggunakan I/O biner, nilai eksaknya di dalam memori disalin ke dalam
file tersebut. Sebagai contoh, suatu nilai tipe-byte 199 direpresentasikan
sebagai 0xC7
di dalam memori dan muncul persis sama di
dalam file, seperti tertampil pada Gambar 5.2b. Ketika Anda membaca satu byte
menggunakan I/O biner, satu nilai byte dibaca dari masukan.
Gambar
5.2 I/O teks memerlukan pengkodea dan
pendekodean, sedangkan I/O biner tidak
5.4
Kelas I/O Biner
Perancangan
kelas-kelas I/O JAVA merupakan contoh penerapan pewarisan, dimana
operasi-operasi umum digeneralisasi di dalam superkelas, dan sub-subkelas
menyediakan operasi-operasi yang lebih spesifik. Gambar 5.3 menampilkan beberapa
kelas yang melaksanakan I/O biner. InputStream
merupakan akar untuk semua kelas-kelas masukan biner, dan OutputStream merupakan akar untuk semua kelas-kelas keluaran biner.
Gambar 5.4 dan Gambar 5.5 mencantumkan semua metode di dalam InputStream dan OutputStream.
Gambar
5.3 InputStream, OutputStream,
dan sub-subkelasnya untuk I/O biner
Gambar
5.4 Kelas abstrak InputStream mendefinisikan
metode-metode aliran byte masukan
Gambar
5.5 Kelas abstrak OutputStream mendefinisikan
metode-metode aliran byte keluaran
5.4.1
FileInputStream/FileOutputStream
FileInputStream dan FileOutputStream digunakan untuk
membaca/menulis dari/ke file. Semua metode di dalam kedua kelas ini diwarisi
dari InputStream dan OutputStream. FileInputStream dan FileOutputStream
tidak mengintroduksi metode-metode baru. Untuk menciptakan suatu
FileInputStream, digunakan konstruktor-konstruktor berikut, seperti tertampil
pada Gambar 5.6.
Gambar
5.6 FileInputStream memasukkan aliran byte dari file
Ekspesi java.io.FileNotFoundException akan
terjadi bila Anda mencoba untuk menciptakan suatu FileInputStream dari file yang tidak ada. Untuk menciptakan suatu FileOutputStream, digunakan
konstruktor-konstruktor berikut, seperti tertampil pada Gambar 5.7.
Gambar
5.7 FileOutputStream mengeluarkan
aliran byte ke dalam file
Jika
file tidak ada, suatu file baru akan diciptakan. Jika file sudah ada, maka dua
konstruktor pertama akan menghapus isi semula dari file itu. Untuk tetap
mempertahankan isi semula file dan menyambungnya dengan data baru, digunakan
dua konstruktor terakhir dengan melewatkan parameter true dan append.
Hampir
semua metode di dalam kelas-kelas I/O melempar java.io.IOException. Oleh karena itu, Anda harus mendeklarasikan java.io.IOException untuk melemparkan eksepsi
di dalam metode atau menempatkan kode di dalam blok try-catch, seperti ini:
Kode5.1
menggunakan I/O biner untuk menulis nilai-nilai sepuluh byte dari 1 sampai 10
ke dalam suatu file bernama temp.dat
dan membacanya kembali dari file tersebut.
Kode5.1 UjiAliranFile.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.io.*;
public class UjiAliranFile {
public static void main(String[]
args) throws IOException{
// Menciptakan suatu alitan keluaran ke
file
FileOutputStream keluaran = new FileOutputStream("temp.dat");
// Mengeluarkan nilai-nilai ke file
for (int i = 1; i <= 10;
i++)
keluaran.write(i);
// Menutup aliran keluaran
keluaran.close();
// Menciptakan suatu alitan masukan
FileInputStream masukan = new FileInputStream("temp.dat");
// Membaca nilai-nilai dari file
int nilai;
while ((nilai = masukan.read()) != -1)
System.out.print(nilai + " ");
// Menutup aliran keluaran
masukan.close();
}
}
|
Keluaran
1 2 3 4
5 6 7 8 9 10
Suatu FileOutputStream diciptakan untuk file temp.dat pada baris 6. Loop for menulis
sepuluh nilai byte ke dalam file (baris 9-10). Pemanggilan write(i) sama dengan pemanggilan write((byte)i). Baris 13 menutup aliran keluaran. Baris 16
menciptakan suatu FileInputStream
untuk file temp.dat. Nilai-nilai
dibaca dari file dan ditampilkan pada konsol pada baris 19-21. Ekspresi ((nilai = masukan.read()) != -1) (baris 20) membaca
suatu byte dari masukan.read(), menugaskannya kepada nilai, dan
memeriksa apakah bernilai -1. Jika bernilai -1, maka menandakan akhir suatu
file.
File
yang diciptakan dalam contoh ini adalah file biner, yang dapat dibaca oleh
program JAVA tetapi tidak dari suatu editor teks, seperti tertampil pada Gambar
5.8.
Gambar
5.8 Suatu file biner tidak bisa
ditampilkan dalam mode teks
5.4.2
FilterInputStream/FilterOutputStream
Aliran
filter merupakan aliran yang memilter byte-byte untuk tujuan tertentu. Aliran
masukan byte menyediakan metode read
yang dapat dipakai hanya untuk membaca. Jika Anda membaca integer, double, atau
string, Anda memerlukan suatu kelas filter menyatukan aliran masukan byte. Penggunaan
suatu kelas filter memampukan Anda untuk membaca integer, double, atau string,
bukan byte maupun karakter. FilterInputStream
dan FilterOutputStream merupakan dua
kelas basis untuk memilter data. Jika Anda perlu memproses tipe-tipe numerik
primitif, Anda bisa menggunakan DataInputStream
dan DataOutputStream untuk memilter
byte.
5.4.3
DataInputStream/DataOutputStream
DataInputStream membaca
data byte dari aliran dan mengkonversinya menjadi nilai tipe primitif atau
menjadi string. DataOutputStream
mengkonversi nilai tipe primitif atau string menjadi byte dan menempatkan byte
tersebut ke aliran.
DataInputStream
mewarisi FilterInputStream dan
mengimplementasikan antarmuka DataInput,
seperti tertampil pada Gambar 5.9. DataOutputStream
mewarisi FilterOutputStream dan
mengimplementasikan antarmuka DataOutput,
seperti tertampil pada Gambar 5.10.
DataInputStream
mengimplementasikan metode-metode yang didefinisikan di dalam
antarmuka
DataInput untuk membaca nilai-nilai
tipe primitif dan string. DataOtuputStream
mengimplementasikan metode-metode yang didefinisikan di dalam antarmuka DataOutput untuk menulis nilai-nilai tipe primitif dan string.
Nilai-nilai primitif disalin dari memori ke keluaran tanpa adanya konversi.
Karakter-karakter di dalam suatu string dapat dituliskan dalam beberapa cara,
seperti didiskusikan pada bagian selanjutnya.
Gambar
5.9 DataInputStream memilter suatu aliran byte masukan menjadi nilai
tipe primitif dan string
Gambar
5.10 DataOutputStream memampukan Anda untuk menulis nilai tipe primitif
dan string ke dalam suatu aliran keluaran
Karakter
dan String di dalam I/O Biner
Suatu
Unicode terdiri-dari dua byte. Metode writeChar(char
c) menulis Unciode atas karakter c
ke keluaran. Metode writeString(String
s) menulis Unicode atas setiap karakter di dalam string s ke keluaran. Metode writeBytes(String s) menulis byte rendah
dari Unicode atas setiap karakter di dalam string s ke keluaran. Byte tinggi dari Unicode dibuang. Metode writeBytes cocok untuk string yang
memuat karakter-karakter ASCII, karena suatu kode ASCII disimpan hanya di dalam
byte rendah dari Uncode. Jika suatu string memuat karakter-karakter non-ASCII,
maka Anda harus menggunakan metode writeChar
untuk menulis string.
Metode writeUTF(String s) menulis dua byte
dari informasi ke aliran keluaran, diikuti dengan representasi UTF-8 termodifikasi atas setiap
karakter di dalam string s. UTF-8
merupakan suatu skema pengkodean yang mengijinkan sistem untuk beroperasi
dengan ASCII maupun dengan Unicode. Kebanyakan sistem operasi menggunakan
ASCII. JAVA menggunakan Unicode. Himpunan karakter ASCII merupakan subhimpunan
dari himpunan karakter Unicode. Karena kebanyakan aplikasi hanya membutuhkan
himpunan karakter ASCII, hal yang percuma untuk merepresentasikan suatu
karakter ASCII 8-bit sebagai suatu karakter Unicode 16-bit. Skema pengkodean
UTF-8 termodifikasi menyimpan suatu karakter menggunakan satu, dua, tiga byte. Karakter-karakter
dikodekan dalam satu byte jika kodenya lebih rendah atau sama dengan 0x7F, dalam dua byte jika kodenya lebih
besar dari 0x7F dan lebih kecil dari
atau sama dengan 0x7FF, dalam tiga
byte jika kodenya lebih besar dari 0x7FF.
Biti-bit
awal dari suatu karakter UTF-8 mengindikasikan apakah suatu karakter disimpan
dalam satu byte, dua byte, atau tiga byte. Jika bit pertama adalah 0, maka
karakternya disimpan dalam satu byte. Jika bit-bit pertama adalah 110, maka
maka karakternya disimpan dalam dua byte. Jika bit-bit pertama adalah 1110,
maka maka karakternya disimpan dalam tiga byte. Informasi yang mengindikasikan
jumlah karakter di dalam suatu string disimpan dalam dua byte pertama yang
mendahului karakter-karakter UTF-8. Sebagai contoh, writeUTF(“ABCDEF”) sebenarnya menulis delapan
byte (00 06 41 42 43 44 45 46) ke
dalam file, karena dua byte pertama menyimpan jumlah karakter di dalam string.
Metode writeUTF(String s) mengkonversi suatu
string menjadi suatu runtun byte di dalam format UTF-8 dan menuliskannya ke
dalam suatu aliran biner. Metode readUTF()
membaca suatu string yang telah ditulis menggunakan metode writeUTF().
Format
UTF-8 memiliki keuntungan karena menghemat satu byte untuk setiap karakter
ASCII, karena karakter Unicode memerlukan dua byter dan suatu karakter ASCII
dalam UTF-8 hanya memerlukan satu byte. Jika kebanyakan karakter di dalam suatu
string panjang adalah karakter-karakter ASCII biasa, maka penggunaan UTF-8
sangat efisien.
Menggunakan
DataInputStream/DataOutputStream
Aliran
masukan dan aliran keluaran diciptakan menggunakan konstruktor-konstruktor
berikut ini (lihat Gambar 5.9 dan 5.10):
public DataInputStream(InputStream
instream)
public DataOutputStream(OutputStream
outstream)
Statemen-statemen
berikut menciptakan aliran-aliran data. Statemen pertama menciptakan suatu aliran
masukan untuk file in.dat; statemen
kedua menciptakan suatu aliran keluaran untuk file out.dat:
DataInputStream input =
new DataInputStream (new
FileInputStream("in.dat"));
DataOutputStream output =
new
DataOutputStream
(new FileOutputStream("out.dat"));
Kode5.2
nama dan skor mahasiswa ke dalam suatu file bernama temp.dat dan membaca data kembali dari file itu.
Kode5.2 UjiAliranData.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
|
import java.io.*;
public class UjiAliranData {
public static void main(String[]
args) throws IOException {
// Menciptakan suatu aliran keluaran untuk
file temp.dat
DataOutputStream keluaran =
new DataOutputStream(new FileOutputStream("temp.dat"));
// Menulis nama dan skor mahasiswa ke file
keluaran.writeUTF("Robert");
keluaran.writeDouble(85.5);
keluaran.writeUTF("Sintong");
keluaran.writeDouble(185.5);
keluaran.writeUTF("Rico");
keluaran.writeDouble(105.25);
// Menutup aliran keluaran
keluaran.close();
// Menciptakan suatu aliran masukan untuk
file temp.dat
DataInputStream masukan =
new DataInputStream(new
FileInputStream("temp.dat"));
//
Membaca nama skor mahasiswa dari file
System.out.println(masukan.readUTF() + " " + masukan.readDouble() );
System.out.println(masukan.readUTF() +
" " + masukan.readDouble());
System.out.println(masukan.readUTF() +
" " + masukan.readDouble());
}
}
|
Keluaran
Robert
85.5
Sintong
185.5
Rico
105.25
Suatu DataOutputStream diciptakan untuk file temp.dat pada baris 6-7. Nama dan skor
mahasiswa ditulis ke dalam file pada baris 10-15. Baris 18 menutup aliran
keluaran. Suatu DataInputStream
diciptakan untuk file yang sama pada baris 21-22. Nama dan skor mahasiswa
dibaca kembali dari file dan ditampilkan pada konsol pada baris 25-27.
DataInputStream dan DataOutputStream membaca dan menulis
nilai tipe primitif dan string JAVA dalam teknik bebas-mesin, sehingga
memampukan Anda untuk menulis suatu file data pada suatu mesin dan membacanya
pada mesin yang lain yang memiliki sistem operasi atau struktur file yang
berbeda.
Mendeteksi
Akhir Suatu File
Jika
Anda tetap membaca data pada akhir dari suatu InputStream, maka EOFException
akan terjadi. Eksepsi ini digunakan untuk mendeteksi akhir dari suatu file,
seperti tertampil pada kode5.3.
Kode5.3 DeteksiAkhirFile.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.io.*;
public class DeteksiAkhirFile {
public
static void main(String[] args) {
try {
DataOutputStream keluaran = new
DataOutputStream
(new FileOutputStream("uji.dat"));
keluaran.writeDouble(4.5);
keluaran.writeDouble(43.25);
keluaran.writeDouble(3.2);
keluaran.close();
DataInputStream masukan = new
DataInputStream
(new FileInputStream("uji.dat"));
while (true) {
System.out.println(masukan.readDouble());
}
}
catch (EOFException ex) {
System.out.println("Semua data
terbaca");
}
catch (IOException ex) {
ex.printStackTrace();
}
}
}
|
Keluaran
4.5
43.25
3.2
Semua
data terbaca
Program
menulis tiga nilai double ke dalam file menggunakan DatOutputStream (baris 6-10), dan membaca data menggunakan DataInputStream (baris 13-17). Ketika
pembacaan melewati akhir file, suatu eksepsi EOFException dilempar. Eksepsi ini
ditangkap pada baris 19.
5.4.4
BufferedInputStream/BufferedOutputStream
BufferedInputStream/BufferedOutputStream dapat digunakan
untuk mempercepat masukan dan keluaran dengan mengurangi jumlah pembacaan dan
penulisan. BufferedInputStream/BufferedOutputStream tidak memuat
metode-metode baru. Semua metode di dalam BufferedInputStream/BufferedOutputStream diwariskan dari
kelas InputStream/OutputStream. BufferedInputStream /Buffered-
OutputStream menambah suatu penyangga di dalam aliran untuk menyimpan
byte-byte agar pemrosesan berjalan lebih efisien.
Anda
bisa menciptakan objek BufferedInputStream/BufferedOutputStream menggunakan
konstruktor-konstruktor, seperti tertampil pada Gambar 5.11 dan Gambar 5.12.
Gambar
5.11 BufferedInputStream menyangga aliran masukan
Gambar
5.12 BufferedOutputStream menyangga aliran keluaran
Jika
tidak ada ukuran penyangga yang ditentukan, ukuran defaultnya adalah 512 byte.
Suatu aliran masukan tersangga membaca sebanyak mungkin data ke dalam penyangga
di dalam suatu pemanggilan tunggal. Sebaliknya, suatu aliran keluaran tersangga
memanggil metode write hanya bila penyangga penuh atau bila metode flush() dipanggil.
Anda
dapat memperbaiki kinerja program UjiAliranData.java
dalam contoh terdahulu dengan menambahkan penyangga-penyangga di dalam aliran
pada baris 6-7 dan 13-14 sebagai berikut:
DataOutputStream keluaran = new DataOutputStream(
new BufferedOutputStream (new FileOutputStream("temp.dat")));
DataInputStream masukan = new DataInputStream(
new BufferedInputStream (new FileInputStream("temp.dat")));
5.5
Masalah: Menyalin File
Bagian
ini akan mengembangkan suatu program untuk menyalin file. Pengguna perlu
menyediakan suatu file sumber dan suatu file target sebagai argumen-argumen
command-line menggunakan perintah sebagai berikut:
java Salin sumber target
Program
menyalin suatu file sumber ke file target dan menambilkan jumlah byte yang ada
di dalam file. Jika file sumber tidak ada, maka pengguna diberitahu bahwa file
tidak dapat ditemukan. Jika file target sudah ada, maka pengguna diberitahu
bahwa file tersebut ada. Contoh keluaran program ditampilkan pada Gambar 4.13
berikut:
Gambar
5.13 Program menyalin suatu file
Untuk
menyalin isi dari file sumber ke file target, sangat cocok bila menggunakan
aliran masukan biner untuk membaca byte dari file sumber dan aliran keluaran
biner untuk mengirim byte ke file target, apapun isi file sumber tersebut. File
sumber dan file target ditentukan dari command line. Suatu InputFileStream diciptakan untuk file sumber dan suatu OutputFileStream diciptakan untuk file
target. Metode read() dipakai untuk
membaca suatu byte dari aliran masukan, dan metode write(b) digunakan untuk menulis byte ke aliran keluaran. BufferedInputStream dan BufferedOutputStream digunakan untuk
memperbaiki kinerja. Kode5.4 memberikan solusi terhadap masalah ini.
Kode5.4 Salin.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
9
40
41
42
43
44
45
46
47
48
49
50
51
52
|
import java.io.*;
public class Salin {
/** Metode utama
@param args[0] untuk filesumber
@param args[1] untuk filetarget
*/
public static void main(String[]
args) throws IOException {
// Memeriksa penggunaan parameter
command-line
if (args.length != 2) {
System.out.println(
"Usage: java Salin fileSumber
fileTarget");
System.exit(0);
}
// Memeriksa apakah file sumber ada
File fileSumber = new File(args[0]);
if (!fileSumber.exists()) {
System.out.println("Source file
" + args[0] + " not exist");
System.exit(0);
}
// Memeriksa apakah file target ada
File fileTarget = new
File(args[1]);
if (fileTarget.exists()) {
System.out.println("Target file
" + args[1] + " already
exists");
System.exit(0);
}
// Menciptakan suatu aliran masukan
BufferedInputStream masukan =
new BufferedInputStream(new FileInputStream(fileSumber));
// Menciptakan suatu aliran keluaran
BufferedOutputStream keluaran =
new BufferedOutputStream(new FileOutputStream(fileTarget));
//
Secara kontinyu membaca suatu
byte dari aliran masukan,menulisnya ke aliran keluaran
int r; int jumlahByteDisalin
= 0;
while ((r = masukan.read()) != -1) {
keluaran.write((byte)r);
jumlahByteDisalin++;
}
// Menutup aliran-aliran
masukan.close();
keluaran.close();
// Menampilkan hasil
System.out.println(jumlahByteDisalin +
" bytes copied");
}
}
|
Keluaran
Usage:
java Salin fileSumber fileTarget
Program
pertama-tama memeriksa apakah pengguna telah melewatkan dua argumen yang
diperlukan dari command line pada baris 10-14.
Program
menggunakan kelas File untuk
memeriksa apakah file sumber dan file target sudah atau belum. Jika file sumber
belum ada (baris 18-21) atau jika file target sudah ada, maka program akan
berhenti.
Suatu
aliran masukan diciptakan menggunakan BufferedInputStream
pada baris 32-33, dan suatu aliran keluaran diciptakan menggunakan BufferedOutputStream pada baris 36-37.
Ekspresi ((r = masukan.read()) != -1) (baris 41) membaca suatu byte
dari masukan.read(), menugaskannya kepada r, dan memeriksa apakah
r bernilai -1. Nilai masukan -1 menandakan akhir suatu file. Program
secara kontinyu membaca byte demi byte dari aliran masukan dan mengirimkannya
ke aliran keluaran sampai semua byte terbaca.
5.6
I/O Object
DataInputStream/DataOutputStream memampukan Anda untuk
melakukan operasi I/O untuk nilai tipe primitif dan string. ObjectInputStream/ObjectOutputStream memampukan Anda untuk melakukan I/O untuk objek,
nilai primitif, dan string. Karena ObjectInputStream/ObjectOutputStream memuat semua fungsi
dari DataInputStream/DataOutputStream, Anda bisa
menggantikan DataInputStream /DataOutputStream dengan ObjectInputStream/ObjectOutputStream.
ObjectInputStream
mewarisi InputStream dan
mengimplementasikan ObjectInput dan ObjectStreamConstants, seperti tertampil
pada Gambar 5.14. ObjectInput
merupakan sub-antarmuka dari DataInput.
DataInput ditampilkan pada Gambar
5.9. ObjectStreamConstants memuat
konstanta-konstanta untuk mendukung ObjectInputStream/ObjectOutputStream.
ObjectOutputStream
mewarisi OutputStream dan
mengimplementasikan ObjectOutput dan
ObjectStreamConstants, seperti
tertampil pada Gambar 5.15. ObjectOutput
merupakan sub-antarmuka dari DataOutput.
DataOutput ditampilkan pada Gambar
5.10.
Gambar
5.14 ObjectInputStream dapat membaca objek, nilai primitif, dan string
Gambar
5.15 ObjectOutputStream dapat membaca objek, nilai primitif, dan string
Kode5.5
menulis nama, skor mahasiswa, dan tanggal saat ini ke dalam suatu file bernama objek.dat.
Kode5.5 UjiAliranKeluaranObjek.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
import java.io.*;
public class UjiAliranKeluaranObjek {
public static void main(String[]
args) throws IOException {
// Menciptakan suatu aliran keluaran untuk
fil objek.dat
ObjectOutputStream keluaran =
new ObjectOutputStream(new FileOutputStream("objek.dat"));
// Menulis suatu string, nilai double, dan
objek ke dalam file
keluaran.writeUTF("Butet");
keluaran.writeDouble(85.5);
keluaran.writeObject(new java.util.Date());
// Menutup aliran keluaran
keluaran.close();
}
}
|
Suatu ObjectOutputStream diciptakan untuk
menulis data ke dalam file objek.dat
pada baris 6-7. Suatu string, nilai double, dan objek ditulis ke file pada
baris 10-12. Untuk meningkatkan kinerja, Anda bisa menambahkan suatu penyangga
di dalam aliran menggunakan statemen berikut menggantikan baris 6-7:
ObjectOutputStream keluaran = new
ObjectOutputStream(
new BufferedOutputStream(new
FileOutputStream("objek.dat")));
Objek
jamak atau nilai primitif jamak dapat ditulis ke aliran. Objek-objek dapat
dibaca kembali dari ObjectInputStream
terkait dengan tipe sama dan dengan urutan sama seperti ketika ditulis. Casting
pada JAVA perlu dilakukan untuk mendapatkan tipe yang diinginkan. Kode5.6
membaca data kembali dari file objek.dat.
Kode5.6 UjiAliranMasukanObjek.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
import java.io.*;
public class UjiAliranMasukanObjek {
public static void main(String[]
args)
throws ClassNotFoundException,
IOException {
// Menciptakan suatu aliran masukan untuk
file objek.dat
ObjectInputStream masukan =
new ObjectInputStream(new FileInputStream("objek.dat"));
// Membaca suatu string, nilai double, dan
objek dari file
String nama = masukan.readUTF();
double skor = masukan.readDouble();
java.util.Date tanggal = (java.util.Date)(masukan.readObject());
System.out.println(nama + " " +
skor + " " + tanggal);
// Menutup aliran keluaran
masukan.close();
}
}
|
Keluaran
Butet
85.5 Tue Dec 04 17:41:57 ICT 2012
5.6.1
Antarmuka Serializable
Tidak
semua objek dapat ditulis ke suatu aliran keluaran. Objek yang dapat ditulis
seperti itu dikatakan dengan serializable
atau terserialkan. Suatu objek
terserialkan merupakan instans dari antarmuka java.io.Serializable, sehingga
objek kelas harus mengimplementasikan Serializable.
Antarmuka
Serializable adalah antarmuka
penanda. Karena tidak memiliki metode, Anda tidak perlu menambahkan kode
tambahan di dalam kelas Anda yang mengimplementasikan Serializable. Mengimplementasikan antarmuka ini memampukan
mekanisme serialisasi JAVA untuk mengotomasi proses penyimpanan objek dan
array.
Untuk
menghargai keberadaan fitu otomasi ini, Anda perlu memakainya pada saat akan
menyimpan objek. Dimisalkan Anda akan menyimpan suatu objek JButton. Untuk melakukannya, Anda perlu
menyimpan semua nilai-nilai properti saat ini (misalnya, warna, huruf, teks,
dan penyejajaran) di dalam objek. Karena JButton
merupakan subkelas dari AbstractButton,
nilai-nilai properti AbstractButton
harus pula disimpan berikut dengan nilai-nilai properti semua superkelas dari AbstractButton. Jika suatu properti
bertipe objek (misalnya, background
dengan tipe Color), maka
menyimpannya sama dengan menyimpan semua nilai-nilai properti di dalam objek
tersebut. Seperti Anda bisa perhatikan, hal ini merupakan proses yang sangat
melelahkan. Untungnya, Anda tidak perlu melakukannya secara manual. JAVA
menyediakan suatu mekanisme built-in untuk mengotomasi proses penulisan objek. Proses
ini dikenal dengan serialisasi objek,
yang diimplementasikan di dalam ObjectOutputStream. Sebaliknya, proses
pembacaan objek dikenal dengan deserialisasi
objek, yang diimplementasikan di dalam ObjectInputStream.
Banyak
kelas di dalam JAVA API mengimplementasikan Serializable. Kelas-kelas utilitas, seperti java.util.Date, dan semua kelas-kelas komponen GUI Swing
mengimplementasikan Serializable.
Mencoba untuk menyimpan suatu objek yang tidak mengimplementasikan antarmuka
Serializable akan menyebabkan eksepsi NotSerializableException.
Ketika
suatu objek terserialkan disimpan, kelas dari objek tersebut dienkodekan;
termasuk nama kelas, sidik kelas, dan nilai-nilai variabel instans objek. Sedangkan
nilai-nilai variabel statik di dalam objek tidak disimpan.
5.6.2
Membuat Array Terserialkan
Suatu
array terserialkan bila semua elemennya terserialkan. Keseluruhan array dapat
disimpan menggunakan writeObject ke
dalam suatu file dan kemudian direstorasi menggunakan readObject. Kode5.7 menyimpan suatu array yang memuat lima nilai
int dan suatu array yang memuat tiga string dan membacanya kembali untuk
ditampilkan pada konsol.
Kode5.7 UjiAliranObjekUntukArray.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
|
import java.io.*;
public class UjiAliranObjekUntukArray {
public static void main(String[]
args)
throws
ClassNotFoundException, IOException {
int[] angka = {1, 2, 3, 4, 5};
String[] string = {"Robert",
"Sintong", "Rico"};
// Menciptakan suatu aliran keluaran untuk
file array.dat
ObjectOutputStream keluaran =
new ObjectOutputStream(new FileOutputStream
("array.dat", true));
// Menulis array ke aliran keluaran objek
keluaran.writeObject(angka);
keluaran.writeObject(string);
// Menutup aliran
keluaran.close();
// Menciptakan suatu aliran masukan untuk
file array.dat
ObjectInputStream masukan =
new ObjectInputStream(new FileInputStream("array.dat"));
int[] angkaBaru = (int[])(masukan.readObject());
String[] stringBaru = (String[])(masukan.readObject());
// Menampilkan array
for (int i = 0; i <
angkaBaru.length; i++)
System.out.print(angkaBaru[i] + "
");
System.out.println();
for (int i = 0; i <
stringBaru.length; i++)
System.out.print(stringBaru[i]
+ " ");
}
}
|
Keluaran
1 2 3 4
5
Robert
Sintong Rico
5.7
File Akses-Acak
Semua
aliran yang telah Anda gunakan sejauh ini dikenal sebagai aliran hanya-baca
atau hanya-tulis. File-file eksternal dari aliran-aliran ini adalah file-file
sekuensial yang tidak bisa diperbarui tanpa menciptakan suatu file baru. Sangat
sering diperlukan untuk memodifikasi file atau untuk menyisipkan sepotong data
baru ke dalam file. JAVA menyediakan kelas RandomAccessFile
untuk mengijinkan suatu file dibaca dari atau ditulis ke lokasi-lokasi acak.
Kelas RandomAccessFile mengimplementasikan
antarmuka DataInput dan DataOutput, seperti tertampil pada
Gambar 5.16. Antarmuka DataInput
yang ditampilkan pada Gambar 5.9 mendefinisikan metode-metode (misalnya, readInt, readDouble, readChar, readBoolean, readUTF) untuk membaca nilai primitif dan string.
Gambar
5.16 RandomAccessFile mengimplementasikan antarmuka DataInput dan DataOutput
dengan metode-metode tambahan untuk mendukung akses acak
Ketika
menciptakan suatu RandomAccessFile,
Anda harus menentukan satu dari dua mode (“r” atau “rw”). Mode “r” berarti
bahwa aliran bersifat hanya-baca, mode “rw” berarti bahwa aliran mengijinkan
pembacaan dan penulisan. Sebagai contoh, statemen berikut ini menciptakan suatu
aliran baru, raf, yang mengijinkan
program untuk membaca dari dan menulis ke file uji.dat:
RandomAccessFile raf = new
RandomAccessFile("uji.dat",
"rw");
Jika
file uji.dat telah ada, maka raf diciptakan untuk mengaksesnya; jika
uji.dat belum ada, maka suatu file
bernama uji.dat diciptakan, dan raf
diciptakan untuk mengakses file baru tersebut. Metode raf.length() mengembalikan jumlah byte
di dalam file uji.dat. Jika Anda
menyisipkan data baru pada file uji.dat,
maka nilai raf.length() akan
bertambah.
Suatu
file akses-acak terdiri-dari suatu runtun byte. Penanda khusus yang disebut
dengan pointer file diposisikan pada salah satu dari byte yang ada. Operasi
penulisan dan pembacaan terjadi pada lokasi pointer file. Ketika suatu file
dibuka, pointer file ditetapkan di awal file. Ketika Anda membaca dari atau
menulis data ke file, pointer file bergerak maju ke item data selanjutnya.
Sebagai contoh, jika Anda membaca suatu nilai int di menggunakan readInt(),
maka JVM membaca 4 byte dari pointer file, dan sekarang pointer file lebih maju
4 byte daripada lokasi sebelumnya, seperti tertampil pada Gambar 5.17.
Untuk
suatu RandomAccessFile raf, Anda
dapat menggunakan metode raf.seek(position) untuk menggerakkan
pointer file ke lokasi tertentu. raf.seek(0)
akan menggerakkan pointer file ke awal file,dan raf.seek(raf.length()) akan menggerakkan pointer file ke akhir
file. Kode5.8 mendemonstrasikan RandomAccessFile.
Gambar
5.17 Setelah suatu nilai int dibaca, pointer file bergerak maju
4 byte
Kode5.8 UjiRandomAccessFile.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
45
46
|
import java.io.*;
public class UjiRandomAccessFile {
public static void main(String[]
args) throws IOException {
// Menciptakan suatu file akses-acak
RandomAccessFile inout = new RandomAccessFile("inout.dat",
"rw");
// Membersihkan file dengan menghancurkan
isi semula, jika ada
inout.setLength(0);
// Menulis integer-integer baru ke dalam
file
for (int i = 0; i < 200;
i++)
inout.writeInt(i);
// Menampilkan panjang file saat ini
System.out.println("Panjang file
saat ini adalah "+ inout.length());
// MengambilRetrieve the first number
inout.seek(0);//angka pertama
System.out.println("Angka pertama
adalah "+ inout.readInt());
// Mengambil angka kedua
inout.seek(1 * 4);// Menggerakkan pointer file ke angka kedua
System.out.println("Angka kedua
adalah "+ inout.readInt());
// Mengambil angka kesepuluh
inout.seek(9 * 4);// Menggerakkan pointer file ke angka
kesepuluh
System.out.println("Angka
kesepuluh adalah "+ inout.readInt());
// Memodifikasi angka kesebelas
inout.writeInt(555);
// Menyambungkan suatu angka baru
inout.seek(inout.length());// Menggerakkan pointer file ke akhir
file
inout.writeInt(999);
// Menampilkan panjang baru
System.out.println("Panjang baru file
adalah "+ inout.length());
// Mengambil angka kesebelas yang baru
inout.seek(10 * 4);// Menggerakkan pointer
file ke angka selanjutnya
System.out.println("Angka kesebelas
adalah "+ inout.readInt());
inout.close();
}
}
|
Keluaran
Panjang
file saat ini adalah 800
Angka
pertama adalah 0
Angka
kedua adalah 1
Angka
kesepuluh adalah 9
Panjang
baru file adalah 804
Angka
kesebelas adalah 555
Suatu RandomAccessFile diciptakan untuk file
bernama inout.dat dengan mode
“rw”untuk mengijnkan operasi pembacaan dan penulisan pada file.
inout.setLength(0)
menetapkan panjang file menjadi 0 pada baris 9. Ini akan menghapus semua isi
semula dari file tersebut.
Loop for menulis 200 int mulai 0 sampai 199 ke dalam file pada baris 12-13. Karena
setiap int memerlukan 4 byte, maka
panjang total file sekarang yang didapatkan dari inout.length() adalah 800 (baris 16), seperti ditunjukkan pada
keluaran program.
Pemanggilan
inout.seek(0) pada baris 19
menetapkan pointer file ke awal file. inout.readInt()
membaca nilai pertama pada baris 20 dan menggerakkan pointer file ke angka
kedua. Angka kedua dibaca pada baris 23.
inout.seek(9*4) pada
baris 27 menggerakkan pointer file ke angka kesepuluh. inout.readInt() membaca angka kesepuluh dan menggerakkan pointer
file ke angka kesebelas pada baris 28. inout.write(555)
menulis angka kesebelas yang baru pada posisi saat ini (baris 31). Angka
kesebelas sebelumnya telah dihapus.
inout.seek(inout.length())
menggerakkan pointer file ke akhir file pada baris 34. inout.write(999) menulis 999 pada akhir file. Sekarang panjang file
bertambah 4, sehingga inout.length()
mengembalikan 804 pada baris 38.
inout.seek(10*4)
menggerakkan pointer ke angka kesebelas pada baris 41. Angka kesebelas yang
baru, 555, ditampilkan pada baris 42.
No comments:
Post a Comment