Java_io体系之PrintStream简介、走进源码及示例——09
PrintStream
1、类功能简介:
字节打印流、功能很强大的一个装饰流、作为FilterInputStream的一个子类、他为底层输出流提供的装饰是可以打印各种java类型的数据、包括对象、这里首次接触的时候会有个误解、觉得PrintStream是将结果打印到控制台的、当然、PrintStream可以将结果打印到控制台上、因为输出到控制台的流也可以用PrintStream来装饰、PrintStream是将数据打印或者说写入到被PrintStream装饰的底层字节输出流中去、另一个其强大之处在于他可以自动刷新、当我们构造PrintStream时指定它自动刷新时、则每次调用它的print或则println方法之后都会及时的将数据写入到底层字节输出流中取、而不用我们手动去调用flush去刷新、最后一个强大体现在可以它可以在构造的时候指定编码、使得字节可以以我们想要的编码形式写入到底层字节输出流中、进而保存到存储介质中。这些强大之后会在接下的有关介绍中来说明。
2、PrintStream API简介:
A:关键字段
boolean autoFlush = false; 是否自动flush、
boolean trouble = false; 执行过程中是否产生异常
Formatter formatter; 用于格式化的对象
BufferedWriter textOut; 用于包装OutputStreamWriter的缓冲字符输出流
OutputStreamWriter charOut; 用于包装当前PrintStream、代表其字符输出流的ows引用、进而用BufferedWriter包装
B:构造方法
//额外先讲两个私有方法、很关键
private PrintStream(boolean autoFlush, OutputStream out); 使用传入的out和指定代表是否自动刷新的autoFlush来创建PrintStream
private init(OutputStreamWriter osw); 用于初始化包装当前流PrintStream的缓冲字符输出流BufferedWriter、内部多了一层将当前PrintStream包装成OutputStreamWriter的转换、这也是其可以指定编码、打印各种java类型的关键
PrintStream(OutputStream out); 使用传入的底层out、默认编码创建pw
PrintStream(OutputStream out, boolean autoFlush); 使用传入的底层out、默认的编码、决定是否自动flushautoFlush字段创建pw
PrintStream(OutputStream out, boolean autoFlush, String encoding); 使用传入的底层out、指定编码、决定是否自动flushautoFlush字段创建pw
PrintStream(String fileName); 使用传入的文件名创建pw
PrintStream(String fileName, String csn); 使用文件名、指定编码创建pw
PrintStream(File file); 使用文件对象、默认编码创建pw
PrintStream(File file, String csn); 使用文件对象、指定编码创建pw
C:一般方法
//也要额外讲两个私有方法、这两个方法是输出各种java数据类型的基础、
private void write(char[] chars); 调用前面实例化的BufferedWriter的write(char[] buff)方法将char[]写入bufferedWriter的字符缓存数组中
private void wirte(String s); 调用前面实例化的BufferedWriter的write(String s)将s写入bufferedWriter的字符缓存数组中
//这些方法看名字就知道什么意识、不再详细一个个列出、源码分析中给出了对应介绍
void close()
boolean checkError()
void setError()
void clearError()
void flush();
void write(byte buf[], int off, int len)
void write(char buf[])
void print(boolean b)
void print(char c)
void print(int i)
void print(double d)
void print(char s[])
void print(String s)
void print(Object obj)
void println()
void println(boolean x)
void println(char x)
void println(int x)
void println(long x)
void println(float x)
void println(double x)
void println(char x[])
void println(String x)
void println(Object x)
PrintStream printf(String format, Object ... args)
PrintStream printf(Locale l, String format, Object ... args)
PrintStream format(String format, Object ... args)
PrintStream format(Locale l, String format, Object ... args)
PrintStream append(CharSequence csq)
PrintStream append(CharSequence csq, int start, int end)
PrintStream append(char c)
3、源码分析:
package com.chy.io.original.code;
import java.io.Closeable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.util.Formatter;
import java.util.IllegalFormatException;
import java.util.Locale;
/**
* 打印流:同样是FilterInputStream的子类、用于装饰传入的InputStream的实现类、out、为其添加打印各种数据表示形式。
* 特点:1)不会产生IOException、2)可以控制其是否自动flush、3)可以指定字符集、
*/
public class PrintStream extends FilterOutputStream implements Appendable, Closeable {
/**
* 是否自动flush() 、默认false。
* 1)自动flush、每次调用其print()、println()、write()时自动调用flush()、
* 2)不自动flush、每次调用其print()、println()、write()不会自动调用flush()、如果想要数据及时的打印、需要手动调用flush()、
*/
private boolean autoFlush = false;
//print会不会产生异常、如果产生异常会被自身捕获、并且将trouble设为true。
private boolean trouble = false;
//用于格式化的对象、
private Formatter formatter;
/**
* Track both the text- and character-output streams, so that their buffers
* can be flushed without flushing the entire stream.
*/
private BufferedWriter textOut;
private OutputStreamWriter charOut;
/**
* 创建一个PrintStream、本身不会自动flush。
*/
public PrintStream(OutputStream out) {
this(out, false);
}
/*
* 此方法是创建PrintStream、其意义是代码的重用。即提取下面两个public构造方法的重复代码。
*/
private PrintStream(boolean autoFlush, OutputStream out)
{
super(out);
if (out == null)
throw new NullPointerException("Null output stream");
this.autoFlush = autoFlush;
}
/*
* 初始化 charOut 、textOut、使PrintStream可使用指定字符集。
*/
private void init(OutputStreamWriter osw) {
this.charOut = osw;
this.textOut = new BufferedWriter(osw);
}
/**
* 创建PrintStream、指定是否自动刷新
*/
public PrintStream(OutputStream out, boolean autoFlush) {
this(autoFlush, out);
init(new OutputStreamWriter(this));
}
/**
* 创建PrintStream、指定是否自动刷新、和编码
*/
public PrintStream(OutputStream out, boolean autoFlush, String encoding)
throws UnsupportedEncodingException
{
this(autoFlush, out);
init(new OutputStreamWriter(this, encoding));
}
/**
* 创建一个基于 fileName指定FileOutputStream的PrintStream、使用默认的编码、不自动刷新。
*/
public PrintStream(String fileName) throws FileNotFoundException {
this(false, new java.io.FileOutputStream(fileName));
init(new OutputStreamWriter(this));
}
/**
* 创建一个基于 fileName指定FileOutputStream的PrintStream、使用指定编码、不自动刷新。
*/
public PrintStream(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
this(false, new java.io.FileOutputStream(fileName));
init(new OutputStreamWriter(this, csn));
}
/**
* 创建一个基于 File指定FileOutputStream的PrintStream、使用默认编码、不自动刷新。
*/
public PrintStream(File file) throws FileNotFoundException {
this(false, new java.io.FileOutputStream(file));
init(new OutputStreamWriter(this));
}
/**
* 创建一个基于 File指定FileOutputStream的PrintStream、使用指定编码、不自动刷新。
*/
public PrintStream(File file, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
this(false, new java.io.FileOutputStream(file));
init(new OutputStreamWriter(this, csn));
}
/** 检测out是否关闭 */
private void ensureOpen() throws IOException {
if (out == null)
throw new IOException("Stream closed");
}
/**
* 刷新out、若有异常、则将trouble设为true。
*/
public void flush() {
synchronized (this) {
try {
ensureOpen();
out.flush();
}
catch (IOException x) {
trouble = true;
}
}
}
private boolean closing = false; /* To avoid recursive closing *///避免关闭时递归
/**
* 关闭流、释放与此流有关的所有资源、包括BufferedWriter、OutputStreamWriter、OutputStream、
*/
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;
}
}
}
/**
* 检测PrintStream操作过程中是否有异常发生。
*/
public boolean checkError() {
if (out != null)
flush();
if (out instanceof java.io.PrintStream) {
PrintStream ps = (PrintStream) out;
return ps.checkError();
}
return trouble;
}
/**
* 设置此流Error信息。
*/
protected void setError() {
trouble = true;
}
/**清空此流Error信息。
*/
protected void clearError() {
trouble = false;
}
/*
* Exception-catching, synchronized output operations,
* which also implement the write() methods of OutputStream
*/
/**
* 将一个字节打印到out中、、
*
*/
public void write(int b) {
try {
synchronized (this) {
ensureOpen();
//调用out.write(b)方法将字节b写入out中
out.write(b);
//如果遇到换行符 "\n"、并且PrintStream在创建时被设置成自动flush、 则调用out.flush、将out中现有的字节写入out目的地中。
if ((b == '\n') && autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
/**
* 将下标冲off开始长度为len的buf[]写入到out中。
*/
public void write(byte buf[], int off, int len) {
try {
synchronized (this) {
ensureOpen();
out.write(buf, off, len);
//如果设置自动flush、则写完就会调用out.flush()将out中内容写到目的地中
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
/**
* 将buf中的所有字节写入到out中、
*/
private void write(char buf[]) {
try {
synchronized (this) {
ensureOpen();
textOut.write(buf);
textOut.flushBuffer();
charOut.flushBuffer();
//如果PrintStream设置成自动刷新、则遇到换行符 "\n"就将out中现有char写入out指定的目的地中。
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;
}
}
//将一个String写入到out中、
private void write(String s) {
try {
synchronized (this) {
ensureOpen();
textOut.write(s);
textOut.flushBuffer();
charOut.flushBuffer();
//如果设置成自动刷新、则遇到换行符就将out中数据写入到out指定的目的地中。
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;
}
}
/* Methods that do not terminate lines */
/**
* 向out中打印一个boolean类型的数据。
*/
public void print(boolean b) {
write(b ? "true" : "false");
}
/**
* 向out中打印一个char类型的数据。
*/
public void print(char c) {
write(String.valueOf(c));
}
/**
* 向out中打印一个int类型的数据。
*/
public void print(int i) {
write(String.valueOf(i));
}
/**
* 向out中打印一个long类型的数据。
*/
public void print(long l) {
write(String.valueOf(l));
}
/**
* 向out中打印一个float类型的数据。
*/
public void print(float f) {
write(String.valueOf(f));
}
/**
* 向out中打印一个double类型的数据。
*/
public void print(double d) {
write(String.valueOf(d));
}
/**
* 向out中打印一个字符数组类型的数据。
*/
public void print(char s[]) {
write(s);
}
/**
* 向out中打印一个String类型的数据。 注意:如果传入的参数s为null、写入out的是"null"字符串。//一会测试一下
*/
public void print(String s) {
if (s == null) {
s = "null";
}
write(s);
}
/**
* 向out中打印一个Object类型的数据。
*/
public void print(Object obj) {
write(String.valueOf(obj));
}
/* Methods that do terminate lines */
/**
* 向out中打印一个换行符。
*/
public void println() {
newLine();
}
/**
* 向out中打印一个boolean类型的数据、并且换行。
*/
public void println(boolean x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个char类型的数据、并且换行。
*/
public void println(char x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个int类型的数据、并且换行。
*/
public void println(int x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个long类型的数据、并且换行。
*/
public void println(long x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个float类型的数据、并且换行。
*/
public void println(float x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个double类型的数据、并且换行。
*/
public void println(double x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个char[]类型的数据、并且换行。
*/
public void println(char x[]) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个String类型的数据、并且换行。
*/
public void println(String x) {
synchronized (this) {
print(x);
newLine();
}
}
/**
* 向out中打印一个Object类型的数据、并且换行。
*/
public void println(Object x) {
String s = String.valueOf(x);
synchronized (this) {
print(s);
newLine();
}
}
/**
* 将“数据args”根据“默认Locale值(区域属性)”按照format格式化,并写入到out中。
*/
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
/**
* 将“数据args”根据“Locale值(区域属性)”按照format格式化,并写入到out中。
*/
public PrintStream printf(Locale l, String format, Object ... args) {
return format(l, format, args);
}
/**
* 将“数据args”根据“Locale值(区域属性)”按照format格式化,并写入到out中。
*
*/
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;
}
/**
* 将“数据args”根据“Locale值(区域属性)”按照format格式化,并写入到out中。
*/
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;
}
/**
* 将“数据args”根据“Locale值(区域属性)”按照format格式化,并写入到out中。
*/
public PrintStream append(CharSequence csq) {
if (csq == null)
print("null");
else
print(csq.toString());
return this;
}
/**
* 将字符序列从start(包括)到end(不包括)的全部字符”追加到out中。
*/
public PrintStream append(CharSequence csq, int start, int end) {
CharSequence cs = (csq == null ? "null" : csq);
write(cs.subSequence(start, end).toString());
return this;
}
/**
* 将char cs追加到out中。
*/
public PrintStream append(char c) {
print(c);
return this;
}
}
4、实例演示:
package com.chy.io.original.test;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import com.chy.io.original.utils.StudentDTO;
public class PrintStreamTest {
private static String str = "陈华应";
public static void main(String[] args) throws IOException{
@SuppressWarnings("unused")
PrintStream ps1 = new PrintStream("D:\\ps.txt");
@SuppressWarnings("unused")
PrintStream ps2 = new PrintStream(new File("D:\\ps.txt"));
/**
* 上边两个方法等价于下面的两个构造方法、
* 带有指定字符集的PrintStream也是一样。
*/
@SuppressWarnings("unused")
PrintStream ps1_1 = new PrintStream(new FileOutputStream("D:\\ps.txt"));
@SuppressWarnings("unused")
PrintStream ps2_1 = new PrintStream(new FileOutputStream(new File("D:\\ps.txt")));
/**
* PrintStream 能够指定编码的原理:PrintStream的构造方法中、内部又用OutputStreamWriter将PrintStream包装了一层、
* OutputstreamWriter构造时可以指定编码、PrintStream方法是调用
*/
PrintStream ps3 = new PrintStream("D:\\ps3.txt" , "GBK");
PrintStream ps4 = new PrintStream(new File("D:\\ps4.txt"),"GBK");
/**
* PrintStream的write方法本质是调用传入的OutputStream类的write方法、只是在指定PrintStream自动刷新时
* 根据不同的字符(换行符 "\n")自动刷新out中的流。
*/
byte[] buf = {0x61, 0x62, 0x63, 0x64};
ps3.write(buf);
ps3.write(buf, 0, 0);
/**
* PrintStream的print()分两类、一个是调用自身的write(char[] c)、另一个是调用自身的write(String s)
* 这两个方法都是调用OutputStreamWriter的write(char[] c,int off, int len)和write(String s, int off, int len)
* 参数不对是因为:PrintStream的两个write方法不是直接调用OutputStreamWriter的上面两个方法、而是通过Writer这个抽象类来调用。
*
* 规律:
* 1)只有print方法的参数时char[] 时调用write(char[] c)其他的都是调用write(String s)、只是把参数转换成String
* 2)println方法是 调用上面的print方法之后加上PrintStream的 newLine()两个方法的组合、newLine()是向out中打印一个换行符。
*/
ps3.println();
ps3.println("============");
StudentDTO student = new StudentDTO(1,"andyChen");
/**
* 只能做简单的记录、因为他是将Object对象obj.toString()这个字符串的值写入out中的。取出来的时候已经不能转换成Object了
* 若要保存java基础类型到out中、可以使用DataOutputStream写入、用DataInputStream读取
* 也可以使用ObjectInputStream读取ObjectOutputStream写入的被序列换的Object对象。后面会有叙述。
*/
ps3.print(student);
//向PrintStream中追加 传入的参数、参数可以是char 、CharSequence的全部和一部分、本质也是调用OutputStreamWriter的write方法。
ps3.append("efg");
/**
* 下面的两个方法是用不同的编码从上面PrintStream写入的文件中读取数据、只拿一个中文字符串来测试
*/
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(new File("D:\\ps4.txt")));
BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:\\ps4.txt")),"GBK"));
ps4.println(str);
byte[] b = new byte[bis.available()];
int n = bis.read(b);
System.out.println("result size: "+ n + " str: " + new String(b));
String result = br.readLine();
System.out.println("result size: "+ n + " str: " + result);
/**
* 结果分析:
* 1)我们写入时指定的编码是GBK、而第一个读取时使用的默认的编码UTF-8、结果乱码
* 2)指定用GBK来读取、正常显示
* 从这里也可以看出、PrintStream提供 的指定编码的功能就是通过OutputStreamWriter来对PrintStream在ps内部进行了装饰、
* 即打印时提供指定编码功能。
*
*/
}
}
总结:
其实理解PrintStream的本质很简单、关心两个:
1、是那个私有的构造方法: PrintStream(Boolean autoFlush,OutputStream out)、他的作用就是调用父类FilterInputStream的构造方法将传入的底层的OutPutStream实现类赋给FilterInputStream中对底层字节输入流的引用out、使得PrintStream对传入流的包装对用户透明化、同时将第一个参数autoFlush的值传递给PrintStream自身的全局变量autoFlush、用来标识此流是否具有自动刷新功能。
2、是那个私有的初始化方法:init(OutputStreamWriter osw)、这个方法是做了两层包装、(1):将自身、也就是当前的PrintStream按照默认或者指定的编码转换成OutputStreamWriter(这个类是将字节输出流转换成字符输出流、转换的时候可以使用指定的编码转换)、(2):因为OutputStreamWriter读取字节的效率比较低、所以用BufferedWriter(字符缓冲输出流)将OutputStreamWriter再包装一层(提高效率)。
上面两个关键点就决定了PrintStream的功能与特点、可以自动刷新、可以指定输出时的编码(本质是通过OutputStreamWriter转换时指定的)可以打印各种java类型的数据(本质也是将字节转换成字符之后、借用BufferedWriter写入到输出流中)、所以:我们可以将PrintStream中的方法分成两类、一类是打印字节的方法write(byte b)、writer(byte[] b, int off, int len)这两个方法是直接调用传入的底层字节输出流来实现、其他分成一类、这一类是将数据转换成字符数组、字符串之后借用BufferedWriter的包装输出到底层字节输出流中。