[重學Java基礎][JavaIO流][Part.8] 打印字符輸出流
PrintWriter
概述
PrintWriter緩衝字符數組輸出流,繼承了所有字符輸出流的超類Writer類,用於向文本對象輸入字符內容
很明顯是一個處理流 用於處理字符數據 代理真正的節點流輸出到數據匯 是一個非常實用的輸出流
系統的System.out方法就是調用了內置的PrintWriter流
官方註釋
格式化打印對象到文本輸出流。本類實現了所有PrintStream也包含的方法。
但本類不包含寫入字節流的方法,寫入字節流可以使用無編碼字節流。
與PrintStream不同,只有代理println,printf,format方法時且自動刷新
是啓用狀態,纔會起效,而不是在一個新的換行符被輸出時進行自動刷新。
這些方法使用平臺自己的換行符而不是普通的換行符
此類的所有方法不會跑出I/O異常,除了其中的一些構造方法。
終端可以查詢是否是一些代理方法拋出的受檢異常
源碼分析
成員屬性
代理的節點輸出流
protected Writer out;
是否自動刷新
private final boolean autoFlush;
是否有拋出異常 官方註釋提到PrintWriter不拋出異常
所以如果發生異常只是 此標誌位置爲false
private boolean trouble = false;
格式化參數
private Formatter formatter;
輸出流 適配器模式
private PrintStream psOut = null;
成員方法
構造方法
創建一個不會自動刷新的PrintWriter 對象
public PrintWriter (Writer out) {
this(out, false);
}
創建一個PrintWriter 對象,並設置是否自動刷新
如果自動刷新則 println,printf,format方法將會自動刷新輸出流
public PrintWriter(Writer out,
boolean autoFlush) {
super(out);
this.out = out;
this.autoFlush = autoFlush;
}
創建一個不會自動刷新的PrintWriter對象
調用OutputStream作爲真正的輸出節點流
可以看到此方法和第一個方法的不同之處就是入參是OutputStream
內部調用了下面的方法
public PrintWriter(OutputStream out) {
this(out, false);
}
創建一個PrintWriter對象,並設置是否自動刷新
調用緩衝流包裹的OutputStreamWriter轉換器對象輸出到真正的節點流中
編碼採用默認字符集
public PrintWriter(OutputStream out, boolean autoFlush) {
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
// 爲了傳播異常內容 保存輸出流
if (out instanceof java.io.PrintStream) {
psOut = (PrintStream) out;
}
}
創建一個PrintWriter對象輸出數據到指定文件名的文件,並不自動刷新
內部調用緩衝流包裹的OutputStreamWriter轉換器對象輸出到真正的文件輸出節點流中
內部調用了上面的第二個構造方法
public PrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
}
創建一個PrintWriter對象輸出數據到指定文件名的文件,並不自動刷新,
根據編碼名使用指定的編碼方式
內部調用了下面的私有構造方法
public PrintWriter(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
this(toCharset(csn), new File(fileName));
}
私有構造方法
內部使用緩衝流包裹的OutputStreamWriter轉換器對象輸出到真正的文件輸出節點流中
並根據charset字符集指定編碼方式
private PrintWriter(Charset charset, File file)
throws FileNotFoundException
{
this(new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file), charset)),
false);
}
創建一個PrintWriter對象輸出數據到指定文件對象,並不自動刷新
public PrintWriter(File file) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),
false);
}
創建一個PrintWriter對象輸出數據到指定文件對象,並不自動刷新
且使用指定的編碼字符集
public PrintWriter(File file, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
this(toCharset(csn), file);
}
其他
確認流是否打開
private void ensureOpen() throws IOException {
if (out == null)
throw new IOException("Stream closed");
}
刷新流內容方法
內部捕捉了IOException
所以PrintWriter不會拋出IOException
只會改變成員屬性troubled的標誌位
public void flush() {
try {
synchronized (lock) {
ensureOpen();
out.flush();
}
}
catch (IOException x) {
trouble = true;
}
}
檢查流錯誤方法
同時刷新流內容,返回錯誤標誌位
public boolean checkError() {
if (out != null) {
flush();
}
if (out instanceof java.io.PrintWriter) {
PrintWriter pw = (PrintWriter) out;
return pw.checkError();
} else if (psOut != null) {
return psOut.checkError();
}
return trouble;
}
直接設置錯誤標誌位方法
protected void setError() {
trouble = true;
}
清除錯誤標誌位方法
protected void clearError() {
trouble = false;
}
寫入數據方法
這是一個阻塞方法 並且是線程安全的
所以捕捉了受檢異常InterruptedIOException
並重新設置了線程中斷以方便上級方法檢測
public void write(int c) {
try {
synchronized (lock) {
ensureOpen();
out.write(c);
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
其他入參不同的重寫寫入方法 方法體大同小異
public void write(char buf[], int off, int len)
{……}
public void write(char buf[])
{……}
public void write(String s, int off, int len)
{……}
新一行方法 調用println換行實際上調用的就是此方法
實際上是寫入了對應系統的換行符System.lineSeparator()
private void newLine() {
try {
synchronized (lock) {
ensureOpen();
out.write(System.lineSeparator());
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
打印輸出方法 入參是布爾型
然而實際上輸出時是轉換爲了字符型了
並且採用的是默認編碼
public void print(boolean b) {
write(String.valueOf(b));
}
其他重載的打印輸出方法 大同小異
我們平時調用的系統print方法就是這些方法
不是字符型的全部被轉爲字符型輸出了
public void print(boolean b) {
this.write(String.valueOf(b));
}
public void print(char c) {
this.write(c);
}
public void print(int i) {
this.write(String.valueOf(i));
}
public void print(long l) {
this.write(String.valueOf(l));
}
public void print(float f) {
this.write(String.valueOf(f));
}
public void print(double d) {
this.write(String.valueOf(d));
}
public void print(char[] s) {
this.write(s);
}
public void print(String s) {
this.write(String.valueOf(s));
}
public void print(Object obj) {
this.write(String.valueOf(obj));
}
打印輸出並換行方法
其實就是輸出完後 追加調用了println(newline)方法
public void println(boolean x) {
Object var2 = this.lock;
synchronized(this.lock) {
this.print(x);
this.println();
}
}
重載的都大同小異
public void println(char x)
public void println(int x)
public void println(long x)
public void println(float x)
public void println(double x)
public void println(char[] x)
字符格式化打印輸出方法
入參是格式化參數format 和被格式化的字符內容 是個可變數組Object ... args
字符內容和格式化參數不必一一對應 但不能字符內容少於格式化參數
具體可以看printf的詳解
此方法內部調用的是format方法
public PrintWriter printf(String format, Object ... args) {
return format(format, args);
}
格式化打印方法 String format爲格式化參數 可變數組Object ... args爲需要格式化的字符內容
public PrintWriter format(String format, Object ... args) {
try {
synchronized (lock) {
ensureOpen();
if ((formatter == null)
|| (formatter.locale() != Locale.getDefault()))
formatter = new Formatter(this);
formatter.format(Locale.getDefault(), format, args);
if (autoFlush)
out.flush();
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}
增加了一個可指定字符區位Locale的參數 可以根據指定的區位習慣的格式進行輸出
public PrintWriter format(Locale l, String format, Object ... args) {
try {
synchronized (lock) {
ensureOpen();
if ((formatter == null) || (formatter.locale() != l))
formatter = new Formatter(this, l);
formatter.format(l, format, args);
if (autoFlush)
out.flush();
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}
追加打印方法
public PrintWriter append(CharSequence csq) {
write(String.valueOf(csq));
return this;
}
追加打印方法 打印字符或字符序列到輸出流中
和print方法不同的是返回了本身PrintWriter 對象
可以鏈式調用
public PrintWriter append(CharSequence csq, int start, int end) {
if (csq == null) csq = "null";
return append(csq.subSequence(start, end));
}
public PrintWriter append(char c) {
write(c);
return this;
}
代碼示例
構造一個PrintWriter 對象 並使用write方法輸出 必須手動flush
char[] chars={'愛', '喫', '拉', '面', '的','小','泉','同','學' };
String str="愛喫拉麪的小泉同學";
File file=new File("d:\\animation.txt");
PrintWriter pw=new PrintWriter(new FileOutputStream(file));
pw.write(chars);
pw.write(str);
pw.flush();
PrintWriter pw2=new PrintWriter("d:\\animation2.txt");
pw2.write("王牌御史");
pw2.flush();
運行結果
愛喫拉麪的小泉同學愛喫拉麪的小泉同學
王牌御史
使用print方法輸出
PrintWriter pw = new PrintWriter("d:\\pw.txt");
pw.println("這是一個換行輸出");
// 將字符'A'對應ASCII碼寫入到輸出流中,等於輸出'A'
pw.write(0x41);
// 將字符串"65"寫入到輸出流中。
pw.append('B').append("CDEF");
pw.println();
String str = "今天是";
int mouth=5;
int day=1;
pw.printf("%s%d.%d", str, mouth,day);
pw.flush();
運行結果
這是一個換行輸出
ABCDEF
今天是5.1