day09【字节流、字符流】

day09【字节流、字符流】

主要内容

  • IO流
  • 字节流
  • 字符流
  • 异常处理
  • Properties

教学目标

  • 能够说出IO流的分类和功能
  • 能够使用字节输出流写出数据到文件
  • 能够使用字节输入流读取数据到程序
  • 能够理解读取数据read(byte[])方法的原理
  • 能够使用字节流完成文件的复制
  • 能够使用FileWirter写数据到文件
  • 能够说出FileWriter中关闭和刷新方法的区别
  • 能够使用FileWriter写数据的5个方法
  • 能够使用FileWriter写数据实现换行和追加写
  • 能够使用FileReader读数据
  • 能够使用FileReader读数据一次一个字符数组
  • 能够使用Properties的load方法加载文件中配置信息

第一章 IO概述

1.1 什么是IO

目标

  • 能够说IO流的作用以及分类。

File类它只能操作文件或文件夹,并不能操作文件中的数据。而要操作文件中的数据,则必须使用Java中提供的IO流技术完成。

  • IO:它们的功能是用于操作文件中的数据的。

    • input : 给文件中写数据。
    • output :从文件中读数据。
  • 我们把这种数据读写的传输,看做是一种数据的流动,按照流动的方向,以内存为基准,分为

    • 输入流 :把数据从其他设备上读取到程序中的流。 也称为读操作。
    • 输出流 :把数据从程序 中写出到其他设备上的流。 也称为写操作。
  • 根据数据的类型又将流分为:

  • 字节流 :以字节为单位,读写数据的流。

    • 数据在持久设备上都是以二进制形式保存的。二进制也就是字节数据。因此Java中就给出了字节流可以直接操作字节数据。
  • 字符流 :以字符为单位,读写数据的流。

    • 数据在设备上是以二进制形式表示,但是有些二进制合并在一起可以表示一些字符数据。因此java中又给出了字符流可以直接操作字符数据。

小结

在流中的顶层父类中,规定了输入流或输出流的基本操作行为。流的顶层父类:

  • 字节输入流 :java.io.InputStream。
  • 字节输出流 :java.io.OutputStream。
  • 字符输入流 :java.io.Reader。
  • 字符输出流 :java.io.Writer。

第二章 字节流

一切皆为字节

一切文件数据(文本、图片、视频等)在存储时,都是以二进制的形式保存,也就是以字节形式保存的,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制(字节)数据。

2.1 字节输出流【OutputStream】

目标

  • 能够明确字节输出流的作用和常用方法的具体作用。

  • java.io.OutputStream抽象类是表示字节输出流的所有类的超类,将指定的字节信息写出到目的地。它定义了字节输出流的基本共性功能方法。

  • 成员方法:

    • public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
      • 在使用流操作文件时,会占用内存资源。此方法可以释放资源,提高程序效率。
    • public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
    • public void write( byte[] b ):将 b.length字节从指定的字节数组写入此输出流。
    • public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
    • public abstract void write(int b) :将指定的字节输出流。
      • 注意:字节流一次只能写出一个字节数据。如果超过一个字节,则只能写出最低的8个二进制数据。

在IO包下的大部分方法都存在异常,代码演示时一般为了方便程序阅读,都会直接声明异常不捕获。

2.2 FileOutputStream类

目标

  • 能够使用FileOutputStream将数据写到指定的文件中。
  • java.io.FileOutputStream 类是文件输出流,用于将数据写出到指定的文件中。

构造方法

  • public FileOutputStream( File file ):创建文件输出流以写入由指定的 File对象表示的文件。
  • public FileOutputStream(String name): 创建文件输出流以指定的名称写入文件。
    • 当创建一个流对象时,必须传入一个文件路径。该路径下,如果没有这个文件,会创建该文件。如果有这个文件,会清空这个文件的数据。

步骤

  1. 创建FileOutputStream字节输出流对象,并指定需要操作的文件路径。
  2. 调用write()方法,分写写出字节数据 和 字节数组数据。
  3. 关闭流对象。

实现

public class Demo {
    /*
        void write(int b) 将指定的字节写入此文件输出流。
        注意:
            因为输出的是字节数据,而1个字节只能保存8个二进制数据
            如果输出的数字过大,最终只会将当前数字的最低的8个二进制保写到文件中
     */
    public static void main(String[] args) throws IOException {

        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("e:\\demo\\1.txt");
        fos.write(97);

        // 超出一个字节,则只保存最低的8个二进制数位
        fos.write(353);

        // 关闭流
        fos.close();
    }
}

写出字节数组:write(byte[] b),每次可以写出数组中的数据,代码使用演示:

