【问题标题】:What is the difference between throw e and throw new Exception(e)?throw e 和 throw new Exception(e) 有什么区别?
【发布时间】:2019-03-04 11:35:20
【问题描述】:

考虑:

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

【问题讨论】:

  • throw 后跟一个解析为 Throwable 对象的表达式。 e 是一个 Throwable 对象表达式,new Exception(e) 也是如此。不同之处在于可投掷对象的创建方式。 ecatch 块提供给您,new Exception(e) 正在由您的代码创建。
  • 在这个例子中是没有意义的。但是,如果您想使用自己的异常类型,则可以执行 catch(Exception e) { throw new MyException(e);这可以使您的异常处理代码更容易/最小
  • Former 重新抛出一个已经存在的异常。后者创建了一个新异常,ecause(请参阅文档)。也称为捎带。然后在堆栈跟踪中,您会看到异常,然后是“引起的”,然后是另一个异常的堆栈跟踪。

标签: java exception try-catch throw


【解决方案1】:

如果您不需要调整异常类型,则重新抛出(进一步抛出)同一个实例而不做任何更改:

catch (IOException e) {
    throw e;
}

如果确实需要调整异常类型,则e(作为原因)放入所需类型的新异常中。

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

我认为所有其他情况都是代码异味。你的第二个 sn-p 就是一个的例子。


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

我为什么要重新抛出异常?

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

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

我为什么要调整异常?

根据经验,

较高层应该捕获较低级别的异常,并在它们的位置抛出异常,这些异常可以用较高级别的抽象来解释。

Effective Java - 第 2 版 - 第 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) { }
}

很明显new String(new String("myString"))"myString"的罗嗦版本,应该重构为后者。

【讨论】:

    【解决方案2】:
    catch (IOException e) {
        throw e;
    }
    

    您将仅看到原始堆栈跟踪的原始异常。你不会在堆栈跟踪中看到这个“重新抛出”行,所以它有点透明。

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

    您将看到创建的IllegalStateException 及其堆栈跟踪以及“由”原始异常信息和堆栈跟踪。您将(即将)设置为引发的异常作为新创建的IOException 的原因。上层会看到IllegalStateException,这可能会被捕获(你不会捕获那个捕获原因异常)。

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

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

    【讨论】:

    • 当某事物被称为“透明”时,它意味着它非常清晰;但是,在您的示例中,由于堆栈跟踪会省略重新抛出,因此它实际上更加模糊或模糊。对吗?
    • @Tas 这是透明的,就像“不可见”一样。
    【解决方案3】:

    好吧,基本上,throw e“重新抛出”(传递)所有原始值(因为相同的 Exception 实例,将使用相同的对象)- 这将导致,它会还传递一些代码流-可能应该隐藏,例如出于安全原因。

    如果您将重新创建异常,您将获得 - 或者更好地说“您可以获得” - 使用 new Exception instance 在该位置的另一个堆栈跟踪。

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

    让我们来看看下面的细节:

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

    异常生成器

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

    在这种情况下,您可以看到大部分相同的数据,但也可以看到一些额外的数据,您可以看到原始消息,因为我使用相同的异常来构建新的 - 但没有必要像这样创建新的异常实例,那么你可以掩盖原来的原因,或者你不需要暴露应用程序的逻辑,例如让我们再看一个例子:

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

    所以:

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

    所以,我想说,用同一个实例重新创建异常有点没有意义,但是一旦需要异常转换(重新键入另一个异常类型(那么实际上你应该也可以使用java cast),或者你想要掩码数据

    在现实世界中,我记得很常见的问题,当某些门户网站仅通过打印这种情况来处理 PHP 脚本中的异常时,通常,当与数据库的连接无法正常工作时,连接字符串例如,(包括明文形式的数据库地址和凭据)在 Web 浏览器中可见。 :)

    【讨论】:

    • 在互联网上这是最好的答案
    【解决方案4】:

    这个例子在这种情况下没有多大意义,因为你抛出了同样的异常而没有做任何其他事情。至少记录它会更有意义。您正在捕获异常来处理它或记录它。如果您无法处理,请重新扔(案例 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);
    }
    

    当您想用Unchecked exception 替换Checked Exception 以保留问题的起源 并保留所有信息(称为异常链接)时,通常会发生这种情况。

    常规用例:

    • 您无法处理Checked Exception,并且您不想将其重新抛出给调用者。重新抛出已检查的异常将强制调用者处理它。如果没有正常的康复病例,这不是您想要做的。
    • IOException 之类的异常很少对客户端有用。您需要在您的业务领域范围内发送更具体、更明确的内容。

    IOException 包裹到Unchecked ExceptionDocumentReadException 会阐明实际情况,不会强迫调用者处理:

    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
    

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

    【讨论】:

      【解决方案5】:

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

      throw e 声明 IOException 的一个实例(在设备上分配一个内存空间而不存储数据),因此当您声明一个 pre-例外。

      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
      

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

      【讨论】:

        【解决方案6】:

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

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

        例如:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2018-05-30
          • 1970-01-01
          • 2012-06-04
          • 2014-03-18
          • 2010-12-14
          • 2012-02-27
          相关资源
          最近更新 更多