1. Fluxuri de date

Rareori un program există ca o insulă în sine. De obicei, programele interacționează cumva cu „lumea exterioară”. Acest lucru se poate întâmpla prin citirea datelor de la tastatură, trimiterea de mesaje, descărcarea paginilor de pe Internet sau, dimpotrivă, încărcarea fișierelor pe un server la distanță.

Ne putem referi la toate aceste comportamente într-un singur cuvânt: schimb de date între program și lumea exterioară. Stai, acesta nu este doar un cuvânt.

Desigur, schimbul de date în sine poate fi împărțit în două părți: primirea datelor și trimiterea datelor. De exemplu, citiți date de la tastatură folosind un Scannerobiect - acesta este primirea datelor. Și afișați datele pe ecran folosind o System.out.println()comandă - aceasta este trimiterea datelor.

În programare, termenul „flux” este folosit pentru a descrie schimbul de date. De unde a venit acel termen?

În viața reală, poți avea un curent de apă sau un curent de conștiință. În programare, avem fluxuri de date .

Fluxurile sunt un instrument versatil. Acestea permit programului să primească date de oriunde (fluxuri de intrare) și să trimită date oriunde (fluxuri de ieșire). Astfel, există două tipuri:

  • Un flux de intrare este pentru primirea datelor
  • Un flux de ieșire este pentru trimiterea datelor

Pentru a face fluxurile „tangibile”, creatorii Java au scris două clase: InputStreamși OutputStream.

Clasa InputStreamare o read()metodă care vă permite să citiți datele din ea. Și OutputStreamclasa are o write()metodă care vă permite să scrieți date în ea. Au și alte metode, dar mai multe despre asta mai târziu.

Fluxuri de octeți

Despre ce fel de date vorbim? Ce format are? Cu alte cuvinte, ce tipuri de date acceptă aceste clase?

Acestea sunt clase generice, deci acceptă cel mai comun tip de date - byte. Un OutputStreampoate scrie octeți (și matrice de octeți), iar un InputStreamobiect poate citi octeți (sau matrice de octeți). Asta este — nu acceptă alte tipuri de date.

Drept urmare, aceste fluxuri sunt numite și fluxuri de octeți .

O caracteristică a fluxurilor este că datele lor pot fi citite (sau scrise) numai secvenţial. Nu puteți citi date din mijlocul unui flux fără să citiți toate datele care vin înaintea acestuia.

Așa funcționează citirea datelor de la tastatură prin Scannerclasă: citiți datele de la tastatură secvenţial, rând cu linie. Citim un rând, apoi următorul rând, apoi următorul rând și așa mai departe. În mod potrivit, metoda de citire a liniilor se numește nextLine().

Scrierea datelor într-un OutputStreamare loc, de asemenea, secvenţial. Un bun exemplu în acest sens este ieșirea din consolă. Ieși o linie, urmată de alta și alta. Aceasta este ieșire secvențială. Nu puteți scoate prima linie, apoi a zecea și apoi a doua. Toate datele sunt scrise într-un flux de ieșire numai secvenţial.

Fluxuri de caractere

Ați aflat recent că șirurile de caractere sunt al doilea cel mai popular tip de date și, într-adevăr, sunt. O mulțime de informații sunt transmise sub formă de caractere și șiruri întregi. Un computer excelează la trimiterea și primirea totul ca octeți, dar oamenii nu sunt atât de perfecți.

Luând în considerare acest fapt, programatorii Java au mai scris două clase: Readerși Writer. Clasa Readereste analogă cu InputStreamclasa, dar read()metoda sa citește nu octeți, ci caractere ( char). Clasa Writercorespunde clasei OutputStream. Și la fel ca și Readerclasa, funcționează cu caractere ( char), nu cu octeți.

Dacă comparăm aceste patru clase, obținem următoarea imagine:

octeți (octeți) Caractere (caracter)
Citirea datelor
InputStream
Reader
Scrierea datelor
OutputStream
Writer

Aplicație practică

Clasele InputStream, OutputStream, Readerși Writerîn sine nu sunt folosite direct de nimeni, deoarece nu sunt asociate cu niciun obiect concret din care să poată fi citite date (sau în care să poată fi scrise date). Dar aceste patru clase au o mulțime de clase descendente care pot face multe.


2. InputStreamclasa

Clasa InputStreameste interesantă pentru că este clasa părinte pentru sute de clase descendente. Nu are date proprii, dar are metode pe care toate clasele sale derivate le moștenesc.

În general, este rar ca obiectele de flux să stocheze date intern. Un flux este un instrument de citire/scriere de date, dar nu de stocare. Acestea fiind spuse, există și excepții.

Metode ale InputStreamclasei și ale tuturor claselor sale descendente:

Metode Descriere
int read()
Citește un octet din flux
int read(byte[] buffer)
Citește o serie de octeți din flux
byte[] readAllBytes()
Citește toți octeții din flux
long skip(long n)
Omite nocteți în flux (i citește și îi renunță)
int available()
Verifică câți octeți au mai rămas în flux
void close()
Închide fluxul

