(最近剛來到簡書平臺,以前在CSDN上寫的一些東西,也在逐漸的移到這兒來,有些篇幅是很早的時候寫下的,因此可能會看到一些內容雜亂的文章,對此深感抱歉,以下為正文)
正文
本篇將要講述的是Java IO包中的StringReader和StringWriter類。這兩個類都是Reader和Writer的裝飾類,使它們擁有了對String類型數(shù)據(jù)進行操作的能力。
下面還是先附上源碼,然后對其進行簡單的分析:
StringReader.java
package java.io;
public class StringReader extends Reader {
//內置了一個String類型的變量,用于存儲讀取的內容。因為Reader只需要讀取無需對數(shù)據(jù)進行改變,所以此時一個String類型變量就已經足夠了。
private String str;
//定義了3個int型變量,length表示讀取的字符串數(shù)據(jù)的長度,next表示下一個要讀取的位置,mark表示標記的位置。
private int length;
private int next = 0;
private int mark = 0;
/**
* 一個帶一個參數(shù)的構造方法,傳入的參數(shù)是一個String類型數(shù)據(jù),通過s初始化內置的str和length屬性。
*/
public StringReader(String s) {
this.str = s;
this.length = s.length();
}
/**
* 該方法用于判斷當前流是否處于開啟狀態(tài),本質就是檢測內置的str是否被賦值。
*/
private void ensureOpen() throws IOException {
if (str == null)
throw new IOException("Stream closed");
}
/**
* 每次讀取一個字符的read方法,最終返回讀取字符的int值。
*/
public int read() throws IOException {
synchronized (lock) {
//進行操作前,確保當前流處于開啟狀態(tài)。
ensureOpen();
//如果讀取的位置,超過了數(shù)據(jù)的總長度,那么直接返回-1,表示已無數(shù)據(jù)可讀。
if (next >= length)
return -1;
//正常情況下通過next索引結合String類型的charAt方法,來從str中取出對應的字符數(shù)據(jù)。
return str.charAt(next++);
}
}
/**
* 每次讀入多個字符的read方法,最終返回實際讀取的字符個數(shù)。該方法有3個參數(shù),第一個參數(shù)為一個字符數(shù)組,用于存儲讀取的數(shù)據(jù),第二和第三個參數(shù)為一個int
* 變量,分別為開始在數(shù)組中存儲數(shù)據(jù)的起點和存儲數(shù)據(jù)的長度。
*/
public int read(char cbuf[], int off, int len) throws IOException {
synchronized (lock) {
//進行操作前需要先判斷當前流是否處于開啟狀態(tài)。
ensureOpen();
//對傳入的參數(shù)進行安全檢測,如果不合法則拋出相應異常。
if ((off < 0) || (off > cbuf.length) || (len < 0) ||
((off + len) > cbuf.length) || ((off + len) < 0)) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
}
//如果下一個讀取的位置超過了讀取的數(shù)據(jù)的總長度,表示此時已經無數(shù)據(jù)可讀,此時直接返回-1。
if (next >= length)
return -1;
//定義了一個int型值n,用來接收length-next和len之間的較小值,一般情況下使用len即可,如果len長度超過了數(shù)據(jù)的總長度,那么就使用length-next的值。
int n = Math.min(length - next, len);
//使用String類的getChars方法,將指定str從next到next+n位置的數(shù)據(jù)拷貝到傳入cbuf中,拷貝位置從off開始。
str.getChars(next, next + n, cbuf, off);
//數(shù)據(jù)讀取拷貝完畢后,將下一個讀取的位置向后移位n位,最后返回n,即實際讀取的數(shù)據(jù)長度。
next += n;
return n;
}
}
/**
* 該方法用于跳過指定長度的數(shù)據(jù)。
*/
public long skip(long ns) throws IOException {
synchronized (lock) {
//進行操作前先確定當前流是否處于開啟狀態(tài)。
ensureOpen();
//如果當前讀取的位置已經位于讀取數(shù)據(jù)的末尾或者已經超過了數(shù)據(jù)總長度,那么直接返回0,因為此時已經無法再跳過數(shù)據(jù)進行讀取了。
if (next >= length)
return 0;
//定義了一個long型數(shù)據(jù)n用來存放length-next和ns之間的較小值,一般情況下是ns起作用,如果ns超過了當前未讀取的數(shù)據(jù)總長度,那么使用length-next。
long n = Math.min(length - next, ns);
//這里是為了處理傳入的ns是負數(shù)的情況,當傳入的值為負數(shù)時,此時讀取位置應當向回移動,在上一布操作中如果傳入的ns為負數(shù)的話,那么此時的n必定是ns
//Math.max(-next,n)則保證了只有只有當讀取位置大于回讀的數(shù)量時才可以回讀,所以最多之能回退到數(shù)據(jù)的起點位置。
n = Math.max(-next, n);
//下一次讀取的位置移動n個位置,最終將n返回。
next += n;
return n;
}
}
/**
* 該方法用于判斷當前流是否處于可讀狀態(tài)。
*/
public boolean ready() throws IOException {
synchronized (lock) {
ensureOpen();
return true;
}
}
/**
* 該方法用于判斷當前流是否支持流標記功能。
*/
public boolean markSupported() {
return true;
}
/**
* 該方法用于在指定位置留下流標記,與reset方法連用,可以試當前讀取位置回退到在流中的標記位置
*/
public void mark(int readAheadLimit) throws IOException {
//對傳入的參數(shù)進行安全檢測,標記的位置不能小于0,否則拋出相應的異常。
if (readAheadLimit < 0){
throw new IllegalArgumentException("Read-ahead limit < 0");
}
synchronized (lock) {
//在進行操作前,確定當前流處于開啟狀態(tài)。
ensureOpen();
//使用mark變量記錄下當前讀取的位置。
mark = next;
}
}
/**
* 該方法用于將當前讀取位置回退到流中的標記位置。
*/
public void reset() throws IOException {
synchronized (lock) {
//在進行操作前,確定當前流是否處于開啟狀態(tài)。然后將當前讀取位置回退到mark處。
ensureOpen();
next = mark;
}
}
/**
* close方法,關閉當前流,將內置的str指向null。
*/
public void close() {
str = null;
}
}
StringWriter.java
package java.io;
public class StringWriter extends Writer {
//內置了一個StringBuffer,因為這里牽扯到了數(shù)據(jù)的改變,所以簡單的String類型并不能滿足我們。
private StringBuffer buf;
/**
* 一個不帶參的構造函數(shù),內部為buf進行了初始化,并將該緩存區(qū)作為了內置鎖對象。
*/
public StringWriter() {
buf = new StringBuffer();
lock = buf;
}
/**
* 一個帶一個參數(shù)的構造函數(shù),傳入的參數(shù)為一個int型值,該值決定了內置buf初始化時的容量大小。
*/
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);
}
/**
* 該方法用于向流中每次寫入多個字節(jié)。
*/
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);
}
/**
* 該方法每次向流中寫入一個字符串類型的數(shù)據(jù)。
*/
public void write(String str) {
buf.append(str);
}
/**
* 該方法每次向流中寫入一個字符串數(shù)據(jù)的一部分。
*/
public void write(String str, int off, int len) {
buf.append(str.substring(off, off + len));
}
/**
* 該方法基本等同于上面的write(String str)方法,可以將制定的字符序列寫入流中。
*/
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;
}
/**
* 可以將一個字符數(shù)據(jù)寫入流中的,最后返回流本身。
*/
public StringWriter append(char c) {
write(c);
return this;
}
/**
* 將內置緩存區(qū)中的數(shù)據(jù)裝換為String類型并返回。
*/
public String toString() {
return buf.toString();
}
/**
* 返回內置的StringBuffer對象。
*/
public StringBuffer getBuffer() {
return buf;
}
/**
* 該方法本是將緩存中的數(shù)據(jù)強制寫出,在本類是一個空實現(xiàn)。
*/
public void flush() {
}
/**
* 該方法本市用于關閉流及釋放流相關聯(lián)的系統(tǒng)資源,在本類是一個空實現(xiàn)。
*/
public void close() throws IOException {
}
}
通過上面對源碼的簡單分析,我們隊StringReader和StringWriter有了初步的認識,下面通過一個簡單的小例子來展示其用法。
package StringIO;
import java.io.StringReader;
import java.io.StringWriter;
public class StringIOTest {
public static void main(String[] args) {
try (StringReader sr = new StringReader("just a test~");
StringWriter sw = new StringWriter()) {
int c = -1;
while((c = sr.read()) != -1){
sw.write(c);
}
//這里展示了即使關閉了StringWriter流,但仍然能獲取到數(shù)據(jù),因為其close方法是一個空實現(xiàn)。
sw.close();
System.out.println(sw.getBuffer().toString());
} catch (Exception e) {
e.printStackTrace();
}
}
}
剛開始我是比較奇怪這兩個類為什么會存在的,因為這與直接使用String類來進行數(shù)據(jù)操作,后來在網上看到別人的解釋,如果你遇到一個情景是你必須使用一個Reader或者Writer來作為參數(shù)傳遞參數(shù),但你的數(shù)據(jù)源又僅僅是一個String類型數(shù)據(jù),無需從文件中寫出,那么此時就可以用到它們。并且值得注意的是StringWriter中,寫入的數(shù)據(jù)只是存在于緩存中,并不會寫入實質的存儲介質之中。
以上為本篇的全部內容。