Когда поймать Исключение против Когда бросить Исключения?

Я уже некоторое время кодирую в Java. Но иногда я не понимаю, когда я должен выбросить исключение и когда я должен поймать исключение. Я работаю над проектом, в котором есть много методов. Иерархия - это что-то вроде этого -

Method A will call Method B and Method B will call some Method C and Method C will call Method D and Method E.

Итак, в настоящее время я делаю это: я бросаю исключения во всех методах и перехватываю их в методе A, а затем регистрирую как ошибку.

Но я не уверен, будет ли это правильным путем? Или мне следует начинать перехватывать исключения во всех методах. Так вот почему эта путаница началась в моей-Когда я должен поймать Exception vs, когда я должен выбросить исключения. Я знаю, что это глупый вопрос, но почему-то я пытаюсь понять эту основную концепцию.

Может ли кто-нибудь дать мне подробный пример When to catch the Exception vs When to throw the Exceptions, чтобы мои понятия очистились от этого? И в моем случае, должен ли я продолжать бросать исключение, а затем поймать его в главном вызове метода A?

Ответ 1

Вы должны поймать исключение, когда находитесь в методе, который знает, что делать.

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

Итак, у вас есть класс, скажем:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) { }
}

Теперь, скажем, файл не существует. Что вы должны сделать? Если вы изо всех сил пытаетесь придумать ответ, потому что нет никого... FileInputStream не знает, что делать с этой проблемой. Таким образом, он подбрасывает цепочку, т.е.:

public class FileInputStream extends InputStream {
    public FileInputStream(String filename) throws FileNotFoundException { }
}

Теперь скажем, кто-то использует вашу библиотеку. Они могут иметь код, который выглядит следующим образом:

public class Main {
    public static void main(String... args) {
        String filename = "foo.txt";
        try {
            FileInputStream fs = new FileInputStream(filename);

            // The rest of the code
        } catch (FileNotFoundException e) {
            System.err.println("Unable to find input file: " + filename);
            System.err.println("Terminating...");
            System.exit(3);
        }
    }
}

Здесь программист знает, что делать, поэтому они улавливают исключение и обрабатывают его.

Ответ 2

Исключение должно возникать, когда функция испытывает сбой, то есть ошибку.

Функция - это единица работы, а ошибки должны рассматриваться как ошибки или иначе основаны на их влиянии на функции. Внутри функции f ошибка является ошибкой в ​​том и только в том случае, если она препятствует f выполнению каких-либо предварительных условий вызова, достигая любого из f или восстанавливать любой инвариант, который f разделяет ответственность за поддержание.

Существует три разных типа ошибок:

  • условие, которое не позволяет функции выполнить предварительное условие (например, ограничение параметра) другой функции, которая должна быть вызвана;
  • условие, которое препятствует функции устанавливать одно из своих собственных постусловий (например, создание допустимого возвращаемого значения является постусловием); и
  • условие, которое не позволяет функции переустанавливать инвариант, который отвечает за поддержание. Это особый постусловие, которое особенно относится к функциям члена. Существенным постусловием каждой нечастной функции-члена является то, что он должен восстановить инварианты своих классов.

Любое другое условие не является ошибкой и не должно сообщаться как ошибка.

Сообщать об ошибке, когда функция обнаруживает ошибку, с которой она не может справиться сама по себе, и которая препятствует ее продолжению в любой форме нормальной или предполагаемой операции.

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

Ответ 3

В общем, поймайте на том уровне, где вы можете сделать что-то полезное. Например, пользователь пытается подключиться к какой-либо базе данных, и он не работает в методе D.

Как вы хотите справиться с этим? Возможно, установив диалог "Извините, не могу подключиться к SERVER/DB" или что-то еще. Is - это метод A, B или C, который создал эту информацию SERVER/DB (например, прочитав файл настроек или запросив пользовательский ввод) и попробовал соединение? Вероятно, это метод, который должен обрабатывать исключение. Или, по крайней мере, 1 от метода, который должен обрабатывать его.

Это действительно зависит от вашего приложения, поэтому это может быть только общий совет. Большая часть моего опыта связана с приложениями Swing/desktop, и вы обычно можете почувствовать, какие классы выполняют программную логику (например, "Контроллер" ), и кто создает диалоговые окна (например, "Просмотр" ). Обычно "контроллер" должен поймать исключение и попытаться что-то сделать.

В веб-приложении это может быть другим.

Некоторые очень скелетные коды, большинство классов не существует, и я не уверен, что URL-адрес для БД даже имеет смысл, но вы получаете идею. Смутно свинг...

/*  gets called by an actionListener when user clicks a menu etc... */
public URL openTheDB() {
  URL urlForTheDB = MyCoolDialogUtils.getMeAURL(URL somePreviousOneToFillInTheStart);
  try {
     verifyDBExists(urlForTheDB);
     // this may call a bunch of deep nested calls that all can throw exceptions
     // let them trickle up to here

     // if it succeeded, return the URL
     return urlForTheDB;
  }
  catch (NoDBExeption ndbe) {
    String message = "Sorry, the DB does not exist at " + URL;
    boolean tryAgain = MyCoolDialogUtils.error(message);
    if (tryAgain)
      return openTheDB();
    else
      return null;  // user said cancel...
  }
  catch (IOException joe) {
    // maybe the network is down, aliens have landed
    // create a reasonable message and show a dialog
  }

}

Ответ 4

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

  • catch Если у вас есть метод, который подключается к ресурсу (например, открывает файл/сеть)
  • throw, если класс выше в иерархии нуждается в информации об ошибке

Ответ 5

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

Мотивация

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

Метод

Чтобы достичь этого, я улавливаю все проверенные исключения и повторно бросаю их как исключенные исключения. Затем я использую глобальный захват на границе каждого из моих архитектурных слоев (обычно абстрагированных или инъецируемых, поэтому он только когда-либо записывается один раз). Именно в этих точках я могу добавить дополнительный контекст к стеку ошибок или решить, следует ли регистрировать и игнорировать, или создавать настраиваемое исключенное исключение с переменными для хранения любого дополнительного контекста. В остальном я регистрирую ошибки только на верхнем уровне, чтобы остановить "двойной журнал" (например, задание cron, контроллер spring для ajax)

throw new RuntimeException(checked,"Could not retrieve contact " + id);

При таком подходе нет загромождения ваших сигнатур метода графического интерфейса или бизнес-уровня, чтобы объявить "броски" для исключений, связанных с базой данных.

Пример того, как это работает в реальной жизни:

Позволяет сказать, что моя работа по коду - это автоматизированный процесс обновления многих страховых полисов. Архитектура поддерживает графический интерфейс для ручного запуска обновления для одной политики. Также можно сказать, что почтовый индекс для области рейтинга поврежден в БД для одной из этих политик.

Пример типа журнала ошибок, который я хотел бы достичь, будет.

Сообщение журнала: политика флагов 1234 для ручного вмешательства из-за ошибки:

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

От трассировки стека: вызвано: политикой оценки ошибок 1234... Этот улов будет ошибками при загрузке, извлекающими много других объектов, а также ошибки алгоритма, такие как NPE и т.д.

Из трассировки стека: вызвано: Ошибка определения области рейтинга 73932...

От Stack Trace: вызвано: JPA: неожиданный null в поле "почтовый индекс"

Ответ 6

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

например, недопустимый ввод пользователя, проблемы с базой данных, сбои в сети, отсутствующие файлы

Ответ 7

Как говорили другие, как правило, вы должны поймать исключение, когда сможете его обработать, иначе просто бросьте его.

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