Разница в io при работе с файлом и директорией

Обновлено: 05.07.2024

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

Потратив изрядное количество часов на изучение и реализацию кучи разнообразных туториалов из Интернета, начал чувствовать себя уверенно и вздохнул с облегчением. Но в один прекрасный момент понял, что для меня все только начинается, так как существует еще и пакет java.nio.*, известный ещё под названием Java NIO или Java New IO. Вначале казалось, что это тоже самое, ну типа вид сбоку. Однако, как оказалось, есть существенные отличия, как в принципе работы, так и в решаемых с их помощью задачах.

Разобраться во всем этом мне здорово помогла статья Джакоба Дженкова (Jakob Jenkov) – “Java NIO vs. IO”. Ниже она приводиться в адаптированном виде.

Поспешу заметить, что статья не является руководством по использованию Java IO и Java NIO. Её цель – дать людям, начинающим изучать Java, возможность понять концептуальные отличия между двумя указанными инструментами организации ввода/вывода.

Основные отличия между Java IO и Java NIO


IO NIO
Потокоориентированный Буфер-ориентированный
Блокирующий (синхронный) ввод/вывод Неблокирующий (асинхронный) ввод/вывод
Селекторы

Потокоориентированный и буфер-ориентированный ввод/вывод

Основное отличие между двумя подходами к организации ввода/вывода в том, что Java IO является потокоориентированным, а Java NIO – буфер-ориентированным. Разберем подробней.

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

Подход, на котором основан Java NIO немного отличается. Данные считываются в буфер для последующей обработки. Вы можете двигаться по буферу вперед и назад. Это дает немного больше гибкости при обработке данных. В то же время, вам необходимо проверять содержит ли буфер необходимый для корректной обработки объем данных. Также необходимо следить, чтобы при чтении данных в буфер вы не уничтожили ещё не обработанные данные, находящиеся в буфере.

Блокирующий и неблокирующий ввод/вывод

Потоки ввода/вывода (streams) в Java IO являются блокирующими. Это значит, что когда в потоке выполнения (tread) вызывается read() или write() метод любого класса из пакета java.io.*, происходит блокировка до тех пор, пока данные не будут считаны или записаны. Поток выполнения в данный момент не может делать ничего другого.

Неблокирующий режим Java NIO позволяет запрашивать считанные данные из канала (channel) и получать только то, что доступно на данный момент, или вообще ничего, если доступных данных пока нет. Вместо того, чтобы оставаться заблокированным пока данные не станут доступными для считывания, поток выполнения может заняться чем-то другим.

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

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

Таким образом неблокирующий режим Java NIO позволяет использовать один поток выполнения для решения нескольких задач вместо пустого прожигания времени на ожидание в заблокированном состояний. Наиболее частой практикой является использование сэкономленного времени работы потока выполнения на обслуживание операций ввода/вывода в другом или других каналах.

Селекторы


Селекторы в Java NIO позволяют одному потоку выполнения мониторить несколько каналов ввода. Вы можете зарегистрировать несколько каналов с селектором, а потом использовать один поток выполнения для обслуживания каналов, имеющих доступные для обработки данные, или для выбора каналов, готовых для записи.

Чтобы лучше понять концепцию и выгоду от применения селекторов, давайте абстрагируемся от программирования и представим себе железнодорожный вокзал. Вариант без селектора: есть три железнодорожных пути (каналы), на каждый из них в любой момент времени может прибыть поезд (данные из буфера), на каждом пути постоянно ожидает сотрудник вокзала (поток выполнения), задача которого – обслуживание прибывшего поезда. В результате трое сотрудников постоянно находятся на вокзале даже если там вообще нет поездов. Вариант с селектором: ситуация та же, но для каждой платформы есть индикатор, сигнализирующий сотруднику вокзала (поток выполнения) о прибытии поезда. Таким образом на вокзале достаточно присутствия одного сотрудника.

Влияние Java NIO и Java IO на дизайн приложения

Выбор между Java NIO и Java IO может на следующие аспекты дизайна вашего приложения:
1. API обращений к классам ввода/вывода;
2. Обработка данных;
3. Количество потоков выполнения, использованных для обработки данных.

