day12-编码表,字符流,对象流,其他流

今日目标

  • 编码表
  • 字符输出流
  • 字符输入流
  • 字符缓冲流
  • 转换流
  • 对象操作流
  • 装饰模式
  • commons-io工具包

    1 编码表

    1.1 思考:

  • 既然字节流可以操作所有文件,那么为什么还要学习字符流 ?

    • 如果使用字节流 , 把文本文件中的内容读取到内存时, 可能会出现乱码
    • 如果使用字节流 , 把中文写入文本文件中 , 也有可能会出现乱码

      1.2 编码表介绍

  • 计算机中储存的信息都是用二进制数据表示的;我们在屏幕上看到的英文、汉字等字符是二进制数转换之后的结果

  • 按照编码表规则,将字符存储到计算机中,称为编码。
  • 按照同样的编码表规则,将存储在计算机中的二进制数据解析显示出来,称为解码 。
  • 编码和解码使用的码表必须一致,否则会导致乱码。
  • 简单理解:
    • 存储一个字符a,首先需在码表中查到对应的数字是97,然后按照转换成二进制的规则进行存储。称为编码
    • 读取的时候,先把二进制解析出来,再转成97,通过97查找早码表中对应的字符是a。称为解码
  • ASCII码表:
    • ASCII(American Standard Code for Information Interchange,美国信息交换标准码表):
    • 包括了数字字符,英文大小写字符和一些常见的标点符号字符。
    • 注意:ASCII码表中是没有中文的。
  • day11 字符流,编码表,对象流,其他流 - 图1
  • GBK码表:
    • window系统默认的码表。兼容ASCII码表,也包含了21003个汉字,并支持繁体汉字以及部分日韩文字
    • 注意:GBK是中国的码表,一个中文以两个字节的形式存储。但不包含世界上所有国家的文字。
  • Unicode码表:

    • 由国际组织ISO 制定,是统一的万国码表,计算机科学领域里的一项业界标准,容纳世界上大多数国家的所有常见文字和符号。
    • 但是因为表示的字符太多,所以Unicode码表中的数字不是直接以二进制的形式存储到计算机的,会先通过UTF-7,UTF-7.5,UTF-8,UTF-16,以及 UTF-32的编码方式再存储到计算机,其中最为常见的就是UTF-8。
    • 注意: Unicode是万国码表,以UTF-8编码后一个中文以三个字节的形式存储

      1.3 编码表小结

      day11 字符流,编码表,对象流,其他流 - 图2
      day11 字符流,编码表,对象流,其他流 - 图3

      1.4 字节流读中文出现码的原码

  • 因为字节流一次读一个字节,而不管GBK还是UTF-8一个中文都是多个字节,用字节流每次只能读其中的一部分,所以就会出现乱码问题

    2 字符输出流

    2.1 字符流输出介绍

  • Writer类

    • 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化 , 需要使用其子类FileWriter类
  • FileWriter类 : 用来写入字符文件的便捷类

    2.2 FileWriter的成员

  • 构造方法 :

    • public FileWriter(File file) : 往指定的File路径中写入数据
    • public FileWriter(String fileName) : 往指定的String路径中写入数据
  • 成员方法 : | void write(int c) | 写一个字符 | | —- | —- | | void write(char[] cbuf) | 写入一个字符数组 | | void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 | | void write(String str) | 写一个字符串 | | void write(String str, int off, int len) | 写一个字符串的一部分 |
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据

