02_Java语音进阶||day10_缓冲流、转换流、序列化流、打印流

第一章 缓冲流

1.1 概述

  • 为了提高效率

1.2 BufferedOutputStream_字节缓冲输出流

  1. java.io.BufferedOutputStream extends OutputStream
    • BufferedOutputStream:字节缓冲输出流
  2. 继承自父类的共性成员方法:
    • 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) :将指定的字节输出流。
  3. 构造方法:
    • BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
    • BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流。
    1. 参数:
      • OutputStream out:字节输出流
        • 我们可以传递FileOutputStream,缓冲流会给FileOutputStream增加一个缓冲区,提高FileOutputStream的写入效率
      • int size:指定缓冲流内部缓冲区的大小,不指定默认
  4. 使用步骤【重点】
    1. 创建FileOutputStream对象,构造方法中绑定要输出的目的地
    2. 创建BufferedOutputStream对象,构造方法中传递FileOutputStream对象对象,提高FileOutputStream对象效率
    3. 使用BufferedOutputStream对象中的方法write,把数据写入到内部缓冲区中
    4. 使用BufferedOutputStream对象中的方法flush,把内部缓冲区中的数据,刷新到文件中
    5. 释放资源(会先调用flush方法刷新数据,第4部可以省略)
      • 只用关闭缓冲流
    public static void main(String[] args) throws IOException {
        //1. ==创建FileOutputStream对象==,构造方法中==绑定要输出的目的地==
        FileOutputStream fos = new FileOutputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\a.txt");
        //2. ==创建BufferedOutputStream对象==,构造方法中==传递FileOutputStream对象对象==,提高FileOutputStream对象效率
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        //3. 使用BufferedOutputStream对象中的方法==write==,把数据写入到内部缓冲区中
        bos.write("将数据写入内部缓冲区中".getBytes());
        //4. 使用BufferedOutputStream对象中的方法==flush==,把内部缓冲区中的数据,刷新到文件中
        bos.flush();
        //5. ==释放资源==(会先调用flush方法刷新数据,==第4部可以省略==)
        bos.close();
    }

1.3 BufferedInputStream_字节缓冲输入流

  1. java.io.BufferedInputStream extends InputStream
    • BufferedInputStream:字节缓冲输入流
  2. 继承自父类的成员方法:
    • int read()从输入流中读取数据的下一个字节。
          int len = 0;    //记录每次读取到的字节
          while((len = bis.read()) != -1){
              System.out.println(len);
          }
      
    • int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
          int len = 0;    //记录每次读取的有效字节个数
          byte[] bytes = new byte[1024];  //存储每次读取的数据
          while((len = bis.read(bytes)) != -1){
              System.out.println(new String(bytes, 0, len));  //将数组转为字符串,从0开始到len个有效个数
              //直接写入时:
              //bos.write(bytes, 0, len);
          }
      
    • void close() 关闭此输入流并释放与该流关联的所有系统资源。
  3. 构造方法:
    • BufferedInputStream(InputStream in) 创建一个BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
    • BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用。
    1. 参数:
      • InputStream in:字节输入流
        • 我们可以传递FileInputStream,缓冲流会给FileInputStream增加一个缓冲区,提高FileInputStream的读取效率
      • int size:指定缓冲流内部缓冲区的大小,不指定默认
  4. 使用步骤【重点】:
    1. 创建FileInputStream对象,构造方法中绑定要读取的数据源
    2. 创建BufferedInputStream对象,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
    3. 使用BufferedInputStream对象中的方法read,读取文件
    4. 释放资源
      • 只用关闭缓冲流
    public static void main(String[] args) throws IOException {
        //1. ==创建FileInputStream对象==,构造方法中绑定要读取的数据源
        FileInputStream fis = new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\a.txt");
        //2. ==创建BufferedInputStream对象==,构造方法中传递FileInputStream对象,提高FileInputStream对象的读取效率
        BufferedInputStream bis = new BufferedInputStream(fis);
        //3. 使用BufferedInputStream对象中的==方法read,读取文件==
        //int ==read==()从输入流中读取数据的下一个字节。
        /*int len = 0;    //记录每次读取到的字节
        while((len = bis.read()) != -1){
            System.out.println(len);
        }*/

        //* int ==read==(==byte[] b==)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
        int len = 0;    //记录每次读取的有效字节个数
        byte[] bytes = new byte[1024];  //存储每次读取的数据
        while((len = bis.read(bytes)) != -1){
            System.out.println(new String(bytes, 0, len));  //将数组转为字符串,从0开始到len个有效个数
        }


        //4. 释放资源
        bis.close();
    }

