[重学Java基础][JavaIO流][Part.8] 打印字符输出流
PrintWriter
概述
PrintWriter缓冲字符数组输出流,继承了所有字符输出流的超类Writer类,用于向文本对象输入字符内容
很明显是一个处理流 用于处理字符数据 代理真正的节点流输出到数据汇 是一个非常实用的输出流
系统的System.out方法就是调用了内置的PrintWriter流
官方注释
格式化打印对象到文本输出流。本类实现了所有PrintStream也包含的方法。
但本类不包含写入字节流的方法,写入字节流可以使用无编码字节流。
与PrintStream不同,只有代理println,printf,format方法时且自动刷新
是启用状态,才会起效,而不是在一个新的换行符被输出时进行自动刷新。
这些方法使用平台自己的换行符而不是普通的换行符
此类的所有方法不会跑出I/O异常,除了其中的一些构造方法。
终端可以查询是否是一些代理方法抛出的受检异常
源码分析
成员属性
代理的节点输出流
protected Writer out;
是否自动刷新
private final boolean autoFlush;
是否有抛出异常 官方注释提到PrintWriter不抛出异常
所以如果发生异常只是 此标志位置为false
private boolean trouble = false;
格式化参数
private Formatter formatter;
输出流 适配器模式
private PrintStream psOut = null;
成员方法
构造方法
创建一个不会自动刷新的PrintWriter 对象
public PrintWriter (Writer out) {
this(out, false);
}
创建一个PrintWriter 对象,并设置是否自动刷新
如果自动刷新则 println,printf,format方法将会自动刷新输出流
public PrintWriter(Writer out,
boolean autoFlush) {
super(out);
this.out = out;
this.autoFlush = autoFlush;
}
创建一个不会自动刷新的PrintWriter对象
调用OutputStream作为真正的输出节点流
可以看到此方法和第一个方法的不同之处就是入参是OutputStream
内部调用了下面的方法
public PrintWriter(OutputStream out) {
this(out, false);
}
创建一个PrintWriter对象,并设置是否自动刷新
调用缓冲流包裹的OutputStreamWriter转换器对象输出到真正的节点流中
编码采用默认字符集
public PrintWriter(OutputStream out, boolean autoFlush) {
this(new BufferedWriter(new OutputStreamWriter(out)), autoFlush);
// 为了传播异常内容 保存输出流
if (out instanceof java.io.PrintStream) {
psOut = (PrintStream) out;
}
}
创建一个PrintWriter对象输出数据到指定文件名的文件,并不自动刷新
内部调用缓冲流包裹的OutputStreamWriter转换器对象输出到真正的文件输出节点流中
内部调用了上面的第二个构造方法
public PrintWriter(String fileName) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName))),
false);
}
创建一个PrintWriter对象输出数据到指定文件名的文件,并不自动刷新,
根据编码名使用指定的编码方式
内部调用了下面的私有构造方法
public PrintWriter(String fileName, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
this(toCharset(csn), new File(fileName));
}
私有构造方法
内部使用缓冲流包裹的OutputStreamWriter转换器对象输出到真正的文件输出节点流中
并根据charset字符集指定编码方式
private PrintWriter(Charset charset, File file)
throws FileNotFoundException
{
this(new BufferedWriter(new OutputStreamWriter(
new FileOutputStream(file), charset)),
false);
}
创建一个PrintWriter对象输出数据到指定文件对象,并不自动刷新
public PrintWriter(File file) throws FileNotFoundException {
this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file))),
false);
}
创建一个PrintWriter对象输出数据到指定文件对象,并不自动刷新
且使用指定的编码字符集
public PrintWriter(File file, String csn)
throws FileNotFoundException, UnsupportedEncodingException
{
this(toCharset(csn), file);
}
其他
确认流是否打开
private void ensureOpen() throws IOException {
if (out == null)
throw new IOException("Stream closed");
}
刷新流内容方法
内部捕捉了IOException
所以PrintWriter不会抛出IOException
只会改变成员属性troubled的标志位
public void flush() {
try {
synchronized (lock) {
ensureOpen();
out.flush();
}
}
catch (IOException x) {
trouble = true;
}
}
检查流错误方法
同时刷新流内容,返回错误标志位
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;
}
直接设置错误标志位方法
protected void setError() {
trouble = true;
}
清除错误标志位方法
protected void clearError() {
trouble = false;
}
写入数据方法
这是一个阻塞方法 并且是线程安全的
所以捕捉了受检异常InterruptedIOException
并重新设置了线程中断以方便上级方法检测
public void write(int c) {
try {
synchronized (lock) {
ensureOpen();
out.write(c);
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
其他入参不同的重写写入方法 方法体大同小异
public void write(char buf[], int off, int len)
{……}
public void write(char buf[])
{……}
public void write(String s, int off, int len)
{……}
新一行方法 调用println换行实际上调用的就是此方法
实际上是写入了对应系统的换行符System.lineSeparator()
private void newLine() {
try {
synchronized (lock) {
ensureOpen();
out.write(System.lineSeparator());
if (autoFlush)
out.flush();
}
}
catch (InterruptedIOException x) {
Thread.currentThread().interrupt();
}
catch (IOException x) {
trouble = true;
}
}
打印输出方法 入参是布尔型
然而实际上输出时是转换为了字符型了
并且采用的是默认编码
public void print(boolean b) {
write(String.valueOf(b));
}
其他重载的打印输出方法 大同小异
我们平时调用的系统print方法就是这些方法
不是字符型的全部被转为字符型输出了
public void print(boolean b) {
this.write(String.valueOf(b));
}
public void print(char c) {
this.write(c);
}
public void print(int i) {
this.write(String.valueOf(i));
}
public void print(long l) {
this.write(String.valueOf(l));
}
public void print(float f) {
this.write(String.valueOf(f));
}
public void print(double d) {
this.write(String.valueOf(d));
}
public void print(char[] s) {
this.write(s);
}
public void print(String s) {
this.write(String.valueOf(s));
}
public void print(Object obj) {
this.write(String.valueOf(obj));
}
打印输出并换行方法
其实就是输出完后 追加调用了println(newline)方法
public void println(boolean x) {
Object var2 = this.lock;
synchronized(this.lock) {
this.print(x);
this.println();
}
}
重载的都大同小异
public void println(char x)
public void println(int x)
public void println(long x)
public void println(float x)
public void println(double x)
public void println(char[] x)
字符格式化打印输出方法
入参是格式化参数format 和被格式化的字符内容 是个可变数组Object ... args
字符内容和格式化参数不必一一对应 但不能字符内容少于格式化参数
具体可以看printf的详解
此方法内部调用的是format方法
public PrintWriter printf(String format, Object ... args) {
return format(format, args);
}
格式化打印方法 String format为格式化参数 可变数组Object ... args为需要格式化的字符内容
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;
}
增加了一个可指定字符区位Locale的参数 可以根据指定的区位习惯的格式进行输出
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;
}
追加打印方法
public PrintWriter append(CharSequence csq) {
write(String.valueOf(csq));
return this;
}
追加打印方法 打印字符或字符序列到输出流中
和print方法不同的是返回了本身PrintWriter 对象
可以链式调用
public PrintWriter append(CharSequence csq, int start, int end) {
if (csq == null) csq = "null";
return append(csq.subSequence(start, end));
}
public PrintWriter append(char c) {
write(c);
return this;
}
代码示例
构造一个PrintWriter 对象 并使用write方法输出 必须手动flush
char[] chars={'爱', '吃', '拉', '面', '的','小','泉','同','学' };
String str="爱吃拉面的小泉同学";
File file=new File("d:\\animation.txt");
PrintWriter pw=new PrintWriter(new FileOutputStream(file));
pw.write(chars);
pw.write(str);
pw.flush();
PrintWriter pw2=new PrintWriter("d:\\animation2.txt");
pw2.write("王牌御史");
pw2.flush();
运行结果
爱吃拉面的小泉同学爱吃拉面的小泉同学
王牌御史
使用print方法输出
PrintWriter pw = new PrintWriter("d:\\pw.txt");
pw.println("这是一个换行输出");
// 将字符'A'对应ASCII码写入到输出流中,等于输出'A'
pw.write(0x41);
// 将字符串"65"写入到输出流中。
pw.append('B').append("CDEF");
pw.println();
String str = "今天是";
int mouth=5;
int day=1;
pw.printf("%s%d.%d", str, mouth,day);
pw.flush();
运行结果
这是一个换行输出
ABCDEF
今天是5.1