首页 > 编程语言 >java中是否所有的stream流都需要主动关闭

java中是否所有的stream流都需要主动关闭

时间:2024-04-24 21:55:05浏览次数:35  
标签:文件 调用 java stream 数据 flush 缓冲区 close 流都

流的概念

在输出数据时,内存中的特定数据排成一个序列,依次输出到文件中,这个数据序列就像流水一样源源不断地“流”到文件中,因此该数据序列称为输出流。同样,把文件中的数据输入到内存中时,这个数据序列就像流水一样“流”到内存中,因此把该数据序列称为输入流。

 

输入流与输出流

为什么要按照流的方式来读取和保存数据呢?

因为流可以保证原始数据的先后顺序不会被打乱,在很多情况下,这都是符合实际需求的。比如读一篇文章,肯定是希望从头到尾的读取文章,而不希望打乱文章的章节顺序。

无论是输入流还是输出流,如果数据序列中最小的数据单元是字节,那么称这种流为字节流。

数据单元是字节的字节流如果数据序列中最小的数据单元是字符,那么称这种流为字符流。

流在传输过程中的缓冲区概念

先上个例子:

public class FlushTest {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("F:\\Hello1.txt"); //大文件
FileWriter fileWriter = new FileWriter("F:\\Hello2.txt");
int readerCount = 0;
//一次读取1024个字符
char[] chars = new char[1024];
while (-1 != (readerCount = fileReader.read(chars))) {
fileWriter.write(chars, 0, readerCount);
}
}
}

这里并没有调用close()方法。close()方法包含flush()方法 ,即close会自动flush结果:

 可以看到,复制的文件变小了。明显,数据有丢失,丢失的就是缓冲区“残余”的数据。在计算机层面,Java对磁盘进行操作,IO是有缓存的,并不是真正意义上的一边读一边写,底层的落盘(数据真正写到磁盘)另有方法。所以,最后会有一部分数据在内存中,如果不调用flush()方法,数据会随着查询结束而消失,这就是为什么数据丢失使得文件变小了。

BufferedOutputStream、BufferedFileWriter 同理再举个例子:

class FlushTest2{
public static void main(String[] args) throws IOException {
FileWriter fileWriter = new FileWriter("F:\\Hello3.txt");
fileWriter.write("今天打工你不狠,明天地位就不稳\n" +
"今天打工不勤快,明天社会就淘汰");
}
}

不调用flush()方法你会发现,文件是空白的,没有把数据写进来,也是因为数据在内存中而不是落盘到磁盘了。所以为了实时性和安全性,IO在写操作的时候,需要调用flush()或者close()

close() 和flush()的区别:

关close()是闭流对象,但是会先刷新一次缓冲区,关闭之后,流对象不可以继续再使用了,否则报空指针异常。

flush()仅仅是刷新缓冲区,准确的说是"强制写出缓冲区的数据",流对象还可以继续使用。

总结一下:

1、Java的IO有一个 缓冲区 的概念,不是Buffer概念的缓冲区。

2、如果是文件读写完的同时缓冲区刚好装满 , 那么缓冲区会把里面的数据朝目标文件自动进行读或写(这就是为什么总剩下有一点没写完) , 这种时候你不调用close()方法也0不会出现问题

3、如果文件在读写完成时 , 缓冲区没有装满,也没有flush(), 这个时候装在缓冲区的数据就不会自动的朝目标文件进行读或写 , 从而造成缓冲区中的这部分数据丢失 , 所以这个是时候就需要在close()之前先调用flush()方法 , 手动使缓冲区数据读写到目标文件。

举个很形象的例子加深理解:我从黄桶通过水泵把水抽到绿桶,水管就相当于缓冲区,当我看到黄桶水没有了,我立马关了水泵,但发现水管里还有水没有流到绿桶,这些残留的水就相当于内存中丢失的数据。如果此时我再把水泵打开,把水管里的水都抽了一遍,此时水管里面的水又流到了绿桶,这就相当于调用了flush()方法。

java Stream对象如果不关闭会发生什么?

比如FileStream或者说HttpClient 中的HTTPResponse,不关闭会发生什么呢?或者说调用close防范实际上在底层都做了哪些事?

看是什么类了, 不同的类的close里执行的逻辑当然是不一样的. close就是用来做收尾工作的, 如果你学过servlet, 可以认为就是servletdestroy方法.
有一些类会占用特殊资源(比如文件句柄, 线程, 数据库连接等), 而这些资源是有限的/比较消耗性能的, 而且不会自动释放(或者需要很久才能自动释放), 因此需要你在不用的时候及时释放, 避免浪费资源.

比如IO里面的:

  • FileInputStream会占用系统里的一个文件句柄, 每个进程可以打开的文件数量是有限的, 如果一直打开而不关闭, 理论上迟早会遇到无法打开的情况.
  • StringWriter就没有什么. close方法没什么卵用
Closing a StringWriter has no effect. The methods in this class can be called after the stream has been closed without generating an IOException.

ps: FileInputStreamfinalize方法会自动调用close方法. 但是需要等待很长很长时间. 所以最好自己手工调用.

一般而言, 如果是接口里有close方法, 我们调用时是不应该关注close里究竟执行了什么, 不调用是不是有坏处, 而应该是始终调用

你打开文件是会在系统里有一个文件句柄的,这个句柄数量操作系统里是有限的,如果不close,这个句柄所代表的资源就泄露了,就跟悬垂指针一样,如果量大或时间长了之后再打开文件就可能打不开了,超过了系统的限制

有没有不需要关闭的流

曾几何时,作为java程序员要记住的一条准则就是,流用完了一定要在关闭,一定要写在finally里。

finally {
	out.flush();
 	out.close();
}

