Java-IO 流的Close方法
一、在Java中为何要关闭流
GC运行的时间点是不确定的(因为是一条单独存在的线程),所以很多时候你不能直接控制什么时候发生GC。这个带来的问题有两点,一个是有时候你的内存不足需要立刻回收而GC并不会立刻运行;另外一个是因为GC运行期间会占用大量系统资源所以某些情况下你会希望把它推后,或者干脆关掉以便根据性能需求在合式的时候手动执行。
另外,GC只能回收内存。至于各种stream之类,他们下边一般还开启了各种其他的系统资源,比如文件,比如输入输出设备(键盘/屏幕等),等等。而这些设备第一是不能自动关闭(因为谁知道你程序要用它到什么时候啊),另一个系统内数量有限(比如键盘/屏幕同一时间只有一个)。最后,文件和数据库连接之类的东西还存在读写锁定的问题。这些都导致用户必须手动处理这些资源的开启和关闭。
这年头自动挡汽车都那么好了还不是有那么多人喜欢手挡,一样的。
流不关资源占着内存,你一个小的程序感觉不出来,要是好多流都不关,就会导致死机,内存泄流!建议培养良好的编码意识,一个小的程序也要吧流关了。
二、TryWithResources
这个不妨直接来看Oracle公司所提供的JavaDoc好了,毕竟可以这么说,这只是从Java SE 7开始提供的一个语法糖。 https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
官方描述:
The try
-with-resources statement is a try
statement that declares one or more resources. A resource is an object that must be closed after the program is finished with it. The try
-with-resources statement ensures that each resource is closed at the end of the statement. Any object that implements java.lang.AutoCloseable
, which includes all objects which implement java.io.Closeable
, can be used as a resource.
解释一下:
使用try
语句来声明一个或多个资源,这里的资源都是在程序执行完毕之后必须要被关闭的对象。TryWithResources声明确保了每一个资源最终都会在程序运行的最后被关闭。但我们要求,每一个资源对象必须实现java.lang.AutoCloseable
包括实现了java.io.Closeable
的对象都可以被作为资源对象。
如果在Java SE 7 之前,我们关闭一个流对象,需要如下的写法:
static String readFirstLineFromFileWithFinallyBlock(String path)throws IOException {BufferedReader br = new BufferedReader(new FileReader(path));try {return br.readLine();}catch(//...){//...}finally {if (br != null) br.close();}
}
将close()方法置于finally语句块中是一个常见的做法。
使用Java SE 7 之后,使用TryWithResources,我们就可以更优雅地关闭流对象了:
static String readFirstLineFromFile(String path) throws IOException {try (BufferedReader br =new BufferedReader(new FileReader(path))) {return br.readLine();}catch(//...){//.....}
}
try-with-resource 结构的用法即是,FileInputStream 类型变量就在try关键字后面的括号中声明,而finally{}的处理实际上是一样的。
一个还看不出此语法糖的优势,比如有多个流要进行关闭,在传统方法中,一个流关闭就应该对应一个try-catch语句,例子如下:
try {FileOutputStream fos = new FileOutputStream("d:\\a.txt");OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");BufferedWriter bw = new BufferedWriter(osw);bw.write("java IO close test");// 从外到内顺序关闭okif (bw != null) {bw.close();}if (osw!= null) {osw.close();} if (fos!= null) {fos.close();}}catch (Exception e){}
我们假设一个bw流出现了异常,那么直接被捕获了异常,那么后面两个流的close方法没能成功被调用,那么就会导致流没有被关闭,所以要写成以下写法:
finally {try{if(osw!= null){osw.close();}}catch(Exception e){}try{if(fos!= null){fos.close();}}catch(Exception e){}
}
每一次关闭流我们都单独进行一次try,而且需要写在finally中保证异常了也要执行,不要嫌弃代码繁琐因为这是必须的内容。
然而在使用了try-with-resources(注意:resources使用的是复数,说明可以一次声明多个资源)之后,代码简单多了:
try(FileOutputStream fos = new FileOutputStream("d:\\a.txt");OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");BufferedWriter bw = new BufferedWriter(osw))
}catch (Exception e){}
节约了很多不少额外的相似代码,这也是Java语法糖带给开发者的便利之处,这也是我们应当在Java中使用的流关闭方式。在实际开发中,还是尽量使用新特性吧!
三、包装流的关闭
引用于:Java IO包装流如何关闭?
问题:
JAVA的IO流使用了装饰模式,关闭最外面的流的时候会自动调用被包装的流的close()方吗?
如果按顺序关闭流,是从内层流到外层流关闭还是从外层到内存关闭?
问题 1 的解释:
FileInputStream is = new FileInputStream("."); BufferedInputStream bis = new BufferedInputStream(is); bis.close();
从设计模式上看:
java.io.BufferedInputStream是java.io.InputStream的装饰类。
BufferedInputStream装饰一个 InputStream 使之具有缓冲功能,is要关闭只需要调用最终被装饰出的对象的 close()方法即可,因为它最终会调用真正数据源对象的 close()方法。
因此,可以只调用外层流的close方法关闭其装饰的内层流,验证例子:(我对上述应用博文做了一些改进):
主要思路是:继承后重写close方法,提供一个额外的判断布尔值,来告诉我们内层流对象的close方法是否因为外层流对象调用close方法而调用:
import java.io.*;/*** @author Fisherman*/
public class Test4 {public static void main(String[] args) throws Exception {FileOutputStream fos = new FileOutputStream("d:\\a.txt");OutputStreamWriter_my osw = new OutputStreamWriter_my(fos, "UTF-8");BufferedWriter bw = new BufferedWriter(osw);bw.write("java IO close test");bw.close();if (osw.ifClosed) {System.out.println("外层流导致了内层流的关闭");}}
}class OutputStreamWriter_my extends OutputStreamWriter {public OutputStreamWriter_my(OutputStream out, String charsetName) throws UnsupportedEncodingException {super(out, charsetName);}public boolean ifClosed = false;@Overridepublic void close() throws IOException {super.close();ifClosed = true;}
}
问题 2 的解释:如果不想使用(1)方式关闭流,可以逐个关闭流(可能大家比较习惯吧):
public class Test4 {public static void main(String[] args) throws Exception {FileOutputStream fos = new FileOutputStream("d:\\a.txt");OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");BufferedWriter bw = new BufferedWriter(osw);bw.write("java IO close test");//从内带外顺序顺序会报异常fos.close();osw.close();bw.close();}
}
报出异常:
Exception in thread "main" java.io.IOException: Stream closedat sun.nio.cs.StreamEncoder.ensureOpen(StreamEncoder.java:45)at sun.nio.cs.StreamEncoder.write(StreamEncoder.java:118)at java.io.OutputStreamWriter.write(OutputStreamWriter.java:207)at java.io.BufferedWriter.flushBuffer(BufferedWriter.java:129)at java.io.BufferedWriter.close(BufferedWriter.java:265)at com.fisherman.learnIO.Test4.main(Test4.java:19)
如果改为从外到内的流关闭顺序:
public static void main(String[] args) throws Exception {FileOutputStream fos = new FileOutputStream("d:\\a.txt");OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");BufferedWriter bw = new BufferedWriter(osw);bw.write("java IO close test");// 从外到内顺序关闭okbw.close();osw.close();fos.close();}
程序正确执行。
一般情况下是:先打开的后关闭,后打开的先关闭
另一种情况:看依赖关系,如果流a依赖流b,应该先关闭流a,再关闭流b
例如处理流a依赖节点流b,应该先关闭处理流a,再关闭节点流b
当然完全可以只关闭处理流,不用关闭节点流。处理流关闭的时候,会调用其处理的节点流的关闭方法
如果将节点流关闭以后再关闭处理流,会抛出IO异常;
四、如何正确方式关闭流
使用try-with-resources语句,或者对每个流对象建立一套try-with语句块来进行流的关闭。
Java-IO 流的Close方法相关推荐
- JAVA IO流文本文件读入方法(read方法读入数据)
在字符流通常都使用read方法读入数据,而read方法一般都两种调用方式. 首先先创建一个文件,如Hello.txt,里面输入HelloWorld! 第一种是使用read的空参调用:read() re ...
- java io flush_《文件传输基础——Java IO流》,对其中flush方法的思考
在学习了<文件传输基础--Java IO流> 课程后,发现自己对flush()方法的调用有很多疑惑.在查询资料和自己看源码以及动手试验之后发现有以下几个特点.如有误也请大家指正出来,一切为 ...
- [重学Java基础][Java IO流][Exter.2]IO流中几种不同的读写方法的区别
[重学Java基础][Java IO流][Exter.2]IO流中几种不同的读写方法的区别 Read 读入方法 read(): 一般是这种形式 public int read() 1.从流数据中读取的 ...
- JAVA IO流read方法基础使用
java io流read方法基础用法 一.FileInputStream 二.字节数组读取 三.循环读取 一.FileInputStream 首先使用FileInputStream创建一个读入流,读取 ...
- Java面试题:IO流中read()方法为什么返回值是int
Question:IO流中read()方法为什么返回值是int? anwser:因为字节输入流可以操作任意类型的文件,比如图片音频等,这些文件底层都是以二进制形式的存储的,如果每次读取都返回byte, ...
- Java IO流学习总结四:缓冲流-BufferedReader、BufferedWriter
Java IO流学习总结四:缓冲流-BufferedReader.BufferedWriter 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/article/det ...
- Java IO流学习总结三:缓冲流-BufferedInputStream、BufferedOutputStream
Java IO流学习总结三:缓冲流-BufferedInputStream.BufferedOutputStream 转载请标明出处:http://blog.csdn.net/zhaoyanjun6/ ...
- Java读取文件流用什么对象_使用Java IO流实现对文本文件的读写过程中,通常需要处理下列( )异常。_学小易找答案...
[论述题]请根据第一次平时作业的选题,结合第二次课内容,自拟一个论文提纲. [单选题]在 switch ( expression )语句中, expression 的数据类型不能是 ( ) [单选题] ...
- java IO流面试总结
1.什么是比特(Bit),什么是字节(Byte),什么是字符(Char),它们长度是多少,各有什么区别 答案 Bit最小的二进制单位 ,是计算机的操作部分 取值0或者1 Byte是计算机操作数据的最小 ...
- java io流大全_Java IO流系统整理
Java IO流的分类 Java中的流,可以从不同的角度进行分类. 按流向分类: 输入流: 程序可以从中读取数据的流. 输出流: 程序能向其中写入数据的流. 按数据传输单位分类: 字节流:以字节(8位 ...
最新文章
- Android网络之数据解析----SAX方式解析XML数据
- [SinGuLaRiTy] 2017 百度之星程序设计大赛 初赛A
- 解决mac osx下pip安装ipython权限的问题
- 2019 ICPC Asia-East Continent Final
- 基于SpringMVC进行REST服务开发
- 如何用计算机处理频谱,如何使用PicoScope PC示波器对CD播放器的音频频谱进行分析...
- 前端面试:你应该了解的JS算法相关的知识
- 使用CNN实现图像分类——理解卷积神经网络(卷积、池化、全连接)
- 15-07-22 数据库--存储过程、触发器
- 跨网段和同网段的通信
- 8051单片机实现与GSM通讯
- 使用IIS 共享文件
- 模乘与Montgomery 模乘
- centos安装图形化界面及vnc-server连接
- 视频教程-10分钟搞定 php+H5手机网页微信支付 在线视频教程(含源代码)-微信开发
- 微信小程序之时间计算器
- 到底买苹果XS还是XR_没有5G的苹果到底还值不值得买
- Python+Wind:用Pyautogui轻松下载Wind数据
- opencv学习笔记(八)-- 在图像上绘制形状和文字
- 新闻报道翻译研讨20091128 饱受战争冲突之苦的国家腐败根深蒂固Corruption Digs Deepest in Countries in Conflict...
热门文章
- 五十、头文件和源文件的区别和规范(extern)
- A-LOAM安装与配置
- 计算机“字符”,“字节”以及各单位说明
- 彻底删除360云盘图标
- Ext文件上传总是返回.do
- IDEA_02 修改项目编码格式修改字体,大小
- Keil中使有C99及其新特性
- itertools库常用高效迭代器一览表,帮你快速实现数据的排列组合【python】
- 基于第三方开发Android锁屏
- SpringBoot--maven-wrapper(mvnw)--使用/详解