1. Потоци от данни
Рядко една програма съществува като остров сама по себе си. Програмите обикновено по няHowъв начин взаимодействат с "външния свят". Това може да стане чрез четене на данни от клавиатурата, изпращане на съобщения, изтегляне на страници от интернет or, обратно, качване на файлове на отдалечен сървър.
Можем да посочим всички тези поведения с една дума: обмен на данни между програмата и външния свят. Чакай, това не е само една дума.
Разбира се, самият обмен на данни може да бъде разделен на две части: получаване на данни и изпращане на данни. Например, четете данни от клавиатурата с помощта на Scanner
обект — това е получаване на данни. И вие показвате данни на екрана с помощта на System.out.println()
команда - това е изпращане на данни.
В програмирането терминът "поток" се използва за описание на обмен на данни. Откъде идва този термин?
В реалния живот можете да имате поток от вода or поток от съзнание. В програмирането имаме потоци от данни .
Потоците са универсален инструмент. Те позволяват на програмата да получава данни отвсякъде (входни потоци) и да изпраща данни навсякъде (изходни потоци). По този начин има два вида:
- Входящият поток е за получаване на данни
- Изходен поток е за изпращане на данни
За да направят потоците „осезаеми“, създателите на Java написаха два класа: InputStream
и OutputStream
.
Класът InputStream
има read()
метод, който ви позволява да четете данни от него. И OutputStream
класът има write()
метод, който ви позволява да записвате данни в него. Те имат и други методи, но повече за това по-късно.
Байтови потоци
За Howви данни говорим? Какъв формат приема? С други думи, Howви типове данни поддържат тези класове?
Това са общи класове, така че поддържат най-често срещания тип данни - byte
. Може OutputStream
да записва byteове (и масиви от byteове), а InputStream
обект може да чете byteове (or масиви от byteове). Това е всичко — те не поддържат ниHowви други типове данни.
В резултат на това тези потоци се наричат още потоци от byteове .
Една от характеристиките на потоците е, че техните данни могат да се четат (or записват) само последователно. Не можете да прочетете данни от средата на поток, без да прочетете всички данни, които идват преди него.
Ето How работи четенето на данни от клавиатурата през Scanner
класа: четете данни от клавиатурата последователно, ред по ред. Четем ред, след това следващия ред, след това следващия ред и т.н. Уместно методът за четене на редове се нарича nextLine()
.
Записването на данни в OutputStream
също се извършва последователно. Добър пример за това е конзолният изход. Извеждате ред, последван от още един и още един. Това е последователен изход. Не можете да изведете първия ред, след това десетия и след това втория. Всички данни се записват в изходен поток само последователно.
Потоци от символи
Наскоро научихте, че низовете са вторият най-популярен тип данни и наистина е така. Много информация се предава под формата на знаци и цели низове. Компютърът е отличен в изпращането и получаването на всичко като byteове, но хората не са толкова перфектни.
Отчитайки този факт, Java програмистите написаха още два класа: Reader
и Writer
. Класът Reader
е аналогичен на InputStream
класа, но неговият read()
метод чете не byteове, а символи ( char
). Класът Writer
отговаря на OutputStream
класа. И точно като Reader
класа, той работи със знаци ( char
), а не с byteове.
Ако сравним тези четири класа, получаваме следната картина:
Байтове (byte) | Знаци (char) | |
---|---|---|
Четене на данни |
|
|
Записване на данни |
|
|
Практическо приложение
Самите класове InputStream
, и не се използват директно от никого, тъй като не са свързани с конкретни обекти, от които могат да се четат данни (or в които могат да се записват данни) OutputStream
. Но тези четири класа имат много потомствени класове, които могат да направят много.Reader
Writer
2. InputStream
клас
Класът InputStream
е интересен, защото е родителски клас за стотици класове потомци. Той няма ниHowви собствени данни, но има методи, които всички негови производни класове наследяват.
Като цяло, рядко се случва обекти на поток да съхраняват данни вътрешно. Потокът е инструмент за четене/запис на данни, но не и за съхранение. Въпреки това има изключения.
Методи на InputStream
класа и всички негови потомствени класове:
Методи | Описание |
---|---|
|
Чете един byte от потока |
|
Чете масив от byteове от потока |
|
Чете всички byteове от потока |
|
Пропуска n byteове в потока (чете и ги изхвърля) |
|
Проверява колко byteа остават в потока |
|
Затваря потока |
Нека прегледаме накратко тези методи:
read()
метод
Методът read()
чете един byte от потока и го връща. Може да сте объркани от int
вида на връщането. Този тип е избран, защото int
е стандартният целочислен тип. Първите три byteа на int
ще бъдат нула.
read(byte[] buffer)
метод
Това е вторият вариант на read()
метода. Позволява ви да четете byteов масив от InputStream
всичко наведнъж. Масивът, който ще съхранява byteовете, трябва да бъде предаден като аргумент. Методът връща число — броя на действително прочетените byteове.
Да приемем, че имате буфер от 10 килоbyteа и четете данни от файл, използвайки FileInputStream
класа. Ако файлът съдържа само 2 килоbyteа, всички данни ще бъдат заредени в буферния масив и методът ще върне числото 2048 (2 килоbyteа).
readAllBytes()
метод
Много добър метод. Той просто чете всички данни от InputStream
докато не се изчерпи и ги връща като едноbyteов масив. Това е много удобно за четене на малки файлове. Големите файлове може физически да не се побират в паметта и методът ще хвърли изключение.
skip(long n)
метод
Този метод ви позволява да пропуснете първите n byteа от InputStream
обекта. Тъй като данните се четат строго последователно, този метод просто чете първите n byteа от потока и ги изхвърля.
Връща броя byteове, които действително са бor пропуснати (в случай че потокът е приключил преди n
byteовете да бъдат пропуснати).
int available()
метод
Методът връща броя byteове, които все още остават в потока
void close()
метод
Методът close()
затваря потока от данни и освобождава външните ресурси, свързани с него. След като потокът бъде затворен, повече данни не могат да се четат от него.
Нека напишем примерна програма, която копира много голям файл. Не можем да използваме readAllBytes()
метода за четене на целия файл в паметта. Пример:
Код | Забележка |
---|---|
|
InputStream за четене от file OutputStream за запис във файл Буфер, в който ще четем данните Докато има данни в потока Прочетете данни в буфера Запишете данните от буфера във втория поток |
В този пример използвахме два класа: FileInputStream
е наследник на InputStream
за четене на данни от файл и FileOutputStream
е потомък на OutputStream
за запис на данни във файл. За втория клас ще говорим малко по-късно.
Друг интересен момент тук е променливата real
. Когато последният блок данни бъде прочетен от файл, той лесно може да има по-малко от 64 KB данни. Съответно трябва да изведем не целия буфер, а само част от него - първите real
byteове. Точно това се случва в write()
метода.
3. Reader
клас
Класът Reader
е пълен аналог на InputStream
класа. Единствената разлика е, че работи със знаци ( char
), а не с byteове. Точно като InputStream
класа, Reader
класът не се използва никъде самостоятелно: той е родителският клас за стотици класове наследници и дефинира общи методи за всички тях.
Методи на Reader
класа (и всички негови потомствени класове):
Методи | Описание |
---|---|
|
Чете един char от потока |
|
Чете char масив от потока |
|
Пропуска n chars в потока (чете и ги отхвърля) |
|
Проверява дали все още има нещо останало в потока |
|
Затваря потока |
Методите са много подобни на тези на InputStream
класа, въпреки че има малки разлики.
int read()
метод
Този метод чете един char
от потока и го връща. Типът char
се разширява до int
, но първите два byteа от резултата винаги са нула.
int read(char[] buffer)
метод
Това е вторият вариант на read()
метода. Позволява ви да четете масив от char Reader
наведнъж. Масивът, който ще съхранява знаците, трябва да бъде предаден като аргумент. Методът връща число — броят действително прочетени знаци.
skip(long n)
метод
Този метод ви позволява да пропуснете първите n знака от Reader
обекта. Работи точно по същия начин като аналогичния метод на InputStream
класа. Връща броя на действително пропуснатите знаци.
boolean ready()
метод
Връща, true
ако има непрочетени byteове в потока.
void close()
метод
Методът close()
затваря потока от данни и освобождава външните ресурси, свързани с него. След като потокът бъде затворен, повече данни не могат да се четат от него.
За сравнение, нека напишем програма, която копира текстов файл:
Код | Забележка |
---|---|
|
Reader за четене от файл Writer за запис във файл Буфер, в който ще четем данните Докато има данни в потока Четене на данни в буфер Записване на данните от буфера във втория поток |
GO TO FULL VERSION