Java学习16(IO设计模式)

这篇具有很好参考价值的文章主要介绍了Java学习16(IO设计模式)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、装饰器模式?

装饰器(Decorator)模式 可以在不改变原有对象的情况下拓展其功能。

装饰器模式通过组合替代继承扩展原始类的功能,在一些继承关系比较复杂的场景(IO 这一场景各种类的继承关系就比较复杂)更加实用。

对于字节流来说, FilterInputStream (对应输入流)和FilterOutputStream(对应输出流)是装饰器模式的核心,分别用于增强 InputStreamOutputStream子类对象的功能。

我们常见的BufferedInputStream(字节缓冲输入流)、DataInputStream 等等都是FilterInputStream 的子类,BufferedOutputStream(字节缓冲输出流)、DataOutputStream等等都是FilterOutputStream的子类。

举个例子,我们可以通过 BufferedInputStream(字节缓冲输入流)来增强 FileInputStream 的功能。

BufferedInputStream 构造函数如下:

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

可以看出,BufferedInputStream 的构造函数其中的一个参数就是 InputStream

BufferedInputStream 代码示例:

try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("input.txt"))) {
    int content;
    long skip = bis.skip(2);
    while ((content = bis.read()) != -1) {
        System.out.print((char) content);
    }
} catch (IOException e) {
    e.printStackTrace();
}

这个时候,你可以会想了:为啥我们直接不弄一个BufferedFileInputStream(字符缓冲文件输入流)呢?

BufferedFileInputStream bfis = new BufferedFileInputStream("input.txt");

如果 InputStream的子类比较少的话,这样做是没问题的。不过, InputStream的子类实在太多,继承关系也太复杂了。如果我们为每一个子类都定制一个对应的缓冲输入流,那岂不是太麻烦了。

如果你对 IO 流比较熟悉的话,你会发现ZipInputStreamZipOutputStream 还可以分别增强 BufferedInputStreamBufferedOutputStream 的能力。

BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName));
ZipInputStream zis = new ZipInputStream(bis);

BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName));
ZipOutputStream zipOut = new ZipOutputStream(bos);

ZipInputStreamZipOutputStream 分别继承自InflaterInputStreamDeflaterOutputStream

public
class InflaterInputStream extends FilterInputStream {
}

public
class DeflaterOutputStream extends FilterOutputStream {
}

这也是装饰器模式很重要的一个特征,那就是可以对原始类嵌套使用多个装饰器。

为了实现这一效果,装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口。上面介绍到的这些 IO 相关的装饰类和原始类共同的父类是 InputStreamOutputStream

对于字符流来说,BufferedReader 可以用来增加 Reader (字符输入流)子类的功能,BufferedWriter 可以用来增加 Writer (字符输出流)子类的功能。

BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8"));

IO 流中的装饰器模式应用的例子实在是太多了,不需要特意记忆,完全没必要哈!搞清了装饰器模式的核心之后,你在使用的时候自然就会知道哪些地方运用到了装饰器模式。

2、适配器模式

适配器(Adapter Pattern)模式 主要用于接口互不兼容的类的协调工作,你可以将其联想到我们日常经常使用的电源适配器。

适配器模式中存在被适配的对象或者类称为 适配者(Adapter) ,作用于适配者的对象或者类称为适配器(Adapter)

适配器分为对象适配器类适配器类适配器使用继承关系来实现对象适配器使用组合关系来实现

IO 流中的字符流和字节流的接口不同,它们之间可以协调工作就是基于适配器模式来做的,更准确点来说是对象适配器

通过适配器,我们可以将字节流对象适配成一个字符流对象这样我们可以直接通过字节流对象来读取或者写入字符数据

InputStreamReaderOutputStreamWriter 就是两个适配器(Adapter), 同时,它们两个也是字节流和字符流之间的桥梁。

InputStreamReader 使用 StreamDecoder (流解码器)对字节进行解码,实现字节流到字符流的转换OutputStreamWriter 使用StreamEncoder(流编码器)对字符进行编码,实现字符流到字节流的转换

InputStreamOutputStream 的子类是被适配者InputStreamReaderOutputStreamWriter是适配器。

// InputStreamReader 是适配器,FileInputStream 是被适配的类
InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
// BufferedReader 增强 InputStreamReader 的功能(装饰器模式)
BufferedReader bufferedReader = new BufferedReader(isr);

java.io.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) {
            throw new Error(e);
        }
    }
    // 使用 StreamDecoder 对象做具体的读取工作
	public int read() throws IOException {
        return sd.read();
    }
}

java.io.OutputStreamWriter 部分源码:

public class OutputStreamWriter extends Writer {
    // 用于编码的对象
    private final StreamEncoder se;
    public OutputStreamWriter(OutputStream out) {
        super(out);
        try {
           // 获取 StreamEncoder 对象
            se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
        } catch (UnsupportedEncodingException e) {
            throw new Error(e);
        }
    }
    // 使用 StreamEncoder 对象做具体的写入工作
    public void write(int c) throws IOException {
        se.write(c);
    }
}