但是最近发现一个stream是不需要关闭的。它就是ByteArrayOutputStream,当然还有它的妹妹ByteArrayInputStream和表哥StringWriter。道理一样,我们就不讨论亲戚们了。 作为一种OutputStream它也extendsOutputStream,自然也有继承了flush()close()。 但这2个方法的方法体为空。

    /**
     * Closing a <tt>ByteArrayOutputStream</tt> has no effect. The methods in
     * this class can be called after the stream has been closed without
     * generating an <tt>IOException</tt>.
     */
    public void close() throws IOException {
    }
    
    /***
    * OutputStream的方法,ByteArrayInputStream并未重写
    */
    public void flush() throws IOException {
    }    

究其原因也不难理解。其实ByteArrayInputStream内部实现是一个byte数组,是基于内存中字节数据的访问。并没有占用硬盘,网络等资源。就算是不关闭,用完了垃圾回收器也会回收掉。这点跟普通数组并没有区别。既然是操作内存,就要考虑到内存大小,如果字节流太大,就要考虑内存溢出的情况。

但是,作为一个蛋疼的程序员,习惯性关闭流是一个好习惯,不管三七五十八,先close掉再说,现在close是空方法,保不齐哪天就有了呢?这也是百利无一害的事,就好像保健品,吃了不治病,但是也吃不坏。

  • 结论就是:指向内存的流可以不用关闭,指向硬盘/网络等外部资源的流一定要关闭。

 

本篇文章如有帮助到您,请给「翎野君」点个赞,感谢您的支持。

首发链接:https://www.cnblogs.com/lingyejun/p/18156434

标签:文件,调用,java,stream,数据,flush,缓冲区,close,流都
From: https://www.cnblogs.com/lingyejun/p/18156434

相关文章

  • 9.prometheus监控--监控springboot2.x(Java)
    一、环境部署yumsearchjava|grepjdkyuminstall-yjava-11-openjdk-devel二、监控java应用(tomcat/jar)JMXexporter负责收集Java虚拟机信息---没举例,等以后再做测试进入到tomcat安装目录,vimPROMETHEUS_JMX_EXPORTER_OPTS="-javaagent:../prometheus-exporter......
  • K8S集群问题:案例一:Java调用Glibc2.28-69内存分配器无法限制虚拟内存VIRT问题
    一、问题描述1、背景:租户反馈,Apr711:22容器出现夯死现象,容器部署的单个java进程;宿主机上,top显示的容器进程virt内存持续增长32G,目前messages日志没有看到oom的记录,基本是。租户其他bc7、8系统上有添加参数MALLOC_ARENA_MAX进行限制,基本维持在16G左右,目前bcoe21.10系统配置......
  • Java源码阅读-String中的private final char value[];
    /**Thevalueisusedforcharacterstorage.*/privatefinalcharvalue[];在Java的源码中是这样来实现String对字符串的存储的首先使用final关键字来修饰这个变量,来保证value不会被重写,确保字符串的内容在创建后不会被修改,从而保持字符串的不可变性。final是......
  • Java源码阅读-String.startsWith(String prefix, int toffset)
    /***Testsifthesubstringofthisstringbeginningatthe*specifiedindexstartswiththespecifiedprefix.**@paramprefixtheprefix.*@paramtoffsetwheretobeginlookinginthisstring.*@return{@codetrue}ifthecharacter......
  • JavaScript精粹:26个关键字深度解析,编写高质量代码的秘诀!
    JavaScript关键字是一种特殊的标识符,它们在语言中有固定的含义,不能用作变量名或函数名。这些关键字是JavaScript的基础,理解它们是掌握JavaScript的关键。今天,我们将一起探索JavaScript中的26个关键字,了解这些关键字各自独特的含义、特性和使用方法。一、JavaScript关键字是什么......
  • 设计模式-状态模式在Java中的使用示例-信用卡业务系统
    场景在软件系统中,有些对象也像水一样具有多种状态,这些状态在某些情况下能够相互转换,而且对象在不同的状态下也将具有不同的行为。为了更好地对这些具有多种状态的对象进行设计,我们可以使用一种被称之为状态模式的设计模式。状态模式用于解决系统中复杂对象的状态转换以及不同状......
  • Windows中jps命令无法查看java进程问题
    参考 https://blog.csdn.net/qq_43413788/article/details/107772563原因:在Windows系统中,每个java进程启动之后都在%TMP%/hsperfdata_${user}(${user}为当前登录用户名)目录下建立一个以该java进程pid为文件名的文件,用以记录该java进程的一些信息。通常是因为没......
  • 最近5年133个Java面试问题列表
    来源:https://github.com/YHGui/easy-job/blob/master/面试题/133个Java常见面试题.mdJava面试随着时间的改变而改变。在过去的日子里,当你知道String和StringBuilder的区别(String类型和StringBuffer类型的主要性能区别其实在于String是不可变的对象。因此在每次对Stri......
  • Java引用拷贝、浅拷贝、深拷贝
    原链接在开发、刷题、面试中,我们可能会遇到将一个对象的属性赋值到另一个对象的情况,这种情况就叫做拷贝。拷贝与Java内存结构息息相关,搞懂Java深浅拷贝是很必要的!在拷贝中这里就分为引用拷贝、浅拷贝、深拷贝进行讲述。引用拷贝引用拷贝会生成一个新的对象引用地址,但是两个最......
  • Java 中什么是弱引用
    java弱引用对象被回收后,弱引用会变为null吗会。对象被回收后,空间会被回收,给空间地址分配的地址也会被回收,其引用都变成了null。什么是弱引用弱引用(WeakReference)是一种特殊的引用关系,在计算机程序设计中,与强引用相对。弱引用的主要特点是它不能确保其引用的对象不会被垃圾回收......