Варианты шаблона закрытия потока в Java

Я часто вижу следующий шаблон как в документации Java, так и в коде других людей при работе с потоками:

FileInputStream fis = null;
try {
  fis = new FileInputStream("some.file");
  // do something useful with fis
} finally {
  if (fis != null) {
    fis.close();
  }
}

Однако я лично предпочитаю другую модель:

FileInputStream fis = new FileInputStream("some.file");
try {
  // do something useful with fis
} finally {
  fis.close();
}

Мне нравится краткость последнего, и я думаю, что это правильно. Но прав ли я относительно правильности? Есть ли объективная причина предпочитать одно другому?

Обновить

Даже если я напишу примеры в значительной степени, как я бы написал такой код в реальной жизни, мой шаблон будет еще более кратким. Сравните подход документации:

public Object processFile(String fn) throws MyException {
  FileInputStream fis = null;
  try {
    fis = new FileInputStream(fn);
    return new Object(); // somehow derived from fis
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  } finally {
    if (fis != null) {
      IOUtils.closeQuietly(fis);
    }
  }
}

с моим подходом:

public Object processFile(String fn) throws MyException {
  try {
    FileInputStream fis = new FileInputStream(fn);
    try {
      return new Object(); // somehow derived from fis
    } finally {
      IOUtils.closeQuietly(fis);
    }
  } catch (FileNotFoundException e) {
    throw new MySubExceptionForNotFound(e);
  } catch (IOException e) {
    throw new MySubExceptionForIoError(e);
  }
}

Мой на одну строчку короче!:D

Интересно, что мой код не изменится, если вы решите использовать fis.close() вместо IOUtils.closeQuietly() пока "официальный" код вырастет еще на 4 строки.

3 ответа

Используйте try-with-resources:

try (FileInputStream fis = new FileInputStream("some.file")) {
    // some code here
}

Чтобы ответить на вопрос, почему рекомендуемый шаблон такой, какой он есть, рассмотрим тот факт, что new FileInputStream(String) выдает проверенное исключение, которое вы, вероятно, должны поймать;

FileInputStream fis = null;
try {
    fis = new FileInputStream("somefile.txt");
} catch (FileNotFoundException e) {
    return false;
} finally {
    if (fis != null) {
        fis.close();
    }
}

позволяет обрабатывать его в том же блоке try / catch, что и другие исключения, связанные с файлами. Это имеет краткость и уменьшает вложенные преимущества по сравнению со следующими:

try {
    FileInputStream fis = new FileInputStream("some.file");
    try {
        // do something useful with fis
    } finally {
        fis.close();
    }
} catch (FileNotFoundException e) {
    return false;
}

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

Второй пример не компилируется, если вы не выбросите исключение из метода, потому что FileNotFoundException это проверенное исключение.

Compile.java:5: error: unreported exception FileNotFoundException; must be caught or declared to be thrown
        FileInputStream fis = new FileInputStream("some.file");

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

import java.io.*;
public class Compile {

    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("some.file");
            // do something useful with fis

            if (fis != null) {
                fis.close();
            }
        }
        catch (FileNotFoundException fnf) {
        }
        catch (IOException ioe) {
        }
        finally {
        }
    }
}

Второй пример, который вы опубликовали, не будет пытаться / поймать возможные ошибки, которые ... = new FileInputStream("..."); вернется, если не удастся загрузить файл. В первом примере вы автоматически обнаруживаете ошибки, которые FileInputStream дает.

Я лично пошел бы с первым, чтобы иметь лучшую обработку ошибок.

Другие вопросы по тегам