PrintStream是字符類型的打印輸出流,用於格式化文本輸出流的對象表示形式。此類實現了PrintStream類的全部print方法,除了寫入原始字節的方法,如有寫入原始字節的需求應該使用未編碼的字節輸出流寫入,例如FileInputStream等。與其餘輸出流不一樣的是,PrintStream永遠不會拋出IO異常,取而代之的是它在內部維護了一個異常標誌,能夠經過checkError方法去檢測。咱們在建立PrintStream的時候能夠經過指定autoFlush爲true設置PrintStreamWriter輸出流自動刷新,這意味着當寫入字節數組,println方法調用,寫入換行符或者「\n」字符都會引起PrintStreamWriter輸出流的自動刷新。全部PrintStream打印的字符都是使用平臺默認的字符編碼方法將字符轉化爲字節寫入,PrintStreamWriter適用於寫入字符而不是字節的場合。java
public class PrintWriter extends Writer { // PrintWriter綁定的底層字符輸出流 protected Writer out; //是否自動刷新 private final boolean autoFlush; //異常標誌,當PrintWriter產生異常會被自身捕獲,而且設置trouble爲true private boolean trouble = false; //格式化字符串的對象 private Formatter formatter; //字節打印輸出流 private PrintStream psOut = null; //行分隔符 private final String lineSeparator; }
/** * 構造函數,指定底層的字符輸出流,默認不自動flush,採用默認的字符編碼方式 */ public PrintWriter (Writer out) { this(out, false); } /** * 構造函數,指定底層的字符輸出流和是否自動flush,採用默認的字符編碼方式 */ public PrintWriter(Writer out, boolean autoFlush) { super(out); this.out = out; this.autoFlush = autoFlush; lineSeparator = java.security.AccessController.doPrivileged( new sun.security.action.GetPropertyAction("line.separator")); } /** * 構造函數,默認不自動刷新, 採用默認字符集,底層字符輸出流綁定的是基於方法指定的字符輸出流對象out建立的 * BufferedWriter對象 */ public PrintWriter(OutputStream out) { this(out, false); } /** * 構造函數,基於方法指定底層字節輸出流和自動flush模式. 此構造函數將爲指定的底層字節輸出流建立中間的 * OutputStreamWriter它將使用平臺默認的字符編碼方式將字符轉化爲字節存儲,並在外部使用BufferedWriter裝飾它 * 爲PrintWriter對象提供額外的字符緩衝功能 */ public PrintWriter(OutputStream out, boolean autoFlush) { this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush); //若out爲PrintStream對象 if (out instanceof java.io.PrintStream) { psOut = (PrintStream) out; } } /** * 構造函數, 指定寫入的文件名,默認不自動flush,採用默認的字符編碼方式. */ public PrintWriter(String fileName) throws FileNotFoundException { this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))), false); } /* 私有構造函數, 指定寫入文件和字符編碼方式,默認不自動flush */ private PrintWriter(Charset charset, File file) throws FileNotFoundException { this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)), false); } /** * 構造函數, 默認不自動flush, 指定寫入文件的文件名和使用的字符編碼名稱,這個構造方法將使用字符字節轉化的中間 * 橋樑OutputStreamWriter包裹底層基於fileName的字節文件輸出流FileOutputStream使用方法指定的字符編碼將 * 字符轉化爲字節 */ public PrintWriter(String fileName, String csn) throws FileNotFoundException, UnsupportedEncodingException { this(toCharset(csn), new File(fileName)); } /** * 構造函數, 指定寫入文件,默認不自動flush,採用默認的字符編碼方式將寫入字符轉化爲字節存儲 */ public PrintWriter(File file) throws FileNotFoundException { this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))), false); } /** * 構造函數,指定寫入的文件file,默認不自動flush,採用指定的字符編碼將字符轉化爲字節數據 */ public PrintWriter(File file, String csn) throws FileNotFoundException, UnsupportedEncodingException { this(toCharset(csn), file); }
經過閱讀PrintStream的構造函數源碼,咱們基於底層字符輸出流的產生方式能夠將PrintStream的構造函數分爲兩種類型,一種是構造方法直接指定了底層字符輸出流out,例如PrintWriter(Writer out),另外一種構造方法不直接指定底層字符輸出流out,而是經過指定底層字節輸出流、FIle對象或者fileName,基於這些要素建立字符字節轉化的中間流對象OutputStreamWriter,並讓PrintWriter對象內部的底層字符輸出流引用指向內部包裹了該OutputStreamWriter的BufferedWriter對象,提供了額外的數據緩衝功能。數組
PrintWriter支持int、char[]、String對象的寫入咱們不許備將全部數據類型的write方法拿出來分析,這裏只分析寫入字符串方法write(String s),其餘方法參照本方法研讀便可,下面是方法源碼:數據結構
/** * 寫入字符串,該方法不能從Writer方法繼承由於他不能拋出IOException異常而要本身處理 */ public void write(String s) { write(s, 0, s.length()); }
方法內部不直接處理而是調用了write(s,0,s.length)方法,進入該方法源碼:app
public void write(String s, int off, int len) { try { synchronized (lock) { ensureOpen(); out.write(s, off, len); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }
write寫入字符串的方法在方法內部對父類lock對象加鎖,首先調用了ensureOpen方法檢驗該流的狀態是否已關閉(經過判斷綁定的底層字符輸出流對象是否爲空),若未關閉調用底層字符輸出流對象out的write方法寫入字符串。函數
/** * 打印字符串x而後終止方法 */ public void println(String x) { synchronized (lock) { print(x); println(); } }
該方法的邏輯也比較簡單,首先調用本類方法print(x),咱們跟着進入方法源碼:源碼分析
public void print(String s) { if (s == null) { s = "null"; } write(s); }
該方法沒作啥當方法參數爲null時爲它分配一個值爲"null"的字符串,最後調用write方法,將字符串s寫入到底層字符輸出流對象out中,咱們繼續回到頂層的int write(String s)方法繼續分析接下來的println方法作了什麼?ui
public void println() { newLine(); } private void newLine() { try { synchronized (lock) { ensureOpen(); out.write(lineSeparator); if (autoFlush) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } }
println方法內部直接調用了newLine方法將一個換行符寫入了底層的字符輸出流對象out中,基本邏輯就是首先判斷底層字符輸出流是否關閉,未關閉則調用它的write方法寫入換行符lineSeparator字符串,最後判斷是否開啓自動flush,如果指定自動flush則刷新底層字符輸出流。this
總結println(String s)方法基本邏輯以下:編碼
1)判斷寫入的字符串是否爲null,若爲null則從新爲它分配一個值爲「null」的字符串;spa
2)底層字符輸出流對象out寫入該字符串;
3)底層字符輸出流對象out寫入換行符lineSeperator;
4)若指定了自動刷新(autoflush=true)則刷新底層字符輸出流。
/** * 返回指定字符編碼名稱csn對應的Charset對象,若csn爲null或者是不支持的字符編碼方式則拋出異常 */ private static Charset toCharset(String csn) throws UnsupportedEncodingException { Objects.requireNonNull(csn, "charsetName"); try { return Charset.forName(csn); } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) { // UnsupportedEncodingException should be thrown throw new UnsupportedEncodingException(csn); } } /** 檢查輸出流是否開啓,若底層字符輸出流不爲null則認爲是開啓的 **/ private void ensureOpen() throws IOException { if (out == null) throw new IOException("Stream closed"); } /** * 刷新流,其實就是刷新底層字符輸出流out的緩衝區 */ public void flush() { try { synchronized (lock) { ensureOpen(); out.flush(); } } catch (IOException x) { trouble = true; } } /** * 關閉該流並釋放相關的系統資源 */ public void close() { try { synchronized (lock) { if (out == null) return; out.close(); out = null; } } catch (IOException x) { trouble = true; } } /** * 若流沒有關閉,則刷新流並檢查其錯誤狀態,這裏注意若底層字符輸出流爲PrintWriter或者綁定的底層字節輸出流是 * PrintOutputStream則調用它們自己的checkError檢查流的異常狀態,若底層輸出流不在上述範圍則返回本類標識異常狀 * 態的成員變量 trouble */ 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; } /** * 標誌該流已發生IO異常,調用該方法將致使在調用clearError方法以前checkError方法返回爲true */ protected void setError() { trouble = true; } /** * 清除PrintWriter流的異常狀態 */ protected void clearError() { trouble = false; } /* * 寫入單個字符到PrintWriter底層字符輸出流 */ public void write(int c) { try { synchronized (lock) { ensureOpen(); out.write(c); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } } /** * 寫入字符數組的一部分到PrintWriter底層字符輸出流 */ public void write(char buf[], int off, int len) { try { synchronized (lock) { ensureOpen(); out.write(buf, off, len); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } } /** * 將指定字符數組寫入PrintWriter底層字符輸出流out */ public void write(char buf[]) { write(buf, 0, buf.length); } /** * 寫入字符串的一部分 */ public void write(String s, int off, int len) { try { synchronized (lock) { ensureOpen(); out.write(s, off, len); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true; } } /** * 打印boolean值,先將boolean值轉爲爲字符串true和false基於平臺默認的字符編碼方式寫入底層 * 輸出流中 */ public void print(boolean b) { write(b ? "true" : "false"); } /** * 打印單個字符,基於平臺默認的字符編碼方式寫入底層輸出流中 */ public void print(char c) { write(c); } /** * 打印指定整數i,先將整數轉爲字符串,基於平臺默認的字符編碼方式寫入底層輸出流out中 */ public void print(int i) { write(String.valueOf(i)); } /** * 打印指定長整形l,先將長整形轉爲字符串,基於平臺默認的字符編碼方式寫入底層輸出流out中 */ public void print(long l) { write(String.valueOf(l)); } /** * 打印指定浮點數f,先將浮點形轉爲字符串,基於平臺默認的字符編碼方式寫入底層輸出流out中 */ public void print(float f) { write(String.valueOf(f)); } /** * 打印指定雙精度浮點數d,先將浮點數d轉爲字符串,基於平臺默認的字符編碼方式寫入底層輸出流out中 */ public void print(double d) { write(String.valueOf(d)); } /** * 打印指定字符數組s,基於平臺默認的字符編碼方式寫入底層輸出流 */ public void print(char s[]) { write(s); } /** * 打印對象obj,先將obj轉爲字符串形式,再基於平臺默認的字符編碼方式寫入底層輸出流 */ public void print(Object obj) { write(String.valueOf(obj)); } /* Methods that do terminate lines */ /** * 底層字符輸出流寫入行分隔符終止當前行,行分隔符由系統屬性line.seperator定義。 */ public void println() { newLine(); } /** * 打印boolean值,而後終止該行,實質就是在底層字符輸出流將boolean轉爲字符串後寫入而後在後面寫入行分隔符結束該行 */ public void println(boolean x) { synchronized (lock) { print(x); println(); } } /** * 打印字符x,而後終止該行,實質就是在底層字符輸出流寫入字符x而後在後面寫入行分隔符結束該行 */ public void println(char x) { synchronized (lock) { print(x); println(); } } /** * 打印int值x,而後終止該行,實質就是在底層字符輸出流將int轉爲字符串後寫入而後在後面寫入行分隔符結束該行 */ public void println(int x) { synchronized (lock) { print(x); println(); } } /** * 打印long值x,而後終止該行,實質就是在底層字符輸出流將long轉爲字符串後寫入而後在後面寫入行分隔符結束該行 */ public void println(long x) { synchronized (lock) { print(x); println(); } } /** * 打印float值x,而後終止該行,實質就是在底層字符輸出流將float轉爲字符串後寫入而後在後面寫入行分隔符結束該行 */ public void println(float x) { synchronized (lock) { print(x); println(); } } /** * 打印double值x,而後終止該行,實質就是在底層字符輸出流將double 轉爲字符串後寫入而後在後面寫入行分隔符結束該行 */ public void println(double x) { synchronized (lock) { print(x); println(); } } /** * 打印字符數組x,而後終止該行,實質就是在底層字符輸出流寫入字符數組x而後在後面寫入行分隔符結束該行 */ public void println(char x[]) { synchronized (lock) { print(x); println(); } } /** * 打印字符串x而後換行. 實質就是在底層字符輸出流寫入字符串x而後在後面寫入行分隔符結束該行 */ public void println(String x) { synchronized (lock) { print(x); println(); } } /** * 打印對象x,而後換行。實質就是獲取對象x的字符串形式str在底層字符輸出流寫入str而後在後面寫入行分隔符結束該行 */ public void println(Object x) { String s = String.valueOf(x); synchronized (lock) { print(s); println(); } } /** * 基於方法指定的格式化字符串format,將參數格式化的字符串寫入該PrintWriter底層字符輸出流out * 若啓用自動刷新autoflush則刷新底層字符輸出流out的緩衝區,做用和format(String format, Object ... args) * 相同 */ public PrintWriter printf(String format, Object ... args) { return format(format, args); } /** * 基於方法指定的格式化字符串format和指定地區對象l,將參數格式化的字符串寫入該PrintWriter底層字符輸出流out * 若啓用自動刷新autoflush則刷新底層字符輸出流out的緩衝區,和format(Locale l,String format, Object ... * args)方法相同 */ public PrintWriter printf(Locale l, String format, Object ... args) { return format(l, format, args); } /** * 基於方法指定的格式化字符串format,將參數格式化的字符串寫入該PrintWriter底層字符輸出流out * 若啓用自動刷新autoflush則刷新底層字符輸出流out的緩衝區 */ 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; } /** * 基於方法指定的格式化字符串format和指定地區對象l,將參數格式化的字符串寫入該PrintWriter底層字符輸出流out * 若啓用自動刷新autoflush則刷新底層字符輸出流out的緩衝區 */ 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; } /** * 往PrintWriter底層字符輸出流追加指定字符序列csq,經過該方法可能將整個字符序列的字符數據寫入底層輸出流out也可 * 能只寫入一部分,具體取決於字符序列的toString方法實現 */ public PrintWriter append(CharSequence csq) { if (csq == null) write("null"); else write(csq.toString()); return this; } /** * 往PrintWriter底層字符輸出流追加指定字符序列csq下標start到end不包括end的子字符序列 * 注意,經過該方法可能將整個字符序列的字符數據寫入底層輸出流out也可能只寫入一部分,具體取決於字符序列的 * toString方法實現 */ public PrintWriter append(CharSequence csq, int start, int end) { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this; } /** * 往PrintWriter底層字符輸出流追加指定字符 */ public PrintWriter append(char c) { write(c); return this; }