2.3 FileWriter写数据的步骤

  • 1 创建字符输出流对象
    • 注意事项:
      如果文件不存在,就创建。但是要保证父级路径存在。
      如果文件存在就清空
  • 2 写数据
    • 注意事项:
      写出int类型的整数,实际写出的是整数在码表上对应的字母。
      写出字符串数据,是把字符串本身原样写出。
  • 3 释放资源

    • 注意事项:
      每次使用完流必须要释放资源。
      1. package com.itheima.writer_demo;
      2. import java.io.FileWriter;
      3. import java.io.IOException;
      4. /*
      5. Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化
      6. 需要使用其子类FileWriter类
      7. FileWriter类 : 用来写入字符文件的便捷类
      8. 构造方法 :
      9. public FileWriter(File file) : 往指定的File路径中写入数据
      10. public FileWriter(String fileName) : 往指定的String路径中写入数据
      11. 成员方法
      12. void write(int c) 写一个字符
      13. void write(char[] cbuf) 写入一个字符数组
      14. void write(char[] cbuf, int off, int len)写出字符数组中从off位置开始的len个字符
      15. void write(String str) 写一个字符串
      16. void write(String str, int off, int len)写出字符串中从off位置开始的len个字符
      17. flush() 刷新流,还可以继续写数据
      18. close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
      19. */
      20. public class WriterDemo1 {
      21. public static void main(String[] args) throws IOException {
      22. // 1. 创
      23. FileWriter fw = new FileWriter("day11_demo/file02.txt");
      24. // 2. 写
      25. fw.write('A');
      26. char[] chars = "HelloWorld".toCharArray();
      27. fw.write(chars);
      28. //写出Hello
      29. fw.write(chars, 0, 5);
      30. //写字符串
      31. String str = "HelloJava";
      32. fw.write(str);
      33. //写出字符串中的Java
      34. fw.write(str, 5, 4);
      35. // 3. 关
      36. fw.close();
      37. // fw.flush();
      38. }
      39. }

      2.4 字符输出流练习

      1. package com.itheima.writer_demo;
      2. import java.io.FileWriter;
      3. import java.io.IOException;
      4. import java.util.Scanner;
      5. /*
      6. 需求 : 将用户键盘录入的用户名和密码保存到本地实现永久化存储。
      7. 要求 : 用户名和密码在文件中各占一行
      8. 步骤:
      9. 1 用户键盘录入用户名,密码
      10. 2 创建字符输出流对象
      11. 3 将用户名和密码写到本地文件中
      12. 注意:如果要求文件存在不能覆盖,需要在原有内容中续写
      13. public FileWriter(String path, boolean append): 把append设置为true,表示有续写
      14. public FileWriter(File path, boolean append): 把append设置为true,表示有续写
      15. */
      16. public class WriterTest {
      17. public static void main(String[] args) throws IOException {
      18. System.out.println("注册开始!");
      19. Scanner sc = new Scanner(System.in);
      20. System.out.println("请输入用户名:");
      21. String name = sc.nextLine();
      22. System.out.println("请输入用密码:");
      23. String password = sc.nextLine();
      24. //要求 : 用户名和密码在文件中各占一行
      25. FileWriter fw = new FileWriter("day11_demo/file04.txt",true);//续写
      26. fw.write(name+"\r\n");
      27. fw.write(password+"\r\n");
      28. fw.close();
      29. }
      30. }

      2.5 刷新和关流

      1. package com.itheima.writer_demo;
      2. import java.io.FileWriter;
      3. import java.io.IOException;
      4. /*
      5. Writer类 : 写入字符流的最顶层的类 , 是一个抽象类 ,不能实例化
      6. 需要使用其子类FileWriter类
      7. //重点记住
      8. flush() 刷新流,还可以继续写数据【涉及到缓冲区的流,用法都是一样】
      9. close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
      10. */
      11. public class WriterDemo2 {
      12. public static void main(String[] args) throws IOException {
      13. //刷新和close的区别
      14. FileWriter fw = new FileWriter("day11_demo/file03.txt");
      15. //字符流底层会有一个字节数组缓冲区,大小8192
      16. //当字符输出流写出字符时,先将字符编码成为字节,放到缓冲区。
      17. //如果缓冲区没满,不会自动刷新到文件。如果满了放不下了,就会自动刷新。
      18. //或者调用flush方法也可以主动刷新
      19. for (int i = 0; i < 8192; i++) {
      20. fw.write('A');
      21. }
      22. fw.write('B'); // 缓冲区存满,再放一个导致自动刷新
      23. fw.flush(); //flush调用,主动刷新
      24. fw.write("C");//flush之后可以继续写数据
      25. fw.close();//刷新+关流 ,后续不能再用
      26. //fw.write("D"); //续不能再用
      27. }
      28. }

      3 字符输入流

      3.1 字节输入流介绍

  • Reader类 :

    • 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
    • 需要使用其子类FileReader类
  • FileReader类 :

    • 用来读取字符文件的便捷类

      3.2 FileReader的成员

  • 构造方法 :

    • public FileReader(File file) : 从指定的File路径中读取数据
    • public FileReader(String fileName) : 从指定的String路径中读取数据
  • 成员方法 : | int read() | 一次读一个字符数据 | | —- | —- | | int read(char[] cbuf) | 一次读一个字符数组数据 |
  1. package com.itheima.reader_demo;
  2. import java.io.FileNotFoundException;
  3. import java.io.FileReader;
  4. import java.io.IOException;
  5. /*
  6. Reader类 : 读取字符流的最顶层的类 , 是一个抽象类 ,不能实例化
  7. 需要使用其子类FileReader类
  8. FileReader类 : 用来读取字符文件的便捷类
  9. 构造方法 :
  10. public FileReader(File file) : 从指定的File路径中读取数据
  11. public FileReader(String fileName) : 从指定的String路径中读取数据
  12. 成员方法 :
  13. int read() : 一次读一个字符数据 , 如果读到文件的末尾 , 则返回-1
  14. int read(char[] cbuf) : 一次读一个字符数组数据
  15. 数据源 : 斗罗大陆.txt
  16. 目的地 : 直接输出打印在控制台
  17. */
  18. public class ReaderDemo1 {
  19. public static void main(String[] args) throws IOException {
  20. //1. 创
  21. FileReader fr = new FileReader("day11_demo/斗罗大陆.txt");
  22. //2. 读
  23. //int read() : 一次读一个字符数据 , 如果读到文件的末尾 , 则返回-1
  24. /*
  25. int c;//用来保存每次读取的字符
  26. while ((c = fr.read()) != -1) {//每次循环读取一个字符,如果不是-1就是有效的字符
  27. System.out.print((char)c);
  28. }
  29. */
  30. //int read(char[] cbuf) :
  31. // 一次读一个字符数组数据,读取的字符保存到字符数组中,返回有效读取字符数据的个数,如果读取完成返回-1
  32. char[] cbuf = new char[1024];//循环中每次保存读取到的字符
  33. int len; //每次读取字符的有效个数
  34. while ((len = fr.read(cbuf)) != -1) {
  35. //将有效的字符数据转换为字符串
  36. //public String(char[] cbuf, int off,int len) 将字符数组中,从off位置开始的len个字符变成字符串
  37. System.out.print(new String(cbuf, 0, len));
  38. }
  39. //3. 关
  40. fr.close();
  41. }
  42. }

