JavaRush /Java блог /Java Developer /Для чего нужен класс PrintStream
Автор
Василий Малик
Senior Java-разработчик в CodeGym

Для чего нужен класс PrintStream

Статья из группы Java Developer
Привет! Сегодня поговорим о классе PrintStream и всем, что он умеет делать. Для чего нужен класс PrintStream - 1Собственно, с двумя методами класса PrintStream ты уже знаком. Это методы print() и println(), которыми ты пользуешься, наверное, каждый день :) Поскольку переменная System.out является объектом PrintStream, вызывая метод System.out.println(), ты вызываешь метод именно этого класса. Общее назначение класса PrintStream — вывод информации в какой-то поток. У данного класса есть несколько конструкторов. Вот несколько наиболее распространенных:
  • PrintStream(OutputStream outputStream)
  • PrintStream(File outputFile) throws FileNotFoundException
  • PrintStream(String outputFileName) throws FileNotFoundException
Как видишь, мы можем передать в конструктор объекта PrintStream, например, название файла, в который необходимо вывести данные. Или же, как альтернативу, сам объект File. Давай рассмотрим, как это работает на примерах:

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(), или вывод форматированной строки. Что значит «форматированная строка»? Чтобы объяснить, приведу пример:

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 и перенаправлять системный вывод в нужное нам место. Для чего нужен класс PrintStream - 2Ты мог подзабыть что такое 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);
       }
Но запустив его теперь ты увидишь, что в консоль просто выведется наш текст из программы, никакого считывания с клавиатуры не будет. Мы подменили источник данных, теперь им является не клавиатура, а наша строка! Вот так легко и просто :) В сегодняшней лекции мы познакомились с новым классом и рассмотрели новый небольшой «хак» по работе с вводом-выводом. Самое время вернуться к курсу и решить несколько задач :) Увидимся на следующей лекции!
Комментарии (153)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Anatoly Уровень 30
16 февраля 2024
Многое прояснилось!
Arturka_tx Уровень 30
1 февраля 2024
Может кому поможет лучше понять, для примера я взял класс Scanner который принимает System.in, но поскольку он подменен, от нас не требуется ввод с клавиатуры, а просто выводится изначально заданный текст

    public static void main(String[] args) throws IOException {
        String text = "Hello, how are you?";
        byte[] allBytes = text.getBytes();

        InputStream inputStream = new ByteArrayInputStream(allBytes);

        System.setIn(inputStream);

        Scanner scanner = new Scanner(System.in);

        System.out.println(scanner.nextLine());
    }
Dmitry Vidonov Уровень 29 Expert
5 октября 2023
Хорошая лекция. Подмена переменной In в System оказалась очень простой. Просто передали в нее поток и все) Вообще только на этой лекции понял нормально смысл метода getBytes и что на самом деле удобная штука для потоков получается!
Rustam Уровень 35 Student
16 августа 2023
Тяжело пока понять про подмену System.in
Ислам Уровень 33
2 июля 2023
Nice
Ислам Уровень 33
2 июля 2023
Хорошая лекция
Murat Уровень 51
11 апреля 2023
Чётко
Gans Electro Уровень 50
27 марта 2023
Если стерли изначальную System.in. Просто перезагрузите программу. Или передайте самый примитивный поток читающие байты или вот что у меня по дефолту

InputStream s = System.in;
System.out.println(s.getClass());

Вывод:
class java.io.BufferedInputStream
Alexei Уровень 42
11 января 2023
🙃
25 декабря 2022
Я ведь не один до сих пор всегда когда нужно ввести что-нибудь с консоли использую Scanner? В этом ведь нет ничего плохого? Я просто не понимаю зачем писать больше кода 🤔