В чем разница между throw e и throw new Exception (e)?

Рассматривать:

try  {
    // Some code here
} catch (IOException e) {
    throw e;
} catch (Exception e) {
    throw e;
}

В чем разница между throw e и throw new Exception(e)?

try  {
   // Some code here
} catch (IOException e) {
   throw new IOException(e);
} catch (Exception e) {
   throw new Exception(e);
}

Ответ 1

Если вам не нужно настраивать тип исключения, вы перебрасываете (бросаете дальше) тот же экземпляр без каких-либо изменений:

catch (IOException e) {
    throw e;
}

Если вам нужно настроить тип исключения, вы включаете e (как причину) в новое исключение требуемого типа.

catch (IOException e) {
    throw new IllegalArgumentException(e);
}

Все остальные сценарии я считаю запахом кода. Ваш второй фрагмент является хорошим примером этого.


Вот ответы на вопросы, которые могут появиться.

Почему я хотел бы отбросить исключение?

Вы можете отпустить это. Но если это произойдет, вы не сможете ничего сделать на этом уровне.

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

Почему я хочу настроить исключение?

Как правило большого пальца,

Более высокие уровни должны перехватывать исключения более низкого уровня и, вместо них, генерировать исключения, которые можно объяснить с помощью абстракции более высокого уровня.

Эффективная Java - 2-е издание - Элемент 61: выбросить исключения, соответствующие абстракции

Другими словами, в какой-то момент неясное IOException должно быть преобразовано в MySpecificBusinessRuleException.

Я назвал это "корректировкой типа исключения", умные ребята называют это преобразованием исключений (в частности, цепочкой исключений).


Чтобы было понятно, приведем несколько глупых примеров.

class StupidExample1 {
    public static void main(String[] args) throws IOException {
        try {
            throw new IOException();
        } catch (IOException e) {
            throw new IOException(new IOException(e));
        }
    }
}

приводит к подробному следу стека, как

Exception in thread "main" java.io.IOException: java.io.IOException: java.io.IOException
    at StupidExample1.main(XXX.java:XX)
Caused by: java.io.IOException: java.io.IOException
    ... 1 more
Caused by: java.io.IOException
    at StupidExample1.main(XXX.java:XX)

которые могут (и должны) быть эффективно сокращены до

Exception in thread "main" java.io.IOException
    at StupidExample1.main(XXX.java:XX)

Другой:

class StupidExample2 {
    public static void main(String[] args) {
        takeString(new String(new String("myString")));
    }

    static void takeString(String s) { }
}

Очевидно, что new String(new String("myString")) является многословной версией "myString" и должна подвергаться рефакторингу для последнего.

Ответ 2

catch (IOException e) {
    throw e;
}

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

catch (IOException e) {
    throw new IllegalStateException(e);
}

Вы увидите созданное IllegalStateException и его трассировку стека с "вызванной" исходной информацией об исключении и трассировкой стека. Вы устанавливаете (должно быть) выброшенное исключение как причину вновь созданного IOException. Верхний уровень будет видеть IllegalStateException и это будет возможно поймать (вы не поймаете это исключение причины захвата).

catch (IOException e) {
     throw new IOException();
}

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

Ответ 3

Ну, в общем, throw e будет "отбрасывать" весь оригинальный values- также некоторый поток кода, который должен быть скрыт, например, например, из соображений безопасности. Если вы заново создадите исключение, вы получите - или вы можете получить - еще одну трассировку стека на месте.

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

Позвольте немного проверить следующее:

  • Я создал один класс как простой генератор исключений
  • другой класс позволяет перебрасывать или воссоздавать исключение
  • потом я просто печатаю трассировку стека и сравниваю результаты

Генератор исключений

public class ExceptionsThrow {
    public static void throwNewException() throws Exception {
        throw new Exception("originally thrown message");
    }
}

Класс для переброса/воссоздания исключений

  public class Exceptions {

        public static void reThrowException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                throw e;
            }
        }

        public static void reCreateNewException() throws Exception {
            try {
                ExceptionsThrow.throwNewException();
            } catch (Exception e) {
                throw new Exception(e);
            }
        }
    }

Пример кода тестирования:

try {
     Exceptions.reThrowException();
} catch (Exception e) {
    System.out.println("1st RETHROW");
    e.printStackTrace();
    System.out.println("===========");
}

try {
    Exceptions.reCreateNewException();
} catch (Exception e) {
    System.out.println("2nd RECREATE");
    e.printStackTrace();
    System.out.println("===========");
}

И вывод наконец:

1st RETHROW
java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.java:5)
    at test.main.stackoverflow.Exceptions.reThrowException(Exceptions.java:7)
    at test.main.MainTest.main(MainTest.java:110)
java.lang.Exception: java.lang.Exception: originally thrown message===========
2nd RECREATE

    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.java:17)
    at test.main.MainTest.main(MainTest.java:118)
Caused by: java.lang.Exception: originally thrown message
    at test.main.stackoverflow.ExceptionsThrow.throwNewException(ExceptionsThrow.java:5)
    at test.main.stackoverflow.Exceptions.reCreateNewException(Exceptions.java:15)
    ... 1 more
