前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >字节流与缓冲流

字节流与缓冲流

作者头像
用户5745563
发布2019-07-04 11:41:55
6600
发布2019-07-04 11:41:55
举报
文章被收录于专栏:码思客码思客
java零基础入门-高级特性篇(十一) IO 流 2

本章先来看两大“流”派中的字节流。字节流相对字符流总体结构简单一点,只用记住它的4个最基本的操作类就可以了。下面一张图来看看这四个基本的操作类。

图解

上面这张图通过两个方面进行划分,一个是输入和输出,一个是具不具备缓冲功能。下面来具体分析一下。

不带缓冲的输入输出

FileInputStream

首先在磁盘上创建一个txt文件,我在D盘根目录创建(文件名为demo.txt),然后使用FileInputStream这个类来读取这个文件。

读取文件

这是最基本的文件读取方法。这段代码中有几个地方要注意一下。首先是File类型。这个也是处理文件的重要类型,下面先插个队,来先介绍一下File。

File

File用来操作文件,注意,这里是操作文件本身,而不是获得文件的内容,获取文件的内容就需要使用流了。比如上面的demo.txt文件,可以用File类通过文件在系统中的路径获取文件,但File无法读取demo.txt中的内容。通过文件路径创建File类型的对象以后,就可以通过一系列的API来操作文件,比如常用的一些方法:

getName():用于返回文件名或者路径。getPath():返回对象的路径。exists():判断文件是否存在等等。除了操作文件,还可以操作文件夹,比如mkdir()方法可以创建文件夹,经常和exists方法一起使用,判断是否需要创建文件夹,如果需要的文件夹不存在则创建它。

File类

以上代码就是File的基本操作,其他的操作可以参考API,这里不再逐一演示。总之,记住一点,File类型操作的是文件本身,无法操作文件内容。

第二个要注意的是D:\\demo.txt这个路径。我们通常使用windows作为编码的系统,而windows中路径分隔符是单个 \ ,但是在java代码中,需要添加一个\作为转义符,这样才能被java识别为路径分隔符。注意,我这里强调了windows系统,因为好死不死,在linux里面的路径分隔符恰恰是反的 / 。由于我们的代码最终会放在服务器上运行,所以我们不能将路径写死成只有windows系统可以识别的 \\ 。我们需要一个在windows里是 \ ,在linux里是 / 的方法。

这个方法File类也帮我们做好了,就是File.separator,将上面的路径改造为平台无关的写法就是

"D:" + File.separator + "demo.txt"

平台无关的路径分隔符:意思就是无论哪个平台都可以获取正确的路径分隔符,在windows下File.separator是 \,在linux下File.separator是 / 。

好了,File的基本操作介绍完了,下面继续介绍流。使用File类型根据文件路径创建一个文件的对象,然后用这个对象作为FileInputStream输入流的构造器参数,创建一个输入流。这样就可以通过流来获取文件的内容了。上例中,通过while循环逐个字节的读取文件中的内容,然后转换为char类型进行输出。

来看一下FileInputStream的构造器。FileInputStream有两个我们常用的构造器,一个接受File类型参数,就是上例中的写法。还有一个构造器接受一个字符串的参数,也就是文件路径。

构造器

这里顺便复习一下this关键字,在构造器中的this表示调用这个类的另一个不同参数的构造器,来看看this后面括号表示的是什么意思。如果参数中的文件不为空,那么就根据参数地址创建一个匿名文件对象,然后调用下面这个参数为File类型的构造器,所以上例中可以省略掉File对象的创建,直接给流传递一个文件路径也是可以的,因为接受字符串的构造器也可以完成创建File类型对象的工作。

FileInputStream fis = new FileInputStream("D:\\demo.txt");

但是这种输出方式有缺点,上一章介绍过中文编码,由于中文数量过于庞大,所以根据编码表中的编码,一个汉字可能占用2个或者3个甚至4个字节,这样如果逐个字节输出的话,当需要输出的内容是中文的时候,就会出现乱码。因为可能只输出了二分之一个或者三分之一个中文,这样没法显示一个完整的中文,只能是乱码。所以如果需要输出一个正确的中文,需要对代码进行改造。

