IO流
Java中的IO流概念介绍及使用方法,此IO只针对于磁盘IO,没有网络IO相关知识
概述
IO就是input/output,这个是相对于内存而言的;
- iniput就是往内存里面放数据,数据从哪里来的呢?可以是本地磁盘,也可以是从网络获取的数据
- 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对象可以表示目录,也可以表示文件
路径说明:
- 传一个 “.” 代表的是当前目录,当前目录就是你的java项目的目录
- 传一个 “/“ 或 “\“ 就是表示的java项目所在磁盘的根目录
- 传一个 “..” 就是代表着上一级目录,也就是项目所属文件夹
- 也可以传绝对路径,就是表示的绝对路径目录
- 如果里面传的是具体文件file就是表示的具体文件,传目录就是表示的目录,file就是用来操作文件和目录的
API
创建File相关
- File file = new File(“..”); 构造方法
- file.getAbsolutePath() 返回绝对路径
- file.getPath() 返回创建file时传入构造方法的路径
- file.getCanonicalPath() 返回规范路径
- file.isFile() 是否是文件
- file.isDirectory() 是否是做
- File.separator 可以获取当前系统路径分隔符的表示符号,比如win是 “" ,linux是 “/“
file相关操作:判断 文件/目录 读写权限、创建删除 文件/目录
- file.canRead():是否可读
- file.canWrite():是否可写
- file.canExecute():是否可执行,如果file是目录,canExecute代表就是是否可以列出它包含的文件夹和子目录
- long length():文件字节大小。
- file.exists() 文件是否存在
- file.createNewFile() 创建一个新文件
- file.delete() 删除文件或目录,如果是目录的话,目录下必须为空才会删除
- file.mkdir() 创建目录
- file.mkdirs() 创建当前file对象表示的目录,如果父目录不存在也会把不存在的父目录创建出来
遍历目录
方式一:File[] listFiles = file.listFiles() 直接列出当前目录下的所有东西
方式二:通过new FileFilter()一个过滤器,过滤出自己想要的东西
方式三:方式二的lambda表达式写法:File[] listFiles1 = file.listFiles(File::isFile);
InputStream使用
基本说明
InputStream就是Java标准库提供的最基本的输入流,要特别注意的一点是,InputStream并不是一个接口,而是一个抽象类,它是所有输入流的超类。这个抽象类定义的一个最重要的方法就是int read()
这个方法会读取输入流的下一个字节,并返回字节表示的int值(0~255)。如果已读到末尾,返回-1表示不能继续读取了。
使用方法
普通读取,一个字节一个字节读取,效率低
使用缓冲区读取
注意事项
- read方法是阻塞的,意思就是必须得等read这个方法执行完之后才能执行后面的代码,因为读取IO流相比执行普通代码,速度会慢很多,因此,无法确定read()方法调用到底要花费多长时间。
- 实际上,InputStream也有缓冲区。例如,从FileInputStream读取一个字节时,操作系统往往会一次性读取若干字节到缓冲区,并维护一个指针指向未读的缓冲区。然后,每次我们调用int read()读取下一个字节时,可以直接返回缓冲区的下一个字节,避免每次读一个字节都导致IO操作。当缓冲区全部读完后继续调用read(),则会触发操作系统的下一次读取并再次填满缓冲区。
- 这个IO流里read和write的重载方法说明【重要】
OutputStream使用
基本说明
和InputStream类似,OutputStream也是抽象类,它是所有输出流的超类。这个抽象类定义的一个最重要的方法就是void write(int b),签名如下:
这个方法会写入一个字节到输出流。要注意的是,虽然传入的是int参数,但只会写入一个字节,即只写入int最低8位表示字节的部分(相当于b & 0xff)。
和InputStream类似,OutputStream也提供了close()方法关闭输出流,以便释放系统资源。要特别注意:OutputStream还提供了一个flush()方法,它的目的是将缓冲区的内容真正输出到目的地。
flush
为什么要有flush()
?因为向磁盘、网络写入数据的时候,出于效率的考虑,操作系统并不是输出一个字节就立刻写入到文件或者发送到网络,而是把输出的字节先放到内存的一个缓冲区里(本质上就是一个byte[]
数组),等到缓冲区写满了,再一次性写入文件或者网络。对于很多IO设备来说,一次写一个字节和一次写1000个字节,花费的时间几乎是完全一样的,所以OutputStream
有个flush()
方法,能强制把缓冲区内容输出。
通常情况下,我们不需要调用这个flush()
方法,因为缓冲区写满了OutputStream
会自动调用它,并且,在调用close()
方法关闭OutputStream
之前,也会自动调用flush()
方法。
使用方法
普通写入:write(byte[]) / write(int n) / write(byte[], off, len)
注意事项
注意flush方法的使用
FilterStream
这个是干嘛的:当我们需要inputstream具有很多功能的时候,比如需要带缓冲,计算签名功能,我们不能搞出来多个子类继承inputstream然后再操作,所以就有了这个
具体使用
当我们需要给一个“基础”InputStream
附加各种功能时,我们先确定这个能提供数据源的InputStream
,因为我们需要的数据总得来自某个地方,例如,FileInputStream
,数据来源自文件:
紧接着,我们希望FileInputStream
能提供缓冲的功能来提高读取的效率,因此我们用BufferedInputStream
包装这个InputStream
,得到的包装类型是BufferedInputStream
,但它仍然被视为一个InputStream
:
最后,假设该文件已经用gzip压缩了,我们希望直接读取解压缩的内容,就可以再包装一个GZIPInputStream
:
无论我们包装多少次,得到的对象始终是InputStream
,我们直接用InputStream
来引用它,就可以正常读取:
上述这种通过一个“基础”组件再叠加各种“附加”功能组件的模式,称之为Filter模式(或者装饰器模式:Decorator)。它可以让我们通过少量的类来实现各种功能的组合,类似的,OutputStream也是以这种模式来提供各种功能:
自己编写一个定制化的filterstream
只需要继承FilterInputStream,即可,关键就是构造方法传入的是inputstream,所以可以进行各种包装(装饰)
操作zip
基本说明
明白一个概念,zipEntry,可以看作是一个zip包中的具体文件,也可以看作是目录,但是entry并不存储数据,读取数据或写入数据还是操作的ZipInputStream或ZipOutputStream对象
ZipInputStream和ZipOutputStream都是filterstream
具体使用
创建zip文件
读取zip文件
读取ClassPath资源
基本说明
classPath就是编译后的classes文件,相对路径就是相对于classes文件夹来说的,classes文件夹就是classPath的根目录 “/“ ,所以从这里获取文件也是有特殊的方法的,一行代码
使用方法
基本使用
从classPath获取,从外部获取;场景:jar包里打进去默认的配置文件,也就是classPath中的,然后还有一个方法是可以获取外部配置文件,给用户自定义配置