throw e和throw new Exception(e)有什么区别?

时间:2019-03-04 11:35:21

标签: java exception try-catch throw

考虑:

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

throw ethrow new Exception(e)有什么区别?

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

6 个答案:

答案 0 :(得分:48)

如果您不需要调整异常类型,则无需更改即可重新抛出(进一步抛出)同一实例

catch (IOException e) {
    throw e;
}

如果您确实需要调整例外类型,请e (作为原因)包装为所需类型的新例外

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

我认为所有其他情况都是代码异味。您的第二个片段是一个 good 示例。


以下是可能弹出的问题的答案。

  

我为什么要抛出异常?

您可以放手。但是,如果发生这种情况,您将无法在此级别上做任何事情。

当我们在方法中捕获异常时,我们仍在该方法中并可以访问其范围(例如,局部变量及其状态)。在抛出异常之前,我们可以做任何需要做的事情(例如,记录一条消息,将其发送到某个地方,对当前状态进行快照)。

  

我为什么要调整例外情况?

根据经验,

  

较高的层应捕获较低级别的异常,并代之以可以根据较高层抽象进行解释的异常。

     

有效的Java-第2版-项目61:抛出适用于抽象的异常

换句话说,在某个时候,应该将晦涩的IOException转换为清晰的MySpecificBusinessRuleException

我将其称为“调整异常类型” ,聪明的人将其称为 exception translation (尤其是 exception chaining


为清楚起见,让我们举一些愚蠢的例子。

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"的罗汉版,应该重构为后者。

答案 1 :(得分:14)

catch (IOException e) {
    throw e;
}

您将看到仅具有原始stacktrace的原始异常。您不会在堆栈跟踪中看到此“重新抛出”行,因此它是透明的。

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

您将看到创建的IllegalStateException及其堆栈跟踪以及原始异常信息和堆栈跟踪的“原因”。您正在(即将)将引发的异常设置为新创建的IOException的原因。上层将看到IllegalStateException,并且可以捕获(您不会捕获该捕获原因异常)。

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

您只会看到IOException创建中的当前堆栈跟踪,没有添加原因。

答案 2 :(得分:3)

好吧,基本上,throw e“重新抛出” 所有原始值-以及某些代码流,应将其隐藏,例如,出于安全原因。如果您要重新创建异常,则将在该位置获得-或可以获取-另一个堆栈跟踪。

因此,我想说,您可以选择屏蔽某些数据(不知道,例如,您可以将异常记录到特殊日志中,但是您希望将其他诊断数据传递给最终用户)。

让我们检查一下以下内容:

  • 我创建了一个仅作为异常的简单生成器的类
  • 另一个类允许重新抛出或重新创建异常
  • 然后,我只是打印堆栈跟踪并比较结果

异常生成器

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
===========

在这种情况下,您几乎可以看到相同的数据,但是可以看到一些其他的原始消息,因为我使用了相同的Exception来构建新数据,但是您不需要这样做,因此您可以掩盖原始原因,或者您不需要公开应用程序的逻辑,例如让我们再看一个示例:

  • 我将仅从原始异常中排除原因,但它将覆盖数据
  • 如您所见,
  • ,新创建的异常不包含完整的堆栈跟踪,因为 起源

所以:

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)
===========

所以,我想说,用相同的实例重新创建异常是没有意义的,但是在某些情况下,您可能想要那样做-屏蔽数据,或者如果您使用另一种情况,也可以想要更改异常类型-例如,从I / O异常更改为通用异常等。

在现实世界中,我可以记住真正经常发生的问题:当某些Web门户仅通过这种打印情况处理PHP脚本中的异常时,然后通常是在与数据库的连接无法正常工作时,出现连接字符串(例如,数据库地址和明文凭证)在Web浏览器中可见。 :)

答案 3 :(得分:2)

在这种情况下,此示例没有多大意义,因为您将抛出相同的异常并且不执行其他任何操作。至少将其记录会更有意义。您正在捕获一个异常来处理或记录它。如果您无法处理它,请将其重新扔掉(情况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这样的异常对客户端很少有用。您需要在您的业务领域范围内发送更具体明确的信息。

IOExceptionUnchecked 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

从堆栈跟踪中可以看到,根本原因是记录器可以帮助您找到原始问题,并且业务域异常已发送给用户。

答案 4 :(得分:1)

引发异常时,您所做的事情与创建或声明实例非常相似。

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

这是我一直在学习正确处理异常的方法,但我希望我能回答您的问题。

答案 5 :(得分:0)

首先,我们需要了解为什么要创建一个新的异常类型来封装异常。

当您想捕获Java异常并将其映射为代码中的一种特定错误情况时,这很有用。

例如:

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

因此,在上述情况下,您将能够跟踪应用中的特定错误。
当您有一个类来处理应用程序中的所有异常并将它们映射到特定的错误消息时,这很有用。