这个所谓的try-with-resources,是个语法糖。实际上就是自动调用资源的close()函数。和Python里的with语句差不多。

例如:

[java] view plain copy

 

  1. static String readFirstLineFromFile(String path) throws IOException {
  2. try (BufferedReader br = new BufferedReader(new FileReader(path))) {
  3. return br.readLine();
  4. }
  5. }

可以看到try语句多了个括号,而在括号里初始化了一个BufferedReader。

这种在try后面加个括号,再初始化对象的语法就叫try-with-resources。

实际上,相当于下面的代码(其实略有不同,下面会说明):

[java] view plain copy

 

  1. static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
  2. BufferedReader br = new BufferedReader(new FileReader(path));
  3. try {
  4. return br.readLine();
  5. } finally {
  6. if (br != null) br.close();
  7. }
  8. }

很容易可以猜想到,这是编绎器自动在try-with-resources后面增加了判断对象是否为null,如果不为null,则调用close()函数的的字节码。

只有实现了java.lang.AutoCloseable接口,或者java.io.Closable(实际上继随自java.lang.AutoCloseable)接口的对象,才会自动调用其close()函数。

有点不同的是Java.io.Closable要求一实现者保证close函数可以被重复调用。而AutoCloseable的close()函数则不要求是幂等的。具体可以参考Javadoc。

下面从编绎器生成的字节码来分析下,try-with-resources到底是怎样工作的:

[java] view plain copy

 

  1. public class TryStudy implements AutoCloseable{
  2. static void test() throws Exception {
  3. try(TryStudy tryStudy = new TryStudy()){
  4. System.out.println(tryStudy);
  5. }
  6. }
  7. @Override
  8. public void close() throws Exception {
  9. }
  10. }

TryStudy实现了AutoCloseable接口,下面来看下test函数的字节码:

[java] view plain copy

 

  1. static test()V throws java/lang/Exception
  2. TRYCATCHBLOCK L0 L1 L2
  3. TRYCATCHBLOCK L3 L4 L4
  4. L5
  5. LINENUMBER 21 L5
  6. ACONST_NULL
  7. ASTORE 0
  8. ACONST_NULL
  9. ASTORE 1
  10. L3
  11. NEW TryStudy
  12. DUP
  13. INVOKESPECIAL TryStudy.<init> ()V
  14. ASTORE 2
  15. L0
  16. LINENUMBER 22 L0
  17. GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
  18. ALOAD 2
  19. INVOKEVIRTUAL java/io/PrintStream.println (Ljava/lang/Object;)V
  20. L1
  21. LINENUMBER 23 L1
  22. ALOAD 2
  23. IFNULL L6
  24. ALOAD 2
  25. INVOKEVIRTUAL TryStudy.close ()V
  26. GOTO L6
  27. L2
  28. FRAME FULL [java/lang/Throwable java/lang/Throwable TryStudy] [java/lang/Throwable]
  29. ASTORE 0
  30. ALOAD 2
  31. IFNULL L7
  32. ALOAD 2
  33. INVOKEVIRTUAL TryStudy.close ()V
  34. L7
  35. FRAME CHOP 1
  36. ALOAD 0
  37. ATHROW
  38. L4
  39. FRAME SAME1 java/lang/Throwable
  40. ASTORE 1
  41. ALOAD 0
  42. IFNONNULL L8
  43. ALOAD 1
  44. ASTORE 0
  45. GOTO L9
  46. L8
  47. FRAME SAME
  48. ALOAD 0
  49. ALOAD 1
  50. IF_ACMPEQ L9
  51. ALOAD 0
  52. ALOAD 1
  53. INVOKEVIRTUAL java/lang/Throwable.addSuppressed (Ljava/lang/Throwable;)V
  54. L9
  55. FRAME SAME
  56. ALOAD 0
  57. ATHROW
  58. L6
  59. LINENUMBER 24 L6
  60. FRAME CHOP 2
  61. RETURN
  62. LOCALVARIABLE tryStudy LTryStudy; L0 L7 2
  63. MAXSTACK = 2
  64. MAXLOCALS = 3

从字节码里可以看出,的确是有判断tryStudy对象是否为null,如果不是null,则调用close函数进行资源回收。

再仔细分析,可以发现有一个Throwable.addSuppressed的调用,那么这个调用是什么呢?

其实,上面的字节码大概是这个样子的(当然,不完全是这样的,因为汇编的各种灵活的跳转用Java是表达不出来的):

[java] view plain copy

 

  1. static void test() throws Exception {
  2. TryStudy tryStudy = null;
  3. try{
  4. tryStudy = new TryStudy();
  5. System.out.println(tryStudy);
  6. }catch(Throwable suppressedException) {
  7. if (tryStudy != null) {
  8. try {
  9. tryStudy.close();
  10. }catch(Throwable e) {
  11. e.addSuppressed(suppressedException);
  12. throw e;
  13. }
  14. }
  15. throw suppressedException;
  16. }
  17. }

有点晕是吧,其实很简单。使用了try-with-resources语句之后,有可能会出现两个异常,一个是try块里的异常,一个是调用close函数里抛出的异常。

当然,平时我们写代码时,没有关注到。一般都是再抛出close函数里的异常,前面的异常被丢弃了。

