Метод BufferedReader.ready() гарантирует, что метод readLine() не возвращает NULL?

У меня есть такой код для чтения текстового файла с помощью BufferedReader:

BufferedReader reader=null;
    try {
        reader = new BufferedReader(new FileReader("file1.txt"));

        while (reader.ready()) {
            final String line = reader.readLine();
            System.out.println("<"+line+">");
        } catch (..)
    {
        ...
    }

Он работает правильно, но Findbugs сообщает о предупреждении:

NP_DEREFERENCE_OF_READLINE_VALUE: результатом вызова readLine() является разыменован без проверки, чтобы видеть если результат равен нулю. Если нет больше строк текста для чтения, readLine() вернет null и разыменовывает который будет генерировать нулевой указатель исключение.

Когда я меняю FileReader на StringReader, т.е.

BufferedReader reader=null;
    try {
        reader = new BufferedReader(new StringReader("ABCD"));

        while (reader.ready()) {
            final String line = reader.readLine();
            System.out.println("<"+line+">");
        } catch (..)
    {
        ...
    }

метод readLine возвращает null, в то время как метод ready всегда возвращает true - действительно, это бесконечный цикл.

Кажется, что readLine может возвращать null, даже если ready возвращает true. Но почему поведение отличается для разных Reader s?

UPDATE:

Я знаю нормальный способ чтения текстового файла (как показано на примере Питера и Али). но я прочитал этот фрагмент кода от своего коллеги и понял, что не знаю метода ready. Затем я прочитал JavaDoc, но не понимаю block. Затем я сделал тест и разместил этот вопрос. Итак, лучший способ задать этот вопрос:

Когда будет блокироваться вход? Как использовать метод ready (или почему бы не использовать его)? Почему те 2 Reader (FileReader и StringReader) ведут себя по-разному относительно метода ready?

Ответ 1

Готовый метод сообщает нам, готов ли поток к чтению.

Представьте, что ваш поток считывает данные из сетевого сокета. В этом случае поток может не закончиться, потому что сокет не был закрыт, но он может быть не готов к следующему фрагменту данных, потому что другой конец сокета не вытолкнул больше данных.

В приведенном выше сценарии мы не можем читать больше данных, пока удаленный конец не подтолкнет его, поэтому нам нужно дождаться, когда данные станут доступными, или для закрытия сокета. Метод ready() сообщает нам, когда данные доступны.

Ответ 2

Reader.ready() и InputStream.available() редко работают так, как вам может быть, и я не предлагаю вам их использовать. Чтобы прочитать файл, вы должны использовать

String line;
while ((line = reader.readLine()) != null)
    System.out.println("<"+line+">");

Ответ 3

Вот что должны сказать Javadocs:

Сообщает, готов ли этот поток к чтению. Буферизованный поток символов готов, если буфер не пуст, или если базовый поток символов готов.

Итак, BufferedReader считается готовым просто, если базовый поток также готов. Поскольку BufferedReader является оболочкой, этот базовый поток может быть любой реализацией Reader; поэтому семантикой ready() являются те, которые объявлены на интерфейсе:

Возвращает true, если следующий read() гарантированно не блокирует ввод, иначе false. Обратите внимание, что возврат false не гарантирует, что следующее чтение будет заблокировано.

Таким образом, вы действительно получаете гарантии времени, т.е. что read() не будет блокироваться. Результат вызова ready() абсолютно ничего не сообщает о содержимом, которое вы получите от вызова read(), и поэтому не может использоваться для исключения нулевой проверки.

Ответ 4

Посмотрите API для готовых.

То, что вы делаете, неверно. ready() сообщает только, является ли поток доступным и допустимым. Прочитайте комментарий по ссылке на эту ссылку.

Что вы хотите сделать:

String thisLine;

//Loop across the arguments
for (int i=0; i < args.length; i++) {

  //Open the file for reading
  try {
    BufferedReader br = new BufferedReader(new FileReader(args[i]));
    while ((thisLine = br.readLine()) != null) { // while loop begins here
      System.out.println(thisLine);
    } // end while 
  } // end try
  catch (IOException e) {
    System.err.println("Error: " + e);
  }
} // end for