EMZETT.
Login

Files

In short: Java offers several APIs for file access — from the classic java.io classes (File, FileReader) to the more modern java.nio.file.Files class.

In more detail: The newer java.nio.file API (since Java 7) offers more compact, static methods for common operations (Files.readAllLines(path), Files.write(...)) and better error handling than the older java.io.File class. All file operations can fail (file doesn’t exist, no permission) and therefore throw checked exceptions.

In Depth

Path path = Path.of("notes.txt");
 
// Read the whole file at once (only suitable for small/medium files)
List<String> lines = Files.readAllLines(path);
String content = Files.readString(path); // since Java 11
 
// Write the whole file at once
Files.writeString(path, "New content");
 
// Check existence and properties without opening the file
boolean exists = Files.exists(path);
long size = Files.size(path);
boolean isDirectory = Files.isDirectory(path);
 
// Iterate over a directory
try (Stream<Path> files = Files.list(Path.of("."))) {
    files.filter(Files::isRegularFile).forEach(System.out::println);
}

The Path type itself only represents a file path as a value — it doesn’t “open” or “check” anything on its own, but serves as an argument for the static methods of the Files class, which do the actual work. This separation (path as a pure value, Files as a toolbox with operations on it) is a deliberate design and replaces the older model, in which a single File instance both represented the path and carried dozens of instance methods for operations on it. For very large files that don’t fit entirely into memory, the line-by-line/stream-based variants (Files.lines(), BufferedReader) are preferable to readAllLines()/readString().

Composing and normalising paths

Path offers platform-independent methods for composing and resolving paths, instead of manually concatenating strings with / or \ (which would lead to different results on Windows vs. Unix):

Path base = Path.of("project", "data");
Path file = base.resolve("config.json"); // "project/data/config.json" (OS-dependent separator)
Path absolute = file.toAbsolutePath();
Path parent = file.getParent(); // "project/data"
String filename = file.getFileName().toString(); // "config.json"
 
// Relative paths with ".." are correctly resolved
Path normalized = Path.of("a/b/../c").normalize(); // "a/c"

Creating and copying directories

Files.createDirectories(Path.of("new/nested/folder")); // creates all missing intermediate folders
Files.copy(Path.of("source.txt"), Path.of("target.txt"), StandardCopyOption.REPLACE_EXISTING);
Files.move(Path.of("old.txt"), Path.of("new.txt")); // rename/move

createDirectories() (plural) creates the entire missing folder structure as needed, while createDirectory() (singular) fails if the parent folder doesn’t exist yet — a common mix-up that leads to an unexpected NoSuchFileException.

File attributes and permissions

Besides content and existence, metadata like modification time and permissions can also be queried and set via Files, without opening the file itself:

FileTime lastModified = Files.getLastModifiedTime(path);
boolean readable = Files.isReadable(path);
boolean writable = Files.isWritable(path);
Files.setLastModifiedTime(path, FileTime.fromMillis(System.currentTimeMillis()));

On Unix systems, POSIX file permissions can additionally be set directly (Files.setPosixFilePermissions()) — on Windows this API is technically available, but throws an UnsupportedOperationException, since Windows uses a different permission model.

See also: Create Files, Read Files, Write Files, Delete Files, I/O Streams