如何从JavaIO源码看装饰者模式的用法

这篇文章主要讲解了“如何从Java IO源码看装饰者模式的用法”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“如何从Java IO源码看装饰者模式的用法”吧!

创新互联公司是一家集网站建设,民丰企业网站建设,民丰品牌网站建设,网站定制,民丰网站建设报价,网络营销,网络优化,民丰网站推广为一体的创新建站企业,帮助传统企业提升企业形象加强企业竞争力。可充分满足这一群体相比中小企业更为丰富、高端、多元的互联网需求。同时我们时刻保持专业、时尚、前沿,时刻以成就客户成长自我,坚持不断学习、思考、沉淀、净化自己,让我们为更多的企业打造出实用型网站。

前言

设计模式(design pattern)是对软件设计中普遍存在(反复出现)的各种问题,所提出的解决方案。

我们在做业务开发的时候,要对业务进行抽象,抽象就是在理解了业务之后再给出一个定义,一个概念。所以概念是从实际场景反推出来的,而不是先有概念。设计模式既然是对问题的总结,自然符合先有场景再有概念。

要理解设计模式就要到具体的场景中去,事实证明,网上流传的诸多书籍或文章举出的例子,都很勉强。

众所周期,Java IO体系使用了装饰者模式,如果不理解这个模式,就很难熟练的使用io相关的API,不如直接到最经典的例子中去,直接看Java io源码设计,既能理解模式的使用方式,又能熟悉Java IO的用法和体系!

JavaIO源码示例

    public static void main(String[] args) throws IOException {
        File file = new File("file.txt");//文件内容为:abcdefgh
        InputStream in =
                new BufferedInputStream(
                        new FileInputStream(file), 512);
        int firstByte = in.read();
        System.out.println(firstByte);
        int secondByte = in.read();
        System.out.println(secondByte);
    }
   //输出结果:97,98

这段代码的意思是:使用FileInputStream从文件中读取数据,使用BufferedInputStream包装数据,并且分配一个512字节大小的缓冲区。使用 in.read()读取一个字节的数据。当读取第一个字节的时候,因为缓冲区没有内容,所以会调用FileInputStream从文件中读取512个字节放入内存缓冲区,当读取第二个字节的时候,因为缓存区已经有数据,所以直接返回。如果不使用BufferedInputStream,则每次都要到文件中读取一个字节,效率低,这就是使用BufferedInputStream做缓冲的意义。

这里为什么输出97?文件中使用utf-8编码,使用不定长的1-4个字节来表示一个字符,对于ascii里面的字符只使用一个字节,所以文件中第一个字节即字符a的utf-8的编码,也等于ascii编码,换算成十进制即97。

下面是BufferedInputStream的构造方法:

public BufferedInputStream(InputStream in, int size) {
    super(in);
    if (size <= 0) {
        throw new IllegalArgumentException("Buffer size <= 0");
    }
    buf = new byte[size];
}

下面是BufferedInputStream的read方法:这里判断pos与count的比较,count是缓冲区中的有效字节数,pos即当前读取位置,构造方法只是初始化一个字节数组,里面还没有有效数据,所以第一次读取时count为0,pos为0,这时候调用fill方法填充缓存区数据。

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

下面是BufferedInputStream的fill方法,删减一部分不必要的内容,这里的buffer就是初始化的缓冲区,其调用了getInIfOpen方法将数据写入缓冲区。

private void fill() throws IOException {
    byte[] buffer = getBufIfOpen();
    count = pos;
    int n = getInIfOpen().read(buffer, pos, buffer.length - pos);
    if (n > 0)
        count = n + pos;
}

getInIfOpen方法使用in这个对象,这个in即构造方法传入的FileInputStream,所以就相当于调用FileInputStream读取512字节的数据写入到BufferedInputStream的缓冲区。

private InputStream getInIfOpen() throws IOException {
    InputStream input = in;
    if (input == null)
        throw new IOException("Stream closed");
    return input;
}

可以看到BufferedInputStream起到的是一个提供缓冲区的增强功能。那么还有没有别的增强功能?

    public static void main(String[] args) throws IOException {
        File file = new File("file.txt");//文件内容:abcde
        DataInputStream in =
                new DataInputStream(new BufferedInputStream(
                        new FileInputStream(file), 512));
        char c = in.readChar();
        System.out.println(c);
    }//输出结果:慢

这里使用DataInputStream继续包装BufferedInputStreamDataInputStream从基础输入流中读取原始Java数据类型,比如这里使用readChar方法读取第一个字符,但是为什么会输出这个字?查看源码:

   public final char readChar() throws IOException {
        int ch2 = in.read(); //97
        int ch3 = in.read();//98
        if ((ch2 | ch3) < 0)
            throw new EOFException();
        return (char)((ch2 << 8) + (ch3 << 0));
    }

因为Java中的char类型使用2个字节来存储,所以这里读取连续的两个字节来当做一个字符。由于文件中存储的是字母,前面已经解释了,ch2的值应该是97,ch3的值是98,执行(ch2 << 8) + (ch3 << 0)的结果是十进制的24930,而24930在utf8中对应的就是汉字。这里只是解释原理,不涉及如何正确读取原始数据的问题。

在这个例子当中,DataInputStreamBufferedInputStream用于增强功能,也就是装饰者角色,而FileInputStream是数据源,是被装饰者,他们都有着共同的方法,并且装饰者要持有一个被装饰者或者其他装饰者的引用(这里通过构造方法注入),所以他们应当有共同的父类。这些装饰器可以自由组合,以实现不同的功能。所以,如果不这样做的话,还有更好的实现方式来达到同样的效果吗?完整的继承关系如下图:

如何从Java IO源码看装饰者模式的用法

抽象类图

如何从Java IO源码看装饰者模式的用法

  1. 首先有个被装饰对象

  2. 然后有一堆的装饰器用于增强不同的功能

  3. 装饰器与被装饰对象具有相同的行为(超类中的抽象方法)

  4. 装饰器应当持有被装饰者的引用或者其他装饰者(持有超类的引用,多态的体现)

  5. 组合这些装饰器和被装饰对象(构造方法注入)

感谢各位的阅读,以上就是“如何从Java IO源码看装饰者模式的用法”的内容了,经过本文的学习后,相信大家对如何从Java IO源码看装饰者模式的用法这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是创新互联,小编将为大家推送更多相关知识点的文章,欢迎关注!


分享名称:如何从JavaIO源码看装饰者模式的用法
文章分享:http://hbruida.cn/article/gehosd.html