如何在Try/Catch块内抛出异常?

huangapple go评论120阅读模式
英文:

How to throw an Exception inside a Try/Catch block?

问题

我有以下的Java方法:

public Class createClass(Class class) {
    try {
        // 检索班级的教授并检查是否存在
        Professor professorFound = professorRepository.findById(class.getProfessorId());
        if (professorFound != null) {
           // 如果教授存在,则检查是否已经有相同id的班级
           List<Class> classes = professorFound.getClasses();
           List<Class> classFound = classes.stream().... // 循环查找班级... 

           // 如果已经有了,抛出异常
           if(!classFound.isEmpty()) {
                throw new ClassAlreadyRegisteredException();

           } else {
                // 如果没有,创建班级
                Class newClass = new Class();
                professorFound.getClasses().add(newClass);
                return professorRepository.save(professorFound);
           }
        } else {
           // 如果教授不存在,抛出异常
           throw new ProfessorNotFoundException();
    } catch (Exception e) {
        // 如果在与数据库通信期间出现任何其他错误,抛出通用的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;
}

提前致谢。

英文:

I have the following Java method:

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&lt;Class&gt; classes = professorFound.getClasses();
           List&lt;Class&gt; 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();
    }
}

Basically, what I need is to throw specific Exceptions (if the professor informed in the request does not exist, or if the professor already has a class with the same id), or throw a generic IOException if there is any other error during the communication with the database.

However, in the way that I have developed, if any specific Exception is thrown, the try block will catch the exception and will throw a generic IOException.

How can I solve this problem?

I'm very interested in understanding what are the best practices in this case.
Should I catch each specific exception separately and throw them twice?
Is that a good practice?

EDIT:

This is how my ClassAlreadyRegisteredException looks like:

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

This is how my ApiException looks like:

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

Thanks in advance.

答案1

得分: 6

捕获并重新抛出

try {
   ... 与之前相同 ...
} catch (ClassAlreadyRegisteredException | ProfessorNotFoundException e) {
    throw e;
} catch (Exception e) {
    // 如果在与数据库通信期间出现任何其他错误,
    // 抛出通用的 IOException
    throw new ClassIOException();
}


或者记住异常以便稍后抛出

Exception fail = null;

try {
    .
    // 在现有的抛出语句位置保存异常
    // 例如:
    fail = new ClassAlreadyRegisteredException();
    
} catch (Exception ex) {
    ... 与原始代码相同 ...
}

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


我会同时使用这两种技术选择取决于特定情况下哪种更简单两种方法都没有绝对更好的说法

对于捕获并重新抛出的方法您必须始终将已捕获和重新抛出的异常列表与在 try 代码块内实际抛出的异常保持一致在较大的情况下我会通过使用异常层次结构来避免这个问题这样我就可以捕获通用的基类异常

对于保存并抛出的方法您必须安排控制流以便在检测到故障后不要执行任何重要操作因为您没有立即的throw命令来退出 try 代码块尽管如此在某些情况下情况就足够简单原始示例就是这样一个例子
英文:

Catch and re-throw.

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

Alternatively, remember the exception to throw it later.

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

I use both techniques; the choice depends on what is simpler in any given situation. Neither is uncontestably better.

For the catch and re-throw method, you have to keep the list of caught-and-rethrown exceptions consistent with the exceptions you actually throw from within the try-clause. In larger cases, I'd avoid that problem by using an exception hierarchy, so I could catch the common base class.

For the save-and-throw method, you have to arrange control flow so that nothing significant is done after detecting the failure, since you don't have the immediate 'throw' command to exit the try-clause. Nevertheless there are cases where it is simple enough; the original example is one such.

答案2

得分: 2

已检查 vs 未检查异常

在 catch 块中抛出异常是完全可以接受的。一个常见的用例是捕获已检查的 Exception 并抛出未检查的 RuntimeException,这将允许异常上升到需要处理的位置。

对于连接/IO、SQL 异常等情况,您会想要使用已检查的异常。

处理已检查的异常

回答你的问题,在大多数连接到数据库的库中,如果有任何连接问题,都会抛出已检查的 IOException。对于这些情况,你可以在方法签名中指定:public Class createClass() throws IOException

这表示调用 createClass() 的任何调用者都必须履行处理 IOException 的约定。

或者

你可以将其重新抛出为 RuntimeException

try {
   ...
} catch (IOException e) {
   throw new RuntimeException(e); // &lt;- 将原始异常传递,以便保留异常和堆栈跟踪。
}

这实际上会上升到标准输出(STDOUT)或者您的框架指定的处理程序。

注意:

然而,捕获所有 Exception 并抛出更具体的 ClassIOException 可能会产生意想不到的后果。

如果出现 NullPointerException,它将被你的 catch (Exception e) 块捕获,并作为 ClassIOException 重新抛出。

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

理解何为已检查异常。

另一个提示是考虑你的 Exception 情况。

如果它们是应用程序、服务或业务逻辑的标准流程,这些可能不是合适的异常。

ClassAlreadyRegisteredExceptionProfessorNotFoundException 可能不是你的应用程序中的异常情况... 除非这些已经被你的教授指定了。

有时,如果情况需要,可以将这些异常抛出为 RuntimeException

最佳实践?

关于如何处理异常仍然有许多不同的意见。所以在处理异常时,以下是我自己提出的一些经验法则问题:

  1. 堆栈跟踪是否得以保留?我是否能够追溯到根异常?
  2. 这是否是常见逻辑,并且代表了与我应用程序提供的内容不同的特殊情况?
  3. 如果我抛出这个异常,对于调用这个方法的任何东西来说,处理这个特殊逻辑是否是绝对必要的?
英文:

Checked vs Unchecked Exceptions

It's totally acceptable to throw an exception in a catch block. A common use case is to take a checked Exception and throw a unchecked RuntimeException which would allow the exception bubble up to where it needs to be handled.

You'll want to use checked exceptions for use cases such as Connectivity/IO, SQL exceptions..

Handling Checked Exceptions

To answer your question, in most libraries that connect to the database, an checked IOException is thrown if there are any connectivity issues. For these cases, you can always specify this in the method signature public Class createClass() throws IOException

this specifies that any callers of createClass() has to fulfill the contract that the IOException is handled.

or

You can rethrow this as a RuntimeException

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

This will essentially bubble up to STDOUT or whatever handler your framework specifies.

CAUTION:

However, catching an cover all Exception and throwing a more specific ClassIOException can have unintended consequences.

If you have a NullPointerException this will be captured by your catch (Exception e) block and rethrown as a ClassIOException

Doing this will corrupt the stacktrace and cause your error logs to be much more difficult to debug.

Understanding what constitutes an checked Exceptions.

Another tip is to consider what your Exception cases are.

If they are standard flow of the application, service, or business logic -- these may not be appropriate exceptions.

The ClassAlreadyRegisteredException and ProfessorNotFoundException may not be exceptional cases in your application... unless these are already specified by your professor.

Sometimes these can be thrown as RuntimeException if the situation calls for it.

Best Practices?

There are still many opinions on how exceptions are handled. So here are some rule of thumb questions I ask myself when working with exceptions

  1. Is the stacktrace preserved? Will I be able to trace back to the root Exception
  2. Is this common logic and represents an exceptional case that deviates from what my application provides?
  3. If I throw this exception, is it an absolute necessity for anything calling this method to handle this exceptional logic?

huangapple
  • 本文由 发表于 2020年8月28日 10:45:41
  • 转载请务必保留本文链接:https://go.coder-hub.com/63626734.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定