API обращений к классам ввода/вывода

Естественно, использование Java NIO серьезно отличается от использования Java IO. Так как, вместо чтения данных байт за байтом с использованием, например InputStream, данные для начала должны быть считаны в буфер и браться для обработки уже оттуда.

Обработка данных

Обработка данных при использовании Java NIO тоже отличается.
Как уже упоминалось, при использовании Java IO вы читаете данные байт за байтом с InputStream или Reader. Представьте, что вы проводите считывание строк текстовой информации:

Этот поток строк текста может обрабатываться следующим образом:


Обратите внимание, как состояние процесса обработки зависит от того, насколько далеко продвинулось выполнение программы. Когда первый метод readLine() возвращает результат выполнения, вы уверенны – целая строка текста была считана. Метод является блокирующим и действие блокировки продолжается до тех пор, пока вся строка не будет считана. Вы также четко понимаете, что данная строка содержит имя. Подобно этому, когда метод вызывается во второй раз, вы знаете, что в результате получите возраст.

Как вы видите, прогресс в выполнении программы достигается только тогда, когда доступны новые данные для чтения, и для каждого шага вы знаете что это за данные. Когда поток выполнения достигает прогресса в считывании определенной части данных, поток ввода (в большинстве случаев) уже не двигает данные назад. Данный принцип хорошо демонстрирует следующая схема:


Имплементация с использованием Java IO будет выглядеть несколько иначе:


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

Представьте, что после первого вызова метода read(buffer), в буфер было считано только половину строки. Например, “Name: An”. Сможете ли вы обработать такие данные? Наверное, что нет. Вам придется ждать пока, по крайней мере, одна полная строка текста не будет считана в буфер.

Так как же вам узнать, достаточно ли данных для корректной обработки содержит буфер? А никак. Единственный вариант узнать, это посмотреть на данные, содержащиеся внутри буфера. В результате вам придется по нескольку раз проверять данные в буфере, пока они не станут доступными для корректной обработки. Это неэффективно и может негативно сказаться на дизайне программы. Например:

Метод bufferFull() должен следить за тем, сколько данных считано в буфер и возвращать true или false, в зависимости от того, заполнен буфер или нет. Другими словами, если буфер готов к обработке, то он считается заполненным.

Также метод bufferFull() должен оставлять буфер в неизмененном состоянии, поскольку в противном случае следующая порция считанных данных может быть записана в неправильное место.

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

Следующая схема демонстрирует процесс определения готовности данных в буфере для корректной обработки:


Итоги

Java NIO позволяет управлять несколькими каналами (сетевыми соединениями или файлами) используя минимальное число потоков выполнения. Однако ценой такого подхода является более сложный, чем при использовании блокирующих потоков, парсинг данных.

Если вам необходимо управлять тысячами открытых соединений одновременно, причем каждое из них передает лишь незначительный объем данных, выбор Java NIO для вашего приложения может дать преимущество. Дизайн такого типа схематически изображен на следующем рисунке:


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


Важно понимать, что Java NIO отнюдь не является заменой Java IO. Его стоит рассматривать как усовершенствование – инструмент, позволяющий значительно расширить возможности по организации ввода/вывода. Грамотное использование мощи обоих подходов позволит вам строить хорошие высокопроизводительные системы.

Стоит заметить, что с выходом версии Java 1.7 появился еще и Java NIO.2, но присущие ему новшества касаются, в первую очередь, работы с файловым вводом/выводом, поэтому выходят за рамки этой статьи.

Пакет java.io содержит почти каждый класс, который может потребоваться Вам для совершения ввода и вывода в Java. Все данные потоки представлены потоком ввода и адресом вывода. Поток в пакете java.io осуществляет поддержку различных данных, таких как примитивы, объекты, локализованные символы и т.д.

Содержание

Потоки

Потоки в Java определяются в качестве последовательности данных. Существует два типа потоков:

  • InPutStream – поток ввода используется для считывания данных с источника.
  • OutPutStream – поток вывода используется для записи данных по месту назначения.

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

Байтовый поток

