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 == '
') && 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] == '
')
                            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('
') >= 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()函数进行打印。而打印呢,也是一层一层向上调用,回到最开始的输出流。一下一上。

一层层封装,每一层都实现了各自不同的职责,源码设计思想博大精深。

原文地址:https://www.cnblogs.com/lippon/p/14123861.html