zoukankan      html  css  js  c++  java
  • Effective java 系列之更优雅的关闭资源-try-with-resources

    背景:

    在Java编程过程中,如果打开了外部资源(文件、数据库连接、网络连接等),我们必须在这些外部资源使用完毕后,手动关闭它们。因为外部资源不由JVM管理,无法享用JVM的垃圾回收机制,如果我们不在编程时确保在正确的时机关闭外部资源,就会导致外部资源泄露,紧接着就会出现文件被异常占用,数据库连接过多导致连接池溢出等诸多很严重的问题。

    传统关闭资源方式(一般JDK版本低于1.7)

    /**
         * 根据路径创建文件,如果路径文件夹不存在,就创建
         * @param filePath
         * @return
         */
        public static File createFileAbsolute(String filePath) throws IOException {
            File file = new File(filePath);
            File fileParent = file.getParentFile();
    
            if(!fileParent.exists()){
                fileParent.mkdirs();
            }
            file.createNewFile();
            return file;
        }
    
        /**
         * 传统写法,JDK1.6
         * @param dataStr
         * @return
         */
        public static long write2FileTradition(String dataStr,String filePath){
            long fileSize = 0L;
            RandomAccessFile raf = null;
            FileChannel fchannel = null;
            try {
                raf = new RandomAccessFile(createFileAbsolute(filePath),"rw");
                fchannel = raf.getChannel();
    
                ByteBuffer buf = ByteBuffer.allocate(1024);
    
                buf.clear();
                buf.put(dataStr.getBytes());
                buf.flip();
    
                while(buf.hasRemaining()){
                    fchannel.write(buf);
                }
    
                fileSize = fchannel.size();
    
            } catch (FileNotFoundException e) {
                logger.error("file not found",e);
            } catch (IOException e) {
                logger.error("IO exception",e);
            }finally{
           IOUtils.close(fchannel); IOUtils.close(raf); }
    return fileSize; }

    注意:如果是节点流和处理流,可直接关闭处理流,因为io流使用了装饰模式,所以关闭处理流时,会调用节点流的close()方法。

    JDK1.7以后关闭资源的方式

    try-with-resource语法(语法糖)

    在JDK7以前,Java没有自动关闭外部资源的语法特性,直到JDK7中新增了try-with-resource语法,才实现了这一功能。

    那什么是try-with-resource呢?简而言之,当一个外部资源的句柄对象(比如FileInputStream对象)实现了AutoCloseable接口,那么就可以将上面的板式代码简化为如下形式:

    /**
         * JDK1.7 关闭资源语法糖
         *
         * @param dataStr
         * @param filePath
         * @return
         */
        public static long write2File(String dataStr, String filePath) {
            long fileSize = 0L;
            try (
                    RandomAccessFile raf = new RandomAccessFile(createFileAbsolute(filePath), "rw");
                    FileChannel fchannel = raf.getChannel()
            ) {
                ByteBuffer buf = ByteBuffer.allocate(1024);
    
                buf.clear();
                buf.put(dataStr.getBytes());
                buf.flip();
    
                while (buf.hasRemaining()) {
                    fchannel.write(buf);
                }
    
                fileSize = fchannel.size();
    
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
    
            return fileSize;
        }

    将外部资源的句柄对象的创建放在try关键字后面的括号中,当这个try-catch代码块执行完毕后,Java会确保外部资源的close方法被调用。

    反编译后源码:

      public static long write2File(String dataStr, String filePath) {
            long fileSize = 0L;
    
            try {
                RandomAccessFile raf = new RandomAccessFile(filePath, "rw");
                Throwable var5 = null;
    
                try {
                    FileChannel fchannel = raf.getChannel();
                    Throwable var7 = null;
    
                    try {
                        ByteBuffer buf = ByteBuffer.allocate(1024);
                        buf.clear();
                        buf.put(dataStr.getBytes());
                        buf.flip();
    
                        while(buf.hasRemaining()) {
                            fchannel.write(buf);
                        }
    
                        fileSize = fchannel.size();
                    } catch (Throwable var34) {
                        var7 = var34;
                        throw var34;
                    } finally {
                        if (fchannel != null) {
                            if (var7 != null) {
                                try {
                                    fchannel.close();
                                } catch (Throwable var33) {
                                    var7.addSuppressed(var33);
                                }
                            } else {
                                fchannel.close();
                            }
                        }
    
                    }
                } catch (Throwable var36) {
                    var5 = var36;
                    throw var36;
                } finally {
                    if (raf != null) {
                        if (var5 != null) {
                            try {
                                raf.close();
                            } catch (Throwable var32) {
                                var5.addSuppressed(var32);
                            }
                        } else {
                            raf.close();
                        }
                    }
    
                }
            } catch (FileNotFoundException var38) {
                logger.error("file not found", var38);
            } catch (IOException var39) {
                logger.error("IO exception", var39);
            }
    
            return fileSize;
        }

    异常抑制

    通过反编译的代码,大家可能注意到代码中有一处对异常的特殊处理:

    var2.addSuppressed(var11);

    这是try-with-resource语法涉及的另外一个知识点,叫做异常抑制。当对外部资源进行处理(例如读或写)时,如果遭遇了异常,且在随后的关闭外部资源过程中,又遭遇了异常,那么你catch到的将会是对外部资源进行处理时遭遇的异常,关闭资源时遭遇的异常将被“抑制”但不是丢弃,通过异常的getSuppressed方法,可以提取出被抑制的异常。

    这里理解一点,就是try块中抛出的异常,在finally也抛出异常时,就会丢失,所以通常在finally不抛异常,而是通过 addSuppressed()方法将异常抑制住。

    总结:

    1、当一个外部资源的句柄对象实现了AutoCloseable接口,JDK7中便可以利用try-with-resource语法更优雅的关闭资源,消除板式代码。

    2、try-with-resource时,如果对外部资源的处理和对外部资源的关闭均遭遇了异常,“关闭异常”将被抑制,“处理异常”将被抛出,但“关闭异常”并没有丢失,而是存放在“处理异常”的被抑制的异常列表中。

    参考:

    https://www.cnblogs.com/itZhy/p/7636615.html

    https://www.cnblogs.com/langtianya/p/5139465.html

  • 相关阅读:
    U盘安装CentOS 7系统
    生产库中遇到mysql的子查询
    mysql 储存类型自增主键区别
    MySQL主从数据库同步延迟问题解决
    MySQL 加锁处理分析
    120篇精华文章打包送,干货慎入!
    mysql批量删除相同前缀的表和修改表名
    用pt-online-schema-change给大表在线加字段的时候导致从库数据丢失的问题
    【MySQL】online ddl 工具之pt-online-schema-change
    互联网公司为啥不使用mysql分区表?
  • 原文地址:https://www.cnblogs.com/xiaozhuanfeng/p/10731492.html
Copyright © 2011-2022 走看看