public class Demo {
    /*
        void  write(byte[] b) 将 b.length字节从指定的字节数组写入此文件输出流。 
        将字节数组中的数据,写到指定的文件中
    */
    public static void main(String[] args) throws IOException {

        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("e:\\demo\\1.txt");
        // 创建字节数组
        byte[] buff = {97,98,99,100};
        // 将数组中的所有数据写到文件中
        fos.write(buff);

        // 将字符串数据转为字节数组,写到指定文件中
        fos.write( "明天又可以休息了,欧耶".getBytes() );

        // 关闭流
        fos.close();
    }
}

写出指定长度字节数组:write(byte[] b, int off, int len) ,每次写出从off索引开始,len个字节,代码使用演示:

public class Demo {
    /*
        write(byte[] b, int off, int len)
        byte[] b:需要写出数据的数据
        int off:需要写出数据的起始下标
        int len:需要写出的数据的个数
    */
    public static void main(String[] args) throws IOException {

        // 创建字节输出流对象
        FileOutputStream fos = new FileOutputStream("e:\\demo\\1.txt");
        // 创建字节数组
        byte[] buff = {97,98,99,100};
        // 将数组中的所有数据写到文件中
        fos.write(buff,1,3);

        // 关闭流
        fos.close();
    }
}

小结

FileOutputStream类是用于给指定文件中写字节数据的类。构造方法中指定需要操作的文件对象或文件路径。如果当前文件不存在则创建当前文件,如果当前文件存在则覆盖原来文件中的内容。因为是字节输出流,所以只能写出一个字节的数据,如果超过一个字节则只能写出最低的8个二进制数据。

2.2.1 数据追加续,写出换行

目标

  • 能够使用字节输出流写出追加的数据和换行写出数据。

经过以上的演示,每次程序运行,创建输出流对象,都会清空目标文件中的数据。如何保留目标文件中数据,还能继续添加新数据呢? 可以使用构造方法中重载的方法:

  • public FileOutputStream(File file, boolean append): 创建文件输出流以写入由指定的 File对象表示的文件。
    • boolean append: 默认值为false。指定true值可以追加数据。
  • public FileOutputStream(String name, boolean append): 创建文件输出流以指定的名称写入文件。
    • boolean append: 默认值为false。指定true值可以追加数据。
  • Windows系统里,换行符号是\r\n 。使用writer()方法写出"\r\n"即可完成换行输出。

步骤

  1. 创建FileOutputStream对象,并指定文件路径。并将append参数置位true。
  2. 调用writer()方法写出字节数据。并追加换行。
  3. 关闭流对象。释放资源。
  4. 多次启动程序查看数据是否追加以及换行。

实现

public class Demo {
    /*
        FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。
        FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。
        boolean append: 如果结果为true,则给当前文件中追加内容,不会删除文件中原来的数据
        默认值为false。
        
        Windows系统中的换行符号为: \r\n
    */
    public static void main(String[] args) throws IOException {
		// 创建字节输出流对象 , 并将append参数置位true。
        FileOutputStream fos = new FileOutputStream("e:\\demo\\1.txt", true );
        // 创建字节数组
        byte[] buff = {97,98,99,100,101,102};
        // 将数组中的所有数据写到文件中,没写一个换一行
        for( byte bu : buff ) {
            fos.write(bu);
            fos.write("\r\n".getBytes());
        }

        fos.write("床前明月光\r\n疑是地上霜\r\n举头望明月\r\n低头思故乡\r\n".getBytes());
        // 关闭流
        fos.close();
    }
}

小结

字节输出流的构造方法中有一个boolean append参数,只要将值置位true,即可完成数据的追加。而使用write("\r\n")即可完成换行输出。

  • 回车符\r和换行符\n :
  • 回车符:回到一行的开头(return)。
  • 换行符:下一行(newline)。
  • 系统中的换行:
    • Windows系统里,每行结尾是 回车+换行 ,即\r\n;
    • Unix系统里,每行结尾只有 换行 ,即\n;
    • Mac系统里,每行结尾是 回车 ,即\r。从 Mac OS X开始与Linux统一。

2.3 字节输入流【InputStream】

目标

  • 能够理解字节输入流的作用,和常用成员方法的作用。
  • java.io.InputStream抽象类是表示字节输入流的所有类的超类,可以读取字节信息到内存中。它定义了字节输入流的基本共性功能方法。
    • public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
      • 在使用流操作文件时,会占用内存资源。此方法可以释放资源,提高程序效率。
    • public abstract int read(): 从输入流读取数据的下一个字节。
    • public int read( byte[] b ): 从输入流中读取一些字节数,并将它们存储到字节数组 b中 。
      • byte[] b:保存字节流读取到的字节数据,空间一般开辟1024的整倍数。
      • int:输入流读取到的真实有效的字节数。
    • 注意:当读取到的结果为 -1时,则表示文件中的数据已经全部读取结束。

