Привет!
Сегодняшняя лекция будет разделена на две условные части. Мы повторим кое-что из старых тем, которых уже касались ранее, и рассмотрим некоторые новые фичи :)
Начнем с первого. Повторение — мать учения :)
Ты уже не раз пользовался таким классом как
Разумеется,
Итак, что нам для этого нужно?
Во-первых, нужен новый объект класса

BufferedReader
. Эту команду, надеюсь, ты еще не успел забыть:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Прежде чем читать дальше, попробуй вспомнить за что отвечает каждая составляющая (System.in, InputStreamReader, BufferedReader
) и для чего они нужны.
Получилось?
Если нет — не страшно :) Если к этому моменту ты о чем-то позабыл, перечитай еще раз эту лекцию, посвященную ридерам.
Вкратце вспомним, что умеет каждый из них.
System.in
— это поток для получения данных с клавиатуры. В принципе, чтобы реализовать логику чтения текста, нам хватило бы и его одного. Но, как ты помнишь, System.in
умеет считывать только байты, а не символы:
public class Main {
public static void main(String[] args) throws IOException {
while (true) {
int x = System.in.read();
System.out.println(x);
}
}
}
Если мы выполним этот код и введем в консоли букву «Й», вывод будет таким:
Й
208
153
10
Символы кириллицы занимают в памяти 2 байта, которые и выводятся на экран (а число 10 — это байтовое представление переноса строки, т.е. нажатия Enter).
Чтение байтов — такое себе удовольствие, поэтому использовать System.in
в чистом виде будет неудобно. Для того, чтобы считывать понятные всем кириллические (и не только) буквы, мы используем InputStreamReader
в качестве обертки:
public class Main {
public static void main(String[] args) throws IOException {
InputStreamReader reader = new InputStreamReader(System.in);
while (true) {
int x = reader.read();
System.out.println(x);
}
}
}
Если мы введем в консоль ту же букву «Й», результат в этот раз будет иным:
Й
1049
10
InputStreamReader
преобразовал два считанных байта (208, 153) к единому числу 1049. Это и есть считывание по символам. 1049 соответствует букве «Й», в чем можно легко убедиться:
public class Main {
public static void main(String[] args) throws IOException {
char x = 1049;
System.out.println(x);
}
}
Вывод в консоль:
Й
Ну а что касается BufferedReader
’a (да и вообще — BufferedЧегоУгодно), буферизированные классы используются для оптимизации производительности.
Обращение к источнику данных (файлу, консоли, ресурсу в Сети) — достаточно дорогая в плане производительности операция. Поэтому чтобы сократить число таких обращений, BufferedReader
считывает и накапливает данные в специальном буфере, откуда потом мы можем их получить.
В результате количество обращений к источнику данных сокращается в разы или даже десятки раз!
Еще одна дополнительная фича BufferedReader
’a и его преимущество над обычным InputStreamReader
’ом — это крайне полезный метод readLine()
, который считывает данные целыми строками, а не отдельными числами. Это, конечно, сильно прибавляет удобства при реализации, например, большого текста.
Вот как будет выглядеть считывание строки:
public class Main {
public static void main(String[] args) throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String s = reader.readLine();
System.out.println("Пользователь ввел следующий текст:");
System.out.println(s);
reader.close();
}
}
BufferedReader+InputStreamReader работает быстрее, чем просто InputStreamReader
Пользователь ввел следующий текст:
BufferedReader+InputStreamReader работает быстрее, чем просто InputStreamReader

BufferedReader
— очень гибкий механизм, и позволяет работать не только с клавиатурой.
Считывать данные можно, например, напрямую из Сети, просто передав ридеру нужный URL:
public class URLReader {
public static void main(String[] args) throws Exception {
URL oracle = new URL("https://www.oracle.com/index.html");
BufferedReader in = new BufferedReader(
new InputStreamReader(oracle.openStream()));
String inputLine;
while ((inputLine = in.readLine()) != null)
System.out.println(inputLine);
in.close();
}
}
Можно считывать данные из файла, передав путь к нему:
public class Main {
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream = new FileInputStream("testFile.txt");
BufferedReader reader = new BufferedReader(new InputStreamReader(fileInputStream));
String str;
while ((str = reader.readLine()) != null) {
System.out.println (str);
}
reader.close();
}
}
Подмена System.out
Теперь давай рассмотрим одну интересную возможность, которую мы ранее не затрагивали. Как ты наверняка помнишь, в классеSystem
есть два статических поля — System.in
и System.out
.
Эти братья-близнецы являются объектами классов-потоков. System.in
— абстрактного класса InputStream
. А System.out
— класса PrintStream
.
Сейчас мы поговорим именно о System.out
. Если мы зайдем в исходный код класса System
, увидим вот что:
public final class System {
……………...
public final static PrintStream out = null;
…………
}
Итак, System.out
— просто обычная статическая переменная класса System
. Никакой магии в ней нет :)
Переменная out
относится к классу PrintStream
.
Вот интересный вопрос: а почему при выполнении кода System.out.println()
вывод производится именно в консоль, а не куда-то еще? И можно ли это как-то изменить?
Например, мы хотим считывать данные из консоли, и записывать их в текстовый файл. Можно ли как-то реализовать такую логику, не используя дополнительные классы ридеров и райтеров, а просто пользуясь System.out
?
Еще как можно :)
И хотя переменная System.out
обозначена модификатором final
, мы все равно можем это сделать!

PrintStream
вместо нынешнего. Текущий объект, установленный в классе System
по умолчанию, нам не подходит: он указывает на консоль. Надо создать новый, который будет указывать на текстовый файл в качестве «места назначения» для наших данных.
Во-вторых, нужно понять, как присвоить новое значение переменной System.out
. Просто так этого не сделать, ведь она помечена final
.
Начнем с конца.
В классе System
как раз есть нужный нам метод — setOut()
. Он принимает на вход объект PrintStream
и устанавливает его в качестве точки вывода. Как раз то, что нам нужно!
Осталось только создать объект PrintStream
.
Это сделать тоже несложно:
PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
Код целиком будет выглядеть так:
public class SystemRedirectService {
public static void main(String arr[]) throws FileNotFoundException
{
PrintStream filePrintStream = new PrintStream(new File("C:\\Users\\Username\\Desktop\\test.txt"));
/*Сохраним текущее значение System.out в отдельную переменную, чтобы потом
можно было переключиться обратно на вывод в консоль*/
PrintStream console = System.out;
// Присваиваем System.out новое значение
System.setOut(filePrintStream);
System.out.println("Эта строка будет записана в текстовый файл");
// Возвращаем System.out старое значение
System.setOut(console);
System.out.println("А эта строка - в консоль!");
}
}
В результате первая строка будет записана в текстовый файл, а вторая — выведена в консоль :)
Ты можешь скопировать этот код в свою IDE и запустить. Открыв текстовый файл, ты увидишь, что нужная строка успешно туда записалась :)
На этом лекция подходит к концу. Сегодня мы вспомнили, как работать с потоками и ридерами, восстановили в памяти, чем они отличаются друг от друга и узнали о новых возможностях System.out
, которой пользовались чуть ли не в каждом уроке :)
До встречи на следующих лекциях!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
SPRICH DEUTSCH DU....ну вы поняли.🤣