Предопределенные потоки ввода-вывода в Java: System.in, System.out, System.err - Вопросы

Всего: 5 вопросов

1. 

Что такое предопределенные потоки ввода-вывода в Java: System.in, System.out и System.err?

Это три готовых потока, которые JVM создаёт при запуске программы и хранит в полях public static final класса java.lang.System. Пакет java.lang импортируется автоматически, поэтому потоки доступны без import и без создания объектов.

  • System.in — стандартный ввод, тип InputStream (байтовый поток), дескриптор ОС 0, по умолчанию — клавиатура.
  • System.out — стандартный вывод, тип PrintStream, дескриптор 1, по умолчанию — экран терминала.
  • System.err — стандартный поток ошибок, тоже PrintStream, дескриптор 2, по умолчанию — экран терминала.

Связь с терминалом не жёсткая: оболочка может перенаправить любой из потоков в файл (<, >, 2>), а программа — заменить их методами System.setIn(), setOut(), setErr().

2. 

Чем System.err отличается от System.out и когда его использовать?

Оба поля — объекты PrintStream с одинаковыми методами (print(), println(), printf()). Разница в канале ОС: System.out пишет в стандартный вывод (дескриптор 1), System.err — в стандартный поток ошибок (дескриптор 2).

  • Ошибки можно отделить от полезного вывода: при запуске java App > out.txt обычный вывод уходит в файл, а сообщения System.err остаются на экране; 2> err.txt отправляет ошибки в отдельный файл, > all.txt 2>&1 — оба потока в один.
  • В System.err пишет e.printStackTrace() без аргументов, поэтому трассировки в IDE подсвечены красным.
  • Так как это два независимых канала, в консоли IDE строки out и err могут перемешаться; если важен порядок, пишите в один поток.

В реальных приложениях вместо прямой печати в System.err используют логгер (java.util.logging, SLF4J + Logback).

3. 

Почему System.in.read() на ввод 5 возвращает 53 и как правильно прочитать число с консоли?

System.in — байтовый поток (InputStream). Метод read() возвращает один байт как int от 0 до 255 или -1 в конце ввода. 53 — код символа '5', а следующий вызов вернёт 10 (перевод строки от Enter; в Windows первым придёт 13). Вызов блокирует программу, пока пользователь не нажмёт Enter.

Чтобы прочитать число, читают строку и разбирают её:

BufferedReader console = new BufferedReader(new InputStreamReader(System.in));
int n = Integer.parseInt(console.readLine().trim());
  • InputStreamReader переводит байты в символы, BufferedReader даёт readLine().
  • Нечисловой ввод даёт NumberFormatException; конец ввода (Ctrl+D в Linux/macOS, Ctrl+Z и Enter в Windows) — readLine() вернёт null.
  • Обёртку вокруг System.in не закрывают: вместе с ней закроется сам System.in, и открыть его заново нельзя.
4. 

Как перенаправить стандартные потоки из программы с помощью System.setIn(), System.setOut() и System.setErr()?

Статические методы System.setIn(InputStream), setOut(PrintStream) и setErr(PrintStream) заменяют стандартные потоки: после вызова весь код, который пишет в System.out или читает System.in, работает с новым потоком — файлом или буфером в памяти.

  • setIn(new ByteArrayInputStream("42\n".getBytes())) подставляет заготовленный ввод в тесте.
  • setOut(new PrintStream(buffer, true)) с ByteArrayOutputStream перехватывает вывод метода, который печатает результат вместо возврата.
  • setErr(...) собирает сообщения об ошибках и трассировки в файл журнала.

Поля in, out, err объявлены final, поэтому System.out = ... не компилируется; методы setXxx() меняют их через нативный код JVM. Исходный поток нужно сохранить и вернуть в блоке finally, иначе весь последующий вывод программы и других тестов уйдёт в чужой поток.

5. 

Почему System.out.println() не выбрасывает IOException и почему он может тормозить программу в больших циклах?

Методы PrintStream (print(), println(), printf()) не выбрасывают IOException: ошибка записи только выставляет внутренний флаг. Узнать о сбое можно единственным способом — вызвать checkError(), который сбрасывает буфер и возвращает true, если ошибка была. Поэтому, например, после System.out.close() весь дальнейший вывод молча пропадает.

JVM создаёт System.out с автоматическим сбросом буфера (autoflush): текст появляется в консоли сразу после println(), без flush(). Обратная сторона — каждый вызов обращается к ОС, а методы PrintStream ещё и синхронизированы, так что миллион вызовов println() может занимать секунды. Для большого вывода текст собирают в StringBuilder и печатают один раз или пишут через new PrintWriter(new BufferedWriter(new OutputStreamWriter(System.out))) с одним flush() в конце.

Страница 1 из 1