3、适配器模式和装饰器模式有什么区别呢?

  • 装饰器模式侧重于动态地增强原始类的功能装饰器类需要跟原始类继承相同的抽象类或者实现相同的接口。并且,装饰器模式支持对原始类嵌套使用多个装饰器
  • 适配器模式侧重于让接口不兼容而不能交互的类可以一起工作,当我们调用适配器对应的方法时,适配器内部会调用适配者类或者和适配类相关的类的方法,这个过程透明的。

就比如说 StreamDecoder (流解码器)和StreamEncoder(流编码器)就是分别基于 InputStreamOutputStream 来获取 FileChannel对象并调用对应的 read 方法和 write 方法进行字节数据的读取和写入。

StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
    // 省略大部分代码
    // 根据 InputStream 对象获取 FileChannel 对象
    ch = getChannel((FileInputStream)in);
}

适配器和适配者两者不需要继承相同的抽象类或者实现相同的接口

另外,FutureTask 类使用了适配器模式Executors 的内部类 RunnableAdapter 实现属于适配器,用于将 Runnable 适配成 Callable

FutureTask参数包含 Runnable 的一个构造方法:

public FutureTask(Runnable runnable, V result) {
    // 调用 Executors 类的 callable 方法
    this.callable = Executors.callable(runnable, result);
    this.state = NEW;
}

Executors中对应的方法和适配器:

// 实际调用的是 Executors 的内部类 RunnableAdapter 的构造方法
public static <T> Callable<T> callable(Runnable task, T result) {
    if (task == null)
        throw new NullPointerException();
    return new RunnableAdapter<T>(task, result);
}
// 适配器
static final class RunnableAdapter<T> implements Callable<T> {
    final Runnable task;
    final T result;
    RunnableAdapter(Runnable task, T result) {
        this.task = task;
        this.result = result;
    }
    public T call() {
        task.run();
        return result;
    }
}

4、工厂模式

工厂模式用于创建对象,NIO 中大量用到了工厂模式,比如 Files 类的 newInputStream 方法用于创建 InputStream 对象(静态工厂)Paths 类的 get 方法创建 Path 对象(静态工厂)ZipFileSystem 类(sun.nio包下的类,属于 java.nio 相关的一些内部实现)的 getPath 的方法创建 Path 对象(简单工厂)。

InputStream is Files.newInputStream(Paths.get(generatorLogoPath))

5、观察者模式

NIO 中的文件目录监听服务使用到了观察者模式。

NIO 中的文件目录监听服务基于 WatchService 接口和 Watchable 接口。WatchService 属于观察者,Watchable 属于被观察者。

Watchable 接口定义了一个用于将对象注册到 WatchService(监控服务) 并绑定监听事件的方法 register 。

public interface Path
    extends Comparable<Path>, Iterable<Path>, Watchable{
}

public interface Watchable {
    WatchKey register(WatchService watcher,
                      WatchEvent.Kind<?>[] events,
                      WatchEvent.Modifier... modifiers)
        throws IOException;
}

WatchService 用于监听文件目录的变化,同一个 WatchService 对象能够监听多个文件目录。

// 创建 WatchService 对象
WatchService watchService = FileSystems.getDefault().newWatchService();

// 初始化一个被监控文件夹的 Path 类:
Path path = Paths.get("workingDirectory");
// 将这个 path 对象注册到 WatchService(监控服务) 中去
WatchKey watchKey = path.register(
watchService, StandardWatchEventKinds...);

Path 类 register 方法的第二个参数 events (需要监听的事件)为可变长参数,也就是说我们可以同时监听多种事件。

WatchKey register(WatchService watcher,
                  WatchEvent.Kind<?>... events)
    throws IOException;

常用的监听事件有 3 种

  • StandardWatchEventKinds.ENTRY_CREATE :文件创建。
  • StandardWatchEventKinds.ENTRY_DELETE : 文件删除。
  • StandardWatchEventKinds.ENTRY_MODIFY : 文件修改。

register 方法返回 WatchKey 对象,通过WatchKey 对象可以获取事件的具体信息比如文件目录下是创建、删除还是修改了文件、创建、删除或者修改的文件的具体名称是什么

WatchKey key;
while ((key = watchService.take()) != null) {
    for (WatchEvent<?> event : key.pollEvents()) {
      // 可以调用 WatchEvent 对象的方法做一些事情比如输出事件的具体上下文信息
    }
    key.reset();
}

WatchService 内部是通过一个 daemon thread(守护线程)采用定期轮询的方式来检测文件的变化,简化后的源码如下所示。文章来源地址https://www.toymoban.com/news/detail-429002.html

class PollingWatchService
    extends AbstractWatchService
{
    // 定义一个 daemon thread(守护线程)轮询检测文件变化
    private final ScheduledExecutorService scheduledExecutor;

    PollingWatchService() {
        scheduledExecutor = Executors
            .newSingleThreadScheduledExecutor(new ThreadFactory() {
                 @Override
                 public Thread newThread(Runnable r) {
                     Thread t = new Thread(r);
                     t.setDaemon(true);
                     return t;
                 }});
    }

  void enable(Set<? extends WatchEvent.Kind<?>> events, long period) {
    synchronized (this) {
      // 更新监听事件
      this.events = events;

        // 开启定期轮询
      Runnable thunk = new Runnable() { public void run() { poll(); }};
      this.poller = scheduledExecutor
        .scheduleAtFixedRate(thunk, period, period, TimeUnit.SECONDS);
    }
  }
}