1.4 缓冲流的效率测试_复制文件

  1. 文件复制练习:一读一写
  2. 明确:
    数据源: F:\picture\1.jpg
    数据的目的地: E:\picture\1.jpg
  3. 文件复制的步骤:
    1. 创建字节缓冲输入流对象,构造方法中传递字节输入流
    2. 创建字节缓冲输出流对象,构造方法中传递字节输出流
    3. 使用字节缓冲输入流对象中的方法read,读取文件
      • int read()从输入流中读取数据的下一个字节。
      • int read(byte[] b)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
    4. 使用字节缓冲输出流中的方法write,把读取的数据写入到内部缓冲区中
    5. 释放资源(会先把缓冲区中的数据,刷新到文件中)
      • 先释放写入的
  • 输入流和输出流的结果:(文件的大小:780,831 字节)
    • 一次读写一个字节:6043毫秒
    • 使用数组缓冲读取多个字节,写入多个字节:32毫秒
  • 缓冲输入流和缓冲输出流的结果:(文件的大小:780,831 字节)
    • 一次读写一个字节:32毫秒
    • 使用数组缓冲读取多个字节,写入多个字节:2毫秒
    public static void main(String[] args) throws IOException {
        long s = System.currentTimeMillis();
        //1. 创建字节缓冲输入流对象,构造方法中传递字节输入流
        BufferedInputStream bis = new BufferedInputStream(new FileInputStream("F:\\picture\\1.jpg"));
        //2. 创建字节缓冲输出流对象,构造方法中传递字节输出流
        BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("E:\\picture\\1.jpg"));
        //3. 使用字节缓冲输入流对象中的方法read,读取文件
        //* int ==read==()从输入流中读取数据的下一个字节。
        /*int len = 0;    //记录每次读取到的字节
        while((len = bis.read()) != -1){
            //4. 使用字节缓冲输出流中的方法==write==,把读取的数据写入到内部缓冲区中
            bos.write(len);
        }*/
        //* int ==read==(==byte[] b==)从输入流中读取一定数量的字节,并将其存储在缓冲区数组 b 中。
        int len = 0;    //记录每次读取的有效字节个数
        byte[] bytes = new byte[1024];  //存储每次读取的数据
        while((len = bis.read(bytes)) != -1){
            //4. 使用字节缓冲输出流中的方法==write==,把读取的数据写入到内部缓冲区中
            bos.write(bytes, 0, len);
        }


        //5. ==释放资源==(会先把缓冲区中的数据,刷新到文件中)
        bos.close();
        bis.close();
        long e = System.currentTimeMillis();
        System.out.println("复制文件共耗时:" + (e - s) + "毫秒");
    }

