What is the difference between throw e and throw new Exception(e)?
考虑:
1 2 3 4 5 6 7 |
1 2 3 4 5 6 7 | try { // Some code here } catch (IOException e) { throw new IOException(e); } catch (Exception e) { throw new Exception(e); } |
如果不需要调整异常类型,则无需更改即可重新抛出(进一步抛出)同一实例:
1 2 3 |
如果确实需要调整异常类型,则将
1 2 3 |
我认为所有其他情况都有代码气味。您的第二个片段就是一个很好的例子。
以下是可能弹出的问题的答案。
Why would I want to rethrow an exception?
你可以放手但是,如果发生这种情况,您将无法在此级别上执行任何操作。
当我们在方法中捕获异常时,我们仍在该方法中并有权访问其范围(例如,局部变量及其状态)。在我们抛出异常之前,我们可以做任何需要做的事情(例如,记录一条消息,将其发送到某个地方,对当前状态进行快照)。
Why would I want to adjust an exception?
根据经验,
Higher layers should catch lower-level exceptions and, in their place, throw exceptions that can be explained in terms of the higher-level abstraction.
Effective Java - 2nd Edition - Item 61: Throw exceptions appropriate to the abstraction
换句话说,在某个时候,晦涩的
我称它为"调整异常类型",聪明的人称之为异常转换(尤其是异常链接)。 sub>
为了清楚起见,让我们举一些愚蠢的例子。
1 2 3 4 5 6 7 8 9 | class StupidExample1 { public static void main(String[] args) throws IOException { try { throw new IOException(); } catch (IOException e) { throw new IOException(new IOException(e)); } } } |
导致像这样的详细堆栈跟踪
1 2 3 4 5 6 | 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) |
可以(并且应该)有效地减少到
1 2 |
另一个:
1 2 3 4 5 6 7 |
很明显,
1 2 3 |
您将看到仅具有原始stacktrace的原始异常。您不会在堆栈跟踪中看到此"重新抛出"行,因此它是透明的。
1 2 3 |
您将看到创建的
1 2 3 |
您将仅看到
好吧,基本上,
因此,我想说,您可以选择屏蔽某些数据(不知道,您可以将异常记录到特殊日志中,但是您希望将其他诊断数据传递给最终用户)。
让我们检查一下以下内容:
- 我创建了一个类,它只是异常的简单生成器
- 另一个类允许重新抛出或重新创建异常
- 之后,我只是打印stacktrace并比较结果
异常产生器
1 2 3 4 5 |
重新抛出/重新创建异常的类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | 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); } } } |
测试代码示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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("==========="); } |
最后输出:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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 =========== |
在这种情况下,您可以看到几乎相同的数据,但还可以看到其他消息,因为我使用相同的Exception来构建新数据,但是您不需要这样做,所以您可以掩盖原始原因,或者您不需要公开应用程序的逻辑,例如,让我们再看一个示例:
- 我将仅从原始异常中获取原因,但它将覆盖数据
-
如您所见,新创建的异常不包含完整的堆栈跟踪,因为
起源
所以:
1 2 3 4 5 6 7 |
结果:
1 2 3 4 5 6 | =========== 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) =========== |
因此,我想说,用相同的实例重新创建异常是没有意义的,但是在某些情况下,您可能想要这样做-屏蔽数据,或者在其他情况下也可以更改异常类型-例如,从I / O异常到通用Exception等。
在现实世界中,我记得一个真正经常发生的问题:当某些Web门户仅通过这种打印情况处理PHP脚本中的异常,然后通常是在与数据库的连接无法正常工作时,出现连接字符串(包括数据库)例如,地址和凭据(以纯文本格式)在网络浏览器中可见。 :)
在这种情况下,此示例没有多大意义,因为您将抛出相同的异常并且不执行任何其他操作。至少将其记录会更有意义。您正在捕获一个异常来处理或记录它。如果您无法处理它,请将其扔回(情况1)或换成其他东西(情况2)。
情况1:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | 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"); } } |
在输出中,您将看到类似以下内容的内容:
1 2 3 4 5 | 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:** |
下面的模式允许您更改异常的类型并保留原始异常的详细信息:
1 2 3 4 5 |
当您想用
常规用例:
-
您不能处理
Checked Exception ,也不想将其重新扔给调用方。重新抛出检查的异常将强制调用方对其进行处理。如果没有常规的恢复案例,则这不是您要执行的操作。 -
像
IOException 这样的异常对客户端很少有用。您需要在您的业务领域范围内发送更具体明确的信息。
像
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | 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"); } } |
输出将类似于:
1 2 3 4 5 6 7 | 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 |
从堆栈跟踪中可以看到,根本原因是记录器可以帮助您找到原始问题,并且业务域异常已发送给用户。
引发异常时,您所做的事情与创建或声明实例非常相似。
然而
您通常不会在catch块上引发异常。在大多数情况下,您将:
示例代码如下所示:
1 2 3 4 5 6 7 | 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 |
1 2 3 4 5 |
这就是我一直在学习正确处理异常的方法,但我希望我能回答您的问题。
首先,我们需要了解为什么要创建一个新的异常类型来封装一个异常。
当您想捕获Java异常并将其映射为代码中的一种特定错误情况时,这很有用。
例如:
1 2 3 4 5 |
因此,在上述情况下,您将能够跟踪整个应用程序中的特定错误。
当您有一个类来处理应用程序中的所有异常并将它们映射到特定的错误消息时,这很有用。