Read a File in Java: The Modern Way

Reading a file in Java used to be verbose. Since Java 7 (NIO.2) and Java 11 (readString), it's a one-liner. This guide covers the modern approaches and when to pick each.

Read a whole file as a string (Java 11+)

import java.nio.file.Files;
import java.nio.file.Path;

String content = Files.readString(Path.of("config.json"));

Default charset is UTF-8. Perfect for small configuration or template files.

Read a file line by line into a List

import java.util.List;

List<String> lines = Files.readAllLines(Path.of("data.txt"));

for (String line : lines) {
    System.out.println(line);
}

Warning: readAllLines loads the entire file into memory. Don't use it for files larger than ~10 MB.

Stream large files line by line

import java.util.stream.Stream;

try (Stream<String> lines = Files.lines(Path.of("huge.log"))) {
    lines.filter(l -> l.contains("ERROR"))
         .limit(100)
         .forEach(System.out::println);
}

Files.lines returns a lazy Stream. You must close it β€” the try-with-resources block does that for you. Handles multi-GB files in constant memory.

BufferedReader β€” classic but still useful

import java.io.BufferedReader;

try (BufferedReader reader = Files.newBufferedReader(Path.of("data.txt"))) {
    String line;
    while ((line = reader.readLine()) != null) {
        // process line
    }
}

Use when you need fine control β€” for example, reading a header line, then streaming the rest differently.

Read bytes

byte[] bytes = Files.readAllBytes(Path.of("image.png"));
// Same size-warning as readAllLines

For stream-based byte reading:

try (InputStream in = Files.newInputStream(Path.of("image.png"))) {
    byte[] buffer = new byte[8192];
    int n;
    while ((n = in.read(buffer)) != -1) {
        // process n bytes
    }
}

Charset control

Don't rely on the platform default β€” be explicit:

import java.nio.charset.StandardCharsets;

String c = Files.readString(Path.of("data.txt"), StandardCharsets.ISO_8859_1);
List<String> l = Files.readAllLines(Path.of("data.txt"), StandardCharsets.UTF_8);

Common charsets: UTF_8 (default), ISO_8859_1 (Latin-1), UTF_16. Pass them via StandardCharsets to avoid typos.

Error handling

import java.io.IOException;
import java.nio.file.NoSuchFileException;

try {
    String c = Files.readString(Path.of("missing.txt"));
} catch (NoSuchFileException e) {
    System.err.println("Not found: " + e.getFile());
} catch (IOException e) {
    System.err.println("I/O error: " + e.getMessage());
}

Always catch the most specific subclass you can β€” NoSuchFileException, AccessDeniedException, MalformedInputException β€” before falling back to generic IOException.

Reading from the classpath (resource files)

For files bundled inside your JAR (e.g. src/main/resources/config.json):

try (InputStream in = getClass().getResourceAsStream("/config.json")) {
    if (in == null) throw new IllegalStateException("Missing resource");
    String content = new String(in.readAllBytes(), StandardCharsets.UTF_8);
}

Note the leading / β€” classpath paths are absolute relative to the resources root.

Parsing common formats

CSV

split(",") doesn't handle quoted fields, commas in values, or multiline entries. Use a library:

<dependency>
  <groupId>com.fasterxml.jackson.dataformat</groupId>
  <artifactId>jackson-dataformat-csv</artifactId>
  <version>2.18.2</version>
</dependency>

JSON

ObjectMapper mapper = new ObjectMapper();
Config cfg = mapper.readValue(Path.of("config.json").toFile(), Config.class);

Properties

Properties p = new Properties();
try (Reader r = Files.newBufferedReader(Path.of("app.properties"))) {
    p.load(r);
}
String port = p.getProperty("server.port", "8080");

Which method for which situation?

File sizeStructureRecommended
Small (< 10 MB)Single blob (JSON, XML, config)Files.readString
SmallLine-basedFiles.readAllLines
Large (> 100 MB)Line-basedFiles.lines in try-with-resources
BinarySmallFiles.readAllBytes
BinaryLargeFiles.newInputStream with buffered read
Classpath resourceAnygetResourceAsStream

Common mistakes

  • Not closing streams: Files.lines and newBufferedReader must be closed via try-with-resources.
  • Using platform default charset: causes bugs when code moves between systems.
  • Reading a huge log file with readAllLines: OutOfMemoryError waiting to happen.
  • Forgetting that Paths.get (older API) and Path.of (Java 11+) do the same thing; prefer the newer.

For everyday file reading in modern Java, Files.readString and Files.lines cover nearly every real-world need in a single line, with explicit charset control and proper resource handling.