Java 抛出e和抛出新异常(e)之间有什么区别?
考虑:Java 抛出e和抛出新异常(e)之间有什么区别?,java,exception,try-catch,throw,Java,Exception,Try Catch,Throw,考虑: try { // Some code here } catch (IOException e) { throw e; } catch (Exception e) { throw e; } 抛出e和抛出新异常(e)之间有什么区别 您将只看到原始异常和原始stacktrace。你不会在stacktrace中看到这条“rethrow”线,所以它是透明的 catch (IOException e) { throw new IllegalStateExcepti
try {
// Some code here
} catch (IOException e) {
throw e;
} catch (Exception e) {
throw e;
}
抛出e
和抛出新异常(e)
之间有什么区别
您将只看到原始异常和原始stacktrace。你不会在stacktrace中看到这条“rethrow”线,所以它是透明的
catch (IOException e) {
throw new IllegalStateException(e);
}
您将看到创建的IllegalStateException
及其stacktrace,其中包含“由”原始异常信息和stacktrace。您正在将(即将)引发的异常设置为新创建的IOException
的原因。上层将看到IllegalStateException
,这将有可能捕获(您不会捕获捕获原因异常)
您将只看到创建的
IOException
的当前堆栈跟踪,没有添加原因。如果不需要调整异常类型,您可以重新运行(进一步抛出)相同的实例,而不做任何更改:
catch (IOException e) {
throw e;
}
如果确实需要调整异常类型,请将e
(作为原因)包装到所需类型的新异常中
catch (IOException e) {
throw new IllegalArgumentException(e);
}
我认为所有其他场景都有代码的味道。您的第二个代码片段就是一个很好的例子
下面是可能出现的问题的答案 为什么我要重新引发一个异常 你可以放手。但如果发生这种情况,你将无法在这个级别上做任何事情 当我们在方法中捕获异常时,我们仍然在该方法中,并且可以访问其范围(例如局部变量及其状态)。在重新显示异常之前,我们可以执行任何需要执行的操作(例如,记录消息、将其发送到某处、制作当前状态的快照) 为什么我要调整异常 根据经验 更高的层应该捕获较低级别的异常,并在它们的位置抛出可以用更高级别抽象来解释的异常 有效Java-第二版-第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) { }
}
很明显,
newstring(newstring(“myString”))
是“myString”
的一个冗长版本,应该重构为后者。这个例子在这个上下文中没有多大意义,因为你抛出了相同的异常,没有做任何其他事情。至少把它记录下来会更有意义。您正在捕获一个异常来处理它或记录它。如果您无法处理它,请重新旋转它(案例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);
}
当您希望用未检查的异常
替换已检查的异常
以保留问题的来源并保留所有信息(称为异常链接)时,通常会发生这种情况
常规用例:
- 您无法处理
,也不想将其重新发送给调用方。重新引用选中的异常将强制调用方处理它。如果没有正常的恢复案例,这不是您想要做的选中的异常
- 像
这样的异常很少对客户端有用。您需要在您的业务领域范围内发送更具体、更清晰的内容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
从堆栈跟踪中可以看出,根本原因是一个日志记录程序帮助您找出原始问题,并且业务域异常已发送给用户。好的,基本上,
抛出e
将“rethrow”(传递)所有原始值(因为相同的异常
实例将使用相同的对象)-这将导致,它还将传递一些代码流-可能应该隐藏,例如由于安全原因
如果您将重新创建异常,您将使用新异常实例
在该位置获得另一个stacktrace,或者更好地说“您可以获得”
因此,我想说,在这种情况下,您可以选择屏蔽一些数据(例如,您将异常记录到一个特殊的日志中,但您希望将其他诊断数据传递给最终用户)
让我们检查一下以下细节:
- 我创建了一个类,作为唯一一个简单的异常生成器
- 另一个类允许重新刷新或重新创建异常
- 之后,我只是打印stacktrace并比较结果
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) {
//re-throw existing exception
throw e;
}
}
public static void reCreateNewException() throws Exception {
try {
ExceptionsThrow.throwNewException();
} catch (Exception e) {
//recreation of the exception > new instance is thrown
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
===========
在这种情况下,您可以看到大部分相同的数据,但也可以看到一些额外的原始消息,因为我使用了相同的异常来构建新的异常-但没有必要像这样创建新的异常实例,因此您可以屏蔽原始原因,或者不需要公开应用程序的逻辑,例如,让我们再检查一个示例:
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("===========");
}
- 我将仅从原始异常中获取原因,但它将覆盖数据
- 如您所见,新创建的异常不包含
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) ===========
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
try { // read file } catch (IOException e) { throw new MyAppFailedToReadFile(e); }