Java IO源码分析(四)——PrintStream
简介
PrintStream继承于FilterOutputStream,而FilterOutputStream用于封装其他的输出流。
PrintStream用于给其他的输出流封装了一层打印的功能,它内部重载了很多数据类型,这样可以方便打印不同类型的数据。
实际的输出工作,还是调用了被封装的输出流的打印函数。
源码分析
public class PrintStream extends FilterOutputStream
implements Appendable, Closeable
{
// 自动fulsh标志位,如果为真,那么每次执行print、println、write 都会调用flush函数
private final boolean autoFlush;
// 是否有异常产生
private boolean trouble = false;
// 用于格式化对象
private Formatter formatter;
// 用于实现printStream支持的字符集
private BufferedWriter textOut;
private OutputStreamWriter charOut;
private static <T> T requireNonNull(T obj, String message) {
if (obj == null)
throw new NullPointerException(message);
return obj;
}
// 返回字符串对应的字符集对象
private static Charset toCharset(String csn)
throws UnsupportedEncodingException
{
requireNonNull(csn, "charsetName");
try {
return Charset.forName(csn);
} catch (IllegalCharsetNameException|UnsupportedCharsetException unused) {
// UnsupportedEncodingException should be thrown
throw new UnsupportedEncodingException(csn);
}
}
// 设置输出流对象和自动flush模式
private PrintStream(boolean autoFlush, OutputStream out) {
super(out);
this.autoFlush = autoFlush;
this.charOut = new OutputStreamWriter(this);
this.textOut = new BufferedWriter(charOut);
}
// 增加了输出字符集
private PrintStream(boolean autoFlush, OutputStream out, Charset charset) {
super(out);
this.autoFlush = autoFlush;
this.charOut = new OutputStreamWriter(this, charset);
this.textOut = new BufferedWriter(charOut);
}
// 换了参数位置
private PrintStream(boolean autoFlush, Charset charset, OutputStream out)
throws UnsupportedEncodingException
{
this(autoFlush, out, charset);
}
// 只设置了输出流对象,采用默认字符集和不自动flush
public PrintStream(OutputStream out) {
this(out, false);
}
public PrintStream(OutputStream out, boolean autoFlush) {
this(autoFlush, requireNonNull(out, "Null output stream"));
}
// 全部进行初始化构造,采用encoding的字符集
public PrintStream(OutputStream out, boolean autoFlush, String encoding)
throws UnsupportedEncodingException
{
this(autoFlush,
requireNonNull(out, "Null output stream"),
toCharset(encoding));
}
public PrintStream(String fileName) throws FileNotFoundException {
this(false, new FileOutputStream(fileName));
}
public PrintStream(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
// ensure charset is checked before the file is opened
this(false, toCharset(csn), new FileOutputStream(fileName));
}
public PrintStream(File file) throws FileNotFoundException {
this(false, new FileOutputStream(file));
}
public PrintStream(File file, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
// ensure charset is checked before the file is opened
this(false, toCharset(csn), new FileOutputStream(file));
}
// 确保是否存在可用的输出流
private void ensureOpen() throws IOException {
if (out == null)
throw new IOException("Stream closed");
}
// 将输出流缓冲的数据输出
public void flush() {
synchronized (this) {
try {
ensureOpen();
// 调用了输出流的flush函数
out.flush();
}
catch (IOException x) {
// 捕获处理错误
trouble = true;
}
}
}
private boolean closing = false; /* To avoid recursive closing */
// 关闭输出流
public void close() {
synchronized (this) {
if (! closing) {
closing = true;
try {
textOut.close();
out.close();
}
catch (IOException x) {
trouble = true;
}
textOut = null;
charOut = null;
out = null;
}
}
}
// flush数据,再检查错误标志
public boolean checkError() {
if (out != null)
flush();
if (out instanceof java.io.PrintStream) {
PrintStream ps = (PrintStream) out;
return ps.checkError();
}
return trouble;
}
// 设置错误标志位
protected void setError() {
trouble = true;
}
// 清除错误标志位
protected void clearError() {
trouble = false;
}
// 写入一个字节到输出流当中,虽然参数是int,但是输出流中将只会取低八位
public void write(int b) {
try {
synchronized (this) {
ensureOpen();
out.write(b);
// 如果是换行符,或者是自动flush,那么就会将输出流的数据进行输出
if ((b == '\n') && autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
// 写入字节数字组的指定范围
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;
}
}
// 吸写入全部的数组
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;
}
}
// 写入字符串
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;
}
}
// 向输出流中写入一个换行符
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;
}
}
// 打印布尔类型
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);
}
// 打印对象,实际调用了对象的toString函数
public void print(Object obj) {
write(String.valueOf(obj));
}
// 打印换行
public void println() {
newLine();
}
// 下面的Println就是调用了上面的print加上了换行
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();
}
}
// 将args根据默认的Locale的值,按照format格式化,再写入printStream输出流
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
// 将args根据输入的Locale的值,按照format格式化,再写入printStream输出流
public PrintStream printf(Locale l, String format, Object ... args) {
return format(l, format, args);
}
// 根据“默认的Locale值(区域属性)”来格式化数据
public PrintStream format(String format, Object ... args) {
try {
synchronized (this) {
ensureOpen();
if ((formatter == null)
|| (formatter.locale() != Locale.getDefault()))
formatter = new Formatter((Appendable) this);
formatter.format(Locale.getDefault(), format, args);
}
} catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
} catch (IOException x) {
trouble = true;
}
return this;
}
// 根据“Locale值(区域属性)”来格式化数据
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;
}
// 将字符序列的全部字符添加到输出流中
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;
}
}
总结
PrintStream源码中,大多数都是给传入的输出流做了一层封装,底层还是使用的传入输出流的函数。
做些这些封装的目的就是更好的打印我们想要的数据,比如字符串是空的,我们就应该打印出一个null,而不是什么都不答应出来。
在我们最开始学Java的时候,System.out.println("hello world");
中就是使用了PrintStream进行打印。也就是将“输出到屏幕“的输出流经过文件输出流、缓冲输出流,最后装入到这里的打印输出流,最后调用这里println()
函数进行打印。而打印呢,也是一层一层向上调用,回到最开始的输出流。一下一上。
一层层封装,每一层都实现了各自不同的职责,源码设计思想博大精深。