Какой класс является родительским для потоков вывода данных
Перейти к содержимому

Какой класс является родительским для потоков вывода данных

  • автор:

Потоки ввода-вывода. Работа с файлами

Отличительной чертой многих языков программирования является работа с файлами и потоками. В Java основной функционал работы с потоками сосредоточен в классах из пакета java.io .

Ключевым понятием здесь является понятие потока . Хотя понятие «поток» в программировании довольно перегружено и может обозначать множество различных концепций. В данном случае применительно к работе с файлами и вводом-выводом мы будем говорить о потоке (stream), как об абстракции, которая используется для чтения или записи информации (файлов, сокетов, текста консоли и т.д.).

Поток связан с реальным физическим устройством с помощью системы ввода-вывода Java. У нас может быть определен поток, который связан с файлом и через который мы можем вести чтение или запись файла. Это также может быть поток, связанный с сетевым сокетом, с помощью которого можно получить или отправить данные в сети. Все эти задачи: чтение и запись различных файлов, обмен информацией по сети, ввод-ввывод в консоли мы будем решать в Java с помощью потоков.

Объект, из которого можно считать данные, называется потоком ввода , а объект, в который можно записывать данные, — потоком вывода . Например, если надо считать содержание файла, то применяется поток ввода, а если надо записать в файл — то поток вывода.

В основе всех классов, управляющих потоками байтов, находятся два абстрактных класса: InputStream (представляющий потоки ввода) и OutputStream (представляющий потоки вывода)

Но поскольку работать с байтами не очень удобно, то для работы с потоками символов были добавлены абстрактные классы Reader (для чтения потоков символов) и Writer (для записи потоков символов).

Все остальные классы, работающие с потоками, являются наследниками этих абстрактных классов. Основные классы потоков:

Классы потоков в Java

Потоки байтов

Класс InputStream

Класс InputStream является базовым для всех классов, управляющих байтовыми потоками ввода. Рассмотрим его основные методы:

  • int available() : возвращает количество байтов, доступных для чтения в потоке
  • void close() : закрывает поток
  • int read() : возвращает целочисленное представление следующего байта в потоке. Когда в потоке не останется доступных для чтения байтов, данный метод возвратит число -1
  • int read(byte[] buffer) : считывает байты из потока в массив buffer. После чтения возвращает число считанных байтов. Если ни одного байта не было считано, то возвращается число -1
  • int read(byte[] buffer, int offset, int length) : считывает некоторое количество байтов, равное length, из потока в массив buffer. При этом считанные байты помещаются в массиве, начиная со смещения offset, то есть с элемента buffer[offset] . Метод возвращает число успешно прочитанных байтов.
  • long skip(long number) : пропускает в потоке при чтении некоторое количество байт, которое равно number
Класс OutputStream

Класс OutputStream является базовым классом для всех классов, которые работают с бинарными потоками записи. Свою функциональность он реализует через следующие методы:

  • void close() : закрывает поток
  • void flush() : очищает буфер вывода, записывая все его содержимое
  • void write(int b) : записывает в выходной поток один байт, который представлен целочисленным параметром b
  • void write(byte[] buffer) : записывает в выходной поток массив байтов buffer.
  • void write(byte[] buffer, int offset, int length) : записывает в выходной поток некоторое число байтов, равное length , из массива buffer , начиная со смещения offset , то есть с элемента buffer[offset] .

Абстрактные классы Reader и Writer

Абстрактный класс Reader предоставляет функционал для чтения текстовой информации. Рассмотрим его основные методы:

  • absract void close() : закрывает поток ввода
  • int read() : возвращает целочисленное представление следующего символа в потоке. Если таких символов нет, и достигнут конец файла, то возвращается число -1
  • int read(char[] buffer) : считывает в массив buffer из потока символы, количество которых равно длине массива buffer. Возвращает количество успешно считанных символов. При достижении конца файла возвращает -1
  • int read(CharBuffer buffer) : считывает в объект CharBuffer из потока символы. Возвращает количество успешно считанных символов. При достижении конца файла возвращает -1
  • absract int read(char[] buffer, int offset, int count) : считывает в массив buffer, начиная со смещения offset, из потока символы, количество которых равно count
  • long skip(long count) : пропускает количество символов, равное count. Возвращает число успешно пропущенных символов

