1. Datastrømmer

Sjelden eksisterer et program som en øy for seg selv. Programmer samhandler vanligvis på en eller annen måte med "verden utenfor". Dette kan skje ved å lese data fra tastaturet, sende meldinger, laste ned sider fra Internett, eller omvendt, laste opp filer til en ekstern server.

Vi kan referere til alle disse atferdene i ett ord: datautveksling mellom programmet og omverdenen. Vent, det er ikke bare ett ord.

Selve datautvekslingen kan selvfølgelig deles inn i to deler: mottak av data og sending av data. For eksempel leser du data fra tastaturet ved hjelp av et Scannerobjekt - dette er å motta data. Og du viser data på skjermen ved å bruke en System.out.println()kommando - dette er å sende data.

I programmering brukes begrepet "strøm" for å beskrive datautveksling. Hvor kom det begrepet fra?

I det virkelige liv kan du ha en strøm av vann eller en strøm av bevissthet. I programmering har vi datastrømmer .

Strømmer er et allsidig verktøy. De lar programmet motta data fra hvor som helst (inndatastrømmer) og sende data hvor som helst (utdatastrømmer). Dermed er det to typer:

  • En inngangsstrøm er for å motta data
  • En utdatastrøm er for å sende data

For å gjøre strømmer "håndgripelige", skrev Javas skapere to klasser: InputStreamog OutputStream.

Klassen InputStreamhar en read()metode som lar deg lese data fra den. Og OutputStreamklassen har en write()metode som lar deg skrive data til den. De har andre metoder også, men mer om det senere.

Bytestrømmer

Hva slags data snakker vi om? Hvilket format tar det? Med andre ord, hvilke datatyper støtter disse klassene?

Dette er generiske klasser, så de støtter den vanligste datatypen - byte. En OutputStreamkan skrive byte (og byte-matriser), og et InputStreamobjekt kan lese byte (eller byte-matriser). Det er det - de støtter ikke noen andre datatyper.

Som et resultat kalles disse strømmene også bytestrømmer .

En funksjon ved strømmer er at dataene deres bare kan leses (eller skrives) sekvensielt. Du kan ikke lese data fra midten av en strøm uten å lese alle dataene som kommer før den.

Slik fungerer lesing av data fra tastaturet gjennom klassen Scanner: du leser data fra tastaturet sekvensielt, linje for linje. Vi leser en linje, så neste linje, så neste linje, og så videre. Passende nok heter metoden for å lese linjer nextLine().

Å skrive data til en OutputStreamskjer også sekvensielt. Et godt eksempel på dette er konsollutgang. Du skriver ut en linje, etterfulgt av en og en til. Dette er sekvensiell utgang. Du kan ikke skrive ut den første linjen, deretter den tiende og deretter den andre. Alle data skrives kun sekvensielt til en utdatastrøm.

Karakterstrømmer

Du har nylig lært at strenger er den nest mest populære datatypen, og det er de faktisk. Mye informasjon sendes rundt i form av tegn og hele strenger. En datamaskin utmerker seg ved å sende og motta alt som byte, men mennesker er ikke så perfekte.

På grunn av dette, skrev Java-programmerere to klasser til: Readerog Writer. Klassen Readerer analog med InputStreamklassen, men read()metoden leser ikke bytes, men tegn ( char). Klassen Writertilsvarer klassen OutputStream. Og akkurat som Readerklassen fungerer den med tegn ( char), ikke byte.

Hvis vi sammenligner disse fire klassene, får vi følgende bilde:

Byte (byte) Tegn (char)
Leser data
InputStream
Reader
Skrive data
OutputStream
Writer

Praktisk anvendelse

Selve InputStream, OutputStream, Readerog Writerklassene brukes ikke direkte av noen, siden de ikke er assosiert med noen konkrete objekter som data kan leses fra (eller som data kan skrives inn). Men disse fire klassene har nok av etterkommerklasser som kan mye.


2. InputStreamklasse

Klassen InputStreamer interessant fordi den er foreldreklassen for hundrevis av etterkommerklasser. Den har ingen egne data, men den har metoder som alle dens avledede klasser arver.

Generelt er det sjelden at strømobjekter lagrer data internt. En strøm er et verktøy for lesing/skriving av data, men ikke lagring. Når det er sagt, finnes det unntak.

Metoder for InputStreamklassen og alle dens etterkommerklasser:

Metoder Beskrivelse
int read()
Leser én byte fra strømmen
int read(byte[] buffer)
Leser en rekke byte fra strømmen
byte[] readAllBytes()
Leser alle bytene fra strømmen
long skip(long n)
Hopper over nbyte i strømmen (leser og forkaster dem)
int available()
Sjekker hvor mange byte som er igjen i strømmen
void close()
Lukker strømmen

La oss kort gå gjennom disse metodene:

