修飾模式(裝飾模式),是面向對象編程領域中,一種動態地往一個類中添加新的行爲的設計模式。就功能而言,修飾模式相比生成子類更爲靈活,這樣能夠給某個對象而不是整個類添加一些功能。java
裝飾模式的UML以下所示:
編程
裝飾模式中有四個角色:設計模式
Java IO流就是裝飾模式的典型應用。數組
與裝飾模式中角色對應的類以下:緩存
InputStream
,OutputStream
FileInputStream
,PipeInputStream
,ByteArrayInputStream
...FilterInputStream
, FilterOutputStream
DataInputStream
,BufferedInputStream
,LineNumberInputStream
...FilterInputStream
和FilterOutputStream
作的事情很簡單,只是持有了一個Stream的引用並作了代理:ui
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
的代碼(固然只是一部分):this
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
中讀出的字節;fill()
方法從指定的stream中讀取字節到buf數組中(或者直接去stream中讀取足夠的字節,再調用fill()
方法);BufferedInputStream
支持mark,fill()
方法會在buf中保留markpos到pos的這個區間內(包括markpos,不包括pos)的字節,固然前提是markpos有效;BufferedInputStream
的mark()
方法是這樣的:atom
/** * 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源碼
《設計模式之禪》第二版
修飾模式