小贴士:

close方法,当完成流的操作时,必须调用此方法,释放系统资源。

2.4 FileInputStream类

目标

  • 能够使用FileInputStream类通过两种模板代码读取文件中的数据。
  • java.io.FileInputStream类是文件输入流,从文件中读取字节。

构造方法

  • FileInputStream( File file ): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的 File对象 file命名。
    • File file : 输入流读取数据的目标文件,不存在则抛出FileNotFoundException 。
  • FileInputStream(String name): 通过打开与实际文件的连接来创建一个 FileInputStream ,该文件由文件系统中的路径名 name命名。
    • String name:输入流读取数据的路径,该路径下,如果没有该文件,会抛出FileNotFoundException 。

步骤

  1. 创建FileInputStream输入流对象,并指定文件路径。
  2. 使用一次读取一个字节的模板代码读取文件中的数据。
    1. 定义变量空间 int ch = 0; 用来记录保存读取到的字节数据。
    2. 通过read()循环读取文件中的数据赋值给ch变量,如果 != -1则表示文件中依然有数据,继续读取,否则结束读取。
    3. 打印输出读取到的字节数据。
  3. 使用一次读取多个字节的模板代码读取文件中的数据。
    1. 定义byte[]数组,保存读取到的字节数据。开辟空间建议为1024的整倍数。
    2. 定义变量 int len = 0; 用来记录读取到的有效字节个数。
    3. 通过read(byte[] b)方法循环读取文件中的数据赋值给len变量,如果 != -1则表示文件中依然有数据,继续读取,否则结束读取。

实现

读取字节数据

  1. 读取字节:read方法,每次可以读取一个字节的数据,提升为int类型,读取到文件末尾,返回-1,代码使用演示:

    public class Demo {
    /*
    int read() 从该输入流读取一个字节的数据。
    每次读取一个字节数据,提升为int类型,读取到文件末位返回 -1
    */
    public static void main(String[] args) throws IOException {
    // 使创建字节输入流对象
    FileInputStream fis = new FileInputStream(“e:\demo\1.txt”);
    int read1 = fis.read();
    System.out.println((char)read1);
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());
    System.out.println((char)fis.read());// 一个回车相当于两个字节

         // 关闭流对象
         fis.close();
     }
    

    }

  2. 一次读取一个字节模板代码演示:

    public class Demo {
    public static void main(String[] args) throws IOException {
    // 使创建字节输入流对象
    FileInputStream fis = new FileInputStream(“e:\demo\1.txt”);
    /*
    一次读取了两个字节数据
    // 使用循环读取的方式
    while( fis.read() != -1 ) {
    System.out.println((char)fis.read());
    }*/
    // 定义变量,用来记录每次读取的字节数据
    int ch = 0;
    while( ( ch = fis.read() ) != -1 ) {
    System.out.println( (char)ch );
    }

         // 关闭流对象
         fis.close();
     }
    

    }

小贴士:

  1. 虽然读取了一个字节,但是会自动提升为int类型。

  2. 流操作完毕后,必须释放系统资源,调用close方法,千万记得。

  3. 使用字节数组一次读取多个字节:read(byte[] b),每次读取b的长度个字节到数组中,返回读取到的有效字节个数,读取到末尾时,返回-1 ,代码使用演示:

    public class Demo {
    /*
    int read(byte[] b) 从该输入流读取最多 b.length个字节的数据到一个字节数组。
    将读取到的字节数据保存到数组b中,并返回当前保存的具体有效字节个数
    byte[] b:读取到的字节数据保存到此数组。 建议开辟的空间为1024的倍数
    返回值int: 记录数组中真实读取到的字节个数
    */
    public static void main(String[] args) throws IOException {
    // 使创建字节输入流对象
    FileInputStream fis = new FileInputStream(“e:\demo\1.txt”);

         // 定义数组,用来保存读取到的字节个数
         byte[] buff = new byte[1024];
         // 定义变量,用来记录数组中真实读取到的字节个数
         int len = 0;
         while( (len = fis.read( buff ) ) != -1 ) {
             /*
                 len = fis.read( buff ):将从文件中读取的有效字节个数赋值给len空间
                 new String(buff , 0 ,len):
                     buff :将buff数组中的数据转为字符串数据
                     0 :从buff数组中的0下标开始转换
                     len:len记录的是真实读取到的字节个数,只需要转换到len个即可。后续的都是默认数据
              */
             System.out.println(new String(buff,0,len));
         }
    
         // 关闭流对象
         fis.close();
     }
    

    }

小结

FileInputStream类主要是用于读取文件中数据的类。读取文件有模板代码。需要掌握两种读取的模板代码。

小贴士:

使用数组读取,每次读取多个字节,减少了系统间的IO操作次数,从而提高了读写的效率,建议开发中使用。

