装饰模式和Java IO

装饰模式

修饰模式(装饰模式),是面向对象编程领域中,一种动态地往一个类中添加新的行为的设计模式。就功能而言,修饰模式相比生成子类更为灵活,这样可以给某个对象而不是整个类添加一些功能。

装饰模式的UML如下所示:

装饰模式中有四个角色:

  • Component 抽象构件,最基本、最核心、最原始的接口或抽象类
  • ConcreteComponent 具体构件的引用
  • Decorator 装饰角色, 持有对构件的引用
  • ConcreteDecorator 具体装饰角色

Java IO中的装饰模式

Java IO流就是装饰模式的典型应用。

与装饰模式中角色对应的类如下:

  • Component:InputStreamOutputStream
  • ConcreteComponent: FileInputStreamPipeInputStreamByteArrayInputStream ...
  • Decorator:FilterInputStreamFilterOutputStream
  • ConcreteDecorator:DataInputStreamBufferedInputStreamLineNumberInputStream...

FilterInputStreamFilterOutputStream做的事情很简单,只是持有了一个Stream的引用并做了代理:

package java.io;

public
class FilterInputStream extends InputStream {

    protected volatile InputStream in;

    protected FilterInputStream(InputStream in) {
        this.in = in;
    }

    public int read() throws IOException {
        return in.read();
    }

    //...省略掉一些方法
}

BufferedInputStream

来看下BufferedInputStream的代码(当然只是一部分):

package java.io;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;

public
class BufferedInputStream extends FilterInputStream {
    private static int DEFAULT_BUFFER_SIZE = 8192;

    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];
    }

    private void fill() throws IOException {
        byte[] buffer = getBufIfOpen();
        if (markpos < 0)
            pos = 0;            /* no mark: throw away the buffer */
        else if (pos >= buffer.length)  /* no room left in buffer */
            if (markpos > 0) {  /* can throw away early part of the buffer */
                int sz = pos - markpos;
                System.arraycopy(buffer, markpos, buffer, 0, sz);
                pos = sz;
                markpos = 0;
            } else if (buffer.length >= marklimit) {
                markpos = -1;   /* buffer got too big, invalidate mark */
                pos = 0;        /* drop buffer contents */
            } else if (buffer.length >= MAX_BUFFER_SIZE) {
                throw new OutOfMemoryError("Required array size too large");
            } else {            /* grow buffer */
                int nsz = (pos <= MAX_BUFFER_SIZE - pos) ?
                        pos * 2 : MAX_BUFFER_SIZE;
                if (nsz > marklimit)
                    nsz = marklimit;
                byte nbuf[] = new byte[nsz];
                System.arraycopy(buffer, 0, nbuf, 0, pos);
                if (!bufUpdater.compareAndSet(this, buffer, nbuf)) {

                    throw new IOException("Stream closed");
                }
                buffer = nbuf;
            }
        count = pos;
        int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
        if (n > 0)
            count = n + pos;
    }

    public synchronized int read() throws IOException {
        if (pos >= count) {
            fill();
            if (pos >= count)
                return -1;
        }
        return getBufIfOpen()[pos++] & 0xff;
    }

    public synchronized int read(byte b[], int off, int len)
        throws IOException
    {
        getBufIfOpen(); // Check for closed stream
        if ((off | len | (off + len) | (b.length - (off + len))) < 0) {
            throw new IndexOutOfBoundsException();
        } else if (len == 0) {
            return 0;
        }

        int n = 0;
        for (;;) {
            int nread = read1(b, off + n, len - n);
            if (nread <= 0)
                return (n == 0) ? nread : n;
            n += nread;
            if (n >= len)
                return n;
            // if not closed but no bytes available, return
            InputStream input = in;
            if (input != null && input.available() <= 0)
                return n;
        }
    }
}
  • BufferedInputStream中有一个byte数组作为缓存,存放从制定的InputStream中读出的字节;
  • 它的read放回会先查看buf数组中是否还有可读的字节,如果没有就先调用一次fill()方法从指定的stream中读取字节到buf数组中(或者直接去stream中读取足够的字节,再调用fill()方法);
  • BufferedInputStream支持mark,fill()方法会在buf中保留markpos到pos的这个区间内(包括markpos,不包括pos)的字节,当然前提是markpos有效;
  • 当markpos为0,buf数组中没有空间,buf数组的长度小于等于pos并小于 marklimit和MAX_BUFFER_SIZE,buf将被一个长度为 marklimit、MAX_BUFFER_SIZE和 2 * p中较小值的数组代替(原数组中的字节会被拷贝)。

关于mark的问题

BufferedInputStreammark()方法是这样的:

    /**
     * See the general contract of the <code>mark</code>
     * method of <code>InputStream</code>.
     *
     * @param   readlimit   the maximum limit of bytes that can be read before
     *                      the mark position becomes invalid.
     * @see     java.io.BufferedInputStream#reset()
     */
    public synchronized void mark(int readlimit) {
        marklimit = readlimit;
        markpos = pos;
    }

按照doc的意思,markpos应该在读取的字节数超过了readlimit的时候就应该失效。
但是实际上,只有fill方法中的这一段代码让markpos失效了:

if (buffer.length >= marklimit) {
  markpos = -1;   /* buffer got too big, invalidate mark */
  pos = 0;        /* drop buffer contents */
}

也就是说,如果marklimit小于buf数组长度,markpos是不会失效的:

