流
java将输入与输出比喻为"流",Stream
以同一个方向顺序移动的过程,流动的是字节(2进制)
IO中有输入流和输出流之分
java.io.InputStream
所有字节输入流的超类
- int read()
读取一个字节,以int形式返回,该int值的"低八位"有效,若返回值为-1则表示EOF - int read(byte[] data)
块读取,尝试最多读取给定数组的length个字节并存入该数组,返回值为实际读取到的字节量
java.io.OutputStream
所有字节输出流的超类
- void write(int d)
写出一个字节,写的是给定的int的"低八位" - void write(byte[] data)
块写,将给定的字节数组中的所有字节全部写出 - void write(byte[] data,int offset,int len)
块写,将给定的字节数组中从offset处开始的连续len个字节一次性写出
java将流分为两类:节点流与处理流
- 节点流:也称为低级流
1.节点流的另一端是明确的,是实际读写数据的流,
2.IO读写一定是建立在节点流基础上进行的
3.文件流就是典型的节点流 - 处理流:也称为高级流
1.处理流不能独立存在,必须连接在其他流上,
2.目的是当数据经过当前高级流时可以对数据进行某种加工操作,来简化我们的同等操作
3.实际开发中我们经常"串联"一组高级流最终到某个低级流上,使读写数据以流水线式的加工处
理完成。这一操作也被称为使"流的链接"。流链接也是JAVA IO的精髓所在
java将流按照读写单位划分为字节流与字符流
文件流(节点流)
是连接我们的程序与文件之间的管道,用来读写文件字节数据的流
文件流是继承自InputStream和OutputStream
java.io.FileOutputStream
使用文件输出流向文件中写入字节数据
构造器
覆盖模式是指若指定的文件存在,文件流在创建时会先将该文件原内容清除
- FileOutputStream(String pathname)
创建文件输出流对指定的path路径表示的文件进行写操作,如果该文件不存在则将其创建出来 - FileOutputStream(File file)
创建文件输出流对指定的File对象表示的文件进行写操作,如果该文件不存在则将其创建出来
但是如果该文件所在的目录不存在会抛出异
常:java.io.FileNotFoundException
追加模式是指若指定的文件存在,文件流会将写出的数据陆续追加到文件中
- FileOutputStream(String pathname,boolean append)
如果第二个参数为true则为追加模式,
false则为覆盖模式 - FileOutputStream(File file,boolean append)
同上
方法
-
void write(int d)
用来向文件中写入1个字节
会将给定的int值对应的2进制的"低八位"写出 -
void write(byte[] data)
向文件中块写数据。将数组data中所有字节一次性写入文件 -
void write(byte[] data,int off,int len)
向文件中块写数据。将数组data中从下标off开始的连续len个字节一次性写入文件 -
close()
当IO操作完后要关闭
java.io.FileInputStream
使用文件输入流向从文件中读取字节数据
构造器
- FileInputStream(String path)
基于给定的路径对应的文件创建文件输入流
如果指定的文件不存在则会抛出异常java.io.FileNotFoundException - FileInputStream(File file)
基于给定的File对象所表示的文件创建文件输入流
如果File表示的文件不存在则会抛出
异常java.io.IOException
方法
- int read()
读取1个字节,以int形式返回该字节内容。int值只有"低八位"有数据,高24位 全部补0
如果返回的int值为整数-1,则表示EOF; EOF:end of file 文件末尾 - int read(byte[] data)
块读数据,从文件中一次性读取给定的data数组总长度的字节量并从数组第一 个元素位置开始存入数组中。返回值为实际读取到的字节数。如果返回值为整数-1则表示读取到了文件末尾 - close()
文件复制
- 复制文件的原理就是使用文件输入流从原文件中陆续读取出每一个字节,然后再使用文件输出流将字节
陆续写入到另一个文件中完成的; - 循环进行读取直到某次 fileInputStream.read() 方法返回值为-1,表示读取到了文件末尾,那么就不再写出即可
块读写
效率问题
- 上述案例在复制文件时的读写效率是很低的。因为硬盘的特性,决定着硬盘的读写效率差,而单字节读
写就是在频繁调用硬盘的读写,从而产生了"木桶效应"。 - 为了解决这个问题,我们可以采取使用块读写的方式来复制文件,减少硬盘的实际读写的次数,从而提
高效率。
InputStream定义了块读的方法
块读: 一次性读取一组字节的方式
- int read(byte[] data)
一次性读取给定字节数组总长度的字节量并存入到该数组中。 返回值为实际读取到的字节数。如果返回值为-1表示本次没有读取到任何字节已经是流的末尾了
OutputStream定义了块写的方法
块写: 一次性写出一组字节的方式
- void write(byte[] data)
一次性将给定数组中所有字节写出 - void write(byte[] data,int offset,int len)
一次性将data数组中从下标offset处开始的连续len个字节一次性写出
提高每次读写的数据量减少读写次数,可以提高读写效率
写出/入文本数据
文件中只能保存2进制信息,因此我们要想写出文本数据,需要先将字符串转换为2进制。
String提供了将字符串转换为一组字节的方法
- byte[] getBytes(charset cs)
将当前字符串按照指定的字符集转换为一组字节
读取文本数据
先将文件中的字节读取出来,然后再将这些字节按照对应的字符集转换为字符串即可
String的构造器提供了对字节解码为字符串的操作
- String(byte[] data,charset cn)
将data数组中的所有字节按照指定的字符集转换为字符串
缓冲流(处理流)
功能
- 在流链接中的作用:加快读写效率
- 通常缓冲是最终链接在低级流上的流
原理
- 内部定义了一个属性byte buf[]。它等同于块写操作。
并且默认创建时,该buf数组的长度为8192(8kb)长度。 - 缓冲流在读写数据时总是以块读写数据(默认是8kb)来保证读写效率的
缓冲流提供了多种构造器,可以自行指定缓冲区大小。
Java.io.BufferedOutputStream
缓冲字节输出流
构造器
- BufferedOutputStream(OutputStream out)
实例化一个缓冲字节输出流并链接在指定的字节输出流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192) - BufferedOutputStream(OutputStream out,int size)
实例化一个指定缓冲区大小的缓冲字节输出流并链接在指定的字节输出流上。
方法
写缓冲问题
由于缓冲输出流会将写出的数据装满内部缓冲区(默认8kb的字节数组)后才会进行一次真实的写出操作。
当我们的数据不足时,如果想要及时写出数据,可以调用缓冲流的**flush()**方法,强制将缓冲区中已经缓
存的数据写出一次。
- void flush()
强制将缓冲流的缓冲取(内部维护的字节数组)中已经缓存的字节一次性写出 - close()
缓冲输出流的close()方法内部会自动调用一次flush()方法确保数据写出
flush的传递
- flush()方法是被定义在java.io.Flushable中。而字节输出流的超类java.io.OutputStream实现了该接口,这意味着所有的字节输出流都有flush方法。而除了缓冲流之外的高级流的flush方法作用就是调用它链接的流的flush方法将该动作传递下去。最终传递给缓冲流来清空缓冲区。
java.io.BufferedInputStream
缓冲字节输入流
构造器
- BufferedInputStream(InputStream in)
实例化一个缓冲字节输入流并链接在指定的字节输入流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192) - BufferedInputStream(InputStream in,int size)
实例化一个指定缓冲区大小的缓冲字节输入流并链接在指定的字节输入流上
对象流(处理流)
序列化
将一个对象转换为一组可被传输或保存的字节。这组字节中除了包含对象本身的数据外,还会包含结构信息
序列化的意义
实际开发中,我们通常会将对象
- 写入磁盘,进行长久保存
- 在网络间两台计算机中的java间进行传输
无论是保存在磁盘中还是传输,都需要将对象转换为字节后才可以进行
java.io.ObjectOutputStream
对象输出流: 将我们的java对象进行序列化
方法
- void writeObject(Object obj)
将给定的对象转换为一组可保存或传输的字节然后通过其链接的流将字节写出
序列化要求
序列化对象时要求该对象对应的类必须实现接口:java.io.Serializable
如果写出的对象对应的类没有实现该接口,那么writeObject会抛出下面异常 java.io.NotSerializableException
java.io.ObjectInputStream
对象输入流: 将java对象进行反序列化
构造器
- ObjectInputStream(InputStream in)
将当前创建的对象输入流链接在指定的输入流上
方法
Object readObject()
进行对象反序列化并返回。该方法会从当前对象输入流链接的流中读取若干字节并将其还原为对象。这里要注 意读取的字节必须是由ObjectOutputStream序列化一个对象所得到的字节。
transient关键字
- 当一个属性被transient关键字修饰后,该对象在进行序列化时,转换出来的字节中是不包含该属性的。
- 序列化时不包含的属性,反序列化时该属性值为null
字符流
java.io.Writer
所有字符输出流的超类
方法
- void write(int c)
写出一个字符,写出给定int值”低16”位表示的字符 - void write(char[] chs)
将给定字符数组中所有字符写出 - void write(String str)
将给定的字符串写出 - void write(char[] chs,int offset,int len)
将给定的字符数组中从offset处开始连续的len个字符写出
java.io.Reader
所有字符输入流的超类
方法
- int read()
读取一个字符,返回的int值“低16”位有效。当返回值为-1时表达流读取到了末尾 - int read(char[] chs)
从该流中读取一个字符数组的length个字符并存入该数组,返回值为实际读取到的字符量。当返回值为-1时表达流读取到了末尾
转换流(处理流)
- InputStreamReader和OutputStreamWriter是常用的字符流的实现类
- 实际开发中我们不会直接操作他们,但是他们在流连接中是必不可少的一环
流连接中的作用
- 衔接字节流与其他字符流
- 将字符与字节相互转换
意义
实际开发中我们还有功能更好用的字符高级流.但是其他的字符高级流都有一个共通点:不能直接连接在字节流上.而实际操作设备的流都是低级流同时也都是字节流.因此不能直接在流连接中串联起来.转换流是一对可以连接在字节流上的字符流,其他的高级字符流可以连接在转换流上.在流连接中起到"转换器"的作
用(负责字符与字节的实际转换)
java.io.OutputStreamWriter
输出转换流
构造器
- OutputStreamWriter(OutputStream out,Charset cs)
基于给定的字节输出流以及字符编码创建写出转换流 - OutputStreamWriter(OutputStream out)
该构造方法会根据系统默认字符集创建写出转换流
java.io.InputStreamReader
输入转换流
构造器
- InputStreamReader(InputStream in,Charset cs)
基于给定的字节输入流以及字符编码创建当前读取转换流 - InputStreamReader(InputStream in)
该构造方法会根据系统默认字符集创建当前读取转换流
缓冲字符流(处理流)
缓冲字符流内部也有一个缓冲区,读写文本数据以块读写形式加快效率.并且缓冲流有一个特别的功能:可以按行读写文本数据.缓冲流内部维护一个char数组,默认长度8192.以块读写方式读写字符数据保证效率
java.io.PrintWriter
缓冲字符输出流
-
具有自动行刷新的缓冲字符输出流,实际开发中更常用.它内部总是会自动连接BufferedWriter作为块写加速使用
-
可以按行写出字符串
-
具有自动行刷新功能
对文件写操作的构造器
- PrintWriter(File file)
- PrintWriter(String path)
还支持指定字符集
- PrintWriter(File file,String csn)
- PrintWriter(String path,String csn)
上述构造器看似PW可以直接对文件进行操作,但是它是一个高级流,实际内部会进行流连接:
- this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),false);
其它构造器
- PritWriter(Writer writer)
将当前实例化的PrintWriter链接在指定的字符输出流上 - PrintWriter(OutputStream out)
将当前实例化的PrintWriter链接在指定的字节输出流上
由于除了转换流外的其他字符流都不能直接连在字节流上,因此这个构造器内部会自动链接在 BufferedWriter上
并且让BufferedWriter链接在转换流OutputStream上,最后再让转换流链接再指定的字节输出流上
自动行刷新
PrintWriter支持自动行刷新,每当我们调用println方法写出一行内容后自动flush一次
对应的构造器
- PritWriter(Writer writer,boolean autoflush) 如果第二个参数为true则开启自动行刷新
java.io.BufferedReader
缓冲字符输入流
- 内部维护一个默认8192长度的char数组,总是以块读取文本数据保证读取效率。
- 缓冲输入流提供了一个按行读取文本数据的方法
方法
- String readLine()
返回一行字符串。方法内部会连续扫描若干个字符,直到遇到换行符为止,将换行符之前的内容以一个字符串 形式返回。
返回的字符串中不含有最后的换行符。
返回值有三种情况:
1:正常一行内容
2:空字符串。当读取了一个空行时(这一行只有一个换行符)。 3:null。当流读取到了末尾时。
当我们第一次调用readLine()时,流并不是只读取了一行字符串,而是先进行块读操作(一次性读取8192个 字符并转入到内部的char数组中),然后扫描内部的char数组,然后将第一行字符串返回。第二次调用后再继 续扫描后去的内容以此类推。