Java IO操作之 PrintWriter (超详细解析,使用方法说明)

本文深入探讨了Java中的`PrintWriter`,包括缓冲的作用、`flush()`和`close()`方法的使用,以及`PrintWriter`在字符编码处理中的应用。通过实例分析了`flush()`和`close()`在不同场景下的行为,强调了缓冲区在提高I/O效率中的重要性。此外,还讨论了`PrintWriter`的构造器、`BufferedWriter`的角色,以及`charset`在处理文本流中的作用。最后,文章介绍了`PrintWriter`的常用方法,如`write()`、`append()`和`println()`,并提醒了使用`getBytes()`时的常见误区。
摘要由CSDN通过智能技术生成

PrintWriter

缓冲

Java默认的缓冲区大小是8kb的字节缓冲。也就是8192个字节。

缓冲的作用

应用程序每一次进行I/O操作都需要和设备进行通信,而且通信效率非常低。因此缓冲区的存在,大大提高了I/O效率。

例如写入设备时,每一次通信时,尽可能多的将字节流写入缓冲区,然后等到缓冲区满了(使用flush()或者close()),将数据一次性写入设备。这样一来,避免每写入一个字节都需要与设备进行一次通信的操作。大大提高了效率。

flush()&close()

代码示例1

package flush;
import java.io.PrintWriter;
public class TestFlush{
   
    public static void main(String[] args) {
   
        String s = "Hello World!\n";
        PrintWriter p = null;
        try {
   
            p = new PrintWriter(System.out);
            p.write(s);
            //p.flush();    //1
        }catch(Exception e) {
   
            e.printStackTrace();
        }finally{
   
            //p.close();    //2
        }
    }
}
flush()

当缓冲区没有满时,无法将字节流输出到目的地。flush()的主要作用就是强制将缓冲区存储的剩余字节清出,输出到目的地。

如果目的地是另一个字节流或者字符流,它也会被flush,输出给下一个目的地。所以一旦flush()调用,整个输出链都会被flush,输出给最后的目的地。

如果最终目的地是底层系统提供的抽象的目的地(例如file),flush()只能够保证之前写入缓冲的字节可以作为底层系统去写入的数据源,但是不能保证写入硬盘。

以PrintWriter为例,它的flush()实际上调用的是成员变量out的方法,类型是Writer。

不使用flush()

在代码示例1中,注释了代码行12,Console并不会输出Hello World!。这是因为缓冲区没有满,不会自动输出。

使用flush()

将代码中示例1中注释1去掉,执行,最终Console会输出:Hello World!。这是因为flush()强制清空缓冲区,输出到目的地。

close()

由于这里以PrintWriter为例,带有缓冲区,所以调用close()后首先回去flush缓冲区。而如果是OutputStream,则只会关闭流。

关闭流同时会释放所有与该流关联的系统资源。一个被关闭的流无法在进行相应的I/O操作,而且无法被重新打开。

一旦流最外层包装类调用了close(),整个链中的流对象都会被close。

将代码示例1中注释2去掉,执行,最终Console一样输出了:Hello World!

参考

那些你一直没有搞明白的Java缓冲流细节!

PrintWriter中的缓冲区

构造器

  1. PrintWriter(File file)
  2. PrintWriter(File file, String csn)
  3. PrintWriter(OutputStream out)
  4. PrintWriter(OutputStream out, boolean autoFlush)
  5. PrintWriter(String fileName)
  6. PrintWriter(String fileName, String csn)
  7. PrintWriter(Writer out)
  8. PrintWriter(Writer out, boolean autoFlush)
有无缓冲区

前六种构造器都是通过BufferedWriter包装后作为参数使用最后一种构造器创建PrintWriter对象。所以使用前六种构造器创建的PrintWriter对象一定有缓冲区。

而使用后两种构造器创建的PrintWriter对象有无缓冲区取决于传递的Writer参数自身有无缓冲区。PrintWriter类并不会自动提供缓冲区。

