PrintStream和PrintWriter的区别和联系

前言

  • 这两者在往文件中写入字符串时,最终都需要通过字符集的映射关系得到对应字节。
  • 但这二者在通过char得到对应若干字节的时机不一样,以new PrintStream( new BufferedOutputStream( new FileOutputStream("BasicFileOutput.out")));new PrintWriter( new BufferedWriter( new FileWriter("BasicFileOutput.out")))为例,前者在存字符串时,从PrintStream传到BufferedOutputStream时就已经是字节了;后者在存字符串时,直到FileWriter真正写入文件时,才将字符转换为字节。
  • 如果PrintStream被设置为autoFlush,那么这些情况flush方法将会自动执行:写入字节数组、任何重载版本的println被调用、一个换行符(char)被写入、一个换行符的字节存储(\n)被写入。
  • 如果PrintWriter被设置为autoFlush,那么这些情况flush方法将会自动执行:println、printf、format方法被调用。
  • 它们都不会抛出IO异常,因为它们在方法内部捕获住了,可以通过checkError()来判断是否发生异常。
  • PrintWriter会使用平台特有的换行符(比如Windows和linux),PrintStream则固定使用\n
  • 总的来说,Reader/Writer相比InputStream/OutputStream算是一种升级,将当初设计得不好的地方进行了优化。

二者的构造器分析

这二者真的很像,你可以去看它们俩的api文档,可以发现它们的构造器和方法几乎一模一样。
你会注意到PrintWriter的构造器中,也可以指定字符集,这可能有点奇怪,因为作为用来装饰的Writer(PrintWriter)来说,它应该不需要关心字符应该怎么对应到字节上去,只需要关心字符就好了呀。

  • PrintWriter有两个构造器都可以指定字符集:PrintWriter(File file, String csn)PrintWriter(String fileName, String csn),但这两个构造器最终都会调用到下面这个构造器,可以看出charset是给装饰器最里层的FileOutputStream使用的,而外层的两个Writer都不用关心。这说明Writer确实不需要关心字符集。
    /* Private constructor */
    private PrintWriter(Charset charset, File file)
        throws FileNotFoundException
    {
        this(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file), charset)),
             false);
    }
  • 其实PrintStream也很low,观察它的构造器可以发现它竟然是靠BufferedWriter来驱动的(这看起来和上面贴的PrintWriter的构造器里的逻辑一样,这里指装饰器的装饰过程):
    /* Private constructors */
    private PrintStream(boolean autoFlush, OutputStream out) {
        super(out);
        this.autoFlush = autoFlush;
        this.charOut = new OutputStreamWriter(this);//往自己身上装饰一层
        this.textOut = new BufferedWriter(charOut);//往自己身上再装饰一层
    }

    private void write(String s) {
        try {
            synchronized (this) {
                ensureOpen();
                textOut.write(s);//先调用最外层的流的write函数
                textOut.flushBuffer();
                charOut.flushBuffer();
                if (autoFlush && (s.indexOf('\n') >= 0))
                    out.flush();
            }
        }
    ...
    }

也就是说,这句new PrintStream( new BufferedOutputStream( new FileOutputStream("BasicFileOutput.out")));实际上会产生五个流对象(装饰器模式会套五层,禁止套娃!)。看write函数,每次写入字符串时,竟然是先通过装饰器最外层的BufferedWriter进行的写入,然后再执行BufferedWriter、OutputStreamWriter的flushBuffer函数把字符转换为字节(像挤牙膏一样,只不过这是从外往里挤),并且将字节弄到this对象里面。

  • PrintStream的三种类型(File、OutputStream、String)的构造器都可以带字符集。
    在这里插入图片描述
    我们随便看一个带字符集的构造器,发现字符集被设置在this的外面一层流上,所以通过OutputStreamWriter向this传递字节数组时,这个字节数组就已经经过了特定字符集charset的encode了:
    private PrintStream(boolean autoFlush, OutputStream out, Charset charset) {
        super(out);
        this.autoFlush = autoFlush;
        this.charOut = new OutputStreamWriter(this, charset);
        this.textOut = new BufferedWriter(charOut);
    }
  • PrintWriter相比PrintStream多了一种类型的构造器(File、OutputStream、String、Writer),那就是它还可以接受一个Writer。
    在这里插入图片描述

