Обработка исключений ввода-вывода в Java

По сути, я хочу открыть файл, прочитать несколько байтов, а затем закрыть файл. Вот что я придумал:

try
{
    InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
    try
    {
        // ...
        inputStream.read(buffer);
        // ...
    }
    catch (IOException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    finally
    {
        try
        {
            inputStream.close();
        }
        catch (IOException e)
        {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }
}
catch (FileNotFoundException e)
{
    // TODO Auto-generated catch block
    e.printStackTrace();
}

Может быть, я избалован RAII, но должен быть лучший способ сделать это на Java, верно?


person fredoverflow    schedule 06.06.2011    source источник
comment
Что ты хочешь делать ? Избежать обработки всех этих исключений?   -  person user703016    schedule 06.06.2011
comment
А как бы вы лучше определили ?   -  person Damian Leszczyński - Vash    schedule 06.06.2011
comment
Лучше на codereview.stackexchange.com   -  person Kevin    schedule 06.06.2011
comment
@Vash: Не могу поверить, что мне нужно столько шаблонного кода и вложенных блоков try/catch для выполнения такой простой задачи.   -  person fredoverflow    schedule 06.06.2011
comment
@Heandel, @Vash: в этом образце я насчитал 21 строку кода, не считая комментариев. И все, что он хотел сделать, это открыть файл, прочитать данные, закрыть файл. Если возникает ошибка, закройте файл, но также распечатайте информацию об исключении. Это должно быть 3, может быть, 4 строки кода. Если вам нужно спросить, как можно было бы улучшить код, вам серьезно нужно начать изучать другие языки, кроме Java.   -  person jalf    schedule 06.06.2011
comment
если вы не обрабатываете исключение, достаточно одной попытки/поймать.   -  person bestsss    schedule 06.06.2011
comment
@jalf, Можно и в одну строку, но форматирование не то. Также знание других языков не даст возможности читать мысли человека. Тем не менее, это, возможно, правильный способ решения этой проблемы, в этом конкретном случае мы можем использовать иерархию исключений (уже представлено). Но обычно вам нужно так много кода try catch для такого рода операций. Разница между Java и большинством других языков заключается в том, что компилятор не заставляет вас обрабатывать исключения, которые могут возникнуть.   -  person Damian Leszczyński - Vash    schedule 06.06.2011
comment
@Vash: нет, в некоторых языках вам не нужно столько блоков try/catch для такого рода операций, и это не имеет ничего общего с компилятором, заставляющим вас обрабатывать исключения. Как я уже говорил, вам нужно начать изучать некоторые другие языки. Открой свои глаза. Быть вынужденным писать уродливый код из-за того, что вы работаете на уродливом языке, уже достаточно плохо, но даже не осознавать, что может существовать лучшее решение, для программиста в значительной степени непростительно.   -  person jalf    schedule 06.06.2011
comment
Вы правы, что вас заставляют, но в 7-й версии Java будет что-то вроде использования из C#. Но для этого случая на Java нет лучшего кадра, чем код, представленный vitaut. Я открываю глаза, поэтому знание того, что в других языках вы можете сделать это, используя меньше строк, ничего не меняет в исходном коде Java, и, как правило, не существует решения, которое обеспечивает правильное управление ресурсами с меньшим количеством кода. Так что дело не в том, что я не знаю других языков, потому что вы ошибаетесь в этом, а в том, что в Java это делается так, а не иначе.   -  person Damian Leszczyński - Vash    schedule 06.06.2011
comment
Ребята, некрасивый код можно написать на любом языке, не только на Java. Причина, по которой этот пример неуклюж, заключается в том, что он написан таким образом. Можно даже избавиться от всех предложений catch, распространив исключение. Конечно, вы не можете избавиться от предложения finally, если хотите правильно закрыть файл. Однако, с моей точки зрения, это незначительное неудобство, и оно будет устранено Java 7, как я уже упоминал в ответе. Основная причина, по которой автоматическое управление ресурсами не так полезно в Java, как в C++, связана с GC. И я в основном использую C++ BTW, поэтому я не сторонник Java.   -  person vitaut    schedule 06.06.2011
comment
@jalf: 21+ строк? Я могу написать код, который делает то же самое, в 1000 строк кода, но это не значит, что я должен это делать =D. Язык не требует, чтобы вы ловили после каждой строки кода.   -  person vitaut    schedule 06.06.2011
comment
@Vash: еще раз, нет, другие языки есть, имеют решения, которые обеспечивают надлежащее надежное управление ресурсами без необходимости писать ни одной попытки/поймать. С++ может это сделать. Чтобы распечатать информацию об исключении, может потребоваться один блок try/catch, но только для печати. Открытие, чтение и закрытие файла можно выполнить с идеальной обработкой ошибок, без записи ни одной попытки отлова и, конечно же, без finally.   -  person jalf    schedule 06.06.2011
comment
@vitaut: я был бы более впечатлен, если бы вы могли написать код, который делает то же самое менее чем за 10 строк, не жертвуя при этом правильностью. Не могли бы вы? Я могу на С++.   -  person jalf    schedule 06.06.2011
comment
@jalf: О, я думал, что это IOCCC. Могу даже в одну строчку написать, если краткость кода стала требованием buffer = readAllBytes(); Теперь ваша очередь =D   -  person vitaut    schedule 06.06.2011
comment
@vitaut: хорошо, сделай это, не жертвуя правильностью или удобочитаемостью. Я должен задаться вопросом, то ли вы просто глупы, чтобы раздражать меня, то ли вы буквально настолько ослеплены Java, что не можете уложить в голове мысль о том, что код мог бы быть проще для выражения на другом языке.   -  person jalf    schedule 06.06.2011
comment
@jalf: Не имеет значения, если вы не поняли, что я имел в виду. Просто хочу сказать, что вы правильно заметили, что нужно учить несколько языков. Иначе слишком много пламени.   -  person vitaut    schedule 06.06.2011
comment
Возможно, вас заинтересует эта статья о проверенных и непроверенных исключениях Java: mindview.net/Etc/Discussions/CheckedExceptions   -  person Alvin    schedule 06.06.2011
comment
Новая функция try-with-resources в Java 7 решает эту проблему, записывая, но подавляя исключения из close(): stackoverflow.com/questions/7849416/   -  person Raedwald    schedule 21.10.2011


Ответы (9)


Если у вас есть одинаковый код обработки исключений для IOException и FileNotFoundException, вы можете переписать свой пример более компактным способом, используя только одно предложение catch:

try {
    InputStream input = new BufferedInputStream(new FileInputStream(file));
    try {
        // ...
        input.read(buffer);
        // ...
    }
    finally {
        input.close();
    }
}
catch (IOException e) {
    e.printStackTrace();
}

Вы даже можете избавиться от внешнего try-catch, если сможете распространить исключение, что, вероятно, имеет больше смысла, чем ручная печать трассировки стека. Если вы не поймаете какое-то исключение в своей программе, вы автоматически распечатаете трассировку стека.

Кроме того, необходимость ручного закрытия потока будет решена в Java 7 с помощью автоматического управления ресурсами. .

При автоматическом управлении ресурсами и распространении исключений код сводится к следующему:

try (InputStream input = new BufferedInputStream(new FileInputStream(file))) {
    // ...
    input.read(buffer);
    // ...
}
person vitaut    schedule 06.06.2011
comment
Таким образом, в своей 7-й версии, примерно через 15 лет после первого выпуска, Java, наконец (без каламбура), предоставит одну из основных функций языка, для улучшения которого он был создан. Я не впечатлен. - person sbi; 06.06.2011
comment
@sbi: и он по-прежнему не может предоставить ту же функцию, вместо этого он просто отрезает часть шаблонного кода. Управление ресурсами в Java7 больше похоже на использование в C#, чем на RAII в C++. Гораздо лучше, чем то, что у них есть сейчас, но все же не так хорошо, как вы сказали, основная особенность языка, которую он должен был улучшить. - person jalf; 06.06.2011
comment
О нет! Мой ответ захватывают сторонники C++. знак равно - person vitaut; 06.06.2011
comment
Ага. Это забавно. Вопрос может быть закрыт, поскольку вопросы такого типа [...] обычно приводят к конфронтации и спору. Конечно, этого не произойдет, потому что он подчеркивает верную точку зрения. Просто говорю... - person musiKk; 06.06.2011
comment
Я сомневаюсь, что это правильно. поток открывается в конструкторе и используется read. Что такое поток открыт, а затем в конструкторе было выбрано исключение? не будет ли утечки ресурса? - person JavaDeveloper; 12.03.2014

Обычно эти методы завернуты в библиотеки. Если вы не хотите писать на этом уровне, лучше всего создать свои собственные вспомогательные методы или использовать существующие, такие как FileUtils.

String fileAsString = Fileutils.readFileToString(filename);
// OR
for(String line: FileUtils.readLines(filename)) {
    // do something with each line.
}
person Peter Lawrey    schedule 06.06.2011

Я не знаю, правильно ли это, но вы можете иметь весь свой код в одном блоке try, а затем иметь разные блоки catch сразу друг за другом.

try {
  ...
}
catch (SomeException e) {
  ...
}
catch (OtherException e) {
  ...
}
person Rasmus Øvlesen    schedule 06.06.2011

Если вы хотите сделать это на простой Java, опубликованный вами код выглядит нормально.

Вы можете использовать сторонние библиотеки, такие как Commons IO, в которых вам нужно будет написать гораздо меньше кода. например

Ознакомьтесь с Commons IO по адресу:

http://commons.apache.org/io/description.html

person Dave    schedule 06.06.2011

Иногда вы можете сократить код до следующего:

public void foo(String name) throws IOException {
    InputStream in = null;
    try {
        in = new FileInputStream(name);
        in.read();
        // whatever
    } finally {
        if(in != null) {
            in.close();
        }
    }
}

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

person musiKk    schedule 06.06.2011

Мое мнение об этом без использования утилит:

InputStream inputStream = null;
try {
    inputStream = new BufferedInputStream(new FileInputStream(file));
    // ...
    inputStream.read(buffer);
    // ...
} catch (IOException e) {
    e.printStackTrace();
    throw e; // Rethrow if you cannot handle the exception
} finally {
    if (inputStream != null) {
        inputStream.close();
    }
}

Не однострочный, но не очень плохой. Используя, скажем, Apache Commons IO, это будет:

//...
buffer = FileUtils.readFileToByteArray(file);
//...

Следует помнить, что в стандартном Java отсутствуют многие из этих небольших утилит и простых в использовании интерфейсов, которые нужны всем, поэтому вам придется полагаться на некоторые вспомогательные библиотеки, такие как Apache Commons, Google Guava, ... в ваших проектах (или реализуйте свои собственные служебные классы).

person gpeche    schedule 06.06.2011

Используйте org.apache.commons.io.FileUtils.readFileToByteArray(File) или что-то подобное из этого пакета. Он по-прежнему выдает IOException, но занимается очисткой за вас.

person artbristol    schedule 06.06.2011

Попробуйте следующее:

try
{
    InputStream inputStream = new BufferedInputStream(new FileInputStream(file));
    byte[] buffer = new byte[1024];
    try
    {
        // ...
        int bytesRead = 0;
        while ((bytesRead = inputStream.read(buffer)) != -1) {                
           //Process the chunk of bytes read
        }
    }
    catch (IOException e)
    {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }
    finally
    {           
        inputStream.close();
    }
}
catch (FileNotFoundException e)
{
    // TODO Auto-generated catch block
    e.printStackTrace();
}
person Hasan Fahim    schedule 06.06.2011

Google guava попытался решить эту проблему, внедрив Закрывающиеся.

В противном случае вам придется подождать, пока не появится AutoCloseable из JDK 7. out, поскольку он касается некоторых случаев, когда выбрасывается исключение IOException.

person mindas    schedule 06.06.2011