1. Gegevensstromen
Zelden bestaat een programma als een eiland op zich. Programma's hebben meestal op de een of andere manier interactie met de "buitenwereld". Dit kan gebeuren door gegevens van het toetsenbord te lezen, berichten te verzenden, pagina's van internet te downloaden of, omgekeerd, bestanden naar een externe server te uploaden.
We kunnen al deze gedragingen in één woord aanduiden: gegevensuitwisseling tussen het programma en de buitenwereld. Wacht, dat is niet zomaar een woord.
Natuurlijk kan de gegevensuitwisseling zelf in twee delen worden verdeeld: gegevens ontvangen en gegevens verzenden. U leest bijvoorbeeld gegevens van het toetsenbord met behulp van een Scannerobject - dit is het ontvangen van gegevens. En u geeft gegevens op het scherm weer met een System.out.println()opdracht - dit is het verzenden van gegevens.
Bij het programmeren wordt de term "stroom" gebruikt om gegevensuitwisseling te beschrijven. Waar komt die term vandaan?
In het echte leven kun je een stroom van water of een stroom van bewustzijn hebben. Bij het programmeren hebben we datastromen .
Streams zijn een veelzijdige tool. Ze stellen het programma in staat om overal vandaan gegevens te ontvangen (invoerstromen) en gegevens overal te verzenden (uitvoerstromen). Er zijn dus twee soorten:
- Een invoerstroom is voor het ontvangen van gegevens
- Een uitvoerstroom is voor het verzenden van gegevens
Om streams 'tastbaar' te maken, schreven de makers van Java twee klassen: InputStreamen OutputStream.
De InputStreamklasse heeft een read()methode waarmee u er gegevens uit kunt lezen. En de OutputStreamklasse heeft een write()methode waarmee je er gegevens naar kunt schrijven. Ze hebben ook andere methoden, maar daarover later meer.
Bytestromen
Over wat voor gegevens hebben we het? Welk formaat heeft het nodig? Met andere woorden, welke gegevenstypen ondersteunen deze klassen?
Dit zijn generieke klassen, dus ze ondersteunen het meest voorkomende gegevenstype: de byte. Een OutputStreamkan bytes (en byte-arrays) schrijven en een InputStreamobject kan bytes (of byte-arrays) lezen. Dat is alles - ze ondersteunen geen andere gegevenstypen.
Daarom worden deze streams ook wel bytestreams genoemd .
Een kenmerk van streams is dat hun gegevens alleen sequentieel kunnen worden gelezen (of geschreven). U kunt geen gegevens uit het midden van een stream lezen zonder alle gegevens te lezen die eraan voorafgaan.
Dit is hoe het lezen van gegevens van het toetsenbord door de Scannerklas gaat: u leest de gegevens opeenvolgend van het toetsenbord, regel voor regel. We lezen een regel, dan de volgende regel, dan de volgende regel, enzovoort. Passend heet de methode voor het lezen van regels nextLine().
Het schrijven van gegevens naar een OutputStreamgebeurt ook sequentieel. Een goed voorbeeld hiervan is console-uitvoer. U voert een regel uit, gevolgd door nog een en nog een. Dit is sequentiële uitvoer. U kunt niet de eerste regel uitvoeren, dan de tiende en dan de tweede. Alle gegevens worden slechts sequentieel naar een uitvoerstroom geschreven.
Karakterstromen
U heeft onlangs vernomen dat tekenreeksen het op één na populairste gegevenstype zijn, en dat zijn ze ook. Er wordt veel informatie doorgegeven in de vorm van karakters en hele strings. Een computer blinkt uit in het verzenden en ontvangen van alles als bytes, maar mensen zijn niet zo perfect.
Om dit feit te verklaren, schreven Java-programmeurs nog twee klassen: Readeren Writer. De Readerklasse is analoog aan de InputStreamklasse, maar de read()methode leest geen bytes, maar tekens ( char). De Writerklas komt overeen met de OutputStreamklas. En net als de Readerklasse werkt het met tekens ( char), niet met bytes.
Als we deze vier klassen vergelijken, krijgen we het volgende beeld:
| byte (byte) | Karakters (char) | |
|---|---|---|
| Gegevens lezen |
|
|
| Gegevens schrijven |
|
|
Praktische toepassing
De klassen InputStream, OutputStream, Readeren Writerzelf worden door niemand rechtstreeks gebruikt, omdat ze niet zijn gekoppeld aan concrete objecten waaruit gegevens kunnen worden gelezen (of waarin gegevens kunnen worden geschreven). Maar deze vier klassen hebben genoeg afstammelingen die veel kunnen.
2. InputStreamklasse
De InputStreamklasse is interessant omdat het de ouderklasse is voor honderden afstammelingenklassen. Het heeft geen eigen gegevens, maar het heeft wel methoden die alle afgeleide klassen erven.
Over het algemeen is het zeldzaam dat stream-objecten gegevens intern opslaan. Een stream is een hulpmiddel voor het lezen/schrijven van gegevens, maar geen opslag. Dat gezegd hebbende, er zijn uitzonderingen.
Methoden van de InputStreamklasse en al zijn onderliggende klassen:
| methoden | Beschrijving |
|---|---|
|
Leest één byte uit de stream |
|
Leest een reeks bytes uit de stream |
|
Leest alle bytes uit de stream |
|
Slaat nbytes in de stream over (leest en verwijdert ze) |
|
Controleert hoeveel bytes er nog over zijn in de stream |
|
Sluit de stroom af |
Laten we deze methoden kort doornemen:
read()methode
De read()methode leest één byte uit de stream en retourneert deze. U kunt in de war raken door het intretourtype. Dit type is gekozen omdat intdit het standaard integer type is. De eerste drie bytes van de intzullen nul zijn.
read(byte[] buffer)methode
Dit is de tweede variant van de read()methode. Hiermee kunt u een byte-array van een InputStreamalles tegelijk lezen. De array waarin de bytes worden opgeslagen, moet als argument worden doorgegeven. De methode retourneert een getal — het aantal werkelijk gelezen bytes.
Laten we zeggen dat je een buffer van 10 kilobyte hebt en dat je gegevens uit een bestand leest met behulp van de FileInputStreamklasse. Als het bestand slechts 2 kilobytes bevat, worden alle gegevens in de bufferarray geladen en retourneert de methode het getal 2048 (2 kilobytes).
readAllBytes()methode
Een hele goede methode. Het leest gewoon alle gegevens van InputStreamtotdat het op is en retourneert het als een enkele byte-array. Dit is erg handig voor het lezen van kleine bestanden. Grote bestanden passen mogelijk fysiek niet in het geheugen en de methode genereert een uitzondering.
skip(long n)methode
Met deze methode kunt u de eerste n bytes van het object overslaan InputStream. Omdat de gegevens strikt opeenvolgend worden gelezen, leest deze methode eenvoudigweg de eerste n bytes uit de stream en verwijdert deze.
Retourneert het aantal bytes dat daadwerkelijk is overgeslagen (in het geval dat de stream eindigde voordat er nbytes werden overgeslagen).
int available()methode
De methode retourneert het aantal bytes dat nog over is in de stream
void close()methode
De close()methode sluit de gegevensstroom af en geeft de bijbehorende externe bronnen vrij. Zodra een stream is gesloten, kunnen er geen gegevens meer uit worden gelezen.
Laten we een voorbeeldprogramma schrijven dat een heel groot bestand kopieert. We kunnen de readAllBytes()methode niet gebruiken om het hele bestand in het geheugen te lezen. Voorbeeld:
| Code | Opmerking |
|---|---|
|
InputStreamvoor het lezen van het bestand OutputStreamvoor het schrijven naar het bestand Buffer waarin we de gegevens zullen lezen Zolang er gegevens in de stroom zijn Lees gegevens in de buffer Schrijf de gegevens van de buffer naar de tweede stroom |
In dit voorbeeld hebben we twee klassen gebruikt: FileInputStreamis een afstammeling van InputStreamvoor het lezen van gegevens uit een bestand, en FileOutputStreamis een afstammeling van OutputStreamvoor het schrijven van gegevens naar een bestand. We zullen het later over de tweede klas hebben.
Een ander interessant punt hier is de realvariabele. Wanneer het laatste gegevensblok uit een bestand wordt gelezen, kan het gemakkelijk minder dan 64 KB aan gegevens bevatten. Daarom moeten we niet de hele buffer uitvoeren, maar slechts een deel ervan - de eerste realbytes. Dit is precies wat er in de write()methode gebeurt.
3. Readerklasse
De Readerklas is een volledig analoog van de InputStreamklas. Het enige verschil is dat het werkt met karakters ( char), niet met bytes. Net als de InputStreamklasse wordt de Readerklasse nergens op zichzelf gebruikt: het is de bovenliggende klasse voor honderden onderliggende klassen en definieert gemeenschappelijke methoden voor al deze klassen.
Methoden van de Readerklasse (en al zijn onderliggende klassen):
| methoden | Beschrijving |
|---|---|
|
Leest er een charuit de stream |
|
Leest een chararray uit de stream |
|
Springt n charsin de stream (leest en verwijdert ze) |
|
Controleert of er nog iets in de stream staat |
|
Sluit de stroom af |
De methoden lijken erg op die van de InputStreamklas, hoewel er kleine verschillen zijn.
int read()methode
Deze methode leest er een charuit de stream en retourneert deze. Het chartype verbreedt naar een int, maar de eerste twee bytes van het resultaat zijn altijd nul.
int read(char[] buffer)methode
Dit is de tweede variant van de read()methode. Hiermee kunt u Readerin één keer een char-array van een all-in lezen. De array waarin de tekens worden opgeslagen, moet als argument worden doorgegeven. De methode retourneert een getal — het aantal tekens dat daadwerkelijk is gelezen.
skip(long n)methode
Met deze methode kunt u de eerste n tekens van het object overslaan Reader. Het werkt precies hetzelfde als de analoge methode van de InputStreamklasse. Retourneert het aantal tekens dat daadwerkelijk is overgeslagen.
boolean ready()methode
Retourneert trueals er ongelezen bytes in de stream zijn.
void close()methode
De close()methode sluit de gegevensstroom af en geeft de bijbehorende externe bronnen vrij. Zodra een stream is gesloten, kunnen er geen gegevens meer uit worden gelezen.
Laten we ter vergelijking een programma schrijven dat een tekstbestand kopieert:
| Code | Opmerking |
|---|---|
|
Readervoor het lezen van een bestand Writervoor het schrijven naar een bestand Buffer waarin we de gegevens zullen lezen Zolang er gegevens in de stroom zijn Lees gegevens in een buffer Schrijf de gegevens van de buffer naar de tweede stroom |
GO TO FULL VERSION