Перейти к основному содержимому

В чем разница между NIO и NIO.2?

· 6 мин. чтения

1. Введение

В этом руководстве мы рассмотрим функции Java IO и то, как они изменились в разных версиях Java. Сначала мы рассмотрим пакет java.io из начальной версии Java. Далее мы рассмотрим пакет java.nio , представленный в Java 1.4. В конце мы рассмотрим пакет java.nio.file , широко известный как пакет NIO.2.

2. Пакет Java NIO

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

Есть у него и некоторые недостатки:

  • Отсутствие метода копирования — чтобы скопировать файл, нам нужно создать два экземпляра File и использовать буфер для чтения из одного и записи в другой экземпляр File .
  • Плохая обработка ошибок — некоторые методы возвращают логическое значение в качестве индикатора, если операция выполнена успешно или нет.
  • Доступен ограниченный набор атрибутов файла — имя, путь, права на чтение/запись, объем памяти и многие другие.
  • Blocking API — наш поток блокируется до завершения операции ввода-вывода.

Чтобы прочитать файл, нам нужен экземпляр FileInputStream для чтения байтов из файла:

@Test
public void readFromFileUsingFileIO() throws Exception {
File file = new File("src/test/resources/nio-vs-nio2.txt");
FileInputStream in = new FileInputStream(file);
StringBuilder content = new StringBuilder();
int data = in.read();
while (data != -1) {
content.append((char) data);
data = in.read();
}
in.close();
assertThat(content.toString()).isEqualTo("Hello from file!");
}

Затем в Java 1.4 представлен неблокирующий API-интерфейс ввода-вывода, включенный в пакет java.nio (nio означает новый ввод-вывод). NIO был введен для преодоления ограничений пакета java.io. В этом пакете представлены три основных класса: Channel , Buffer и Selector .

2.1. Канал

Java NIO Channel — это класс, который позволяет нам читать и писать в буфер . Класс Channel похож на Streams (здесь мы говорим о IO Streams , а не о потоках Java 1.8 ) с парой отличий. Канал — это улица с двусторонним движением, в то время как потоки обычно односторонние, и они могут читать и записывать асинхронно.

Существует пара реализаций класса Channel , в том числе FileChannel для чтения/записи файловой системы, DatagramChannel для чтения/записи по сети с использованием UDP и SocketChannel для чтения/записи по сети с использованием TCP.

2.2. Буфер

Буфер — это блок памяти, из которого мы можем читать или записывать в него данные . Объект NIO Buffer оборачивает блок памяти. Класс Buffer предоставляет набор функций для работы с блоком памяти. Чтобы работать с объектами Buffer , нам нужно понимать три основных свойства класса Buffer : емкость, положение и предел.

  • Емкость определяет размер блока памяти. Когда мы записываем данные в буфер, мы можем записать только ограниченную длину. Когда буфер заполнен, нам нужно прочитать данные или очистить их.
  • Позиция — это отправная точка, где мы записываем наши данные. Пустой буфер начинается с 0 и достигает емкости – 1 . Кроме того, когда мы читаем данные, мы начинаем со значения позиции.
  • Ограничение означает, как мы можем писать и читать из буфера.

Существует несколько вариантов класса Buffer . По одному для каждого примитивного типа Java, за исключением логического типа плюс MappedByteBuffer .

Для работы с буфером нам нужно знать несколько важных методов:

  • allocate(int value) — мы используем этот метод для создания буфера определенного размера.
  • flip() — этот метод используется для переключения из режима записи в режим чтения.
  • clear() — метод очистки содержимого буфера
  • compact() — метод очистки только того контента, который мы уже прочитали
  • rewind () — сбрасывает позицию обратно в 0, чтобы мы могли перечитать данные в буфере.

Используя ранее описанные концепции, давайте воспользуемся классами Channel и Buffer для чтения содержимого из файла:

@Test
public void readFromFileUsingFileChannel() throws Exception {
RandomAccessFile file = new RandomAccessFile("src/test/resources/nio-vs-nio2.txt", "r");
FileChannel channel = file.getChannel();
StringBuilder content = new StringBuilder();
ByteBuffer buffer = ByteBuffer.allocate(256);
int bytesRead = channel.read(buffer);
while (bytesRead != -1) {
buffer.flip();
while (buffer.hasRemaining()) {
content.append((char) buffer.get());
}
buffer.clear();
bytesRead = channel.read(buffer);
}
file.close();
assertThat(content.toString()).isEqualTo("Hello from file!");
}

После инициализации всех необходимых объектов читаем из канала в буфер. Далее, в цикле while мы помечаем буфер для чтения с помощью метода flip() и читаем по одному байту за раз, и добавляем его к нашему результату. В конце мы очищаем данные и читаем другую партию.

2.3. Селектор

Java NIO Selector позволяет нам управлять несколькими каналами с помощью одного потока. Чтобы использовать объект селектора для мониторинга нескольких каналов, каждый экземпляр канала должен находиться в неблокирующем режиме, и мы должны его зарегистрировать. После регистрации канала мы получаем объект SelectionKey , представляющий связь между каналом и селектором. Когда у нас есть несколько каналов, подключенных к селектору, мы можем использовать метод select() , чтобы проверить, сколько каналов готовы к использованию. После вызова метода select() мы можем использовать метод selectedKeys() для получения всех готовых каналов.

2.4. Недостатки пакета NIO

Изменения, внесенные пакетом java.nio , больше связаны с низкоуровневым вводом-выводом данных. Хотя они разрешили неблокирующий API, другие аспекты оставались проблематичными:

  • Ограниченная поддержка символических ссылок
  • Ограниченная поддержка доступа к атрибутам файла
  • Отсутствуют лучшие инструменты управления файловой системой

3. Пакет Java NIO.2

В Java 1.7 представлен новый пакет java.nio.file , также известный как пакет NIO.2 . Этот пакет следует асинхронному подходу к неблокирующему вводу-выводу, который не поддерживается в пакете java.nio . Наиболее значительные изменения связаны с высокоуровневыми манипуляциями с файлами. Они добавляются с помощью классов Files, Path и Paths . Наиболее заметным низкоуровневым изменением является добавление AsynchroniousFileChannel и AsyncroniousSocketChannel .

Объект пути представляет собой иерархическую последовательность имен каталогов и файлов, разделенных разделителем . Корневой компонент крайний слева, а файл справа. Этот класс предоставляет служебные методы, такие как getFileName() , getParent() и т. д. Класс Path также предоставляет методы разрешения и релятивизации , которые помогают создавать пути между различными файлами. Класс Paths — это набор статических служебных методов, которые получают String или URI для создания экземпляров Path .

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

Наконец, давайте посмотрим, как NIO.2 сравнивается с предыдущими версиями IO, когда дело доходит до чтения файла:

@Test
public void readFromFileUsingNIO2() throws Exception {
List<String> strings = Files.readAllLines(Paths.get("src/test/resources/nio-vs-nio2.txt"));
assertThat(strings.get(0)).isEqualTo("Hello from file!");
}

4. Вывод

В этой статье мы рассмотрели основы пакетов java.nio и java.nio.file . Как мы видим, NIO.2 не является новой версией пакета NIO. Пакет NIO представил низкоуровневый API для неблокирующего ввода-вывода, а NIO.2 представил улучшенное управление файлами. Эти два пакета не являются синонимами, а скорее дополняют друг друга. Как всегда, все примеры кода можно найти на GitHub .