java 为什么在捕获异常时顺序很重要?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/15167899/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why does order matter when catching exceptions?
提问by user2124033
I had to answer this question with some code:
我不得不用一些代码来回答这个问题:
Suppose I wrote the following method specification:
public void manipulateData ( ) throws java.sql.SQLException, java.sql.SQLDataException
You are writing code for a database program that will use this method and you want to handle each specifically. What should the try/catch clause look like?
You can use no-ops-- empty blocks {}--for the catch clause contents.
We are only interested in the syntax and structure of the statements here.
假设我编写了以下方法规范:
public void manipulateData ( ) throws java.sql.SQLException, java.sql.SQLDataException
您正在为将使用此方法的数据库程序编写代码,并且您希望专门处理每个程序。try/catch 子句应该是什么样的?
您可以使用 no-ops--empty blocks {}--作为 catch 子句的内容。
我们只对这里的语句的语法和结构感兴趣。
I answered with this:
我是这样回答的:
try {
} catch(java.sql.SQLException e) {
}
catch(java.sql.SQLDataException e) {
}
He did not accept the answer for this reason:
他不接受这个答案是因为这个:
"Your catch clauses are in the wrong order. Can you explain why the order matters?"
“你的catch子句顺序错误。你能解释一下为什么顺序很重要吗?”
Is he correct in his answer?
他的回答正确吗?
回答by user000001
Yes he is correct. As you can see in the Javadoc, SQLDataException
is a subclass of SQLException
. Therefore, your answer is wrong, since it would create a block of unreachable code in the second catch
.
是的,他是对的。正如您在Javadoc 中看到的,SQLDataException
是SQLException
. 因此,您的答案是错误的,因为它会在第二个catch
.
In Java, this code would not even compile. In other languages (e.g. python) such code would create a subtle bug, because SQLDataException
would actually be caught in the first block, and not in the second one (as would be the case if it was not a subclass).
在 Java 中,此代码甚至无法编译。在其他语言(例如python)中,这样的代码会产生一个微妙的错误,因为SQLDataException
实际上会在第一个块中被捕获,而不是在第二个块中(如果它不是子类就会出现这种情况)。
Had you answered catch(java.sql.SQLException | java.sql.SQLDataException e) { }
, it would still be incorrect, since the question asks to handle each exception specifically.
如果您回答catch(java.sql.SQLException | java.sql.SQLDataException e) { }
,它仍然是不正确的,因为该问题要求专门处理每个异常。
The correct answer is in Grijesh's answer
正确答案在Grijesh 的答案中
回答by DigCamara
In Java you have to put the least inclusive Exception first. The next exceptions must be more inclusive (when they are related).
在 Java 中,您必须首先放置包含最少的异常。下一个例外必须更具包容性(当它们相关时)。
For instance: if you put the most inclusive of all (Exception) first, the next ones will never be called. Code like this:
例如:如果您将最包容的(异常)放在首位,则永远不会调用下一个。像这样的代码:
try {
System.out.println("Trying to prove a point");
throw new java.sql.SqlDataException("Where will I show up?");
}catch(Exception e){
System.out.println("First catch");
} catch(java.sql.SQLException e) {
System.out.println("Second catch");
}
catch(java.sql.SQLDataException e) {
System.out.println("Third catch");
}
will never print the message you'd expect it to print.
永远不会打印您希望它打印的消息。
回答by Grijesh Chauhan
Order matter when catching exceptions because of following reason:
由于以下原因捕获异常时的顺序很重要:
Remember:
记住:
- A base class variable can also reference child class object.
e
is a reference variable
- 基类变量也可以引用子类对象。
e
是参考变量
catch(ExceptionType e){ }
catch(ExceptionType e){ }
The lowercase character e is a reference to the thrown (and caught) ExceptionType
object.
小写字符 e 是对抛出(和捕获)ExceptionType
对象的引用。
Reason why your code not accepted?
为什么您的代码不被接受?
It is important to remember that exception subclass must come before any their superclasses. This is because a catch statement that uses a superclasses will catch exception of that type plus any of its subclasses. Thus, a subclass would never be reached if it came after its superclass.
Further, in Java, Unreachable code is an error.
重要的是要记住异常子类必须出现在它们的任何超类之前。这是因为使用超类的 catch 语句将捕获该类型及其任何子类的异常。因此,如果子类出现在其超类之后,则永远不会到达该子类。
此外,在 Java 中,Unreachable 代码是一个错误。
SQLException
is supperclass of SQLDataException
SQLException
是超类 SQLDataException
+----+----+----+
| SQLException | `e` can reference SQLException as well as SQLDataException
+----+----+----+
^
|
|
+----+----+----+---+
| SQLDataException | More specific
+----+----+----+---+
And if your write like having error Unreachable code
(read comments):
如果你的写作喜欢有错误 Unreachable code
(阅读评论):
try{
}
catch(java.sql.SQLException e){//also catch exception of SQLDataException type
}
catch(java.sql.SQLDataException e){//hence this remains Unreachable code
}
If you try to compile this program, you will receive an error messagestating that the first catch statement will handle all SQLException-based errors, including SQLDataException. This means that the second catch statement will never execute.
如果您尝试编译此程序,您将收到一条错误消息,指出第一个 catch 语句将处理所有基于 SQLException 的错误,包括 SQLDataException。这意味着第二个 catch 语句永远不会执行。
Correct Solution?
正确的解决方案?
To fix it reverse the order of the catch statements.That is following:
要修复它,请颠倒 catch 语句的顺序。即如下:
try{
}
catch(java.sql.SQLDataException e){
}catch(java.sql.SQLException e){
}
回答by kbbucks
SQLDataException
will never be hit as SQLException
will catch any SQL exceptions before they reach SQLDataException
.
SQLDataException
永远不会被命中,因为SQLException
在它们到达之前会捕获任何 SQL 异常SQLDataException
。
SQLDataException
is a sub-classof SQLException
SQLDataException
是一个子类的SQLException
回答by PhD
Think of the inheritance hierarchies of the Exceptions: SQLDataException extends SQLException
So if you catch the 'generic' one first (i.e. the topmost base-class of the hierarchy) then every thing 'below' it is of the same type due to polymorphism i.e., SQLDataExceptionisa
SQLException
考虑异常的继承层次结构:SQLDataException extends SQLException
因此,如果您首先捕获“通用”一个(即层次结构的最顶层基类),那么由于多态性,它“下面”的所有事物都属于同一类型,即SQLDataException isa
SQLException
Thus you should capture them in bottom up order w.r.t. the inheritance hierarchy i.e., subclasses first all the way to (generic) base class. This is so because the catch
clauses are evaluated in the order you've declared them.
因此,您应该按照继承层次结构自下而上的顺序捕获它们,即子类首先一直到(通用)基类。这是因为这些catch
子句是按照您声明它们的顺序进行评估的。
回答by Marcin Szymczak
Java Language Specification§11.2.3 explains this situation:
Java Language Specification§11.2.3 解释了这种情况:
It is a compile-time errorif a catch clause can catch checked exception class E1 and a preceding catch clause of the immediately enclosing try statement can catch E1 or a superclass of E1.
如果 catch 子句可以捕获已检查的异常类 E1,并且紧接其后的 try 语句的前面的 catch 子句可以捕获 E1 或 E1 的超类,则这是编译时错误。
Version in plain english:
纯英文版本:
More general exceptions must be after specific ones. More general exceptions must be after specific ones.
更一般的例外必须在特定例外之后。更一般的例外必须在特定例外之后。
回答by Mikhail
When exception happens in the method, a special method exception table is checked, it contains records for each catch block: exception type, start instruction and end instruction. If the order of exception is incorrect, some catch block would be unreachable. Sure javac could sort records in this table for developer, but it does not.
当方法发生异常时,会检查一个特殊的方法异常表,它包含每个catch块的记录:异常类型、开始指令和结束指令。如果异常顺序不正确,则某些 catch 块将无法访问。当然,javac 可以为开发人员排序该表中的记录,但它不能。
The order in which the exception handlers of a method are searched for a match is important. Within a class file, the exception handlers for each method are stored in a table (§4.7.3). At run time, when an exception is thrown, the Java Virtual Machine searches the exception handlers of the current method in the order that they appear in the corresponding exception handler table in the class file, starting from the beginning of that table.
在方法的异常处理程序中搜索匹配项的顺序很重要。在类文件中,每个方法的异常处理程序都存储在一个表中(第 4.7.3 节)。在运行时,当抛出异常时,Java 虚拟机按照它们在类文件中相应异常处理程序表中出现的顺序搜索当前方法的异常处理程序,从该表的开头开始。
As long as first exception is a parent of the second, the second block becomes unreacheble.
只要第一个异常是第二个异常的父异常,第二个块就无法访问。
回答by Apurv
For compiler multiple catch statements are like if..else if..else if..
对于编译器,多个 catch 语句就像 if..else if..else if..
So, from the point where compiler can map the generated exception (directly or by implicit type conversion), it will not execute subsequent catch statements.
因此,从编译器可以映射生成的异常(直接或通过隐式类型转换)的角度来看,它不会执行后续的 catch 语句。
To avoid this implicit type conversion you should keep the more generic exception at last. More derived should be stated at the beginning of he catch statements and most generic should go at the last catch statements.
为了避免这种隐式类型转换,您应该最后保留更通用的异常。更多的派生应该在他的 catch 语句的开头说明,最通用的应该放在最后的 catch 语句中。
SQLDataException is derived from SQLException which is interns deiced from Exception. So, you will not be able to execute any code written in catch(java.sql.SQLDataException e){}
this block. Compiler even flags for that situation that its a dead code and will not be executed.
SQLDataException 派生自 SQLException,SQLException 是从 Exception 中分离出来的实习生。因此,您将无法执行catch(java.sql.SQLDataException e){}
此块中编写的任何代码。编译器甚至会在这种情况下标记它是死代码并且不会被执行。