1.5 BufferedWriter_字符缓冲输出流(拥有特有方法newLine)

  1. java.io.BufferedWriter extends Writer
    • BufferedWriter:字符缓冲输出流
  2. 继承自父类的共性成员方法:
    • void write(int c) 写入单个字符。
    • void write(char[] cbuf)写入字符数组。
    • abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
    • void write(String str)写入字符串。
    • void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
    • void flush()刷新该流的缓冲。
    • void close() 关闭此流,但要先刷新它。
  3. 构造方法:
    • BufferedWriter(Writer out)创建一个使用默认大小输出缓冲区的缓冲字符输出流。
    • BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流。
    1. 参数:
      • Writer out:字符输出流
        • 我们可以传递FileWriter,缓冲流会给FileWriter增加一个缓冲区,提高FileWriter的写入效率
      • int sz:指定缓冲区的大小,不写默认大小
  4. 特有的成员方法【!】:
    • void newLine():写入一个行分隔符。会根据不同的操作系统,获取不同的行分隔符
          换行:换行符号
          windows:\r\n
          linux:/n
          mac:/r
      
  5. 使用步骤:
    1. 创建字符缓冲输出流对象,构造方法中传递字符输出流
    2. 调用字符缓冲输出流中的方法write,把数据写入到内存缓冲区中
    3. 调用字符缓冲输出流中的方法flush,把内存缓冲区中的数据,刷新到文件中
    4. 释放资源
    public static void main(String[] args) throws IOException {
        //1. ==创建字符缓冲输出流对象==,==构造方法中传递字符输出流==
        BufferedWriter bw = new BufferedWriter(new FileWriter("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\c.txt"));

        //2. 调用字符缓冲输出流中的==方法write==,把数据写入到内存缓冲区中
        //并用到特有成员方法newLine()来替代换行bw.write("\r\n");
        for(int i = 0; i < 10; i++){
            bw.write("程序员");
            //bw.write("\r\n");
            bw.newLine();
        }

        //3. 调用字符缓冲输出流中的==方法flush==,把内存缓冲区中的数据,刷新到文件中
        bw.flush();

        //4. ==释放资源==
        bw.close();
    }

1.6 BufferedReader_字符缓冲输入流(拥有特有方法readLine)

  1. java.io.BufferedReader extends Reader
    • BufferedReader:字符缓冲输入流
  2. 继承自父类的共性成员方法:
    • int read() 读取单个字符并返回。
    • int read(char[] cbuf)一次读取多个字符,将字符读入数组。
    • void close() 关闭该流并释放与之关联的所有资源。
  3. 构造方法:
    • BufferedReader(Reader in)创建一个使用默认大小输入缓冲区的缓冲字符输入流。
    • BufferedReader(Reader in, int sz)创建一个使用指定大小输入缓冲区的缓冲字符输入流。
    1. 参数:
      • Reader in:字符输入流
        • 我们可以传递FileReader,缓冲流会给FileReader增加一个缓冲区,提高FileReader的读取效率
  4. 特有的成员方法:
    • String readLine() 读取一个文本行。读取一行数据
      • 行的终止符号:通过下列字符之一即可认为某行已终止:换行 (’\n’)、回车 (’\r’) 或回车后直接跟着换行(\r\n)。
    • 返回值:
      • 包含该行内容的字符串不包含任何行终止符(需要换行得要自己加【!】),如果已到达流末尾,则返回 null【!】
        String line;
        while((line = br.readLine()) != null){
            System.out.println(line);
        }
    
  5. 使用步骤:
    1. 创建字符缓冲输入流对象,构造方法中传递字符输入流
    2. 使用字符缓冲输入流对象中的方法read/readLine读取文本
    3. 释放资源
    public static void main(String[] args) throws IOException {
        //1. ==创建字符缓冲输入流对象==,==构造方法中传递字符输入流==
        BufferedReader br = new BufferedReader(new FileReader("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\c.txt"));

        //2. 使用字符缓冲输入流对象中的==方法read/readLine读取文本==
        //这里使用readLine
        /*String line = br.readLine();
        System.out.println(line);
        line = br.readLine();
        System.out.println(line);
        line = br.readLine();
        System.out.println(line);
        line = br.readLine();
        System.out.println(line);*/
        /*
            发现以上读取是一个重复的过程,所以可以使用循环优化
            不知道文件中有多少行数据,所以使用while循环
            while的结束条件,读取到null结束
         */
        String line;
        while((line = br.readLine()) != null){
            System.out.println(line);
        }


        //3. ==释放资源==
        br.close();
    }