Примечание по примеру: чтобы скопировать файл, необходимо в папке проекта создать файл file.txt с любым или пустым содержимым.

Пример

Теперь рассмотрим файл file.txt со следующим содержимым:

В качестве следующего шага необходимо скомпилировать java-программу и выполнить ее, что позволит создать файл copied_file.txt с тем же содержимым, что имеется в file.txt. Таким образом, разместим обозначенный код в файле FileCopy.java и выполним следующее действие:

Символьные потоки

Мы можем переформулировать представленный выше пример, в котором два данных класса используются для копирования файла ввода (с символами юникода) в файл вывода.

Примечание по примеру: чтобы скопировать файл, необходимо в папке проекта создать файл file.txt с любым или пустым содержимым.

Пример

Теперь рассмотрим файл file.txt со следующим содержимым:

В качестве следующего шага необходимо скомпилировать программу и выполнить ее, что позволит создать файл copied_file.txt с тем же содержимым, что имеется в file.txt. Таким образом, разместим обозначенный код в файле FileCopy.java и выполним следующее действие:

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

Все языки программирования обеспечивают поддержку стандартного ввода/вывода, где программа пользователя может произвести ввод посредством клавиатуры и осуществить вывод на экран компьютера. Если вы знакомы с языками программирования C либо C++, вам должны быть известны три стандартных устройства STDIN, STDOUT и STDERR. Аналогичным образом, Java предоставляет следующие три стандартных потока:

Ниже представлена простая программа, которая создает InputStreamReader для чтения стандартного потока ввода, до введения пользователем "q":

Пример

Разместим представленный выше код в файле ReadConsole.java и попробуем скомпилировать и выполнить его согласно тому, как это представлено в следующей программе. Данная программа продолжает чтение и вывод одного и того же символа до нажатия 'q':

Чтение и запись файла

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

Ниже представлена иерархия классов для управления потоками Ввода и Вывода.

Java иерархия классов, чтение файла в Java, запись файла в Java, Java, потоки ввода/вывода, потоки ввода в Java, потоки вывода в Java

В данном уроке нам предстоит рассмотреть два важных потока: FileInputStream и FileOutputStream.

Поток FileInputStream – чтение из файла

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

Представленный конструктор использует имя файла в качестве потока с целью создания объекта входного потока для считывания файла:

Представленный ниже конструктор использует объектный файл с целью создания объекта входного потока для чтения файла. Сперва мы создаем объектный файл при использовании метода File() следующим образом:

Теперь, получив объект InputStream, следует ознакомиться со следующим перечнем вспомогательных методов, которые могут быть использованы для считывания потока либо выполнения иных операций в потоке.

Метод и описание
1 public void close() throws IOException<>
Данный метод в Java закрывает выходной файловый поток. Освобождает какие-либо системные ресурсы, связанные с файлом. Выдает IOException.
2 protected void finalize()throws IOException <>
Данный метод выполняет очистку соединения с файлом. Позволяет удостовериться в вызове закрытого метода данного выходного файлового потока при отсутствии каких-либо ссылок на данный поток. Выдает IOException.
3 public int read(int r)throws IOException<>
Данный метод осуществляет в Java считывание заданных байтов данных из InputStream. Возврат данных типа int. Возврат следующего байта данных, в конце файла будет произведен возврат к -1.
4 public int read(byte[] r) throws IOException<>
Данный метод производит считывание байтов r.length из входного потока в массив. Возврат общего числа считанных байтов. В конце файла будет произведен возврат к -1.
5 public int available() throws IOException<>
Выдает число байтов, которые могут быть считаны из входного файлового потока. Возврат данных типа int.

Существуют также другие доступные входные потоки, более детальные сведения о которых представлены по следующим ссылкам:

Поток FileOutputStream – создание и запись файла

Поток FileOutputStream – это поток, который используется в Java для создания файла и последующей записи в него. Поток создаст файл в случае его отсутствия перед его открытием для вывода.

Далее представлены два конструктора, которые могут быть задействованы при создании объекта FileOutputStream.

Представленный конструктор использует имя файла в качестве строки с целью создания объекта входного потока для записи файла в Java:

