字符输出流

昨天我们学习了字符输入流的学习,而字符流中还剩下字符输出流等待着我们进行解锁。

所以今天我们准备对字符输出流进行一个详细的了解吧!

字符输出流的父类(Writer)

Writer是所有的输出字符流的父类,它是一个抽象类。主要作用是可以将指定的字符信息写出到目的地。它定义了字符输出流的基本共性功能方法。

体系结构

  • OutputStreamWriter是一个连接字节流和字符流的桥梁。
  • FilterWriter 是所有自定义具体装饰流的父类。
  • BufferedWriter 字符缓冲流,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
  • FileWriter 是写出字符到文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

Writer中的方法

与字节输入流的用法完全类似,输入流后续继承的子类都有这些方法,需要writer()方法进行读取文件,使用close()方法关闭资源。

FileWriter

1. 构造方法

  • public FileWriter(File file):创建一个新的 FileWriter,给定要读取的File对象。
  • public FileWriter(File file, boolean append):创建一个新的 FileWriter,可以追加数据。
  • public FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。
  • public FileWriter(String fileName, boolean append):创建一个新的 FileWriter,可以追加数据。

演示如下:

public class WriterTest {
    public static void main(String[] args) throws IOException {
        //public FileWriter(File file)
        // 使用File对象创建流对象
        File file = new File("e:\\demo\\bjx.txt");
        FileWriter fw = new FileWriter(file);


        //public FileWriter(String fileName)
        // 使用文件名称创建流对象
        FileWriter fw2 = new FileWriter("e:\\demo\\bjx.txt");
    }
}
复制代码

可以发现,程序运行后,在文件中自动帮我们生成了bjx.txt文件。跟字节输出流一样。

追加数据的构造方法就不带大家演示了,上面也有专门演示过了。嘿嘿,不要说我懒,因为我相信你学了上面的字节输出流的,这里你肯定懂了,对吧。大家都这么聪明,学习Java的天才,一点就懂的啦。

2. 写出数据

既然能创建出来FileWriter对象了,那我们就具体实践下写出数据吧。

  1. public void write(int c):每次可以写出一个字符数据。

    public class WriterTest2 {
        public static void main(String[] args) throws IOException {
            // 使用文件名称创建流对象
            FileWriter fw = new FileWriter("e:\\demo\\bjx.txt");
    
            // 写出数据
            fw.write(97);//存入到文件中应为:a
            fw.write(26666);//存入到文件中应为:株
    
            /*
            【特别注意】与字节输出流FileOutputStream不同的是。
          	 如果不关闭资源,数据只是保存到缓冲区,并未保存到文件。
            */
            //fw.close();
        }
    }
    复制代码

    程序运行后,可以看到如果我没有注释掉fw.close()即有关闭资源的情况下,是可以保存到文件中,但是呢,如果没有关闭资源,它则没有保存到文件。这是为什么呢?

    • 一个字符 = 两个字节,而文本中数据存储的基本单位是字节。是不是字节输出流就可以直接写进去呢,而字符流,是没办法直接存储进去。

    • 而字符流是通过缓冲区的方式进行存储的,如果没有使用方法是无法将缓冲区的内容保存到文件中的。

      可以通过write()方法一步一步点进去看源码,可以发现他存进去了一个CharBuffer缓冲区中。

    • 因为内置缓冲区的原因,如果不关闭输出流,无法写出字符到文件中。而如果一旦关闭流资源后,我们就没办法再写入数据,所以如果我我们既想写出数据,又想继续使用流,就需要flush() 方法了。

      看源码可以发现,flush() 方法和close() 方法最终都会调用StreamEncoder中的writeBytes()

      flush() :刷新缓冲区,流对象可以继续使用。 close() :先刷新缓冲区,然后通知系统释放资源,流对象不可以再被使用了。

    最终可以改进为:

    public class WriterTest3 {
        public static void main(String[] args) throws IOException {
            // 使用文件名称创建流对象
            FileWriter fw = new FileWriter("e:\\demo\\bjx.txt");
    
            // 写出数据
            //public void write(String str)
            fw.write("北极星");
            //fw.write('叫我');//字符不能多写,而字符串可以
            fw.flush();
            fw.write("叫我");
            fw.flush();//一般每写入一个就刷新下缓冲区。
            // 如果你在最后写,如果这过程中出现异常等错误,是不是写入了呢?
            fw.write("视频聊天啦");
            fw.flush();
    
            fw.close();//就算我注释掉,也是有加入数据了的
            //fw.write("能成功吗"); // 当我们要执行这里后,就会【报错】java.io.IOException: Stream closed
            //fw.close();
        }
    }
    复制代码

    可以发现:

    • 我们可以通过flush()方法实现写入数据到文件中。
    • 即便是flush()方法写出了数据,操作的最后还是要调用close() 方法,释放系统资源。
  2. puiblic write(char[] cbuf)public write(char[] cbuf, int off, int len) :每次可以写出字符数组中的数据。这两个功能用法和字节输出流类似,直接演示看下吧:

    public class WriterTest4 {
        public static void main(String[] args) throws IOException {
            // 使用文件名称创建流对象
            FileWriter fw = new FileWriter("e:\\demo\\bjx.txt");
    
            // 字符串转换为字节数组
            char[] chars = "北极星".toCharArray();
    
            // 写出字符数组
            //puiblic write(char[] cbuf)
            fw.write(chars); // 北极星
            fw.flush();
    
            //写出从索引0开始,2个字节
            //public write(char[] cbuf, int off, int len)
            fw.write(chars,0,2);//北极
            fw.flush();
    
            fw.close();
        }
    }
    
    程序执行后,bjx.txt的文件信息为:
    北极星北极
    复制代码

