Last updated
Last updated
Входно-изходни операции в Java
Входно-изходните операции в Java са базирани на концепцията на потоци (streams), като под „поток” се има предвид последователност от данни.
Дадено Java-приложение използва входен поток за прочитане на данни от източник и изходен поток за запис на тези данни в приемник. Източникът и приемникът могат да бъдат файл, конзола, друго приложение или някакво периферно устройство, както е показано на фигурата по-долу.
Потоците поддържат различни типове данни (байтове, данни от прост тип, символи, обекти), като някои потоци просто предават данните, докато други обработват тези данни и ги преобразуват в подходящ за конкретния случай вид.
Байтови потоци. ByteStream класове
Както подсказва името им, тези класове четат и записват данни в 8-битов формат, т.е. представляват байтови потоци. Намират се в пакета java.io.
Всеки един от класовете наследява или абстрактния клас InputStream, или абстрактния клас OutputStream.
Към класовете, които наследяват InputStream, спадат:
- BufferedInputStream – за прочитане на байтове от буфер;
- ByteArrayInputStream – за прочитане на байтове от масив;
- DataInputStream – за прочитане на прости типове данни;
- FileInputStream – за четене на байтове от файл;
- ObjectInputStream – за прочитане на обекти;
- др.
Част от методите на класа InputStream:
· public abstract int read() – чете поредния байт данни от входния поток; при достигане на край на входния поток връща -1;
· public int available() – връща колко байта от текущия входен поток могат да бъдат прочетени;
· public void close() – затваря текущия входен поток.
Аналогично, към класовете, наследяващи OutputStream, спадат:
- BufferedOutputStream – за запис на байтове в буфер;
- ByteArrayOutputStream – за запис на байтове в масив;
- DataOutputStream – за запис на прости типове данни;
- FileOutputStream – за запис на байтове във файл;
- ObjectOutputStream – за запис на обекти;
- др.
Част от методите на класа OutputStream:
· public void write(int i) – записва подавания байт в текущия изходен поток;
· public void write(byte[]) – записва масива байтове в текущия изходен поток;
· public void flush() – изчиства текущия изходен поток;
· public void close() – затваря текущия изходен поток.
Важно е да се запомни, че всеки един входно-изходен поток трябва да бъде затворен.
Байтовите потоци се използват за четене/запис на примитивни типове данни. Съществуват други типове потоци, за по-сложни данни – но всички те се базират на байтовите потоци.
Пример за използване на байтов поток:
Символни потоци. CharacterStream класове
Класовете за работа с байтови потоци могат да оперират само с един байт и не са подходящи за директна работа с Unicode символи, които са 16-битови. За работа с потоци от такива символи се използват съответно символни потоци.
По подобие на класовете за работа с байтови потоци, класовете за работа със символни потоци също са два основни типа: наследници на абстрактен клас Reader или на абстрактен клас Writer.
Класове, които наследяват клас Reader:
- BufferedReader;
- FileReader;
- InputStreamReader – този клас предоставя методи за преобразуване на байтове в символи;
- StringReader;
- др.
Класът Reader и всички негови наследници имат сходни методи с тези на клас InputStream като int read() и void close().
Класове, които наследяват клас Writer:
- BufferedWriter;
- FileWriter;
- OutputStreamWriter;
- StringWriter;
- др.
Класът Writer и всички негови наследници имат подобни на OutputStream методи: void write(), void write(int i) за запис на един символ, void flush() и void close().
Често символните потоци се явяват „обгръщащи” (wrappers) за байтовите потоци. Така например, FileReader използва FileInputStream, а FileWriter – FileOutputStream.
Символните потоци поддържат всички символи за край на ред – “\r”, “\n” и “\r\n”. Това позволява работа с текстови файлове.
Примери за използване на символни потоци:
Показаните два примера извършват едно и също: четат от един файл и записват прочетеното съдържание в друг. В първият пример са използвани само класове FileReader и FileWriter, докато вторият реализира същото с помощта на BufferedReader и PrintWriter.
Клас Scanner
Този клас дава възможност за работа с текст като предоставя методи за неговото преобразуване в различни примитивни типове данни. За разлика от входно-изходните потоци, класът се намира в пакета java.util.
Scanner разбива входа на токени чрез използване на разделител (по подразбиране това е интервала). Получените в резултат на това разделение токени могат да бъдат превърнати в стойности от различен тип, благодарение на различните next-методи.
Често използвани методи от клас Scanner:
· boolean hasNext() – връща истина ако има следващ токен;
· boolean hasNextInt() – връща истина ако следващия токен може да се интерпретира като цяло число;
· boolean hasNextDouble() – връща истина ако следващия токен може да се интерпретира като дробно число;
· boolean hasNextLong() – връща истина ако следващия токен може да се интерпретира като число от тип long;
· boolean hasNextLine() – връща истина ако входът има следващ/и ред/ове;
· String next() – намира и връща следващия токен;
· boolean nextBoolean() – намира и връща следващия токен, който може да се интерпретира като булева стойност;
· int nextInt() - намира и връща следващия токен, който може да се интерпретира като целочислена стойност;
· double nextDouble() - намира и връща следващия токен, който може да се интерпретира като дробно число;
· long nextLong() - намира и връща следващия токен, който може да се интерпретира като число от тип long;
· Scanner useDelimeter(String pattern) – задава какво да се използва за разделител;
· void close().
Примери за използване на клас Scanner:
Примерът по-долу прочита входен символен низ и след това извежда всяка една дума на отделен ред.
Следващият пример чете от файл и сумира дробните числа, записани в него (пропуска данните от всички други типове, които се съдържат в този файл).
Стандартни потоци. Клас Console
Java поддържа три стандартни потока:
- System.in – системен вход (от клавиатура);
- System.out – системен изход (на екран/конзола);
- System.err – стандартен поко за грешки.
Никой от тези потоци не трябва да бъде деклариран, те се създават автоматично.
System.out и System.err са дефинирани като PrintWriter-обекти, т.е. символни потоци. От своя страна, System.in е дефиниран като байтов поток.
Алтернатива на стандартните потоци е класът Console. Той предоставя функционалностите на стандартните потоци, както и допълнителни такива. Обектът Console предоставя входни и изходни символни потоци чрез методите си reader() и writer().
Пример за използване на стандартни потоци:
Даннови потоци. Интерфейси DataInput и DataOutput
Данновите потоци поддържат входно-изходни операции върху всички прости типове данни, както и върху символни низове. Всички даннови потоци имплементират или интерфейс DataInput, или интерфейс DataOutput.
Методи на интерфейс DataInput:
· boolean readBoolean();
· byte readByte();
· char readChar();
· double readDouble();
· int reading();
· String readLine();
· и др.
Всички тези методи имат своите съответстващи write() методи от интерфейса DataOutput: void writeBoolean(boolean value), void writeByte(byte value) и т.н.
Класове, които имплементират DataInput: DataInputStream, FileImageInputStream, ObjectInputStream, RandomAccessFile.
Класове, които имплементират DataOutput: DataOutputStream, FileImageOutputStream, ObjectOutputStream, RandomAccessFile.
Пример за използване на даннови потоци:
Примерът прочита файл чрез RandomAccessFile и запълва колекция от обекти. Файлът съдържа по един обект на ред.