注:本文出自博主 Chloneda个人博客 | 博客园 | Github | Gitee | 知乎

本文源链接https://www.cnblogs.com/chloneda/p/java-close.html

Java基础系列,尽量采用通俗易懂、循序渐进的方式,让大家真正优雅地使用close()方法!

问题场景

平时我们使用资源后一般都会关闭资源,即close()方法,但这个步骤重复性很高,还面临上述执行顺序不明的风险,而且很多人还是不能正确合理地关闭资源。

我们来看看close()是怎么错误地关闭资源的?

错误的close()

先来看看如下的错误关闭资源方式:

package com.chloneda.jutils.test;

import java.sql.*;

/**
* @author chloneda
* @description: close()方法测试
* 错误的close()
*/
public class CloseTest {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库链接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/common", "root", "123456");
//3.通过数据库的连接操作数据库,实现增删改查
st = conn.createStatement();
rs = st.executeQuery("select * from new_table");
//4.处理数据库的返回结果
while (rs.next()) {
System.out.println(rs.getString("id") + " " + rs.getString("name"));
}
//5.关闭资源
rs.close();
st.close();
conn.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}

上面代码的资源关闭写在了try代码块中,一旦close方法调用之前(比如3步骤)就抛出异常,那么关闭资源的代码就永远不会得到执行。

