PrintStream это класс из пакета java.io, который печатает данные в поток вывода. Именно его объектом является System.out, поэтому привычный System.out.println() это вызов метода PrintStream. Тот же класс умеет писать не в консоль, а в файл: путь или объект File передают прямо в конструктор. Кроме print() и println() у него есть printf() для форматированного вывода.

Кратко

  • System.out это объект PrintStream, поэтому System.out.println() это метод именно данного класса.
  • В конструктор можно передать OutputStream, объект File или строку с путем к файлу, и тогда вывод пойдет в файл, а не в консоль.
  • printf() печатает форматированную строку: %s подставит строку, %d целое число, %b значение boolean.
  • Шаблон для printf() удобно вынести в константу и менять только подставляемые значения.
  • System.in это InputStream, по умолчанию привязанный к клавиатуре.
  • Источник ввода можно подменить: обернуть данные в ByteArrayInputStream и передать в System.setIn(). Присвоить System.in напрямую нельзя, поле объявлено final.
Привет! Сегодня поговорим о классе PrintStream и всем, что он умеет делать. Печатный станок в работе: класс PrintStream отвечает за печать данных в потокСобственно, с двумя методами класса PrintStream ты уже знаком. Это методы print() и println(), которыми ты пользуешься, наверное, каждый день :) Поскольку переменная System.out является объектом PrintStream, вызывая метод System.out.println(), ты вызываешь метод именно этого класса. Общее назначение класса PrintStream это вывод информации в какой-то поток.

Конструкторы PrintStream: вывод в файл

У данного класса есть несколько конструкторов. Вот несколько наиболее распространенных:
  • PrintStream(OutputStream outputStream)
  • PrintStream(File outputFile) throws FileNotFoundException
  • PrintStream(String outputFileName) throws FileNotFoundException
Как видишь, мы можем передать в конструктор объекта PrintStream, например, название файла, в который необходимо вывести данные. Или же, как альтернативу, сам объект File. Начиная с Java 10 у тех же конструкторов есть варианты с явным указанием кодировки, например PrintStream(String fileName, Charset charset). Держи их в уме, если пишешь в файл не только латиницу: до Java 18 кодировкой по умолчанию была системная, а не UTF-8. Давай рассмотрим, как это работает на примерах:
import java.io.File;
import java.io.FileNotFoundException;
import java.io.PrintStream;

public class Main {

   public static void main(String arr[]) throws FileNotFoundException
   {
       PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));

       filePrintStream.println(222);
       filePrintStream.println("Hello world");
       filePrintStream.println(false);
   }
}
Этот код создаст на рабочем столе файл test.txt (если он там еще не существует) и запишет туда последовательно наши число, строку и boolean-переменную. Вот содержимое нашего файла после работы программы:

222
Hello world
false
Как мы и сказали выше, не обязательно передавать сам объект файла File. Достаточно просто указать в конструкторе путь к нему:
import java.io.FileNotFoundException;
import java.io.PrintStream;

public class Main {

   public static void main(String arr[]) throws FileNotFoundException
   {
       PrintStream filePrintStream = new PrintStream("C:\\Users\\Username\\Desktop\\test.txt");

       filePrintStream.println(222);
       filePrintStream.println("Hello world");
       filePrintStream.println(false);
   }
}
Этот код сделает то же, что и предыдущий.

Метод printf(): форматированный вывод

Еще один интересный метод, на который стоит обратить внимание, это printf(), или вывод форматированной строки. Что значит «форматированная строка»? Чтобы объяснить, приведу пример:
import java.io.IOException;
import java.io.PrintStream;

public class Main {

   public static void main(String[] args) throws IOException {

       PrintStream printStream = new PrintStream("C:\\Users\\Евгений\\Desktop\\test.txt");

       printStream.println("Hello!");
       printStream.println("I'm robot!");

       printStream.printf("My name is %s, my age is %d!", "Amigo", 18);

       printStream.close();

   }
}
Здесь вместо того, чтобы явно записывать в строке имя и возраст нашего робота, мы как бы оставляем для этой информации «свободное место» с помощью указателей %s и %d. А те данные, которые должны оказаться в этих местах, мы передаем в качестве параметров. В нашем случае это строка «Amigo» и число 18. Мы могли бы, например, создать еще одно пространство: скажем, %b, и передать еще один параметр. Для чего это нужно? Прежде всего, для повышения гибкости. Если в твоей программе необходимо будет часто выводить приветственное сообщение, для каждого нового робота тебе придется вручную набирать нужный текст. Ты не сможешь даже вынести этот текст в константу: имена и возраст-то у всех разные! Но используя новый метод, ты можешь вынести строку с приветствием в константу, а при необходимости просто менять параметры в методе printf().
import java.io.IOException;
import java.io.PrintStream;

public class Main {

   private static final String GREETINGS_MESSAGE = "My name is %s, my age is %d!";

