
节点流和处理流
基本介绍
- 节点流可以从一个特定的数据源读写数据,如FileReader、FileWriter
 - 处理流(也叫包装流)是“连接” 在已经存在的流(节点流或处理流)之上,为程序提供更强大的读写功能,也更加灵活,如BufferedReader、BufferedWriter。
 
节点流和处理流一览表
分类  | 字节输入流  | 字节输出流  | 字符输入流  | 字符输出流  | 
抽象基类  | InputStream  | OutputStream  | Reader  | Writer  | 
访问文件  | FileInputStream  | FileOutputStream  | FileReader  | FileWriter  | 
访问数组  | ByteArrayInputStream  | ByteArrayOutputStream  | CharArrayReader  | CharArrayWriter  | 
访问管道  | PipedInputStream  | PipedOutputStream  | PipedReader  | PipedWriter  | 
访问字符串  | StringReader  | StringWriter  | ||
缓冲流  | BufferedInputStream  | BufferedOutputStream  | BufferedReader  | BufferedWriter  | 
转换流  | InputStreamReader  | OutputStreamWriter  | ||
对象流  | ObjectInputStream  | ObjectOutputStream  | ||
抽象基类  | FilterInputStream  | FilterOutputStream  | FilterReader  | Filter  | 
打印流  | PrintStream  | PrintWriter  | ||
推回输入流  | PushbackInputStream  | PushbackReader  | ||
特殊流  | DataInputStream  | DataOutputStream  | 
注:红色为字节流,紫色为处理流
节点流和处理流的区别和联系
- 节点流是底层流/低级流,直接跟数据源相接。
 - 处理流(包装类)包装节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入输出。
 - 处理流(也叫包装流)对节点流进行包装,使用了修饰器设计模式,不会直接与数据源相连
 
处理流的功能主要体现在一下两个方面
- 性能的提高:主要以增加缓冲的方式来提高输入输出的效率。
 - 操作的便捷:处理流可能提供了一系列便捷的方法来一次输入输出大批量的数据,使用更加灵活方便。
 
处理流-BufferedReader和BufferedWriter

在源码中,BufferedReader类中,有属性Reader,即可以封装一个节点流,该节点流可以是任意的,只要是Reader子类都可(FileReader、CharArrayReader、PipedReader等等,只要是Reader的子类就可以,但是一次只能放一个),这也说明包装流更加强大。通过传入的Reader(子类)来决定访问文件/数组/管道。
BufferedWriter原理同上:

代码模拟:
package com.study.srv.demo17;
/**
 * @author Wen先森
 * @version 1.0
 * @date 2022/6/29 10:37
 * 模拟流
 */
public class Demo09 {
    public static void main(String[] args) {
        BuffReader buffReader = new BuffReader(new FileReader());
        buffReader.reader();
        buffReader.fileReader(4);
        BuffReader buffReader1 = new BuffReader(new ArrayReader());
        buffReader1.fileReader(3);
        BuffReader buffReader2 = new BuffReader(new PipeReader());
        buffReader2.fileReader(2);
    }
}
abstract class AbstractReader {
    public void printf() {
    }
    //抽象方法可以用空方法体,也可以用abstract
    //public abstract void printf();
}
/**
 * 模拟节点流
 */
class FileReader extends AbstractReader {
    @Override
    public void printf() {
        System.out.println("对文件进行打印");
    }
}
/**
 * 模拟节点流
 */
class ArrayReader extends AbstractReader {
    @Override
    public void printf() {
        System.out.println("对数组进行打印");
    }
}
/**
 * 模拟节点流
 */
class PipeReader extends AbstractReader {
    @Override
    public void printf() {
        System.out.println("对管道进行打印");
    }
}
/**
 * 模拟包装流/处理流
 */
class BuffReader extends AbstractReader {
    /**
     * 属性是AbstractReader类型
     */
    private AbstractReader in;
    /**
     * 接受AbstractReader子类对象
     *
     */
    public BuffReader(AbstractReader in) {
        this.in = in;
    }
    /**
     * 封装一层方法
     */
    public void reader() {
        in.printf();
    }
    /**
     * 多次调用
     *
     */
    public void fileReader(int num) {
        for (int i = 0; i < num; i++) {
            in.printf();
        }
    }
}BufferedReader和BufferedWriter属于字符流,是按照字符来读取数据,关闭时处理流,只需要关闭外层流即可。
因为在使用包装流,就是通过外层的包装流最终调用了内层包装的节点流,所以在关闭处理流/包装流的时候,底层会自动关闭内层包装的节点流。
BufferedReader示例:
package com.study.srv.demo17;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
/**
 * @author Wen先森
 * @version 1.0
 * @date 2022/6/29 15:53
 */
public class Demo10 {
    public static void main(String[] args) throws IOException {
        //读取文件路径
        String src="E:\\c.txt";
        //创建包装流BufferedReader对象
        BufferedReader bufferedReader = new BufferedReader(new FileReader(src));
        String line;
        //bufferedReader.readLine())是按行读取,
        //通过下列字符之一即认为某行已终止:换行 ('\n')、回车 ('\r') 或回车后直接跟着换行。
        //包含该行内容的字符串,不包含任何行终止符,如果已到达流末尾,则返回 null
        while ((line=bufferedReader.readLine())!=null){
            System.out.println(line);
        }
        //关闭外层包装/处理流,就会自动关闭内部的节点流
        bufferedReader.close();
    }
}BufferedWriter示例:
package com.study.srv.demo17;
import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
/**
 * @author Wen先森
 * @version 1.0
 * @date 2022/6/29 16:23
 * 演示BufferedWriter使用
 */
public class Demo11 {
    public static void main(String[] args) throws IOException {
        //文件地址,有则直接写入,无则先创建文件再进行写入
        String src="E:\\test.txt";
        //创建一个处理流BufferedWriter对象,并且设置节点流默认拼接写入
        BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(src,true));
        bufferedWriter.write("这是一条测试");
        //换行,写入一个行分隔符。行分隔符字符串由系统属性 line.separator 定义,
        // 并且不一定是单个新行 ('\n') 符。 
        bufferedWriter.newLine();
        bufferedWriter.write("这是一条新的测试数据2");
        //同样写入完毕后,关闭包装流/处理流。
        bufferedWriter.close();
    }
}文件拷贝:
package com.study.srv.demo17;
import java.io.*;
import java.io.FileReader;
/**
 * @author Wen先森
 * @version 1.0
 * @date 2022/6/29 17:10
 * 利用包装流实现文件拷贝
 */
public class Demo12 {
    public static void main(String[] args) {
        //所选复制文件地址
        String src="E:\\c.txt";
        //所选复制后文件地址
        String path="E:\\copy.txt";
        BufferedReader bufferedReader=null;
        BufferedWriter bufferedWriter = null;
        String line;
        try {
            //创建处理流/包装流,BufferedReader和BufferedWriter对象
            //值得注意的是BufferedReader和BufferedWriter是字符处理流
            //不能去操作二进制文件,会造成文件损坏
            bufferedReader = new BufferedReader(new FileReader(src));
            bufferedWriter = new BufferedWriter(new FileWriter(path));
            //一行一行读取
            while ((line=bufferedReader.readLine())!=null){
                //一行一行写入
                bufferedWriter.write(line);
                //换行
                bufferedWriter.newLine();
            }
            System.out.println("文件拷贝完毕");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            //关闭包装流
            try {
                if (bufferedReader!=null) {
                    bufferedReader.close();
                }
                if (bufferedWriter!=null) {
                    bufferedWriter.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}处理流-BufferedInputStream 和 BufferedOutputStream
BufferedInputStream是字节流,在创建BufferInputStream时,会创建一个内部缓冲区数组。
package com.study.srv.demo17;
import java.io.*;
/**
 * @author Wen先森
 * @version 1.0
 * @date 2022/6/30 11:48
 * 使用BufferedInputStream和BufferedOutputStream拷贝文件
 * 完成二进制文件拷贝
 */
public class Demo13 {
    public static void main(String[] args) {
        //复制对象地址
        String pathA = "E:\\c.txt";
        //复制后地址
        String pathB = "E:\\x.txt";
        //创建BufferedInputStream和BufferedOutputStream对象
        BufferedInputStream bufferedInputStream = null;
        BufferedOutputStream bufferedOutputStream = null;
        byte[] bytes = new byte[1024];
        int length;
        try {
            //FileInputStream是InputStream子类
            bufferedInputStream = new BufferedInputStream(new FileInputStream(pathA));
            bufferedOutputStream = new BufferedOutputStream(new FileOutputStream(pathB));
            //循环读取文件,被写入到复制文件中去。
            //如果到达流末尾,则返回 -1。
            while ((length = bufferedInputStream.read(bytes)) != -1) {
                //将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此缓冲的输出流。
                bufferedOutputStream.write(bytes, 0, length);
            }
            System.out.println("拷贝完毕");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                if (bufferedInputStream != null) {
                    bufferedInputStream.close();
                }
                if (bufferedOutputStream != null) {
                    bufferedOutputStream.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}值得注意的是,字节流可以操作二进制文件(字节),也可以操作字符文件(因为字节是基本单位)。但是字符流不能操作二进制文件。