3. 实现换行

既然在字节输出流中,我们可以实现换行的功能,那我们字符流可以吗?答案当然可以的啦,跟字节输出流一样的操作。来看看吧:

public class WriterTest5 {
    public static void main(String[] args) throws IOException {
        // 使用文件名称创建流对象
        FileWriter fw = new FileWriter("e:\\demo\\bjx.txt");

        fw.write("北极星叫");
        fw.flush();
        // 写出换行
        fw.write("\r\n");
        fw.flush();
        fw.write("我一起看流星雨");
        fw.flush();

        fw.close();
    }
}

程序执行后,bjx.txt的文件信息为:
北极星叫
我一起看流星雨
复制代码

复制文本案例

复制的原理已经在字节流的时候讲解过了。现在直接上代码给各位看官瞧瞧如何:

public class CopyTxtTest {
    public static void main(String[] args) throws IOException {
        // 1. 创建输入和输出流对象。
        // 1.1 指定要读文件的路径。
        FileReader fr = new FileReader("E:\\demo\\青春.txt");
        // 1.2 指定目的地文件的路径。
        FileWriter fw = new FileWriter("E:\\demoCopy\\青春.txt");

        // 2.读写数据
        // 2.1 定义字符数组
        char[] chs = new char[1024];
        int len;
        // 2.2 循环读取出数据
        while ((len = fr.read(chs)) != -1) {
            // 2.3 将得到的字节数据写出
            fw.write(chs, 0, len);
            fw.flush();
        }

        // 3.关闭所有资源
        fw.close();
        fr.close();

    }
}
复制代码

程序执行后,看下文件的信息:

小结

字符流,只能操作文本文件,不能操作图片,视频等非文本文件。当我们单纯读或者写文本文件时可以使用字符流更高效的操作文本文件,而其他情况使用字节流。

  • 两者的区别:
    • 读写单位不同:字节流以字节(8bit)为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
    • 处理对象不同:字节流能处理所有类型的数据(如图片、avi等),而字符流只能处理字符类型的数据。

总结

相信各位看官都对IO流中字符输出流类有了一定了解,期待等待下一章的IO高级流教学吧!

当然还有很多流等着下次一起看吧!欢迎期待下一章的到来!

学到这里,今天的世界打烊了,晚安!虽然这篇文章完结了,但是我还在,永不完结。我会努力保持写文章。来日方长,何惧车遥马慢!

感谢各位看到这里!愿你韶华不负,青春无悔!

注: 如果文章有任何错误和建议,请各位大佬尽情留言!如果这篇文章对你也有所帮助,希望可爱亲切的您给个三连关注下,非常感谢啦!