Представленный ниже конструктор использует объектный файл с целью создания объекта выходного потока для записи файла. Сперва мы создаем объектный файл при использовании метода File() следующим образом:

Теперь, получив объект OutputStream, следует ознакомиться со следующим перечнем вспомогательных методов, которые могут быть использованы для записи потока либо выполнения иных операций в потоке.

Метод и описание
1 public void close() throws IOException<>
Данный метод в Java закрывает выходной файловый поток. Освобождает какие-либо системные ресурсы, связанные с файлом. Выдает IOException.
2 protected void finalize()throws IOException <>
Данный метод выполняет очистку соединения с файлом. Позволяет удостовериться в вызове закрытого метода данного выходного файлового потока при отсутствии каких-либо ссылок на данный поток. Выдает IOException.
3 public void write(int w)throws IOException<>
Данный метод осуществляет запись заданного байта в выходной поток.
4 public void write(byte[] w)
Запись байтов w.length из указанного массива байтов в OutputStream.

Существуют также другие доступные выходные потоки, более детальные сведения о которых представлены по следующим ссылкам:

Пример

В следующем примере представлены InputStream и OutputStream – потоки для чтения, создания и записи файла:

Представленный выше java-код создаст файл file.txt и пропишет заданные символы в формате char. То же самое будет выводиться на экран стандартного вывода.

Навигация по файловой системе и вводу/выводу

Существует ряд других классов, которые нам предстоит рассмотреть с целью ознакомления с основами навигации в файловой системе и вводу/выводу.

Каталоги в Java

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

Создание каталогов

Существуют два служебных метода File, которые могут быть использованы для создания каталогов:

  • Метод mkdir() позволяет создать папку в Java, возвращая значение true при успехе операции, и false в случае сбоя. Сбой свидетельствует о том, что путь указанный в объекте File уже существует, либо что каталог не может быть создан в связи с тем, что полный путь еще не существует.
  • Метод mkdirs() создает каталог и все вышестоящие каталоги.

В следующем примере представлено создание папки "/java/proglang/newdir":

Пример

Скомпилируйте и выполните следующий код для создания каталога "/java/proglang/newdir".

Примечание ? Java автоматически формирует разделители пути в UNIX и Windows с учетом соглашений. При использовании косой черты (/) при работе с Java в системе Windows, производится корректное разрешение пути.

Список файлов в папке

Метод list(), представленный объектом File, может быть использован для предоставления перечня всех файлов и каталогов, имеющихся в заданной папке, в следующем виде:

Пример

Вследствие этого будет получен следующий результат, основанный на каталогах и файлах, доступных в вашем каталоге /NetBeans 8.2/Projects/ReadDirectory/ReadDirectory/:

Класс File пакета java.io используется для управления информацией о файлах и каталогах. На уровне операционной системы файлы и каталоги имеют существенные отличия, но в Java они описываются одним классом File. Каталог в Java трактуется как обычный файл, но с дополнительным свойством — списком имен файлов, который можно просмотреть с помощью метода list.

В зависимости от назначения объект File - файл или каталог, можно использовать один из конструкторов для создания объекта:

Примеры создания объектов File

Свойства и методы класса File

Для определения стандартных свойств файла в классе File имеются различные методы. Однако класс File несимметричен, т.е. методы определения свойств объекта существуют, но соответствующие функции для изменения этих свойств отсутствуют.

ФункцииОписание
String getName() Наименование файла или каталога.
String getParent() Наименование родительского каталога.
long length() Функция определения размера файла в байтах.
String getAbsolutePath() Функция определения абсолютного пути файла или каталога.
boolean delete() Удаления файла или каталога.
boolean exists() Проверка существования файла или каталога.
boolean isDirectory() Проверка, является ли данный объект каталогом.
boolean isFile() Проверка, является ли данный объект файлом.
long lastModified() Функция определения даты последней модификации файла.
boolean canRead() Проверка, можно ли читать данные из файла.
boolean canWrite() Проверка, можно ли записывать данные в файл.
boolean isHidden() Проверка, являются ли каталог или файл скрытыми.
String[] list() Чтение массива наименований файлов и подкаталогов.
File[] listFiles() Чтение массива файлов и подкаталогов.
boolean mkdir() Создание нового каталога.
boolean renameTo(File dest)Переименовывание файла или каталога.