2.5 字节流练习:图片复制

目标

  • 能够完成图片文件的复制。

步骤

复制原理图解

实现

复制图片文件,代码使用演示:

public class Demo {
    /*
        文件复制: 从硬盘上将指定的文件读取到程序中,然后将读取到的数据在写到指定的文件中
        1、创建输入流对象,读取需要复制的文件
        2、创建输出流对象,将读取到的数据写到指定的文件
     */
    public static void main(String[] args) throws IOException {
        // 使创建字节输入流对象  用来读取需要复制的文件
        FileInputStream fis = new FileInputStream("e:\\demo\\1.jpg");

        // 创建输出流对象,用来将读取到的数据写到指定的文件中
        FileOutputStream fos = new FileOutputStream("e:\\demo\\11.jpg");
        // 定义数组,用来保存读取到的字节个数
        byte[] buff = new byte[1024];
        // 定义变量,用来记录数组中真实读取到的字节个数
        int len = 0;
        while( (len = fis.read( buff ) ) != -1 ) {
           // 将读取到的数据,写到指定文件中
            fos.write( buff, 0 ,len );
        }

        // 关闭流对象
        fis.close();
        fos.close();
    }
}

小结

文件的复制其实就是将一个文件读取到程序中,然后在写入到另外一个文件的过程。

小贴士:

流的关闭原则:先开后关,后开先关。

第三章 字符流

3.1 为什么使用字符流

目标

  • 能够理解为什么要使用字符流。

当使用字节流读取文本文件时,一次是读取一个字节数据。而在计算机中有时多个字节组合在一起会表示一个字符数据。所以在使用字节流读取文本数据时,如果读取到中文字符,可能不会显示完整的字符,那是因为一个中文字符需要多个字节存储。因此我们不应该一个一个字节的读取,而是应该把表示汉字的几个字节一起读取,然后把这些字节合并在一起表示一个汉字。

步骤

  1. 创建字节输入流对象,并指定文件路径。
  2. 使用一次读取一个字节的模板代码读取文件中的数据。
  3. 将读取到的文件数据转换为字符类型并输出打印。
  4. 关闭流对象,释放资源。

实现

public class Demo {
    /*
        字节流读取中文字符:在UTF-8编码表中,一个中文字符需要3个字节组合。
     */
    public static void main(String[] args) throws IOException {
        // 创建字节输入流对象,并指定文件路径。
        FileInputStream fis = new FileInputStream("e:\\demo\\a.txt");
      	// 模板代码
        int ch = 0;
        while( (ch = fis.read()) != -1 ) {
          	// 将读取到的字节数据转换为字符数据。
            System.out.println((char)ch);
        }
        // 关闭流对象
        fis.close();
    }
}

小结

我们使用字节流读取字符数据,发现读取到的是每个字符对应字节数据,而不是真正的字符内容。而我们更希望读取到的是具体字符数据。这样我们就需要把读取到的某几个字节合并在一起,拼成一个汉字。但是在文件中,有时一个字节就表示一个字符数据,比如英文字母。有时是多个字节表示一个汉字。那么到底是应该把一个字节转成字母,还是把多个字节转成汉字就无法确定了。

为了解决上述问题,Java中为我们提供了字符流,在它的底层会根据读取到的字节数据的特点,基于对应的编码表帮助我们把字节转成字符,最后我们就可以得到想要的字符数据。

  • 字符流,以字符为单位读写数据,专门用于读写文本文件。

3.2 字符流底层原理

目标

  • 能够理解字符流读写字节数据的原理。

因为计算机中,是以二进制的形式保存文件中的数据的。所以当使用字符流去读写文件中的数据时,其实底层还是要以字节形式进行读写的操作。

  • 字符输入流:当使用字符输入流读取文件中的字符数据时,底层使用的依然是字节输入流。字节输入流将读取到的字节数据交给字符流。而字符流根据字节数据的特点在对照编码表将其转变为字符数据。
  • 字节输出流:当使用字符输出流写出数据时,底层使用的依然是字节输出流。字符输出流先将写出的字符数据通过编码表找到对应的编码值,然后将编码值转为字节数据保存到缓冲区中,交给字节输出流以字节的形式写到文件中。

小结

  • 字符流的底层其实就是使用:字节流 + 编码表。
  • 字符编码表:字节与字符的对应规则。Windows系统的中文编码默认是GBK编码表。idea中UTF-8。

3.3 字符输入流【Reader】

目标

  • 能够理解字符输入流的作用和常用方法的作用。
  • java.io.Reader抽象类是表示用于读取字符流的所有类的超类,可以读取字节信息到内存中。它定义了字符输入流的基本共性功能方法。

