Java中的I/O库是对各种流源、流汇以及处理过程的抽象化。Socket其实也可以说是I/O的一种,只是针对的读取、写入对象不是磁盘而已。
要理解JavaI/O的实现,需要知道它所使用的两个重要的设计模式。
正是由于存在这两种设计模式,才将I/O类的个数降低了很多(虽然现在也很多)
装饰者模式
InputStream、OutputStream、Reader和Writer等对基础流处理器起装饰作用,从而形成新的、功能改进的流处理器。简而言之就是在装饰I/O轮子。
装饰模式是透明的以动态的方式给一个对象附加上更多的责任,换言之客户并不会觉得对象在装饰前后有什么不同。仅仅是增加对象的功能!可以在不增加更多子类的情况下对对象进行扩展。
<br />//从数据源中读入字节数据
public class FileInputStream extends InputStream
{
//持有这个InputStream
private final FileDescriptor fd;
...
//native 方法中读取数据,FileDescriptor中持有
}
<br />//通过FilterInputStream持有FileInputStream的字节流,同时
//实现了FileInputStream实现的接口
public class FilterInputStream extends InputStream {
//持有读入的InputStream
protected volatile InputStream in;
protected FilterInputStream(InputStream in) {
this.in = in;
}
public int read() throws IOException {
return in.read();
}
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
public int read(byte b[], int off, int len) throws IOException {
return in.read(b, off, len);
}
...
//方法完全实现自InputStream
}
<br />//该装饰器的目的就是,将字节流、存入到内存当中字符数组、提高性能
//具体字节流,通过构造方法、保持在父类FilterInputStream的引用中
public class BufferedInputStream extends FilterInputStream {
private static int DEFAULT_BUFFER_SIZE = 8192;//默认的数组大小
private static int MAX_BUFFER_SIZE = Integer.MAX_VALUE - 8;
protected volatile byte buf[];//内存中的字符数组
private static final
AtomicReferenceFieldUpdater<BufferedInputStream, byte[]> bufUpdater =
AtomicReferenceFieldUpdater.newUpdater
(BufferedInputStream.class, byte[].class, "buf");
protected int count;
protected int pos;
protected int markpos = -1;
protected int marklimit;
private InputStream getInIfOpen() throws IOException {
InputStream input = in;
if (input == null)
throw new IOException("Stream closed");
return input;
}
private byte[] getBufIfOpen() throws IOException {
byte[] buffer = buf;
if (buffer == null)
throw new IOException("Stream closed");
return buffer;
}
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)) {
// Can't replace buf if there was an async close.
// Note: This would need to be changed if fill()
// is ever made accessible to multiple threads.
// But for now, the only way CAS can fail is via close.
// assert buf == null;
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;
}
private int read1(byte[] b, int off, int len) throws IOException {
...
}
public synchronized int read(byte b[], int off, int len)
throws IOException
{
...
}
...
}
BufferedInputStream装饰器起到的作用其实就是将字节流保存在字符数组中提高性能而已。
适配器模式
InputStream、OutputStream、Reader和Writer等基础流处理器对其他类型的流处理器的适配。简而言之就是适配I/O轮子。
把类的接口变成客户端能够接受的接口,从而避免不匹配的问题。
<br />//想要以字符的方式来处理
public abstract class Reader implements Readable, Closeable {
protected Object lock;
protected Reader() {
this.lock = this;
}
public int read(java.nio.CharBuffer target) throws IOException {
int len = target.remaining();
char[] cbuf = new char[len];
int n = read(cbuf, 0, len);
if (n > 0)
target.put(cbuf, 0, n);
return n;
}
public int read() throws IOException {
char cb[] = new char[1];
if (read(cb, 0, 1) == -1)
return -1;
else
return cb[0];
}
public int read(char cbuf[]) throws IOException {
return read(cbuf, 0, cbuf.length);
}
//具体的适配器来实现
abstract public int read(char cbuf[], int off, int len) throws IOException;
...
}
<br />public class StreamDecoder extends Reader {
//略过具体处理
//实现了Reader接口,间接持有引用
public static StreamDecoder forInputStreamReader(InputStream var0, Object var1, String var2) throws UnsupportedEncodingException {
String var3 = var2;
if(var2 == null) {
var3 = Charset.defaultCharset().name();
}
try {
if(Charset.isSupported(var3)) {
//返回该构件的引用
//该构件实现了Reader接口
return new StreamDecoder(var0, var1, Charset.forName(var3));
}
} catch (IllegalCharsetNameException var5) {
;
}
throw new UnsupportedEncodingException(var3);
}
}
<br />//适配InputStream的话,只需要将字节Stream传给InputStreamReader即可
//具体的InputStream就不介绍了、读取数据源不同而已、同时也包含原生的native方法
public class InputStreamReader extends Reader {
//将字节码转换为字符码,以适配Reader
private final StreamDecoder sd;
...
//构造方法,几个构造方法类似,举一个说明
public InputStreamReader(InputStream in, CharsetDecoder dec) {
//父类构造方法,让Reader类持有InputStream的引用
super(in);
if (dec == null)
throw new NullPointerException("charset decoder");
//适配作用,主要是把字节码转字符码
sd = StreamDecoder.forInputStreamReader(in, this, dec);
}
...
public int read() throws IOException {
return sd.read();
}
//适配器具体的实现,实现字节码和字符码的转换
public int read(char cbuf[], int offset, int length) throws IOException {
//从适配的构件引用里面,返回具体的字符
return sd.read(cbuf, offset, length);
}
}
两种模式的区别
适配器模式的用意是改变对象的接口而不一定改变对象的性能,将一个接口转变为另外一个接口,来达到重复使用的目的。(上面例:从InputStream->Reader)
装饰模式的用意是保持接口并增加对象的职责,保持原来的接口,在此之上增加新功能。(上面保持接口不变)
原文链接:源码中的设计模式之适配器和装饰者模式,转载请注明来源!
嗨大神!窝来看你了惹