1.7 练习_对文本的内容进行排序

  1. 练习:
    • 对文本的内容进行排序
    • 按照(1,2,3…)顺序排序
  2. 分析:
    1. 创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,…);value:存储每行的文本
    2. 创建字符缓冲输入流对象,构造方法中绑定字符输入流
    3. 创建字符缓冲输出流对象,构造方法中绑定字符输出流
    4. 使用字符缓冲输入流中的方法readline,逐行读取文本
    5. 对读取到的文本进行切割,获取行中的序号和文本内容
    6. 把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4…)
    7. 遍历HashMap集合,获取每一个键值对
    8. 把每一个键值对,拼接为一个文本行
    9. 把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
    10. 因为readLine方法不包含任何行终止符,所以需要自己加换行
    11. 释放资源
    public static void main(String[] args) throws IOException {
        //1. 创建一个HashMap集合对象,可以:存储每行文本的序号(1,2,3,..);value:存储每行的文本
        HashMap<String, String> mapH = new HashMap<>();

        //2. 创建字符缓冲输入流对象,构造方法中绑定字符输入流
        BufferedReader br = new BufferedReader(new FileReader("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\in.txt"));

        //3. 创建字符缓冲输出流对象,构造方法中绑定字符输出流
        BufferedWriter bw = new BufferedWriter(new FileWriter("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\out.txt"));

        //4. 使用字符缓冲输入流中的方法readline,逐行读取文本
        String line;
        while((line = br.readLine()) != null){
            //5. 对读取到的文本进行切割,获取行中的序号和文本内容
            String[] arr = line.split("\\."); //返回字符串数组
            //6. 把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的,会自动排序1,2,3,4..)
            mapH.put(arr[0], arr[1]);

        }
        //7. 遍历HashMap集合,获取每一个键值对
        for(String key : mapH.keySet()){
            String value = mapH.get(key);
            //8. 把每一个键值对,拼接为一个文本行
            line = key + "." + value;
            //9. 把拼接好的文本,使用字符缓冲输出流中的方法write,写入到文件中
            bw.write(line);
            //10. 因为readLine方法不包含任何行终止符,所以需要自己加换行
            bw.newLine();   //写换行
        }
        //11. 释放资源
        bw.close();
        br.close();
    }

第二章 转换流

2.1 字符编码和字符集

  1. 字符编码
    • 编码:字符(能看懂的)–>字节(看不懂的)
    • 解码:字节(看不懂的)–>字符(能看懂的)
    • 字符编码【!】(Character Encoding):就是一套自然语言的字符与二进制数之间的对应规则
    • 编码表:生活中文字和计算机中二进制的对应规则
  2. 字符集
    • 字符集【!】(charset):也叫++编码表++。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
    • 注:当指定了编码,它所对应的字符集自然就指定了


  • 注:GBK存储中文是2个字节,UTF-8存储中文是3个字节

2.2 编码引出的问题_FileReader读取GBK格式的文件

    public static void main(String[] args) throws IOException {
        FileReader fr = new FileReader("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\我是GBK格式的文本.txt");
        
        //一次获取多个字节的方式
        char[] chars = new char[1024];
        int len = 0;
        while((len = fr.read(chars)) != -1){
            System.out.print(new String(chars, 0, len));
        }

        fr.close();
    }
  • 解决方法:转换流

2.3 转换流的原理

  1. InputStreamReader&FileReader:


2. OutputStreamWriter&FileWriter:

  • 转换流总结【重点】可以指定编码表
    • 可以读取或写入任意编码文件