4 字符缓冲流

4.1 字符缓冲流

  • BufferedWriter:可以将数据高效的写出
  • BufferedReader:可以将数据高效的读入到内存
  • 注意 : 字符缓冲流不具备读写功能 , 只提供缓冲区 , 真正读写还是需要依赖于构造接收的基本的字符流
  • 构造方法:

    • public BufferedWriter(Writer out) : 构造方法中需要接收一个基本的字符输出流
    • public BufferedReader(Reader in) : 构造方法中需要接收一个基本的字符输入流
      1. package com.itheima.bufferedstream_demo;
      2. import java.io.*;
      3. /*
      4. 1 字符缓冲流:
      5. BufferedWriter:可以将数据高效的写出
      6. BufferedReader:可以将数据高效的读入到内存
      7. 2 注意 : 字符缓冲流不具备读写功能 , 只提供缓冲区 , 真正读写还是需要依赖于构造接收的基本的字符流
      8. 3 构造方法:
      9. public BufferedWriter(Writer out) : 构造方法中需要接收一个基本的字符输出流
      10. public BufferedReader(Reader in) : 构造方法中需要接收一个基本的字符输入流
      11. 需求 : 使用字符缓冲流复制纯文本文件
      12. 将当日课程资料中的 ‘斗罗大陆.txt’ 复制到当前模块下 'copy.txt'
      13. 数据源 : 斗罗大陆.txt
      14. 目的地 : day11_demo\copy.txt
      15. */
      16. public class BufferedStreamDemo1 {
      17. public static void main(String[] args) {
      18. String src = "day11_demo/斗罗大陆.txt";
      19. String dest = "day11_demo/斗罗大陆copy2.txt";
      20. copyTextFile(src, dest);
      21. }
      22. public static void copyTextFile(String src, String dest) {
      23. try (
      24. FileReader fr = new FileReader(src);
      25. //包装为高效流
      26. BufferedReader br = new BufferedReader(fr);
      27. FileWriter fw = new FileWriter(dest);
      28. //包装为高效流
      29. BufferedWriter bw = new BufferedWriter(fw)
      30. ) {
      31. //缓冲流的基本使用和基本流是一样的
      32. //BufferedReader ---> FileReader
      33. //BufferedWriter ---> FileWriter
      34. //边读边写
      35. int len;
      36. char[] cbuf = new char[1024];
      37. while ((len = br.read(cbuf)) != -1) {//读取文本中的数据到内存
      38. //将内存的数据写到文件中
      39. bw.write(cbuf, 0, len);
      40. }
      41. //自动关流
      42. } catch (IOException e) {
      43. e.printStackTrace();
      44. }
      45. }
      46. }

      4.2 字符缓冲流特有的功能

  • BufferedWriter类

    • void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
  • BufferedReader类

    • public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
      1. package com.itheima.bufferedstream_demo;
      2. import java.io.*;
      3. /*
      4. 1 字符缓冲流:
      5. BufferedWriter:可以将数据高效的写出
      6. BufferedReader:可以将数据高效的读入到内存
      7. 2 字符缓冲流特有功能
      8. BufferedWriter类
      9. void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
      10. BufferedReader类
      11. public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
      12. 远桥之下泛莲舟
      13. 岱岩石上松溪流
      14. 万仞翠山梨亭在
      15. 莫闻空谷声悠悠
      16. */
      17. public class BufferedStreamDemo2 {
      18. public static void main(String[] args) {
      19. //newLineTest();
      20. readLineTest();
      21. }
      22. public static void newLineTest() {
      23. //BufferedWriter类
      24. //void newLine():写一个行分隔符,会根据操作系统的不同,写入不同的行分隔符
      25. try (FileWriter fw = new FileWriter("day11_demo/诗歌.txt", true);
      26. BufferedWriter bw = new BufferedWriter(fw)
      27. ) {
      28. //用下newLine
      29. bw.write("静夜思");
      30. bw.newLine();//换行
      31. bw.write("李白");
      32. bw.newLine();//换行
      33. bw.write("床前明月光");
      34. bw.newLine();//换行
      35. bw.write("疑是地上霜");
      36. bw.newLine();//换行
      37. bw.write("举头望明月");
      38. bw.newLine();//换行
      39. bw.write("地上鞋两双");
      40. bw.newLine();//换行
      41. } catch (IOException e) {
      42. e.printStackTrace();
      43. }
      44. }
      45. public static void readLineTest() {
      46. try (FileReader fr = new FileReader("day11_demo/诗歌.txt");
      47. BufferedReader br = new BufferedReader(fr);
      48. ) {
      49. //BufferedReader类
      50. //public String readLine() :读取文件一行数据, 不包含换行符号 , 读到文件的末尾返回null
      51. String line;//保存每次读取的行
      52. while ((line = br.readLine()) != null) {
      53. System.out.println(line);
      54. }
      55. } catch (IOException e) {
      56. e.printStackTrace();
      57. }
      58. }
      59. }

      4.3 字符缓冲流练习

      1. package com.itheima.bufferedstream_demo;
      2. import java.io.*;
      3. import java.util.Arrays;
      4. /*
      5. 需求:读取文件中的数据 : 33 22 11 55 44
      6. 排序后 : 11 22 33 44 55 再次写到本地文件
      7. 分析 :
      8. 步骤 :
      9. 1 创建高效的字符输入流对象
      10. 2 读取文件中的一行数据
      11. 3 将数据按照空格切割
      12. 4 把字符串数组转成int类型数组
      13. 5 对int类型的数组进行排序
      14. 6 创建高效的字符输出流对象
      15. 7 遍历数组,把数组中的数据写入到文件中
      16. 8 释放资源
      17. */
      18. public class BufferedStreamDemo3 {
      19. public static void main(String[] args) {
      20. //1 创建高效的字符输入流对象
      21. String lineNumber = null;
      22. try (FileReader fr = new FileReader("day11_demo/file01.txt");
      23. BufferedReader br = new BufferedReader(fr)) {
      24. //2 读取文件中的一行数据
      25. lineNumber = br.readLine();
      26. } catch (IOException e) {
      27. e.printStackTrace();
      28. }
      29. //3 将数据按照空格切割
      30. String[] strArr = lineNumber.split(" +");//正则表达式 用一个或者多个空格进行切割
      31. //System.out.println(Arrays.toString(strArr));
      32. //4 把字符串数组转成int类型数组
      33. int[] arr = new int[strArr.length];
      34. //把字符串的数字转为int数据
      35. for (int i = 0; i < strArr.length; i++) {
      36. arr[i] = Integer.parseInt(strArr[i]);
      37. }
      38. //5 对int类型的数组进行排序
      39. Arrays.sort(arr);
      40. //System.out.println("Arrays.toString(arr) = " + Arrays.toString(arr));
      41. //6 创建高效的字符输出流对象
      42. try (FileWriter fw = new FileWriter("day11_demo/file01_1.txt");
      43. BufferedWriter bw = new BufferedWriter(fw)) {
      44. //7 遍历数组,把数组中的数据写入到文件中
      45. for (int i : arr) {
      46. bw.write(i +" ");//把数字转为字符串,写出
      47. }
      48. //8 释放资源【自动完成】
      49. } catch (IOException e) {
      50. e.printStackTrace();
      51. }
      52. }
      53. }

      5 转换流

      5.1 转换流介绍

  • 转换流就是来进行字节流和字符流之间转换的桥梁

    5.2 转换流分类

  • InputStreamReader是从字节流到字符流的桥梁

    • public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。
    • public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
  • OutputStreamWriter是从字符流到字节流的桥梁
    • public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
    • public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
  • 练习

    1. package com.itheima.conversion_demo;
    2. import java.io.*;
    3. /*
    4. 转换流就是来进行字节流和字符流之间转换的桥梁
    5. InputStreamReader : 是从字节流到字符流的桥梁
    6. public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。 idea默认编码是UTF-8
    7. public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
    8. charsetName: GBK UTF-8
    9. OutputStreamWriter : 是从字符流到字节流的桥梁
    10. public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
    11. public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
    12. charsetName: GBK UTF-8
    13. 需求1 : 使用转换流 , 把以下数据按照GBK的编码写入文件 , 在使用GBK的编码读取数据
    14. 思考:转换流属于字符流还是属于字节流? 转换流属于字符流
    15. FileReader 的父类就是InputStreamReader
    16. FileWriter 的父类就是OutputStreamWriter
    17. 用法上和普通的字符流是一样的,只是构建对象的时候不一样
    18. 数据如下 :
    19. 远桥之下泛莲舟
    20. 岱岩石上松溪流
    21. 万仞翠山梨亭在
    22. 莫闻空谷声悠悠
    23. */
    24. public class ConversionDemo1 {
    25. // 需求1 : 使用转换流 , 把以下数据按照GBK的编码写入文件 , 在使用GBK的编码读取数据
    26. public static void main(String[] args) {
    27. //writeGBKText();
    28. readGBKText();
    29. }
    30. //指定GBK编码写文本
    31. public static void writeGBKText() {
    32. //OutputStreamWriter : 是从字符流到字节流的桥梁
    33. // public OutputStreamWriter(OutputStream out) : 创建使用默认字符编码的 OutputStreamWriter
    34. // public OutputStreamWriter(OutputStream out, String charsetName) : 创建使用指定编码的 OutputStreamWriter。
    35. // charsetName: GBK UTF-8
    36. //1. 创
    37. try (FileOutputStream fos = new FileOutputStream("day11_demo/file05GBK.txt");
    38. //OutputStreamWriter osw = new OutputStreamWriter(fos);//默认编码转换字符 IDEA:utf-8
    39. OutputStreamWriter osw = new OutputStreamWriter(fos, "GBK");//指定GBK编码
    40. ) {
    41. //2. 写
    42. osw.write("远桥之下泛莲舟\r\n");
    43. osw.write("岱岩石上松溪流\r\n");
    44. osw.write("万仞翠山梨亭在\r\n");
    45. osw.write("莫闻空谷声悠悠\r\n");
    46. //3. 关【自动关】
    47. } catch (FileNotFoundException e) {
    48. e.printStackTrace();
    49. } catch (IOException e) {
    50. e.printStackTrace();
    51. }
    52. }
    53. public static void readGBKText() {
    54. //InputStreamReader : 是从字节流到字符流的桥梁
    55. // public InputStreamReader(InputStream in) : 创建一个使用默认编码的 InputStreamReader。 idea默认编码是UTF-8
    56. // public InputStreamReader(InputStream in , String charsetName) : 创建使用指定编码的 InputStreamReader。
    57. // charsetName: GBK UTF-8
    58. //1. 创
    59. try (FileInputStream fis = new FileInputStream("day11_demo/file05GBK.txt");
    60. //InputStreamReader isr = new InputStreamReader(fis);//默认采用IDEA的UTF-8
    61. InputStreamReader isr = new InputStreamReader(fis, "gbk");//默认采用IDEA的UTF-8
    62. ) {
    63. //2. 读
    64. int c;
    65. while ((c = isr.read()) != -1) {
    66. System.out.print((char) c);
    67. }
    68. //3. 关
    69. } catch (IOException e) {
    70. e.printStackTrace();
    71. }
    72. }
    73. }
    1. package com.itheima.conversion_demo;
    2. import java.io.*;
    3. /*
    4. 转换流也可以进一步包装为高效流使用
    5. 字节流 ---> 字符流 ---->高效流
    6. 需求2 : 将模块根目录中GBK编码的文本文件 , 转换为UTF-8编码的文本文件
    7. //1.使用GBK编码方式读取到内存中,然后在使用UTF-8的方式写到文件中
    8. */
    9. public class ConversionDemo2 {
    10. public static void main(String[] args) {
    11. String gbkFile = "day11_demo/GBK编码文件.txt";
    12. String utf8File = "day11_demo/UTF8编码文件.txt";
    13. gbkConvertUtf8(gbkFile,utf8File);
    14. }
    15. /**
    16. * 将GBK文件转换为UTF8的文件
    17. *
    18. * @param gbkFile
    19. * @param utf8File
    20. */
    21. public static void gbkConvertUtf8(String gbkFile, String utf8File) {
    22. //1.创
    23. try (FileInputStream fis = new FileInputStream(gbkFile);
    24. InputStreamReader isr = new InputStreamReader(fis, "GBK");//以GBK方式读取文本
    25. FileOutputStream fos = new FileOutputStream(utf8File);
    26. OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8")//以UTF-8的方式写文本
    27. ) {
    28. //2.读,写 (和字符流的读写是一样的)
    29. int c;
    30. while ((c = isr.read()) != -1) {//以GBK编码读
    31. osw.write(c);//以UTF-8编码写
    32. }
    33. //3.关 [自动]
    34. } catch (IOException e) {
    35. e.printStackTrace();
    36. }
    37. }
    38. /**
    39. * 转换字符流能不能转化为高效字符流? 可以
    40. * @param gbkFile
    41. * @param utf8File
    42. */
    43. public static void gbkConvertUtf82(String gbkFile, String utf8File) {
    44. //1.创
    45. try (FileInputStream fis = new FileInputStream(gbkFile);
    46. InputStreamReader isr = new InputStreamReader(fis, "GBK");//以GBK方式读取文本
    47. BufferedReader br = new BufferedReader(isr);//将转换流变成高效流
    48. FileOutputStream fos = new FileOutputStream(utf8File);
    49. OutputStreamWriter osw = new OutputStreamWriter(fos, "UTF-8");//以UTF-8的方式写文本
    50. BufferedWriter bw = new BufferedWriter(osw);//将转换流变成高效流
    51. ) {
    52. //2.读,写 (和字符流的读写是一样的)
    53. int c;
    54. while ((c = br.read()) != -1) {//以GBK编码读
    55. bw.write(c);//以UTF-8编码写
    56. }
    57. //3.关 [自动]
    58. } catch (IOException e) {
    59. e.printStackTrace();
    60. }
    61. }
    62. }

    6 对象操作流

    6.1 对象操作流介绍

  • 可以把对象以字节的形式写到本地文件,直接打开文件,是读不懂的,需要再次用对象操作流读到内存中

    6.2 对象操作流的分类

  • ObjectOutputStream :
    对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
    写对象方法:public void writeObject(Object obj)

  • ObjectInputStream :
    对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
    读对象方法:public Object readObject()

