IO 之IO流体系和IO中的设计模式(装饰器适配器)

流是一种抽象概念,它代表了数据的无结构化传递
按照流的方式进行输入输出,数据被当成无结构的字节序列或字符序列。从流中取得数据的操作称为提取操作,亦称操作;而向流中添加数据的操作称为插入操作,亦称操作。
用来进行输入输出操作的流就称为IO流。

流的分类

  1. 流的流向:输入输出流
  2. 流的数据单位:字符流(Writer、Reader)和字节流(InputStream、OutputStream)
  3. 流的功能:节点流、处理流。可以从/向一个特定的IO设备(如磁盘、网络)读/写数据的流,称为节点流,节点流也被成为低级流。:ByteArrayInputStream、FileInputStream
    处理流是对一个已存在的流进行连接或封装,通过封装后的流来实现数据读/写功能,处理流也被称为高级流。:BufferedInputStream、DataInputStream等
//节点流,直接传入的参数是IO设备
FileInputStream fis = new FileInputStream("test.txt");
//处理流,直接传入的参数是流对象
BufferedInputStream bis = new BufferedInputStream(fis);

实际上,**Java使用处理流来包装节点流是一种典型的装饰器设计模式,**通过使用处理流来包装不同的节点流,既可以消除不同节点流的实现差异,也可以提供更方便的方法来完成输入/输出功能。

java I/O库的设计原则

java语言的I/O库是对各种常见的流源、流汇以及处理过程的抽象化。客户端的java 程序不必知道最终的的流源、流汇是磁盘上的文件还是一个数组,或者是一个线程;也不比插手到诸如数据是否缓存、可否按照行号读取等处理的细节中去。要理解java I/O 这个庞大而复杂的库,关键是掌握两个对称性和两个设计模式。

IO流的框架

IO 之IO流体系和IO中的设计模式(装饰器适配器)_第1张图片
注意关闭输入输出流:在执行完流操作后,要调用close()方法来关系输入流,因为程序里打开的IO资源不属于内存资源,垃圾回收机制无法回收该资源,所以应该显式关闭文件IO资源。

使用Java的IO流执行输出时,不要忘记关闭输出流,关闭输出流除了可以保证流的物理资源被回收之外,还能将输出流缓冲区的数据flush到物理节点里(因为在执行close()方法之前,自动执行输出流的flush()方法)

1.InputStream

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) ; 
2.Reader

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

OutputStream 是所有的输出字节流的父类,它是一个抽象类,主要包含如下四个方法:

//向输出流中写入一个字节数据,该字节数据为参数b的低8位。 
void write(int b) ; 
//将一个字节类型的数组中的数据写入输出流。 
void write(byte[] b); 
//将一个字节类型的数组中的从指定位置(off)开始的,len个字节写入到输出流。 
void write(byte[] b, int off, int len); 
//将输出流中缓冲的数据全部写出到目的地。 
void flush();
Writer

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中的两个对称性

  1. 输入-输出对称:比如InputStream和OutputStream各自占据Byte流的输入和输出的两个平行的等级结构的根部;而Reader和Writer各自占据Char流的输入和输出的两个平行的等级结构的根部。
  2. byte-char对称:InputStream和Reader的子类分别负责byte和插入流的输入;OutputStream和Writer的子类分别负责byte和Char流的输出,它们分别形成平行的等级结构

IO中的适配器模式

我们其实都知道IO中有装饰器模式,我们先来介绍一下适配器模式。

适配器模式概念

所谓适配器模式就是将某个类的接口转换为客户端期望的另一个接口表示,其主要目的是兼容性,让原本不相干的两个类可以协同一起工作。

IO中使用适配器比较典型的就是字节流转字符流
我们知道InputStreamReader和OutPutStreamWriter类分别继承了Reader和Writer接口,但是他们的对象必须在构造函数中传入一个InputStream和OutputStream的实例。InputStreamReader和OutputStreamWriter的作用是将InputStream和OutputStream适配到Reader和Writer中
IO 之IO流体系和IO中的设计模式(装饰器适配器)_第2张图片
拿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);

IO中的装饰器模式

装饰器模式

装饰模式指的是在不必改变原类文件和使用继承的情况下,**动态的扩展一个对象的功能。**它是通过创建一个包装对象,也就是装饰来包裹真实对象。装饰模式又叫包装(Wrapper)模式

装饰器中的各个角色有:

  1. 抽象构件角色(component):给出一个抽象接口,以规范准备接收附加责任的对象。
  2. 具体构件角色(Concrete Component):定义一个将要接收附加责任的类。
  3. 装饰角色(Decorator):持有一个构件(Component)对象的实例,并实现一个与抽象构件接口一致的接口。
  4. 具体装饰角色(Concrete Decorator

IO中的装饰器模式

  1. InputStream抽象类就是抽象构件角色,定义了字节输入流的基本方法
  2. ByteArrayInputStream类是具体构件角色,继承了InputStream类,重写了相关方法:read().
  3. FilterInputStream类继承了InputStream类,是装饰角色。持有InputStream对象,重写了相关方法(read()),将方法委派给具体构件角色(ByteArrayInputStream)执行。
  4. BufferedInputStream类是具体装饰角色,继承装饰角色FilterInputSream类,添加了相关的属性,重写了read()方法,增强了原有read()方法功能,提高了读效率。
//抽象构件角色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

你可能感兴趣的:(Java)