Повторное выбрасывание исключений
Иногда вам будет нужно вновь выбросить исключение, которое вы только что поймали, обычно это происходит, когда вы используете Exception, чтобы поймать любое исключение. Так как вы уже имеете ссылку на текущее исключение, вы можете просто вновь бросить эту ссылку:
catch(Exception e) { System.err.println("An exception was thrown"); throw e; }
Повторное выбрасывание исключения является причиной того, что исключение переходит в обработчик следующего, более старшего контекста. Все остальные предложения catch для того же самого блока try игнорируются. Кроме того, все, что касается объекта исключения, сохраняется, так что обработчик старшего контекста, который поймает исключение этого специфического типа, может получить всю информацию из этого объекта.
Если вы просто заново выбросите текущее исключение, то информация, которую вы печатаете об этом исключении, в printStackTrace( ) будет принадлежать источнику исключения, а не тому месту, откуда вы его вновь выбросили. Если вы хотите установить новый стек информации трассировки, вы можете сделать это, вызвав функцию fillInStackTrace( ), которая возвращает объект исключения, для которого текущий стек наполняется информацией для старого объекта исключения. Вот как это выглядит:
//: c10:Rethrowing.java
// Демонстрация fillInStackTrace()
public class Rethrowing { public static void f() throws Exception { System.out.println( "originating the exception in f()"); throw new Exception("thrown from f()"); } public static void g() throws Throwable { try { f(); } catch(Exception e) { System.err.println( "Inside g(), e.printStackTrace()"); e.printStackTrace(System.err); throw e; // 17
// throw e.fillInStackTrace(); // 18
} } public static void
main(String[] args) throws Throwable { try { g(); } catch(Exception e) { System.err.println( "Caught in main, e.printStackTrace()"); e.printStackTrace(System.err); } } } ///:~
Важные строки помечены комментарием с числами. При раскомментированной строке 17 (как показано), на выходе получаем:
originating the exception in f() Inside g(), e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:8) at Rethrowing.g(Rethrowing.java:12) at Rethrowing.main(Rethrowing.java:24) Caught in main, e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:8) at Rethrowing.g(Rethrowing.java:12) at Rethrowing.main(Rethrowing.java:24)
Так что стек трассировки исключения всегда помнит исходное место, не имеет значения, сколько прошло времени перед повторным выбрасыванием.
Если закомментировать строку 17, а строку 18 раскомментировать, будет использоваться функция fillInStackTrace( ), и получим результат:
originating the exception in f() Inside g(), e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.f(Rethrowing.java:8) at Rethrowing.g(Rethrowing.java:12) at Rethrowing.main(Rethrowing.java:24) Caught in main, e.printStackTrace() java.lang.Exception: thrown from f() at Rethrowing.g(Rethrowing.java:18) at Rethrowing.main(Rethrowing.java:24)
Поскольку fillInStackTrace( ) в строке 18 становится новой исходной точкой исключения.
Класс Throwable должен появиться в спецификации исключения для g( ) и main( ), потому что fillInStackTrace( ) производит ссылку на объект Throwable. Так как Throwable - это базовый класс для Exception, можно получить объект, который является Throwable, но не Exception, так что обработчик для Exception в main( ) может промахнуться. Чтобы убедится, что все в порядке, компилятор навязывает спецификацию исключения для Throwable. Например, исключение в следующем примере не перехватывается в main( ):
//: c10:ThrowOut.java
public class ThrowOut { public static void
main(String[] args) throws Throwable { try { throw new Throwable(); } catch(Exception e) { System.err.println("Caught in main()"); } } } ///:~
Также возможно вновь выбросить исключение, отличающееся от того, которое вы поймали. Если вы делаете это, вы получаете сходный эффект, как если бы вы использовали fillInStackTrace( ) — информация об оригинальном состоянии исключения теряется, а то, с чем вы остаетесь - это информация, относящаяся к новому throw:
//: c10:RethrowNew.java
// Повторное выбрасывание объекта, // отличающегося от пойманного.
class OneException extends Exception { public OneException(String s) { super(s); } }
class TwoException extends Exception { public TwoException(String s) { super(s); } }
public class RethrowNew { public static void f() throws OneException { System.out.println( "originating the exception in f()"); throw new OneException("thrown from f()"); } public static void main(String[] args) throws TwoException { try { f(); } catch(OneException e) { System.err.println( "Caught in main, e.printStackTrace()"); e.printStackTrace(System.err); throw new TwoException("from main()"); } } } ///:~
Вот что напечатается:
originating the exception in f() Caught in main, e.printStackTrace() OneException: thrown from f() at RethrowNew.f(RethrowNew.java:17) at RethrowNew.main(RethrowNew.java:22) Exception in thread "main" TwoException: from main() at RethrowNew.main(RethrowNew.java:27)
Конечное исключение знает только то, что оно произошло в main( ), а не в f( ).
Вам никогда не нужно заботится об очистке предыдущего исключения или что другое исключение будет иметь значение. Они являются объектами, базирующимися в куче и создающимися с помощью new, так что сборщик мусора автоматически очистит их все.