Utility Classes of the JDK: Dealing With I/O
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
Table of Contents
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.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
:
APPEND
CREATE
CREATE_NEW
— Fail if it already existsDELETE_ON_CLOSE
DSYNC
— Requires that synchronous content writes to storageREAD
SPARSE
SYNC
— Require synchronous writes of content and metadata to storageTRUNCATE_EXISTING
WRITE
Creating files and directories
Different kinds of filesystem objects can be created easily:
Path createDirectory(Path dir, FileAttribute<?>... attrs)
Create a single directory, no intermediaries.Path createDirectories(Path dir, FileAttribute<?>... attrs)
Create a directory, including all non-existent parents.Path createFile(Path path, FileAttribute<?>... attrs)
Create an empty fail, fails if exists.Path createLink(Path link, Path existing)
Create a hard-link.Path createSymbolicLink(Path link, Path target, FileAttribute<?>... attrs)
Create a symbolic link.
We can also create a file by using a java.nio.channels.SeekableByteChannel
by using the appropriate java.nio.file.OpenOption
:
SeekableByteChannel newByteChannel(Path path, OpenOption... options)
Open or create a seekable byte channel.SeekableByteChannel newByteChannel(Path path, Set<OpenOption> options, FileAttribute<?>... attrs)
Open or create a seekable byte channel with initial file attributes for creation.
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:
Path createTempFile(String prefix, String suffix, FileAttribute<?>... attrs)
Create a file directly in the temporary-file directory.Path createTempDirectory(String prefix, FileAttribute<?>... attrs)
Create a directory directly in the temporary-file directory.Path createTempDirectory(Path dir, String prefix, FileAttribute<?>... attrs)
Create a new directory in the provided directory, but use the temporary naming logic.
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
byte[] readAllBytes(Path path)
Read all bytes, not recommended for large files.InputStream newInputStream(Path path, OpenOption... options)
Open a file and create an unbuffered InputStream. Don’t forget to close it!SeekableByteChannel newByteChannel(Path path, OpenOption... options)
Open or create a seekable byte channel.SeekableByteChannel newByteChannel(Path path, Set<OpenOption> options, FileAttribute<?>... attrs)
Open or create a seekable byte channel with initial file attributes for creation.
Character-based
List<String> readAllLines(Path path)
Read all lines as UTF-8, not recommended for large files.List<String> readAllLines(Path path, Charset cs)
Read all lines as a specific charset, not recommended for large files.
Stream<String> lines(Path path)
Read lines lazy as UTF-8, backed by ajava.io.Reader
.Stream<String> lines(Path path, Charset cs)
Read lines lazy as a specific charset, backed by a java.io.Reader
BufferedReader newBufferedReader(Path path)
Read lines buffered as UTF8BufferedReader newBufferedReader(Path path, Charset cs)
Read lines buffered as a specific charset
Writing
Like reading, writing content can be separated into two groups:
Byte-based
Path write(Path path, byte[] bytes, OpenOption... options)
Write bytes directly to a path.
OutputStream newOutputStream(Path path, OpenOption... options)
Opens or creates a file, returning ajava.io.OutputStream
.SeekableByteChannel newByteChannel(Path path, OpenOption... options)
Opens or creates a seekable byte channel.SeekableByteChannel newByteChannel(Path path, Set<OpenOption> options, FileAttribute<?>... attrs)
Opens or creates a seekable byte channel with initial file attributes for creation.
Character-based
Path write(Path path, Iterable<CharSequence> lines, OpenOption... options)
Writes text lines as UTF-8
Path write(Path path, Iterable<CharSequence> lines, Charset cs, OpenOption... options)
Writes text lines as the specified charsetBufferedWriter newBufferedWriter(Path path, OpenOption... options)
Opens or creates a file, returning ajava.io.BufferedWriter
, using UTF-8.BufferedWriter newBufferedWriter(Path path, Charset cs, OpenOption... options)
Opens or creates a file, returning ajava.io.BufferedWriter
, using the specified charset.
Directory content
There are three different kinds of iterating/walking over directory content:
java.nio.file.DirectoryStream
A specializedjava.lang.Iterable
.java.util.stream.Stream
Use the more modern Stream API.
Each one has its merits, depending on our requirements.
DirectoryStream<Path> newDirectoryStream(Path dir)
Iterate over all entries.DirectoryStream<Path> newDirectoryStream(Path dir, Filter<Path> filter)
Iterate filtered entries.DirectoryStream<Path> newDirectoryStream(Path dir, String glob)
Iterate over entries filtered by globbing.
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.
Stream<Path> walk(Path start, FileVisitOption... options)
Lazily populated Steam, traversed depth-first.Stream<Path> walk(Path start, int maxDepth, FileVisitOption... options)
Lazily populated Steam, traversed depth-first, up tomaxDepth
Path walkFileTree(Path start, FileVisitor<Path> visitor)
Visitor-pattern.Path walkFileTree(Path start, Set<FileVisitOption> options, int maxDepth, FileVisitor<Path> visitor)
Visitor-pattern, traversed depth-first, up tomaxDepth
.
There are two additional methods for listing/finding directory content:
Stream<Path> list(Path dir)
Lazily populated Stream of directory content.Stream<Path> find(Path start, int maxDepth, BiPredicate<Path,BasicFileAttributes> matcher, FileVisitOption... options)
Lazily populated filtered Stream.
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.
boolean isSameFile(Path path, Path path2)
If the arguments are equals == true, the files themselves are not checked.
Even the MIME content type can be detected:
Attributes
The usual file attributes can be read and written:
Set<PosixFilePermission> getPosixFilePermissions(Path path, LinkOption... options)
Path setPosixFilePermissions(Path path, Set<PosixFilePermission> perms)
But we can also access any file attributes directly:
Object getAttribute(Path path, String attribute, LinkOption... options)
Path setAttribute(Path path, String attribute, Object value, LinkOption... options)
A readAttributes(Path path, Class<A> type, LinkOption... options)
Map<String,Object> readAttributes(Path path, String attributes, LinkOption... options)
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
:
Path copy(Path source, Path target, CopyOption... options)
Copy a file to a target file (not directory!)long copy(InputStream in, Path target, CopyOption... options)
Copy all bytes from InputStream to aPath
, returns bytes read or writtenlong copy(Path source, OutputStream out)
Copy all bytes from a file to anOutputStream
, returns bytes read or written.
void delete(Path path)
Delete a file or empty directory. Symbolic links aren’t followed. ThrowsNoSuchFileException
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.
boolean exists(Path path, LinkOption... options)
Test whether a file or directory exists.boolean notExists(Path path, LinkOption... options)
Test whether a file or directory doesn’t exist.
Path move(Path source, Path target, CopyOption... options)
Move/rename a file or directory. CopyOptionCOPY_ATTRIBUTES
isn’t supported.
Path readSymbolicLink(Path link)
Read target of symbolic link. ThrowsNotLinkExpcetion
if the target isn’t a symbolic link.
Java 11 additions
The release of Java 11 brought us four new methods for reading/writing Strings:
String readString(Path path)
Read file to String as UTF-8.String readString(Path path, Charset cs)
Read file to String as specified charset.
Path writeString(Path path, CharSequence csq, OpenOption... options)
Write String to file as UTF-8.Path writeString(Path path, CharSequence csq, Charset cs, OpenOption... options)
Write String to file as specified charset.
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.
Resources
java.nio.file.Paths
(Oracle)java.nio.file.Files
(Oracle)- Java Non-blocking I/O (Wikipedia)
- Java I/O, NIO, and NIO2 (Oracle)
- Introduction to the Java NIO2 File API (Baeldung)
- Utility Classes of the JDK Collections and Arrays