public static void main(String[] args) throws IOException {
    byte[] bytes = new byte[]{0, 1, 2, 3};
    ByteArrayInputStream in = new ByteArrayInputStream(bytes);
    BufferedInputStream bin = new BufferedInputStream(in);
    //如果制定了size为1,这段代码将会报错
    //BufferedInputStream bin = new BufferedInputStream(in, 1);
    bin.mark(1);
    bin.read();
    bin.read();
    bin.reset();
}

当然,之前也有提到,如果markpos为0, buf是有可能扩容的。

参考资料

JDK8源码
《设计模式之禅》第二版
修饰模式

原文地址:https://www.cnblogs.com/FJH1994/p/9131484.html

时间: 2024-10-30 16:49:09

装饰模式和Java IO的相关文章

《java与设计模式》之装饰模式详解&amp;Java IO中的装饰器模式

1 概述 在一个项目中,你会有非常多的因素考虑不到,特别是业务的变更,不时的冒出一个需求是很正常的情况.有三个继承关系的类:Father.Son.GrandSon,我们要在Son类上增强一些功能怎么办?给Son类增加方法吗?那对GrandSon的影响呢?特别是对GrandSon有多个的情况,你会怎么办?认真看完本文,你会找到你的答案. JavaIO中,像下面的嵌套语句是不是很常见,为什么要怎样定义呢?理解装饰模式后,你会找到答案. DataInputStream in = new DataInp

java IO之输入流——InputStream

java的基本输入流是java.io.InputStream,该抽象类定义了输入流的基本输入操作方法,实现自该抽象类的子类都有定义自己的数据源,例如ByteArrayInputStream的构造函数指定了ByteArrayInputStream输入流的数据源必须是一个字符数组.这就可以有多种不同的数据源,包括:字符数组.String对象.文件."管道".一个由其他种类的流组成的序列... 1 public ByteArrayInputStream(byte buf[]) {} 2 3

java io 学习

1,io 体系 在整个Java.io包中最重要的就是5个类和一个接口.5个类指的是File.OutputStream.InputStream.Writer.Reader:一个接口指的是Serializable.掌握了这些IO的核心操作那么对于Java中的IO体系也就有了一个初步的认识了. Java.io包中定义了多个流类型类实现输入输出的功能,从不同的角度可以分为: ~按照数据流方向的不同可以分为输入流和输出流. ~按照按照数据处理单位的不同可以分为字节流和字符流. 如下几张图可以直观的描述io

java io 流分类表

Java输入/输出流体系中常用的流分类(表内容来自java疯狂讲义) 注:下表中带下划线的是抽象类,不能创建对象.粗体部分是节点流,其他就是常用的处理流. 流分类 使用分类 字节输入流 字节输出流 字符输入流 字符输出流   抽象基类 InputStream OutputStream Reader Writer 节点流 访问文件 FileInputStream FileOutStream FileReader FileWriter 访问数值 ByteArrayInputStream ByteAr

Java IO 装饰者模式

装饰模式(Decorator) 装饰模式又名包装(Wrapper)模式. 装饰模式以对客户端透明的方式扩展对象的功能,是继承关系的一个替代方案. 装饰模式通过创建一个包装对象,也就是装饰,来包裹真实的对象. 装饰模式以对客户端透明的方式动态地给一个对象附加上更多的责任.换言之,客户端并不会觉得对象在装饰前和装饰后有什么不同. 装饰模式可以在不创造更多子类的情况下,将对象的功能加以扩展. 装饰模式把客户端的调用委派到被装饰类.装饰模式的关键在于这种扩展是完全透明的. 装饰模式的角色 抽象构件角色(

装饰器模式及JAVA IO流例子★★★☆☆

一.什么是装饰模式 通过关联机制给类增加行为,其行为的扩展由修饰对象来决定: 二.补充说明 与继承相似,不同点在于继承是在编译期间扩展父类,而装饰器模式在运行期间动态扩展原有对象: 或者说,继承是对类进行扩展,装饰模式是对对象进行扩展: 三.角色 抽象构件 具体构件 抽象装饰类 具体装饰类 说明:具体构件.抽象装饰类.具体装饰类的共同父类是抽象构件,具体装饰类继承抽象装饰类并在运行期间装饰具体构件: 四.例子 例子说明: 画家接口Painter,为抽象构件,有两个方法,获取画家描述信息及绘画:

Java IO深入浅出

1.What is stream? 流失一个很形象的概念,当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或者是网络连接.类似的,当程序需要写入数据的时候,就会开启通向目的地的流. 在Java中,IO的部分很庞大,基本包括两大部分:字节流和字符流.具体结构如下图(图源于网上): 2:什么是字节流? 以字节为单位传输的流.字节流的输入流祖先是:Inputstream(数据单位是byte (8bits))输出的祖先是OutputStream(数据单位是byte(8b

黑马程序员——【Java基础】——Java IO流

一.IO概述 1.IO:是Input.Output的缩写. 2.特点: (1)用于处理设备间的数据传输. (2)Java对数据的处理是通过“流”完成的. (3)Java用于操作流的对象都在IO包中. (4)流按操作分为两种:字节流和字符流. (5)流按流向分为:输入流和输出流(输入输出是相对于“内存”而言的). 3.IO流常用基类 (1)字节流abstract基类:InputStream.OutputStream: (2)字符流抽象基类:Reader.Writer: 二.字符流(Reader.W

Java IO包装流如何关闭?

http://www.cnblogs.com/qqzy168/p/3670915.html —————————————————————————————————————————————————————— 问题: (1)JAVA的IO流使用了装饰模式,关闭最外面的流的时候会自动调用被包装的流的close()方吗? (2)如果按顺序关闭流,是从内层流到外层流关闭还是从外层到内存关闭? 问题(1)解释: 如下例子代码: FileInputStream is = new FileInputStream("