1. Dataströmmar
Sällan existerar ett program som en ö för sig själv. Program brukar på något sätt interagera med "omvärlden". Detta kan ske genom att läsa data från tangentbordet, skicka meddelanden, ladda ner sidor från Internet eller, omvänt, ladda upp filer till en fjärrserver.
Vi kan referera till alla dessa beteenden i ett ord: datautbyte mellan programmet och omvärlden. Vänta, det är inte bara ett ord.
Självklart kan datautbytet i sig delas upp i två delar: ta emot data och skicka data. Till exempel läser du data från tangentbordet med hjälp av ett Scanner
objekt — det här är att ta emot data. Och du visar data på skärmen med ett System.out.println()
kommando - det här är att skicka data.
I programmering används termen "ström" för att beskriva datautbyte. Var kom den termen ifrån?
I verkliga livet kan du ha en ström av vatten eller en ström av medvetande. Inom programmering har vi dataströmmar .
Strömmar är ett mångsidigt verktyg. De tillåter programmet att ta emot data var som helst (indataströmmar) och skicka data var som helst (outputströmmar). Det finns alltså två typer:
- En ingångsström är för att ta emot data
- En utgångsström är till för att skicka data
För att göra strömmar "påtagliga" skrev Javas skapare två klasser: InputStream
och OutputStream
.
Klassen InputStream
har en read()
metod som låter dig läsa data från den. Och OutputStream
klassen har en write()
metod som låter dig skriva data till den. De har andra metoder också, men mer om det senare.
Byteströmmar
Vilken typ av data pratar vi om? Vilket format tar det? Med andra ord, vilka datatyper stöder dessa klasser?
Dessa är generiska klasser, så de stöder den vanligaste datatypen - byte
. En OutputStream
kan skriva bytes (och byte-arrayer), och ett InputStream
objekt kan läsa bytes (eller byte-arrayer). Det är det — de stöder inte några andra datatyper.
Som ett resultat kallas dessa strömmar även byteströmmar .
En egenskap hos strömmar är att deras data endast kan läsas (eller skrivas) sekventiellt. Du kan inte läsa data från mitten av en ström utan att läsa all data som kommer före den.
Så här fungerar att läsa data från tangentbordet genom klassen Scanner
: du läser data från tangentbordet sekventiellt, rad för rad. Vi läser en rad, sedan nästa rad, sedan nästa rad och så vidare. Passande nog kallas metoden för att läsa rader nextLine()
.
Att skriva data till en OutputStream
sker också sekventiellt. Ett bra exempel på detta är konsolutgång. Du matar ut en rad, följt av en och en till. Detta är sekventiell utgång. Du kan inte skriva ut den första raden, sedan den tionde och sedan den andra. All data skrivs till en utgångsström endast sekventiellt.
Karaktärsströmmar
Du lärde dig nyligen att strängar är den näst mest populära datatypen, och det är de faktiskt. Mycket information skickas runt i form av karaktärer och hela strängar. En dator utmärker sig på att skicka och ta emot allt som byte, men människor är inte så perfekta.
Med hänsyn till detta, skrev Java-programmerare ytterligare två klasser: Reader
och Writer
. Klassen Reader
är analog med InputStream
klassen, men dess read()
metod läser inte bytes, utan tecken ( ) char
. Klassen Writer
motsvarar klassen OutputStream
. Och precis som Reader
klassen fungerar den med tecken ( char
), inte byte.
Om vi jämför dessa fyra klasser får vi följande bild:
Byte (byte) | Tecken (char) | |
---|---|---|
Läser data |
|
|
Att skriva data |
|
|
Praktisk applikation
Klasserna InputStream
, OutputStream
, Reader
och Writer
själva används inte direkt av någon, eftersom de inte är förknippade med några konkreta objekt från vilka data kan läsas (eller i vilka data kan skrivas). Men dessa fyra klasser har gott om ättlingklasser som kan mycket.
2. InputStream
klass
Klassen InputStream
är intressant eftersom den är föräldraklass för hundratals efterkommande klasser. Den har inga egna data, men den har metoder som alla dess härledda klasser ärver.
I allmänhet är det sällsynt att strömobjekt lagrar data internt. En stream är ett verktyg för att läsa/skriva data, men inte lagring. Som sagt, det finns undantag.
Metoder för InputStream
klassen och alla dess efterkommande klasser:
Metoder | Beskrivning |
---|---|
|
Läser en byte från strömmen |
|
Läser en array av byte från strömmen |
|
Läser alla bytes från strömmen |
|
Hoppa över n byte i flödet (läser och slänger dem) |
|
Kontrollerar hur många byte som finns kvar i strömmen |
|
Stänger strömmen |
Låt oss kortfattat gå igenom dessa metoder:
read()
metod
Metoden read()
läser en byte från strömmen och returnerar den. Du kan bli förvirrad av int
returtypen. Denna typ valdes eftersom int
den är standard heltalstyp. De första tre byten av int
kommer att vara noll.
read(byte[] buffer)
metod
Detta är den andra varianten av read()
metoden. Det låter dig läsa en byte-array från en InputStream
allt på en gång. Arrayen som kommer att lagra byte måste skickas som ett argument. Metoden returnerar ett tal — antalet byte som faktiskt lästs.
Låt oss säga att du har en 10 kilobyte buffert och att du läser data från en fil med hjälp av FileInputStream
klassen. Om filen endast innehåller 2 kilobyte kommer all data att laddas in i buffertmatrisen och metoden returnerar talet 2048 (2 kilobyte).
readAllBytes()
metod
En mycket bra metod. Den läser bara all data från InputStream
tills den tar slut och returnerar den som en enda byte-array. Detta är mycket praktiskt för att läsa små filer. Stora filer kanske inte får plats fysiskt i minnet, och metoden kommer att skapa ett undantag.
skip(long n)
metod
Denna metod låter dig hoppa över de första n byte från objektet InputStream
. Eftersom data läses strikt sekventiellt läser denna metod helt enkelt de första n byten från strömmen och kasserar dem.
Returnerar antalet byte som faktiskt hoppades över (om strömmen avslutades innan n
byte hoppades över).
int available()
metod
Metoden returnerar antalet byte som fortfarande finns kvar i flödet
void close()
metod
Metoden close()
stänger dataströmmen och frigör de externa resurser som är associerade med den. När en ström är stängd kan ingen mer data läsas från den.
Låt oss skriva ett exempelprogram som kopierar en mycket stor fil. Vi kan inte använda readAllBytes()
metoden för att läsa in hela filen i minnet. Exempel:
Koda | Notera |
---|---|
|
InputStream för läsning från filen OutputStream för skriv till fil Buffert som vi kommer att läsa in data i Så länge det finns data i strömmen Läs data in i bufferten Skriv data från bufferten till den andra strömmen |
I det här exemplet använde vi två klasser: FileInputStream
är en avkomling av InputStream
för att läsa data från en fil, och FileOutputStream
är en avkomling av OutputStream
för att skriva data till en fil. Vi kommer att prata om den andra klassen lite senare.
En annan intressant punkt här är real
variabeln. När det sista datablocket läses från en fil kan det lätt ha mindre än 64KB data. Följaktligen behöver vi inte mata ut hela bufferten, utan bara en del av den - de första real
byten. Det är precis vad som händer i write()
metoden.
3. Reader
klass
Klassen Reader
är en komplett analog till InputStream
klassen. Den enda skillnaden är att det fungerar med tecken ( char
), inte med byte. Precis som InputStream
klassen Reader
används klassen inte någonstans på egen hand: den är överordnad klass för hundratals avkomliga klasser och definierar gemensamma metoder för dem alla.
Metoder för Reader
klassen (och alla dess underliggande klasser):
Metoder | Beskrivning |
---|---|
|
Läser en char från streamen |
|
Läser en char array från strömmen |
|
Hoppa över n chars i flödet (läser och slänger dem) |
|
Kontrollerar om det fortfarande finns något kvar i strömmen |
|
Stänger strömmen |
Metoderna är väldigt lika de i InputStream
klassen, även om det finns små skillnader.
int read()
metod
Den här metoden läser en char
från strömmen och returnerar den. Typen char
vidgas till en int
, men de första två byten av resultatet är alltid noll.
int read(char[] buffer)
metod
Detta är den andra varianten av read()
metoden. Det låter dig läsa en char-array från en Reader
allt på en gång. Arrayen som kommer att lagra tecknen måste skickas som ett argument. Metoden returnerar ett nummer — antalet tecken som faktiskt lästs.
skip(long n)
metod
Denna metod låter dig hoppa över de första n tecknen från objektet Reader
. Det fungerar exakt på samma sätt som den analoga metoden för InputStream
klassen. Returnerar antalet tecken som faktiskt hoppades över.
boolean ready()
metod
Returnerar true
om det finns olästa bytes i strömmen.
void close()
metod
Metoden close()
stänger dataströmmen och frigör de externa resurser som är associerade med den. När en ström är stängd kan ingen mer data läsas från den.
För jämförelse, låt oss skriva ett program som kopierar en textfil:
Koda | Notera |
---|---|
|
Reader för att läsa från en fil Writer för att skriva till en fil Buffert som vi kommer att läsa in data i Så länge det finns data i strömmen Läs data i en buffert Skriv data från bufferten till den andra strömmen |
GO TO FULL VERSION