What is the difference between NIO and NIO.2?
1. Introduction
In this tutorial, we will introduce Java IO features and how they have changed in different Java versions. First, we'll cover the initial Java release. java.io
Next, we'll cover java.nio
the java.nio package that was introduced in Java 1.4. Finally, we'll introduce java.nio.file
packages, commonly referred to as NIO.2 packages.
2. Java NIO package
The first Java version java.io
was released with the package, introducing a File
classes to access the file system. File
Classes represent files and directories and provide limited operations on the file system. Files can be created and deleted, checked for their existence, checked for read/write access, etc.
It also has some disadvantages:
Missing copy method - to copy a file we need to create two
File
instances and use a buffer to read one and write to the otherFile
.Error handling errors - some methods return
boolean
as an indicator of the success or failure of the operation.A limited set of file properties - name, path, read/write permissions, available memory size, etc.
Blocking API - Our thread is blocked until the IO operation completes.
To read a file, we need an FileInputStream
instance to read bytes from the file:
@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!");
}
Next, Java 1.4 introduced the java.nio
non-blocking IO API (nio stands for New IO) bundled in the package. NIO was introduced to overcome java.io
package limitations. This package introduces three core classes: Channel
, Buffer
and Selector
.
2.1 Channel
Java NIO Channel
is a class that allows us to read and write buffers . Channel
Classes are similar Streams
(here we're talking about IO Streams
, not Java 1.8 Streams
), but with a few differences. Channel
are bidirectional, and Streams
usually unidirectional, they can be read and written asynchronously.
Channel
The class has several implementations, including FileChannel
for filesystem read/write, DatagramChannel
for read/write over the network using UDP, and SocketChannel
for read/write over the network using TCP.
2.2 Buffer
A buffer is a piece of memory from which we can read or write data . A NIO Buffer
object wraps a block of memory. Buffer
Classes provide a set of functions that work with blocks of memory. To work with Buffer
objects, we need to understand Buffer
the three main properties of classes: capacity, location, and constraints.
The capacity defines the size of the memory block. When we write data to the buffer, we can only write to a limited length. When the buffer is full, we need to read the data or clear the data.
The location is the starting point from which we write data. An empty buffer starts at 0
capacity – 1
. Also, when we read the data, we start with the position value.Limits mean how we can write and read from the buffer.
Buffer
There are many variants of classes. One per primitive Java type, excluding Boolean
types and MappedByteBuffer
.
To use buffers, we need to know some important methods:
allocate(int value) –
We use this method to create a buffer of a specific size.flip()
- This method is used to switch from write mode to read modeclear() –
Method to clear buffer contentscompact() –
A way to only clear what we've already readrewind() –
The position is reset to 0 so that we can re-read the data in the buffer
Using the concepts described earlier, let's use the Channel
and Buffer
classes to read content from a file:
@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!");
}
After initializing all required objects, we read from the channel to the buffer. Next, in the while loop, we use the flip()
method to mark the buffer to read and read one byte at a time and append it to our result. Finally, we clear the data and read another batch.
2.3. Selector
Java NIO Selector allows us to manage multiple channels with one thread. To monitor multiple channels with a selector object, each channel instance must be in non-blocking mode and we must register it. After the channel is registered, we get an SelectionKey
object representing the connection between the channel and the selector. When we have multiple channels connected to a selector, we can use select()
methods to check how many channels are available. After calling select()
the method, we can use the selectedKeys()
method to get all prepared channels.
2.4. Disadvantages of NIO package
java.nio
The changes introduced by the package are more related to the underlying data IO. While they allow a non-blocking API, other areas remain problematic:
Limited support for symbolic links
Limited support for file attribute access
Lack of better file system management tools
3. Java NIO.2 package
Java 1.7 introduced new java.nio.file
packages, also known as NIO.2 packages. java.nio
Async methods for non-blocking IO not supported in the package. The most important changes are related to advanced file operations. They are added with Files, Path,
and Paths
classes. The most notable low-level changes are the addition of AsynchroniousFileChannel
and AsyncroniousSocketChannel
.
Path
An object represents a hierarchical sequence of directory and file names separated by a delimiter . The root component is on the far left, and the file is on the right. This class provides utility methods such as getFileName()
, getParent()
etc. Path
The class also provides resolve
and relativize
methods to help build paths between different files. The Paths class is a set of static utility methods that receive String
or URI
create Path
instances.
Files
Classes provide utility methods for using the previously described Path
classes and manipulating files, directories, and symbolic links. It also provides a readAttributes()
way to read many file properties using methods.
Finally, let's see how NIO.2 compares to previous IO versions when reading files:
@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 Conclusion
In this article, we covered the basics of java.nio
and java.nio.file
packages. As we can see, NIO.2 is not a new version of the NIO package. The NIO package introduced a low-level API for non-blocking IO, while NIO.2 introduced better file management. The two packages are not synonymous but complement each other.
0 Comments