Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
кпп.docx
Скачиваний:
2
Добавлен:
01.07.2025
Размер:
238 Кб
Скачать
☆

Классы потокового ввода/вывода из пакета java.Io.

import  java.io.InputStream;

import  java.io.OutputStream; 

Представляют потоки байт для чтения или записи. Оба класса абстрактные, то есть, создать их экземпляры нельзя. Более того, на уровне этих классов не определены также и источники данных. Источник или пункт назначения потока будет зависеть от того, с чем мы работаем в данный момент – файл, архив, файловая система удаленного компьютера и т.д. и располагаться в соответствующем подклассе.

 Подключаются они следующим образом. Допустим, у нас есть какие-то файлы источника и назначения "input.txt" и "output.txt". Откуда мы забираем и куда сливаем поток байт.

 InputStream  inputStream  =  new  FileInputStream(new  File("input.txt"));

 OutputStream  outputStream(new  File("output.txt"));

 Далее можно заниматься пересылкой байт по созданному потоку. Например, так:

 int  totalBytesWritten  =  0;                          // общее количество переданных байт

byte []  buf  =  new byte [1024];       // объявление временного буфера для записи

int blockSize;                                     // размер пересылаемого/читаемого блока байт

while  (( blockSize  =  inputStream.read(buf))  >  0)  {   // читаем файл в буфер

            outputStream.write(buf, 0, blockSize);          // записываем байты из буфера

            totalBytesWritten  += blockSize;                  // считаем, сколько записали

}

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

Информация считывается поблочно с размером блоков меньше, но очевидно кратным размеру буфера. Ну и всегда желательно знать, сколько байт ушло в поток, чтобы сравнить потом, насколько полно и безошибочно передан файл.

 Теперь заглянем "под капот" этих потоковых классов и посмотрим в исходниках, как они устроены внутри. Для справки приведу сокращенный листинг. Полный листинг можно посмотреть в документации java по этим классам. Класс InputStream:

 package  java.io

 public  abstract  class  InputStream  implements  Closeable  {

             public  abstract  int  read()  trows  IOExeption;

             public  int  read(byte  b []) trows  IOExeption  {

                        return  read(b, 0, b.length);

            }

                    public  int  read (byte  b [], int  off,  int  len)

                        throws  IOExeption  {                      // …

          }

                     public  long  skip (long  n)  trows  IOExeption  {

                    // …

            }

                       public void  close()  throws  IOException  {}

            // …

}

Первый метод read()  возвращает следующий байт из входного потока и переходит к следующему байту. То есть, трижды вызвав метод read() мы получим три байта, идущих один за другим в потоке. Возвращаемое значение имеет тип int, а не байт для того, чтобы была возможность обозначить конец потока. Когда байтов больше не остается в виде маркера конца потока возвращается "-1". Для того, чтобы из типа int получить фактически считанный байт, можно сделать приведение типов. Ну или просто указать перед переменной модификатор типа (byte).

При возникновении каких либо ошибок выбрасываются исключения IOExeption.

 Читать поток побайтово не всегда эффективно, поэтому есть еще пара методов, которые используют для чтения потока буфер b [] – это read (byte b []) и  read (byte b [], int off, int len).  Разница между ними только в том, что первый считывает количество байт, заполняя буфер за один раз целиком, а второй считывает байты в буфер, заполняя его начиная с байта int off и до заполнения количеством int len. Обе функции возвращают количество реально считанных байт, которых может быть мало или нет совсем, если поток закончился. В этом случае функции вернут "-1".

Кроме этих функций есть еще функция skip(long  n), которая просто пропускает количество байт n,  никуда их не сохраняя (возвращает количество реально пропущеных байт) и функция close(), которая закрывает поток и освобождает все дескрипторы файлов.

 Листинг второго класса OutputStream:

 package  java.io

 public  abstract  class OutputStream  implements  Closeable,  Flushable  {

            public  abstract  void  write (int  b)  throws  IOExeption;

             public  void  write (byte  b [] )  throws  IOException  {

                        write(b, 0, b.length);

            }

             public  void  write(byte  b [], int  off,  int  len)  throws  IOException {

                        // …

            }

             public  void  flush ()  throws  IOException   {

                        // …            }

 

            public  void  close ()  throws  IOException  {

                        // …

            }

 

}

 Назначение отдельных методов у этого класса и тип возвращаемого значения совершенно аналогичны, с той лишь разницей, что идет не чтение, а запись в файл.

Отдельно пару слов стоит сказать про метод flush(). Дело в том, что кроме нашего программного буфера чтения – записи файлов, есть еще низкоуровневый системный буфер. Вызов команды flush() вызывает сброс системного буфера и если в нем к моменту вызова этой команды еще оставались какие – то данные, они также сбрасываются в файл.

При закрытии потока путем вызова функции close(), вызов функции flush() происходит автоматически и специально беспокоиться о нем в этом случае не нужно.

 Классы потокового ввода/вывода очень хорошо подходят для побайтовой или построчной обработки огромных текстовых файлов в несколько гигабайт, когда считывать их в память целиком за один раз не представляется возможным.

 Приведем еще один пример использования  классов потокового ввода/вывода для получения Internet – страниц через сокетное соединение. 

try  (Socket  socket  =  new  Socket("ya.ru", 80))  {    // открываем сокет

            OutputStream  outputStream  =  socket.getOutputStream();  // получаем поток

            outputStream.write("GET  /  HTTP /1/0\r\n\r\n".getBytes()); //запрашиваем страницу

            outputStream.flush();                                                       // сброс буферов

             InputStream  inputStream  =  socket.getInputStream();   // ждем ответа сервера

int  read  =  inputStream.read();                                // читаем html – код из буфера

while  (read  >= 0)  {

            System.out.print((char) read);            // выводим страницу в консоль

            read  =   inputStream.read();

}

}

 

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

 byte []  data  =  {"1","2","3"};

InputStream  inputStream   =  new ByteArrayInputStream(data);

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

 Или наоборот, загрузить данные на вход нашего кода, которому требуется потоковый ввод: 

ByteArrayOutputStream  outputStream  =  new  ByteArrayOutputStream();

// ..

byte []  result  =  outputStream.toByteArray(); 

 Эти способы упрощенной работы с потоками удобно использовать для отладки собственного кода. Например, отлаживаем программу с соединением к интернету через сокет. Кода, отвечающего за подключение к интернету еще нет. Создали массив байт, закинули на вход нашей функции, обработали, отправили ответ, получили в виде массива, распечатали, посмотрели где ошибка