В следующем примере открываем файл "d:/test/MyFile.txt" (Windows) и извлекаем его характеристики:

В консоли будет отпечатана следующая информация:

Интерфейс FileFilter

Класс File включает метод, позволяющий прочитать список только определенных файлов.

В отличие от одноименного метода, но без параметра, данный метод отбирает только те файлы каталога, которые удовлетворяют определенному условию. Параметр filter предназначен для задания этого условия. При этом тип параметра FileFilter — это не класс, а интерфейс, который имеет всего один метод, возвращающий true, если файл удовлетворяет определенным условиям, и false в противном случае.

Метод listFiles будет вызывать метод accept для каждого файла в каталоге, и те, для которых accept вернет true, будут включены в результирующий список. Остальные будут проигнорированы.

Для использования FileFilter необходимо создать объект и определить в нем соответствующий метод accept.

Пример использования фильтра FileFilter

Чтение содержимого файла FileInputStream

Для чтения содержимого файла можно использовать класс FileInputStream, который является наследником класса InputStream и реализует все его методы. Конструктор класса FileInputStream :

Если файл не может быть открыт то генерируется исключение FileNotFoundException.

Пример считывания данных из файла и вывод содержимого в консоль:

Данные файла можно считать в массив байтов :

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

Пример использования FileInputStream для чтения файла свойств в кодировке UTF-8:

Файл свойств "data.properties" в кодировке UTF-8:

Запись в файл FileOutputStream

Класс FileOutputStream, является производным от класса OutputStream, поэтому наследует всю его функциональность.

Пример записи строки в файл:

Для создания объекта FileOutputStream используется конструктор, принимающий в качестве параметра путь к файлу для записи. Для записи строки ее сначала переводим в массив байт и с помощью метода write строка записывается в файл. Необязательно записывать весь массив байтов. Используя перегрузку метода write(), можно записать и одиночный байт:

Пример перезаписи содержимого из одного файла в другой:

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

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

В чём заключается разница между IO и NIO?

  • Java IO (input-output) является потокоориентированным, а Java NIO (new/non-blocking io) – буфер-ориентированным. Потокоориентированный ввод/вывод подразумевает чтение/запись из потока/в поток одного или нескольких байт в единицу времени поочередно. Данная информация нигде не кэшируются. Таким образом, невозможно произвольно двигаться по потоку данных вперед или назад. В Java NIO данные сначала считываются в буфер, что дает больше гибкости при обработке данных.
  • Потоки ввода/вывода в Java IO являются блокирующими. Это значит, что когда в потоке выполнения вызывается read() или write() метод любого класса из пакета java.io.* , происходит блокировка до тех пор, пока данные не будут считаны или записаны. Поток выполнения в данный момент не может делать ничего другого. Неблокирующий режим Java NIO позволяет запрашивать считанные данные из канала (channel) и получать только то, что доступно на данный момент, или вообще ничего, если доступных данных пока нет. Вместо того, чтобы оставаться заблокированным пока данные не станут доступными для считывания, поток выполнения может заняться чем-то другим. Тоже самое справедливо и для неблокирующего вывода. Поток выполнения может запросить запись в канал некоторых данных, но не дожидаться при этом пока они не будут полностью записаны.
  • В Java NIO имеются селекторы, которые позволяют одному потоку выполнения мониторить несколько каналов ввода. Т.е. существует возможность зарегистрировать несколько каналов с селектором, а потом использовать один поток выполнения для обслуживания каналов, имеющих доступные для обработки данные, или для выбора каналов, готовых для записи.

Какие особенности NIO вы знаете?

  • Каналы и селекторы: NIO поддерживает различные типы каналов. Канал является абстракцией объектов более низкого уровня файловой системы (например, отображенные в памяти файлы и блокировки файлов), что позволяет передавать данные с более высокой скоростью. Каналы не блокируются и поэтому Java предоставляет еще такие инструменты, как селектор, который позволяет выбрать готовый канал для передачи данных, и сокет, который является инструментом для блокировки.
  • Буферы: имеет буферизация для всех классов-обёрток примитивов (кроме Boolean). Появился абстрактный класс Buffer, который предоставляет такие операции, как clear, flip, mark и т.д. Его подклассы предоставляют методы для получения и установки данных.
  • Кодировки: появились кодеры и декодеры для отображения байт и символов Unicode. к оглавлению

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

