高级流
java将IO分为了两类
- 节点流:又称为"低级流"
- 特点:直接链接程序与另一端的"管道",是真实读写数据的流
- IO一定是建立在节点流的基础上进行的。
- 文件流就是典型的节点流(低级流)
- 处理流:又称为"高级流"
- 特点:不能独立存在,必须链接在其他流上
- 目的:当数据经过当前高级流时可以对数据进行某种加工操作,来简化我们的同等操作
- 实际开发中我们经常"串联"一组高级流最终到某个低级流上,使读写数据以流水线式的加工处理完成。这一操作也被称为使"流的链接"。流链接也是JAVA IO的精髓所在。
缓冲流
java.io.BufferedInputStream和BufferedOutputStream
功能
在流链接中的作用:加快读写效率
通常缓冲是最终链接在低级流上的流
构造器
-
缓冲字节输出流
BufferedOutputStream(OutputStream out) 实例化一个缓冲字节输出流并链接在指定的字节输出流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192) BufferedOutputStream(OutputStream out,int size) 实例化一个指定缓冲区大小的缓冲字节输出流并链接在指定的字节输出流上。
-
缓冲字节输入流
BufferedInputStream(InputStream in) 实例化一个缓冲字节输入流并链接在指定的字节输入流上。默认缓冲区大小8kb(内部维护的byte[] buf数组长度8192) BufferedInputStream(InputStream in,int size) 实例化一个指定缓冲区大小的缓冲字节输入流并链接在指定的字节输入流上。
例
package io;
import java.io.*;
/**
* 使用缓冲流完成文件复制操作
*/
public class CopyDemo3 {
public static void main(String[] args) throws IOException {
FileInputStream fis = new FileInputStream("image.png");
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream("image_cp3.png");
BufferedOutputStream bos = new BufferedOutputStream(fos);
int d;
long start = System.currentTimeMillis();
while((d=bis.read())!= -1){
bos.write(d);
}
long end = System.currentTimeMillis();
System.out.println("复制完毕!耗时:"+(end-start)+"ms");
bis.close();
bos.close();
}
}
原理
内部定义了一个属性byte buf[]。它等同于我们之前练习复制案例时的块写操作。
并且默认创建时,该buf数组的长度为8192(8kb)长度。
缓冲流在读写数据时总是以块读写数据(默认是8kb)来保证读写效率的
缓冲流提供了多种构造器,可以自行指定缓冲区大小。
写缓冲问题
由于缓冲输出流会将写出的数据装满内部缓冲区(默认8kb的字节数组)后才会进行一次真实的写出操作。当我们的数据不足时,如果想要及时写出数据,可以调用缓冲流的flush()方法,强制将缓冲区中已经缓存的数据写出一次。
package io;
import java.io.BufferedOutputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
/**
* 缓冲输出流的写缓冲问题
*/
public class BosFlushDemo {
public static void main(String[] args) throws IOException {
FileOutputStream fos = new FileOutputStream("bos.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
String line = "super idol的笑容都没你的甜~";
byte[] data = line.getBytes(StandardCharsets.UTF_8);
bos.write(data);
/*
void flush()
强制将缓冲流的缓冲取(内部维护的字节数组)中已经缓存的字节一次性写出
*/
// bos.flush();
System.out.println("写出完毕!");
bos.close();//缓冲输出流的close()方法内部会自动调用一次flush()方法确保数据写出
}
}
flush的传递
flush()方法是被定义在java.io.Flushable中。而字节输出流的超类java.io.OutputStream实现了
该接口,这意味着所有的字节输出流都有flush方法。而除了缓冲流之外的高级流的flush方法作用就是调用它链接的流的flush方法将该动作传递下去。最终传递给缓冲流来清空缓冲区。
对象流
java.io.ObjectInputStream和ObjectOutputStream
作用
- 对象输出流:将我们的java对象进行序列化
- 对象输入流:将java对象进行反序列化
序列化
将一个对象转换为一组可被传输或保存的字节。这组字节中除了包含对象本身的数据外,还会包含结构信息。
序列化的意义
实际开发中,我们通常会将对象
- 写入磁盘,进行长久保存
- 在网络间两台计算机中的java间进行传输
无论是保存在磁盘中还是传输,都需要将对象转换为字节后才可以进行。
对象输出流的序列化操作
void writeObject(Object obj)
将给定的对象转换为一组可保存或传输的字节然后通过其链接的流将字节写出
例:
package io;
import java.io.*;
/**
* 对象流
* 使用对象输出流完成对象序列化操作并最终保存到文件person.obj中
*/
public class OOSDemo {
public static void main(String[] args) throws IOException {
String name = "张三";
int age = 18;
String gender = "女";
String[] otherInfo = {"黑","壮实","java技术好","来自鄂尔多斯"};
Person p = new Person(name,age,gender,otherInfo);
FileOutputStream fos = new FileOutputStream("person.obj");
ObjectOutputStream oos = new ObjectOutputStream(fos);
/*
对象输出流提供的序列化对象方法:
void writeObject(Object obj)
将给定的对象转换为一组可保存或传输的字节然后通过其链接的流将字节写出
序列化对象时要求该对象对应的类必须实现接口:java.io.Serializable
如果写出的对象对应的类没有实现该接口,那么writeObject会抛出下面异常
java.io.NotSerializableException
*/
oos.writeObject(p);
System.out.println("对象写出完毕");
oos.close();
}
}
序列化要求
对象输出流要求写出的对象必须实现接口:java.io.Serializable
上述案例中我们看到,如果写出的对象Person没有实现java.io.Serializable时会抛出异常:
java.io.NotSerializableException