练习

  1. package com.itheima.objectstream_demo;
  2. import java.io.*;
  3. /*
  4. 对象操作流 :
  5. ObjectOutputStream :
  6. 对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
  7. ObjectOutputStream(OutputStream out)创建一个写入指定的OutputStream的ObjectOutputStream。
  8. 写对象方法: public void writeObject(Object obj) 将对象写出
  9. ObjectInputStream :
  10. 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
  11. ObjectInputStream(InputStream in)
  12. 读对象方法: public Object readObject() 读对象
  13. 练习 : 使用对象操作输出流 , 把一个User对象写入文件中 , 在使用对象操作输入流读取对象数据
  14. 注意 : 如果一个类对象想要被序列化 , 那么此类需要实现Serializable接口
  15. Serializable接口的含义 :
  16. 1 是一个标记性接口 , 里面没有任何抽象方法
  17. 2 只要一个类实现了此接口 , 表示此类的对象可以被序列化
  18. */
  19. public class ObjectStreamDemo1 {
  20. public static void main(String[] args) {
  21. writeObject();
  22. readObject();
  23. }
  24. //将对象写到文件中
  25. public static void writeObject() {
  26. User user = new User("张三", 18);
  27. //把对象user使用ObjectOutputStream写到文件
  28. //1.创
  29. try (FileOutputStream fos = new FileOutputStream("day11_demo/userObj.txt");
  30. ObjectOutputStream oos = new ObjectOutputStream(fos)) {
  31. //2.写
  32. //public void writeObject(Object obj) 写对象的方法
  33. oos.writeObject(user);
  34. //3.关[自动完成]
  35. } catch (IOException e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. //将文件中的对象数据读取到内存
  40. public static void readObject() {
  41. //ObjectInputStream 对象读取流
  42. //1.创
  43. try (FileInputStream fis = new FileInputStream("day11_demo/userObj.txt");
  44. ObjectInputStream ois = new ObjectInputStream(fis)) {
  45. //2.读
  46. Object obj = ois.readObject();//读取对象
  47. System.out.println("obj = " + obj);
  48. //System.out.println(ois.readObject());
  49. //对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException
  50. //3.关[自动]
  51. } catch (IOException e) {
  52. e.printStackTrace();
  53. } catch (ClassNotFoundException e) {
  54. e.printStackTrace();
  55. }
  56. }
  57. }

6.3 对象操作流的注意事项

1 要写出对象(序列化)到文件,该对象的类型一定要去实现接口:Serializable
否则会有异常:NotSerializableException
2 写一个对象,读一个对象,如果对象读取完毕继续读,
Object readObject()
对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException (End Of File Exception)
3 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
给此成员变量加上一个修饰符(关键字) : transient
4 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
会出现问题 : 发生异常InvalidClassException
每个类写好后,都存在一个版本号。
写对象前类的版本号要和读取对象时类的版本号要一致,否则就会报错。
如果出问题了,如何解决呢?
给当前类加上一个写死的序列号 :
private static final long serialVersionUID = 数值L;
private static final long serialVersionUID = -9081456910220446530L;

  1. package com.itheima.objectstream_demo;
  2. import java.io.FileInputStream;
  3. import java.io.IOException;
  4. import java.io.ObjectInputStream;
  5. /*
  6. 对象操作流 :
  7. 对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
  8. ObjectOutputStream :
  9. 对象操作输出流(对象序列化流):就是将对象写到本地文件中,或者在网络中传输对象
  10. ObjectInputStream :
  11. 对象操作输入流(对象反序列化流):把写到本地文件中的对象读到内存中,或者接收网络中传输的对象
  12. 练习 : 使用对象操作输出流 , 把一个User对象写入文件中 , 在使用对象操作输入流读取对象数据
  13. 1 要写出对象(序列化)到文件,该对象的类型一定要去实现接口:Serializable
  14. 否则会有异常:NotSerializableException
  15. 2 写一个对象,读一个对象,如果对象读取完毕继续读,
  16. Object readObject()
  17. 对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException (End Of File Exception)
  18. 3 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
  19. 给此成员变量加上一个修饰符(关键字) : transient
  20. 4 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的Javabean类,读取数据会不会出问题呢?
  21. 会出现问题 : 发生异常InvalidClassException
  22. 每个类写好后,都存在一个版本号。
  23. 写对象前类的版本号要和读取对象时类的版本号要一致,否则就会报错。
  24. 如果出问题了,如何解决呢?
  25. 给当前类加上一个写死的序列号 :
  26. private static final long serialVersionUID = 数值L;
  27. private static final long serialVersionUID = -9081456910220446530L;
  28. */
  29. public class ObjectStreamDemo2 {
  30. public static void main(String[] args) {
  31. readObject();
  32. }
  33. //将文件中的对象数据读取到内存
  34. public static void readObject() {
  35. //ObjectInputStream 对象读取流
  36. //1.创
  37. try (FileInputStream fis = new FileInputStream("day11_demo/userObj.txt");
  38. ObjectInputStream ois = new ObjectInputStream(fis)) {
  39. //2.读
  40. Object obj = ois.readObject();//读取对象
  41. System.out.println("obj = " + obj);
  42. //System.out.println(ois.readObject());
  43. //对象读取完毕,继续读不是返回null,-1 之类的,报异常:EOFException
  44. //3.关[自动]
  45. } catch (IOException e) {
  46. e.printStackTrace();
  47. } catch (ClassNotFoundException e) {
  48. e.printStackTrace();
  49. }
  50. }
  51. }
package com.itheima.objectstream_demo;
import java.io.Serializable;
/*
以后如果类要序列化,干脆就先自己定义一个版本号
    private static final long serialVersionUID = 1000L;
 */
public class User implements Serializable{
    //告诉JVM我的版本号:
    private static final long serialVersionUID = -9081456910220446530L;
    private String name;
    private transient int age;
    // 如果age前使用transient 修饰,意味着age只能短暂存储与内存,不能随对象写到文件中
    private int weight;
    public User(String name, int age) {
        this.name = name;
        this.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;
    }
    public int getWeight() {
        return weight;
    }
    public void setWeight(int weight) {
        this.weight = weight;
    }
    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", weight=" + weight +
                '}';
    }
}

6.4 对象操作流的练习

package com.itheima.objectstream_demo;
import java.io.*;
import java.util.ArrayList;
/*
    需求:创建多个Javabean类对象写到文件中,再次读取到内存。
    思路:
        1 创建学生对象
        2 利用对象操作输出流写到本地
        3 利用对象操作输入流读到内存
 */
public class ObjectStreamTest1 {
    public static void main(String[] args) {
        //1.先定义学生类,并让学生类实现Serializable接口
        //writeStudent();
        readStudent();
    }
    public static void writeStudent() {
        //使用集合存储多个学生对象,将集合对象写出
        ArrayList<Student> list = new ArrayList<>();
        list.add(new Student("张三", 18));
        list.add(new Student("李四", 18));
        list.add(new Student("王五", 18));
        list.add(new Student("赵六", 18));
        try (FileOutputStream fos = new FileOutputStream("day11_demo/students.txt");
             ObjectOutputStream oos = new ObjectOutputStream(fos)) {
            //写集合对象到文件中
            oos.writeObject(list);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    public static void readStudent() {
        try (FileInputStream fis = new FileInputStream("day11_demo/students.txt");
             ObjectInputStream ois = new ObjectInputStream(fis)) {
            //读取对象
            ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
            for (Student student : list) {
                System.out.println("student = " + student);
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
package com.itheima.objectstream_demo;
import java.io.Serializable;
public class Student implements Serializable {
    //写死一个版本号
    private static final long serialVersionUID = 1000000L;
    private String name;
    private int age;
    public Student() {
    }
    public Student(String name, int age) {
        this.name = name;
        this.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;
    }
    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

7 打印流

l打印流可以实现方便、高效的打印数据到文件中去。打印流一般是指:PrintStream,PrintWriter两个类。
l可以实现打印什么数据就是什么数据,例如打印整数97写出去就是97,打印boolean的true,写出去就是true。
day11 字符流,编码表,对象流,其他流 - 图4
day11 字符流,编码表,对象流,其他流 - 图5
PrintStream和PrintWriter的异同
相同:
PrintStream 和 PrintWriter的特有使用功能都是一样的,提供了打印数据较为方便的print/println方法,而且他们在使用的时候都不会抛出异常
区别:

  1. PrintStream是字节流,PrintWriter是字符流
  2. PrintStream在打印输出数据后默认会自动刷新,而PrintWriter默认没有自动刷新的(关流或者刷新)。
    package com.itheima.printstream_demo;
    import java.io.FileNotFoundException;
    import java.io.PrintStream;
    /*
     PrintStream类 : 打印流
     作用 : 就是为了方便记录数据(日志)
     构造方法 :
         public PrintStream(String filePath) : 构建一个打印流对象,传入接收数据的文件路径
     成员方法 :
         public void println(数据)   打印后换行
         public void print(数据)     打印不换行
     需求 : 创建打印流对象,记录一些数据到指定文件
    */
    public class PrintStreamDemo {
     public static void main(String[] args) {
         System.out.print("Hello");
         System.out.println("World");
         //System.out 该对象类型就是PrintStream类型
         try (PrintStream ps = new PrintStream("day11_demo/file01.txt")) {
             ps.println(97);
             ps.println(98);
             ps.println('A');
             ps.println("Hello");
             ps.println(true);
             ps.println(false);
         } catch (FileNotFoundException e) {
             e.printStackTrace();
         }
     }
    }
    
    package com.itheima.printstream_demo;
    import java.io.FileNotFoundException;
    import java.io.PrintStream;
    /*
     PrintStream类 : 打印流
     构造方法 :
         public PrintStream(String filePath) : 构建一个打印流对象,传入接收数据的文件路径
     成员方法 :
         public void println(数据)   打印后换行
         public void print(数据)     打印不换行
     需求 :
         System.out 对象类型,其实就是PrintStream类型。
         可以改变系统输出的流向 : System.setOut(打印流)
    */
    public class PrintStreamDemo2 {
     public static void main(String[] args) throws FileNotFoundException {
         System.out.println("Hello");//控制台
         //想要让 System.out.println("Hello") 输出的数据到文件中去
         PrintStream ps = new PrintStream("day11_demo/log.txt");
         //可以改变系统输出的流向 : System.setOut(打印流)
         System.setOut(ps);
         //System.out.println()  数据输出都不会到控台,到了log.txt
         //System.out.print()   数据输出都不会到控台,到了log.txt
         System.out.println("HelloWorld!!");
         System.out.print("HelloJava!!");
     }
    }
    

    8 装饰设计模式

  • 设计模式 : 一套良好的编码风格 , 经过众多的开发人员不断的测试总结而来

    学习目标

  • 熟悉装饰设计模式的使用

    内容讲解

    【1】概述

  • 作用:在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。

  • 不使用继承技术扩展功能, 可以降低耦合(一个类A明显用到另外一个类B,A对于B是偶尔)
  • 使用原则:

    • 装饰类被装饰类需要有共同的父类型
      • 在之前学习过的 BufferedWriter 和 FileWriter 就是装饰设计模式
      • BufferedWriter的父类为Writer
      • FileWriter的父类也是Writer
      • 我们把FileWriter的对象传递到BufferedWriter的构造中 , 那么可以理解为BufferedWriter是装饰类 , FileWriter是被装饰类
      • BufferedWriter对FileWriter的功能做了增强
    • 装饰类的构造要接收被装饰类的对象
      • FileWriter fw = new FileWriter(“路径”);
      • BufferedWriter bw = new BufferedWriter(fw);
    • 在装饰类中把要增强扩展的功能进行扩展
      • BufferedWriter和FileWriter的功能一样, 都具备Writer中写数据的功能
      • 但是BufferedWriter提供了缓冲区 , 相当于对FileWriter功能做了扩展
    • 对于不要增强的功能直接调用
      • 不需要增强的功能直接继承父类的即可

        【2】代码实践

        已知有接口Star和其子类型LiuDeHua。
        public interface Star {
        public abstract void sing();
        public abstract void dance();
        }
        
        需求 :在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。
        LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展
        思路 :
        定义一个装饰类,去装饰增强 LiuDehua类。
        步骤:
  • 创建LiuDeHua类并实现接口Star【被装饰类】

  • 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
  • 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
  • 在装饰类中对sing方法进行功能扩展
  • 对dance不做改动
  • 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
    package com.itheima.design_demo;
    /*
      装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能
      使用原则 :
          1. 装饰类和被装饰类需要有共同的父类型。
          2. 装饰类要传入被装饰类的对象
          3. 在装饰类中把要增强扩展的功能进行扩展
          4. 对于不要增强的功能直接调用
      需求 : 在不改变LiuDeHua类,及不使用继承的技术前提下,动态的扩展LiuDeHua的sing功能。
              LiuDeHua就是一个被装饰类 , 需要对唱歌的功能进行扩展
      步骤:
          1. 创建LiuDeHua类并实现接口Star【被装饰类】
          2. 定义一个装饰类LiuDeHuaWrapper实现Star 【装饰类】
          3. 在装饰类里面定义一个成员变量类型是LiuDeHua,可以使用构造方法进行传入被装饰类对象。
          4. 在装饰类中对sing方法进行功能扩展
          5. 对dance不做改动
          6. 测试类分别创建装饰类的对象和被装饰类的对象。将被装饰类对象刘德华对象设置给装饰类对象
    */
    public class Test {
      public static void main(String[] args) {
          //被装饰对象
          LiuDeHua deHua = new LiuDeHua();
          //FileReader fr = new FileReader()
          //装饰对象
          LiuDeHuaWrapper liuDeHuaWrapper = new LiuDeHuaWrapper(deHua);
          //BufferedReader br = new BufferedReader(fr);
          liuDeHuaWrapper.sing();
          liuDeHuaWrapper.dance();
      }
    }
    // 明星接口 , 装饰类和被装饰类的父类型
    interface Star {
      void sing(); // 唱歌
      void dance();// 跳舞
    }
    //被装饰类
    class LiuDeHua implements Star{
      @Override
      public void sing() {
          System.out.println("刘德华在唱忘情水!");
      }
      @Override
      public void dance() {
          System.out.println("刘德华在跳舞!");
      }
    }
    //装饰类
    class LiuDeHuaWrapper implements Star{
      private LiuDeHua liuDeHua;
      //使用构造方法设置被装饰类对象
      public LiuDeHuaWrapper(LiuDeHua liuDeHua) {
          this.liuDeHua = liuDeHua;
      }
      @Override
      public void sing() {
          //功能改造,增强
          System.out.println("刘德华在深情的演唱《忘情水》");
      }
      @Override
      public void dance() {
          //不用增强的功能
          liuDeHua.dance();
      }
    }
    

    内容小结

  1. 装饰类和被装饰类需要有共同的父类型。
  2. 装饰类要传入被装饰类的对象
  3. 在装饰类中把要增强扩展的功能进行扩展
  4. 对于不要增强的功能直接调用

    9 commons-io工具包(对文件的拷贝做优化)

    学习目标

  • 能够熟悉导入commons-io工具包,并使用

    内容讲解

    【1】三方库的导入

  1. 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
  2. 把commons-io-2.6.jar包复制到指定的Module的lib目录中
  3. 将commons-io-2.6.jar加入到项目中
    右键jar包:
    day11 字符流,编码表,对象流,其他流 - 图6
    day11 字符流,编码表,对象流,其他流 - 图7

    【2】API

    1)org.apache.commons.io.IOUtils类 ```java public static int copy(InputStream in, OutputStream out): 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)

public static long copyLarge(InputStream in, OutputStream out): 把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)

```java
package com.itheima.commons_io;
import org.apache.commons.io.IOUtils;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
/*
    org.apache.commons.io.IOUtils类
    public static int copy(InputStream in, OutputStream out):
    把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以下)
    public static long copyLarge(InputStream in, OutputStream out):
    把input输入流中的内容拷贝到output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
 */
public class Test1 {
    public static void main(String[] args) throws IOException {
        IOUtils.copy(new FileInputStream("D:\\传智播客\\安装包\\好看的图片\\liqin.jpg") , new FileOutputStream("day12_demo\\copy.jpg"));
    }
}

2)org.apache.commons.io.FileUtils

public static void copyFileToDirectory(final File srcFile, final File destFile): 
    复制文件到另外一个目录下。
public static void copyDirectoryToDirectory(File src , File dest ):
    复制src目录到dest位置。

代码实践:

package com.itheima.commons_io;
import org.apache.commons.io.FileUtils;
import java.io.File;
import java.io.IOException;
/*
    org.apache.commons.io.FileUtils
    public static void copyFileToDirectory(final File srcFile, final File destFile):
    复制文件到另外一个目录下。
    public static void copyDirectoryToDirectory(File src , File dest ):
    复制src目录到dest目录中。
 */
public class Test2 {
    public static void main(String[] args) throws IOException {
        FileUtils.copyDirectoryToDirectory(new File("D:\\传智播客\\安装包\\好看的图片") , new File("D:\\"));
    }
}

内容小结

commons-io可以简化IO复制文件的操作。