Neste artigo eu apresento alguns casos de usos simples de leitura e escrita de arquivos de texto com Java.

Leitura

O código abaixo mostra como retornar todo o conteúdo de um arquivo de texto como uma String.

import static java.nio.charset.StandardCharsets.UTF_16;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class ReadingAllContent {
  public static void main(String... args) {
    try {
      Path path = Paths.get("text.txt");
      byte[] bytes = Files.readAllBytes(path);
      String content = new String(bytes, UTF_16);
      System.out.println(content);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Com Java 11 ou mais recente o código fica mais simples usando o método Files#readString:

Path path = Path.of("text.txt");
String content = Files.readString(path, UTF_16);

Também é possível retornar todas as linhas do arquivo como uma lista de String usando o método Files#readAllLines.

Path path = Paths.get("text.txt");
List<String> lines = Files.readAllLines(path, UTF_16);
lines.forEach(System.out::println);

Os métodos acima carregam todo o conteúdo do arquivo na memória de uma só vez, por isso só devem ser usados quando o arquivo não for tão grande.

Para ler arquivos maiores, linha a linha, podemos usar o método Files#lines que retorna uma Stream que lê as linhas conforme é consumida.

import static java.nio.charset.StandardCharsets.UTF_16;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;

public class ReadingAsStream {
  public static void main(String... args) {
    Path path = Paths.get("text.txt");

    try (Stream<String> stream = Files.lines(path, UTF_16)) {
      stream.forEach(System.out::println);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

A Stream retornada precisa ser fechada quando não for mais útil, por isso, no exemplo eu utilizei o try-with-resource que garante seu fechamento.

Escrita

O código abaixo mostra como escrever uma String em um arquivo.

import static java.nio.charset.StandardCharsets.UTF_16;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class WritingString {
  public static void main(String... args) {
    try {
      Path path = Paths.get("text.txt");
      String content = "abc";
      byte[] bytes = content.getBytes(UTF_16);
      Files.write(path, bytes);
    } catch (IOException e) {
      e.printStackTrace();
    }
  }
}

Com Java 11 ou mais recente o código fica mais simples usando o método Files#writeString.

Path path = Path.of("text.txt");
Files.writeString(path, "abc", UTF_16);

O método Files#write também aceita uma lista de String como argumento.

Path path = Paths.get("text.txt");
List<String> lines = Arrays.asList("a", "b", "c");
Files.write(path, lines, UTF_16);

Neste caso, cada String da lista será escrita como uma nova linha no arquivo.

Observação 1: Escolhendo o charset

Nos exemplos acima eu passei o Charset UTF-16 explicitamente apenas para mostrar que é possível, pois nem sempre é necessário.

O método Files#readString, por exemplo, poderia ser invocado assim:

Path path = Paths.get("text.txt");
String content = Files.readString(path);

Neste caso o charset usado por padrão será o UTF-8 e o mesmo vale para os métodos mostrados abaixo:

Path path = Paths.get("text.txt");

String a = Files.readString(path);

List<String> b = Files.readAllLines(path);

Stream<String> c = Files.lines(path);

Files.writeString(path, "abc");

Files.write(path, Arrays.asList("a", "b", "c"));

Já no método String#getBytes, se o charset não for passado explicitamente, será usado o charset padrão da sua plataforma. Para obter o charset padrão, você pode usar o método abaixo:

Charset charset = Charset.defaultCharset();

Por fim, a classe StandardCharsets possui constantes apenas para os charsets que são requeridos por qualquer implementação da plataforma Java. Se você precisar de um charset não definido nesta classe, é possível obter da seguinte forma:

Charset charset = Charset.forName("UTF-32");

Se o charset desejado for suportado pela JVM, uma instância de Charset será retornada, caso contrário uma exceção será lançada.

Observação 2: Outras opções

Os métodos de escrita Files#writeString e Files#write também aceitam opções extras.

Por padrão eles criarão um novo arquivo caso não exista ou sobrescreverão o conteúdo de um arquivo existente, mas é possível mudar este comportamento.

Considere o código abaixo:

import static java.nio.file.StandardOpenOption.APPEND;
import static java.nio.file.StandardOpenOption.CREATE;
import static java.nio.file.StandardOpenOption.CREATE_NEW;

import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class WritingWithOptions {
  public static void main(String... args) throws IOException {
    byte[] content = "abc".getBytes();

    /* 1 */ Files.write(Paths.get("a.txt"), content, APPEND);
    /* 2 */ Files.write(Paths.get("b.txt"), content, CREATE, APPEND);
    /* 3 */ Files.write(Paths.get("c.txt"), content, CREATE_NEW);
  }
}

O exemplo 1 acrescentará content à um arquivo existente e lançará uma exceção caso o arquivo não exista.

O exemplo 2 é parecido com o primeiro, mas criará um novo arquivo caso não exista ao invés de lançar uma exceção.

Já no exemplo 3 ele sempre tentará criar um novo arquivo e lançará uma exceção se o arquivo já existir.

Há outras opções disponíveis na classe StandardOpenOption, consulte a documentação para saber mais.

Conclusão

Há outras formas de ler e escrever arquivos em Java e compreender os métodos acima é um bom ponto de partida antes de partir para casos mais complexos.

Se encontrar algum erro ou tiver alguma informação adicional, não deixe de usar os comentários.