Класс Writer определяет функционал для всех символьных потоков вывода. Его основные методы:

  • Writer append(char c) : добавляет в конец выходного потока символ c. Возвращает объект Writer
  • Writer append(CharSequence chars) : добавляет в конец выходного потока набор символов chars. Возвращает объект Writer
  • abstract void close() : закрывает поток
  • abstract void flush() : очищает буферы потока
  • void write(int c) : записывает в поток один символ, который имеет целочисленное представление
  • void write(char[] buffer) : записывает в поток массив символов
  • absract void write(char[] buffer, int off, int len) : записывает в поток только несколько символов из массива buffer. Причем количество символов равно len, а отбор символов из массива начинается с индекса off
  • void write(String str) : записывает в поток строку
  • void write(String str, int off, int len) : записывает в поток из строки некоторое количество символов, которое равно len, причем отбор символов из строки начинается с индекса off

Функционал, описанный классами Reader и Writer, наследуется непосредственно классами символьных потоков, в частности классами FileReader и FileWriter соответственно, предназначенными для работы с текстовыми файлами.

Теперь рассмотрим конкретные классы потоков.

Урок №207. Потоки ввода и вывода

Функционал потоков ввода/вывода не определен как часть языка C++, а предоставляется Стандартной библиотекой C++ (и, следовательно, находится в пространстве имен std). На предыдущих уроках мы подключали заголовочный файл библиотеки iostream и использовали объекты cin и cout для простого ввода/вывода данных. На этом уроке мы детально рассмотрим библиотеку iostream.

Оглавление:

  1. Библиотека iostream
  2. Потоки в C++
  3. Ввод/вывод в C++
  4. Стандартные потоки в C++
  5. Пример на практике

Библиотека iostream

При подключении заголовочного файла iostream, мы получаем доступ ко всей иерархии классов библиотеки iostream, отвечающих за функционал ввода/вывода данных (включая класс, который называется iostream). Иерархия этих классов выглядит примерно следующим образом:

Первое, что вы можете заметить в этой иерархии — множественное наследование (то, что на самом деле не рекомендуется использовать). Тем не менее, библиотека iostream была разработана и тщательно протестирована соответствующим образом, дабы избежать типичных ошибок, которые возникают при работе с множественным наследованием, поэтому вы можете спокойно использовать эту библиотеку.

Потоки в С++

Второе, что вы могли бы заметить — это частое использование слова «stream» (т.е. «поток»). По сути, ввод/вывод в языке C++ реализован с помощью потоков. Абстрактно, поток — это последовательность символов, к которым можно получить доступ. Со временем поток может производить или потреблять потенциально неограниченные объемы данных.

Мы будем иметь дело с двумя типами потоков. Поток ввода (или «входной поток») используется для хранения данных, полученных от источника данных: клавиатуры, файла, сети и т.д. Например, пользователь может нажать клавишу на клавиатуре в то время, когда программа не ожидает ввода. Вместо игнорирования нажатия клавиши, данные помещаются во входной поток, где затем ожидают ответа от программы.

И наоборот, поток вывода (или «выходной поток») используется для хранения данных, предоставляемых конкретному потребителю данных: монитору, файлу, принтеру и т.д. При записи данных на устройство вывода, это устройство может быть не готовым принять данные немедленно. Например, принтер все еще может прогреваться, когда программа уже записывает данные в выходной поток. Таким образом, данные будут находиться в потоке вывода до тех пор, пока принтер не начнет их использовать.

Некоторые устройства, такие как файлы и сети, могут быть источниками как ввода, так и вывода данных.

Хорошая новость заключается в том, что программисту не нужно знать детали взаимодействия потоков с разными устройствами и источниками данных, ему нужно только научиться взаимодействовать с этими потоками для чтения и записи данных.

Ввод/вывод в C++

