Utility Classes of the JDK: Dealing With I/O

 · 8 min

Java NIO (“non-blocking I/O”) is a great feature set for dealing with I/O operations. Introduced by Java 1.4, it was further improved in Java 7 (JSR 203).

This release also brought us the two utility classes of this article: java.nio.file.Files and java.nio.file.Paths

At least Java 8 is assumed.
All listed methods omit the static keyword, and generic type information might be simplified to reduce visual clutter and improve readability.


The Path Interface

The core of many I/O operations is the java.nio.file.Path interface. It represents a hierarchical sequence of directories and filename elements, which may be used for locating a file or directory, with a system-dependent file path separator.

It’s mostly interoperable with java.io.File. Both types offer conversion methods: File#toPath() and Path#toFile().


java.nio.file.Paths

The java.nio.file.Paths isn’t a full-fledged utility class with lots of different methods. It’s a mere helper for creating a java.nio.file.Path instance:

Instead of needing to concatenate Strings of directories and filenames, we can use this helper instead. The appropriate filesystem separator will be used:

java
Paths.get("data", "logs", "error")
// => data/logs/error

java.nio.file.Files

The java.nio.file.Files utility class is a behemoth. In Java 8, it consists of 65 static methods. The latest LTS, as of writing the article, Java 11, upped the number to 69.

Some listed methods appear in multiple sections, if appropriate.

java.nio.file.OpenOption

We can influence how files are opened by providing one or more java.nio.file.OpenOption. There are multiple options available, defined in java.nio.file.StandardOpenOption:

Creating files and directories

Different kinds of filesystem objects can be created easily:

We can also create a file by using a java.nio.channels.SeekableByteChannel by using the appropriate java.nio.file.OpenOption:

Temporary Files and Directories

There are also special methods for dealing with temporary objects. They will create a file or directory in the default temporary-file directory, with a randomized name.

But we can provide a prefix/suffix for better identification:

Reading content

Content reading can be categorized into two groups: byte-based and character-based.

Both categories are filled with methods for reading “all-at-once”, or “on-demand/lazy”.

Byte-based

Character-based

Writing

Like reading, writing content can be separated into two groups:

Byte-based

Character-based

Directory content

There are three different kinds of iterating/walking over directory content:

Each one has its merits, depending on our requirements.

When walking over a Path, we can specify java.nio.file.FileVisitOption.

At this point, only FOLLOW_LINKS is available. If a cycle is detected, a FileSystemLoopException is thrown.

There are two additional methods for listing/finding directory content:

Detecting filesystem object properties

While iterating over directory content, we also want to know what we’re dealing with.

Some methods accept java.nio.file.LinkOption, which determines how symbolic links are handled. Only the option NOFOLLOW_LINKS is available.

Even the MIME content type can be detected:

Attributes

The usual file attributes can be read and written:

But we can also access any file attributes directly:

File operations

Last but not least, routine file operations.

Besides java.nio.file.LinkOption, some methods use java.nio.file.CopyOption.

The available options are defined in java.nio.file.StandardCopyOption:

  • void delete​(Path path)
    Delete a file or empty directory. Symbolic links aren’t followed. Throws NoSuchFileException if the file doesn’t exist.

  • boolean deleteIfExists​(Path path)
    Delete a file or empty directory. Symbolic links aren’t followed. Doesn’t throw an exception if the file doesn’t exists.

Java 11 additions

The release of Java 11 brought us four new methods for reading/writing Strings:


Conclusion

Another excellent utility class that simplifies a lot of complicated I/O operations, without the need for a third-party library.

But be aware that almost all the methods throw various exceptions! Never trust I/O operations blindly, and always catch (and handle) their exceptions.


A Functional Approach to Java Cover Image
Interested in using functional concepts and techniques in your Java code?
Check out my book!
Available in English, Polish, Korean, and soon, Chinese.

Resources