InputStream inputStream = System.in;
Reader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
В первой строке мы присваиваем в переменную inputStream объект класса InputStream, ссылка на который содержится в System.in. Класс InputStream используется в Java как
байтовый поток для ввода данных в вашу программу. Так как мы работаем с объектом, на который ссылается System.in - это значит, что мы будем иметь дело со стандартным потоком, который будет читать входные данные, которые вводятся в консоль с клавиатуры.
Далее во второй строке создается и присваивается в переменную объект типа InputStreamReader.
InputStreamReader - это
переходник между байтовыми и символьными потоками, который получает как аргумент байтовый поток (в нашем случае inputStream) и преобразует входящие в него байты в символы, которые затем будут переданы в символьный поток.
В третьей строке создается объект класса BufferedReader. BufferedReader это подкласс класса Reader, который используется в Java, как
символьный поток для ввода данных. BufferedReader отличается от других подклассов Reader, тем что он может зачитывать не только одиночные символы, но и сразу строки символов за один раз. BufferedReader при создании требует объект любого символьного потока в качестве аргумента из которого будет зачитывать потом данные. Так как наш inputStream является байтовым потоком, то он не может быть передан в BufferedReader как аргумент. Для этого как раз и существуют объекты-заглушки типа InputStreamReader. Такой объект передается в BufferedReader, как аргумент. В то же время InputStreamReader инкапсулирует в себе объект нашего байтового потока inputStream, который мы передали ему при создании во второй строке.
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
В этой строке происходит все то же самое, только в сокращенной форме. Такой стиль наиболее применим при написании кода, так как он более читабельный и компактный, чем пример, приведенный выше.