到了这里,关于Java学习16(IO设计模式)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 设计模式——装饰器模式(Decorator Pattern)+ Spring相关源码

    别名:包装模式(Wrapper Pattern) 类型:结构型模式。 目的:指在不改变现有对象结构的情况下,动态地给该对象增加一些职责额外功能。 给对象添加新功能时,并不是在对象类中直接添加,而是在装饰器类中添加。 在装饰类中添加新功能,你可以增强原先对象的方法,也可

    2024年02月06日
    浏览(51)
  • 《golang设计模式》第二部分·结构型模式-04-装饰器模式(Decorator)

    装饰器(Decorator)通过包装(不是继承)的方式向目标对象中动态地添加或删除功能。 Component(抽象组件):定义了原始对象的接口,装饰器也会实现这个接口。 Concrete Component(具体组件):原始对象,以后装饰器会装饰它。 Decorator(抽象装饰器):关联/聚合了抽象组件,

    2024年02月09日
    浏览(49)
  • 【设计模式-05】Facade门面Mediator调停者 | Decorator装饰器 | Chain Of Responsibility责任链

    一般是系统内部相互交错,比如 消息中间件(MQ) 就是这种设计模式,对各个功能或系统之间进行解耦。 业务场景:论坛或者博客发表帖子或者评论,对敏感词过滤处理。使用责任链的模式进行过滤处理。 把变化的进行封装处理 核心代码实现 GitHub - jxaufang168/Design-Patterns: 设计

    2024年01月19日
    浏览(108)
  • 简化代码结构与提高灵活性:学习Java设计模式中的装饰器模式

    简化代码结构与提高灵活性:学习Java设计模式中的装饰器模式 在软件开发中,我们经常会遇到需要在不修改现有代码的情况下,对已有对象进行功能扩展或修改的需求。此时,装饰器模式就是一种非常有用的设计模式,它通过动态地将责任附加到对象上,来扩展对象的功能

    2024年02月16日
    浏览(45)
  • 【Java基础教程】(四十五)IO篇 · 中:转换流、内存流和打印流(探索装饰设计模式与PrintStream类的进阶),文件操作案例实践、字符编码问题~

    掌握内存操作流、转换流、打印流的使用; 掌握文件复制操作; 掌握字符的主要编码类型以及乱码问题产生的原因; 虽然字节流与字符流表示两种不同的数据流操作,但是这两种流彼此间是可以实现互相转换的,而要实现这样的转换可以通过 InputStreamReader 、 OutputStreamWrit

    2024年02月16日
    浏览(36)
  • Java设计模式-装饰模式

    装饰模式在Java领域是一种常见的设计模式,它能够在不改变对象原有结构的情况下,动态地为对象添加新的功能。它通过封装原有对象,在运行时动态地为对象添加新的行为或者修改原有行为,以扩展对象的功能。这种方式避免了继承的静态特性,让对象的行为可以根据需要

    2024年02月04日
    浏览(40)
  • Java 设计模式——装饰者模式

    (1)我们先来看一个快餐店的例子:快餐店有炒面、炒饭这些快餐,可以额外附加鸡蛋、火腿、培根这些配菜,当然加配菜需要额外加钱,每个配菜的价钱通常不太一样,那么计算总价就会显得比较麻烦。而使用继承的方式存在的问题: 扩展性不好 :如果要再加一种配料(

    2024年02月16日
    浏览(48)
  • Java设计模式之一:装饰器模式

    目录 一、什么是装饰器模式 二、装饰器模式如何使用 三、装饰器模式的优势和应用场景   装饰器模式是一种结构型设计模式,允许通过动态地将新功能添加到现有对象上,来扩展其行为或修改其外观,同时不改变其原始类的结构 。 在装饰器模式中,有一个抽象基类(Com

    2024年02月13日
    浏览(55)
  • 学习设计模式之装饰器模式,但是宝可梦

    作者在准备秋招中,学习设计模式,做点小笔记,用宝可梦为场景举例,有错误欢迎指出。 代码同步更新到 github ,要是点个Star您就是我的神 为了不改变组件的结构,动态地扩展其功能。 通常,扩展功能通过子类进行,但是继承的方式具有静态特征,耦合度高。 意图:动态

    2024年02月12日
    浏览(27)
  • Java 设计者模式以及与Spring关系(六) 装饰和模版方法模式

    本文是个系列一次会出两个设计者模式作用,如果有关联就三个,除此外还会讲解在spring中作用。 23设计者模式以及重点模式 我们都知道 设计者模式 有 3 类 23 种设计模式,标红是特别重要的设计者模式建议都会,而且熟读于心,标蓝是指其次重要建议也要明白。 (1)创建

    2024年01月24日
    浏览(40)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包