Хотя класс ios является дочерним классу ios_base , очень часто именно этот класс будет наиболее родительским классом, с которым вы будете работать/взаимодействовать напрямую. Класс ios определяет кучу разных вещей, которые являются общими для потоков ввода/вывода.

Класс istream используется для работы с входными потоками. Оператор извлечения >> используется для извлечения значений из потока. Это имеет смысл: когда пользователь нажимает на клавишу клавиатуры, код этой клавиши помещается во входной поток. Затем программа извлекает это значение из потока и использует его.

Класс iostream может обрабатывать как ввод, так и вывод данных, что позволяет ему осуществлять двунаправленный ввод/вывод.

Наконец, остались 3 класса, оканчивающиеся на _withassign . Эти потоковые классы являются дочерними классам istream , ostream и iostream (соответственно). В большинстве случаев вы не будете работать с ними напрямую.

Стандартные потоки в C++

Стандартный поток — это предварительно подключенный поток, который предоставляется программе её окружением. Язык C++ поставляется с 4-мя предварительно определенными стандартными объектами потоков, которые вы можете использовать (первые три вы уже встречали):

cin — класс istream_withassign , связанный со стандартным вводом (обычно это клавиатура);

cout — класс ostream_withassign , связанный со стандартным выводом (обычно это монитор);

cerr — класс ostream_withassign , связанный со стандартной ошибкой (обычно это монитор), обеспечивающий небуферизованный вывод;

clog — класс ostream_withassign , связанный со стандартной ошибкой (обычно это монитор), обеспечивающий буферизованный вывод.

Небуферизованный вывод обычно обрабатывается сразу же, тогда как буферизованный вывод обычно сохраняется и выводится как блок. Поскольку clog используется редко, то его обычно игнорируют.

Пример на практике

Вот пример использования ввода/вывода данных со стандартными потоками:

Потоки ввода/вывода

— Привет, Амиго! Сегодня мы будет знакомиться с потоками ввода-вывода . Пару дней назад мы немного цепляли данную тему, но сегодня пройдемся по ней основательно. Потоки ввода-вывода делятся на 4 категории:

1) Потоки делятся по направлению: потоки ввода и потоки вывода

2) Потоки делятся по типу данных: работают с байтами или работают с символами.

Поток ввода Поток вывода
Работает с байтами InputStream OutputStream
Работает с символами Reader Writer

Если объект реализует интерфейс InputStream, значит, он поддерживает последовательное чтение из него байт (byte).

Если объект реализует интерфейс OutputStream, значит, он поддерживает последовательную запись в него байт (byte).

Если объект реализует интерфейс Reader, значит, он поддерживает последовательное чтение из него символов (char).

Если объект реализует интерфейс Writer, значит, он поддерживает последовательную запись в него символов (char).

Потоки ввода/вывода - 1

Поток вывода напоминает принтер. На принтер мы можем выводить документы. В поток вывода мы можем выводить данные.

Тогда поток ввода можно сравнить со сканером, ну или с розеткой. С помощью сканера мы можем ввести документы к себе в компьютер. Также мы можем подключится к розетке и получать из нее электричество. Из потока ввода мы можем получать данные.

— А где они используются?

— Эти классы используются в Java повсеместно. Известный нам System.in – это статическая переменная по имени in типа InputStream в классе System.

— Надо же! Оказывается, все это время я пользовался потоком InputStream и не знал об этом. System.out – тоже поток?

— Да, System.out – это статическая переменная по имени out типа PrintStream (наследник OutputStream) в классе System.

— Т.е. я все время пользовался потоками и даже не подозревал об этом?

— Да, и это говорит лишь о том, насколько такие потоки удобны. Просто берешь и пользуешься.

— Хотя этого нельзя сказать про System.in. К нему постоянно приходилось добавлять BufferedReader и InputStreamReader.

— Да, это так. Но на это тоже были свои причины.

Видишь ли, типов данных очень много, как и способов работы с ними. Поэтому количество стандартных классов ввода-вывода очень быстро росло, хоть они и делали все почти то же самое. Чтобы избежать такой сложности, разработчики Java применили принцип абстракции и разделили классы на много маленьких частей.