成员方法

  • public void close() :关闭此流并释放与此流相关联的任何系统资源。
  • public int read(): 从输入流读取一个字符。
    • 虽然读取了一个字符,但是会自动提升为int类型。
  • public int read( char[] cbuf ): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中 。

3.4 FileReader类

目标

  • 能够使用FileReader通过两种模板代码,完成文件数据的读取。
  • java.io.FileReader类是读取字符文件的便利类。构造时使用系统默认的字符编码和默认字节缓冲区。

小贴士:字节缓冲区:一个字节数组,用来临时存储字节数据。

构造方法

  • FileReader(File file): 创建一个新的 FileReader ,给定要读取的File对象。
  • FileReader(String fileName): 创建一个新的 FileReader ,给定要读取的文件的名称。
    • 当创建一个流对象时,必须传入一个文件路径。类似于FileInputStream 。

步骤

  1. 创建字符输入流对象,并指定文件路径。
  2. 使用一次读取一个字符的模板代码,对文件中的数据进行读取。
    1. 定义变量int ch = 0; 接收读取到的字符数据。
    2. 使用read()方法循环读取数据,然后赋值给ch变量,判断是否读取到文件末尾 != -1
    3. 将读取到的数据转换为char类型并输出打印。
  3. 使用一次读取多个字符的模板代码,对文件中的数据进行读取。
    1. 定义字符数组char[] 用于保存读取到的字符数据。
    2. 定义int len = 0;变量,用来记录数组中读取到的真实有效的字符个数。
    3. 使用read(char[] c)方法循环读取数据,然后赋值给len变量,判断是否读取到文件末尾 != -1

实现

  1. 读取字符:read方法,每次可以读取一个字符的数据,提升为int类型,读取到文件末尾,返回-1,循环读取,代码使用演示:

    public class Demo {
    /*
    字符流读取文件的格式和字节流的格式是一样的。
    int read() 读一个字符:将字符转换为对应的int类型
    读取到文件最后,返回-1
    */
    public static void main(String[] args) throws IOException {

         // 创建字符输入流对象
         FileReader fr = new FileReader("e:\\demo\\1.txt");
    
         // 定义变量,记录读取到的字符数据
         int ch = 0;
    
         // 循环读取数据
         while( ( ch = fr.read() ) != -1 ) {
             System.out.println( (char)ch );
         }
    
         // 关闭流
         fr.close();
     }
    

    }

  2. 使用字符数组读取:read(char[] cbuf),每次读取b的长度个字符到数组中,返回读取到的有效字符个数,读取到末尾时,返回-1 ,代码使用演示:

    public class Demo {
    /*
    字符流读取文件的格式和字节流的格式是一样的。
    int read(char[] cbuf) 将字符读入数组。 读取到文件最后,返回-1
    返回值:记录读取到的有效字符个数
    */
    public static void main(String[] args) throws IOException {

         // 创建字符输入流对象
         FileReader fr = new FileReader("e:\\demo\\1.txt");
    
         // 定义char数组,保存读取到的字符数据
         char[] buff = new char[1024];
         // 定义变量,记录读取到的字符数据
         int len = 0;
    
         // 循环读取数据
         while( ( len = fr.read(buff)) != -1 ) {
             /*
                 len = fr.read( buff ):将从文件中读取的有效字节个数赋值给len空间
                 new String(buff , 0 ,len):
                     buff :将buff数组中的数据转为字符串数据
                     0 :从buff数组中的0下标开始转换
                     len:len记录的是真实读取到的字节个数,只需要转换到len个即可。后续的都是默认数据
              */
             System.out.println(new String(buff,0,len));
         }
    
         // 关闭流
         fr.close();
     }
    

    }

小结

FileReader类是用于读取文件中字符数据的类。有读取的模板代码,和字节输入流的操作方式基本一致,不同的此类操作的是字符数据,所以使用的数组为char[]数组。

3.5 字符输出流【Writer】

目标

  • 能够理解Writer类的作用和常用方法的作用。
  • java.io.Writer抽象类是表示用于写出字符流的所有类的超类,将指定的字符信息写出到目的地。它定义了字符输出流的基本共性功能方法。

成员方法:

  • public abstract void close() :关闭此输出流并释放与此流相关联的任何系统资源。
  • public abstract void flush() :刷新此输出流并强制任何缓冲的输出字符被写出。
    • 字符输出流写出的数据需要刷新或关流之后才能写出。
  • public void write(int c) :写出一个字符。
  • public void write(char[] cbuf):将 b.length字符从指定的字符数组写出此输出流。
  • public abstract void write(char[] b, int off, int len) :从指定的字符数组写出 len字符,从偏移量 off开始输出到此输出流。
  • public void write(String str) :写出一个字符串。

3.6 FileWriter类