2.4 OutputStreamWriter介绍&代码实现

  1. java.io.OutputStreamWriter ++extends++ Writer
    • OutputStreamWriter:是字符流通向字节流的桥梁:可使用指定的charset将要写入流中的字符编码成字节。(编码:把能看懂的变成看不懂)
  2. 继承自父类的共性成员方法:
    • void write(int c):写入单个字符。
    • void write(char[] cbuf):写入字符数组。
    • abstract void write(char[] cbuf, int off, int len):写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
    • void write(String str):写入字符串。
    • void write(String str, int off, int len):写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
    • void flush():刷新该流的缓冲。
    • void close():关闭此流,但要先刷新它。
  3. 构造方法:
    • OutputStreamWriter(OutputStream out):创建使用默认字符编码的 OutputStreamWriter。
    • OutputStreamWriter(OutputStream out, String charsetName):创建使用指定字符集的 OutputStreamWriter。
    1. 参数:
      • OutputStream out:字节输出流,可以用来写转换之后的字节到文件中
      • String charsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
        • 带双引号,因为是String类型
  4. 使用步骤【重点】:
    1. 创建OutputStreamWriter对象,构造方法传递字节输出流和指定的编码表名称
    2. 使用OutputStreamWriter对象中的方法write,把字符转换为字节存储缓冲区中(编码)
    3. 使用OutputStreamWriter对象中的方法flush,把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
    4. 释放资源
    public class Demo02OutputStreamWriter {
        public static void main(String[] args) throws IOException {
            //write_utf_8();
            write_gbk();
        }
    
        //使用转换流OutputStreamWriter写GBK的文件
        private static void write_gbk() throws IOException {
            //1. 创建==OutputStreamWriter对象==,==构造方法==中==传递字节输出流和指定的编码表名称==
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\gbk.txt"), "GBK");
            //2. 使用OutputStreamWriter对象中的方法==write==,把字符转换为字节存储缓冲区中(==编码==)
            osw.write("你好");
            //3. 使用OutputStreamWriter对象中的方法==flush==,把内存缓冲区中的字节刷新到文件中(==使用字节流写字节的过程==)
            osw.flush();
            //释放资源
            osw.close();
        }
    
        //使用转换流OutputStreamWriter写UTF-8的文件
        private static void write_utf_8() throws IOException {
            //1. 创建==OutputStreamWriter对象==,==构造方法==中==传递字节输出流和指定的编码表名称==
            //OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\utf_8.txt"), "utf-8");
            //不指定默认也是UTF-8
            OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\utf_8.txt"));
            //2. 使用OutputStreamWriter对象中的方法==write==,把字符转换为字节存储缓冲区中(==编码==)
            osw.write("你好");
            //3. 使用OutputStreamWriter对象中的方法==flush==,把内存缓冲区中的字节刷新到文件中(==使用字节流写字节的过程==)
            osw.flush();
            //释放资源
            osw.close();
        }
    }