Зато их можно соединить последовательно и получить очень сложную функциональность, если она тебе понадобилась. Смотри пример:

Вывод строки на консоль

System.out.println("Hello");

Сохранили поток вывода на консоль в отдельную переменную.
Выводим в поток строку.

PrintStream console = System.out; console.println("Hello");

Создали динамический (растягивающийся) массив байт в памяти.
Связали его с новым потоком вывода – объектов PrintStream
Выводим в поток строку.

ByteArrayOutputStream stream = new ByteArrayOutputStream(); PrintStream console = new PrintStream(stream); console.println("Hello");

— Действительно, чем-то похоже на конструктор Lego. Только непонятно, что весь этот код делает.

— Пусть это тебя не беспокоит сейчас. Всему свое время.

Хочу, чтобы ты запомнил вот что: если класс реализует интерфейс OutputStream – он позволяет записывать в него байты. Почти так же, как ты выводишь данные на консоль. Что он будет с этими данными делать – его задача. В «конструкторе» важно не назначение отдельного элемента, а насколько классные вещи мы можем собрать, благодаря многообразию существующих элементов.

— Хорошо. Тогда с чего мы начнем?

Программируйте во имя добра

Доброго времени суток, дорогой объектно-ориентированный друг!
В предыдущей части мы научились работать с файлами, как с неделимыми единицами файловой системы. Теперь зададимся вопросом — как же работать с содержимым файлов? Работа с содержимым файлов является частным случаем работы с потоками данных. Практически любая программа рано или поздно должна что-то откуда-то прочитать и куда-то потом сохранить. Поэтому владеть средствами чтения и записи информации — архинужно и архиважно! Вперед, товарищи!

Потоки

Источники и приемники информации так разнообразны, что вряд-ли хватит десяти таких статей, чтобы все их перечислить. Поэтому многолетняя эволюция программистской мысли привела к возникновению понятия поток — stream — универсального программного интерфейса для работы с данными. На текущий момент, практически все языки программирования общего назначения поддерживают эту концепцию. И Java не является исключением.

Источник информации называется входным потоком или потоком ввода — input stream.
При использовании входных потоков информация «входит» в программу из некоего источника, как показано на рисунке ниже.

Поток вывода — это приемник информации, куда мы «выводим» данные из программы:

Работа с потоками имеет следующие особенности:

  1. Работа с байтами идет строго последовательно, один за другим. Нельзя одновременно читать из нескольких позиций потока;
  2. Все операции с потоком — блокирующие. На время операций чтения и записи выполнение вашей программы в текущей нити приостанавливается;
  3. После работы с потоком его нужно обязательно закрыть. Нарушение этого правила может нанести урон как вашей программе, так и операционной системе в целом;

Все потоки делятся на 2 большие группы — байтовые, которые оперируют с байтами, и символьные, которые оперируют символами.

В этой статье рассмотрим байтовые потоки.
Байтовые потоки ввода

Для работы с входными байтовыми потоками в Java существует класс java.io.InputStream. Он является родительским классом для всех входных байтовых потоков.