Какие существуют виды потоков ввода/вывода?

Назовите основные классы потоков ввода/вывода.

Разделяют два вида потоков ввода/вывода:

  • байтовые - java.io.InputStream , java.io.OutputStream ;
  • символьные - java.io.Reader , java.io.Writer .

В каких пакетах расположены классы потоков ввода/вывода?

java.io , java.nio . Для работы с потоками компрессированных данных используются классы из пакета java.util.zip

Какие подклассы класса InputStream вы знаете, для чего они предназначены?

  • InputStream - абстрактный класс, описывающий поток ввода;
  • BufferedInputStream - буферизованный входной поток;
  • ByteArrayInputStream позволяет использовать буфер в памяти (массив байтов) в качестве источника данных для входного потока;
  • DataInputStream - входной поток для байтовых данных, включающий методы для чтения стандартных типов данных Java;
  • FileInputStream - входной поток для чтения информации из файла;
  • FilterInputStream - абстрактный класс, предоставляющий интерфейс для классов-надстроек, которые добавляют к существующим потокам полезные свойства;
  • ObjectInputStream - входной поток для объектов;
  • StringBufferInputStream превращает строку ( String ) во входной поток данных InputStream ;
  • PipedInputStream реализует понятие входного канала;
  • PushbackInputStream - разновидность буферизации, обеспечивающая чтение байта с последующим его возвратом в поток, позволяет «заглянуть» во входной поток и увидеть, что оттуда поступит в следующий момент, не извлекая информации.
  • SequenceInputStream используется для слияния двух или более потоков InputStream в единый.

Для чего используется PushbackInputStream ?

Разновидность буферизации, обеспечивающая чтение байта с последующим его возвратом в поток. Класс PushbackInputStream представляет механизм «заглянуть» во входной поток и увидеть, что оттуда поступит в следующий момент, не извлекая информации.

У класса есть дополнительный метод unread().

Для чего используется SequenceInputStream ?

Класс SequenceInputStream позволяет сливать вместе несколько экземпляров класса InputStream . Конструктор принимает в качестве аргумента либо пару объектов класса InputStream , либо интерфейс Enumeration .

Во время работы класс выполняет запросы на чтение из первого объекта класса InputStream и до конца, а затем переключается на второй. При использовании интерфейса работа продолжится по всем объектам класса InputStream . По достижении конца, связанный с ним поток закрывается. Закрытие потока, созданного объектом класса SequenceInputStream , приводит к закрытию всех открытых потоков.

Какой класс позволяет читать данные из входного байтового потока в формате примитивных типов данных?

Класс DataInputStream представляет поток ввода и предназначен для записи данных примитивных типов, таких, как int , double и т.д. Для каждого примитивного типа определен свой метод для считывания:

Какие подклассы класса OutputStream вы знаете, для чего они предназначены?

  • OutputStream - это абстрактный класс, определяющий потоковый байтовый вывод;
  • BufferedOutputStream - буферизированный выходной поток;
  • ByteArrayOutputStream - все данные, посылаемые в этот поток, размещаются в предварительно созданном буфере;
  • DataOutputStream - выходной поток байт, включающий методы для записи стандартных типов данных Java;
  • FileOutputStream - запись данных в файл на физическом носителе;
  • FilterOutputStream - абстрактный класс, предоставляющий интерфейс для классов-надстроек, которые добавляют к существующим потокам полезные свойства;
  • PrintStream - выходной поток, включающий методы print() и println() ;
  • ObjectOutputStream - выходной поток для записи объектов;
  • PipedOutputStream реализует понятие выходного канала.