如果在调用close函数时出现异常,那么前面的异常就被称为Suppressed Exceptions,因此Throwable还有个addSuppressed函数可以把它们保存起来,当用户捕捉到close里抛出的异常时,就可以调用Throwable.getSuppressed函数来取出close之前的异常了。

总结:

使用try-with-resources的语法可以实现资源的自动回收处理,大大提高了代码的便利性,和mutil catch一样,是个好东东。

用编绎器生成的字节码的角度来看,try-with-resources语法更加高效点。

java.io.Closable接口要求一实现者保证close函数可以被重复调用,而AutoCloseable的close()函数则不要求是幂等的。

Java7里try-with-resources分析的更多相关文章

  1. 在WebApi项目里使用MiniProfiler并且分析 Entity Framework Core

    在WebApi项目里使用MiniProfiler并且分析 Entity Framework Core 一.安装配置MiniProfiler 在现有的ASP.NET Core MVC WebApi 项目 ...

  2. Java7里try-with-resources分析--转

    原文地址:http://blog.csdn.net/hengyunabc/article/details/18459463 这个所谓的try-with-resources,是个语法糖.实际上就是自动调 ...

  3. php里进程创建和分析

    pcntl_fork()函数创建一个子进程,这个子进程仅PID(进程号) 和PPID(父进程号)与其父进程不同成功时,在父进程执行线程内返回产生的子进程的PID,在子进程执行线程内返回0.失败时,在 ...

  4. spring-mvc里的 <mvc:resources> 及静态资源访问

    在进行Spring MVC的配置时,通常我们会配置一个dispatcher servlet用于处理对应的URL.配置如下: <servlet> <servlet-name>Sp ...

  5. springMvc里的mvc:resources与静态资源的访问

    在进行Spring MVC的配置时,通常我们会配置一个dispatcher servlet用于处理对应的URL.配置如下:   <servlet>   <servlet-name&g ...

  6. JQuery里的原型prototype分析

    在 JavaScript 中,每个函数对象都有一个默认的属性 prototype,称为函数对象的原型成员,这个属性指向一个对象,称为函数的原型对象,当我们每定义了一个函数的时候,JavaScript ...

  7. 单例模式中的多线程分析synchronized

    谈到单例模式,我们立马会想到饿汉式和懒汉式加载,所谓饿汉式就是在创建类时就创建好了实例,懒汉式在获取实例时才去创建实例,即延迟加载. 饿汉式: 1 package com.bijian.study; ...

  8. 转:XBMC源代码分析

    1:整体结构以及编译方法 XBMC(全称是XBOX Media Center)是一个开源的媒体中心软件.XBMC最初为Xbox而开发,可以运行在Linux.OSX.Windows.Android4.0 ...

  9. Hadoop源代码分析

    http://wenku.baidu.com/link?url=R-QoZXhc918qoO0BX6eXI9_uPU75whF62vFFUBIR-7c5XAYUVxDRX5Rs6QZR9hrBnUdM ...

随机推荐

  1. JVM学习--(三)配置参数

    JVM配置参数分为三类参数: 1.跟踪参数 2.堆分配参数 3.栈分配参数 这三类参数分别用于跟踪监控JVM状态,分配堆内存以及分配栈内存. 跟踪参数 跟踪参数用于跟踪监控JVM,往往被开发人员用于J ...

  2. 阅读源码(IV)

    往期系列: <由阅读源码想到> <由阅读源码想到 | 下篇> <阅读源码(III)> Eric S.Raymond的写于2014年的<How to learn ...

  3. CKEditor5 + ckfinder3(php)

    CKEditor5资源下载,这里我们选择ckeditor5-build-classic下载: https://ckeditor.com/ckeditor-5-builds/download/ ckfi ...

  4. N-Queens(N皇后问题)

    题目: The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that no two que ...

  5. FFPLAY的原理(三)

    播放声音 现在我们要来播放声音.SDL也为我们准备了输出声音的方法.函数SDL_OpenAudio()本身就是用来打开声音设备的.它使用一个叫做SDL_AudioSpec结构体作为参数,这个结构体中包 ...

  6. IOS Swift语言开发 tableView的重用以及自cell的自适应高度

    http://www.aichengxu.com/iOS/11143168.htm 一.准备数据 (这是一个元组,第一个元素为英雄的名字;第二个元素为英雄头像图片的名字,格式为.PNG,如果为其他的格 ...

  7. 文本分类学习(六) AdaBoost和SVM

    直接从特征提取,跳到了BoostSVM,是因为自己一直在写程序,分析垃圾文本,和思考文本分类用于识别垃圾文本的短处.自己学习文本分类就是为了识别垃圾文本. 中间的博客待自己研究透彻后再补上吧. 因为获 ...

  8. javascript封装的参数合并

    o=$.extend(initializationInterface.defaultValue,o); o= $.extend({}, initializationInterface.prototyp ...

  9. java web 实战经典(二)

    一.jsp之间传值时乱码问题解决 request.setCharacterEncoding("GBK");//解决中文乱码 String postData = (String)re ...

  10. python 编码形式简单入门

    为什么使用Python 假设我们有这么一项任务:简单测试局域网中的电脑是否连通.这些电脑的ip范围从192.168.0.101到192.168.0.200. 思路:用shell编程.(Linux通常是 ...