1

我昨天偶然发现了一个有趣的错误,并且已经修复了它,但今天早上它仍然困扰着我,所以我想看看是否有人可以对这个问题有所了解。

有问题的代码:

final ResultSet rs = prepStatement.executeQuery();
try
{
   if (!rs.next())
   {
      throw new IllegalStateException("Expected non-empty result");
   }
   return rs.getInt(0 + 1);
}
finally
{
   rs.close();
}

现在对于从那以后没有制作的部分。每隔一段时间,return 语句就会抛出一个异常,表明 getInt(int) 已在关闭的 ResultSet 上被调用。我验证了代码中的任何地方都没有关闭准备好的语句,如果数据库正在关闭,我也会看到其他错误。这让我相信,有时,finally 块会在 return 语句之前执行。我唯一能想到的是热点编译器并不总是正确的。我正在使用下面列出的 Oracle JVM。

java 版本“1.7.0_45”
Java(TM) SE 运行时环境(构建 1.7.0_45-b18)
Java HotSpot(TM) 64 位服务器 VM(构建 24.45-b08,混合模式)

我觉得我应该提一下,我已经看到了有关此排序的许多其他问题,但它们似乎都表明它是一成不变的,我似乎正在目睹一些不同的事情。

Try-catch-finally-return 澄清
https://stackoverflow.com/questions/20164755/the-order-of-invoking-finally-block
finally 总是在 Java 中执行吗?

4

1 回答 1

1

我编写并编译了以下类

public class Examples {
    public int answer(PreparedStatement prepStatement) throws SQLException {
        final ResultSet rs = prepStatement.executeQuery();
        try {
            if (!rs.next()) {
                throw new IllegalStateException("Expected non-empty result");
            }
            return rs.getInt(1);
        } finally {
            rs.close();
        }
    }
}

使用以下命令

[s_delima@ml-l-sotiriosd Downloads]$ java -version
java version "1.7.0_45"
Java(TM) SE Runtime Environment (build 1.7.0_45-b18)
Java HotSpot(TM) 64-Bit Server VM (build 24.45-b08, mixed mode)
[s_delima@ml-l-sotiriosd Downloads]$ javac Examples.java 
[s_delima@ml-l-sotiriosd Downloads]$ /usr/java/latest/bin/javap -c Examples
Compiled from "Examples.java"
public class Examples {
  public Examples();
    Code:
       0: aload_0       
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return        

  public int answer(java.sql.PreparedStatement) throws java.sql.SQLException;
    Code:
       0: aload_1       
       1: invokeinterface #2,  1            // InterfaceMethod java/sql/PreparedStatement.executeQuery:()Ljava/sql/ResultSet;
       6: astore_2      
       7: aload_2       
       8: invokeinterface #3,  1            // InterfaceMethod java/sql/ResultSet.next:()Z
      13: ifne          26
      16: new           #4                  // class java/lang/IllegalStateException
      19: dup           
      20: ldc           #5                  // String Expected non-empty result
      22: invokespecial #6                  // Method java/lang/IllegalStateException."<init>":(Ljava/lang/String;)V
      25: athrow        
      26: aload_2       
      27: iconst_1      
      28: invokeinterface #7,  2            // InterfaceMethod java/sql/ResultSet.getInt:(I)I
      33: istore_3      
      34: aload_2       
      35: invokeinterface #8,  1            // InterfaceMethod java/sql/ResultSet.close:()V
      40: iload_3       
      41: ireturn       
      42: astore        4
      44: aload_2       
      45: invokeinterface #8,  1            // InterfaceMethod java/sql/ResultSet.close:()V
      50: aload         4
      52: athrow        
    Exception table:
       from    to  target type
           7    34    42   any
          42    44    42   any
}

如果您按照字节码指令进行操作,您将看到 at被调用28rs.getInt(1)并且其值存储在 at 33。在rs.close()处调用35。存储的值在 处检索并在 处40返回41

您所经历的必须来自代码中的其他点。

于 2013-12-04T17:01:37.827 回答