Какие подклассы класса Reader вы знаете, для чего они предназначены?

  • Reader - абстрактный класс, описывающий символьный ввод;
  • BufferedReader - буферизованный входной символьный поток;
  • CharArrayReader - входной поток, который читает из символьного массива;
  • FileReader - входной поток, читающий файл;
  • FilterReader - абстрактный класс, предоставляющий интерфейс для классов-надстроек;
  • InputStreamReader - входной поток, транслирующий байты в символы;
  • LineNumberReader - входной поток, подсчитывающий строки;
  • PipedReader - входной канал;
  • PushbackReader - входной поток, позволяющий возвращать символы обратно в поток;
  • StringReader - входной поток, читающий из строки.

Какие подклассы класса Writer вы знаете, для чего они предназначены?

  • Writer - абстрактный класс, описывающий символьный вывод;
  • BufferedWriter - буферизованный выходной символьный поток;
  • CharArrayWriter - выходной поток, который пишет в символьный массив;
  • FileWriter - выходной поток, пишущий в файл;
  • FilterWriter - абстрактный класс, предоставляющий интерфейс для классов-надстроек;
  • OutputStreamWriter - выходной поток, транслирующий байты в символы;
  • PipedWriter - выходной канал;
  • PrintWriter - выходной поток символов, включающий методы print() и println() ;
  • StringWriter - выходной поток, пишущий в строку;

В чем отличие класса PrintWriter от PrintStream ?

Прежде всего, в классе PrintWriter применен усовершенствованный способ работы с символами Unicode и другой механизм буферизации вывода: в классе PrintStream буфер вывода сбрасывался всякий раз, когда вызывался метод print() или println() , а при использовании класса PrintWriter существует возможность отказаться от автоматического сброса буферов, выполняя его явным образом при помощи метода flush() .

Кроме того, методы класса PrintWriter никогда не создают исключений. Для проверки ошибок необходимо явно вызвать метод checkError() .

Чем отличаются и что общего у InputStream , OutputStream , Reader , Writer ?

  • InputStream и его наследники - совокупность для получения байтовых данных из различных источников;
  • OutputStream и его наследники - набор классов, определяющих потоковый байтовый вывод;
  • Reader и его наследники определяют потоковый ввод символов Unicode;
  • Writer и его наследники определяют потоковый вывод символов Unicode.

Какие классы позволяют преобразовать байтовые потоки в символьные и обратно?

  • OutputStreamWriter — «мост» между классом OutputStream и классом Writer . Символы, записанные в поток, преобразовываются в байты.
  • InputStreamReader — аналог для чтения. При помощи методов класса Reader читаются байты из потока InputStream и далее преобразуются в символы.

Какие классы позволяют ускорить чтение/запись за счет использования буфера?

  • BufferedInputStream(InputStream in) / BufferedInputStream(InputStream in, int size) ,
  • BufferedOutputStream(OutputStream out) / BufferedOutputStream(OutputStream out, int size) ,
  • BufferedReader(Reader r) / BufferedReader(Reader in, int sz) ,
  • BufferedWriter(Writer out) / BufferedWriter(Writer out, int sz)

Какой класс предназначен для работы с элементами файловой системы?

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

Какие методы класса File вы знаете?

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

  • boolean createNewFile() : делает попытку создать новый файл;
  • boolean delete() : делает попытку удалить каталог или файл;
  • boolean mkdir() : делает попытку создать новый каталог;
  • boolean renameTo(File dest) : делает попытку переименовать файл или каталог;
  • boolean exists() : проверяет, существует ли файл или каталог;
  • String getAbsolutePath() : возвращает абсолютный путь для пути, переданного в конструктор объекта;
  • String getName() : возвращает краткое имя файла или каталога;
  • String getParent() : возвращает имя родительского каталога;
  • boolean isDirectory() : возвращает значение true , если по указанному пути располагается каталог;
  • boolean isFile() : возвращает значение true , если по указанному пути находится файл;
  • boolean isHidden() : возвращает значение true , если каталог или файл являются скрытыми;
  • long length() : возвращает размер файла в байтах;
  • long lastModified() : возвращает время последнего изменения файла или каталога;
  • String[] list() : возвращает массив файлов и подкаталогов, которые находятся в определенном каталоге;
  • File[] listFiles() : возвращает массив файлов и подкаталогов, которые находятся в определенном каталоге.