目标

  • 能够使用FileWriter类给指定的文件中写出数据。
  • java.io.FileWriter是写出字符到文件的类。构造时使用系统默认的字符编码和默认字节缓冲区。
    • 可以快速的把一个字符数据写到文件中。底层使用的是本地默认的编码表和字节缓冲区。

在使用字符输出流的时候,底层都会先把当前的字符数据通过编码表查到对应的编码值,然后把这些编码值转成字节数据,把这些字节数据存储到自己的缓冲区中。然后通过字节流写到指定的文件中。

如果不刷新,程序运行完之后,内存中的数据就会消失。所以需要在内存释放之前刷新数据,close方法底层会自动刷新。

注意:使用字符输出流写出数据的时候一定要刷新或者关流。当数据输出的比较多的时候,一定要刷新。

构造方法:

  • FileWriter(File file): 创建一个新的 FileWriter,给定要读取的File对象。
  • FileWriter(String fileName): 创建一个新的 FileWriter,给定要读取的文件的名称。
    • 创建一个流对象时,必须传入一个文件路径,类似于FileOutputStream。

步骤

  1. 创建FileWriter对象,并指定文件路径。
  2. 调用write()方法写出各种不同类型的数据。
  3. 写出数据之后,调用flush()刷新。
  4. 关闭流对象。

实现

public class Demo {
    /*
        在使用字符输出流的时候,底层都会先把当前的字符数据通过编码表查到对应的编码值,
        然后把这些编码值转成字节数据,把这些字节数据存储到自己的缓冲区中。
        所以使用字符输出流写出数据的时候一定要刷新或者关流。当数据输出的比较多的时候,一定要刷新
        void write(char[] cbuf) 写入一个字符数组。
        abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
        void write(int c) 写一个字符
        void write(String str) 写一个字符串
        void write(String str, int off, int len) 写一个字符串的一部分。
     */
    public static void main(String[] args) throws IOException {
        method03();
    }
    /*
         void write(char[] cbuf) 写入一个字符数组。
         abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
            int off: 起始的下标
            int len: 写入的个数
     */
    private static void method03() throws IOException {
        // 创建字符输出流对象
        FileWriter fw = new FileWriter("e:\\demo\\1.txt");

        // 定义字符数组
        char[] ch = "我要敲代码".toCharArray();

        fw.write(ch);

        // 关闭流
        fw.close();
    }

    /*
        void write(String str) 写一个字符串
        void write(String str, int off, int len) 写一个字符串的一部分。
        int off: 起始的下标
        int len: 写入的个数
     */
    private static void method02() throws IOException {
        // 创建字符输出流对象
        FileWriter fw = new FileWriter("e:\\demo\\1.txt");
        fw.write("明天休息....");
        fw.write("但是后天要上课...",0,9);
        // 关闭流
        fw.close();
    }

    /*
        void write(int c) 写一个字符:将指定int编码值对应的字符写入到文件中
     */
    private static void method01() throws IOException {
        // 创建字符输出流对象
        FileWriter fw = new FileWriter("e:\\demo\\1.txt");
        for( int i = 23456; i < 40000; i++ ) {
            fw.write(i);
            fw.flush();
        }
        // 关闭流
        fw.close();
    }
}

小结

FileWriter类是用于写出字符数据的类。写出因为底层有使用缓冲区,所以写出数据之后要刷新或关流,才能够将缓冲区中的数据写到文件中。

字符流,只能操作文本文件,不能操作图片,视频等非文本文件。

第四章 IO异常的处理

IO中的方法,大部分都存在编译时期的异常。而对异常的处理在某些场景下是不能声明的,因此只能捕获处理。而IO中的异常在捕获处理时有固定的模板代码。

4.1 JDK7前处理

目标

  • 能够使用try-catch-finally的方式对IO异常进行处理。

步骤

  1. 创建字符输出流对象,并指定文件路径。
  2. 调用writer()输出字符数据。
  3. 调用flush()方法刷新数据。
  4. 调用close()方法关闭流对象。
  5. 使用捕获的方式对异常进行处理,因为流对象在操作完成之后一定要关闭,所以需要使用finally代码块。

实现

public class Demo {
    /*
        异常处理的模板代码
     */
    public static void main(String[] args)  {
        // 声明变量
        FileWriter fw = null;
        try {
            //创建字符输出流对象
            fw = new FileWriter("e:\\demo\\1.txt");
            // 谢谢胡数据
            fw.write("异常处理的模板代码!");
          	fw.flush();
        } catch (IOException e) {
            e.printStackTrace();
          //关闭输入流的代码,不管程序有没有问题,最后都需要关闭,因此需要放在finally代码块中
        } finally {
            try {
               //因为fw是个引用,如果引用为空,就会报空指针异常,如果fw不是空就需要关闭流
                if( fw != null ) {
                    // 关闭流
                    fw.close();
                }
            } catch (IOException e ){
                e.printStackTrace();
            }
        }
    }
}

