一, 前言
前面的文章主要讲了文件字节输入流 FileInputStream, 文件字节输出流 FileOutputStream 以及对应的字节缓冲流, 文件字符输入流 FileReader, 文件字符输出流 FileWriter 以及对应的字符缓冲流, 这些都是常见的流类. 当然, 除了这些流类, Java 还提供了别的流类供用户使用, 接下来看一下管道流和对象流.
二, 管道流
管道流主要用于连接两个线程的通信, 充当一个信息传递的管道. 管道流也分为字节流 (PipedInputStream,PipedOutputStream) 和字符流(PipedReader,PipedWriter). 比如一个管道输出流 PipedOutputStream 必须和一个管道输入流 PipedInputStream 进行连接而产生一个通信管道, 管道输出流 PipedOutputStream 向管道中写入数据, 管道输入流 PipedInputStream 从管道中读取数据. 管道流的工作如下图所示:
举例说明管道流的用法. 既然管道流的作用适用于线程之间的通信, 那么势必会有发送信息的线程和接收信息的线程, 先定义一个发送数据的线程, 通过管道输出流的 write()方法将数据写入到管道中.
- public class SendDataThread implements Runnable{
- private PipedOutputStream pipedOutputStream = new PipedOutputStream();
- public PipedOutputStream getPipedOutputStream() {
- return pipedOutputStream;
- }
- @Override
- public void run() {
- String sendData = "receiveDataThread:hello!!!";
- try {
- // 像管道输出流中写入数据(发送数据)
- pipedOutputStream.write(sendData.getBytes());
- pipedOutputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
在定义一个接收数据的线程, 通过管道输入流的 read()方法将数据从管道中读取过来
- public class ReceiveDataThread implements Runnable{
- private PipedInputStream pipedInputStream = new PipedInputStream();
- public PipedInputStream getPipedInputStream() {
- return pipedInputStream;
- }
- @Override
- public void run() {
- byte[] bytes = new byte[1024];
- try {
- // 将管道输出流中的内容读取到字节数组中
- int read = pipedInputStream.read(bytes);
- if(read != -1){
- System.out.println("发送过来的信息为:" + new String(bytes));
- }
- pipedInputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
发送数据的线程和接收数据的线程都定义好了, 接下来在利用管道输出流的 connect()方法将管道输出流和管道输入流连接起来, main 线程中测试
- public class ReceiveDataThread implements Runnable{
- private PipedInputStream pipedInputStream = new PipedInputStream();
- public PipedInputStream getPipedInputStream() {
- return pipedInputStream;
- }
- @Override
- public void run() {
- byte[] bytes = new byte[1024];
- try {
- // 将管道输出流中的内容读取到字节数组中
- int read = pipedInputStream.read(bytes);
- if(read != -1){
- System.out.println("发送过来的信息为:" + new String(bytes));
- }
- pipedInputStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
结果如下:
发送过来的信息为: receiveDataThread:hello!!!
注意一下, PipedInputStream 运用的是一个 1024 字节固定大小的字节数组当做循环缓冲区, 写入 PipedOutputStream 的数据实际上保存到了对应的 PipedInputStream 的内部缓冲区. PipedInputStream 执行读操作时, 读取的数据实际上来自这个内部缓冲区. 如果对应的 PipedInputStream 输入缓冲区已满, 任何企图写入 PipedOutputStream 的线程都将被阻塞. 而且这个写操作线程将一直阻塞, 直至出现读取 PipedInputStream 的操作从缓冲区删除数据.
这意味着, 向 PipedOutputStream 写入数据的线程不应该是负责从对应 PipedInputStream 读取数据的那个线程 (所以这里开了两个线程分别用于读写). 假定 t 线程试图一次对 PipedOutputStream 的 write() 方法的调用中向对应的 PipedOutputStream 写入 2000 字节的数据, 在 t 线程阻塞之前, 它最多能够写入 1024 字节的数据(PipedInputStream 内部缓冲区的大小). 然而, 一旦 t 被阻塞, 读取 PipedInputStream 的操作就再也不能出现了, 因为 t 是唯一读取 PipedInputStream 的线程, 这样, t 线程已经完全被阻塞.
三, 对象流
Java 中提供了 ObjectInputStream 和 ObjectOutputStream 这两个类来进行对象的序列化操作, 是完成对象的存储和读取的输入 / 输出流类(字节流), 只要把对象中的所有成员变量都存储起来, 就等于保存了这个对象, 之后从保存的对象之中再将对象读取进来就可以继续使用此对象. ObjectInputStream,ObjectOutputStream 可以帮助开发者完成保存和读取对象成员变量取值的过程, 但要求读写或存储的对象必须实现了 Serializable 接口.
举例: 定义一个 Person 类, 完成该对象的存储和读取
- public class Person implements Serializable{
- private static final long serialVersionUID = 8411755225402460289L;
- private String name;
- private transient int age;
- private final String country = "中国";
- public Person(String name, int age) {
- this.name = name;
- this.age = age;
- }
- @Override
- public String toString() {
- return "Person{" +
- "name='" + name + '\'' +
- ", age=" + age +
- ", country='" + country + '\'' +
- '}';
- }
- }
进行对象的存储和读取(序列化和反序列化)
- public class Test {
- public static void main(String[] args) throws IOException, ClassNotFoundException {
- File file = new File("D:" +File.separator + "person.txt");
- OutputStream outputStream = new FileOutputStream(file);
- // 创建对象输出流
- ObjectOutputStream objectOutputStream = new ObjectOutputStream(outputStream);
- Person person = new Person("zhangsan",15);
- // 将对象存储到文件中
- objectOutputStream.writeObject(person);
- objectOutputStream.close();
- // 创建对象输入流
- InputStream inputStream = new FileInputStream(file);
- ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
- // 读取文件中的对象信息
- Person person1 = (Person) objectInputStream.readObject();
- System.out.println(person1);
- objectInputStream.close();
- }
- }
结果: person.txt 文件的内容
看到乱码, 因为序列化之后本身就是按照一定的二进制格式组织的文件, 这些二进制格式不能被文本文件所识别, 所以乱码也是正常的.
控制台输出结果:
Person{name='zhangsan', age=0, country='中国'}
可以看到 age 为 0, 而不是 15, 这也证明了对象中被 transient 修饰的成员变量不会被序列化.
参考资料: Java IO7: 管道流, 对象流 https://www.cnblogs.com/xrq730/p/4893229.html
来源: http://www.bubuko.com/infodetail-2981672.html