Java如何优雅地使用close()?
注:本文出自博主 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()?的更多相关文章
- 我们一起来排序——使用Java语言优雅地实现常用排序算法
破阵子·春景 燕子来时新社,梨花落后清明. 池上碧苔三四点,叶底黄鹂一两声.日长飞絮轻. 巧笑同桌伙伴,上学径里逢迎. 疑怪昨宵春梦好,元是今朝Offer拿.笑从双脸生. 排序算法--最基础的算法,互 ...
- 怎样用Java 8优雅的开发业务
怎样用Java 8优雅的开发业务 目录 怎样用Java 8优雅的开发业务 函数式编程 流式编程 基本原理 案例 优雅的空处理 新的并发工具类CompletableFuture 单机批处理多线程执行模型 ...
- 你的Kubernetes Java应用优雅停机了吗?
Java 应用优雅停机 我们首先考虑下,一般在什么场景下数据会丢失呢? 升级服务时 pod重启时 服务器断电时 因为服务器断电属于极端情况,我们暂且不考虑.那就只有 Java 退出时我们要保证数据的完 ...
- 哦,这就是java的优雅停机?(实现及原理)
优雅停机? 这个名词我是服的,如果抛开专业不谈,多好的名词啊! 其实优雅停机,就是在要关闭服务之前,不是立马全部关停,而是做好一些善后操作,比如:关闭线程.释放连接资源等. 再比如,就是不会让调用方的 ...
- java如何优雅的实现时间控制
前言:最近小王同学又遇到了一个需求:线上的业务运行了一段时间,后来随着使用人数增多,出现了一个问题是这样的,一个订单会重复创建几次,导致数据库里出现了很多垃圾数据.在测试同学的不断测试下,发现问题出在 ...
- 编码规范 | Java函数优雅之道(上)
导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一.软件系统思维有句话“Less coding, ...
- 编码规范 | Java函数优雅之道(下)
上文背景 本文总结了一套与Java函数相关的编码规则,旨在给广大Java程序员一些编码建议,有助于大家编写出更优雅.更高质.更高效的代码. 内部函数参数尽量使用基础类型 案例一:内部函数参数尽量使用基 ...
- Java函数优雅之道
https://www.cnblogs.com/amap_tech/p/11320171.html 导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化 ...
- Java 函数优雅之道
导读 随着软件项目代码的日积月累,系统维护成本变得越来越高,是所有软件团队面临的共同问题.持续地优化代码,提高代码的质量,是提升系统生命力的有效手段之一.软件系统思维有句话“Less coding, ...
随机推荐
- 每天一道Java题[8]
以下题目及解答属于个人见解,欢迎大家也分享和补充一下解答的内容,互相促进,共同进步! 题目 RESTful WebService与SOAP WebService有什么异同? 解答 SOAP是一个协议, ...
- Java之路——初识Eclipse
零.大纲 一.前言 二.获取Eclipse 三.运行Eclipse 四.创建及运行第一个Java Project 五.界面介绍 六.如何调试 七.获取插件 八.Eclipse 快捷键 九.总结 一.前 ...
- 使用IDEA构建Spring-boot多模块项目配置流程
使用IDEA构建Spring-boot多模块项目配置流程 1.创建项目 点击Create New Project 在左侧选中Spring Initializer,保持默认配置,点击下一步. 在Grou ...
- TLS使用指南(一):如何在Rancher 2.x中进行TLS termination?
引 言 这是一个系列文章,我们将在本系列中探索Rancher使用TLS证书的不同方式.TLS,安全传输层协议,是用于保护网络通信的加密协议.它是目前已经弃用的安全套接层(SSL)的继任者. 你可以从本 ...
- <img>和background-img区别
1. 是否占位 background-image是背景图片,是css的一个样式,不占位 <img />是一个块状元素,它是一个图片,是html的一个标签,占位 2.否可操作 backgro ...
- ERP入门到精通
大家好,最近有空就跟大家分享开发ERP经验,希望对大家有所帮助. 少说废话,直接进入主题吧. ERP定义:企业资源计划 企业资源:物资资源,人力资料,财务资源,信息资源 包含内容:制造,会计,财务,销 ...
- java一些技术
怎么样得到编译.java文件得到.class文件 两个用于反编译的工具Luyten和java decompiler 直接将jar包或则.class字节码文件拖到里面即可得到反汇编得到对应的java文件 ...
- Codeforces 1304E 1-Trees and Queries (树上距离+思维)(翻译向)
题意 给你一棵树,q个询问(x,y,a,b,k),每次问你如果在(x,y)加一条边,那么a到b能不能走k步,同一个点可以走多次 思路(翻译题解) 对于一条a到b的最短路径x,可以通过左右横跳的方法把他 ...
- UVA1623 Enter The Dragon (贪心)
题意: m个坑,n天,起初每个坑都是满的,每天至多有一个坑会下雨,下雨就会满,满了再下就输出no. 在没有雨的时候可以安排龙来喝水,把坑喝空,可行的话输出龙喝水的方案 思路: 边读入边操作,set保存 ...
- android 基础学习笔记2
1.容器布局 一.线性布局 (LineaLayout) 方向:orientation =vertical / horizontal 重力(对齐) :gravity =bottom/right/left ...