Java 7の従来のI/O-文字クラスStringReaderとStringWriter


この2つのクラスはStringクラスをReaderとWriterインタフェースに適合させ、StringWriterクラスが実現する過程で、本当にStringBufferを使用している.前述したように、StringBufferは可変クラスであり、Writerクラスには多くの文字列の操作があるため、このクラスは使いやすい.StringReaderクラスではStringクラスを1つだけ定義すればよい.クラスの読み取りにのみ関与し、変更などの操作がないため、複数の文字列を作成してリソースを浪費することはない.
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;
        }
    }

1文字ずつ読み込むこともできますし、複数の文字を1つの配列に一括して読み込むこともできます.読み出し中には、ロールバック読み出し、ホップ読み出しなどの操作もサポートされています.
lockはStringWriterクラスの初期化時にbufインスタンスとして付与されており、StringBufferはスレッドが安全であるため、読み出し時に同じStringBufferインスタンスをロックすることで書き込みと読み出しの同期を実現することができる.デフォルトのロックはStringReaderクラスインスタンスであり、StringBufferと同期できないため、synchronizedキーワードをメソッドに追加することはできません.
また、読み出し時には、次のようにして、入出力ストリームが開くことを確認する必要があります.
 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がこのオブジェクトをできるだけ早く回収するのを支援します.
思考:このメソッドを呼び出さないと、メモリ漏れは発生しませんか?