关于 java.io.PrintStream 的部分笔记,这个类除了实现了继承自FilterOutputStream的基本的write方法外,还提供了打印输出内容的方法来打印当前输出的内容。本文演示代码段的执行环境基于JDK版本1.7 。
概述 PrintStream提供了打印输出内容的便捷方法,这样可以直接调用其提供的方法来完成输出内容的输出和打印显示两个操作。所有通过PrintStream输出打印的内容都会被转成字节流然后输出到目标位置。除此之外,PrintStream也不会抛出IOException,而是通过内置字段trouble 来判断是否在调用过程中抛出了IOException。
继承关系 1 2 3 4 5 --java.lang.Object --java.io.OutputStream --java.io.FilterOutputStream --java.io.PrintStream
实现接口
类名
实现接口
PrintStream
Closeable, Flushable, Appendable, AutoCloseable
部分方法 public boolean checkError() 1 2 3 4 5 6 7 8 9 public boolean checkError () { if (out != null ) flush(); if (out instanceof java.io.PrintStream) { PrintStream ps = (PrintStream) out; return ps.checkError(); } return trouble; }
检查当前时间点之前是否存在过数据输出异常的情况,如果存在,那么返回true。这里仅针对在方法调用过程中抛出的IOException,或者setError方法被显式调用。如果在过程中出现了InterruptedIOException,那么则通过Thread.currentThread().interrupt() 进行处理。
protected void setError() 1 2 3 protected void setError () { trouble = true ; }
protected void clearError() 1 2 3 protected void clearError () { trouble = false ; }
public void write(int b) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void write (int b) { try { synchronized (this ) { ensureOpen(); out.write(b); if ((b == '\n' ) && autoFlush) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true ; } }
向底层输出流中写入一个字节。如果入参是换行符,那么就根据autoFlush的值来决定是否需要把写入的数据推到目标输出位置。
public void write(byte buf[], int off, int len) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public void write (byte buf[], int off, int len) { try { synchronized (this ) { ensureOpen(); out.write(buf, off, len); if (autoFlush) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true ; } }
向底层输出流中写入一个字节数组。在向底层输出流无异常写完那么就根据autoFlush的值来决定是否需要把写入的数据推到目标输出位置。
private void write(char buf[]) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 private void write (char buf[]) { try { synchronized (this ) { ensureOpen(); textOut.write(buf); textOut.flushBuffer(); charOut.flushBuffer(); if (autoFlush) { for (int i = 0 ; i < buf.length; i++) if (buf[i] == '\n' ) out.flush(); } } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true ; } }
将字符数组输出到底层输出流中。首先将内容写入到textOut的缓冲区中(BufferedWriter),然后把数据强制刷新到textOut的底层输出流charOut (OutputStreamWriter)中。最后再把charOut的数据强制刷新到其中包含的底层输出流(StreamEncoder)。采用OutputStreamWriter的目的是因为这是一个字符流转字节流的类,可以实现把各种类型的数据转成对应的字节流输出。而BufferedWriter带有缓冲区,所以其效率相对来说会高一点。如果支持自动刷新,那么就判断入参字符数组中是否含有换行符“\n”,如果有就底层输出流的flush方法,将数据推到目标输出位置。
private void write(String s) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void write (String s) { try { synchronized (this ) { ensureOpen(); textOut.write(s); textOut.flushBuffer(); charOut.flushBuffer(); if (autoFlush && (s.indexOf('\n' ) >= 0 )) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true ; } }
将字符串输出到底层输出流中。执行逻辑同方法write(char buf[]) ,故不予赘述。
private void newLine() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private void newLine () { try { synchronized (this ) { ensureOpen(); textOut.newLine(); textOut.flushBuffer(); charOut.flushBuffer(); if (autoFlush) out.flush(); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true ; } }
跳转到下一行。执行逻辑同方法write(char buf[]) ,故不予赘述。
打印方法集合 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 public void print (boolean b) { write(b ? "true" : "false" ); } public void print (char c) { write(String.valueOf(c)); } public void print (int i) { write(String.valueOf(i)); } public void print (long l) { write(String.valueOf(l)); } public void print (float f) { write(String.valueOf(f)); } public void print (double d) { write(String.valueOf(d)); } public void print (char s[]) { write(s); } public void print (String s) { if (s == null ) { s = "null" ; } write(s); } public void print (Object obj) { write(String.valueOf(obj)); } public void println () { newLine(); } public void println (boolean x) { synchronized (this ) { print(x); newLine(); } } public void println (char x) { synchronized (this ) { print(x); newLine(); } } public void println (int x) { synchronized (this ) { print(x); newLine(); } } public void println (long x) { synchronized (this ) { print(x); newLine(); } } public void println (float x) { synchronized (this ) { print(x); newLine(); } } public void println (double x) { synchronized (this ) { print(x); newLine(); } } public void println (char x[]) { synchronized (this ) { print(x); newLine(); } } public void println (String x) { synchronized (this ) { print(x); newLine(); } } public void println (Object x) { String s = String.valueOf(x); synchronized (this ) { print(s); newLine(); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public PrintStream printf (Locale l, String format, Object ... args) { return format(l, format, args); } public PrintStream format (Locale l, String format, Object ... args) { try { synchronized (this ) { ensureOpen(); if ((formatter == null ) || (formatter.locale() != l)) formatter = new Formatter(this , l); formatter.format(l, format, args); } } catch (InterruptedIOException x) { Thread.currentThread().interrupt(); } catch (IOException x) { trouble = true ; } return this ; }
按照指定的输出格式打印入参内容。printf方法和单独调用format方法的执行结果是一致的。在format方法中,如果指定Locale,那么就会按照该区域的风格打印指定格式的内容。否则就会按照默认的区域风格打印。format字段用来指定具体的打印格式和结构。args则包含了具体填充的实际内容。在format方法中,实际发挥作用的是Formatter.format的方法。其内部处理逻辑如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 public Formatter format (Locale l, String format, Object ... args) { ensureOpen(); int last = -1 ; int lasto = -1 ; FormatString[] fsa = parse(format); for (int i = 0 ; i < fsa.length; i++) { FormatString fs = fsa[i]; int index = fs.index(); try { switch (index) { case -2 : fs.print(null , l); break ; case -1 : if (last < 0 || (args != null && last > args.length - 1 )) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[last]), l); break ; case 0 : lasto++; last = lasto; if (args != null && lasto > args.length - 1 ) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[lasto]), l); break ; default : last = index - 1 ; if (args != null && last > args.length - 1 ) throw new MissingFormatArgumentException(fs.toString()); fs.print((args == null ? null : args[last]), l); break ; } } catch (IOException x) { lastException = x; } } return this ; } private FormatString[] parse(String s) { ArrayList<FormatString> al = new ArrayList<>(); Matcher m = fsPattern.matcher(s); for (int i = 0 , len = s.length(); i < len; ) { if (m.find(i)) { if (m.start() != i) { checkText(s, i, m.start()); al.add(new FixedString(s.substring(i, m.start()))); } al.add(new FormatSpecifier(m)); i = m.end(); } else { checkText(s, i, len); al.add(new FixedString(s.substring(i))); break ; } } return al.toArray(new FormatString[al.size()]); }
format(Locale l, String format, Object … args)方法用来打印一个格式化了的内容。第9行代码用来识别出入参format中含有的有效的和无效的占位符,具体逻辑可浏览第44 ~ 71行代码。第10 ~ 40则遍历识别的占位符结果,并根据每个特定的占位符从参数集合args中取出对应位置的数据放入占位符所处的位置完成内容的格式化和输出打印。
public PrintStream append(CharSequence csq) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public PrintStream append (CharSequence csq) { if (csq == null ) print("null" ); else print(csq.toString()); return this ; } public PrintStream append (CharSequence csq, int start, int end) { CharSequence cs = (csq == null ? "null" : csq); write(cs.subSequence(start, end).toString()); return this ; } public PrintStream append (char c) { print(c); return this ; }
public int flush() 1 2 3 4 5 6 7 8 9 10 11 public void flush () { synchronized (this ) { try { ensureOpen(); out.flush(); } catch (IOException x) { trouble = true ; } } }
把数据强制刷新到输出流中。这是一个线程安全的方法,需要注意的是,如果在方法调用过程中出现了异常,那么异常不会被向上抛出,而是将trouble字段标记为true,以此来判定方法执行过程中是否出现了异常。
涉及基础知识点
NIL
参考文献
NIL