1. Aliran data

Jarang ada program yang berdiri sendiri sebagai sebuah pulau. Program biasanya entah bagaimana berinteraksi dengan "dunia luar". Ini dapat terjadi dengan membaca data dari keyboard, mengirim pesan, mengunduh halaman dari Internet, atau, sebaliknya, mengunggah file ke server jarak jauh.

Kita dapat mengacu pada semua perilaku ini dalam satu kata: pertukaran data antara program dan dunia luar. Tunggu, itu bukan hanya satu kata.

Tentu saja, pertukaran data itu sendiri dapat dibagi menjadi dua bagian: menerima data dan mengirim data. Misalnya, Anda membaca data dari keyboard menggunakan Scannerobjek — ini menerima data. Dan Anda menampilkan data di layar menggunakan System.out.println()perintah — ini mengirim data.

Dalam pemrograman, istilah "aliran" digunakan untuk menggambarkan pertukaran data. Dari mana asal istilah itu?

Dalam kehidupan nyata, Anda dapat memiliki aliran air atau aliran kesadaran. Dalam pemrograman, kami memiliki aliran data .

Aliran adalah alat serbaguna. Mereka mengizinkan program untuk menerima data dari mana saja (aliran input) dan mengirim data ke mana saja (aliran output). Jadi, ada dua jenis:

  • Aliran input adalah untuk menerima data
  • Aliran output adalah untuk mengirim data

Untuk membuat aliran 'nyata', pembuat Java menulis dua kelas: InputStreamdan OutputStream.

Kelas InputStreammemiliki read()metode yang memungkinkan Anda membaca data darinya. Dan OutputStreamkelas memiliki write()metode yang memungkinkan Anda menulis data ke dalamnya. Mereka memiliki metode lain juga, tetapi lebih dari itu nanti.

Aliran byte

Jenis data apa yang sedang kita bicarakan? Format apa yang dibutuhkan? Dengan kata lain, tipe data apa yang didukung kelas-kelas ini?

Ini adalah kelas generik, sehingga mendukung tipe data yang paling umum — file byte. An OutputStreamdapat menulis byte (dan array byte), dan InputStreamobjek dapat membaca byte (atau array byte). Itu saja — mereka tidak mendukung tipe data lainnya.

Akibatnya, aliran ini juga disebut aliran byte .

Salah satu fitur stream adalah bahwa datanya hanya dapat dibaca (atau ditulis) secara berurutan. Anda tidak dapat membaca data dari tengah aliran tanpa membaca semua data yang datang sebelumnya.

Beginilah cara membaca data dari keyboard melalui kelas Scanner: Anda membaca data dari keyboard secara berurutan, baris demi baris. Kami membaca satu baris, lalu baris berikutnya, lalu baris berikutnya, dan seterusnya. Tepatnya, metode untuk membaca baris disebut nextLine().

Penulisan data ke an OutputStreamjuga terjadi secara berurutan. Contoh bagusnya adalah keluaran konsol. Anda mengeluarkan satu baris, diikuti oleh yang lain dan yang lainnya. Ini adalah output berurutan. Anda tidak dapat menampilkan baris pertama, lalu baris kesepuluh, lalu baris kedua. Semua data ditulis ke aliran keluaran hanya secara berurutan.

Aliran karakter

Anda baru-baru ini mengetahui bahwa string adalah tipe data terpopuler kedua, dan memang demikian. Banyak informasi yang disampaikan dalam bentuk karakter dan seluruh string. Komputer unggul dalam mengirim dan menerima segala sesuatu sebagai byte, tetapi manusia tidak sesempurna itu.

Berdasarkan fakta ini, pemrogram Java menulis dua kelas lagi: Readerdan Writer. Kelasnya Readermirip dengan InputStreamkelas, tetapi read()metodenya tidak membaca byte, tetapi karakter ( char). Kelas Writersesuai dengan OutputStreamkelas. Dan seperti kelasnya Reader, ia bekerja dengan karakter ( char), bukan byte.

Jika kita membandingkan keempat kelas ini, kita mendapatkan gambaran berikut:

Byte (byte) Karakter (karakter)
Membaca data
InputStream
Reader
Menulis data
OutputStream
Writer

Aplikasi praktis

Kelas InputStream, OutputStream, Readerdan Writeritu sendiri tidak digunakan secara langsung oleh siapa pun, karena mereka tidak terkait dengan objek konkret apa pun dari mana data dapat dibaca (atau di mana data dapat ditulis). Tetapi keempat kelas ini memiliki banyak kelas turunan yang dapat melakukan banyak hal.


2. InputStreamkelas

Kelas ini InputStreammenarik karena merupakan kelas induk dari ratusan kelas keturunan. Itu tidak memiliki datanya sendiri, tetapi ia memiliki metode yang diwarisi oleh semua kelas turunannya.

Secara umum, objek aliran jarang menyimpan data secara internal. Aliran adalah alat untuk membaca/menulis data, tetapi bukan penyimpanan. Yang mengatakan, ada pengecualian.

Metode kelas InputStreamdan semua kelas turunannya:

Metode Keterangan
int read()
Membaca satu byte dari aliran
int read(byte[] buffer)
Membaca array byte dari aliran
byte[] readAllBytes()
Membaca semua byte dari aliran
long skip(long n)
Melewati nbyte dalam aliran (membaca dan membuangnya)
int available()
Memeriksa berapa banyak byte yang tersisa di aliran
void close()
Menutup aliran

Mari kita bahas secara singkat metode-metode ini:

read()metode

Metode read()membaca satu byte dari aliran dan mengembalikannya. Anda mungkin bingung dengan intjenis pengembalian. Tipe ini dipilih karena intmerupakan tipe integer standar. Tiga byte pertama dari intakan menjadi nol.

read(byte[] buffer)metode

Ini adalah varian kedua dari read()metode ini. Ini memungkinkan Anda membaca array byte dari InputStreamsemua sekaligus. Larik yang akan menyimpan byte harus diteruskan sebagai argumen. Metode ini mengembalikan angka — jumlah byte yang sebenarnya dibaca.

Katakanlah Anda memiliki buffer 10 kilobyte dan Anda sedang membaca data dari file menggunakan kelas FileInputStream. Jika file hanya berisi 2 kilobyte, semua data akan dimuat ke dalam buffer array, dan metode akan mengembalikan angka 2048 (2 kilobyte).

readAllBytes()metode

Metode yang sangat bagus. Itu hanya membaca semua data dari InputStreamsampai habis dan mengembalikannya sebagai array byte tunggal. Ini sangat berguna untuk membaca file kecil. File besar mungkin tidak muat secara fisik di memori, dan metode ini akan mengeluarkan pengecualian.

skip(long n)metode

Metode ini memungkinkan Anda untuk melewati n byte pertama dari objek InputStream. Karena data dibaca secara berurutan, metode ini hanya membaca n byte pertama dari aliran dan membuangnya.

Mengembalikan jumlah byte yang benar-benar dilewati (jika streaming berakhir sebelum nbyte dilewati).

int available()metode

Metode mengembalikan jumlah byte yang masih tersisa di aliran

void close()metode

Metode close()menutup aliran data dan melepaskan sumber daya eksternal yang terkait dengannya. Setelah aliran ditutup, tidak ada lagi data yang dapat dibaca darinya.

Mari kita tulis contoh program yang menyalin file yang sangat besar. Kami tidak dapat menggunakan readAllBytes()metode untuk membaca seluruh file ke dalam memori. Contoh:

Kode Catatan
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreamuntuk membaca dari file
OutputStreamuntuk menulis ke file

Buffer di mana kita akan membaca data
Selama ada data di aliran

Baca data ke dalam buffer
Tulis data dari buffer ke aliran kedua

Dalam contoh ini, kami menggunakan dua kelas: FileInputStreamadalah turunan InputStreamuntuk membaca data dari file, dan FileOutputStreamturunan OutputStreamuntuk menulis data ke file. Kami akan berbicara tentang kelas kedua nanti.

Poin menarik lainnya di sini adalah variabelnya real. Saat blok data terakhir dibaca dari sebuah file, file tersebut dapat dengan mudah memiliki kurang dari 64KB data. Oleh karena itu, kita perlu menampilkan bukan seluruh buffer, tetapi hanya sebagian — realbyte pertama. Inilah yang terjadi dalam write()metode ini.



3. Readerkelas

Kelas Readeradalah analog lengkap dari InputStreamkelas. Satu-satunya perbedaan adalah ia bekerja dengan karakter ( char), bukan dengan byte. Sama seperti InputStreamclass, Readerclass tidak digunakan di mana pun dengan sendirinya: ini adalah class induk dari ratusan class turunan dan mendefinisikan metode umum untuk semuanya.

Metode kelas Reader(dan semua kelas turunannya):

Metode Keterangan
int read()
Membaca satu chardari aliran
int read(char[] buffer)
Membaca chararray dari aliran
long skip(long n)
Melewatkan n charsarus (membaca dan membuangnya)
boolean ready()
Memeriksa apakah masih ada sesuatu yang tersisa di aliran
void close()
Menutup aliran

Metodenya sangat mirip dengan kelasnya InputStream, meskipun ada sedikit perbedaan.

int read()metode

Metode ini membaca satu chardari aliran dan mengembalikannya. Jenis charmelebar ke int, tetapi dua byte pertama hasilnya selalu nol.

int read(char[] buffer)metode

Ini adalah varian kedua dari read()metode ini. Ini memungkinkan Anda membaca array char dari Readersekaligus. Larik yang akan menyimpan karakter harus diteruskan sebagai argumen. Metode mengembalikan angka — jumlah karakter yang benar-benar dibaca.

skip(long n)metode

Metode ini memungkinkan Anda untuk melewati n karakter pertama dari objek Reader. Ini bekerja persis sama dengan metode analog kelas InputStream. Mengembalikan jumlah karakter yang benar-benar dilewati.

boolean ready()metode

Mengembalikan truejika ada byte yang belum dibaca di aliran.

void close()metode

Metode close()menutup aliran data dan melepaskan sumber daya eksternal yang terkait dengannya. Setelah aliran ditutup, tidak ada lagi data yang dapat dibaca darinya.

Sebagai perbandingan, mari kita tulis program yang menyalin file teks:

Kode Catatan
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readeruntuk membaca dari sebuah file
Writeruntuk menulis ke sebuah file

Buffer dimana kita akan membaca data
Selama ada data di dalam stream

Membaca data ke dalam buffer
Menulis data dari buffer ke stream kedua