小结

对于IO的异常处理比较繁琐,但是幸运的是IO的异常处理有模板代码。通过代码的实现步骤多操作几次,熟练之后任何的IO代码在处理异常时都能够得心应手。

4.2 JDK7的处理

目标

  • 能够使用JDK7的try-with-resource处理IO异常。

因为IO的异常操作比较繁琐,在JDK7时对异常的处理进行了优化。通过try-with-resource 语句可以确保了每个资源在语句结束时关闭。所谓的资源(resource)是指在程序完成后,必须关闭的对象。

格式:

try ( 创建流对象语句,如果多个,使用';'隔开 ) {
	// 读写数据
} catch (IOException e) {
	e.printStackTrace();
}

步骤

  1. 创建字符输出流对象,并指定文件路径。
  2. 调用writer()输出字符数据。
  3. 调用flush()方法刷新数据。
  4. 调用close()方法关闭流对象。
  5. 使用try-with-resource语句处理异常。

实现

public class Demo {
    /*
        try (创建流对象语句,如果多个,使用';'隔开) {
	        // 读写数据
        } catch (IOException e) {
            e.printStackTrace();
        }
        JDK7之后可以在try后面加一个(),而在()中书写创建流对象语句。
        当前()就属于try语句的一部分,try中的代码执行完毕,会自动将
        流关闭,而不用专门书写finally代码块
     */
    public static void main(String[] args)  {
        // 创建字符输入流对象

        try(FileReader fr = new FileReader("e:\\demo\\1.txt");
            // 创建字符输出流对象
            FileWriter fw = new FileWriter("e:\\demo\\1.txt");){
            // 写出数据
            fw.write("大家好!!");
            // 刷新
            fw.flush();
            // 读取数据
            int ch = 0;
            while( (ch = fr.read() ) != -1 ) {
                System.out.println((char)ch);
            }

        }catch (IOException e){
            e.printStackTrace();
        }
    }
}

小结

try-with-resource语句优化的IO异常的处理,不需要手动调用方法释放资源。

4.3 JDK9的改进(了解内容)

目标

  • 了解JDK9对try-with-resource的改进。

改进后格式:

// 流对象
输出流 sc = new 输出流("");
输入流 sr = new 输人流("");
// 引入方式:直接引入
try (sc; sr) {
    // 使用对象
}

步骤

  1. 创建字符输出流对象,并指定文件路径。
  2. 调用writer()输出字符数据。
  3. 调用flush()方法刷新数据。
  4. 调用close()方法关闭流对象。
  5. 使用JDK9改进的try-with-resource语句处理异常。

实现

public class Demo {
    /*
        // 创建对象
        输出流 sc = new 输出流("");
        输入流 sr = new 输人流("");
        // 引入方式:直接引入
        try (sc; sr) {
           使用对象
        }
     */
    public static void main(String[] args) throws IOException {
        // 创建字符输入流对象
        FileReader fr = new FileReader("e:\\demo\\1.txt");
        // 创建字符输出流对象
        FileWriter fw = new FileWriter("e:\\demo\\1.txt");

        try( fr;fw ){
            // 写出数据
            fw.write("大家好!!");
            // 刷新
            fw.flush();
            // 读取数据
            int ch = 0;
            while( (ch = fr.read() ) != -1 ) {
                System.out.println((char)ch);
            }

        } catch (IOException e ) {
            System.out.println(e);
        }
    }
}

小结

JDK9中try-with-resource 的改进,对于引入对象的方式,支持的更加简洁。被引入的对象,同样可以自动关闭,无需手动close,我们来了解一下格式。

第五章 属性集

5.1 Properties类 概述

目标

  • 掌握Properties属性集的作用,能够使用Properties属性集保存数据。

在使用Map集合保存数据时,数据是被临时保存在内存中的,而当程序运行结束之后。集合本身和其中的数据都会被从内存中清除。而如果希望Map集合中的数据可以永久保存,可以使用Properties属性集完成。

  • java.util.Properties 继承于Hashtable ,来表示一个持久的属性集。它使用键值结构存储数据,每个键及其对应值都是一个字符串。其实java.util.Properties它就是一个Map体系的集合,只是在此集合中key 和 value 都必须是字符串数据。
  • java.util.Properties当前属性集的好处在于它可以使用流技术,将集合中的数据写入到持久设备上,也可以将持久设备上的数据读取到属性集集合中。

构造方法

  • public Properties() :创建一个空的属性列表。

特有成员方法

  • public Object setProperty(String key, String value) : 保存一对属性。
    • 相当于Map集合中的put()方法。只能保存String类型。
  • public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。
    • 相当于Map集合中的get(key)方法,通过kye值获取value值。
  • public Set stringPropertyNames() :所有键的名称的集合。
    • 相当于Map集合中的keySet()方法,获取集合中所有的key值。

