工作实践之 try-with-resource 资源关闭

工作实践之 资源关闭

  1. try-with-resource 注“JDK7及其之后的资源关闭方式

注意:下面都是文件创建来举例子
在这里插入图片描述

**
 * 资源关闭优化前与优化后代码量对比
 */
public class ResourceCloseVs {

    @Test
    public void newFileHandle(String url,
                              FileConsumer fileConsumer) {
        try (
                // 声明、创建文件的读取流
                FileInputStream fileInputStream =
                        new FileInputStream(url);

                InputStreamReader inputStreamReader =
                        new InputStreamReader(fileInputStream);

                BufferedReader bufferedReader =
                        new BufferedReader(inputStreamReader);
        ) {

            // 定义行变量和内容sb
            String line;
            StringBuilder stringBuilder = new StringBuilder();

            // 循环读取文件内容
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line + "\n");
            }

            // 调用函数式接口方法,将文件内容传递给lambda表达式,实现业务逻辑
            fileConsumer.fileHandler(stringBuilder.toString());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Test
    public void oldFileHandle(String url,
                              FileConsumer fileConsumer) {
        // 声明
        FileInputStream fileInputStream = null;
        InputStreamReader inputStreamReader = null;
        BufferedReader bufferedReader = null;

        // 创建文件读取流
        try {
            fileInputStream = new FileInputStream(url);

            inputStreamReader =
                    new InputStreamReader(fileInputStream);

            bufferedReader =
                    new BufferedReader(inputStreamReader);

            // 定义行变量和内容sb
            String line;
            StringBuilder stringBuilder = new StringBuilder();

            // 循环读取文件内容
            while ((line = bufferedReader.readLine()) != null) {
                stringBuilder.append(line + "\n");
            }

            // 调用函数式接口方法,将文件内容传递给lambda表达式,实现业务逻辑
            fileConsumer.fileHandler(stringBuilder.toString());

        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            // 关闭流资源
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (inputStreamReader != null) {
                try {
                    inputStreamReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

常见需要手动释放的物理资源

  1. 文件/流资源
  2. 套接字资源
  3. 数据库连接资源
    4.NIO的直接内存资源

物理资源没有手动释放的坏处

  1. 资源被长时间无效占用
  2. 超过最大限制后,将无资源可用
  3. 导致系统无法正常运行

具体案例:文件拷贝(初始版本)

     * 1. 创建输入/输出流
     * 2. 执行文件拷贝,读取文件内容,写入到另一个文件中
     * 3. **关闭文件流资源**
/**
 * JDK7之前的文件拷贝功能
 */
public class FileCopyTest {

    @Test
    public void copyFile() {
        /**
         * 1. 创建输入/输出流
         * 2. 执行文件拷贝,读取文件内容,写入到另一个文件中
         * 3. **关闭文件流资源**
         */

        // 定义输入路径和输出路径
        String originalUrl = "lib/FileCopyTest.java";
        String targetUrl = "targetTest/target.txt";

        // 声明文件输入流,文件输出流
        FileInputStream originalFileInputStream = null;
        FileOutputStream targetFileOutputStream = null;

        try {
            // 实例化文件流对象
            originalFileInputStream =
                    new FileInputStream(originalUrl);

            targetFileOutputStream =
                    new FileOutputStream(targetUrl);

            // 读取的字节信息
            int content;

            // 迭代,读取/写入字节
            while ((content = originalFileInputStream.read()) != -1) {
                targetFileOutputStream.write(content);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {

            // 关闭流资源
            if (targetFileOutputStream != null) {
                try {
                    targetFileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
            if (originalFileInputStream != null) {
                try {
                    originalFileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

    }

}
import org.junit.Test;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

/**
 * 基于JDK7之后,实现正确关闭流资源方法
 * try - with - resource
 */
public class NewFileCopyTest {

    @Test
    public void copyFile() {

        // 先定义输入/输出路径
        String originalUrl = "lib/NewFileCopyTest.java";
        String targetUrl = "targetTest/new.txt";

        // 初始化输入/输出流对象
        try (
                FileInputStream originalFileInputStream =
                        new FileInputStream(originalUrl);

                FileOutputStream targetFileOutputStream =
                        new FileOutputStream(targetUrl);
        ) {

            int content;

            // 迭代,拷贝数据
            while ((content = originalFileInputStream.read()) != -1) {
                targetFileOutputStream.write(content);
            }

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

}

工作实践之 资源关闭

try-with-resource 注“JDK7及其之后的资源关闭方式

  1. java 7引入的新特性
  2. 优雅关闭资源
  3. 一种java语法糖(可以反编译看源码)

案例实战: 源码解析 -> 下面是 NewFileCopyTest.class 反编译的源代码


import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import org.junit.Test;

public class NewFileCopyTest {
    public NewFileCopyTest() {
    }

    @Test
    public void copyFile() {
        String originalUrl = "lib/NewFileCopyTest.java";
        String targetUrl = "targetTest/new.txt";

        try {
            FileInputStream originalFileInputStream = new FileInputStream(originalUrl);
            Throwable var4 = null;

            try {
                FileOutputStream targetFileOutputStream = new FileOutputStream(targetUrl);
                Throwable var6 = null;

                try {
                    int content;
                    try {
                        while((content = originalFileInputStream.read()) != -1) {
                            targetFileOutputStream.write(content);
                        }
                    } catch (Throwable var33) {
                        var6 = var33;
                        throw var33;
                    }
                } finally {
                    if (targetFileOutputStream != null) {
                        if (var6 != null) {
                            try {
                                targetFileOutputStream.close();
                            } catch (Throwable var32) {
                                var6.addSuppressed(var32);
                            }
                        } else {
                            targetFileOutputStream.close();
                        }
                    }

                }
            } catch (Throwable var35) {
                var4 = var35;
                throw var35;
            } finally {
                if (originalFileInputStream != null) {
                    if (var4 != null) {
                        try {
                            originalFileInputStream.close();
                        } catch (Throwable var31) {
                            var4.addSuppressed(var31);
                        }
                    } else {
                        originalFileInputStream.close();
                    }
                }

            }
        } catch (FileNotFoundException var37) {
            var37.printStackTrace();
        } catch (IOException var38) {
            var38.printStackTrace();
        }

    }
}

try-with-resource使用注意下面几点:

package java.lang;

/**
 * An object that may hold resources (such as file or socket handles)
 * until it is closed. The {@link #close()} method of an {@code AutoCloseable}
 * object is called automatically when exiting a {@code
 * try}-with-resources block for which the object has been declared in
 * the resource specification header. This construction ensures prompt
 * release, avoiding resource exhaustion exceptions and errors that
 * may otherwise occur.
 *
 * @apiNote
 * <p>It is possible, and in fact common, for a base class to
 * implement AutoCloseable even though not all of its subclasses or
 * instances will hold releasable resources.  For code that must operate
 * in complete generality, or when it is known that the {@code AutoCloseable}
 * instance requires resource release, it is recommended to use {@code
 * try}-with-resources constructions. However, when using facilities such as
 * {@link java.util.stream.Stream} that support both I/O-based and
 * non-I/O-based forms, {@code try}-with-resources blocks are in
 * general unnecessary when using non-I/O-based forms.
 *
 * @author Josh Bloch
 * @since 1.7
 */
public interface AutoCloseable {
    /**
     * Closes this resource, relinquishing any underlying resources.
     * This method is invoked automatically on objects managed by the
     * {@code try}-with-resources statement.
     *
     * <p>While this interface method is declared to throw {@code
     * Exception}, implementers are <em>strongly</em> encouraged to
     * declare concrete implementations of the {@code close} method to
     * throw more specific exceptions, or to throw no exception at all
     * if the close operation cannot fail.
     *
     * <p> Cases where the close operation may fail require careful
     * attention by implementers. It is strongly advised to relinquish
     * the underlying resources and to internally <em>mark</em> the
     * resource as closed, prior to throwing the exception. The {@code
     * close} method is unlikely to be invoked more than once and so
     * this ensures that the resources are released in a timely manner.
     * Furthermore it reduces problems that could arise when the resource
     * wraps, or is wrapped, by another resource.
     *
     * <p><em>Implementers of this interface are also strongly advised
     * to not have the {@code close} method throw {@link
     * InterruptedException}.</em>
     *
     * This exception interacts with a thread's interrupted status,
     * and runtime misbehavior is likely to occur if an {@code
     * InterruptedException} is {@linkplain Throwable#addSuppressed
     * suppressed}.
     *
     * More generally, if it would cause problems for an
     * exception to be suppressed, the {@code AutoCloseable.close}
     * method should not throw it.
     *
     * <p>Note that unlike the {@link java.io.Closeable#close close}
     * method of {@link java.io.Closeable}, this {@code close} method
     * is <em>not</em> required to be idempotent.  In other words,
     * calling this {@code close} method more than once may have some
     * visible side effect, unlike {@code Closeable.close} which is
     * required to have no effect if called more than once.
     *
     * However, implementers of this interface are strongly encouraged
     * to make their {@code close} methods idempotent.
     *
     * @throws Exception if this resource cannot be closed
     */
    void close() throws Exception;
}

try-with-resource使用注意下面几点:

  1. 多资源自动关闭
  2. 实现AutoCloseable接口
  3. 避免了异常被屏蔽的问题 :addSuppressed

资源关闭顺序问题:

  1. 先开后关原则
  2. 从外到内原则
  3. 底层资源单独声明原则

资源关闭的特殊情况

  1. 资源对象被return的情况下,由调用方关闭
  2. ByteArrayInputStream等不需要检查关闭的资源对象
  3. 使用Socket获取的InputStream和OutputStream对象不需要关闭
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值