转载自https://github.com/Snailclimb/JavaGuide (添加小部分笔记)感谢作者!
装饰器模式 #
类图:
装饰器,Decorator,装饰器模式可以在不改变原有对象的情况下拓展其功能
★装饰器模式,通过组合替代继承来扩展原始类功能,在一些继承关系较复杂的场景(IO这一场景各种类的继承关系就比较复杂)下更加实用
对于字节流,FilterInputStream(对应输入流)和FilterOutputStream(对应输出流)是装饰器模式的核心,分别用于增强(继承了)InputStream和OutputStream子类对象的功能 Filter (过滤的意思),中间(Closeable)下面这两条虚线代表实现;最下面的实线代表继承
其中BufferedInputStream(字节缓冲输入流)、DataInputStream等等都是FilterInputStream的子类,对应的BufferedOutputStream和DataOutputStream都是FilterOutputStream的子类
例子,使用BufferedInputStream(字节缓冲输入流)来增强FileInputStream功能
BufferedInputStream源码(构造函数)
private static int DEFAULT_BUFFER_SIZE = 8192; 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]; }
使用
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(); }
ZipInputStream和ZipOutputStream还可以用来增强BufferedInputStream和BufferedOutputStream的能力
//使用 BufferedInputStream bis = new BufferedInputStream(new FileInputStream(fileName)); ZipInputStream zis = new ZipInputStream(bis); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(fileName)); ZipOutputStream zipOut = new ZipOutputStream(bos);
装饰器模式重要的一点,就是可以对原始类嵌套使用多个装饰器,所以装饰器需要跟原始类继承相同的抽象类或实现相同接口,上面介绍的IO相关装饰器和原始类共同父类都是InputStream和OutputStream 而对于字符流来说,BufferedReader用来增强Reader(字符输入流)子类功能,BufferWriter用来增加Writer(字符输出流)子类功能
BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName), "UTF-8"));
IO流中大量使用了装饰器模式,不需要特意记忆
适配器模式 #
适配器(Adapter Pattern)模式:主要用于接口互不兼容的类的协调工作,你可以将其联想到我们日常使用的电源适配器
- 其中被适配的对象/类称为适配者(Adaptee),作用于适配者的对象或者类称为适配器(Adapter)。对象适配器使用组合关系实现,类适配器使用继承关系实现
IO中字符流和字节流接口不同,而他们能协调工作就是基于适配器模式来做的,具体的,是对象适配器:将字节流对象适配成字符流对象,然后通过字节流对象,读取/写入字符数据
InputStreamReader和OutputStreamWriter为两个适配器,也是字节流和字符流之间的桥梁
InputStreamReader使用StreamDecode(流解码器)对字节进行解码,实现字节流到字符流的转换
OutputStreamWriter使用StreamEncoder(流编码器)对字符进行编码,实现字符到字节流的转换
InputStream和OutputStream的子类是被适配者,InputStreamReader和OutputStreamWriter是适配器 使用:
// InputStreamReader 是适配器,FileInputStream 是被适配的类 InputStreamReader isr = new InputStreamReader(new FileInputStream(fileName), "UTF-8"); // BufferedReader 增强 InputStreamReader 的功能(装饰器模式) BufferedReader bufferedReader = new BufferedReader(isr);
fileReader的源码:
public class FileReader extends InputStreamReader { public FileReader(String fileName) throws FileNotFoundException { super(new FileInputStream(fileName)); } } //其父类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); } }
适配器模式和装饰器模式区别
装饰器模式更侧重于动态增强原始类的功能,(为了嵌套)装饰器类需要跟原始类继承相同抽象类/或实现相同接口。装饰器模式支持对原始类嵌套
适配器模式侧重于让接口不兼容而不能交互的类一起工作,当调用适配器方法时,适配器内部会调用适配者类或者和适配者类相关类的方法,这个过程透明的。就比如说
StreamDecoder
(流解码器)和StreamEncoder
(流编码器)就是分别基于InputStream
和OutputStream
来获取FileChannel
对象并调用对应的read
方法和write
方法进行字节数据的读取和写入。StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) { // 省略大部分代码 // 根据 InputStream 对象获取 FileChannel 对象 ch = getChannel((FileInputStream)in); }
- 适配器和适配者(注意,这里说的都是适配器模式)两者不需要继承相同抽象类/不需要实现相同接口
FutureTask使用了适配器模式 直接调用(构造器)
public FutureTask(Runnable runnable, V result) { // 调用 Executors 类的 callable 方法 this.callable = Executors.callable(runnable, result); this.state = NEW; }
间接:
// 实际调用的是 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; } }
工厂模式 #
NIO中大量出现,例如Files类的newInputStream,Paths类中的get方法,ZipFileSystem类中的getPath
InputStream is = Files.newInputStream(Paths.get(generatorLogoPath))
观察者模式 #
比如NIO中的文件目录监听服务 该服务基于WatchService接口(观察者)和Watchable接口(被观察者)
Watchable接口其中有一个register方法,用于将对象注册到WatchService(监控服务)并绑定监听事件的方法
例子
// 创建 WatchService 对象 WatchService watchService = FileSystems.getDefault().newWatchService(); // 初始化一个被监控文件夹的 Path 类: Path path = Paths.get("workingDirectory"); // 将这个 path 对象注册到 WatchService(监控服务) 中去 WatchKey watchKey = path.register( watchService, StandardWatchEventKinds...);
可以通过WatchKey对象获取事件具体信息
WatchKey key; while ((key = watchService.take()) != null) { for (WatchEvent<?> event : key.pollEvents()) { // 可以调用 WatchEvent 对象的方法做一些事情比如输出事件的具体上下文信息 } key.reset(); }
完整的代码应该是如下
@Test public void myTest() throws IOException, InterruptedException { // 创建 WatchService 对象 WatchService watchService = FileSystems.getDefault().newWatchService(); // 初始化一个被监控文件夹的 Path 类: Path path = Paths.get("F:\\java_test\\git\\hexo\\review_demo\\src\\com\\hp"); // 将这个 path 对象注册到 WatchService(监控服务) 中去 WatchKey key = path.register( watchService, StandardWatchEventKinds.ENTRY_CREATE,StandardWatchEventKinds.ENTRY_DELETE ,StandardWatchEventKinds.ENTRY_MODIFY); while ((key = watchService.take()) != null) { System.out.println("检测到了事件--start--"); for (WatchEvent<?> event : key.pollEvents()) { // 可以调用 WatchEvent 对象的方法做一些事情比如输出事件的具体上下文信息 System.out.println("event.kind().name()"+event.kind().name()); } key.reset(); System.out.println("检测到了事件--end--"); } }
public interface Path
extends Comparable<Path>, Iterable<Path>, Watchable{
}
public interface Watchable {
WatchKey register(WatchService watcher,
WatchEvent.Kind<?>[] events,
WatchEvent.Modifier... modifiers)
throws IOException;
//events,需要监听的事件,包括创建、删除、修改。
@Override
WatchKey register(WatchService watcher,
WatchEvent.Kind<?>... events)
throws IOException;
}
其中events包括下面3种:
StandardWatchEventKinds.ENTRY_CREATE
:文件创建。StandardWatchEventKinds.ENTRY_DELETE
: 文件删除。StandardWatchEventKinds.ENTRY_MODIFY
: 文件修改。WatchService内部通过一个daemon thread (守护线程),采用定期轮询的方式检测文件变化
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); } } }