文章目录
一、IO流的分类:
(1)根据流向分为输入流和输出流:
输出:将程序(内存)中的数据输出到磁盘等存储介质中。
输入:将磁盘等存储介质中的数据输入到程序(内存)中。
(2)根据传输数据单位分为字节流和字符流:
字节流:8bit,适合传输图片,视频。
字符流:16bit,适合传输文本数据。
字节流:
- 字节输出流:OutputSteam
- 字节输入流:InputStream
字符流:
- 字符输出流:Writer
- 字节输入流:Reader
上面也是Java IO流的四大基类,这四大基类都是抽象类,Java涉及IO的流共有40多个类,但是都是通过这四大基类派生的。
有这四大基类派生的子类名称都是以父类名作为子类后缀名。
(3)根据流的角色分为节点流和处理流:
节点流:直接在数据源或目的地读写数据。
![image-20211016100537947](https://gitee.com/oymn/img_upload/raw/master/img/20211016100538.png)
处理流:不直接连接到数据源或目的地,而是”连接“在已存在的流(节点流或者处理流)上,通过对数据的处理为程序提供更强大的读写功能。
![image-20211016100548995](https://gitee.com/oymn/img_upload/raw/master/img/20211016100549.png)
简单来说就是,节点流是用来发送或接受数据的,而处理流是用来增强这个传输过程的。
二、IO流的四大基类
IO流的体系结构:(底色为蓝色的是重点)
(1)InputStream
-
典型实现:FileInputStream
-
传输单位:字节(8bit)
-
适合读取非文本文件
相关方法:
//从输入流读取数据的下一个字节,返回0到255范围内的整数,如果到达流的末尾,则返回-1
int read();
//从输入流将最多b.length个字节的数据读入byte数组中,如果到达流的末尾,则返回-1,否则返回实际读取的字节数
int read(byte[] b)
//将输入流中最多 len 个数据字节读入 byte 数组中,尝试读取 len 个字节,但读取的字节也可能小于该值。
//以整数形式返回实际读取的字节数。如果到达流的末尾,则返回-1。
int read(byte[] b, int off, int len)
//关闭此输入流并释放与该流关联的所有系统资源
void close() throws IOException
(2 )Reader
-
典型实现:FileReader
-
传输单位:字符(16bit)
-
适合读取文本文件
相关方法:
//读取单个字符,作为整数读取的字符,范围在0到65535之间(2个字节),如果已到达流的末尾,则返回-1
int read();
//将字符读入数组。如果已到达流的末尾,则返回 -1。否则返回本次读取的字符数。
int read(char[] cbuf)
//将字符读入数组的某一部分。存到数组cbuf中,从off处开始存储,最多读len个字符。如果已到达流的末尾,则返回-1。否则返回本次读取的字符数。
int read(char[] cbuf,int off,int len)
//关闭此输入流并释放与该流关联的所有系统资源。
void close() throws IOException
(3)OutputStream
- 典型实现:FileOutputStream
- 传输单位:字节(8bit)
- 适合传输非文本文件
//将b.length 个字节从指定的byte 数组写入此输出流。
void write(byte[] b)
//将指定byte 数组中从偏移量off 开始的len 个字节写入此输出流
void write(byte[] b,int off,int len)
//刷新此输出流并强制写出所有缓冲的输出字节,调用此方法指示应将这些字节立即写入它们预期的目标。
public void flush()throws IOException
//关闭此输出流并释放与该流关联的所有系统资源
public void close() throws IOException
(4)Writer
- 典型实现:FileWriter
- 传输单位:字符(16bit)
- 适合传输文本文件
//写入字符数组
void write(char[] cbuf)
//写入字符数组的某一部分。从off开始,写入len个字符
void write(char[] cbuf,int off,int len)
//写入字符串
void write(String str)
//写入字符串的某一部分。
void write(String str,int off,int len)
//刷新该流的缓冲,则立即将它们写入预期目标。
void flush()
//关闭输出流
void close()
三、节点流
也叫文件流。包含 FileInputStream,FileOutputStream,FileReader,FileWriter
(1)FileReader
使用FileReader读取文本文件,并输出到控制台
FileReader reader = null;
try {
File file = new File("src\\hello.txt");
reader = new FileReader(file);
int data;
while((data = reader.read()) != -1){
System.out.print((char)data);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
//关闭流
if(reader != null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
使用数组进行缓冲:
FileReader reader = null;
try {
File file = new File("src\\hello.txt");
reader = new FileReader(file);
char[] cbuf = new char[5];
int len;
while((len = reader.read(cbuf)) != -1){
//转换成String,方便输出
String str = new String(cbuf, 0, len);
System.out.print(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(reader != null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
(2)FileWriter
使用FileWriter将字符串写到文件中
FileWriter writer = null;
try {
File file = new File("src\\hello.txt");
//这里为true表示在原来文件后面进行追加,而不是覆盖
writer = new FileWriter(file,true);
writer.write("i am ironman");
writer.write("i am spiderman");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(writer != null){
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
使用FileReader和FileWriter实现文件的复制:
Reader reader = null;
Writer writer = null;
try {
File srcFile = new File("src\\srcFile.txt");
File destFile = new File("src\\destFile.txt");
reader = new FileReader(srcFile);
writer = new FileWriter(destFile,true);
char[] cbuf = new char[1024];
int len;
while((len = reader.read(cbuf)) != -1){
writer.write(cbuf,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(reader != null){
reader.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try{
if(writer != null){
writer.close();
}
}catch (IOException e) {
e.printStackTrace();
}
}
(3)FileInputStream& FileOutStream
通过前面的学习,对于FileInputStream和FileOutStream其实也是依葫芦画瓢的做法,下面是复制一张图片的过程:
FileInputStream inputStream = null;
FileOutputStream outputStream = null;
try {
File srcFile = new File("src\\onepunch.jpg");
File destFile = new File("src\\newonepunch.jpg");
inputStream = new FileInputStream(srcFile);
outputStream = new FileOutputStream(destFile);
byte[] cbuff = new byte[1024];
int len;
while((len = inputStream.read(cbuff)) != -1){
outputStream.write(cbuff);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if(inputStream != null){
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(outputStream != null){
outputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
四、处理流
(1)缓冲流
作用:提高流的读取和写入速度。
注意:在资源关闭的时候,关闭外层流(处理流)后,会自动帮我们关闭掉内层流(节点流)。如果想要手动关闭节点流的话,就要先关闭处理流,然后再关闭节点流,这个顺序不能颠倒。
示例:
FileInputStream fis = null;
FileOutputStream fos = null;
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
File srcFile = new File("src\\srcFile.txt");
File destFile = new File("src\\destFile.txt");
//创建节点流
fis = new FileInputStream(srcFile);
fos = new FileOutputStream(destFile);
//创建缓冲流
bis = new BufferedInputStream(fis);
bos = new BufferedOutputStream(fos);
byte[] buff = new byte[1024];
int len;
while((len = bis.read(buff)) != -1){
bos.write(buff);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//可以只关闭处理流
try {
if(bis != null){
bis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
try {
if(bos != null){
bos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
关于BufferReader和BufferWriter都类似,这里也就不再赘述。不过对于字符缓冲流这里有另一种写法:
// 创建缓冲流对象:它是处理流,是对节点流的包装
br = new BufferedReader(new FileReader("d:\\IOTest\\source.txt"));
bw = new BufferedWriter(new FileWriter("d:\\IOTest\\dest.txt"));
String str;
while ((str = br.readLine()) != null) { // 一次读取字符文本文件的一行字符
bw.write(str); // 一次写入一行字符串
bw.newLine(); // 写入行分隔符
}
bw.flush(); // 刷新缓冲区
(2)转换流
- 实现字节流和字符流之间的转换。
两个转换流:
- InputStreamReader:将InputStream转换为Reader
- OutputStreamWriter:将Writer转换为OutputStream
很多时候都会使用转换流来处理文本乱码问题。
BufferedReader br = null;
BufferedWriter bw = null;
try {
FileInputStream fis = new FileInputStream("src\\dbcp.txt");
FileOutputStream fos = new FileOutputStream("src\\dbcp_new.txt");
InputStreamReader isr = new InputStreamReader(fis, "GBK");
OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");
br = new BufferedReader(isr);
bw = new BufferedWriter(osw);
String str = null;
while((str = br.readLine()) != null){
bw.write(str);
bw.newLine();
bw.flush();
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if(bw != null){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(br != null){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(3)输入输出流
System.in:是System中一个InputStream类型的静态变量
public final static InputStream in = null;
System.out:是System中一个PrintStream类型的静态变量,而PrintStream继承FilterOutputStream,FilterOutputStream继承了OutputStream。
public final static PrintStream out = null;
(4)打印流
打印流:PrintSystem 和 PrintWriter。
上面说的System.out就是一个PrintStream类型。
里面提供了一系列重载的print()和println()方法,用于多种数据类型的输出。
PrintSystem和PrintWriter的输出不会抛出IOException异常,并且有自动flush功能。
(5)数据流
数据流:DataInputStream 和 DataOutputStream。
用于读取和写出基本数据类型和String类型的数据。
![image-20211021000733676](https://gitee.com/oymn/img_upload/raw/master/img/20211021000733.png)
示例:
DataOutputStream dos = null;
DataInputStream dis = null;
try {
//写文件
dos = new DataOutputStream(new FileOutputStream("src\\data.txt"));
dos.writeUTF("oymn");
dos.writeInt(19);
dos.writeBoolean(true);
//读文件(需要按照存储顺序)
dis = new DataInputStream(new FileInputStream("src\\data.txt"));
String name = dis.readUTF();
int age = dis.readInt();
boolean isMale = dis.readBoolean();
System.out.println(name + " " + age + " " + isMale);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(dos != null){
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(dis != null){
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
(6)对象流
对象流:ObjectInputStream 和 ObjectOutputStream。
用于存储和读取基本数据类型和对象。可以将对象写入数据源(序列化),也可以把对象从数据源中恢复回来(反序列化)。
ObjectInputStream 和 objectOutputStream 不能序列化static 和 transient 修饰的成员变量。
**要想一个对象是可序列化的,必须实现Serializable(推荐)或者 Externalizable 其中一个接口,同时提供一个全局常量:serializableVersionUID **(用于唯一标识对象)。
示例:
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
//写对象
oos = new ObjectOutputStream(new FileOutputStream("src\\object.data"));
oos.writeObject(new String("我爱中国"));
oos.writeObject(new Person());
oos.flush();
//读对象
ois = new ObjectInputStream(new FileInputStream("src\\object.data"));
String str = (String) ois.readObject();
Person person = (Person) ois.readObject();
System.out.println(str);
System.out.println(person);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(oos != null){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(ois != null){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
public class Person implements Serializable {
public static final long serialVersionUID = 43535635626L;
}
(7)随机存取文件流
随机存取文件流:RandomAccessFile,并不是继承于四大基类,而是实现了DataOutput,DataInput接口。
![image-20211021102432532](https://gitee.com/oymn/img_upload/raw/master/img/20211021102432.png)
从实现的接口也可以看出,RandomAccessFile既可以作为输入流,也可以作为输出流。
创建RandomAccessFile实例时需要指定一个mode参数,该参数指定 RandomAccessFile 的访问模式:
- r: 以只读方式打开。
- rw:打开以便读取和写入。
- rwd:打开以便读取和写入;同步文件内容的更新。
- rws:打开以便读取和写入;同步文件内容和元数据的更新。
RandomAccessFile 对象中包含一个记录指针,用于标识当前读写位置,默认是0,我们可以自定义读写位置:
//将文件指针定位到pos位置
public void seek(long pos);
//获取文件指针当前的位置
public long getFilePointer();
示例:(这里是省略来写的)
RandomAccessFile raf1 = null;
RandomAccessFile raf2 = null;
try {
raf1 = new RandomAccessFile(new File("src\\test01.txt"),"rw");
raf2 = new RandomAccessFile(new File("src\\test02.txt"),"rw");
byte[] buff = new byte[5];
int len;
while( (len = raf1.read(buff)) != -1){
raf2.write(buff,0,len);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流操作(省略)
}