Подтвердить что ты не робот

Как я могу справиться с 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?

4b9b3361

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