如果我们把关闭资源的代码放在finally中行不行呢?

    try {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库链接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/common", "root", "123456");
//3.通过数据库的连接操作数据库,实现增删改查
st = conn.createStatement();
rs = st.executeQuery("select * from new_table");
//4.处理数据库的返回结果
while (rs.next()) {
System.out.println(rs.getString("id") + " " + rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.关闭资源
try {
rs.close();
st.close();
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
}

答案是不行!如果在 2步骤 的try中conn获得数据库链接抛出异常,那么conn仍然为null,此时进入finally代码块中,执行close()就报空指针异常了,关闭资源没有意义!因此,我们需要在close()之前判断一下conn等是否为空,只有不为空的时候才需要close。

常见的close()

针对上述场景,得到常见的使用close()方式如下:

/**
* @author chloneda
* @description: close()方法测试
* 常见的close()
*/
public class CloseTest {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库链接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/common", "root", "123456");
//3.通过数据库的连接操作数据库,实现增删改查
st = conn.createStatement();
rs = st.executeQuery("select * from new_table");
//4.处理数据库的返回结果
while (rs.next()) {
System.out.println(rs.getString("id") + " " + rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.关闭资源
if (null != rs) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (null != st) {
try {
st.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
if (null != conn) {
try {
conn.close();
} catch (SQLException e) {
e.printStackTrace();
}
} }
}
}

这是常见的close()!但是finally代码块的代码重复性太高了,这还只是三个资源的关闭,如果有很多个资源需要在finally中关闭,那不是需要编写很多不优雅的代码?其实,关闭资源是没啥逻辑的代码,我们需要精简代码,减少代码重复性,优雅地编程!

使用AutoCloseable接口

自从Java7以后,我们可以使用 AutoCloseable接口 (Closeable接口也可以)来优雅的关闭资源了 看看修改例子:

/**
* @author chloneda
* @description: close()方法测试
* 使用AutoCloseable接口
*/
public class CloseTest {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
//2.获得数据库链接
conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/common", "root", "123456");
//3.通过数据库的连接操作数据库,实现增删改查
st = conn.createStatement();
rs = st.executeQuery("select * from new_table");
//4.处理数据库的返回结果
while (rs.next()) {
System.out.println(rs.getString("id") + " " + rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
} finally {
//5.关闭资源,调用自定义的close()方法
close(rs);
close(st);
close(conn);
}
} public static void close(AutoCloseable closeable) {
if (closeable != null) {
try {
closeable.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

上面的finally代码块的代码量是不是减少了许多,就单纯地调用的静态的 close(AutoCloseable closeable) 方法。为什么可以这样呢?

其实Connection、Statement、ResultSet三个接口都继承了AutoCloseable接口。所以只要涉及到资源的关闭,继承了AutoCloseable接口,实现了close()方法,我们都可以调用 close(AutoCloseable closeable) 方法进行资源关闭。

此外,java IO流的很多类都实现了 Closeable接口,而Closeable接口又继承自 AutoCloseable接口,也可以调用上面的 close(AutoCloseable closeable) 方法进行资源关闭。是不是一语惊醒梦中人啊?

使用try-with-resources

其实Java7以后,还有一种关闭资源的方式,也就是 try-with-resources,这种方式也是我们推荐的!很优雅!

我们来看看它是怎么优雅地关闭资源的!

/**
* @author chloneda
* @description: close()方法测试
* 使用try-with-resources
*/
public class CloseTest {
public static void main(String[] args) throws ClassNotFoundException {
//1.加载驱动程序
Class.forName("com.mysql.jdbc.Driver");
try (//2.获得数据库链接
Connection conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/common", "root", "123456");
//3.通过数据库的连接操作数据库,实现增删改查
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery("select * from new_table")
) {
//4.处理数据库的返回结果
while (rs.next()) {
System.out.println(rs.getString("id") + " " + rs.getString("name"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

这种方式就省略了finally,不必重复编写关闭资源的代码了!而且资源也得到了关闭!怎么验证这个问题?可以查看底下的 实际应用 章节!

其实try-with-resources关闭资源的操作,本质上是继承了java.lang.AutoCloseable接口,实现了close方法,所以使用try-with-resources能关闭资源。很神奇吧!

实际应用

这个章节就是验证使用try-with-resources可以关闭资源的问题的!

上面我们说了使用try-with-resources关闭资源,只要是继承了java.lang.AutoCloseable接口 实现close()方法就可以使用!

我们自定义一个资源类,来看看实际应用吧!

package com.chloneda.jutils.test;

/**
* @author chloneda
* @description: 资源类, 实现AutoCloseable接口.
*/
public class Resources implements AutoCloseable {
public void useResource() {
System.out.println("useResource:{} 正在使用资源!");
} @Override
public void close() {
System.out.println("close:{} 自动关闭资源!");
}
} /**
* @description: 使用try-with-resources自动关闭资源测试.
*/
class AutoClosableTest {
public static void main(String[] args) {
/** 使用try-with-resource,自动关闭资源 */
try (
Resources resource = new Resources()
) {
resource.useResource();
} catch (Exception e) {
e.getMessage();
} finally {
System.out.println("Finally!");
}
}
}

结果输出。

useResource:{} 正在使用资源!
close:{} 自动关闭资源!
Finally!

看到运行结果了吗?Resources类实现AutoCloseable接口,实现了close()方法,try-with-resources 就会自动关闭资源!

一旦Resources类没有继承java.lang.AutoCloseable接口,没有实现close()方法,AutoClosableTest类的try模块就在编译期报错,提示信息如下。

Incompatible types.
Required: java.lang.AutoCloseable
Found: com.chloneda.jutils.test.Resources

最后,需要说明的是try-with-resources就是一个JVM语法糖!关于JVM语法糖可以查查相关资料,看看Java中有哪些有趣的语法糖!

尾语

《Effective Java》在第三版中也推荐使用try-with-resources语句替代try-finally语句。

所以在处理必须关闭的资源时,使用try-with-resources语句替代try-finally语句。生成的代码更简洁,更清晰,并且生成的异常更有用。 try-with-resources语句在编写必须关闭资源的代码时会更容易,也不会出错,而使用try-finally语句实际上是不可能的。

如此,推荐大家使用try-with-resources优雅地关闭资源!


Java如何优雅地使用close()?的更多相关文章

  1. 我们一起来排序——使用Java语言优雅地实现常用排序算法

    破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...

  2. 怎样用Java 8优雅的开发业务

    怎样用Java 8优雅的开发业务 目录 怎样用Java 8优雅的开发业务 函数式编程 流式编程 基本原理 案例 优雅的空处理 新的并发工具类CompletableFuture 单机批处理多线程执行模型 ...

  3. 你的Kubernetes Java应用优雅停机了吗?

    Java 应用优雅停机 我们首先考虑下,一般在什么场景下数据会丢失呢? 升级服务时 pod重启时 服务器断电时 因为服务器断电属于极端情况,我们暂且不考虑.那就只有 Java 退出时我们要保证数据的完 ...

  4. 哦,这就是java的优雅停机?(实现及原理)

    优雅停机? 这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的 ...

  5. java如何优雅的实现时间控制

    前言:最近小王同学又遇到了一个需求:线上的业务运行了一段时间,后来随着使用人数增多,出现了一个问题是这样的,一个订单会重复创建几次,导致数据库里出现了很多垃圾数据.在测试同学的不断测试下,发现问题出在 ...

  6. 编码规范 | Java函数优雅之道(上)

    导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一.软件系统思维有句话“Less coding, ...

  7. 编码规范 | Java函数优雅之道(下)

    上文背景 本文总结了一套与Java函数相关的编码规则,旨在给广大Java程序员一些编码建议,有助于大家编写出更优雅.更高质.更高效的代码. 内部函数参数尽量使用基础类型 案例一:内部函数参数尽量使用基 ...

  8. Java函数优雅之道

    https://www.cnblogs.com/amap_tech/p/11320171.html 导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化 ...

  9. Java 函数优雅之道

    导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一.软件系统思维有句话“Less coding, ...

随机推荐

  1. 每天一道Java题[8]

    以下题目及解答属于个人见解,欢迎大家也分享和补充一下解答的内容,互相促进,共同进步! 题目 RESTful WebService与SOAP WebService有什么异同? 解答 SOAP是一个协议, ...

  2. Java之路——初识Eclipse

    零.大纲 一.前言 二.获取Eclipse 三.运行Eclipse 四.创建及运行第一个Java Project 五.界面介绍 六.如何调试 七.获取插件 八.Eclipse 快捷键 九.总结 一.前 ...

  3. 使用IDEA构建Spring-boot多模块项目配置流程

    使用IDEA构建Spring-boot多模块项目配置流程 1.创建项目 点击Create New Project 在左侧选中Spring Initializer,保持默认配置,点击下一步. 在Grou ...

  4. TLS使用指南(一):如何在Rancher 2.x中进行TLS termination?

    引 言 这是一个系列文章,我们将在本系列中探索Rancher使用TLS证书的不同方式.TLS,安全传输层协议,是用于保护网络通信的加密协议.它是目前已经弃用的安全套接层(SSL)的继任者. 你可以从本 ...

  5. <img>和background-img区别

    1. 是否占位 background-image是背景图片,是css的一个样式,不占位 <img />是一个块状元素,它是一个图片,是html的一个标签,占位 2.否可操作 backgro ...

  6. ERP入门到精通

    大家好,最近有空就跟大家分享开发ERP经验,希望对大家有所帮助. 少说废话,直接进入主题吧. ERP定义:企业资源计划 企业资源:物资资源,人力资料,财务资源,信息资源 包含内容:制造,会计,财务,销 ...

  7. java一些技术

    怎么样得到编译.java文件得到.class文件 两个用于反编译的工具Luyten和java decompiler 直接将jar包或则.class字节码文件拖到里面即可得到反汇编得到对应的java文件 ...

  8. Codeforces 1304E 1-Trees and Queries (树上距离+思维)(翻译向)

    题意 给你一棵树,q个询问(x,y,a,b,k),每次问你如果在(x,y)加一条边,那么a到b能不能走k步,同一个点可以走多次 思路(翻译题解) 对于一条a到b的最短路径x,可以通过左右横跳的方法把他 ...

  9. UVA1623 Enter The Dragon (贪心)

    题意: m个坑,n天,起初每个坑都是满的,每天至多有一个坑会下雨,下雨就会满,满了再下就输出no. 在没有雨的时候可以安排龙来喝水,把坑喝空,可行的话输出龙喝水的方案 思路: 边读入边操作,set保存 ...

  10. android 基础学习笔记2

    1.容器布局 一.线性布局 (LineaLayout) 方向:orientation =vertical / horizontal 重力(对齐) :gravity =bottom/right/left ...