英文:
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<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();
}
}
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); // <- 将原始异常传递,以便保留异常和堆栈跟踪。
}
这实际上会上升到标准输出(STDOUT)或者您的框架指定的处理程序。
注意:
然而,捕获所有 Exception
并抛出更具体的 ClassIOException
可能会产生意想不到的后果。
如果出现 NullPointerException
,它将被你的 catch (Exception e)
块捕获,并作为 ClassIOException
重新抛出。
这样做会破坏堆栈跟踪,并导致错误日志更难以调试。
理解何为已检查异常。
另一个提示是考虑你的 Exception
情况。
如果它们是应用程序、服务或业务逻辑的标准流程,这些可能不是合适的异常。
ClassAlreadyRegisteredException
和 ProfessorNotFoundException
可能不是你的应用程序中的异常情况... 除非这些已经被你的教授指定了。
有时,如果情况需要,可以将这些异常抛出为 RuntimeException
。
最佳实践?
关于如何处理异常仍然有许多不同的意见。所以在处理异常时,以下是我自己提出的一些经验法则问题:
- 堆栈跟踪是否得以保留?我是否能够追溯到根异常?
- 这是否是常见逻辑,并且代表了与我应用程序提供的内容不同的特殊情况?
- 如果我抛出这个异常,对于调用这个方法的任何东西来说,处理这个特殊逻辑是否是绝对必要的?
英文:
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); // <- 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
- Is the stacktrace preserved? Will I be able to trace back to the root Exception
- Is this common logic and represents an exceptional case that deviates from what my application provides?
- If I throw this exception, is it an absolute necessity for anything calling this method to handle this exceptional logic?
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论