流是一种抽象概念,它代表了数据的无结构化传递。
按照流的方式进行输入输出,数据被当成无结构的字节序列或字符序列。从流中取得数据的操作称为提取操作,亦称读操作;而向流中添加数据的操作称为插入操作,亦称写操作。
用来进行输入输出操作的流就称为IO流。
//节点流,直接传入的参数是IO设备
FileInputStream fis = new FileInputStream("test.txt");
//处理流,直接传入的参数是流对象
BufferedInputStream bis = new BufferedInputStream(fis);
实际上,**Java使用处理流来包装节点流是一种典型的装饰器设计模式,**通过使用处理流来包装不同的节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入/输出功能。
java语言的I/O库是对各种常见的流源、流汇以及处理过程的抽象化。客户端的java 程序不必知道最终的的流源、流汇是磁盘上的文件还是一个数组,或者是一个线程;也不比插手到诸如数据是否缓存、可否按照行号读取等处理的细节中去。要理解java I/O 这个庞大而复杂的库,关键是掌握两个对称性和两个设计模式。
注意关闭输入输出流:在执行完流操作后,要调用close()方法来关系输入流,因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。
使用Java的IO流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据flush到物理节点里(因为在执行close()方法之前,自动执行输出流的flush()方法)
InputStream 是所有的输入字节流的父类,它是一个抽象类,主要包含三个方法:
//读取一个字节并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
int read() ;
//读取一系列字节并存储到一个数组buffer,返回实际读取的字节数,如果读取前已到输入流的末尾返回-1。
int read(byte[] buffer) ;
//读取length个字节并存储到一个字节数组buffer,从off位置开始存,最多len, 返回实际读取的字节数,如果读取前以到输入流的末尾返回-1。
int read(byte[] buffer, int off, int len) ;
Reader 是所有的输入字符流的父类,它是一个抽象类,主要包含三个方法:
//读取一个字符并以整数的形式返回(0~255),如果返回-1已到输入流的末尾。
int read() ;
//读取一系列字符并存储到一个数组buffer,返回实际读取的字符数,如果读取前已到输入流的末尾返回-1。
int read(char[] cbuf) ;
//读取length个字符,并存储到一个数组buffer,从off位置开始存,最多读取len,返回实际读取的字符数,如果读取前以到输入流的末尾返回-1。
int read(char[] cbuf, int off, int len)
除此之外:InputStream和Reader还支持如下方法来移动流中的指针位置:
//在此输入流中标记当前的位置
//readlimit - 在标记位置失效前可以读取字节的最大限制。
void mark(int readlimit)
// 测试此输入流是否支持 mark 方法
boolean markSupported()
// 跳过和丢弃此输入流中数据的 n 个字节/字符
long skip(long n)
//将此流重新定位到最后一次对此输入流调用 mark 方法时的位置
void reset()
OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含如下四个方法:
//向输出流中写入一个字节数据,该字节数据为参数b的低8位。
void write(int b) ;
//将一个字节类型的数组中的数据写入输出流。
void write(byte[] b);
//将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。
void write(byte[] b, int off, int len);
//将输出流中缓冲的数据全部写出到目的地。
void flush();
Writer 是所有的输出字符流的父类,它是一个抽象类,主要包含如下六个方法:
//向输出流中写入一个字符数据,该字节数据为参数b的低16位。
void write(int c);
//将一个字符类型的数组中的数据写入输出流,
void write(char[] cbuf)
//将一个字符类型的数组中的从指定位置(offset)开始的,length个字符写入到输出流。
void write(char[] cbuf, int offset, int length);
//将一个字符串中的字符写入到输出流。
void write(String string);
//将一个字符串从offset开始的length个字符写入到输出流。
void write(String string, int offset, int length);
//将输出流中缓冲的数据全部写出到目的地。
void flush()
我们其实都知道IO中有装饰器模式,我们先来介绍一下适配器模式。
所谓适配器模式就是将某个类的接口转换为客户端期望的另一个接口表示,其主要目的是兼容性,让原本不相干的两个类可以协同一起工作。
IO中使用适配器比较典型的就是字节流转字符流
我们知道InputStreamReader和OutPutStreamWriter类分别继承了Reader和Writer接口,但是他们的对象必须在构造函数中传入一个InputStream和OutputStream的实例。InputStreamReader和OutputStreamWriter的作用是将InputStream和OutputStream适配到Reader和Writer中。
拿InputStreamReader来说,适配角色就是InputStreamReader,被适配的角色是InputStream类代表的实例对象,目标接口就是Reader类。
InputStreamReader实现了Reader接口,并且持有了InputStream的引用。这里是通过StreamDecoder类间接持有的,因为从byte到char要经过编码。
/******************Reader类(目标类)******************/
package java.io;
public abstract class Reader implements Readable, Closeable {
abstract public int read(char cbuf[], int off, int len) throws IOException;
abstract public void close() throws IOException;
}
/******************InputStreamReader类(适配器类)******************/
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
//持有对被适配对象的引用
public InputStreamReader(InputStream in) {
super(in);
try {
//通过StreamDecoder类间接引用被适配的对象
sd = StreamDecoder.forInputStreamReader(in, this, (String)null);
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
public InputStreamReader(InputStream in, Charset cs) {
super(in);
if (cs == null)
throw new NullPointerException("charset");
sd = StreamDecoder.forInputStreamReader(in, this, cs);
}
public int read(char cbuf[], int offset, int length) throws IOException {
return sd.read(cbuf, offset, length);
}
public void close() throws IOException {
sd.close();
}
//…(省略的代码)
}
/******************InputStream类(被适配类)******************/
public abstract class InputStream implements Closeable {
//代码省略
}
使用Demo
File file = new File("hello.txt");
FileInputStream in = new FileInputStream(file);
// 将FileInputStream适配成InputStreamReader,即输入的字节流转换成字符流
InputStreamReader inReader = new InputStreamReader(in);
装饰模式指的是在不必改变原类文件和使用继承的情况下,**动态的扩展一个对象的功能。**它是通过创建一个包装对象,也就是装饰来包裹真实对象。装饰模式又叫包装(Wrapper)模式。
装饰器中的各个角色有:
//抽象构件角色InputStream抽象类中的read方法
public abstract int read() throws IOException;
//ByteArrayInputStream类中重写的read()方法
public synchronized int read() {
return (pos < count) ? (buf[pos++] & 0xff) : -1;
}
//装饰角色 FilterInputStream类,
//继承抽象构件角色 InputStream
class FilterInputStream extends InputStream {
//持有抽象构件对象实例
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
//重写read()方法
public int read() throws IOException {
//委派给具体构件角色ByteArrayInputStream执行
return in.read();
}
}
//具体装饰角色BufferedInputStream类 继承装饰角色,添加相关属性重写read方法,提高read的效率
class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
protected volatile byte buf[];
protected int count;
protected int pos;
protected int markpos = -1;
protected int marklimit;
public BufferedInputStream(InputStream in) {
this(in, DEFAULT_BUFFER_SIZE);
}
public BufferedInputStream(InputStream in, int size) {
super(in);
if (size <= 0) {
throw new IllegalArgumentException("Buffer size <= 0");
}
buf = new byte[size];
}
public synchronized int read() throws IOException {
if (pos >= count) {
fill();
if (pos >= count)
return -1;
}
return getBufIfOpen()[pos++] & 0xff;
}
}
适配模式是在适配器中,重写旧接口中的方法来调用新接口方法,来实现旧接口不改变,同时使用新接口的目的。新接口适配旧接口。
装饰模式是装饰器和旧接口实现相同的接口,在调用新接口的方法中,会调用旧接口的方法,并对其进行扩展。
资料:
https://www.jianshu.com/p/80f8a74d4662
https://blog.csdn.net/zt15732625878/article/details/82698777
https://blog.csdn.net/yy455363056/article/details/80105756