Что вы знаете об интерфейсе FileFilter ?

Интерфейс FileFilter применяется для проверки, попадает ли объект File под некоторое условие. Этот интерфейс содержит единственный метод boolean accept(File pathName) . Этот метод необходимо переопределить и реализовать. Например:

Как выбрать все элементы определенного каталога по критерию (например, с определенным расширением)?

Метод File.listFiles() возвращает массив объектов File , содержащихся в каталоге. Метод может принимать в качестве параметра объект класса, реализующего FileFilter . Это позволяет включить в список только те элементы, для которых метод accept возвращает true (критерием может быть длина имени файла или его расширение).

Что вы знаете о RandomAccessFile ?

Класс java.io.RandomAccessFile обеспечивает чтение и запись данных в произвольном месте файла. Он не является частью иерархии InputStream или OutputStream . Это полностью отдельный класс, имеющий свои собственные (в большинстве своем native) методы. Объяснением этого может быть то, что RandomAccessFile имеет во многом отличающееся поведение по сравнению с остальными классами ввода/вывода так как позволяет, в пределах файла, перемещаться вперед и назад.

RandomAccessFile имеет такие специфические методы как:

  • getFilePointer() для определения текущего местоположения в файле;
  • seek() для перемещения на новую позицию в файле;
  • length() для выяснения размера файла;
  • setLength() для установки размера файла;
  • skipBytes() для того, чтобы попытаться пропустить определённое число байт;
  • getChannel() для работы с уникальным файловым каналом, ассоциированным с заданным файлом;
  • методы для выполнения обычного и форматированного вывода из файла ( read() , readInt() , readLine() , readUTF() и т.п.);
  • методы для обычной или форматированной записи в файл с прямым доступом ( write() , writeBoolean() , writeByte() и т.п.).

Так же следует отметить, что конструкторы RandomAccessFile требуют второй аргумент, указывающий необходимый режим доступа к файлу - только чтение ( "r" ), чтение и запись ( "rw" ) или иную их разновидность.

Какие режимы доступа к файлу есть у RandomAccessFile ?

  • "r" открывает файл только для чтения. Запуск любых методов записи данных приведет к выбросу исключения IOException .
  • "rw" открывает файл для чтения и записи. Если файл еще не создан, то осуществляется попытка создать его.
  • "rws" открывает файл для чтения и записи подобно "rw" , но требует от системы при каждом изменении содержимого файла или метаданных синхронно записывать эти изменения на физический носитель.
  • "rwd" открывает файл для чтения и записи подобно "rws" , но требует от системы синхронно записывать изменения на физический носитель только при каждом изменении содержимого файла. Если изменяются метаданные, синхронная запись не требуется.

Какие классы поддерживают чтение и запись потоков в компрессированном формате?

  • DeflaterOutputStream - компрессия данных в формате deflate.
  • Deflater - компрессия данных в формат ZLIB
  • ZipOutputStream - потомок DeflaterOutputStream для компрессии данных в формат Zip.
  • GZIPOutputStream - потомок DeflaterOutputStream для компрессии данных в формат GZIP.
  • InflaterInputStream - декомпрессия данных в формате deflate.
  • Inflater - декомпрессия данных в формате ZLIB
  • ZipInputStream - потомок InflaterInputStream для декомпрессии данных в формате Zip.
  • GZIPInputStream - потомок InflaterInputStream для декомпрессии данных в формате GZIP.

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

  • setIn(InputStream) - для ввода;
  • setOut(PrintStream) - для вывода;
  • setErr(PrintStream) - для вывода ошибок.

Какой символ является разделителем при указании пути в файловой системе?

Для различных операционных систем символ разделителя различается. Для Windows это \ , для Linux - / .

В Java получить разделитель для текущей операционной системы можно через обращение к статическому полю File.separator .

Что такое «абсолютный путь» и «относительный путь»?

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

Относительный путь представляет собой путь по отношению к текущему рабочему каталогу пользователя или активного приложения.

Что такое «символьная ссылка»?

Символьные ссылки используются для более удобной организации структуры файлов на компьютере, так как:

Читайте также: