1. Aliran data

Jarang sekali program wujud sebagai pulau untuk dirinya sendiri. Program biasanya berinteraksi dengan "dunia luar". Ini boleh berlaku melalui membaca data dari papan kekunci, menghantar mesej, memuat turun halaman daripada Internet, atau, sebaliknya, memuat naik fail ke pelayan jauh.

Kita boleh merujuk kepada semua tingkah laku ini dalam satu perkataan: pertukaran data antara program dan dunia luar. Tunggu, itu bukan hanya satu perkataan.

Sudah tentu, pertukaran data itu sendiri boleh dibahagikan kepada dua bahagian: menerima data dan menghantar data. Contohnya, anda membaca data dari papan kekunci menggunakan Scannerobjek — ini menerima data. Dan anda memaparkan data pada skrin menggunakan System.out.println()arahan — ini menghantar data.

Dalam pengaturcaraan, istilah "strim" digunakan untuk menggambarkan pertukaran data. Dari mana datangnya istilah itu?

Dalam kehidupan sebenar, anda boleh mempunyai aliran air atau aliran kesedaran. Dalam pengaturcaraan, kami mempunyai aliran data .

Strim ialah alat serba boleh. Mereka membenarkan program menerima data dari mana-mana (strim input) dan menghantar data ke mana-mana (strim output). Oleh itu, terdapat dua jenis:

  • Strim input adalah untuk menerima data
  • Strim output adalah untuk menghantar data

Untuk menjadikan strim 'ketara', pencipta Java menulis dua kelas: InputStreamdan OutputStream.

Kelas InputStreammempunyai read()kaedah yang membolehkan anda membaca data daripadanya. Dan OutputStreamkelas mempunyai write()kaedah yang membolehkan anda menulis data kepadanya. Mereka mempunyai kaedah lain juga, tetapi lebih lanjut mengenainya kemudian.

Aliran bait

Apakah jenis data yang kita bincangkan? Apakah format yang diperlukan? Dengan kata lain, apakah jenis data yang disokong oleh kelas ini?

Ini adalah kelas generik, jadi mereka menyokong jenis data yang paling biasa — byte. Satu OutputStreamboleh menulis bait (dan tatasusunan bait), dan InputStreamobjek boleh membaca bait (atau tatasusunan bait). Itu sahaja — mereka tidak menyokong mana-mana jenis data lain.

Akibatnya, strim ini juga dipanggil strim bait .

Satu ciri strim ialah data mereka hanya boleh dibaca (atau ditulis) secara berurutan. Anda tidak boleh membaca data dari tengah strim tanpa membaca semua data yang datang sebelum itu.

Beginilah cara membaca data dari papan kekunci berfungsi melalui Scannerkelas: anda membaca data dari papan kekunci secara berurutan, baris demi baris. Kami membaca satu baris, kemudian baris seterusnya, kemudian baris seterusnya, dan seterusnya. Sewajarnya, kaedah untuk membaca baris dipanggil nextLine().

Menulis data ke an OutputStreamjuga berlaku secara berurutan. Satu contoh yang baik ialah output konsol. Anda mengeluarkan satu baris, diikuti dengan satu lagi dan satu lagi. Ini adalah output berurutan. Anda tidak boleh mengeluarkan baris pertama, kemudian yang kesepuluh, dan kemudian yang kedua. Semua data ditulis kepada aliran keluaran hanya secara berurutan.

Aliran watak

Anda baru-baru ini mengetahui bahawa rentetan ialah jenis data kedua paling popular, dan memang begitu. Banyak maklumat disampaikan dalam bentuk aksara dan rentetan keseluruhan. Komputer cemerlang dalam menghantar dan menerima segala-galanya sebagai bait, tetapi manusia tidak begitu sempurna.

Mengambil kira fakta ini, pengaturcara Java menulis dua lagi kelas: Readerdan Writer. Kelas Readeradalah analog InputStreamkelas, tetapi read()kaedahnya tidak membaca bait, tetapi aksara ( char). Kelas Writersepadan dengan OutputStreamkelas. Dan sama seperti Readerkelas, ia berfungsi dengan aksara ( char), bukan bait.

Jika kita membandingkan empat kelas ini, kita mendapat gambar berikut:

Bait (bait) Watak (char)
Membaca data
InputStream
Reader
Menulis data
OutputStream
Writer

Permohonan praktikal

Kelas InputStream, OutputStream, Readerdan Writeritu sendiri tidak digunakan secara langsung oleh sesiapa sahaja, kerana ia tidak dikaitkan dengan mana-mana objek konkrit dari mana data boleh dibaca (atau ke dalamnya data boleh ditulis). Tetapi empat kelas ini mempunyai banyak kelas keturunan yang boleh melakukan banyak perkara.


2. InputStreamkelas

Kelas itu InputStreammenarik kerana ia adalah kelas induk untuk ratusan kelas keturunan. Ia tidak mempunyai sebarang data sendiri, tetapi ia mempunyai kaedah yang diwarisi oleh semua kelas terbitannya.

Secara umum, jarang sekali objek aliran menyimpan data secara dalaman. Strim ialah alat untuk membaca/menulis data, tetapi bukan storan. Yang berkata, terdapat pengecualian.

Kaedah kelas InputStreamdan semua kelas keturunannya:

Kaedah Penerangan
int read()
Membaca satu bait daripada strim
int read(byte[] buffer)
Membaca pelbagai bait daripada strim
byte[] readAllBytes()
Membaca semua bait daripada strim
long skip(long n)
Melangkau nbait dalam strim (membaca dan membuangnya)
int available()
Menyemak berapa banyak bait yang tinggal dalam strim
void close()
Menutup strim

