Writer是一个用于写入字符流的抽象类,子类必须实现write(char[],int,int)、flush()和close()三个方法,我们可以通过覆盖其中的方法以提供更加强大的功能
临时缓冲区用于保存写入的字符串和单个字符,子类无法继承
private char[] writeBuffer;
临时缓冲区的大小必须要大于等于1,而在Writer中默认定义为1024,而且可以看出这是不能修改的子类也无法继承
private static final int WRITE_BUFFER_SIZE = 1024;
用于同步此流上的操作的对象。为了提高效率,字符流对象可以使用除自身以外的对象来保护关键部分。因此,子类应该使用这个字段中的对象,而不是这个或同步方法
protected Object lock;
看完上面官方写的注释你是不是感觉压根没看懂?没错,确实但看注释根本不能知道这个lock属性到底用来干什么,接下来我们继续看下去,如果你感到没有耐心的话可以直接看下面我写的:构造方法,
从构造方法中可以看出,该类将自己当前的实例直接赋给了lock属性,所以显而易见,lock就是当前实例
protected Writer() {
this.lock = this;
}
创建一个新的字符流编写器,其关键部分将在给定对象上同步
这个构造方法是将当前Writer类中的属性lock赋为传入的对象
protected Writer(Object lock) {
if (lock == null) {
throw new NullPointerException();
}
this.lock = lock;
}
write(writeBuffer, 0, 1)方法 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);
}
}
write(cbuf, 0, len)方法 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);
}
}
从源码上看append方法还是去调用write方法进行写入
public Writer append(CharSequence csq) throws IOException {
write(String.valueOf(csq));
return this;
}
public abstract void flush() throws IOException;
关闭流,先刷新。一旦流关闭,进一步的write()或flush()调用将引发IOException。
public abstract void close() throws IOException;
返回一个丢弃所有字符的新Writer。返回的流最初是打开的。通过调用close()方法关闭流。随后对close()的调用无效。 当流处于打开状态时,append(char)、append(CharSequence)、append(CharSequence,int,int)、flush()、write(int)、writ(char[])和write(char[[],int,int])方法什么都不做。流关闭后,这些方法都会抛出IOException
public static Writer nullWriter() {
return new Writer() {
private volatile boolean closed;
private void ensureOpen() throws IOException {
if (closed) {
throw new IOException("Stream closed");
}
}
@Override
public Writer append(char c) throws IOException {
ensureOpen();
return this;
}
@Override
public Writer append(CharSequence csq) throws IOException {
ensureOpen();
return this;
}
@Override
public Writer append(CharSequence csq, int start, int end) throws IOException {
ensureOpen();
if (csq != null) {
Objects.checkFromToIndex(start, end, csq.length());
}
return this;
}
@Override
public void write(int c) throws IOException {
ensureOpen();
}
@Override
public void write(char[] cbuf, int off, int len) throws IOException {
Objects.checkFromIndexSize(off, len, cbuf.length);
ensureOpen();
}
@Override
public void write(String str) throws IOException {
Objects.requireNonNull(str);
ensureOpen();
}
@Override
public void write(String str, int off, int len) throws IOException {
Objects.checkFromIndexSize(off, len, str.length());
ensureOpen();
}
@Override
public void flush() throws IOException {
ensureOpen();
}
@Override
public void close() throws IOException {
closed = true;
}
};
}
从源码中我们发现在nullWriter方法内new了Writer类,重写里面各个方法
一种字符流,将其输出收集在字符串缓冲区中,然后可用于构造字符串。 关闭StringWriter无效。该类中的方法可以在流关闭后调用,而不会生成IOException
StringWriter继承Writer类
字符串缓冲区指的是StringBuffer,我们可以将其理解为是一个容器用于存储String字符串,并且我们可以对其做各类操作
private StringBuffer buf;
public StringWriter() {
buf = new StringBuffer();
lock = buf;
}
使用指定的初始字符串缓冲区大小创建新的字符串编写器。
参数initialSize表示:在自动展开缓冲区之前,将放入该缓冲区的字符值的数量
public StringWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
buf = new StringBuffer(initialSize);
lock = buf;
}
ensureCapacityInternal方法来对容器进行扩容value数组将字符存储起来(注意value数组是个byte[])public void write(int c) {
buf.append((char) c);
}
===========StringBuffer=================
@Override
@HotSpotIntrinsicCandidate
public synchronized StringBuffer append(char c) {
toStringCache = null;
super.append(c);
return this;
}
=============AbstractStringBuilder========
@Override
public AbstractStringBuilder append(char c) {
ensureCapacityInternal(count + 1);
if (isLatin1() && StringLatin1.canEncode(c)) {
value[count++] = (byte)c;
} else {
if (isLatin1()) {
inflate();
}
StringUTF16.putCharSB(value, count++, c);
}
return this;
}
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
int oldCapacity = value.length >> coder;
if (minimumCapacity - oldCapacity > 0) {
value = Arrays.copyOf(value,
newCapacity(minimumCapacity) << coder);
}
}
由此我们得出若使用StringWriter存储单字符,其实是存入了AbstractStringBuilder的byte[] value中
AbstractStringBuilder append方法,这里就不一样了,会调用putStringAt方法,然后调用String的getBytes方法通过System类将字符串复制到byte数组中public void write(String str) {
buf.append(str);
}
===================================
public AbstractStringBuilder append(String str) {
if (str == null) {
return appendNull();
}
int len = str.length();
ensureCapacityInternal(count + len);
putStringAt(count, str);
count += len;
return this;
}
private final void putStringAt(int index, String str) {
if (getCoder() != str.coder()) {
inflate();
}
str.getBytes(value, index, coder);
}
================String===============
void getBytes(byte dst[], int dstBegin, byte coder) {
if (coder() == coder) {
System.arraycopy(value, 0, dst, dstBegin << coder, value.length);
} else { // this.coder == LATIN && coder == UTF16
StringLatin1.inflate(value, 0, dst, dstBegin, value.length);
}
}
这里和写入单字符即更加像了,而最后的appendChars方法就是循环进行单字符的写入
public void write(char cbuf[], int off, int len) {
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return;
}
buf.append(cbuf, off, len);
}
由于字符序列可以使用String.valueOf转为String型所有就是使用的写入字符串的方式进行附加
public StringWriter append(CharSequence csq) {
write(String.valueOf(csq));
return this;
}
附加字符就是再写入单字符
附加子序列同样是写入字符串
public StringBuffer getBuffer() {
return buf;
}