Java IO流有字节流和字符流两种,其顶层抽象类分别为:
字节流:InputStream与OutputStream两个抽象类
字符流:Reader与Writer两个抽象类
以上的图都是来自https://blog.youkuaiyun.com/sinat_37064286/article/details/86537354博客。我只是想记录一下自己的学习情况,大家想了解IO流可以移步至以上博客。
下面是顶层抽象类的实现底层源代码:
InputStream
InputStream 抽象类底层代码:
public abstract class InputStream implements Closeable {
//用于设置可跳过的最大字节
private static final int MAX_SKIP_BUFFER_SIZE = 2048;
//用于读取输入流的一个字节,若未读到任何值,则返回-1
public abstract int read() throws IOException;
//将读取的字节存入到相应的数组
public int read(byte b[]) throws IOException {
return read(b, 0, b.length);
}
//读取字节至相应的数组具体实现
public int read(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
int c = read();
if (c == -1) {
return -1;
}
b[off] = (byte)c;
int i = 1;
try {
for (; i < len ; i++) {
c = read();
if (c == -1) {
break;
}
b[off + i] = (byte)c;
}
} catch (IOException ee) {
}
return i;
}
//跳跃n个字节的具体实现
public long skip(long n) throws IOException {
long remaining = n;
int nr;
if (n <= 0) {
return 0;
}
int size = (int)Math.min(MAX_SKIP_BUFFER_SIZE, remaining);
byte[] skipBuffer = new byte[size];
while (remaining > 0) {
nr = read(skipBuffer, 0, (int)Math.min(size, remaining));
if (nr < 0) {
break;
}
remaining -= nr;
}
return n - remaining;
}
//下面几个方法看具体的实现类
public int available() throws IOException {
return 0;
}
public void close() throws IOException {}
public synchronized void mark(int readlimit) {}
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
public boolean markSupported() {
return false;
}
}
FileInputStream实现类源码:
public class FileInputStream extends InputStream {
/**
*文件描述符类的实例充当基础机器特定结构的不透明句柄,
*这些特定于机器的结构表示打开的文件,打开的套接字或其他字节源或宿。
*文件描述符的主要实际用途是创建一个FileInputStream或FileOutputStream来包含它。
*/
private final FileDescriptor fd;
/**
* The path of the referenced file
* (null if the stream is created with a file descriptor)
*/
private final String path;
private FileChannel channel = null;
private final Object closeLock = new Object();
private volatile boolean closed = false;
public FileInputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null);
}
//构造函数,传入的是File对象
public FileInputStream(File file) throws FileNotFoundException {
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkRead(name);
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
fd = new FileDescriptor();
fd.attach(this);
path = name;
//用于打开文件,底层调用native方法open0()
open(name);
}
//通过FileDescriptor对象类创建一个FileInputStream对象
public FileInputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkRead(fdObj);
}
fd = fdObj;
path = null;
/*
* FileDescriptor is being shared by streams.
* Register this stream with FileDescriptor tracker.
*/
fd.attach(this);
}
//底层用于打开文件
private native void open0(String name) throws FileNotFoundException;
// wrap native call to allow instrumentation
/**
* Opens the specified file for reading.
* @param name the name of the file
*/
private void open(String name) throws FileNotFoundException {
open0(name);
}
//在某些输入可用之前,该方法将阻塞。
public int read() throws IOException {
return read0();
}
//底层用于读文件
private native int read0() throws IOException;
private native int readBytes(byte b[], int off, int len) throws IOException;
//在某些输入可用之前,该方法将阻塞。
public int read(byte b[]) throws IOException {
return readBytes(b, 0, b.length);
}
//在某些输入可用之前,该方法将阻塞。
public int read(byte b[], int off, int len) throws IOException {
return readBytes(b, off, len);
}
//skip方法也是调用native方法实现的
public long skip(long n) throws IOException {
return skip0(n);
}
private native long skip0(long n) throws IOException;
//返回可以从此输入流读取(或跳过)而不会被该输入流的方法的下一次调用阻塞的剩余字节数的估计值。
public int available() throws IOException {
return available0();
}
private native int available0() throws IOException;
//用于关闭该输入流,释放系统资源
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
if (channel != null) {
channel.close();
}
fd.closeAll(new Closeable() {
public void close() throws IOException {
close0();
}
});
}
//返回FileDescriptor对象,该对象表示与此FileInputStream使用的文件系统中实际文件的连接。
public final FileDescriptor getFD() throws IOException {
if (fd != null) {
return fd;
}
throw new IOException();
}
//返回与此文件输入流关联的唯一java.nio.channels.FileChannel FileChannel对象。
public FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, path, true, false, this);
}
return channel;
}
}
private static native void initIDs();
private native void close0() throws IOException;
static {
initIDs();
}
//确保在对象回收时,调用此文件输入流的close方法。
protected void finalize() throws IOException {
if ((fd != null) && (fd != FileDescriptor.in)) {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
}
可以看到对文件的具体操作都是通过底层native来实现的,而java只需要调用这些API来对文件进行相应的操作。
FileOutputStream实现源码很多操作都和FileInputStream相似,下面来具体看看FileOutputStream的源码
OutputStream
OutputStream抽象类底层代码:
public abstract class OutputStream implements Closeable, Flushable {
//写入某个具体字节,字节为8位,因此该int数据的高24位自动忽略,只写入低8位
public abstract void write(int b) throws IOException;
//写入一个字节数组
public void write(byte b[]) throws IOException {
write(b, 0, b.length);
}
public void write(byte b[], int off, int len) throws IOException {
if (b == null) {
throw new NullPointerException();
} else if ((off < 0) || (off > b.length) || (len < 0) ||
((off + len) > b.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
for (int i = 0 ; i < len ; i++) {
write(b[off + i]);
}
}
//刷新此输出流并强制写出所有缓冲的输出字节。
public void flush() throws IOException {
}
public void close() throws IOException {
}
}
FileOutputStream实现类源码:
public class FileOutputStream extends OutputStream {
/**
* The system dependent file descriptor.
*/
private final FileDescriptor fd;
/**
* True if the file is opened for append.
*/
private final boolean append;
/**
* The associated channel, initialized lazily.
*/
private FileChannel channel;
/**
* The path of the referenced file
* (null if the stream is created with a file descriptor)
*/
private final String path;
private final Object closeLock = new Object();
private volatile boolean closed = false;
public FileOutputStream(String name) throws FileNotFoundException {
this(name != null ? new File(name) : null, false);
}
//参数用于控制写入操作的行为,如果append为true的话,写入文件是在具体文件后面添加,而为false的话,则会覆盖原文件
public FileOutputStream(String name, boolean append)
throws FileNotFoundException
{
this(name != null ? new File(name) : null, append);
}
public FileOutputStream(File file) throws FileNotFoundException {
this(file, false);
}
//以上所有的构造方法最终都是调用该构造方法
public FileOutputStream(File file, boolean append)
throws FileNotFoundException
{
String name = (file != null ? file.getPath() : null);
SecurityManager security = System.getSecurityManager();
if (security != null) {
security.checkWrite(name);
}
if (name == null) {
throw new NullPointerException();
}
if (file.isInvalid()) {
throw new FileNotFoundException("Invalid file path");
}
this.fd = new FileDescriptor();
fd.attach(this);
this.append = append;
this.path = name;
open(name, append);
}
public FileOutputStream(FileDescriptor fdObj) {
SecurityManager security = System.getSecurityManager();
if (fdObj == null) {
throw new NullPointerException();
}
if (security != null) {
security.checkWrite(fdObj);
}
this.fd = fdObj;
this.append = false;
this.path = null;
fd.attach(this);
}
private native void open0(String name, boolean append)
throws FileNotFoundException;
private void open(String name, boolean append)
throws FileNotFoundException {
open0(name, append);
}
private native void write(int b, boolean append) throws IOException;
public void write(int b) throws IOException {
write(b, append);
}
private native void writeBytes(byte b[], int off, int len, boolean append)
throws IOException;
public void write(byte b[]) throws IOException {
writeBytes(b, 0, b.length, append);
}
public void write(byte b[], int off, int len) throws IOException {
writeBytes(b, off, len, append);
}
public void close() throws IOException {
synchronized (closeLock) {
if (closed) {
return;
}
closed = true;
}
if (channel != null) {
channel.close();
}
fd.closeAll(new Closeable() {
public void close() throws IOException {
close0();
}
});
}
public final FileDescriptor getFD() throws IOException {
if (fd != null) {
return fd;
}
throw new IOException();
}
public FileChannel getChannel() {
synchronized (this) {
if (channel == null) {
channel = FileChannelImpl.open(fd, path, false, true, append, this);
}
return channel;
}
}
protected void finalize() throws IOException {
if (fd != null) {
if (fd == FileDescriptor.out || fd == FileDescriptor.err) {
flush();
} else {
/* if fd is shared, the references in FileDescriptor
* will ensure that finalizer is only called when
* safe to do so. All references using the fd have
* become unreachable. We can call close()
*/
close();
}
}
}
private native void close0() throws IOException;
private static native void initIDs();
static {
initIDs();
}
}
可以看到FileOutputStream的具体实现与FileInputStream的实现非常相似。
下面再来看看字符流Reader与Writer。
Reader
Reader字符流抽象类源码:
public abstract class Reader implements Readable, Closeable {
protected Object lock;
protected Reader() {
this.lock = this;
}
protected Reader(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
//将字符读入CharBuffer
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;
/** Maximum skip-buffer size */
private static final int maxSkipBufferSize = 8192;
/** Skip buffer, null until allocated */
private char skipBuffer[] = null;
/**
* Skips characters. This method will block until some characters are
* available, an I/O error occurs, or the end of the stream is reached.
*
* @param n The number of characters to skip
*
* @return The number of characters actually skipped
*
* @exception IllegalArgumentException If <code>n</code> is negative.
* @exception IOException If an I/O error occurs
*/
public long skip(long n) throws IOException {
if (n < 0L)
throw new IllegalArgumentException("skip value is negative");
int nn = (int) Math.min(n, maxSkipBufferSize);
synchronized (lock) {
if ((skipBuffer == null) || (skipBuffer.length < nn))
skipBuffer = new char[nn];
long r = n;
while (r > 0) {
int nc = read(skipBuffer, 0, (int)Math.min(r, nn));
if (nc == -1)
break;
r -= nc;
}
return n - r;
}
}
//下面看具体实现类
public boolean ready() throws IOException {
return false;
}
public boolean markSupported() {
return false;
}
public void mark(int readAheadLimit) throws IOException {
throw new IOException("mark() not supported");
}
public void reset() throws IOException {
throw new IOException("reset() not supported");
}
abstract public void close() throws IOException;
}
FileReader实现源码:
public class FileReader extends InputStreamReader {
public FileReader(String fileName) throws FileNotFoundException {
super(new FileInputStream(fileName));
}
public FileReader(File file) throws FileNotFoundException {
super(new FileInputStream(file));
}
public FileReader(FileDescriptor fd) {
super(new FileInputStream(fd));
}
}
FileReader源码中只有三个构造方法,并且三个构造方法都是调用的其父类InputStreamReader 的构造方法,并且传入的参数正好是字节流的FileInputStream对象,通过对象InputStreamReader 将字节流转换为字符流。
InputStreamReader 的源码如下:
public class InputStreamReader extends Reader {
private final StreamDecoder sd;
//由于字符流,就必须要考虑编码问题,该构造函数采用默认的编码,得到StreamDecoder对象,实际操作就是使用StreamDecoder进行字符读
public InputStreamReader(InputStream in) {
super(in);
try {
sd = StreamDecoder.forInputStreamReader(in, this, (String)null); // ## check lock object
} catch (UnsupportedEncodingException e) {
// The default encoding should always be available
throw new Error(e);
}
}
//该构造方法指定了编码
public InputStreamReader(InputStream in, String charsetName)
throws UnsupportedEncodingException
{
super(in);
if (charsetName == null)
throw new NullPointerException("charsetName");
sd = StreamDecoder.forInputStreamReader(in, this, charsetName);
}
//该构造方法也指定了具体的编码,但是编码是由Charset对象给出
public InputStreamReader(InputStream in, Charset cs) {
super(in);
if (cs == null)
throw new NullPointerException("charset");
sd = StreamDecoder.forInputStreamReader(in, this, cs);
}
public InputStreamReader(InputStream in, CharsetDecoder dec) {
super(in);
if (dec == null)
throw new NullPointerException("charset decoder");
sd = StreamDecoder.forInputStreamReader(in, this, dec);
}
//获取字符编码
public String getEncoding() {
return sd.getEncoding();
}
//读操作
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);
}
//告诉此流是否已准备好被读取。 如果InputStreamReader的输入缓冲区不为空,或者可以从基础字节流中读取字节,则它准备就绪。
public boolean ready() throws IOException {
return sd.ready();
}
public void close() throws IOException {
sd.close();
}
}
通过StreamDecoder.forInputStreamReader()方法,实现字节流与字符流的转换,然后返回一个StreamDecoder对象,对字符流的读操作都是通过StreamDecoder进行操作的。下面来看StreamDecoder类的具体实现。
public class StreamDecoder extends Reader {
//定义最小以及默认的ByteBuffer的大小
private static final int MIN_BYTE_BUFFER_SIZE = 32;
private static final int DEFAULT_BYTE_BUFFER_SIZE = 8192;
// 确保流是被打开状态否则不能输入输出
private volatile boolean isOpen = true;
private void ensureOpen() throws IOException {
if (!isOpen)
throw new IOException("Stream closed");
}
//read方法要求返回一个字符,但是实际是读取2个字符,剩余1个字符会被赋予给leftoverChar变量,而haveLeftoverChar用于判断
private boolean haveLeftoverChar = false;
private char leftoverChar;
// 以下的forInputStreamReader方法最后都是调用返回StreamDecoder 的构造方法
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
String charsetName)
throws UnsupportedEncodingException
{
String csn = charsetName;
if (csn == null)
csn = Charset.defaultCharset().name();
try {
if (Charset.isSupported(csn))
return new StreamDecoder(in, lock, Charset.forName(csn));
} catch (IllegalCharsetNameException x) { }
throw new UnsupportedEncodingException (csn);
}
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
Charset cs)
{
return new StreamDecoder(in, lock, cs);
}
public static StreamDecoder forInputStreamReader(InputStream in,
Object lock,
CharsetDecoder dec)
{
return new StreamDecoder(in, lock, dec);
}
// Factory for java.nio.channels.Channels.newReader
public static StreamDecoder forDecoder(ReadableByteChannel ch,
CharsetDecoder dec,
int minBufferCap)
{
return new StreamDecoder(ch, dec, minBufferCap);
}
//返回字符编码
public String getEncoding() {
if (isOpen())
return encodingName();
return null;
}
public int read() throws IOException {
return read0();
}
//用于读取一个字符,实际读取了两个字符
@SuppressWarnings("fallthrough")
private int read0() throws IOException {
synchronized (lock) {
// Return the leftover char, if there is one
if (haveLeftoverChar) {
haveLeftoverChar = false;
return leftoverChar;
}
// Convert more bytes
char cb[] = new char[2];
int n = read(cb, 0, 2);
switch (n) {
case -1:
return -1;
case 2:
leftoverChar = cb[1];
haveLeftoverChar = true;
// FALL THROUGH
case 1:
return cb[0];
default:
assert false : n;
return -1;
}
}
}
//读取长度为length的字符至cbuf,其会调用implRead方法
public int read(char cbuf[], int offset, int length) throws IOException {
int off = offset;
int len = length;
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
}
if (len == 0)
return 0;
int n = 0;
if (haveLeftoverChar) {
// Copy the leftover char into the buffer
cbuf[off] = leftoverChar;
off++; len--;
haveLeftoverChar = false;
n = 1;
if ((len == 0) || !implReady())
// Return now if this is all we can produce w/o blocking
return n;
}
if (len == 1) {
// Treat single-character array reads just like read()
int c = read0();
if (c == -1)
return (n == 0) ? -1 : n;
cbuf[off] = (char)c;
return n + 1;
}
return n + implRead(cbuf, off, off + len);
}
}
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return haveLeftoverChar || implReady();
}
}
public void close() throws IOException {
synchronized (lock) {
if (!isOpen)
return;
implClose();
isOpen = false;
}
}
private boolean isOpen() {
return isOpen;
}
// -- Charset-based stream decoder impl --
// In the early stages of the build we haven't yet built the NIO native
// code, so guard against that by catching the first UnsatisfiedLinkError
// and setting this flag so that later attempts fail quickly.
//
private static volatile boolean channelsAvailable = true;
private static FileChannel getChannel(FileInputStream in) {
if (!channelsAvailable)
return null;
try {
return in.getChannel();
} catch (UnsatisfiedLinkError x) {
channelsAvailable = false;
return null;
}
}
private Charset cs;
private CharsetDecoder decoder;
private ByteBuffer bb;
// Exactly one of these is non-null
private InputStream in;
private ReadableByteChannel ch;
StreamDecoder(InputStream in, Object lock, Charset cs) {
this(in, lock,
cs.newDecoder()
.onMalformedInput(CodingErrorAction.REPLACE)
.onUnmappableCharacter(CodingErrorAction.REPLACE));
}
StreamDecoder(InputStream in, Object lock, CharsetDecoder dec) {
super(lock);
this.cs = dec.charset();
this.decoder = dec;
// This path disabled until direct buffers are faster
if (false && in instanceof FileInputStream) {
ch = getChannel((FileInputStream)in);
if (ch != null)
bb = ByteBuffer.allocateDirect(DEFAULT_BYTE_BUFFER_SIZE);
}
if (ch == null) {
this.in = in;
this.ch = null;
bb = ByteBuffer.allocate(DEFAULT_BYTE_BUFFER_SIZE);
}
bb.flip(); // So that bb is initially empty
}
StreamDecoder(ReadableByteChannel ch, CharsetDecoder dec, int mbc) {
this.in = null;
this.ch = ch;
this.decoder = dec;
this.cs = dec.charset();
this.bb = ByteBuffer.allocate(mbc < 0
? DEFAULT_BYTE_BUFFER_SIZE
: (mbc < MIN_BYTE_BUFFER_SIZE
? MIN_BYTE_BUFFER_SIZE
: mbc));
bb.flip();
}
private int readBytes() throws IOException {
bb.compact();
try {
if (ch != null) {
// Read from the channel
int n = ch.read(bb);
if (n < 0)
return n;
} else {
// Read from the input stream, and then update the buffer
int lim = bb.limit();
int pos = bb.position();
assert (pos <= lim);
int rem = (pos <= lim ? lim - pos : 0);
assert rem > 0;
int n = in.read(bb.array(), bb.arrayOffset() + pos, rem);
if (n < 0)
return n;
if (n == 0)
throw new IOException("Underlying input stream returned zero bytes");
assert (n <= rem) : "n = " + n + ", rem = " + rem;
bb.position(pos + n);
}
} finally {
// Flip even when an IOException is thrown,
// otherwise the stream will stutter
bb.flip();
}
int rem = bb.remaining();
assert (rem != 0) : rem;
return rem;
}
//该方法实现将字节缓冲内的字节解码成字符,放入字符缓冲
int implRead(char[] cbuf, int off, int end) throws IOException {
// In order to handle surrogate pairs, this method requires that
// the invoker attempt to read at least two characters. Saving the
// extra character, if any, at a higher level is easier than trying
// to deal with it here.
assert (end - off > 1);
CharBuffer cb = CharBuffer.wrap(cbuf, off, end - off);
if (cb.position() != 0)
// Ensure that cb[0] == cbuf[off]
cb = cb.slice();
boolean eof = false;
for (;;) {
//具体解码
CoderResult cr = decoder.decode(bb, cb, eof);
// 代表字节缓冲区内容已全部解码放入到字符缓冲区中
if (cr.isUnderflow()) {
if (eof)
break;
if (!cb.hasRemaining())
break;
if ((cb.position() > 0) && !inReady())
break; // Block at most once
//读取字节到字节缓冲区bb中
int n = readBytes();
if (n < 0) {
eof = true;
if ((cb.position() == 0) && (!bb.hasRemaining()))
break;
decoder.reset();
}
continue;
}
//代表着字符缓冲区已满,应该使用未满的字符缓冲区在次调用该方法
if (cr.isOverflow()) {
assert cb.position() > 0;
break;
}
cr.throwException();
}
if (eof) {
// ## Need to flush decoder
decoder.reset();
}
if (cb.position() == 0) {
if (eof)
return -1;
assert false;
}
return cb.position();
}
String encodingName() {
return ((cs instanceof HistoricallyNamedCharset)
? ((HistoricallyNamedCharset)cs).historicalName()
: cs.name());
}
private boolean inReady() {
try {
return (((in != null) && (in.available() > 0))
|| (ch instanceof FileChannel)); // ## RBC.available()?
} catch (IOException x) {
return false;
}
}
boolean implReady() {
return bb.hasRemaining() || inReady();
}
void implClose() throws IOException {
if (ch != null)
ch.close();
else
in.close();
}
}
Writer
Writer字符流抽象类源码:
public abstract class Writer implements Appendable, Closeable, Flushable {
/**
* Temporary buffer used to hold writes of strings and single characters
*/
private char[] writeBuffer;
/**
* Size of writeBuffer, must be >= 1
*/
private static final int WRITE_BUFFER_SIZE = 1024;
protected Object lock;
protected Writer() {
this.lock = this;
}
protected Writer(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
//具体写操作,写的时候上锁
public void write(int c) throws IOException {
synchronized (lock) {
if (writeBuffer == null){
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
writeBuffer[0] = (char) c;
write(writeBuffer, 0, 1);
}
}
public void write(char cbuf[]) throws IOException {
write(cbuf, 0, cbuf.length);
}
abstract public void write(char cbuf[], int off, int len) throws IOException;
public void write(String str) throws IOException {
write(str, 0, str.length());
}
//直接写入一个字符串
public void write(String str, int off, int len) throws IOException {
synchronized (lock) {
char cbuf[];
if (len <= WRITE_BUFFER_SIZE) {
if (writeBuffer == null) {
writeBuffer = new char[WRITE_BUFFER_SIZE];
}
cbuf = writeBuffer;
} else { // Don't permanently allocate very large buffers.
cbuf = new char[len];
}
str.getChars(off, (off + len), cbuf, 0);
write(cbuf, 0, len);
}
}
//将指定的字符序列附加到Writer
public Writer append(CharSequence csq) throws IOException {
if (csq == null)
write("null");
else
write(csq.toString());
return this;
}
//将指定字符序列的子序列追加到此writer
public Writer append(CharSequence csq, int start, int end) throws IOException {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
public Writer append(char c) throws IOException {
write(c);
return this;
}
abstract public void flush() throws IOException;
abstract public void close() throws IOException;
}
FileWriter源码如下:
public class FileWriter extends OutputStreamWriter {
public FileWriter(String fileName) throws IOException {
super(new FileOutputStream(fileName));
}
public FileWriter(String fileName, boolean append) throws IOException {
super(new FileOutputStream(fileName, append));
}
public FileWriter(File file) throws IOException {
super(new FileOutputStream(file));
}
public FileWriter(File file, boolean append) throws IOException {
super(new FileOutputStream(file, append));
}
public FileWriter(FileDescriptor fd) {
super(new FileOutputStream(fd));
}
}
与FileReader相似,也是通过继承OutputStreamWriter来实现的,采用适配器模式:
下面是OutputStreamWriter实现源码,与InputStreamReader也很相似
public class OutputStreamWriter extends Writer {
private final StreamEncoder se;
public OutputStreamWriter(OutputStream out, String charsetName)
throws UnsupportedEncodingException
{
super(out);
if (charsetName == null)
throw new NullPointerException("charsetName");
se = StreamEncoder.forOutputStreamWriter(out, this, charsetName);
}
public OutputStreamWriter(OutputStream out) {
super(out);
try {
se = StreamEncoder.forOutputStreamWriter(out, this, (String)null);
} catch (UnsupportedEncodingException e) {
throw new Error(e);
}
}
public OutputStreamWriter(OutputStream out, Charset cs) {
super(out);
if (cs == null)
throw new NullPointerException("charset");
se = StreamEncoder.forOutputStreamWriter(out, this, cs);
}
public OutputStreamWriter(OutputStream out, CharsetEncoder enc) {
super(out);
if (enc == null)
throw new NullPointerException("charset encoder");
se = StreamEncoder.forOutputStreamWriter(out, this, enc);
}
public String getEncoding() {
return se.getEncoding();
}
void flushBuffer() throws IOException {
se.flushBuffer();
}
public void write(int c) throws IOException {
se.write(c);
}
public void write(char cbuf[], int off, int len) throws IOException {
se.write(cbuf, off, len);
}
public void write(String str, int off, int len) throws IOException {
se.write(str, off, len);
}
public void flush() throws IOException {
se.flush();
}
public void close() throws IOException {
se.close();
}
}
最后也是通过StreamEncoder对象,将字符流编码成字节流。
由上面可知,字符流实际上就是通过字节流解码而来的,底层通过FileInputStream读取字节流,有点适配器模式的味道。
关于IO流的其它类源码以后再看吧。