实际上在使用BufferedWriter包装之前,还会使用OutputStreamWriter进行包装。其作用是用指定或者默认的字符集对字节进行编码,转换成字符。而OutputStreamWriter内部通过StreamEncoder实现。在StreamEncoder中内置了8kb的字节缓冲区。而前六种构造器都是这样包装,生成流。

BufferedWriter作用

既然有无BufferedWriter的存在,PrintWriter的前六种创建的对象必定有缓冲区,那么BufferedWriter对于PrintWriter存在的必要性是什么?BufferedWriter主要作用是减少方法调用的次数。这种优化在大量数据输出时尤为明显。

    /**
     * Flushes the stream.
     * @see #checkError()
     */
    public void flush() {
   
        try {
   
            synchronized (lock) {
   
                ensureOpen();
                out.flush();
            }
        }
        catch (IOException x) {
   
            trouble = true;
        }
    }

举个例子,在源代码中可以PrintWriter.flush()实现是去调用成员变量outflush()。而out是通过构造器初始化的,对于前六种构造器out变量的实际类型就是BufferedWriter。所以会直接去调用BufferedWriter.flush()。

而如果使用PrintWriter(OutputStreamWriter(new FileOutputStream(fileName)))来创建对象,保证了对象拥有缓冲区,但是out实际类型就是OutputStreamWriter。一旦调用PrintWriter.flush()后回去调用OutputStreamWriter.flush(),而它内部回去调用StreamEncoder.flush()

构造器参数csn和autoFlush

参数csn用于指定字符集(Charset),然后通过toCharset()转换为Charset对象。

    /**
     * Returns a charset object for the given charset name.
     * @throws NullPointerException          is csn is null
     * @throws UnsupportedEncodingException  if the charset is not supported
     */
    private static Charset toCharset(String csn)
        throws UnsupportedEncodingException
    {
   
        Objects.requireNonNull(csn, "charsetName");
        try {
   
            return Charset.forName(csn);
        } catch (IllegalCharsetNameException|UnsupportedCharsetException unused) {
   
            // UnsupportedEncodingException should be thrown
            throw new UnsupportedEncodingException(csn);
        }
    }

最后使用私有构造器

private PrintWriter(Charset charset, File file)
        throws FileNotFoundException
    {
   
        this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
             false);
    }

创建对象。可以看到最后还是用了最后一种构造器。

参数autoFlush如果为true,println, printf, or format方法会触发缓冲区flush操作。

参考

java.io.PrintWriter

作用

PrintWriter是字符型打印输出流,继承于Writer。可以对字符或者字符序列进行格式化。属于处理流。

PrintWriter优势

除了构造器,PrintWriter其他方法都不会抛出I/O异常。

如果要检查I/O操作过程中是否有错误,可以调用checkError()进行查询。如果流还没有关闭,会进行flush,同时检查有无错误。如果有错误,就会返回true。出错可能在底层流输出时或者格式化时。

常用方法

write()
    public void write(int c) {
   
        try {
   
            synchronized (lock) {
   
                ensureOpen();
                out.write(c);
            }
        }
        catch (InterruptedIOException x) {
   
            Thread.currentThread().interrupt();
        }
        catch (IOException x) {
   
            trouble = true;
        }
    }

write()的实现主要是利用成员变量out.write(),而且文档中说明:write()并不会直接从Writer基类中继承,因为PrintWriter需要压制异常。一旦发生IOException就会将trouble至为true,通过checkError()可以检测有无错误。

append()

该系列重载方法,将指定的字符或者字符序列追加到PrintWriter流中。其内部实现主要是利用了write()方法。

    public PrintWriter append(CharSequence csq) {
   
        if (csq == null)
            write("null");
        else
            write(csq.toString());
        return this;
    }
print()

该系列重载方法,将指定类型的数值转换为String、字符、字符数组、字符序列等参数通过内部write()方法添加到PrintWriter流中。

    public void print(boolean b) {
   
        write(b ? "true" : "false");
    
  • 11
    点赞
  • 46
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值