Рассмотрим методы класса InputStream:

 /** * Читает один байт из входного потока. * * @return прочитанный байт в виде целого числа от 0 до 255 или -1, * если байтов больше нет * @exception IOException ошибка ввода-вывода. */ public int read() throws IOException; /** * Метод читает несколько байтов из потока и сохраняет их в переданный массив байтов b. * @param b массив байтов, в котором будут храниться прочитанные байты. * @return количество прочитанных байтов или -1, если байтов в потоке больше нет. * @exception IOException если невозможно прочитать следующий байт или если поток уже закрыт * или любая другая ошибка ввода-вывода. * @exception NullPointerException если b равен null. */ public int read(byte b[]) throws IOException; /** * Метод читает несколько байтов из потока и сохраняет их в указанную область массива b. * * @param b массив, в котором будут храниться прочитанные байты. * @param off стартовая позиция в массиве b, куда будут читаться данные. * @param len максимальное число байтов, которые будут прочитаны из потока. * @return реальное число прочитанных из потока байтов или -1, если не байтов больше нет. * @exception IOException если невозможно прочитать следующий байт или если поток уже закрыт * или любая другая ошибка ввода-вывода. * @exception NullPointerException если b равен null. * @exception IndexOutOfBoundsException если off отрицательное число * или если len - отрицательное число или если len > b.length - off */ public int read(byte b[], int off, int len) throws IOException; /** * Метод пропускает n байтов из входного потока. * * @param n число байтов, которое нужно пропустить. * @return число реально пропущенных байтов * @exception IOException если поток не поддерживает пропуск байтов * или если возникла любая другая ошибка ввода-вывода. */ public long skip(long n) throws IOException; /** * Метод возвращает число доступных байтов для чтения на текущий момент. * Это число не всегда совпадает с полным числом байтов в потоке. * * @return число доступных для чтения байтов. * @exception IOException если возникла ошибка ввода-вывода */ public int available() throws IOException; /** * Метод закрывает поток. * * @exception IOException если возникла ошибка ввода-вывода */ public void close() throws IOException; /** * Метод ставит метку на текущей позиции в потоке, чтобы иметь возможность потом к ней вернуться. * Не все потоки поддерживают механизм меток. * * @param readlimit максимальное количество символов, прочитанных после установки метки, * чтобы метка оставалась актуальной. * Если будет прочитано большее количество символов - метка становится неактуальной * и метод reset завершится с ошибкой. */ public void mark(int readlimit); /** * Перемещает чтение потока в позицию, ранее помеченную меткой. * * @exception IOException Если метка не была установлена или * если метка стала неактуальной или * если поток не поддерживает метод reset() или * в случае возникновения любой другой ошибки ввода-вывода */ public void reset() throws IOException; /** * Метод для определения, поддерживает ли данный поток механизм меток. * * @return true, если метки поддерживаются и false - если нет */ public boolean markSupported(); 

InputStream — абстрактный класс и мы не можем его использовать для непосредственно создания потока.
Поэтому на практике используется один из его подклассов. Чаще всего используются:

/** * Класс для чтения массива байтов в виде потока. */ java.io.ByteArrayInputStream /** * Класс для чтения байтов из файла. */ java.io.FileInputStream /** * Класс, который добавляет возможность буферизации данных при чтении из другого потока. */ java.io.BufferedInputStream

Байтовые потоки вывода

Для работы с выходными потоками существует класс java.io.OutputStream.
Этот класс содержит следующие методы:

/** * Метод записывает один байт в поток вывода. * * @param b значение байта. * @exception IOException если поток закрыт или в случае любая другая ошибка ввода-вывода. */ public void write(int b) throws IOException; /** * Метод записывает массив байтов в поток вывода. * * @param b массива байтов. * @exception IOException в случае любой ошибки ввода-вывода. */ public void write(byte b[]) throws IOException; /** * Метод записывает в поток вывода часть массива байтов. * * @param b массив байтов, часть которого будет записана в поток. * @param off начальная позиция в массиве, с которой начинается блок информации. * @param len число байтов, которые будут записаны в поток. * * @exception IOException если поток закрыт или в случае любой другой ошибки ввода-вывода. * @exception NullPointerException если b равен null * @exception IndexOutOfBoundsException если off - отрицательное число * или len - отрицательное число или off+len > b.length */ public void write(byte b[], int off, int len) throws IOException; /** * Вся информация в буфере потока насильно записывается в поток вывода. * * @exception IOException в случае любой ошибки ввода-вывода */ public void flush() throws IOException; /** * Метод закрывает поток. * * @exception IOException в случае любой ошибки ввода-вывода. */ public void close() throws IOException; 

Наиболее часто используемые подклассы OutputStream:

/** * Класс для записи данных в массив байтов в памяти. */ java.io.ByteArrayOutputStream /** * Класс для записи байтов в файл. */ java.io.FileOutputStream /** * Класс, который добавляет буферизацию при записи данных в поток. */ java.io.BufferedOutputStream