===========

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

  • Я возьму только причину из исходного исключения, но это переопределит данные
  • как видите, вновь созданное исключение не содержит полную трассировку стека, как источник

Так:

public static void maskException() throws Exception {
    try {
        ExceptionsThrow.throwNewException();
    } catch (Exception e) {
        throw new Exception("I will dont tell you",e.getCause());
    }
}

И результат:

===========
3rd mask
java.lang.Exception: I will don't tell you
    at test.main.stackoverflow.Exceptions.maskException(Exceptions.java:25)
    at test.main.MainTest.main(MainTest.java:126)
===========

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

В реальном мире я могу вспомнить действительно частую проблему, когда некоторые веб-порталы обрабатывали исключения в сценариях PHP только в этом случае печати, а затем обычно, когда соединение с базой данных работало некорректно, строки подключения (включая базу данных адрес и учетные данные в виде открытого текста) были видны, например, в веб-браузере. :)

Ответ 4

Этот пример не имеет большого смысла в этом контексте, потому что вы выкидываете то же исключение и больше ничего не делаете. Регистрация по крайней мере будет иметь больше смысла. Вы ловите исключение для обработки или регистрации. Если вы не можете справиться с этим, сбросьте его (случай 1) или перенесите на что-то другое (случай 2).

Случай 1:

public class Main {

    // forced to handle or rethrow again
    public static void main(String[] args) throws IOException {
        read();
    }

    public static void read() throws IOException {
        try {
            readInternal();
        } catch (IOException e) {
            throw e;
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

В выводе вы увидите что-то вроде:

Exception in thread "main" java.io.IOException: Output error
    at com.alex.java.Main.readInternal(Main.java:26)
    at com.alex.java.Main.read(Main.java:19)
    at com.alex.java.Main.main(Main.java:14)
**Case 2:**

Шаблон ниже позволяет вам изменить тип исключения, а также сохранить исходные детали исключения:

try {
   // Some code here
} catch (IOException e) {
    throw new IllegalStateException(e);
}

Этот случай часто случается, когда вы хотите заменить Checked Exception Unchecked exception сохраняющим источник проблемы и сохраняющим всю информацию (что называется цепочкой исключений).

Регулярные варианты использования:

  • Вы не можете обработать Checked Exception и вы не хотите перебрасывать его вызывающей стороне. Повторное отбрасывание проверенных исключений заставит вызывающего обработать его. Это не то, что вы хотите сделать, если нет регулярных случаев для восстановления.
  • Исключения, такие как IOException, редко бывают полезны для клиента. Вам необходимо отправить что-то более конкретное и понятное в области вашего бизнеса.

IOException в Unchecked Exception например DocumentReadException, проливает свет на реальную ситуацию и не заставляет вызывающих ее обрабатывать:

public class Main {

    public static void main(String[] args) {
        read();
    }

    public static void read() {
        try {
            readInternal();
        } catch (IOException e) {
            // log and wrap the exception to a specific business exception
            logger.error("Error reading the document", e);
            throw new DocumentReadException(e);
        }
    }

    private static void readInternal() throws IOException {
        throw new IOException("Output error");
    }
}

Вывод будет похож на:

Exception in thread "main" java.lang.IllegalArgumentException: Error reading the document
    at com.alex.java.Main.read(Main.java:21)
    at com.alex.java.Main.main(Main.java:14)
Caused by: java.io.IOException: Output error
    at com.alex.java.Main.readInternal(Main.java:26)
    at com.alex.java.Main.read(Main.java:19)
    ... 1 more

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

Ответ 5

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

throw e объявляет экземпляр IOException (назначает пространство памяти на устройстве без сохранения данных), поэтому вы перебрасываете его в блок catch, когда объявляете заранее сделанное исключение.

throw new IOException e инициализирует новый экземпляр IOException, поэтому вы создаете новое исключение в блоке catch, поскольку оно не ссылается на другое исключение.

тем не мение

Вы обычно не бросаете исключения в блоке catch. В большинстве случаев вы бы:

  1. Предупредите компилятор, что вы собираетесь сгенерировать исключение для метода (throws IOException после объявления параметра)
  2. Бросьте исключение (throw new IOException("Warning text") в теле метода) всякий раз, когда вводимые данные не могут быть проверены в любом случае.
  3. В блоках try/catch поместите вызов или вызов метода в блок try; в блоке catch поместите сообщение об ошибке.

Пример кода будет выглядеть так:

public static void myMethod() throws IOException{
int prueba=0;
if(prueba>9 //some condition){
    //do a thing
}else
    throw new IOException("This number is not correct");
}//end of method
try{
    myClass.myMethod();
}catch{
    System.out.println("An error has occurred");
}//end of try/catch

Так я научился правильно делать исключения, но надеюсь, что ответил на ваш вопрос.

Ответ 6

Сначала нам нужно понять, почему вы создаете новый тип исключения для инкапсуляции исключения.

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

Например:

try {
  // read file
} catch (IOException e) {
  throw new MyAppFailedToReadFile(e);
}

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