二者的方法分析

对比二者的api文档,你会发现前面的方法签名,这二者都是一模一样,除了后面的write方法们,所以我们只看不同的部分。相同的部分占大部分,同学们可以自行查看。

PrintStream的write方法们

从方法的描述也可以看出,这些write方法都是针对byte或者byte[ ]的。

发现有一个write方法是从FilterOutputStream继承而来,因为这个重载版本的write方法PrintStream没有去重写父类方法(下面这两个就是重写了FilterOutputStream了的)。
在这里插入图片描述
FilterOutputStream的write(byte[] b)版本如下:
在这里插入图片描述

PrintWriter的write方法们

从方法的描述也可以看出,这些write方法都是针对char或者string的。
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: PrintStreamPrintWriter都是Java中的输出流类,用于将数据输出到文件或控制台等输出设备。 PrintStream是字节流,可以输出字节、字节数组、字符串等数据类型。它的输出目标可以是文件、控制台等。 PrintWriter是字符流,可以输出字符、字符串等数据类型。它的输出目标可以是文件、控制台等。 两者的使用方式类似,都可以使用printprintln等方法输出数据。但是,PrintWriterPrintStream更加灵活,可以设置输出编码、自动刷新等属性。因此,在处理文本数据时,建议使用PrintWriter。 ### 回答2: PrintStreamPrintWriter都是Java IO库中的输出流类,它们主要用于向外部设备、文件或控制台输出数据。虽然它们的用途类似,但是它们之间也有许多区别。 首先,PrintStream是Java IO流中的一个字节流(byte stream),而PrintWriter是Java IO流中的一个字符流(character stream)。也就是说,PrintStream是用来处理字节数据,而PrintWriter是用来处理字符数据。因此,在处理文本输出时,例如输出字符串时,PrintWriterPrintStream更方便一些。 其次,PrintStream是线程安全的,而PrintWriter则不是。因此,在多线程环境下,PrintStreamPrintWriter更适合使用。 此外,从性能方面来说,PrintStreamPrintWriter更快,因为PrintWriter需要将字符转换为字节再输出,而PrintStream直接输出字节。因此,如果需要处理大量的二进制数据,则使用PrintStream较为合适。 最后需要注意的是,在Java 5之前,PrintStream是用来输出异常信息的首选,因为它可以自动刷新缓冲区。但是,从Java 5开始,PrintWriter也可以自动刷新缓冲区,因此,现在使用PrintWriter来输出异常信息也很常见。 总之,PrintStreamPrintWriter都是用于输出数据的Java IO流类。当需要输出字符数据时,使用PrintWriter更为便捷;当需要输出二进制数据时,使用PrintStream更为高效。在多线程环境下,PrintStreamPrintWriter更加适合使用。 ### 回答3: printstreamprintwriter都是Java IO库中的输出流类,用于将数据写入文件或控制台输出。 printstream主要用于输出二进制数据,比如字节数据或者字节流,它是OutputStream类的子类,可以将数据输出到文件或者控制台,printstream的最大特点是可以使用print()和println()方法来输出数据,这两个方法默认使用平台的默认字符编码将数据转换为字符流输出。printstream还支持设置分隔符,对于数据的分割和处理比较方便,但是不支持Unicode编码。 printwriter主要用于输出文本数据,比如字符数据或者字符流,它是Writer类的子类,可以将文本数据输出到文件或者控制台,printwriter也支持使用print()和println()方法来输出数据,但是不同于printstreamprintwriter默认使用Unicode编码输出数据。printwriter还支持自动刷新输出缓冲区,可以保证输出的数据及时更新到目标设备,比如文件或者屏幕。 总体来说,printstreamprintwriter都是很实用的输出流类,但是需要根据不同的需求选择合适的类,如果需要处理二进制数据,就使用printstream类,如果需要输出文本数据,就使用printwriter类。另外需要特别注意的是,这两个流都是高级流,必须在底层流的基础上使用,否则无法输出数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值