read()metode

Metoden read()leser én byte fra strømmen og returnerer den. Du kan bli forvirret av intreturtypen. Denne typen ble valgt fordi inter standard heltallstype. De tre første bytene av intvil være null.

read(byte[] buffer)metode

Dette er den andre varianten av read()metoden. Den lar deg lese en byte-array fra en InputStreamalt på en gang. Matrisen som skal lagre bytene må sendes som et argument. Metoden returnerer et tall - antallet byte som faktisk er lest.

La oss si at du har en buffer på 10 kilobyte og at du leser data fra en fil ved hjelp av FileInputStreamklassen. Hvis filen bare inneholder 2 kilobyte, vil alle dataene bli lastet inn i bufferarrayen, og metoden vil returnere tallet 2048 (2 kilobyte).

readAllBytes()metode

En veldig god metode. Den leser bare alle dataene fra InputStreamtil den går tom og returnerer den som en enkeltbyte-array. Dette er veldig nyttig for å lese små filer. Store filer passer kanskje ikke fysisk inn i minnet, og metoden vil gi et unntak.

skip(long n)metode

Denne metoden lar deg hoppe over de første n bytene fra objektet InputStream. Fordi dataene leses strengt sekvensielt, leser denne metoden ganske enkelt de første n bytene fra strømmen og forkaster dem.

Returnerer antall byte som faktisk ble hoppet over (i tilfelle strømmen ble avsluttet før nbyte ble hoppet over).

int available()metode

Metoden returnerer antall byte som fortsatt er igjen i strømmen

void close()metode

Metoden close()lukker datastrømmen og frigjør de eksterne ressursene knyttet til den. Når en strøm er stengt, kan ikke flere data leses fra den.

La oss skrive et eksempelprogram som kopierer en veldig stor fil. Vi kan ikke bruke readAllBytes()metoden til å lese hele filen inn i minnet. Eksempel:

Kode Merk
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);
   }
}



InputStreamfor lesing fra filen
OutputStreamfor skriv til fil

Buffer som vi skal lese dataene inn i
Så lenge det er data i strømmen

Les data inn i bufferen
Skriv dataene fra bufferen til den andre strømmen

I dette eksemplet brukte vi to klasser: FileInputStreamer en etterkommer av InputStreamfor å lese data fra en fil, og FileOutputStreamer en etterkommer av OutputStreamfor å skrive data til en fil. Vi skal snakke om den andre klassen litt senere.

Et annet interessant poeng her er realvariabelen. Når den siste blokken med data leses fra en fil, kan den lett ha mindre enn 64KB med data. Følgelig må vi ikke sende ut hele bufferen, men bare en del av den - de første realbytene. Det er nettopp dette som skjer i write()metoden.



3. Readerklasse

Klassen Readerer en komplett analog av InputStreamklassen. Den eneste forskjellen er at det fungerer med tegn ( char), ikke med byte. Akkurat som InputStreamklassen, Readerbrukes ikke klassen noe sted alene: den er overordnet klasse for hundrevis av etterkommerklasser og definerer vanlige metoder for dem alle.

Metoder for Readerklassen (og alle dens etterkommerklasser):

Metoder Beskrivelse
int read()
Leser en charfra strømmen
int read(char[] buffer)
Leser en charmatrise fra strømmen
long skip(long n)
Hopper over n charsi strømmen (leser og forkaster dem)
boolean ready()
Sjekker om det fortsatt er noe igjen i strømmen
void close()
Lukker strømmen

Metodene er veldig like de i InputStreamklassen, selv om det er små forskjeller.

int read()metode

Denne metoden leser en charfra strømmen og returnerer den. Typen charutvides til en int, men de to første bytene av resultatet er alltid null.

int read(char[] buffer)metode

Dette er den andre varianten av read()metoden. Den lar deg lese en char array fra en Readeralt på en gang. Matrisen som skal lagre tegnene må sendes som et argument. Metoden returnerer et tall – antallet tegn som faktisk er lest.

skip(long n)metode

Denne metoden lar deg hoppe over de første n tegnene fra objektet Reader. Det fungerer nøyaktig det samme som den analoge metoden i InputStreamklassen. Returnerer antall tegn som faktisk ble hoppet over.

boolean ready()metode

Returnerer truehvis det er uleste byte i strømmen.

void close()metode

Metoden close()lukker datastrømmen og frigjør de eksterne ressursene knyttet til den. Når en strøm er stengt, kan ikke flere data leses fra den.

Til sammenligning, la oss skrive et program som kopierer en tekstfil:

Kode Merk
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);
   }
}



Readerfor å lese fra en fil
Writerfor å skrive til en fil

Buffer som vi skal lese dataene inn i
Så lenge det er data i strømmen

Les data inn i en buffer
Skriv dataene fra bufferen til den andre strømmen