Java Try-Catch比Try-With-Resources贵还是便宜 问题:

Java Try-Catch比Try-With-Resources贵还是便宜 问题:,java,exception-handling,Java,Exception Handling,我最近刚开始重返Java,从未有机会使用尝试使用资源。从表面上看,它看起来很棒,因为它可以减少代码,但在幕后,它比传统的try-catch更昂贵还是更便宜?我知道try catch已经是一项昂贵的操作,因此我很好奇 我对这两种类型都做了一个简单的测试,根本没有注意到有什么不同: 测试示例 试用资源测试 long startTime=System.currentTimeMillis(); ArrayList list=null; try(Scanner sc=new Scanner(新文件(“Fi

我最近刚开始重返Java,从未有机会使用
尝试使用资源
。从表面上看,它看起来很棒,因为它可以减少代码,但在幕后,它比传统的
try-catch
更昂贵还是更便宜?我知道try catch
已经是一项昂贵的操作,因此我很好奇

我对这两种类型都做了一个简单的测试,根本没有注意到有什么不同:

测试示例 试用资源测试
long startTime=System.currentTimeMillis();
ArrayList list=null;
try(Scanner sc=new Scanner(新文件(“File.txt”)){
列表=新的ArrayList();
while(sc.hasNext()){
list.add(sc.next());
}
}捕获(例外情况除外){
System.err.println(“错误:+ex.getMessage());
}最后{
long-endTime=System.currentTimeMillis();
System.out.println(“程序在”+(endTime-startTime)+“ms”中完成”;
}
传统的试捕试验
long startTime=System.currentTimeMillis();
ArrayList list=null;
扫描仪sc=空;
试一试{
sc=新扫描仪(新文件(“File.txt”);
列表=新的ArrayList();
while(sc.hasNext()){
list.add(sc.next());
}
}捕获(例外情况除外){
System.err.println(“错误:+ex.getMessage());
}最后{
sc.close();
long-endTime=System.currentTimeMillis();
System.out.println(“程序在”+(endTime-startTime)+“ms”中完成”;
}
结果 这两种方法都产生了15-16毫秒的时间——根本没有明显的差别。但不可否认,这是一个非常小的测试示例

我的问题又来了:幕后黑手是
使用资源试一试
比传统的
试一试
贵多少

  • 试捕不是昂贵的部分。引发异常的是(生成stacktrace)
  • 上面的“昂贵”意思是“花费几微秒”
  • try with resources就是使用可靠关闭资源所需的适当代码进行try catch
  • 您的度量代码无法证明任何事情,因为在优化运行时(如HotSpot)中尝试度量性能存在着众所周知的缺陷。你需要热身,重复同样的动作很多次,甚至更多
  • 如果您的结果超过10毫秒,那么很明显您不会遇到try-catch问题,这会造成几微秒的开销

  • Try-catch-finally和Try-with-resources具有基本相同的性能,因为它们在后台生成基本相同的字节码

    但是,您的第二个版本(try..catch..finally)的格式不太正确,因为在调用
    sc.close()
    时(理论上)它可能会导致不需要的
    NullPointerException
    。如果构造
    扫描器的动作
    导致引发异常,则将不会分配
    sc
    ,并将
    null

    您应该在
    try..finally
    之外构造扫描仪,并更改此设置:

    Scanner sc = null;
    try {
        sc = new Scanner(new File("file.txt"));
        ...
    
    致:

    或者,您应该检查
    sc!=调用
    sc.close()之前的
    finally
    子句中的null
    。如果您在try之外创建扫描仪,则不需要这样做。最后,我建议您这样做


    要执行与try with resources相同的工作,还需要在带有空catch块的
    sc.close()
    周围放置第二个
    try..catch
    ,以忽略关闭期间引发的任何异常。如果您这样做,我想您不必太担心空检查。

    这是苹果和桔子。ARM(自动资源管理,或资源试用)块比您显示的老式try-catch-finally块做得更多。这是因为它生成代码来处理资源闭包中抛出的异常(A对此进行了详细讨论)


    如果您正在编写新代码,请使用ARM块。它更易于阅读、维护,而且功能更强大。除非您是在严格约束的环境(如智能卡)中运行,否则这些优势可能会超过几个额外字节码的成本。

    传统的Try-catch:在Try块中,您的代码可能会出现异常,该异常将被catch块抛出,然后被catch块捕获。然后catch块将处理异常。如果您在
    尝试之前打开一个源代码,如文件或其他内容,并且需要在
    最终
    中关闭它们,则
    最终
    中执行的
    关闭()
    也可能引发异常,这将替换在
    try
    中抛出的异常,该位置由SE7之前的嵌套try-catch解决,如下所示

    试试看
    {
    尝试
    {
    代码例外;
    }
    最后
    {
    close();
    }
    }
    捕获(例外e)
    {
    处理例外情况
    }
    
    这非常复杂,因此在SE 7之后,我们使用try with resource来解决这个问题,当您完成
    try
    块时,
    try(此处)
    中的资源将全部关闭


    参考“CoreJava”版本:基于SE7的9检查使用
    javap-cYourClassCompiled.class生成的字节码。如果是相同的,那么就没有什么可担心的了。你不能用
    currentTimeMillis
    来测量毫秒级延迟。粒度可能是15-16毫秒,这意味着它总是会报告其中的一些倍数。基准测试是一件复杂的事情,天真的方法很少是正确的——请看。使用资源的尝试和您的尝试捕获之间的区别在于您的尝试捕获是有缺陷的
    sc.close()
    可能引发
    NullPointerException
    。即使“资源试用”对性能有影响(事实并非如此),也要始终使用它。它可以防止像这样的错误。看来我有些书要读。谢谢你的明确回答。@Drew如果你对benchmark感兴趣,请看一看
    long startTime = System.currentTimeMillis();
    ArrayList<String> list = null;
    Scanner sc = null;
    
    try {
        sc = new Scanner(new File("file.txt"));
        list = new ArrayList();
        while (sc.hasNext()) {
            list.add(sc.next());
        }
    } catch (Exception ex) {
        System.err.println("Error: " + ex.getMessage());
    } finally {
        sc.close();
        long endTime = System.currentTimeMillis();
        System.out.println("The program completed in " + (endTime - startTime) + " ms");
    }
    
    Scanner sc = null;
    try {
        sc = new Scanner(new File("file.txt"));
        ...
    
    Scanner sc = new Scanner(new File("file.txt"));
    try {
        ...