Как я могу справиться с IOException, которое, как я знаю, никогда не может быть брошен в безопасном и понятном виде?
"Основное различие между вещью это может пойти не так, и вещь, которая не может ошибиться в том, что когда вещь, которая не может пойти не так, как надо идет не так, как обычно оказывается невозможно получить или восстановить". -Дуглас Адамс
У меня есть класс FileItems. Конструктор FileItems принимает файл и генерирует исключение (FileNotFoundException), если файл не существует. Другие методы этого класса также связаны с файловыми операциями и, таким образом, могут вызывать исключение FileNotFoundException. Я бы хотел найти лучшее решение. Решение, которое не требует, чтобы другие программисты обрабатывали все эти крайне маловероятные FileNotFoundExceptions.
Факты:
- Файл проверен, чтобы существовать, но крайне маловероятная возможность существует, что через некоторую серьезную ошибку реальность файл может быть удален до вызова этого метода.
- Так как вероятность того, что 1 произойдет, очень непохожа и невосстановима, я бы предпочел определить неконтролируемое исключение.
- Файл уже найден, чтобы существовать, заставляя других программистов писать код и улавливать проверенное FileNotFoundException кажется утомительным и бесполезным. В этот момент программа должна полностью завершиться. Например, всегда есть вероятность, что компьютер может загореться, но никто не настолько сумасшедший, чтобы заставить других программистов обрабатывать это как проверенное исключение.
- Время от времени я сталкиваюсь с проблемой исключения, и каждый раз, когда сталкиваюсь с этой проблемой (мое старое решение), вы определяете выборочные исключения, отмеченные флажками, и добавляет код-раздувание.
В настоящее время код выглядит следующим образом:
public Iterator getFileItemsIterator() {
try{
Scanner sc = new Scanner(this.fileWhichIsKnowToExist);
return new specialFileItemsIterator(sc);
} catch (FileNotFoundException e){ //can never happen}
return null;
}
Как я могу сделать это лучше, не определяя пользовательское исключенное исключение FileNotFoundException? Есть ли способ применить исключение checkedException к исключению uncheckException?
Ответы
Ответ 1
Обычным шаблоном для этого является цепочка исключений. Вы просто завершаете FileNotFoundException в RuntimeException:
catch(FileNotFoundException e) {
throw new RuntimeException(e);
}
Этот шаблон применим не только тогда, когда исключение не может произойти в конкретной ситуации (например, ваша), но также когда у вас нет средств или намерений действительно справиться с этим исключением (например, сбой базы данных).
Изменить. Опасайтесь этого похожего анти-шаблона, который я видел в дикой природе слишком часто:
catch(FileNotFoundException e) {
throw new RuntimeException(e.getMessage());
}
Делая это, вы выбрасываете всю важную информацию в исходном стеке, что часто затрудняет поиск проблем.
Другое редактирование: Как правильно указывает Торбьёрн Равн Андерсен в своем ответе, не больно утверждать, почему вы цепляете исключение, как в комментарии, так и, что еще лучше, сообщение об ошибке:
catch(FileNotFoundException e) {
throw new RuntimeException(
"This should never happen, I know this file exists", e);
}
Ответ 2
Вы можете преобразовать проверенное исключение в unchecked, разместив его внутри RuntimException. Если исключение попадает выше в стек и выводится с помощью printStackTrace(), будет также отображаться стек для исходного исключения.
try {
// code...
}
catch (IOException e) {
throw new RuntimeException(e);
}
Это хорошее решение, которое вы не должны стесняться использовать в этих ситуациях.
Ответ 3
Рассмотрим форму
throw new RuntimeException("This should never happen", e);
вместо этого. Это позволяет передать смысл сопровождающему, который будет следовать за вами, как при чтении кода, так и СЛЕДУЕТ исключить случайное исключение в каком-то странном сценарии.
EDIT: Это также хороший способ передать исключения через механизм, не ожидающий этих исключений. Например. если у вас есть итератор "получить больше строк из базы данных", интерфейс Итератора не позволяет бросать, например. исключение FileNotFoundException, чтобы вы могли обернуть его вот так. В коде ИСПОЛЬЗОВАНИЕ Итератора вы можете поймать исключение runtimeexception и проверить исходное excpetion с помощью getCause(). ОЧЕНЬ полезно при прохождении устаревших путей кода.
Ответ 4
Если ваша позиция такова, что это так маловероятно и должно просто закончить программу, используйте существующее исключение во время выполнения, даже само RuntimeException (если не IllegalStateException).
try {
....
} catch (FileNotFoundException e) {
throw new RuntimeException(e);
}
Ответ 5
Вероятно, вам лучше отказаться от суперкласса и более общего исключения IOException
в любой момент вашего кода, который включает чтение или запись из файла.
Файл может существовать, когда выполняется конструктор класса, но это не гарантирует, что:
- Он существует, когда методы называются
- Он доступен для записи/читается
- Другой поток не имеет доступа к нему и каким-то образом испортит ваши потоки.
- Ресурс не уходит в середине обработки
и др.
Вместо того, чтобы повторно изобретать колесо, я бы сказал, просто перебросьте IOException везде, где вы используете JDK/ java.io
, заставляя вас это делать.
Также я для одного класса ненависти, который бросает Исключения из своего конструктора - я бы избавился от них, если бы я был вами.
Ответ 6
Я немного искал Google и нашел этот код кода. Это немного более гибкий подход, который я считаю
Комплименты этой статьи
class SomeOtherException extends Exception {}
public class TurnOffChecking {
private static Test monitor = new Test();
public static void main(String[] args) {
WrapCheckedException wce = new WrapCheckedException();
// You can call f() without a try block, and let
// RuntimeExceptions go out of the method:
wce.throwRuntimeException(3);
// Or you can choose to catch exceptions:
for(int i = 0; i < 4; i++)
try {
if(i < 3)
wce.throwRuntimeException(i);
else
throw new SomeOtherException();
} catch(SomeOtherException e) {
System.out.println("SomeOtherException: " + e);
} catch(RuntimeException re) {
try {
throw re.getCause();
} catch(FileNotFoundException e) {
System.out.println(
"FileNotFoundException: " + e);
} catch(IOException e) {
System.out.println("IOException: " + e);
} catch(Throwable e) {
System.out.println("Throwable: " + e);
}
}
monitor.expect(new String[] {
"FileNotFoundException: " +
"java.io.FileNotFoundException",
"IOException: java.io.IOException",
"Throwable: java.lang.RuntimeException: Where am I?",
"SomeOtherException: SomeOtherException"
});
}
} ///:~
Ответ 7
У меня возникла та же проблема.
В простейшем случае вы сообщаете пользователю об ошибке и предлагаете либо повторить, либо отменить операцию.
В общем случае рабочий процесс представляет собой последовательность действий (включая ввод-вывод), где каждое действие "предполагает", что предыдущий преуспел.
Подход, который я выбрал, - это создать список действий "откат". Если рабочий процесс завершается успешно, они игнорируются. Если возникает исключение, я выполняю откаты и представляю для пользователя исключение.
Это:
- сохраняет целостность данных
- позволяет значительно упростить код
Типичная функция:
returntype func(blah-blah-blah, Transaction tr)
{
// IO example
Stream s = null;
try
{
s = new FileStream(filename);
tr.AddRollback(File.Delete(filename));
}
finally
{
if (s != null)
s.close();
}
}
Типичное использование:
Transaction tr = new Transaction();
try
{
DoAction1(blah1, tr);
DoAction2(blah2, tr);
//...
}
catch (Exception ex)
{
tr.ExecuteRollbacks();
// queue the exception message to the user along with a command to repeat all the actions above
}
Это немного сложнее в реальном мире, потому что
- иногда необходимо получить кучу блокировок перед действиями выполнения.
- Отказоустойчивый код должен быть сам по себе без исключения (например)
Но я уже привык к этому подходу, теперь мои приложения более стабильны.
Ответ 8
Не зацикливайте все свое приложение с помощью RuntimeException
, когда вы столкнетесь с маловероятным состоянием ошибки. RuntimeException
должен быть зарезервирован для ошибок программиста, а IOException
, скорее всего, не вызван ошибкой программиста.
Вместо этого инкапсулируйте исключение нижнего уровня в исключение и повторное создание более высокого уровня. Затем обработайте исключение более высокого уровня по цепочке вызовов.
Например:
class SomeClass {
public void doActions() {
try {
someAction();
} catch (HigherLevelException e) {
notifyUser();
}
someOtherUnrelatedAction();
}
public void someAction() throws HigherLevelException {
try {
// user lower-level abstraction to do our bidding
} catch(LowerLevelException e) {
throw new HigherLevelException(e);
}
}
public void someOtherUnrelatedAction() {
// does stuff
}
}
Скорее всего, стек вызовов, который выбрал исключение, выполнял некоторую задачу в вашем приложении. Вместо того, чтобы сбой всего вашего приложения с помощью RuntimeException
выяснить, что делать, когда проблема возникает во время этой задачи. Например, пытались ли вы сохранить файл? Не сработайте, а не сообщите пользователю о проблеме.