为什么订单在捕获异常时很重要?

时间:2013-03-01 22:26:12

标签: java exception-handling

我不得不用一些代码回答这个问题:

  

假设我编写了以下方法规范:
   public void manipulateData ( ) throws java.sql.SQLException, java.sql.SQLDataException

     

您正在为数据库编写代码   将使用此方法并且您想要处理每个方法的程序   特别。 try / catch子句应该是什么样的?
  您可以使用no-ops-- empty blocks {} - 用于catch子句内容   我们只对这里的语句的语法和结构感兴趣。

我回答说:

try {

} catch(java.sql.SQLException e) {

}
catch(java.sql.SQLDataException e) {

}

由于这个原因,他没有接受答案:

“您的捕获条款的顺序错误。您能解释一下订单的重要性吗?”

他的回答是否正确?

8 个答案:

答案 0 :(得分:42)

是的,他是对的。正如您在Javadoc中看到的那样,SQLDataExceptionSQLException的子类。因此,您的答案是错误的,因为它会在第二个catch中创建一个无法访问的代码块。

在Java中,这段代码甚至无法编译。在其他语言(例如python)中,这样的代码会创建一个微妙的bug,因为SQLDataException实际上会在第一个块中捕获,而不是在第二个块中捕获(如果它不是子类的话就是这种情况)。

如果你回答catch(java.sql.SQLException | java.sql.SQLDataException e) { },它仍然是不正确的,因为问题要求具体处理每个例外。

正确答案在Grijesh's answer

答案 1 :(得分:22)

在Java中,您必须首先放置包含最少的Exception。接下来的例外必须更具包容性(当它们相关时)。

例如:如果你把所有(最多)的包容性放在首位,那么永远不会调用下一个。像这样的代码:

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

永远不会打印您希望打印的信息。

答案 2 :(得分:6)

由于以下原因,在捕获异常时排序很重要:

记住:

  • 基类变量也可以引用子类对象。
  • e是参考变量
catch(ExceptionType e){
}

小写字符e是对抛出(和捕获)ExceptionType对象的引用。

您的代码未被接受的原因是什么?

  

重要的是要记住异常子类必须在它们的超类之前。这是因为使用超类的catch语句将捕获该类型及其任何子类的异常。因此,如果它来自超类,则永远不会到达子类   此外,在Java中,无法访问的代码是错误。

SQLExceptionSQLDataException

的超类
   +----+----+----+
   | SQLException |  `e` can reference SQLException as well as SQLDataException
   +----+----+----+
          ^
          |
          |
+----+----+----+---+
| SQLDataException |   More specific 
+----+----+----+---+

如果你的写作有错误Unreachable code阅读评论):

try{

} 
catch(java.sql.SQLException e){//also catch exception of SQLDataException type 

}
catch(java.sql.SQLDataException e){//hence this remains Unreachable code

}

如果尝试编译此程序,您将收到一条错误消息,指出第一个catch语句将处理所有基于SQLException的错误,包括SQLDataException。 这意味着第二个catch语句永远不会执行。

正确解决方案?

要修复它反转catch语句的顺序。接下来是:

try{

} 
catch(java.sql.SQLDataException e){

}catch(java.sql.SQLException e){

}

答案 3 :(得分:5)

SQLDataException将永远不会被点击,因为SQLException会在到达SQLDataException之前捕获任何SQL例外。

SQLDataExceptionSQLException子类

答案 4 :(得分:5)

考虑异常的继承层次结构:SQLDataException extends SQLException因此,如果首先捕获“泛型”(即层次结构中最顶层的基类),那么“下面”的每个东西都属于同一类型由于多态性,即 SQLDataException isa SQLException

因此你应该按照自下而上的顺序捕获它们w.r.t.继承层次结构,即子类首先一直到(泛型)基类。这是因为catch子句按照您声明它们的顺序进行评估。

答案 5 :(得分:2)

Java Language Specification§11.2.3解释了这种情况:

  

如果catch子句可以捕获,则编译时错误   异常类E1和紧接着的前一个catch子句   封闭的try语句可以捕获E1或E1的超类。

普通英语版本:

  

更一般的例外必须在具体的例外之后。   更一般的例外必须在特定的例外之后。

答案 6 :(得分:1)

对于编译器,多个catch语句类似于if..else if..else if ..

因此,从编译器可以映射生成的异常(直接或通过隐式类型转换)的角度来看,它不会执行后续的catch语句。

为了避免这种隐式类型转换,您应该最后保留更通用的异常。应该在他的捕获语句的开头说明更多的派生,并且最通用的应该在最后的捕获声明中说明。

SQLDataException派生自SQLException,它是来自Exception的实习生。因此,您将无法执行catch(java.sql.SQLDataException e){}此块中编写的任何代码。编译甚至是那种情况的标志,它是一个死代码并且不会被执行。

答案 7 :(得分:1)

当方法中发生异常时,会检查特殊方法异常表,它包含每个catch块的记录:异常类型,开始指令和结束指令。如果异常的顺序不正确,则某些catch块将无法访问。当然javac可以为开发人员对此表中的记录进行排序,但事实并非如此。

JVM规范:12

  

搜索方法的异常处理程序以进行匹配的顺序非常重要。在类文件中,每个方法的异常处理程序都存储在一个表中(第4.7.3节)。在运行时,当抛出异常时,Java虚拟机将按照它们出现在类文件中相应异常处理程序表中的顺序搜索当前方法的异常处理程序,从该表的开头开始。

只要第一个例外是第二个例句的父节点,第二个块就会变得不可解密。