Kata pengantar
Pada artikel sebelumnya, kami memperkenalkan jenis file file disk abstrak. Ini hanya digunakan untuk secara abstrak menggambarkan file atau direktori disk, tetapi tidak memiliki kemampuan untuk mengakses dan memodifikasi konten file.
Java IO Stream adalah desain yang digunakan untuk membaca dan menulis konten file. Ini dapat menyelesaikan transfer data konten file disk output ke memori atau data memori output ke file disk.
Desain Java IO Streams tidak sempurna. Ini telah merancang sejumlah besar kelas, yang telah meningkatkan pemahaman kita tentang aliran IO, tetapi hanya ada dua kategori utama: satu adalah aliran byte untuk file biner, dan yang lainnya adalah aliran karakter untuk file teks. Dalam artikel ini, pertama -tama kita akan mempelajari prinsip -prinsip dan skenario penggunaan jenis aliran byte terkait. Jenis aliran spesifik terutama yang terlibat adalah sebagai berikut:
Input/OutputStream aliran byte kelas dasar
InputStream dan OutputStream adalah kelas dasar untuk membaca aliran byte dan menulis byte stream. Semua aliran yang berhubungan dengan byte harus mewarisi dari mereka. Sebagai kelas abstrak, mereka juga mendefinisikan operasi baca dan tulis paling mendasar. Mari kita lihat:
Ambil inputstream sebagai contoh:
Publik abstrak int read () melempar ioException;
Ini adalah metode abstrak, dan tidak memberikan implementasi default, mensyaratkan bahwa subkelas harus diimplementasikan. Tujuan dari metode ini adalah untuk mengembalikan byte berikutnya dari file saat ini untuk Anda.
Tentu saja, Anda juga akan menemukan bahwa nilai pengembalian metode ini diterima menggunakan tipe integer "int", jadi mengapa tidak menggunakan "byte"?
Pertama-tama, nilai yang dikembalikan dengan metode baca haruslah biner delapan-bit, dan interval nilai yang dapat diambil dengan biner delapan-bit adalah: "0000 0000, 1111 1111", yaitu kisaran [-128, 127].
Metode yang dibaca juga menentukan bahwa ketika file dibaca sampai akhir, yaitu file tidak memiliki byte berikutnya untuk membaca, nilai -1 akan dikembalikan. Jadi jika byte digunakan sebagai jenis nilai pengembalian, maka ketika metode mengembalikan A -1, haruskah kita menentukan apakah ini adalah konten data dalam file atau akhir aliran?
Tipe int menempati empat byte, dan tiga byte di bit tinggi semuanya 0. Kami hanya menggunakan byte bit terendah. Saat menghadapi ujung bendera aliran, ia mengembalikan -1 (32 1s) diwakili oleh empat byte, yang secara alami berbeda dari nilai -1 (24 0 + 8 1s) yang mewakili data.
Berikutnya juga merupakan metode baca, tetapi InputStream menyediakan implementasi default:
Public int read (byte b []) melempar ioException {return read (b, 0, b.length);} public int read (byte b [], int off, int len) melempar ioException {// agar tidak membuat panjang terlalu lama, Anda dapat melihat kode sumber JDK sendiri {//Kedua metode ini pada dasarnya sama. Metode pertama adalah bentuk khusus dari metode kedua, yang memungkinkan array byte dilewatkan dan mengharuskan program untuk mengisi byte yang dibaca dalam file mulai dari posisi indeks array 0 untuk mengisi jumlah byte dalam panjang array.
Metode kedua sedikit lebih luas, yang memungkinkan Anda untuk menentukan posisi awal dan jumlah total byte.
Ada beberapa metode lain di InputStream, yang pada dasarnya tidak diimplementasikan secara rinci. Mari kita lihat sebentar.
Metode tanda akan menandai bendera pada posisi membaca aliran saat ini, dan metode reset akan mengatur ulang pointer baca ke bendera.
Bahkan, tidak mungkin untuk mengatur ulang bacaan untuk membaca file, tetapi umumnya semua byte antara posisi bendera dan titik reset disimpan sementara. Ketika metode reset dipanggil, sebenarnya dibaca diulang dari set byte sementara yang disimpan, sehingga ReadLimit digunakan untuk membatasi kapasitas cache maksimum.
Metode Marksupported digunakan untuk menentukan apakah aliran saat ini mendukung operasi baca "fallback" ini.
OutputStream dan InputStream serupa, kecuali bahwa satu ditulis dan yang lainnya dibaca. Kami tidak akan mengulanginya di sini.
File Byte Stream FileInput/OutputStream
Kami masih fokus pada FileInputStream, dan FileOutputStream serupa.
Pertama, FileInputStream memiliki konstruktor berikut untuk membuat objek instantiate:
public fileInputStream (nama string) melempar FileNotFoundException {this (name! = null? File baru (nama): null);} public fileInputStream (file file) melempar FileNotFoundException {String name = (file! = null? File.getPath (): null); SecurityManager Security = System.GetSecurityManager (); if (Security! = null) {Security.Checkread (name); } if (name == null) {lempar nullPointerException baru (); } if (file.isInvalid ()) {lempar filenotfoundException baru ("path file tidak valid"); } fd = new filedescriptor (); fd.attach (ini); jalur = nama; buka (nama);}Kedua konstruktor ini pada dasarnya sama, yang pertama adalah bentuk khusus dari yang terakhir. Faktanya, jangan lihat metode yang terakhir, yang sebagian besar hanya melakukan verifikasi keamanan. Inti adalah metode terbuka, yang digunakan untuk membuka file.
Terutama dua konstruktor ini, jika file tidak ada atau jalur file dan namanya ilegal, fileNoTFoundException akan dilemparkan.
Ingat kami mengatakan bahwa ada metode abstrak yang dibaca di inputstream kelas dasar yang mengharuskan semua subclass untuk diimplementasikan, dan FileInputStream diimplementasikan menggunakan metode lokal:
Public int read () melempar ioException {return read0 ();} private asli int read0 () melempar ioException;Kami tidak memiliki cara untuk mengeksplorasi implementasi spesifik Read0 untuk saat ini, tetapi Anda harus jelas bahwa fungsi metode baca ini digunakan untuk mengembalikan byte berikutnya dalam aliran, dan mengembalikan -1. Ini berarti dibacakan sampai akhir file dan tidak ada byte untuk dibaca.
Selain itu, ada beberapa metode terkait membaca lainnya di FileInputStream, tetapi kebanyakan diimplementasikan menggunakan metode lokal. Mari kita lihat di sini:
Metode internal FileInputStream pada dasarnya seperti ini, dan ada beberapa yang canggih dan kompleks yang tidak dapat kita gunakan untuk saat ini. Kami akan mempelajarinya nanti. Mari kita lihat secara singkat contoh membaca file:
public static void main (string [] args) melempar ioException {fileInputStream input = new fileInputStream ("c: //users//yanga//desktop//test.txt"); byte [] buffer = byte baru [1024]; int len = input.read (buffer); String str = string baru (buffer); System.out.println (str); System.out.println (len); input.close ();}Hasil output sangat sederhana. Ini akan mencetak konten dalam file uji kami dan jumlah byte yang sebenarnya dibacakan, tetapi siswa yang cermat akan mengetahuinya, bagaimana Anda dapat memastikan bahwa konten dalam file tes tidak akan melebihi 1024 byte?
Untuk membaca sepenuhnya konten file, satu solusi adalah menentukan buffer yang cukup besar untuk berharap untuk menyimpan semua konten file sebanyak mungkin.
Metode ini jelas tidak diinginkan karena tidak mungkin bagi kita untuk menyadari ukuran sebenarnya dari file yang akan dibaca. Ini adalah solusi yang sangat buruk untuk hanya membuat array byte yang terlalu besar.
Cara kedua adalah dengan menggunakan aliran array byte dinamis kami, yang secara dinamis dapat menyesuaikan ukuran array byte internal untuk memastikan kapasitas yang sesuai, yang akan kami perkenalkan secara rinci nanti.
Mengenai FileOutputStream, satu hal lagi untuk ditekankan adalah konstruktornya, yang memiliki dua konstruktor berikut:
FileOutputStream publik (nama string, boolean append) FileOutputStream publik (file file, boolean append)
Parameter ditambahkan menunjukkan apakah operasi penulisan aliran ini ditimpa atau ditambahkan, benar -benar berarti ditambahkan, salah berarti ditimpa.
BYTEARRAYINPUT/OutputStream
Yang disebut "aliran array byte" adalah aliran yang beroperasi di sekitar array byte. Itu tidak membaca dan menulis aliran ke file seperti aliran lainnya.
Meskipun aliran array byte bukan aliran berbasis file, itu masih merupakan aliran yang sangat penting, karena array byte yang dienkapsulasi di dalam tidak tetap, tetapi secara dinamis dapat diperluas, dan sering didasarkan pada skenario tertentu, yang sangat cocok.
BytearrayInputStream adalah aliran array byte baca yang dapat dipakai oleh konstruktor berikut:
Byte BuF yang dilindungi []; intropted int pos; jumlah int yang dilindungi; bytearrayinputStream (byte buf []) {this.buf = buf; this.pos = 0; this.count = buf.length;} publik bytearrayInputStream (byte buf [], int offset, int int)BUF adalah array byte yang dienkapsulasi di dalam bytearrayinputstream. Semua membaca operasi BytearRayInputStream berputar di sekitarnya.
Oleh karena itu, ketika membuat objek BytearRayInputStream, setidaknya satu array byte target dilewati.
Atribut POS digunakan untuk merekam posisi pembacaan aliran saat ini, dan menghitung mencatat posisi terakhir dari indeks byte valid terakhir dari array byte target.
Setelah memahami ini, tidak sulit untuk membaca berbagai cara untuk membacanya:
// Baca byte publik berikutnya yang disinkronkan int read () {return (pos <count)? (buf [pos ++] & 0xff): -1;} // Baca len byte dan masukkan ke dalam array byte b publik disinkronkan int read (byte b [], int, int len) {// sama, metode metode lebih panjang, semua orang memeriksa jdk} mereka sendiri}Selain itu, bytearrayInputStream juga mengimplementasikan operasi "berulang baca" dengan sangat sederhana.
mark public void (int readaheadlimit) {mark = pos;} public disinkronkan void reset () {pos = mark;}Karena bytearrayInputStream didasarkan pada array byte, semua operasi baca yang diulang lebih mudah diimplementasikan, dan itu cukup untuk diimplementasikan berdasarkan indeks.
BytearrayoutputStream adalah aliran array byte yang ditulis. Banyak implementasi masih memiliki karakteristik sendiri. Mari kita lihat bersama.
Pertama, kedua properti ini diperlukan:
Byte Buf yang dilindungi []; // Hitungan di sini mewakili jumlah byte yang valid dalam jumlah int yang dilindungi;
Konstruktor:
publik bytearrayoutputStream () {this (32);} publik bytearrayoutputStream (int int) {if (size <0) {lempar baru ilegalArgumentException ("ukuran awal negatif:"+ ukuran); } buf = byte baru [ukuran];}Tugas inti dari konstruktor adalah untuk menginisialisasi buf byte array internal, memungkinkan Anda untuk melewati ukuran untuk secara eksplisit membatasi ukuran array byte yang diinisialisasi, jika tidak panjang default akan menjadi 32.
Tulis Konten ke ByTeArrayOutputStream dari luar:
public disinkronkan void write (int b) {ensureCapacity (count + 1); buf [count] = (byte) b; Hitung + = 1;} public disinkronkan void write (byte b [], int off, int len) {if ((off <0) || (off> b.length) || (len <0) || ((OFF + len) - b.length> 0)) {throw indexOutOfBoundsException (); } EnsureCapacity (Count + Len); System.arraycopy (b, off, buf, count, len); hitung += len;}Melihat itu, langkah pertama dari semua operasi penulisan adalah untuk memanggil metode Ensurecapacity, tujuannya adalah untuk memastikan bahwa array byte dalam aliran saat ini dapat mengakomodasi operasi penulisan ini.
Metode ini juga sangat menarik. Jika Anda menemukan bahwa BUF internal tidak dapat mendukung operasi penulisan ini setelah perhitungan, metode pertumbuhan akan dipanggil untuk ekspansi. Prinsip ekspansi kapasitas mirip dengan arraylist, diperluas menjadi dua kali kapasitas asli.
Selain itu, ByTeArrayOutputStream juga memiliki metode Writeto:
public disinkronkan void writeTo (outputStream out) melempar ioException {out.write (buf, 0, count);}Tulis array byte yang dienkapsulasi secara internal kami ke dalam aliran output.
Beberapa metode yang tersisa juga sangat umum digunakan:
Perhatikan bahwa meskipun kedua aliran ini disebut "aliran", mereka pada dasarnya tidak mengalokasikan beberapa sumber daya seperti aliran nyata, jadi kita tidak perlu menyebut metode yang dekat, dan tidak ada gunanya menyebutnya (kata pejabat itu, tidak berpengaruh).
Kasus uji tidak akan dirilis. Saya akan mengunggah semua kasus kode yang digunakan dalam artikel ini nanti. Anda dapat memilih untuk mengunduhnya sendiri.
Untuk mengontrol panjangnya, pembelajaran yang tersisa akan ditempatkan di artikel berikutnya.
Semua kode, gambar, dan file dalam artikel disimpan di cloud di github saya:
(https://github.com/singleyam/overview_java)
Anda juga dapat memilih untuk mengunduh secara lokal.
Meringkaskan
Di atas adalah seluruh konten artikel ini. Saya berharap konten artikel ini memiliki nilai referensi tertentu untuk studi atau pekerjaan semua orang. Jika Anda memiliki pertanyaan, Anda dapat meninggalkan pesan untuk berkomunikasi. Terima kasih atas dukungan Anda ke wulin.com.