跳到内容
Caiden's Blog
返回

IO流

Java中的IO流概念介绍及使用方法,此IO只针对于磁盘IO,没有网络IO相关知识

概述

IO就是input/output,这个是相对于内存而言的;

  1. iniput就是往内存里面放数据,数据从哪里来的呢?可以是本地磁盘,也可以是从网络获取的数据
  2. output就是从内存里往外面传数据,数据要传到哪里去呢?可以是本地磁盘,也可以是网络

IO是一种按照顺序读写的数据的模式,特点就是单向流动,就像自来水在水管里面流动一样,所以叫IO流

注意:InputStream流获取之后只能用一次,读取完了这个流就是空的了

InputStream/OutputStream

InputStream和OutputStream是以字节为单位读数据的,就是针对于字节流来做输入输出的,最小单位是byte,注意:nputStream/OutputStream这是两个抽象类

Reader/Writer

Reader和Writer是以字符为单位读数据的,针对于字符数据来做输入输出很方便,底层其实还是字节数据,只不过加了一层字节转字符和字符转字节的转化,最小单位是char。注意:Reader/Writer是抽象类

同步和异步

同步:读写IO代码时必须得等待数据返回后,才能执行后续代码,优点是代码编写简单,缺点是cpu利用率不足,InputStream/OutputStream和Reader/Writer都是同步IO

异步:读写IO时,仅仅发出请求,然后就可以执行后续代码了,优点是cpu利用率高,缺点是代码编写复杂

File使用

java.io提供了File类来操作文件和目录

创建File对象

File对象,创建的时候构造方法,可以传路径(相对/绝对),也可以传具体文件的路径(“D:\test.txt”),传目录就表示目录,传具体文件file就代表具体文件了,所以file对象可以表示目录,也可以表示文件

路径说明:

  1. 传一个 ”.” 代表的是当前目录,当前目录就是你的java项目的目录
  2. 传一个 ”/” 或 ”\” 就是表示的java项目所在磁盘的根目录
  3. 传一个 ”..” 就是代表着上一级目录,也就是项目所属文件夹
  4. 也可以传绝对路径,就是表示的绝对路径目录
  5. 如果里面传的是具体文件file就是表示的具体文件,传目录就是表示的目录,file就是用来操作文件和目录的

API

InputStream使用

基本说明

InputStream就是Java标准库提供的最基本的输入流,要特别注意的一点是,InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()

public abstract int read() throws IOException;

这个方法会读取输入流的下一个字节,并返回字节表示的int值(0~255)。如果已读到末尾,返回-1表示不能继续读取了。

使用方法

注意事项

  1. read方法是阻塞的,意思就是必须得等read这个方法执行完之后才能执行后面的代码,因为读取IO流相比执行普通代码,速度会慢很多,因此,无法确定read()方法调用到底要花费多长时间。
  2. 实际上,InputStream也有缓冲区。例如,从FileInputStream读取一个字节时,操作系统往往会一次性读取若干字节到缓冲区,并维护一个指针指向未读的缓冲区。然后,每次我们调用int read()读取下一个字节时,可以直接返回缓冲区的下一个字节,避免每次读一个字节都导致IO操作。当缓冲区全部读完后继续调用read(),则会触发操作系统的下一次读取并再次填满缓冲区。
  3. 这个IO流里read和write的重载方法说明【重要】

教你完全理解IO流里的read和write

OutputStream使用

基本说明

和InputStream类似,OutputStream也是抽象类,它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b),签名如下:

public abstract void write(int b) throws IOException;

这个方法会写入一个字节到输出流。要注意的是,虽然传入的是int参数,但只会写入一个字节,即只写入int最低8位表示字节的部分(相当于b & 0xff)。

和InputStream类似,OutputStream也提供了close()方法关闭输出流,以便释放系统资源。要特别注意:OutputStream还提供了一个flush()方法,它的目的是将缓冲区的内容真正输出到目的地。

flush

为什么要有flush()?因为向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个byte[]数组),等到缓冲区写满了,再一次性写入文件或者网络。对于很多IO设备来说,一次写一个字节和一次写1000个字节,花费的时间几乎是完全一样的,所以OutputStream有个flush()方法,能强制把缓冲区内容输出。

通常情况下,我们不需要调用这个flush()方法,因为缓冲区写满了OutputStream会自动调用它,并且,在调用close()方法关闭OutputStream之前,也会自动调用flush()方法。

使用方法

注意事项

注意flush方法的使用

FilterStream

这个是干嘛的:当我们需要inputstream具有很多功能的时候,比如需要带缓冲,计算签名功能,我们不能搞出来多个子类继承inputstream然后再操作,所以就有了这个

具体使用

当我们需要给一个“基础”InputStream附加各种功能时,我们先确定这个能提供数据源的InputStream,因为我们需要的数据总得来自某个地方,例如,FileInputStream,数据来源自文件:

InputStream file =new FileInputStream("test.gz");

紧接着,我们希望FileInputStream能提供缓冲的功能来提高读取的效率,因此我们用BufferedInputStream包装这个InputStream,得到的包装类型是BufferedInputStream,但它仍然被视为一个InputStream

InputStream buffered =new BufferedInputStream(file);

最后,假设该文件已经用gzip压缩了,我们希望直接读取解压缩的内容,就可以再包装一个GZIPInputStream

InputStream gzip =new GZIPInputStream(buffered);

无论我们包装多少次,得到的对象始终是InputStream,我们直接用InputStream来引用它,就可以正常读取:

┌─────────────────────────┐
│GZIPInputStream          │
│┌───────────────────────┐│
││BufferedFileInputStream││
││┌─────────────────────┐││
│││   FileInputStream   │││
││└─────────────────────┘││
│└───────────────────────┘│
└─────────────────────────┘

上述这种通过一个“基础”组件再叠加各种“附加”功能组件的模式,称之为Filter模式(或者装饰器模式:Decorator)。它可以让我们通过少量的类来实现各种功能的组合,类似的,OutputStream也是以这种模式来提供各种功能:

自己编写一个定制化的filterstream

只需要继承FilterInputStream,即可,关键就是构造方法传入的是inputstream,所以可以进行各种包装(装饰)

操作zip

基本说明

明白一个概念,zipEntry,可以看作是一个zip包中的具体文件,也可以看作是目录,但是entry并不存储数据,读取数据或写入数据还是操作的ZipInputStream或ZipOutputStream对象

ZipInputStream和ZipOutputStream都是filterstream

┌───────────────────┐
│    InputStream    │
└───────────────────┘


┌───────────────────┐
│ FilterInputStream │
└───────────────────┘


┌───────────────────┐
│InflaterInputStream│
└───────────────────┘


┌───────────────────┐
│  ZipInputStream   │
└───────────────────┘


┌───────────────────┐
│  JarInputStream   │
└───────────────────┘

具体使用

读取ClassPath资源

基本说明

classPath就是编译后的classes文件,相对路径就是相对于classes文件夹来说的,classes文件夹就是classPath的根目录 ”/” ,所以从这里获取文件也是有特殊的方法的,一行代码

InputStream resourceAsStream = ClassPathDemo.class.getResourceAsStream("/default.properties")

使用方法


分享到:

上一篇
TCP/IP五层模型简单介绍
下一篇
Linux命令总结