   public static void main(String[] args) throws IOException {

       PrintStream printStream = new PrintStream("C:\\Users\\Евгений\\Desktop\\test.txt");

       printStream.println("Hello!");
       printStream.println("We are robots!");

       printStream.printf(GREETINGS_MESSAGE, "Amigo", 18);
       printStream.printf(GREETINGS_MESSAGE, "R2-D2", 35);
       printStream.printf(GREETINGS_MESSAGE, "C-3PO", 35);

       printStream.close();
   }
}

Подмена System.in

В этой лекции мы будем «бороться с системой» и научимся подменять переменную System.in и перенаправлять системный ввод в нужное нам место. Деревянный указатель-стрелка на фоне заснеженных гор: System.in можно перенаправить на другой источник данныхТы мог подзабыть, что такое System.in, но эту конструкцию ни один студент JavaRush не забудет никогда:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
System.in (как и System.out) это статическая переменная класса System. Но, в отличие от System.out, она относится к другому классу, а именно к InputStream. По умолчанию System.in это поток, считывающий данные с системного устройства, с клавиатуры. Однако, как и в случае с System.out, мы можем подменить источник данных, и чтение будет происходить не с клавиатуры, а из нужного нам места! Давай рассмотрим пример:
import java.io.*;

public class Main {

   public static void main(String[] args) throws IOException {

       String greetings = "Привет! Меня зовут Амиго!\nЯ изучаю Java на сайте JavaRush.\nОднажды я стану крутым программистом!\n";
       byte[] bytes = greetings.getBytes();

       InputStream inputStream = new ByteArrayInputStream(bytes);

       System.setIn(inputStream);

       BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

       String str;

       while ((str = reader.readLine())!= null) {

           System.out.println(str);
       }

   }
}
Итак, что же мы сделали? Обычно System.in «привязана» к клавиатуре. Но мы не хотим, чтобы данные читались с клавиатуры: пусть читаются из обычной строки с текстом! Мы создали строку, и получили ее в виде массива байт. Зачем нам байты? Дело в том, что InputStream это абстрактный класс, и мы не можем создать его экземпляр. Придется выбрать какой-то класс из числа его наследников. Для примера можем взять ByteArrayInputStream. Он простой, и по одному названию понятно, как он работает: источником данных для него является массив байт. Так что мы создаем этот самый массив байт и передаем в конструктор нашему stream’у, который будет читать данные. По сути, все уже готово! Теперь нам достаточно использовать метод System.setIn(), чтобы явно установить значение переменной in. В случае с out, как ты помнишь, установить значение переменной явно тоже было нельзя: приходилось использовать специальный метод setOut(). После того, как мы присвоили созданный нами InputStream переменной System.in, надо проверить, сработала ли наша задумка. В этом нам поможет старый знакомый BufferedReader. В обычной ситуации этот код привел бы к тому, что у тебя в IntelliJ IDEA открылась бы консоль, и оттуда считывались данные, которые ты вводил с клавиатуры.
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));

       String str;

       while ((str = reader.readLine())!= null) {

           System.out.println(str);
       }
Но, запустив его теперь, ты увидишь, что в консоль просто выведется наш текст из программы, никакого считывания с клавиатуры не будет. Мы подменили источник данных, теперь им является не клавиатура, а наша строка! Вот так легко и просто :) В сегодняшней лекции мы познакомились с новым классом и рассмотрели новый небольшой «хак» по работе с вводом-выводом. Самое время вернуться к курсу и решить несколько задач :) Увидимся на следующей лекции!

Вопросы и ответы

Почему System.out.println() это метод класса PrintStream?

System.out это статическая переменная класса System, и ее тип как раз PrintStream. Вызывая System.out.println(), ты вызываешь метод объекта PrintStream, который привязан к консоли.

Как вывести данные в файл с помощью PrintStream?

Передать в конструктор объект File или строку с путем к файлу. После этого println() и printf() пишут не в консоль, а в этот файл. Если файла еще нет, он будет создан.

Что означают %s и %d в printf()?

Это места для подстановки значений: %s подставит строку, %d целое число, %b значение boolean. Сами значения передают следующими аргументами метода, в том же порядке, в каком идут указатели.

Нужно ли закрывать PrintStream?

Поток, открытый на файл, закрывать нужно, иначе останется занятый дескриптор. Для этого вызывают close() или, что удобнее, создают поток в конструкции try-with-resources. Поток System.out закрывать не надо.

Зачем подменять System.in?

Чтобы программа читала данные не с клавиатуры, а из заранее подготовленного источника. Это удобно для тестов и автопроверки: не нужно каждый раз вручную вводить строки в консоль.

Чем System.in отличается от System.out?

Обе переменные статические и принадлежат классу System, но System.out это PrintStream для вывода, а System.in это InputStream для ввода. Присвоить им значение напрямую нельзя, поля объявлены final, поэтому используют методы setOut() и setIn(). Источник: Oracle: класс PrintStream.

Читайте также