Раньше у различных закрытых обществ, были распространены особые виды приветствия, чтобы узнавать друг друга не привлекая на себя внимания. По какому приветствию или паролю, один ученик джава раша может узнать другого ученика? Я думаю, наиболее вероятный кандидат, это фраза-пароль:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
Я предлагаю рассмотреть что происходит при считывании информации с помощью этой конструкции. Итак, дана задача, ввести с клавиатуры число, наименование валюты и вывести "25 USD" к примеру. Программа будет выглядеть так:
public static void main(String[] args) throws IOException {
    BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
    String currency = reader.readLine();  //запрос ввода с клавиатуры строки
    String amount = reader.readLine();    //запрос ввода с клавиатуры еще одной строки
    int number = Integer.parseInt(amount);  // переводим с типа Строка (String) в тип Число (int)
    System.out.println(number + " " + currency);  //вывод на экран
}
Разберем конструкцию, с помощью которой мы вводим данные с клавиатуры. По сути происходит матрешка: 1. System.in, это поток считывания с клавиатуры, он считывает в байтах. оборачиваем ее в 2. InputStreamReader, это адаптер между символьными потоками и байтовыми. InputStreamReader и символьные потоки мы используем потому, что не хотим работать с байтами, а с символами, ведь если мы введем "Name" мы хотим работать с Name, а не 78 97 109 101 (набор байт N a m e). Далее мы InputStreamReader оборачиваем в 3. BufferedReader это обертка, которая содержит буфер экономящий системные ресурсы (он считывает по 8192 байта за раз. Если мы в цикле пропишем считывание 8192 символов по одной штуке, то БЕЗ BufferedReader это будет 8192 обращения к системному ресурсу/файловой системе, это ударит по производительности. А используя BufferedReader, физически обращение к файловой системе будет 1 (один) раз, а мы 8192 раз будем читать не с файла непосредственно или с ресурса, а с буфера. Если вы только начали изучение джавы, советую просто использовать эту конструкцию AS-IS. Набрали ее, вы знаете, что у нее есть метод .readLine() который читает строку с консоли, потом это все будет объяснятся. Но можем и рассмотреть детальней. 1. System.in; Конструкцию можно записать и так:
InputStream inputStream = System.in;
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
Данный пример для наглядности, чтобы увидеть чем является System.in, это переменная. Строка
InputStream inputStream = System.in;
говорит нам о том, что есть статическая переменная in, которая находится в классе System, и эта переменная содержит ссылку на объект, который является системным ресурсом (потоком - stream), который связывает поток данных с клавиатуры через операционную систему и JVM и процессор. Эта переменная типа InputStream, и имеет соответствующие методы (как мы помним, доступные методы у объекта определяются не типом фактического объекта, а типом ссылочной переменной), поскольку System.in содержит ссылку на объект, который является экземпляром класса, наследующего InputStream. 2. Класс InputStreamReader НЕ является наследником класса InputStream. InputStreamReader наследник класса Reader. Есть две отдельные большие иерархии входных потоков: байтовые потоки (InputStream и его наследники) и символьные потоки (Reader и его наследники). Разделение понятно, если нас интересует байты сплошняком, передача данных потоком, работа с файлами целиком, мы работаем с байтами и используем наследников InputStream. Если нас интересует работа с символами, текстом, чарами и тд, мы используем Reader и его наследников. 3. System.in - это переменная класса System с именем in или вместе, System.in. Это переменная, которой при старте виртуальной машины Java, по умолчанию присваивается поток данных с консоли (клавиатуры). Это особый системный ресурс, который к слову можно закрыть только один раз за исполнение программы, второй раз открыть его не получится (поскольку он открывается особым способом самой виртуальной машиной). Если говорить о файлах, то с них считывание идет через другие потоки. 4. Символы: буквы, цифры и прочие знаки, имеют свое отображение в коде: они хранятся и передаются системой в виде последовательности байт. Какое именно значение для какого символа зависит от кодировок. Например, если мы считаем с файла в формате UTF-16, кириллическую А, это будет два байта, 208 и 144 (unsigned byte), которые вместе образуют русское А. Если мы считаем с потока непосредственно наследником суперкласса Reader, например InputStreamReader, то этот класс под капотом содержит логику преобразования входящих данных по стандартной кодировке, которая стоит на платформе (ОС) по умолчанию. Но можно использовать и перегруженный конструктор и прямо указать в какой кодировке мы хотим читать. У меня стоит UTF, поэтому когда я ввожу кириллическую А: в системную переменную System.in попадает через ОС два байта, 208 и 144 (unsigned), потом InputStreamReader это все дело преобразовывает используя системные настройки, у меня UTF, значит получается char с кодом 1040, который соответствует по таблице Юникод букве А. Есть еще BufferedReader это обертка, которая под капотом содержит буфер (8192 символа), и загоняет все в буфер, чтобы не нужно было за каждой буквой гонять системные ресурсы (консоль или файловую систему). То есть и без BufferedReader, можно спокойно читать символы с потока ввода. Просто по одному. bufferedReader.readLine(); - этот код исполняется так: если в буфере ничего нет, тогда запросить информацию с потока (в данном случае InputStreamReader). Но если бы там был файл, даже подав запрос на чтение одной строки, буффередРидер бы скачал 8192 символов. И при каждом вызове readLine() не было бы обращения к файлу, он просто отдавал бы данные с буфера, пока буфер не опустошиться, как только это произойдет, он пошлет гонца в файловый поток, чтобы выкачать еще 8192 символов, поэтому этот класс и называется Буферризованный ридер. Если говорить про другие варианты кодировок, то кириллическая буква А в: KOI8-R - 225 CP 855 - 161 Windows-1251 - 192 ISO-8859-5 - 176 и тд. 5. unsigned byte я подчеркнул, поскольку в джава на уровне языка реализован только signed byte , с диапазоном -128..127. Но так как по ряду причин это не совсем удобно, то ряд методов возвращает int, в котором содержится число, соответствующее signed byte, с диапазоном 0..255 или -1, если поток вычитан. Причина проста, метод должен возвращать 256 значений (диапазон байта) + 1 сверху (отсутствие значение, поток вычитан), поэтому пришлось использовать другой подход. Также хочу обратить внимание на правило работы с потоками/ресурсами: "[без явных, прямых и четких указаний] мы не закрываем те ресурсы, которые не открывали" Если в условиях задачи джава раш требуется "закрыть поток А, Б и В" безусловно, закрывайте. Вам дали прямое и четкое указание. Если вы работаете сами, четких указаний нету, и вам попал в руки поток, который вы не открывали, то вы его и не закрываете, как правило. Также хотел бы привести ссылки на интересные материалы по теме: - https://javarush.com/groups/posts/2139-pattern-proektirovanija-adapter Здесь рассматривается, что за зверь InputStreamReader как частный случай реализации паттерна Адаптер - https://betacode.net/13361/java-bufferedreader статья по BufferedReader с картинками, как происходит считывание с файла и с буфера - Java_io_Writer_Reader.pdf java_IO_input.pdf эти две крутые схемы сделал пользователь Anonymous #2297535 и оставил комментом к квесту Java Core п.с. По любым возникающим вопросам не забывайте использовать раздел Помощь, там можно задавать вопросы по любым моментам, связанными с Java, не обязательно именно по конкретной задаче.