Как я могу обрабатывать исключение IOException, которое, как я знаю, никогда не может быть брошен безопасным и понятным образом?

17
  

«Основное различие между вещью   это может пойти не так, и вещь, которая   не может ошибиться в том, что когда   вещь, которая не может пойти не так, как надо   идет не так, как обычно оказывается   невозможно получить или восстановить ».       -Дуглас Адамс

У меня есть класс FileItems. Конструктор FileItems принимает файл и генерирует исключение (FileNotFoundException), если файл не существует. Другие методы этого класса также связаны с файловыми операциями и, таким образом, могут вызывать исключение FileNotFoundException. Я бы хотел найти лучшее решение. Решение, которое не требует, чтобы другие программисты обрабатывали все эти крайне маловероятные FileNotFoundExceptions.

Факты:

  1. Файл проверен, чтобы существовать, но крайне маловероятная возможность существует, что через некоторую серьезную ошибку реальность файл может быть удален до вызова этого метода.
  2. Так как вероятность того, что 1 произойдет, очень непохожа и невосстановима, я бы предпочел определить неконтролируемое исключение.
  3. Файл уже найден, чтобы существовать, заставляя других программистов писать код и улавливать проверенное FileNotFoundException кажется утомительным и бесполезным. В этот момент программа должна полностью завершиться. Например, всегда есть вероятность, что компьютер может загореться, но никто не настолько сумасшедший, чтобы заставить других программистов обрабатывать это как проверенный исключение .
  4. Время от времени я сталкиваюсь с проблемой исключения, и каждый раз, когда сталкиваюсь с этой проблемой (мое старое решение), вы определяете выборочные исключения, отмеченные флажками, и добавляет код-раздувание.

В настоящее время код выглядит следующим образом:

 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?

    
задан Ethan Heilman 25.06.2009 в 21:41
источник
  • Основные недостатки реальности случаются на удивление часто. –  Tom Hawtin - tackline 25.06.2009 в 21:48
  • Неисправности реальности нарушают мое идеальное программное обеспечение всю неделю. Я попытался записать несколько отчетов об ошибках, но, похоже, их сервер jira также недоступен jira.reality.net –  Ethan Heilman 25.06.2009 в 22:41

8 ответов

46

Обычным шаблоном для этого является цепочка исключений . Вы просто завершаете 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);
}
    
ответ дан Henning 25.06.2009 в 21:46
  • +1 по анти-шаблону. что меня заводит. –  akf 25.06.2009 в 22:10
  • +1, я не думал, что конструктор RuntimeException принял исключение из-за всего того кода, с которого я с тех пор просто передал сообщение. Спасибо. –  Ethan Heilman 25.06.2009 в 22:21
  • Теперь перейдите и посетите dailywtf.com для скриншота диалога, в котором говорится: «Этого никогда не должно быть, я знаю, что этот файл существует» –  oxbow_lakes 25.06.2009 в 22:33
  • При редактировании контента, вдохновленного другими ответами, по крайней мере, нужно сказать, что ;-) –  Thorbjørn Ravn Andersen 25.06.2009 в 23:51
  • Я думаю, что антипаттерн - это отсрочка давно (1.3?), когда цепочка исключений не существовала. –  Michael Borgwardt 26.06.2009 в 09:23
11

Вы можете преобразовать проверенное исключение в unchecked, разместив его внутри RuntimException. Если исключение попадает выше в стек и выводится с помощью printStackTrace (), будет также отображаться стек для исходного исключения.

try {
    // code...
}
catch (IOException e) {
    throw new RuntimeException(e);
}

Это хорошее решение, которое вы не должны стесняться использовать в этих ситуациях.

    
ответ дан Emil H 25.06.2009 в 21:45
10

Рассмотрим форму

throw new RuntimeException("This should never happen", e);

вместо этого. Это позволяет передать смысл сопровождающему, который будет следовать за вами, как при чтении кода, так и СЛЕДУЕТ исключить случайное исключение в каком-то странном сценарии.

EDIT: Это также хороший способ передать исключения через механизм, не ожидающий этих исключений. Например. если у вас есть итератор «получить больше строк из базы данных», интерфейс Итератора не позволяет бросать, например. исключение FileNotFoundException, чтобы вы могли обернуть его вот так. В коде ИСПОЛЬЗОВАНИЕ Итератора вы можете поймать исключение runtimeexception и проверить исходное excpetion с помощью getCause (). ОЧЕНЬ полезно при прохождении устаревших путей кода.

    
ответ дан Thorbjørn Ravn Andersen 25.06.2009 в 21:58
7

Если ваша позиция такова, что это так маловероятно и должно просто закончить программу, используйте существующее исключение во время выполнения, даже само RuntimeException (если не IllegalStateException).

try {
  ....

} catch (FileNotFoundException e) {
    throw new RuntimeException(e);
}
    
ответ дан Yishai 25.06.2009 в 21:46
1

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

Файл может существовать, когда выполняется конструктор вашего класса, но это не гарантирует, что:

  1. Он существует, когда методы называются
  2. Он доступен для записи / читается
  3. Другой поток не имеет доступа к нему и каким-то образом испортит ваши потоки.
  4. Ресурс не уходит в середине обработки

и др.

Вместо того, чтобы изобретать колесо, я бы сказал, что просто переименуйте IOException везде, где вы используете JDK / java.io , заставляя вас это делать.

Также я для одного класса ненависти, который бросает Исключения из своего конструктора - я бы избавился от них, если бы я был вами.

    
ответ дан matt b 25.06.2009 в 21:47
1

Я немного искал 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"
    });
  }
} ///:~
    
ответ дан branchgabriel 25.06.2009 в 21:57
1

У меня возникла та же проблема.

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

В общем случае рабочий процесс представляет собой последовательность действий (включая ввод-вывод), где каждое действие «предполагает», что предыдущий преуспел.

Подход, который я выбрал, - это создать список действий «откат». Если рабочий процесс завершается успешно, они игнорируются. Если возникает исключение, я выполняю откаты и представляю для пользователя исключение.

Это:

  • сохраняет целостность данных
  • позволяет значительно упростить код

Типичная функция:

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
}

Это бит little сложнее в реальном мире, потому что

  • иногда необходимо получить кучу блокировок перед действиями выполнения.
  • Отказоустойчивый код должен быть сам по себе без исключения (например)

Но я уже привык к этому подходу, теперь мои приложения более стабильны.

    
ответ дан bohdan_trotsenko 25.06.2009 в 22:02
0

Не зацикливайте все свое приложение с 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 , выясните, что делать, когда проблема возникает во время этой задачи. Например, пытались ли вы сохранить файл? Не сработайте, а не сообщите пользователю о проблеме.

    
ответ дан Christopher Perry 16.04.2014 в 00:04