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, ...
随机推荐
- 《Python学习手册 第五版》 -第9章 元组、文件与其他核心类型
本章的主要内容是介绍了元组和文件的使用,同时作为介绍数据类型的最后一个章节,本章也总结和复习了前面所介绍的核心数据类型,总结的部分在此不多介绍,因为前面章节都有,在此就重点介绍以下两点内容 1.元组 ...
- (二)Mybatis动态sql
首先动态sql简单来讲就是可以根据传入参数的不同来动态的生成sql语句,拼接where语句,这样你就不用写很多个sql语句了,并且它里面有一些特性也可以帮助你避免sql语句的拼接错误,主要分为4个: ...
- Linux Firewalld用法及案例
Firewalld概述 动态防火墙管理工具 定义区域与接口安全等级 运行时和永久配置项分离 两层结构 核心层 处理配置和后端,如iptables.ip6tables.ebtables.ipset和模块 ...
- 软件bug描述(android)
1.bug主题:主要操作+bug主题 主题要简单明了,即开发一看主题就知道该问题. 2.描述: 作用:便于开发重现和定位缺陷的 2.1前置条件 2.2操作步骤 2.3预期结果 2.4实际结果 2.5备 ...
- 404 not found Webshell
404 not found Webshell 克隆或下载项目: git clone https://github.com/CosasDePuma/SecurityNotFound.git Securi ...
- bootstrap4网格
Bootstrap 4 网格系统有以下 5 个类: .col- 针对所有设备 .col-sm- 平板 - 屏幕宽度等于或大于 576px .col-md- 桌面显示器 - 屏幕宽度等于或大于 768p ...
- windows下python3使用pip安装scrapy提示安装失败
我的环境: python3.6, win10, 原因:不能成功安装twisted,因为twisted与高版本的python有兼容问题. 解决:1,先下载twisted二进制文 ...
- Java集合那点事, 满满干货~
说到Java集合,可以说是初学者必踩的坑了. 那么怎么才能学好Java集合呢?个人认为关键是要理清楚思路,从上而下,建立一套完整的思维体系,这样才能更好的去认识事物的本质. 先确立一下学习Java集合 ...
- JDBC 及 sql注入问题
一.相关概念 1.什么是JDBC JDBC(Java Database Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由 ...
- MySql的命令介绍
1,连接数据库服务器命令 mysql -u 用户名 -p 密码 mysql是连接MySql数据库的命令,-u后跟用户名,-p后跟密码,如果登陆后展示"mysql",则表示登录成功. ...