Să trecem pe scurt prin aceste metode:

read()metodă

Metoda read()citește un octet din flux și îl returnează. S-ar putea să fii confuz de inttipul de returnare. Acest tip a fost ales deoarece inteste tipul întreg standard. Primii trei octeți ai intvor fi zero.

read(byte[] buffer)metodă

Aceasta este a doua variantă a read()metodei. Vă permite să citiți o matrice de octeți InputStreamdintr-o dată. Matricea care va stoca octeții trebuie să fie transmisă ca argument. Metoda returnează un număr - numărul de octeți citiți efectiv.

Să presupunem că aveți un buffer de 10 kiloocteți și citiți date dintr-un fișier folosind FileInputStreamclasa. Dacă fișierul conține doar 2 kiloocteți, toate datele vor fi încărcate în matricea tampon, iar metoda va returna numărul 2048 (2 kiloocteți).

readAllBytes()metodă

O metoda foarte buna. Pur și simplu citește toate datele din InputStreampână când se epuizează și le returnează ca o matrice de un singur octet. Acest lucru este foarte util pentru citirea fișierelor mici. Este posibil ca fișierele mari să nu încapă fizic în memorie, iar metoda va crea o excepție.

skip(long n)metodă

Această metodă vă permite să omiteți primii n octeți din InputStreamobiect. Deoarece datele sunt citite strict secvențial, această metodă citește pur și simplu primii n octeți din flux și îi aruncă.

Returnează numărul de octeți care au fost săriți efectiv (în cazul în care fluxul s-a încheiat înainte ca nocteții să fie săriți).

int available()metodă

Metoda returnează numărul de octeți care au mai rămas în flux

void close()metodă

Metoda close()închide fluxul de date și eliberează resursele externe asociate acestuia. Odată ce un flux este închis, nu mai pot fi citite date din acesta.

Să scriem un exemplu de program care copiază un fișier foarte mare. Nu putem folosi readAllBytes()metoda pentru a citi întregul fișier în memorie. Exemplu:

Cod Notă
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);
   }
}



InputStreampentru citirea din fișier
OutputStreampentru scriere în fișier

Buffer în care vom citi datele
Atâta timp cât există date în flux

Citiți date în buffer
Scrieți datele din buffer în al doilea flux

În acest exemplu, am folosit două clase: FileInputStreameste un descendent al lui InputStreampentru citirea datelor dintr-un fișier și FileOutputStreameste un descendent al lui OutputStreampentru scrierea datelor într-un fișier. Despre clasa a doua vom vorbi puțin mai târziu.

Un alt punct interesant aici este realvariabila. Când ultimul bloc de date este citit dintr-un fișier, acesta ar putea avea cu ușurință mai puțin de 64 KB de date. În consecință, trebuie să scoatem nu întregul buffer, ci doar o parte din acesta - primii realocteți. Este exact ceea ce se întâmplă în write()metodă.



3. Readerclasa

Clasa Readereste un analog complet al InputStreamclasei. Singura diferență este că funcționează cu caractere ( char), nu cu octeți. La fel ca InputStreamclasa, Readerclasa nu este folosită nicăieri singură: este clasa părinte pentru sute de clase descendente și definește metode comune pentru toate.

Metode ale Readerclasei (și toate clasele sale descendente):

Metode Descriere
int read()
Citește unul chardin flux
int read(char[] buffer)
Citește o charmatrice din flux
long skip(long n)
Omite n charsîn flux (le citește și le elimină)
boolean ready()
Verifică dacă a mai rămas ceva în flux
void close()
Închide fluxul

Metodele sunt foarte asemănătoare cu cele ale InputStreamclasei, deși există mici diferențe.

int read()metodă

Această metodă citește unul chardin flux și îl returnează. Tipul charse lărgește la un int, dar primii doi octeți ai rezultatului sunt întotdeauna zero.

int read(char[] buffer)metodă

Aceasta este a doua variantă a read()metodei. Vă permite să citiți o matrice de caractere Readerdintr-o dată. Matricea care va stoca caracterele trebuie transmisă ca argument. Metoda returnează un număr — numărul de caractere citite efectiv.

skip(long n)metodă

Această metodă vă permite să săriți peste primele n caractere din Readerobiect. Funcționează exact la fel ca metoda analogă a InputStreamclasei. Returnează numărul de caractere care au fost de fapt sărit.

boolean ready()metodă

Returnează truedacă există octeți necitiți în flux.

void close()metodă

Metoda close()închide fluxul de date și eliberează resursele externe asociate acestuia. Odată ce un flux este închis, nu mai pot fi citite date din acesta.

Pentru comparație, să scriem un program care copiază un fișier text:

Cod Notă
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);
   }
}



Readerpentru citirea dintr-un fișier
Writerpentru scrierea într-un fișier

Buffer în care vom citi datele
Atâta timp cât există date în flux

Citiți date într-un buffer
Scrieți datele din buffer în al doilea flux