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);
    }