中文乱码

改造的话就不能再是逐个字节的输出,而是需要将多个字节放在一起,同时读出来。

无乱码

这样将多个字节内容,通过String的构造器将字节转换为字符串,就可以正确的输出中文了。

为什么不读取一个视频或者图片,而要读取一个文本文件?文本文件不是应该使用字符流吗?

因为这里使用文本文件方便演示,如果读取一个图片或者视频,Eclipse没有办法来展示读取的图片或者视频,所以用文本文件来做例子比较方便。

FileOutputStream

既然输入流是读取文件的内容,那么相对应的,输出流就是将内容写入到文件中。下面来看看如何将内容写入文件。

输出流

首先看代码,首先是系统无关的分隔符写法,这里没有使用 \\ 而是使用File.separator替代。另外,和输入流类似的,输出流也有字符串参数的构造器。在这个构造器中,也有将文件路径转为File对象的操作,所以这里没有创建File对象的过程。

与输入流对应的,输出流将字符转为对应的int,然后逐个将int使用输出流的write方法,写入到文件中。除了使用int类型写入文件,还可以使用字节写入文件,这里与输入流操作类似,就不在过多解释,各位可以参照上面输入流的方法和API自行完成。

还是给点提示吧,字符串转为字节,可以使用getBytes()方法完成。上例中不再需要循环逐个读入字符,而是将str转为字节,str.getBytes(),然后用输出流fos调用write方法的重载方法write(str.getBytes())即可。

具有缓冲功能的输入输出

介绍完两个最基本的输入输出流后,再来看看具有缓冲功能的流如何使用。在看代码之前,首先要弄清楚,什么是具有缓冲功能。

缓冲流

上面讲解的普通流是逐个字节进行输入或输出,这样虽然可以完成工作,但是在效率上有很大的问题。当我们将文件读取的时候,会先加载到内存,然而刚刚加载了一个字节到内存,马上又要告诉磁盘,喂~大兄弟,给我把这个字节写到磁盘上,我们知道磁盘的效率比内存要低很多的,在磁盘写入的过程中,内存只能干瞪眼,当磁盘写完一个字节后,内存再把下一个字节交给磁盘,喂~大兄弟,继续写下一个,然后内存又等着磁盘写下一个字节。

普通流效率低下的最大原因就在于此,频繁的调用磁盘,导致无法发挥内存速度快的优点。于是为了提高效率,缓冲流出现了。看看缓冲流缓冲了什么?缓冲流并不是每一个字节都要调用一次磁盘,而是根据设置的缓冲区大小,每当缓冲区满了以后,再调用一次磁盘,比如上图中,缓冲区设置为3,结果就是每次缓冲区有3个字节的数据以后,再调用一次磁盘,这样一来,调用磁盘的次数就减少了很多,使效率得到了很大的提升。文件越大,缓冲流效率的提升越明显。

下面来看一个例子,首先是普通流。

普通流的文件复制

这里的普通流没有设置缓冲区,逐个字节进行文件读入和写入,花了17秒完成5m文件的复制。这里要注意的是流是需要关闭的,如果不关闭流可能会出现资源被占用或者内存泄漏的问题,通常在finally中关闭流,避免导致没有执行到流的关闭就抛出异常导致关闭流不成功。

缓冲流的文件复制

使用缓冲流进行文件的复制,可以看到文件的复制效率提高了很多。缓冲流的创建,需要InputStream子类作为参数,除了将普通流外面包装了一层,其他代码与普通流没有区别,这种包一层就能有更强功能的流,还有个名称叫做高级流,这种包一层的做法,有种更优雅的名称---“装饰模式”。关于装饰模式,后面章节来具体说明。

缓冲流自带缓冲区,这个缓冲区多大?

部分源码

理解了普通流的用法,缓冲流用起来没有什么难度,它仅仅是包装了一层而已,所以当我们需要对磁盘上的文件进行读写操作的时候,建议使用缓冲流,效率要高很多。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2019-03-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码思客 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档