【问题标题】:How to throw an Exception inside a Try/Catch block?如何在 Try/Catch 块中抛出异常?
【发布时间】:2020-12-16 23:19:11
【问题描述】:

我有以下 Java 方法:

public Class createClass(Class class) {
    try {
        // retrieve the professor of the class and check if he exists
        Professor professorFound = professorRepository.findById(class.getProfessorId());
        if (professorFound != null) {
           // if the professor exists, then check if he already has a class
           // with the same id
           List<Class> classes = professorFound.getClasses();
           List<Class> classFound = classes.stream().... // loop to find the class... 

           // if he has, throw an exception
           if(!classFound.isEmpty()) {
                throw new ClassAlreadyRegisteredException();

           } else {
                // if he does not have, then create the class
                Class class = new Class();
                professorFound.getClasses().add(class);
                return professorRepository.save(professorFound);
           }
        } else {
           // if the professor does not exist, throw an exception
           throw new ProfessorNotFoundException();
    } catch (Exception e) {
        // if there is any other error during the communication with the database, 
        // throw a generic IOException
        throw new ClassIOException();
    }
}

基本上,我需要的是抛出特定的异常(如果请求中通知的教授不存在,或者教授已经有一个具有相同 id 的课程),或者如果有任何抛出一个通用的IOException与数据库通信过程中的其他错误。

但是,按照我开发的方式,如果抛出任何特定的Exception,try 块将捕获异常并抛出通用 IOException。

我该如何解决这个问题?

我很想了解这种情况下的最佳做法。 我应该分别捕获每个特定异常并抛出两次吗? 这是一个好习惯吗?

编辑:

这就是我的ClassAlreadyRegisteredException 的样子:

public class ClassAlreadyRegisteredException extends ApiException {

    private static final long serialVersionUID = 1L;

    public ClassAlreadyRegisteredException(String code, String message, String developerMessage, String origin, HttpStatus status) {
        super(code,message,developerMessage,origin, status);
    }
}

这就是我的ApiException 的样子:

@Data
@AllArgsConstructor
@RequiredArgsConstructor
@EqualsAndHashCode(callSuper = false)
public class ApiException extends RuntimeException{
    
    private static final long serialVersionUID = 1L;
    
    private String code;
    private String userMessage;
    private String developerMessage;
    private String origin;
    private HttpStatus status;
}

提前致谢。

【问题讨论】:

  • 你绝对应该改变你的Class类的名字;永远不要将一个类命名为与 java.lang 中的任何东西相同的东西。
  • 谢谢,你是对的。实际上ClassProfessor 并不是类的原始名称;为了更好地解释问题,我使用了这些名称。

标签: java spring-boot


【解决方案1】:

接住再扔。

try {
   ... same as before ...
} catch (ClassAlreadyRegisteredException | ProfessorNotFoundException e) {
    throw e;
} catch (Exception e) {
    // if there is any other error during the communication with the database, 
    // throw a generic IOException
    throw new ClassIOException();
}

或者,记住稍后抛出的异常。

 Exception fail = null;

 try {
      ….
     // save exception in place of existing throws
     // for example:
     fail = new ClassAlreadyRegisteredException();
     …
 } catch (Exception ex) {
     ...same as original...
 }

 if (fail != null) {
     throw fail;
 }

我同时使用这两种技术;选择取决于在任何给定情况下哪个更简单。两者都无可争辩地更好。

对于 catch 和 re-throw 方法,您必须使 catch-and-rethrow 异常列表与您在 try 子句中实际抛出的异常保持一致。在较大的情况下,我会通过使用异常层次结构来避免这个问题,这样我就可以捕获公共基类。

对于保存并抛出方法,您必须安排控制流,以便在检测到故障后不做任何重要的事情,因为您没有立即的“抛出”命令来退出 try 子句。尽管如此,在某些情况下它还是很简单的。原来的例子就是这样。

【讨论】:

  • 使用第二种技术有什么好处?
  • 答案已更新以添加基本原理,但只是在某些情况下它会导致代码更易于遵循。很多编程都是审美判断,而不是固定规则的应用。
【解决方案2】:

已检查与未检查的异常

在 catch 块中抛出异常是完全可以接受的。一个常见的用例是获取已检查的 Exception 并抛出未检查的 RuntimeException,这将使异常冒泡到需要处理的地方。

您需要将检查异常用于连接/IO、SQL 异常等用例。

处理检查的异常

为了回答您的问题,在大多数连接到数据库的库中,如果存在任何连接问题,则会抛出选中的 IOException。对于这些情况,您始终可以在方法签名public Class createClass() throws IOException中指定这一点

这指定createClass() 的任何调用者必须履行处理IOException 的合同。

您可以将其重新抛出为 RuntimeException

try {
   ...
} catch (IOException e) {
   throw new RuntimeException(e); // <- send the original exception so you can preserve the exception and stacktrace.
}

这实际上会冒泡到 STDOUT 或您的框架指定的任何处理程序。

注意:

但是,掩盖所有Exception 并抛出更具体的ClassIOException 可能会产生意想不到的后果。

如果您有 NullPointerException,这将被您的 catch (Exception e) 块捕获并重新抛出为 ClassIOException

这样做会破坏堆栈跟踪并导致您的错误日志更难调试。

了解什么是检查异常。

另一个提示是考虑您的 Exception 案例是什么。

如果它们是应用程序、服务或业务逻辑的标准流程 - 这些可能不是适当的例外。

ClassAlreadyRegisteredExceptionProfessorNotFoundException 在您的申请中可能不是例外情况...除非您的教授已经指定了这些情况。

如果情况需要,有时可以将它们作为RuntimeException 抛出。

最佳实践?

关于如何处理异常仍有很多意见。因此,在处理异常时,我会问自己一些经验法则问题

  1. 堆栈跟踪是否保留?我是否能够追溯到根异常
  2. 这是一种常见的逻辑,是否代表了与我的应用程序提供的不同的例外情况?
  3. 如果我抛出此异常,是否绝对有必要调用此方法来处理此异常逻辑?

【讨论】:

  • 能否详细说明这句话:“ClassAlreadyRegisteredExceptionProfessorNotFoundException 在您的申请中可能不是例外情况……除非您的教授已经指定了这些情况。” ?如果我不将它们作为异常处理,有什么替代方法?
  • 您的 ClassAlreadyRegisteredException 和 ProfessorNotFoundException 在当前状态下很好。但是,由于您使用的是 Java8,因此它是 Optional 的合适用例
  • 不应将 NullPointerException 作为 ClassIOException 重新抛出。通常,捕获 ExceptionThrowable 的 catch 块太大了,会导致这样的副作用。
  • ClassAlreadyRegisteredException 或 ProfessorNotFoundException 是从什么扩展而来的?
  • 用清晰的语言解释得很好。喜欢它
猜你喜欢
  • 2011-03-18
  • 1970-01-01
  • 1970-01-01
  • 2012-02-20
  • 2011-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多