1. Обзор
FileReader
и BufferedReader
— два класса, которые могут считывать символы из входного потока.
В этом туториале мы с вами рассмотрим различия между ними.
2. FileReader
Класс FileReader предназначен для чтения символьного потока из файла. К сожалению, он может читать файл только посимвольно, и каждый раз, когда мы вызываем его метод read()
, он напрямую обращается к файлу на жестком диске, чтобы прочитать из него ровно один символ. В результате FileReader
— очень медленный и неэффективный способ чтения символов из файлов. Кроме того, FileReader
не может читать символы ни из каких типов потоков входных данных, кроме файлов.
2.1. Конструкторы
FileReader
имеет три конструктора:
FileReader(File file)
: получает в качестве аргумента экземпляр файлаFileReader(FileDescriptor fd)
: получает в качестве аргумента дескриптор файлаFileReader(String fileName)
: получает в качестве аргумента имя файла (включая его путь)
2.2. Что он возвращает
Каждый раз, когда мы вызываем метод read()
, он возвращает целое число, представляющее значение символа в формате Unicode, который был прочитан из файла, или -1, если достигнут конец символьного потока.
2.3. Пример
Давайте посмотрим на использование FileReader
в коде примера, в котором мы будем считывать символы из текстового файла, содержащего всего одну строку “qwerty”:
@Test
public void whenReadingAFile_thenReadsCharByChar() {
StringBuilder result = new StringBuilder();
try (FileReader fr = new FileReader("src/test/resources/sampleText2.txt")) {
int i = fr.read();
while(i != -1) {
result.append((char)i);
i = fr.read();
}
} catch (IOException e) {
e.printStackTrace();
}
assertEquals("qwerty", result.toString());
}
В приведенном выше коде мы приводим возвращаемое значение из метода read()
к типу char, а затем добавляем его в строку результата.
3. BufferedReader
Класс BufferedReader создает буфер для хранения данных из символьного потока. Более того, входной поток может быть файлом, консолью, строкой или любым другим типом символьного потока.
Его конструктор требует в качестве входного символьного потока объект типа Reader
. Следовательно, мы можем передать в качестве входного потока для чтения символов в BufferedReader
любой класс, реализующий абстрактный класс Reader
.
Когда мы начинаем читать символы с помощью BufferedReader
, он считывает весь блок данных из входного потока и сохраняет его в буфере. После этого, если мы продолжим чтение с помощью BufferedReader
, он будет возвращать символы не из исходного потока символов, а из буфера, пока полностью не опустеет. Затем он считает следующий блок данных из входного потока и сохранит его в буфере для дальнейших вызовов операций чтения.
Класс BufferedReader
уменьшает количество операций чтения, вызываемых над входным потоком, а чтение из буфера обычно происходит намного быстрее, чем доступ к базовому входному потоку. Поэтому BufferedReader
обеспечивает более быстрый и эффективный способ чтения символов из входного потока.
3.1. Конструкторы
BufferedReader
имеет два конструктора:
BufferedReader(Reader in)
: в качестве аргумента получает поток символьных входных данных (который должен реализовывать абстрактный классReader
)BufferedReader(Reader in, int sz)
: в качестве аргументов получает символьный поток и размер буфера
3.2. Что он возвращает
Если мы вызовем метод read()
, он возвращает целое число, представляющее значение символа в формате Unicode, который был прочитан из входного потока. Более того, если мы вызовем метод readLine()
, он считает сразу целую строку из буфера и вернет ее как string
.
3.3. Пример
Давайте посмотрим на пример использования BufferedReader
для чтения символов из текстового файла, содержащего три строки:
@Test
public void whenReadingAFile_thenReadsLineByLine() {
StringBuilder result = new StringBuilder();
try (BufferedReader br = new BufferedReader(new FileReader("src/test/resources/sampleText1.txt"))) {
String line;
while((line = br.readLine()) != null) {
result.append(line);
result.append('\n');
}
} catch (IOException e) {
e.printStackTrace();
}
assertEquals("first line\nsecond line\nthird line\n", result.toString());
}
Приведенный выше тестовый код выполняется без ошибок, а это означает, что BufferedReader
успешно читает все три строки текста из файла.
4. В чем разница?
BufferedReader
гораздо быстрее и эффективнее, чем FileReader
поскольку он считывает целый блок данных из входного потока и сохраняет его в буфере для дальнейших вызовов метода чтения, в то время как FileReader
должен получать доступ к файлу для считывания каждого символа. Более того, FileReader
может читать файл только посимвольно, в то время как BufferedReader
имеет такие полезные методы, как readLine()
, который может считать из буфера сразу целую строку. И наконец, FileReader
может производить чтение только из файла, а BufferedReader
может читать из любого типа потока символьных входных данных (файл, консоль, строка и т.д.):
FileReader | BufferedReader |
Медленнее и менее эффективен | Быстрее и эффективнее |
Может читать только посимвольно | Может читать символы и строки |
Можно читать только из файла | Может читать из любого символьного потока |
Вам может быть достаточно и FileReader
, если вы читаете из небольших файлов, или когда вызовов операции чтения не очень много. Однако для больших файлов или когда нужно выполнять много операций чтения данных, BufferedReader
— это лучший вариант.
5. Заключение
В этом туториале мы с вами рассмотрели различия между FileReader
и BufferedReader
и когда их следует применять.
Как всегда, полный исходный код туториала доступен на GitHub.
Уже сегодня вечером состоится открытое занятие, на котором познакомимся с Reactor Kafka. На этом уроке посмотрим, как в java-приложении можно работать с Kafka в реактивном стиле. Разберемся, для чего это может быть полезно и когда стоит использовать.
Зарегистрироваться на открытое занятие можно на странице курса "Java Developer Professional".