步骤

  1. 创建属性集Properties属性集对象。
  2. 通过setProperty()方法给属性集中保存数据。
  3. 通过stringPropertyNames()方法获取属性集中的所有key值,保存到set集合中。
  4. 遍历保存保存key值的set集合,取出每个key值。
  5. 通过getProperty()方法,通过key值获取所有的value值并打印输出。

实现

public class Demo {
    /*
        Properties() 创建一个没有默认值的空属性列表。  创建集合对象
        - public Object setProperty(String key, String value) : 保存一对属性。
            它其实对应的就是Map集合体系中的put()方法,只是key和value都要求必须是字符串数据
        - public String getProperty(String key) :使用此属性列表中指定的键搜索属性值。
            它其实就是Map集合体系中的get()方法,根据key获取指定的value
        - public Set stringPropertyNames() :所有键的名称的集合。
            它其实就是Map集合体系中的keySet()方法,将所有的key值获取并保存到一个Set集合中
     */
    public static void main(String[] args) {
        // 创建属性集对象
        Properties prop = new Properties();
        
        // 给属性集中保存数据
        prop.setProperty("孙悟空","神话人物");
        prop.setProperty("黄继光","民族英雄");
        prop.setProperty("曹操","大奸雄");
        prop.setProperty("秦桧","大汉奸");

        // 获取属性集中的所有key值
        Set strs = prop.stringPropertyNames();

        // 遍历属性集中的数据
        for( String key :strs ) {
            // 通过key值获取value值
            String value = prop.getProperty(key);
            System.out.println( key + "..." + value );
        }
    }
}

小结

Properties属性集本质就是一个Map集合,主要用于保存String类型的数据,并通过流将保存到属性集中的数据写到文件中,获取从文件中读取被保存的String类型的数据。集合本身的操作方式和Map集合一致。

5.2 与流相关的方法

目标

  • 能够将属性集中的数据写到文件中,并从文件中再次读取到属性集中。

和流相关的方法:

  • public void load(InputStream inStream)
    • 使用字节流将保存到文件中的数据读取到属性集中。
  • public void load(Reader reader)
    • 使用字符流将保存到文件中的数据读取到属性集中。
  • public void store(OutputStream out, String comments)
    • 使用字节流,将属性集中的数据写到文件中。
  • public void store(Writer writer, String comments)
    • 使用字节流,将属性集中的数据写到文件中。

注意:参数中使用了字节输入流,通过流对象,可以关联到某文件上,这样就能够加载文本中的数据了。

步骤

  1. 将上述案例复制一份。
  2. 通过属性集调用store()方法并指定字符输出流对象,将数据写到文件中。
  3. 通过属性集调用load()方法并指定字符输入流对象,将文件中红的数据读取到属性集中。
  4. 遍历属性集查看数据

实现

public class Demo {
    public static void main(String[] args) throws IOException {
        method02();
    }
    /*
        void load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)。
            使用字节输入流,从持久设备上读取数据到属性集中
        void load(Reader reader) 以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。
            使用字符输入流,从持久设备上读取数据到属性集中 JDK1.6特性
     */
    private static void method02() throws IOException {
        // 创建属性集对象
        Properties prop = new Properties();
        // 从持久设备上读取数据到属性集中
        prop.load(new FileInputStream("e:\\demo\\1.txt"));

        // 遍历集合
        Set strs = prop.stringPropertyNames();
        for( String key : strs ) {
            // 通过key值获取value值
            String value = prop.getProperty(key);
            System.out.println( key + ".." + value );
        }
    }


    /*
        void store(OutputStream out, String comments) 。
            使用字节输出流将属性集中的数据写到持久设备上
        void store(Writer writer, String comments)
            使用字符输出流将属性集中的数据写到持久设备上  JDK1.6特性
        String comments : 注释内容,可以直接写""字符串
     */
    private static void method01() throws IOException {
        // 创建属性集对象
        Properties prop = new Properties();

        // 给属性集中保存数据
        prop.setProperty("孙悟空","神话人物");
        prop.setProperty("黄继光","民族英雄");
        prop.setProperty("曹操","大奸雄");
        prop.setProperty("秦桧","大汉奸");

        // 使用store方法,将属性集合中的数据写到持久设备上
        prop.store(new FileOutputStream("e:\\demo\\1.txt"),"");
    }
}

小结

属性集中提供了和流相关的方法,可以将属性集中的数据写到文件中,然后再次从文件中读取到属性集中。

小贴士:文本中的数据,必须是键值对形式,可以使用空格、等号、冒号等符号分隔。

你可能感兴趣的:(day09【字节流、字符流】)