Особенности архитектуры библиотеки ввода-вывода

В системе ввода-вывода Java активно используется такой шаблон проектирования, как декоратор. То есть, для добавления дополнительной функциональности к уже существующему потоку, этот поток «вкладывается» в другой поток, который реализует требуемую функциональность.
Например, открытие потока для чтения файла выглядит так:

InputStream input = new FileInputStream(new File("/tmp/1.txt"));

Если вы хотите добавить буферизацию при чтении данных из файла, то поток выше оборачивается в BufferedInputStream:

InputStream input = new BufferedInputStream(new FileInputStream(new File("/tmp/1.txt")));

Пример 1 — чтение файла

Допустим, у нас есть текстовый файл на диске — C:/1.txt, в котором хранится текст — «Hello, world». Прочитаем этот файл и распечатаем текст в консоль.

Для того, чтобы прочитать данные из файла, нам понадобится входной поток FileInputStream. Всю прочитанную информацию мы должны сохранить в памяти, чтобы потом сконструировать из нее строку. Полученную строку мы распечатаем в консоль и увидим содержимое файла.
Для хранения байтов в памяти нужно использовать ByteArrayOutputStream.

При этом помним, что потоки нужно закрывать после использования. Итого, получаем:

import java.io.*; public class CopyFile < public static void main(String[] args) throws IOException < //Создаем объект File File file = new File("C:/1.txt"); //Входной поток - для чтения информации из файла InputStream input = null; //Выходной поток - для хранения прочитанного в памяти ByteArrayOutputStream output = null; try < //Инициализируем входной поток input = new FileInputStream(file); //Инициализируем выходной поток output = new ByteArrayOutputStream(); //b - прочитанный байт int b; //Читаем из входного потока по одному байту до тех пор, пока не встретим -1 while ((b = input.read()) != -1) < //Каждый прочитанный байт записываем в поток вывода output.write(b); >> finally < //Закрываем входной поток if (input != null) < input.close(); >//Закрываем выходной поток if(output != null) < output.close(); >> byte[] bytes = output.toByteArray(); System.out.println(new String(bytes, "UTF-8")); > > 

Код кажется многословным, но, к счастью, его можно упростить.
Во-первых, все потоки реализуют интерфейс AutoCloseable, что означает, что мы можем воспользоваться конструкцией try-with-resources.
Во-вторых — нет необходимости закрывать поток ByteArrayOutputStream, так как все, что он делает — хранит данные в памяти. Если вы посмотрите исходный код класса ByteArrayOutputStream, то увидите, что метод close() ничего не делает.
В итоге, код будет выглядеть так:

import java.io.*; public class CopyFile < public static void main(String[] args) throws IOException < //Создаем объект File File file = new File("C:/1.txt"); ByteArrayOutputStream output = new ByteArrayOutputStream(); try (InputStream input = new FileInputStream(file)) < //b - прочитанный байт int b; //Читаем из входного потока по одному байту до тех пор, пока не встретим -1 while ((b = input.read()) != -1) < //Каждый прочитанный байт записываем в поток вывода output.write(b); >> //Массив байтов в памяти, которые мы прочитали из файла byte[] bytes = output.toByteArray(); //Создаем строку из массива байтов String string = new String(bytes, "UTF-8"); //Распечатываем строку в консоль System.out.println(string); > > 

Нужно отметить, что вообще говоря, копирование по одному байту — не самая оптимальная реализация. Создадим буфер в памяти и будем копировать по несколько байтов одновременно. В этом случае, код будет выглядит так:

import java.io.*; public class CopyFile < public static void main(String[] args) throws IOException < //Создаем объект File File file = new File("C:/1.txt"); ByteArrayOutputStream output = new ByteArrayOutputStream(); try (InputStream input = new FileInputStream(file)) < //len - количество прочитанных в буфер байтов int len; //Читаем из входного потока до тех пор, пока не встретим -1 byte[] bytes = new byte[1024]; while ((len = input.read(bytes)) != -1) < //Записываем в поток вывода len байтов, начиная с начала буфера bytes output.write(bytes, 0, len); >> //Массив байтов в памяти, которые мы прочитали из файла byte[] bytes = output.toByteArray(); //Создаем строку из массива байтов String string = new String(bytes, "UTF-8"); //Распечатываем строку в консоль System.out.println(string); > >