2.5 InputStreamReader介绍&代码实现

  1. java.io.InputStreamReader extends Reader
    • InputStreamReader:是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。(解码:把看不懂的变成能看懂的)
  2. 继承自父类的共性成员方法:
    • int read():读取单个字符并返回。
    • int read(char[] cbuf):一次读取多个字符,将字符读入数组。
    • void close():关闭该流并释放与之关联的所有资源。
  3. 构造方法:
    • InputStreamReader(InputStream in):创建一个使用默认字符集的 InputStreamReader。
    • InputStreamReader(InputStream in, String charsetName):创建使用指定字符集的 InputStreamReader。
    1. 参数:
      • InputStream in:字节输入流,用来读取文件中保存的字节
      • StringcharsetName:指定的编码表名称,不区分大小写,可以是utf-8/UTF-8,gbk/GBK,…不指定默认使用UTF-8
  4. 使用步骤【重点】:
    1. 创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
    2. 使用InputStreamReader对象中的方法read读取文件
    3. 释放资源
  5. 注意事项【重点】:
    构造方法中指定的编码表名称要和文件的编码相同,否则会发生乱码
    public class Demo03InputStreamReader {
        public static void main(String[] args) throws IOException {
            //read_utf_8();
            read_gbk();
        }
    
        //使用转换流InputStreamReader读取GBK的文件
        private static void read_gbk() throws IOException {
            //1. 创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
            InputStreamReader isr = new InputStreamReader(new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\gbk.txt"), "GBK");
            //InputStreamReader isr = new InputStreamReader(new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\gbk.txt"), "UTF-8"); //乱码
            //2. 使用InputStreamReader对象中的方法read读取文件
            //一次读取多个的方式
            char[] chars = new char[1024];
            int len = 0;
            while((len = isr.read(chars)) != -1){
                System.out.print(new String(chars, 0, len));
            }
            //一次去一个字节的方式
            /*int len = 0;
            while((len = isr.read()) != -1){
                System.out.print((char)len);
            }*/
            //3. 释放资源
            isr.close();
        }
    
        //使用转换流InputStreamReader读取UTF-8的文件
        private static void read_utf_8() throws IOException {
            //1. 创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称
            //InputStreamReader isr = new InputStreamReader(new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\utf_8.txt"), "UTF-8");
            //默认格式UTF-8
            InputStreamReader isr = new InputStreamReader(new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\utf_8.txt"));
            //2. 使用InputStreamReader对象中的方法read读取文件
            //一次读取多个的方式
            /*char[] chars = new char[1024];
            int len = 0;
            while((len = isr.read(chars)) != -1){
                System.out.print(new String(chars, 0, len));
            }*/
            //一次去一个字节的方式
            int len = 0;
            while((len = isr.read()) != -1){
                System.out.print((char)len);
            }
            //3. 释放资源
            isr.close();
        }
    }

2.6 练习:转换文件编码

  1. 练习:转换文件编码(就读取最开始创建的:我是GBK格式的文本)
    • 将GBK编码的文本文件,转换为UTF-8编码的文本文件。
  2. 分析:
    1. 创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
    2. 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
    3. 使用InputStreamReader对象中的方法read读取文件
    4. 使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
    5. 释放资源
    public static void main(String[] args) throws IOException {
        //1. 创建InputStreamReader对象,构造方法中传递字节输入流和指定的编码表名称GBK
        InputStreamReader isr = new InputStreamReader(new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\我是GBK格式的文本.txt"), "GBK");
        //2. 创建OutputStreamWriter对象,构造方法中传递字节输出流和指定的编码表名称UTF-8
        OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\我是UTF-8格式的文本.txt"), "UTF-8");
        //3. 使用InputStreamReader对象中的方法read读取文件
        /*char[] chars = new char[1024];
        int len = 0;
        while((len = isr.read(chars)) != -1){
            //4. 使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
            osw.write(chars, 0, len);
        }*/
        int len = 0;
        while ((len = isr.read()) != -1){
            //4. 使用OutputStreamWriter对象中的方法write,把读取的数据写入到文件中
            osw.write(len);
        }
        //5. 释放资源
        osw.close();
        isr.close();
    }

第三章 序列化流

3.1序列化和反序列化的概述


3.2对象的序列化流_ObjectOutputStream(特有方法writeObject)

  1. java.io.ObjectOutputStream extends OutputStream
    • ObjectOutputStream:对象的序列化流
      作用:把对象以流的方式写入到文件中保存
  2. 构造方法:
    • ObjectOutputStream(OutputStream out):创建写入指定OutputStream 的 ObjectOutputStream。
    1. 参数:
      • OutputStream out:字节输出流
  3. 特有的成员方法:
    • void writeObject(Object obj):将指定的对象写入 ObjectOutputStream。
  4. 使用步骤:
    1. 创建ObjectOutputStream对象,构造方法中传递字节输出流
    2. 使用ObjectOutputStream对象中的方法writeObject,把对象写入到文件中
    3. 释放资源
  5. 执行上面步骤会出现的异常【重点】–>类实现Serializable接口
    1. 序列化和反序列化的时候,会抛出NotSerializableException没有序列化异常
    2. 类通过实现java.io.Serializable接口以启用其序列化功能,未实现此接口的类将无法使其任何状态序列化或反序列化。
    3. Serializable接口也叫标记型接口(接口中什么都没有)
      • 要进行序列化和反序列化的类必须实现Serializable接口,就会给类添加一个标记
      • 当我们进行序列化和反序列化的时候,就会检测类上是否有这个标记
        • 有:就可以序列化和反序列化
        • 没有:就会抛出 NotSerializableException异常
    • 去市场买肉–>肉上有一个蓝色章(检测合格)–>放心购买–>买回来怎么吃随意
  6. 序列化的前提【重点】:
    1. 类必须实现Serializable
    //定义一个Person类
    import java.io.Serializable;

    public class Person implements Serializable {
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    //主方法:Demo01ObjectOutputStream.java
    public static void main(String[] args) throws IOException {
        //1. ==创建ObjectOutputStream对象==,==构造方法中传递字节输出流==
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\Person.txt"));
        //2. 使用ObjectOutputStream对象中的==方法writeObject==,==把对象写入到文件中==
        oos.writeObject(new Person("小美女", 18));
        //3. 释放资源
        oos.close();
    }
    
    //结果是将对象以二进制方式存储

3.3 对象的反序列化流_ObjectInputStream(特有成员方法readObject)

  1. java.io.ObjectInputStream extends InputStream
    • ObjectInputStream:对象的反序列化流
  2. 作用:把文件中保存的对象,以流的方式读取出来使用
  3. 构造方法:
    • ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream。
    1. 参数:
      • InputStream in:字节输入流
  4. 特有的成员方法:
    • Object readObject() 从 ObjectInputStream 读取对象。
  5. 使用步骤【重点】:
    1. 创建ObjectInputStream对象,构造方法中传递字节输入流
    2. 使用ObjectInputStream对象中的方法readObject读取保存对象的文件
    3. 释放资源
    4. 使用读取出来的对象(打印)
  6. readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)–>该异常不属于IO异常
    • 当不存在对象的class文件时抛出此异常
  7. 反序列化的前提【重点】:
    1. 类必须实现Serializable
    2. 必须存在类对应的class文件
    //定义一个Person类:Person.java
    import java.io.Serializable;

    public class Person implements Serializable {
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    //主方法:Demo02ObjectInputStream.java
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        //1. ==创建ObjectInputStream对象==,==构造方法中传递字节输入流==
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\Person.txt"));
        //2. 使用ObjectInputStream对象中的==方法readObject读取保存对象的文件==
        Object o = ois.readObject();
        //3. ==释放资源==
        ois.close();
        //4. ==使用读取出来的对象(打印)==
        System.out.println(o);
        //转换为Person类型
        Person p = (Person)o;
        System.out.println(p.getName() + "=" + p.getAge());
    }
    
    //结果:(将3.2Person中存储的内容读取出来)
    Person{name='小美女', age=18}
    小美女+18

3.4 transient关键字_瞬态关键字

  1. static关键字:静态关键字
    • 静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
    • 被static修饰的成员变量不能被序列化的,序列化的都是对象
        private static int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}
    
  2. transient关键字:瞬态关键字
    • 被transient修饰成员变量,不能被序列化
        private transient int age;
        oos.writeObject(new Person("小美女",18));
        Object o = ois.readObject();
        Person{name='小美女', age=0}
    
  3. 作用:
    • 不想让某个成员变量序列化时候使用

3.5 InvalidClassException异常_原理和解决方案

  • 解决方法:
    • 在类中手动加一个序列号
    • 这样修改为public后序列号不会进行更改,直接反序列化可以出来值
        static final long serialVersionUID = 1L;
    

3.6 练习_序列化集合

  1. 练习:序列化集合
    • ++当我们想在文件中保存多个对象的时候++
    • 可以把多个对象存储到一个集合中
    • 对集合进序列化和反序列化
  2. 分析:
    1. 定义一个存储Person对象的ArrayList集合
    2. 往ArrayList集合中存储Person对象
    3. 创建一个序列化流ObjectOutputStream对象
    4. 使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
    5. 创建一个反序列化ObjectInputStream对象
    6. 使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
    7. 把Object类型的集合转换为ArrayList类型
          ArrayList<Person> listA2 = (ArrayList<Person>)o;
      
    8. 遍历ArrayList集合
    9. 释放资源
    //定义一个对象:Person.java
    import java.io.Serializable;

    public class Person implements Serializable {
        static final long serialVersionUID = 1L;
        private String name;
        private int age;
    
        public Person() {
        }
    
        public Person(String name, int age) {
            this.name = name;
            this.age = age;
        }
    
        @Override
        public String toString() {
            return "Person{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    '}';
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public int getAge() {
            return age;
        }
    
        public void setAge(int age) {
            this.age = age;
        }
    }
    
    //主方法:Demo03Test.java
    import java.io.*;
    import java.util.ArrayList;
    
    public class Demo03Test {
        public static void main(String[] args) throws IOException, ClassNotFoundException {
            //1. 定义一个存储Person对象的ArrayList集合
            ArrayList<Person> listA = new ArrayList<>();
            //2. 往ArrayList集合中存储Person对象
            listA.add(new Person("张三", 18));
            listA.add(new Person("李四", 30));
            listA.add(new Person("王五", 43));
            //3. 创建一个序列化流ObjectOutputStream对象
            ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\list.txt"));
            //4. 使用ObjectOutputStream对象中的方法writeObject,对集合进行序列化
            oos.writeObject(listA);
            //5. 创建一个反序列化ObjectInputStream对象
            ObjectInputStream ois = new ObjectInputStream(new FileInputStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\list.txt"));
            //6. 使用ObjectInputStream对象中的方法readObject读取文件中保存的集合
            Object o = ois.readObject();
            //7. 把Object类型的集合转换为ArrayList类型
            ArrayList<Person> listA2 = (ArrayList<Person>)o;
            //8. 遍历ArrayList集合
            for (Person p : listA2) {
                System.out.println(p);
            }
            //9. 释放资源
            ois.close();
            oos.close();
        }
    }
    
    //结果:
    Person{name='张三', age=18}
    Person{name='李四', age=30}
    Person{name='王五', age=43}

第四章 打印流

4.1 打印流_概述和使用(特有的方法,print,println)

  1. 概述
  2. java.io.PrintStream:打印流
    • PrintStream为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
  3. PrintStream特点:
    1. 只负责数据的输出,不负责数据的读取
    2. 与其他输出流不同,PrintStream 永远不会抛出 IOException
      • 但是会抛出文件找不到异常FileNotFoundException
    3. 有特有的方法,print,println
      • void print(任意类型的值)
      • void println(任意类型的值并换行)
  4. 构造方法:
    • PrintStream(File file):输出的目的地是一个文件
    • PrintStream(OutputStream out):输出的目的地是一个字节输出流
    • PrintStream(String fileName) :输出的目的地是一个文件路径
  5. PrintStream extends 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):将指定的字节输出流。
  6. 使用步骤【重点】
    1. 创建打印流PrintStream对象,构造方法中绑定要输出的目的地
    2. 使用继承自父类的write方法写数据或者使用自己特有的方法print/println方法写数据
    3. 释放资源
  7. 注意:
    • 如果使用继承自父类的write方法写数据,那么查看数据的时候会查询编码表 97->a
    • 如果使用自己特有的方法print/println方法写数据,写的数据原样输出 97->97
    public static void main(String[] args) throws FileNotFoundException {
        //创建打印流PrintStream对象,构造方法中绑定要输出的目的地
        PrintStream ps = new PrintStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\print.txt");
        //使用继承自父类的write方法写数据-->a
        ps.write(97);
        //使用自己特有的方法print/println方法写数据-->97
        ps.println(97);
        ps.print(97);


        //释放资源
        ps.close();
    }
    
    //结果:
    a97
    97
  1. 特点:
    • 可以改变输出语句的目的地(打印流的流向)
      • 输出语句,默认在控制台输出
      • 使用System.setOut方法,改变输出语句的目的地++改为++参数中传递的打印流的目的地
        • static void setOut(PrintStream out)
          • 重新分配“标准”输出流。
    import java.io.FileNotFoundException;
    import java.io.PrintStream;
    
    public class Demo02PrintStream {
        public static void main(String[] args) throws FileNotFoundException {
            System.out.println("我是控制台输出");
    
            PrintStream ps = new PrintStream("Dark-horse-teaching\\src\\cn\\javaadvance\\day10\\目的地是打印流.txt");
            System.setOut(ps);  //把输出语句的目的地改变为打印流的目的地
            System.out.println("我在打印流的目的地中输出");
    
            ps.close();
        }
    }
    
    //结果:控制台中
    我是控制台输出
    
    //结果:目的地是打印流.txt文件中
    我在打印流的目的地中输出
发布了42 篇原创文章 · 获赞 6 · 访问量 1129

猜你喜欢

转载自blog.csdn.net/qq_40572023/article/details/104809280