Mari kita secara ringkas melalui kaedah ini:

read()kaedah

Kaedah read()membaca satu bait daripada aliran dan mengembalikannya. Anda mungkin keliru dengan intjenis pemulangan. Jenis ini dipilih kerana intmerupakan jenis integer piawai. Tiga bait pertama intakan menjadi sifar.

read(byte[] buffer)kaedah

Ini adalah varian kedua kaedah read(). Ia membolehkan anda membaca tatasusunan bait daripada InputStreamsemua sekali gus. Tatasusunan yang akan menyimpan bait mesti diluluskan sebagai hujah. Kaedah ini mengembalikan nombor — bilangan bait yang sebenarnya dibaca.

Katakan anda mempunyai penimbal 10 kilobait dan anda sedang membaca data daripada fail menggunakan FileInputStreamkelas. Jika fail mengandungi hanya 2 kilobait, semua data akan dimuatkan ke dalam tatasusunan penimbal, dan kaedah akan mengembalikan nombor 2048 (2 kilobait).

readAllBytes()kaedah

Kaedah yang sangat baik. Ia hanya membaca semua data dari InputStreamsehingga ia habis dan mengembalikannya sebagai tatasusunan bait tunggal. Ini sangat berguna untuk membaca fail kecil. Fail besar mungkin tidak sesuai secara fizikal dalam ingatan, dan kaedah itu akan membuang pengecualian.

skip(long n)kaedah

Kaedah ini membolehkan anda melangkau n bait pertama daripada objek InputStream. Oleh kerana data dibaca secara berurutan, kaedah ini hanya membaca n bait pertama daripada strim dan membuangnya.

Mengembalikan bilangan bait yang sebenarnya dilangkau (sekiranya strim tamat sebelum nbait dilangkau).

int available()kaedah

Kaedah ini mengembalikan bilangan bait yang masih tinggal dalam strim

void close()kaedah

Kaedah ini close()menutup aliran data dan mengeluarkan sumber luaran yang berkaitan dengannya. Setelah strim ditutup, tiada lagi data boleh dibaca daripadanya.

Mari kita tulis contoh program yang menyalin fail yang sangat besar. Kami tidak boleh menggunakan readAllBytes()kaedah untuk membaca keseluruhan fail ke dalam ingatan. Contoh:

Kod 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 fail
OutputStreamuntuk menulis ke fail

Penampan di mana kita akan membaca data
Selagi terdapat data dalam aliran

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

Dalam contoh ini, kami menggunakan dua kelas: FileInputStreamialah turunan InputStreamuntuk membaca data daripada fail, dan FileOutputStreamturunan daripada OutputStreamuntuk menulis data ke fail. Kami akan bercakap tentang kelas kedua sedikit kemudian.

Satu lagi perkara yang menarik di sini ialah realpembolehubah. Apabila blok terakhir data dibaca daripada fail, ia boleh mempunyai kurang daripada 64KB data dengan mudah. Oleh itu, kita perlu mengeluarkan bukan keseluruhan penimbal, tetapi hanya sebahagian daripadanya - realbait pertama. Inilah yang berlaku dalam write()kaedah.



3. Readerkelas

Kelas Readeradalah analog lengkap kelas InputStream. Satu-satunya perbezaan ialah ia berfungsi dengan aksara ( char), bukan dengan bait. Sama seperti InputStreamkelas, Readerkelas itu tidak digunakan di mana-mana sahaja: ia adalah kelas induk untuk beratus-ratus kelas keturunan dan mentakrifkan kaedah biasa untuk kesemuanya.

Kaedah kelas Reader(dan semua kelas keturunannya):

Kaedah Penerangan
int read()
Membaca satu chardaripada aliran
int read(char[] buffer)
Membaca chartatasusunan daripada strim
long skip(long n)
Melangkau n charsdalam strim (membaca dan membuangnya)
boolean ready()
Menyemak sama ada masih terdapat sesuatu dalam strim
void close()
Menutup strim

Kaedah-kaedahnya sangat serupa dengan kaedah kelas InputStream, walaupun terdapat sedikit perbezaan.

int read()kaedah

Kaedah ini membaca satu chardaripada aliran dan mengembalikannya. Jenis charmelebar kepada int, tetapi dua bait pertama hasilnya sentiasa sifar.

int read(char[] buffer)kaedah

Ini adalah varian kedua kaedah read(). Ia membolehkan anda membaca tatasusunan char daripada Readersemua sekali gus. Tatasusunan yang akan menyimpan aksara mesti diluluskan sebagai hujah. Kaedah ini mengembalikan nombor — bilangan aksara yang sebenarnya dibaca.

skip(long n)kaedah

Kaedah ini membolehkan anda melangkau n aksara pertama daripada objek Reader. Ia berfungsi sama seperti kaedah analog kelas InputStream. Mengembalikan bilangan aksara yang sebenarnya dilangkau.

boolean ready()kaedah

Mengembalikan truejika terdapat bait yang belum dibaca dalam strim.

void close()kaedah

Kaedah ini close()menutup aliran data dan mengeluarkan sumber luaran yang berkaitan dengannya. Setelah strim ditutup, tiada lagi data boleh dibaca daripadanya.

Sebagai perbandingan, mari tulis program yang menyalin fail teks:

Kod 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 fail
Writeruntuk menulis ke fail

Penampan di mana kita akan membaca data
Selagi terdapat data dalam aliran

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