這兩個類將String類適配到了Reader和Writer接口,在StringWriter類實現的過程中,真正使用的是StringBuffer,前面講過,StringBuffer是一個可變類,由於Writer類中有許多字符串的操作,所以這個類用起來比較方便;在StringReader類中只定義一個String類即可,因爲只涉及到類的讀取,而沒有修改等的操作,不會創建多個字符串而造成資源浪費。
1、StringWriter類
public class StringWriter extends Writer {
private StringBuffer buf; // 一個可變的、線程安全的字符串變量
public StringWriter() {
buf = new StringBuffer();
lock = buf; // lock是一個Object類的實例對象
}
public StringWriter(int initialSize) {
if (initialSize < 0) {
throw new IllegalArgumentException("Negative buffer size");
}
buf = new StringBuffer(initialSize);
lock = buf;
}
public void write(int c) {
buf.append((char) c);
}
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);
}
public void write(String str) {
buf.append(str);
}
public void write(String str, int off, int len) {
buf.append(str.substring(off, off + len));
}
//...
}
可以看出,提供了各種各樣的write()方法,如可以將字符數組追加到字符串尾、單個字符添加到字符串末尾行等等,源代碼的實現都非常簡單,下面來舉個例子:
public class testStringWriter {
public static void main(String[] args) {
char[] x = { 'm', 'n' };
StringWriter s = new StringWriter(20);
s.write(x, 0, 1);
s.write('a');
s.write("bcd");
s.write("012", 1, 2);
System.out.println(s.toString());
}
}
最後的輸出結果如下:
mabcd12
或者還可以使用append()方法將要添加的內容追加到字符串末尾。如下源代碼:
public StringWriter append(CharSequence csq) {
if (csq == null)
write("null");
else
write(csq.toString());
return this;
}
public StringWriter append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
public StringWriter append(char c) {
write(c);
return this;
}
2、StringReader類
下面來看一下StringReader類的read()方法,如下:
public int read() throws IOException {
synchronized (lock) {
ensureOpen();
if (next >= length)
return -1;
return str.charAt(next++);
}
}
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
ensureOpen();
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
if (next >= length)
return -1;
int n = Math.min(length - next, len);
str.getChars(next, next + n, cbuf, off);
next += n;
return n;
}
}
可以每次讀取一個字符,也可以將多個字符一次性讀入一個數組中。在讀取的過程中還支持回退讀取、跳讀取等操作。
lock在StringWriter類初始化時已經賦值爲buf實例,由於StringBuffer是線程安全的,所以在讀取的時候,加鎖同一個StringBuffer實例就可以實現寫入和讀取的同步。切不可在方法上加synchronized關鍵字,因爲這樣默認的鎖是StringReader類實例,無法與StringBuffer實現同步。
讀取時還需要確保輸入輸出流打開(ensureOpen()方法),源碼如下:
private void ensureOpen() throws IOException {
if (str == null)
throw new IOException("Stream closed");
}
也就是確保有內容可以讀取。如果沒有則直接拋出異常。
來看一下使用標記讀取字符串,主要的方法有3個,如下:
private int next = 0; // 下一個要讀取的字符標記
private int mark = 0; // 對字符串中的字符進行標記
public boolean markSupported() { // 是否支持標記讀取
return true;
}
/**
* 標記當前的位置,調用reset()方法後可以重新回到標記處讀取數據.
*/
public void mark(int readAheadLimit) throws IOException {
if (readAheadLimit < 0){
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
ensureOpen();
mark = next; // 標記爲當前的讀取位置
}
}
public void reset() throws IOException {
synchronized (lock) {
ensureOpen();
next = mark;
}
}
mark()和reset()方法同樣需要實現同步。所以看出StringReader和StringWriter類是線程安全的。下面簡單示例一下這個類中重要方法的應用。
StringReader sr=new StringReader("abcdefg");
System.out.println((char)sr.read()); // a
// 如果支持標記讀取,則標記當前的讀取位置,也就是字符串中的第二個字符b
if(sr.markSupported()){
sr.mark(3); // 查看源碼後知道,這個參數3無任何意義。
}
System.out.println((char)sr.read()); // b
System.out.println((char)sr.read()); // c
sr.reset(); // 從做標記的mark開始讀取
System.out.println((char)sr.read()); // 當前讀取位置設置爲mark標記的值,輸出爲b
char[] x=new char[3];
sr.read(x,0,2);
System.out.println(String.valueOf(x));// bc
如果sr對象結尾時最好調用close()方法進行關閉,close()方法源代碼如下:
public void close() {
str = null;
}
幫助JVM儘快回收這個對象。
思考:如果不調用這個方法,會不會產生內存泄漏?