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 Scanner
object - 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: InputStream
en OutputStream
.
De InputStream
klasse heeft een read()
methode waarmee u er gegevens uit kunt lezen. En de OutputStream
klasse 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 OutputStream
kan bytes (en byte-arrays) schrijven en een InputStream
object 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 Scanner
klas 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 OutputStream
gebeurt 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: Reader
en Writer
. De Reader
klasse is analoog aan de InputStream
klasse, maar de read()
methode leest geen bytes, maar tekens ( char
). De Writer
klas komt overeen met de OutputStream
klas. En net als de Reader
klasse 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
, Reader
en Writer
zelf 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. InputStream
klasse
De InputStream
klasse 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 InputStream
klasse 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 n bytes 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 int
retourtype. Dit type is gekozen omdat int
dit het standaard integer type is. De eerste drie bytes van de int
zullen nul zijn.
read(byte[] buffer)
methode
Dit is de tweede variant van de read()
methode. Hiermee kunt u een byte-array van een InputStream
alles 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 FileInputStream
klasse. 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 InputStream
totdat 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 n
bytes 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 |
---|---|
|
InputStream voor het lezen van het bestand OutputStream voor 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: FileInputStream
is een afstammeling van InputStream
voor het lezen van gegevens uit een bestand, en FileOutputStream
is een afstammeling van OutputStream
voor het schrijven van gegevens naar een bestand. We zullen het later over de tweede klas hebben.
Een ander interessant punt hier is de real
variabele. 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 real
bytes. Dit is precies wat er in de write()
methode gebeurt.
3. Reader
klasse
De Reader
klas is een volledig analoog van de InputStream
klas. Het enige verschil is dat het werkt met karakters ( char
), niet met bytes. Net als de InputStream
klasse wordt de Reader
klasse nergens op zichzelf gebruikt: het is de bovenliggende klasse voor honderden onderliggende klassen en definieert gemeenschappelijke methoden voor al deze klassen.
Methoden van de Reader
klasse (en al zijn onderliggende klassen):
methoden | Beschrijving |
---|---|
|
Leest er een char uit de stream |
|
Leest een char array uit de stream |
|
Springt n chars in 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 InputStream
klas, hoewel er kleine verschillen zijn.
int read()
methode
Deze methode leest er een char
uit de stream en retourneert deze. Het char
type 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 Reader
in éé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 InputStream
klasse. Retourneert het aantal tekens dat daadwerkelijk is overgeslagen.
boolean ready()
methode
Retourneert true
als 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 |
---|---|
|
Reader voor het lezen van een bestand Writer voor 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