Потоки символов

Reader и Writer работают с UTF-16 code units (char). На границе с байтами всегда участвует Charset.

try (BufferedReader reader = Files.newBufferedReader(
             path, StandardCharsets.UTF_8);
     BufferedWriter writer = Files.newBufferedWriter(
             target, StandardCharsets.UTF_8)) {
 
    String line;
    while ((line = reader.readLine()) != null) {
        writer.write(line);
        writer.newLine();
    }
}

Кодировку лучше указывать явно. Иначе default charset зависит от JDK/окружения, а файл может читаться не там, где был записан.

Reader.read()

Как и байтовый stream, одиночный read() возвращает int: значение code unit 0..65535 или -1 для EOF.

int unit;
while ((unit = reader.read()) != -1) {
    char ch = (char) unit;
}

Одна code unit может быть половиной surrogate pair; для полноценной Unicode-обработки удобнее прочитать строку и идти по code points.

Обертки

  • InputStreamReader и OutputStreamWriter — мост byte ↔ char;
  • BufferedReader.readLine() читает строку без разделителя;
  • StringReader/StringWriter — текст в памяти;
  • PrintWriter.printf() — форматированный вывод;
  • Scanner — токенизация и примитивы с учетом locale, но не самый быстрый parser.

PrintWriter по умолчанию не бросает IOException из методов записи: ошибку проверяют checkError(). Это важное отличие от BufferedWriter.

Кодировка — часть контракта

byte[] bytes = text.getBytes(StandardCharsets.UTF_8);
String restored = new String(bytes, StandardCharsets.UTF_8);

Нельзя декодировать произвольные chunks независимо: multibyte-последовательность UTF-8 может пересекать границу. InputStreamReader хранит промежуточное состояние decoder.

На собеседовании

  • InputStream и Reader? Байты против UTF-16 code units.
  • Кто превращает байты в символы? Decoder выбранного Charset.
  • Возвращает ли readLine разделитель? Нет.
  • Почему указывать UTF-8 явно? Не зависеть от окружения и зафиксировать формат.
  • Чем опасен PrintWriter? Может скрывать IOException; проверять checkError.