Пример 2 — HTTP запросы

Для каждого приемника или источника информации или есть отдельный класс потока или существует метод, который возвращает объект потока.
Допустим, мы хотим прочитать исходный код страницы, открываемой по ссылке https://ya.ru.
Для этого нужно послать HTTP GET запрос серверу ya.ru, после этого открыть поток и прочитать содержимое, которое отправляет нам сервер.

Воспользуемся для этого классом java.net.URL, с помощью которого можно посылать HTTP-запросы.
Итоговый код программы выглядит так:

import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.URL; public class HttpGet < public static void main(String[] args) throws IOException < //Конструируем ссылку на сайт URL url = new URL("https://ya.ru/"); //Открываем соединение до сервера HttpURLConnection connection = (HttpURLConnection) url.openConnection(); //Указываем, что мы собираемся читать данные с сервера connection.setDoInput(true); //Указываем, что мы собираемся послать GET-запрос connection.setRequestMethod("GET"); //Отправляем запрос на сервер и читаем код состояния. int responseCode = connection.getResponseCode(); //Если сервер вернул код 200 - OK - значит запрос корректный и сервер, обработав его, вернул данные if (responseCode == HttpURLConnection.HTTP_OK) < //Буфер в памяти, куда будем сохранять данные с сервера ByteArrayOutputStream output = new ByteArrayOutputStream(); //открываем поток ввода и читаем его, копируя данные в output try (InputStream input = connection.getInputStream()) < //Переносим данные из input в output порциями по 100 байт byte[] bytes = new byte[100]; int read; while ((read = input.read(bytes)) != -1) < output.write(bytes, 0, read); >> //Все байты, прочитанные с сервера конвертируем в строку и распечатываем в консоль String response = output.toString("UTF-8"); System.out.println(response); > > > 

Результатом работы этой программы будет исходный код поисковой страницы Yandex, распечатанный в консоль.

Пример 3 — создание архива

Подклассы потоков могут иметь специализированные методы, которые нужны для создания необходимой структуры данных в приемнике.

Например, при создании zip-архива нужно использовать класс java.util.zip.ZipOutputStream и его методы:

 /** * Метод сообщает потоку о начале записи в архив нового элемента содержимого. * * @param entry элемент для записи в архив * @exception ZipException если структура zip-файла нарушена * @exception IOException в случае любой ошибки ввода-вывода */ public void putNextEntry(ZipEntry entry) throws IOException; /** * Сообщает потоку о конце записи элемента содержимого. * * @exception ZipException если структура zip-файла нарушена * @exception IOException в случае любой ошибки ввода-вывода */ public void closeEntry() throws IOException; 

Исходный код программы создания архива выглядит так:

import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.util.zip.ZipEntry; import java.util.zip.ZipOutputStream; public class ZipArchive < public static void main(String[] args) throws IOException < try(ZipOutputStream output = new ZipOutputStream(new FileOutputStream(new File("D:/1.zip")))) < ZipEntry entry = new ZipEntry("TEST.txt"); output.putNextEntry(entry); output.write("ABC".getBytes("UTF-8")); output.closeEntry(); >> > 

После выполнения этой программы на диске D: будет создан файл 1.zip, содержимое которого выглядит так:

Заключение
Итак, друзья, мы рассмотрели основные принципы работы с байтовыми потоками в Java.
Если у вас возникла задача чтения или хранения данных — ищите нужный поток.
А если такого потока нет — не бойтесь создавать свой=)

Мы научились работать с байтами, но в реальности, намного чаще приходится работать с текстом. К счастью, Java позволяет легко это сделать, поэтому в следующий раз рассмотрим символьные потоки и их широкое применение.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *