节点流和处理流
基本介绍
- 节点流可以从一个特定的数据源读写数据,如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();
}
}
}
}
值得注意的是,字节流可以操作二进制文件(字节),也可以操作字符文件(因为字节是基本单位)。但是字符流不能操作二进制文件。