File类与IO流

File类与IO流

  1. 分隔符

    • Demo01File类–(File类)

      package cn.xiaoge.day18.demo01;
      
      /*
          java.io.File类
          文件和目录路径名的抽象表示形式.
          java把电脑中的文件和文件夹(目录)封装为了一个File类, 我们可以使用File类对文件和文件夹进行操作
          我们可以使用File类的方法
              创建一个文件/文件夹
              删除文件/文件夹
              获取文件/文件夹
              判断文件/文件夹
              对文件进行遍历
              获取文件的大小
          File类是一个与系统无关的类, 任何的操作系统都可以使用这个类中的方法
      
          重点: 记住是哪个单词
              file: 文件
              directory: 文件夹
              path: 路径
       */
      
      import java.io.File;
      
      public class Demo01File {
      
          public static void main(String[] args) {
              /*
                  static String pathSeparator 与系统有关的路径分隔符, 为了方便, 它被表示为一个字符串.
                  static char pathSeparatorChar 与系统有关的路径分隔符.
      
                  static String separator 与系统有关的默认名称分隔符, 为了方便, 它被表示为一个字符串
                  static char separatorChar 与系统有关的默认名称分隔符
      
                  操作路径: 路径不能写死
                  C:\develop\a\a.txt windows
                  C:/develop/a/a.txt Linux
      
                  "C:" + File.separator +develop + File.separator + a + File.separator + a.txt"
               */
              String pathSeparator = File.pathSeparator;
              System.out.println(pathSeparator); // : 路径分隔符 windows: 分号;  linux: 冒号:
      
              char pathSeparatorChar = File.pathSeparatorChar;
              System.out.println(pathSeparatorChar); // :
      
              String separator = File.separator;
              System.out.println(separator); // / 文件名称分隔符 Windows: 文件分隔符\  Linux: 文件分割符/
      
              char separatorChar = File.separatorChar;
              System.out.println(separatorChar); // /
          }
      
      }
      
      
      // 运行结果
      :
      :
      /
      /
      
  2. 绝对路径和相对路径–(File类)

    • Demo02File

      package cn.xiaoge.day18.demo01;
      
      /*
          路径:
              绝对路径: 事宜额完整的路径
                  以盘(C: D:)开始的路径
                  D:\\demo\b.txt
              相对路径: 是一个简化的路径
                  相对指的是相对于当前项目的根目录(C:\\Users\\XiaoGe\\123.txt)
                  C:\\Users\\XiaoGe\\123.txt ---> 简化为: 123.txt(可以省略项目的根目录)
      
              注意:
                  1. 路径不区分大小写
                  2. 路径中的文件名称分隔符Windows使用反斜杠, 反斜杠是转义字符, 两个反斜杠代表一个普通的反斜杠
       */
      
      import java.io.File;
      
      public class Demo02File {
      
          public static void main(String[] args) {
              /*
                  File类的构造方法
               */
              show01();
              System.out.println("=========================================");
              show02("/", "a.txt");
              System.out.println("=========================================");
              show03();
              System.out.println("=========================================");
          }
      
          /*
              File(File parent, String child) 根据 parent 抽象路径和 child 路径名字符串创建新 File 实例.
              参数: 把路径分成了两部分
                  File parent: 父路径
                  String child: 子路径
              好处:
                  父路径和子路径, 可以单独书写, 使用起来非常灵活, 父路径和子路径都可以变化
                  父路径是File类型, 可以使用File类的方法对路径进行一些操作, 在使用路径创建对象
           */
          private static void show03() {
              File parent = new File("/");
              File file = new File(parent, "hello.java");
              System.out.println(file); // /hello.java
          }
      
          /*
              File(String parent, String child) 根据 parent 路径名字符串和 child 路径名字符串创建一个新 File 实例
              参数: 把路径分成了两部分
                  String parent: 父路径
                  String child: 子路径
              好处:
                  父路径和子路径, 可以单独书写, 使用起来非常灵活, 父路径和子路径都可以变化
           */
          private static void show02(String parent, String child) {
      
              File f = new File(parent, child);
              System.out.println(f); // /a.txt
      
          }
      
          /*
              File(String pathname) 通过将给定路径名字符串转换为抽象路径名来创建一个新 File 实例.
              参数:
                  String pathname: 字符串的路径名称
                  路径可以是以文件结尾, 也可以是文件夹结尾
                  路径可以是相对路径, 也可以是绝对路径
                  路径可以是存在的, 也可以是不存在
                  创建File对象, 只是把字符串路径封装为File对象, 不考虑路径的真假情况
           */
          private static void show01() {
              // 我是linux所以路径是这样的
              File f1 = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt");
              System.out.println(f1); // 重写了Object类的toString方法 /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt
      
              File f2 = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01");
              System.out.println(f2); // /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01
      
              File f3 = new File("a.txt");
              System.out.println(f3); // a.txt
          }
      
      }
      
      // 运行结果
      /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt
      /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01
      a.txt
      =========================================
      /a.txt
      =========================================
      /hello.java
      =========================================
      
  3. File类获取功能的方法–(File类)

    • Demo03File

      package cn.xiaoge.day18.demo01;
      
      /*
          File类获取功能的方法
              public String getAbsolutePath() :返回此File的绝对路径名字符串。
              public String getPath() :将此File转换为路径名字符串。
              public String getName() :返回由此File表示的文件或目录的名称。
              public long length() :返回由此File表示的文件的长度。
       */
      
      import java.io.File;
      
      public class Demo03File {
      
          public static void main(String[] args) {
              show01();
              System.out.println("==========================================");
              show02();
              System.out.println("==========================================");
              show03();
              System.out.println("==========================================");
              show04();
          }
      
          /*
              public long length() :返回由此File表示的文件的长度。
              获取的是构造方法指定的文件的大小, 以字节为单位
              注意:
                  文件夹是没有大小概念的. 不能获取文件夹大小
                  如果构造方法中给出的路径不存在, 那么length返回0
           */
          private static void show04() {
              File f1 = new File("/Users/xiaoge/Downloads/桌面项目/web/images/3.jpeg");
              long l1 = f1.length();
              System.out.println(l1); // 65395字节
      
              File f2 = new File("/Users/xiaoge/Downloads/桌面项目/web/images/3.jpg");
              long l2 = f2.length();
              System.out.println(l2); // 0字节
      
              File f3 = new File("/Users/xiaoge/Downloads/桌面项目/web/images");
              long l3 = f3.length();
              System.out.println(l3); // 0字节 文件夹没有大小概念的
          }
      
          /*
              public String getName() :返回由此File表示的文件或目录的名称。
              获取的就是构造方法传递路径的结尾部分(文件/文件夹)
           */
          private static void show03() {
      
              File f1 = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt");
              String name1 = f1.getName();
              System.out.println(name1); // a.txt
      
              File f2 = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01");
              String name2 = f2.getName();
              System.out.println(name2); // demo01
      
          }
      
          /*
              public String getPath() :将此File转换为路径名字符串。
              获取的构造方法中传递的路径
      
              toString方法调用的就是getPath方法
              源码:
                  public String toString() {
                      return getPath();
                  }
           */
          private static void show02() {
              File f1 = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt");
              String absolutePath = f1.getPath();
              System.out.println(absolutePath); // /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt
      
              File f2 = new File("a.txt");
              String absolutePath2 = f2.getPath();
              System.out.println(absolutePath2); // a.txt
          }
      
          /*
              public String getAbsolutePath() :返回此File的绝对路径名字符串。
              获取的构造方法中传递的路径
              无论路径是绝对的还是相对的, getAbsolutePath返回此File的绝对路径名字符串。
           */
          private static void show01() {
              File f1 = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt");
              String absolutePath = f1.getAbsolutePath();
              System.out.println(absolutePath); // /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt
      
              File f2 = new File("a.txt");
              String absolutePath2 = f2.getAbsolutePath();
              System.out.println(absolutePath2); // /Users/xiaoge/IdeaProjects/day18-code/a.txt
          }
      
      }
      
      // 运行结果
      /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt
      /Users/xiaoge/IdeaProjects/day18-code/a.txt
      ==========================================
      /Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/a.txt
      a.txt
      ==========================================
      a.txt
      demo01
      ==========================================
      65395
      0
      578
      
  4. File类判断功能的方法–(File类)

    • Demo04File

      package cn.xiaoge.day18.demo01;
      
      /*
          File判断功能的方法:
              public boolean exists() :此File表示的文件或目录是否实际存在。
              public boolean isDirectory() :此File表示的是否为目录。
              public boolean isFile() :此File表示的是否为文件。
       */
      
      import java.io.File;
      
      public class Demo04File {
      
          public static void main(String[] args) {
              show01();
              System.out.println("======================================");
              show02();
              System.out.println("======================================");
          }
      
          /*
              public boolean isDirectory() :此File表示的是否为目录。
                  用于判断构造方法中给定的路径是否以文件夹结尾
                      是: true
                      否: false
              public boolean isFile() :此File表示的是否为文件。
                  用于判断构造方法中给定的路径是否以文件结尾
                      是: true
                      否: false
              注意:
                  电脑的硬盘中只有文件/文件夹, 两个方法是互斥
                  这两个方法使用前提, 路径必须是存在的, 否则都返回false
           */
          private static void show02() {
              File f1 = new File("/Users/xiaoge/Downloads/桌面项目/web/images/3.jpg");
      
              // 不存在, 没有必要获取
              if (f1.exists()) {
                  System.out.println(f1.isDirectory());
                  System.out.println(f1.isFile());
              }
      
              File f2 = new File("/Users/xiaoge/Downloads/桌面项目/web/images/3.jpeg");
              if (f2.exists()) {
                  System.out.println(f2.isDirectory()); // false
                  System.out.println(f2.isFile()); // true
              }
      
              File f3 = new File("/Users/xiaoge/Downloads/桌面项目/web/images");
              if (f3.exists()) {
                  System.out.println(f3.isDirectory()); // true
                  System.out.println(f3.isFile()); // false
              }
      
          }
      
          /*
              public boolean exists() :此File表示的文件或目录是否实际存在。
              用于判断构造方法中的路径是否存在
                  存在: true
                  不存在: false
           */
          private static void show01() {
              File f1 = new File("/Users/xiaoge/Downloads/桌面项目/web/images/3.jpeg");
              System.out.println(f1.exists()); // true
      
              File f2 = new File("/Users/xiaoge/Downloads/桌面项目/web/images/3.jpg");
              System.out.println(f2.exists()); // false
      
              File f3 = new File("day18-code");
              System.out.println(f3.exists()); // true
      
              File f4 = new File("day19-code");
              System.out.println(f4.exists()); // false
          }
      
      }
      
      
      // 运行结果
      true
      false
      true
      false
      ======================================
      false
      true
      true
      false
      ======================================
      
      
  5. File类创建删除功能的方法–(File类)

    • Demo05File

      package cn.xiaoge.day18.demo01;
      
      /*
          File类创建删除功能的方法
              public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
              public boolean delete() :删除由此File表示的文件或目录。
              public boolean mkdir() :创建由此File表示的目录。
              public boolean mkdirs() :创建由此File表示的目录,包括任何必需但不存在的父目录。
       */
      
      import java.io.File;
      import java.io.IOException;
      
      public class Demo05File {
      
          public static void main(String[] args) throws IOException {
              show01();
              System.out.println("==================================");
              show02();
              System.out.println("==================================");
              show03();
          }
      
          /*
              public boolean delete() :删除由此File表示的文件或目录.
              吃方法, 可以删除构造方法路径中给出的文件/文件夹
              返回值: 布尔值
                  true: 文件/文件夹删除成功, 返回true
                  false: 文件夹中有内容, 不会删除返回false; 构造方法中路径不存在false
              注意:
                  delete方法是直接在硬盘删除文件/文件夹, 不走回收站, 删除要谨慎
           */
          private static void show03() {
              File f1 = new File("day18-code/src/cn/xiaoge/day18/demo01/AAA");
              boolean b1 = f1.delete();
              System.out.println("b1: " + b1);
      
              File f2 = new File("day18-code/src/cn/xiaoge/day18/demo01/BBB/CCC/DDD");
              boolean b2 = f2.delete();
              System.out.println("b2: " + b2);
      
              File f3 = new File("day18-code/src/cn/xiaoge/day18/demo01/a.txt");
              boolean b3 = f3.delete();
              System.out.println("b3: " + b3);
      
              File f4 = new File("day18-code/src/cn/xiaoge/day18/demo01/1.txt");
              boolean b4 = f4.delete();
              System.out.println("b4: " + b4);
      
              File f5 = new File("day18-code/src/cn/xiaoge/day18/demo01/2.txt");
              boolean b5 = f5.delete();
              System.out.println("b5: " + b5);
      
              File f6 = new File("day18-code/src/cn/xiaoge/day18/demo01/新建文件夹");
              boolean b6 = f6.delete();
              System.out.println("b6: " + b6);
      
          }
      
          /*
              public boolean mkdir() : 创建单级空文件夹
              public boolean mkdirs() :既可以创建单级空文件夹, 也可以创建多级文件夹
              创建文件的路径和名称在构造方法中给出(构造方法的参数)
              返回值: 布尔值
                  true: 文件夹不存在, 创建文件, 返回true
                  false: 文件夹存在, 不会创建, 返回false; 构造方法中给出的路径不存在返回false
              注意:
                  1. 此方法只能创建文件夹, 不能创建文件
                  2. 创建文件夹的路径必须存在, 否则抛出异常
           */
          private static void show02() {
              File f1 = new File("day18-code/src/cn/xiaoge/day18/demo01/AAA");
              boolean b1 = f1.mkdir();
              System.out.println("b1: " + b1);
      
              File f2 = new File("day18-code/src/cn/xiaoge/day18/demo01/BBB/CCC/DDD");
              boolean b2 = f2.mkdirs();
              System.out.println("b2: " + b2);
      
              File f3 = new File("day18-code/src/cn/xiaoge/day18/demo01/a.txt");
              boolean b3 = f3.mkdir();
              System.out.println("b3: " + b3); // 看类型, 是一个文件夹
      
              File f4 = new File("day18-code/src/cn/xiaoge/day19/demo01/a.txt");
              boolean b4 = f4.mkdir();
              System.out.println("b4: " + b4); // 不会抛出异常, 路径不存在, 不会创建
          }
      
          /*
              public boolean createNewFile() :当且仅当具有该名称的文件尚不存在时,创建一个新的空文件。
              创建文件的路径和名称在构造方法中给出(构造方法的参数)
              返回值: 布尔值
                  true: 文件不存在, 创建文件, 返回true
                  false: 文件存在, 不会创建, 返回false
              注意:
                  1. 此方法只能创建文件, 不能创建文件夹
                  2. 创建文件夹的路径必须存在, 否则抛出异常
      
              public boolean createNewFile() throws IOException
              createNewFile声明抛出了IOException, 我们调用这个方法, 就必须的处理这个异常, 要么throws, 要么try..catch
           */
          private static void show01() throws IOException {
              File f1 = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/1.txt");
              boolean b1 = f1.createNewFile();
              System.out.println("b1: " + b1);
      
              File f2 = new File("day18-code/src/cn/xiaoge/day18/demo01/2.txt");
              boolean b2 = f2.createNewFile();
              System.out.println("b2: " + b2);
      
              File f3 = new File("day18-code/src/cn/xiaoge/day18/demo01/新建文件夹");
              boolean b3 = f3.createNewFile();
              System.out.println("b3: " + b3); // 不要被名称迷惑, 要看类型
      
              /*File f4 = new File("day18-code/src/cn/xiaoge/day19/demo01/新建文件夹");
              boolean b4 = f4.createNewFile();
              System.out.println("b4: " + b4); // 路径不存在, 抛出IOException*/
          }
      
      }
      
      
      // 运行结果
      b1: true
      b2: true
      b3: true
      ==================================
      b1: true
      b2: true
      b3: true
      b4: false
      ==================================
      b1: true
      b2: true
      b3: true
      b4: true
      b5: true
      b6: true
      
  6. File类遍历(文件夹)目录功能–(File类)

    • Demo06File

      package cn.xiaoge.day18.demo01;
      
      /*
          File类遍历(文件夹)目录功能
              public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
              public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
      
          注意:
              list方法和listFiles方法遍历的是构造方法中给出的目录
      
              如果构造方法中给出的目录的路径不存在, 会抛出空指针异常
      
              如果构造方法中给出的路径不是一个目录, 也会抛出空指针异常
       */
      
      import java.io.File;
      
      public class Demo06File {
      
          public static void main(String[] args) {
              show01();
              System.out.println("===================================");
              show02();
          }
      
          /*
              public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
              遍历构造方法中给出的目录, 会获取目录中所有的文件/文件夹, 把文件/文件夹封装为File对象, 多个File对象存储到File数组中
           */
          private static void show02() {
              File file = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01");
              File[] files = file.listFiles();
              for (File f: files) {
                  System.out.println(f.getName());
              }
          }
      
          /*
              public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
              遍历构造方法中给出的目录, 会获取目录中所有文件/文件夹的名称, 把获取到的多个名称存储到一个String类型的数组中
           */
          private static void show01() {
              File file = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01");
              // File file = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo01/Demo01File.java"); // NullPointerException
              // File file = new File("/Users/xiaoge/IdeaProjects/day18-code/day18-code/src/cn/xiaoge/day18/demo20"); // NullPointerException
              String[] arr = file.list();
              for (String fileName: arr) {
                  System.out.println(fileName);
              }
          }
      
      }
      
      
      // 运行结果
      BBB
      Demo01File.java
      Demo02File.java
      Demo03File.java
      Demo04File.java
      Demo05File.java
      Demo06File.java
      ===================================
      BBB
      Demo01File.java
      Demo02File.java
      Demo03File.java
      Demo04File.java
      Demo05File.java
      Demo06File.java
      
  7. 递归自己调用自己–(递归)

    • Demo07Recurison

      package cn.xiaoge.day18.demo02;
      
      /*
          递归: 方法自己调用自己。
      
          递归的分类:
              递归分为两种,直接递归和间接递归。
              直接递归称为方法自身调用自己。
              间接递归可以A方法调用B方法,B方法调用C方法,C方法调用A方法。
      
          注意事项:
              递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
              在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
              构造方法,禁止递归
      
          递归的使用前提:
              当调用方法的时候, 方法的主体不变, 每次调用方法的参数不同, 可以使用递归
       */
      
      public class Demo07Recurison {
          public static void main(String[] args) {
              // a();
              // b(1);
          }
      
          /*
              构造方法,禁止递归
                  编译报错: 构造方法是创建对象使用的, 一直递归会导致内存中与无数个对象, 直接编译报错.
           */
          public Demo01Recurison() {
              // Demo01File();
          }
      
          /*
                  在递归中虽然有限定条件,但是递归次数不能太多。否则也会发生栈内存溢出。
                  Exception in thread "main" java.lang.StackOverflowError
               */
          private static void b(int i) {
              System.out.println(i);
              if (i == 20000) {
                  return; // 结束函数
              }
              b(++i);
          }
      
          /*
              递归一定要有条件限定,保证递归能够停止下来,否则会发生栈内存溢出。
              Exception in thread "main" java.lang.StackOverflowError
           */
          private static void a() {
              System.out.println("a方法!");
              a();
          }
      }
      
      
      
  8. 使用递归计算1-n之间的和–(递归)

    • Demo08Recurison

      package cn.xiaoge.day18.demo02;
      
      /*
          练习:
              使用递归计算1-n之间的和
       */
      
      public class Demo08Recurison {
      
          public static void main(String[] args) {
              int s = sum(3);
              System.out.println(s);
          }
      
          /*
              定义一个方法, 使用递归计算1-n之间的和
              1+2+3..+n
              n+(n-1)+(n-2)+..+1
              已知:
                  最大值: n
                  最小值: 1
              使用递归必须明确:
                  1. 递归的结束条件
                      获取到1的时候结束
                  2. 递归的目的
                      获取下一个被加的数字(n-1)
           */
          public static int sum(int n) {
              if (n == 1) {
                  return 1;
              }
      
              return n + sum(n-1);
          }
      }
      
      
      // 运行结果
      6
      
  9. 使用递归计算阶乘–(递归)

    • Demo09Recurison

      package cn.xiaoge.day18.demo02;
      
      /*
          练习:
              使用递归计算阶乘
              n的阶乘: n! = n * (n-1)*...* 3 * 2 * 1
       */
      
      public class Demo09Recurison {
          public static void main(String[] args) {
              int flag = factorial(5);
              System.out.println(flag);
          }
      
          /*
              定义方法使用递归计算阶乘
              5的阶乘: 5! = 5 * (5-1) * (5-2)* (5-3) * (5-4) = 5 * 4 * 3 * 2 * 1
              递归结束的条件
                  获取到1的时候结束
              递归的目的
                  获取下一个被乘的数字(n-1)
              方法的参数发生变化
                  5, 4, 3, 2, 1
           */
          public static int factorial(int n) {
              if (n == 1) {
                  return 1;
              }
              return n * factorial(n-1);
          }
      }
      
      
      // 运行结果
      120
      
  10. 递归打印多级目录–(递归)

    • Demo10Recurison

      package cn.xiaoge.day18.demo02;
      
      /*
          练习:
              递归打印多级目录
          需求:
              遍历/Users/xiaoge/Desktop/abc文件夹, 及abc文件夹的子文件夹
              /Users/xiaoge/Desktop/abc
              /Users/xiaoge/Desktop/abc/adc.txt
              /Users/xiaoge/Desktop/abc/abc.java
              /Users/xiaoge/Desktop/abc/a
              /Users/xiaoge/Desktop/abc/a/a.jpg
              /Users/xiaoge/Desktop/abc/a/a.java
              /Users/xiaoge/Desktop/abc/b
              /Users/xiaoge/Desktop/abc/b/b.java
              /Users/xiaoge/Desktop/abc/b/b.txt
       */
      
      import java.io.File;
      
      public class Demo10Recurison {
          public static void main(String[] args) {
              File file = new File("/Users/xiaoge/Desktop/abc");
              getAllFile(file);
          }
      
          /*
              定义一个方法, 参数传递File类型的目录
              方法中对目录进行遍历
           */
          public static void getAllFile(File dir) {
              System.out.println(dir);
              File[] files = dir.listFiles();
              for (File file : files) {
                  if (file.isDirectory()){
                      // file是一个文件夹, 则继续遍历这个文件夹
                      // 我们发现getAllFile方法就是传递文件夹, 遍历文件夹的方法
                      // 所以直接调用getAllFile方法即可: 递归(自己调用自己)
                      getAllFile(file);
                  }
                  System.out.println(file);
              }
          }
      }
      
      // 运行结果
      /Users/xiaoge/Desktop/abc
      /Users/xiaoge/Desktop/abc/.DS_Store
      /Users/xiaoge/Desktop/abc/a
      /Users/xiaoge/Desktop/abc/a/a.java
      /Users/xiaoge/Desktop/abc/a/a.jpg
      /Users/xiaoge/Desktop/abc/a
      /Users/xiaoge/Desktop/abc/abc.java
      /Users/xiaoge/Desktop/abc/adc.txt
      /Users/xiaoge/Desktop/abc/b
      /Users/xiaoge/Desktop/abc/b/.DS_Store
      /Users/xiaoge/Desktop/abc/b/b.java
      /Users/xiaoge/Desktop/abc/b/b.txt
      /Users/xiaoge/Desktop/abc/b
      
  11. 递归打印多级目录–(递归)

    • Demo11Recurison

      package cn.xiaoge.day18.demo02;
      
      /*
          练习:
              递归打印多级目录
          需求:
              遍历/Users/xiaoge/Desktop/abc文件夹, 及abc文件夹的子文件夹, 打印出所有后缀为.java的文件
              /Users/xiaoge/Desktop/abc/abc.java
              /Users/xiaoge/Desktop/abc/a/a.java
              /Users/xiaoge/Desktop/abc/b/b.java
       */
      
      import java.io.File;
      
      public class Demo11Recurison {
          public static void main(String[] args) {
              File file = new File("/Users/xiaoge/Desktop/abc");
              getAllFile(file);
          }
      
          /*
              定义一个方法, 参数传递File类型的目录
              方法中对目录进行遍历
           */
          public static void getAllFile(File dir) {
              File[] files = dir.listFiles();
              for (File file : files) {
                  if (file.isDirectory()){
                      // file是一个文件夹, 则继续遍历这个文件夹
                      // 我们发现getAllFile方法就是传递文件夹, 遍历文件夹的方法
                      // 所以直接调用getAllFile方法即可: 递归(自己调用自己)
                      getAllFile(file);
                  }
      
                  /*
                      只要.java结尾的文件
                      1. 把File对象file, 转换为字符串对象
                   */
                  // String name = file.getName();
                  // String path = file.getPath();
                  // String s = file.toString();
                  // 把字符串转换成小写, 判断后缀是否为.java
                  if (file.getName().toLowerCase().endsWith(".java")){
                      System.out.println(file);
                  }
              }
          }
      }
      
      
      // 运行结果
      /Users/xiaoge/Desktop/abc/a/a.java
      /Users/xiaoge/Desktop/abc/abc.java
      /Users/xiaoge/Desktop/abc/b/b.java
      
  12. 递归打印多级目录–(过滤器)

    • 创建过滤器的实现类FileFilterImpl

      package cn.xiaoge.day18.demo03;
      
      import java.io.File;
      import java.io.FileFilter;
      /*
          创建过滤器FileFilter的实现类, 重写过滤方法accept, 定义过滤规则
       */
      public class FileFilterImpl implements FileFilter {
          @Override
          public boolean accept(File pathname) {
              /*
                  过滤的规则:
                      在accept方法中, 判断File对象是否是以.java结尾
                      是就返回true
                      不是就返回false
               */
              // 如果pathname是一个文件夹, 返回true, 继续遍历这个文件夹
              if (pathname.isDirectory()) {
                  return true;
              }
      
              return pathname.getName().toLowerCase().endsWith(".java");
          }
      }
      
      
    • Demo12File

      package cn.xiaoge.day18.demo03;
      
      /*
          练习:
              递归打印多级目录
          需求:
              遍历/Users/xiaoge/Desktop/abc文件夹, 及abc文件夹的子文件夹, 打印出所有后缀为.java的文件
              /Users/xiaoge/Desktop/abc/abc.java
              /Users/xiaoge/Desktop/abc/a/a.java
              /Users/xiaoge/Desktop/abc/b/b.java
          我们可以使用过滤器来实现
          在File类中有两个和listFiles重载的方法, 方法的参数传递的就是过滤器
          File[] listFiles(FileFilter filter)
          java.io.FileFilter接口: 用于抽象路径名(File对象)的过滤器
              作用: 用来过滤文件(File对象)
              抽象方法: 过滤文件的方法
                  boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中
                  参数:
                      File pathname: 使用listFiles方法遍历目录得到的每一个问价对象
      
          File[] listFiles(FilenameFilter filter)
          java.io.FilenameFilter接口: 实现此接口的类实例可用于过滤器文件名
              作用: 用于过滤文件名称
              抽象方法: 过滤文件的方法
                  boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中
                  参数:
                      File dir: 构造方法中传递的被遍历的目录
                      String name: 使用listFiles方法遍历目录, 获取的每一个文件/文件夹的名称
      
          注意:
              啷个过滤器接口是没有实现类的, 需要我们自己写实现类, 重写过滤的方法accept, 在方法中自己定义过滤的规则
       */
      
      import java.io.File;
      
      public class Demo12File {
          public static void main(String[] args) {
              File file = new File("/Users/xiaoge/Desktop/abc");
              getAllFile(file);
          }
      
          /*
              定义一个方法, 参数传递File类型的目录
              方法中对目录进行遍历
           */
          public static void getAllFile(File dir) {
              File[] files = dir.listFiles(new FileFilterImpl()); // 传递过滤器对象
              for (File file : files) {
                  if (file.isDirectory()){
                      // file是一个文件夹, 则继续遍历这个文件夹
                      // 我们发现getAllFile方法就是传递文件夹, 遍历文件夹的方法
                      // 所以直接调用getAllFile方法即可: 递归(自己调用自己)
                      getAllFile(file);
                  } else {
                      System.out.println(file);
                  }
              }
          }
      }
      
      // 运行结果
      /Users/xiaoge/Desktop/abc/a/a.java
      /Users/xiaoge/Desktop/abc/abc.java
      /Users/xiaoge/Desktop/abc/b/b.java
      
  13. 递归打印多级目录–(过滤器)

    • 创建过滤器的实现类FileFilterImpl

      package cn.xiaoge.day18.demo03;
      
      import java.io.File;
      import java.io.FileFilter;
      /*
          创建过滤器FileFilter的实现类, 重写过滤方法accept, 定义过滤规则
       */
      public class FileFilterImpl implements FileFilter {
          @Override
          public boolean accept(File pathname) {
              /*
                  过滤的规则:
                      在accept方法中, 判断File对象是否是以.java结尾
                      是就返回true
                      不是就返回false
               */
              // 如果pathname是一个文件夹, 返回true, 继续遍历这个文件夹
              if (pathname.isDirectory()) {
                  return true;
              }
      
              return pathname.getName().toLowerCase().endsWith(".java");
          }
      }
      
      
    • Demo13File

      package cn.xiaoge.day18.demo03;
      
      /*
          练习:
              递归打印多级目录
          需求:
              遍历/Users/xiaoge/Desktop/abc文件夹, 及abc文件夹的子文件夹, 打印出所有后缀为.java的文件
              /Users/xiaoge/Desktop/abc/abc.java
              /Users/xiaoge/Desktop/abc/a/a.java
              /Users/xiaoge/Desktop/abc/b/b.java
          我们可以使用过滤器来实现
          在File类中有两个和listFiles重载的方法, 方法的参数传递的就是过滤器
          File[] listFiles(FileFilter filter)
          java.io.FileFilter接口: 用于抽象路径名(File对象)的过滤器
              作用: 用来过滤文件(File对象)
              抽象方法: 过滤文件的方法
                  boolean accept(File pathname) 测试指定抽象路径名是否应该包含在某个路径名列表中
                  参数:
                      File pathname: 使用listFiles方法遍历目录得到的每一个问价对象
      
          File[] listFiles(FilenameFilter filter)
          java.io.FilenameFilter接口: 实现此接口的类实例可用于过滤器文件名
              作用: 用于过滤文件名称
              抽象方法: 过滤文件的方法
                  boolean accept(File dir, String name) 测试指定文件是否应该包含在某一文件列表中
                  参数:
                      File dir: 构造方法中传递的被遍历的目录
                      String name: 使用listFiles方法遍历目录, 获取的每一个文件/文件夹的名称
      
          注意:
              啷个过滤器接口是没有实现类的, 需要我们自己写实现类, 重写过滤的方法accept, 在方法中自己定义过滤的规则
       */
      
      import com.sun.org.apache.bcel.internal.generic.IF_ACMPEQ;
      
      import java.io.File;
      import java.io.FileFilter;
      import java.io.FilenameFilter;
      
      public class Demo13File {
          public static void main(String[] args) {
              File file = new File("/Users/xiaoge/Desktop/abc");
              getAllFile(file);
          }
      
          /*
              定义一个方法, 参数传递File类型的目录
              方法中对目录进行遍历
           */
          public static void getAllFile(File dir) {
              /*File[] files = dir.listFiles(new FileFilter() {
                  @Override
                  public boolean accept(File pathname) {
                      // 过滤规则, pathname是文件夹或则是.java结尾的文件返回true
                      return pathname.isDirectory() || pathname.getName().toLowerCase().endsWith(".java");
                  }
              }); */
      
      
              /*File[] files = dir.listFiles(new FilenameFilter() {
                  @Override
                  public boolean accept(File dir, String name) {
                      // dir是主目录, name是dir里每一个文件/文件夹的名称
                      // 过滤规则, pathname是文件夹或则是.java结尾的文件返回true
                      // dir父路径, name子路径
                      return new File(dir, name).isDirectory() || name.toLowerCase().endsWith(".java");
                  }
              });*/
      
      
              // 使用Lambda表达式优化匿名内部类(接口中只有一个抽象方法)
      /*
              File[] files = dir.listFiles((File d, String name) -> {
                      // dir是主目录, name是dir里每一个文件/文件夹的名称
                      // 过滤规则, pathname是文件夹或则是.java结尾的文件返回true
                      // dir父路径, name子路径
                      return new File(d, name).isDirectory() || name.toLowerCase().endsWith(".java");
              });
      */
      
              // 使用Lambda继续简化
              File[] files = dir.listFiles((d, name) ->
                  // dir是主目录, name是dir里每一个文件/文件夹的名称
                  // 过滤规则, pathname是文件夹或则是.java结尾的文件返回true
                  // dir父路径, name子路径
                  new File(d, name).isDirectory() || name.toLowerCase().endsWith(".java")
              );
      
      
      
      
              // 传递过滤器对象
              for (File file : files) {
                  if (file.isDirectory()){
                      // file是一个文件夹, 则继续遍历这个文件夹
                      // 我们发现getAllFile方法就是传递文件夹, 遍历文件夹的方法
                      // 所以直接调用getAllFile方法即可: 递归(自己调用自己)
                      getAllFile(file);
                  } else {
                      System.out.println(file);
                  }
              }
          }
      }
      
      // 运行结果
      /Users/xiaoge/Desktop/abc/a/a.java
      /Users/xiaoge/Desktop/abc/abc.java
      /Users/xiaoge/Desktop/abc/b/b.java
      
  14. 字节输出流OutputStream子类的共性方法–(IO字节流)

    • Demo14OutputStream

      package cn.xiaoge.day18.demo04;
      
      /*
          java.io.Demo01OutputStream: 字节输出流
              此抽象类是表示字节流的所有类的超类
      
          定义一些子类共性的成员方法:
              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) :将指定的字节输出流。
      
          java.io.FileOutputStream extends Demo01OutputStream
          FileOutputStream: 文件字节流输出
          作用: 把内存中的数据写入到硬盘当中
      
          构造方法:
              FileOutputStream(String name)创建一个向具有指定名称的文件中写入数据的输出文件流.
              FileOutputStream(File file) 创建一个向指定 File 对象表示的文件中写入数据的文件输出流.
              参数: 写入数据的目的
                  String name: 目的地是一个文件路径
                  File file: 目的地是一个文件
              构造方法的作用:
                  1. 创建一个FileOutputStream对象
                  2. 会根据构造方法中传递的文件/文件路径, 创建一个空文件
                  3. 会把FileOutputStream对象指向创建好的文件
      
          写入数据的原来(内存--->硬盘)
              java程序--->JVM(java虚拟机)--->OS(操作系统)--->OS调用写数据的方法--->把数据写入到文件中
      
          字节输出流的使用步骤(重点):
              1. 创建一个FileOutputStream对象, 构造方法中传递写入数据的目的地
              2. 调用FileOutputStream对象中的方法write, 把数据写入到文件中
              3. 释放资源(流使用会占用一定的内存, 使用完毕要把内存清空, 提供程序的效率)
       */
      
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo14OutputStream {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个FileOutputStream对象, 构造方法中传递写入数据的目的地
              FileOutputStream fos = new FileOutputStream("./a.txt");
              // 2. 调用FileOutputStream对象中的方法write, 把数据写入到文件中
              // public abstract void write(int b) :将指定的字节输出流。
              fos.write(97);
              // 3. 释放资源(流使用会占用一定的内存, 使用完毕要把内存清空, 提供程序的效率)
              fos.close();
          }
      
      }
      
  15. 一次多个字节的方法–(IO字节流)

    • Demo15OutputStream

      package cn.xiaoge.day18.demo04;
      
      /*
          一次写多个字节的方法:
               public void write(byte[] b) :将 b.length字节从指定的字节数组写入此输出流。
               public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。
       */
      
      import java.io.File;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.lang.reflect.Array;
      import java.util.Arrays;
      
      public class Demo15OutputStream {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个FileOutputStream对象, 构造方法中传递写入数据的目的地
              FileOutputStream fos = new FileOutputStream(new File("./b.txt"));
              // 2. 调用FileOutputStream对象中的方法write, 把数据写入到文件中
              // 在文件中显示100, 写个字节
              fos.write(49);
              fos.write(48);
              fos.write(48);
      
              /*
                  public void write(byte[] b): 将 b.length字节从指定的字节数组写入此输出流.
                  一次写多个字节:
                      如果写的第一个字节是整数(0-127), 那么显示的时候查询ASCII表
                      如果写的第一个字节是负数, 那第一个字节会和第二个字节, 两个字节组成一个中文显示, 查询系统默认码表(GBK)
               */
              // byte[] bytes = {65, 66, 67, 68, 69}; // ABCDE
              byte[] bytes = {-65, -66, -67, 68, 69}; // 100烤(-65, -66组成)紻(-67, 68组成)E
              fos.write(bytes);
      
              /*
                  public void write(byte[] b, int off, int len) :  把字节数组的一部分写入到文件中
                      int off: 数组的开始索引
                      int len: 写几个字节
               */
              fos.write(bytes, 1, 2); // BC
      
              /*
                  写入字符的方法: 可以使用string类中的方法把字符串, 转换为字节数组
                      byte[] getBytes() 把字符串转换为字节数组
               */
              byte[] bytes2 = "您好".getBytes();
              // System.out.println(Arrays.toString(bytes2)); // [-26, -126, -88, -27, -91, -67]
              fos.write(bytes2);
      
              // 3. 释放资源(流使用会占用一定的内存, 使用完毕要把内存清空, 提供程序的效率)
              fos.close();
          }
      
      }
      
  16. 追加写/续写–(IO字节流)

    • Demo16OutputStream

      package cn.xiaoge.day18.demo04;
      
      /*
          追加写/续写: 使用两个参数的构造方法
              FileOutputStream(String name, boolean append)创建一个向具有指定 name 的文件中写入数据的输出文件流.
              FileOutputStream(File file, boolean append)创建一个向指定 File 对象表示的文件中写入数据的文件输出流.
      
              参数:
                  String name, File file: 写入数据的目的地
                  boolean append: 追加写开关
                  true: 创建对象不会覆盖原文件, 继续在文件的末尾追加写数据
                  false: 创建一个新文件, 覆盖源文件
      
          写换行: 写换行符号
              windows: \r\n
              linux: \n
              mac: \r 或 \n
       */
      
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo16OutputStream {
      
          public static void main(String[] args) throws IOException {
              FileOutputStream fos = new FileOutputStream("./c.txt", true);
              for (int i = 0; i < 10; i++) {
                  fos.write("你好".getBytes());
                  fos.write("\n".getBytes());
              }
              fos.close();
          }
      
      }
      
  17. 字节输入流InputStream子类的共性方法–(IO字节流)

    • Demo17InputStream

      package cn.xiaoge.day18.demo05;
      
      /*
          java.io.InputStream: 字节流入流
          此抽象类是表示字节输入流的所有类的超类
      
          定义了所有子类共性的方法:
              int read()从输入流中读取数据的下一个字节
              int read(byte[] b) 从输入流中读取一定数量的字节, 并将其存储在缓冲区数组 b 中
              void close() 关闭此输入流并释放与该流关联的所有系统资源
      
          java.io.FileInputStream extends InputStream
          FileInputStream: 文件字节输入流
          作用: 把硬盘文件中的数据, 读取到内存中使用
      
          构造方法:
              FileInputStream(String name)
              FileInputStream(File file)
              参数: 读取文件的数据源
                  String name: 文件的路径
                  File file: 文件
              构造方法的作用:
                  1. 会创建一个FileInputStream对象
                  2. 会把FileInputStream对象指定构造方法中要读取的文件
      
          读取数据的原理(硬盘-->内存)
              java程序-->JVM-->OS-->读取数据的方法-->读取文件
      
          字节输入流的使用步骤(重点):
              1. 创建FileInputStream对象, 构造方法中绑定要读取的数据源
              2. 使用FileInputStream对象中的方法read, 读取文件
              3. 释放资源
       */
      
      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class Demo17InputStream {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建FileInputStream对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("./a.txt");
              // 2. 使用FileInputStream对象中的方法read, 读取文件
              // int read()读取文件中的一个字节并返回, 读取到文件的末尾返回-1
      
              /*
                  int len = fis.read();
                  System.out.println(len); // 97 a
      
                  len = fis.read();
                  System.out.println(len); // 98 b
      
                  len = fis.read();
                  System.out.println(len); // 99 c
      
                  len = fis.read();
                  System.out.println(len); // -1
      
                  len = fis.read();
                  System.out.println(len); // -1
              */
      
              /*
                  发现以上的读取文件是一个重复的过程, 所以可以使用循环优化
                  不知道文件中有多少个字节, 使用while循环
                  while循环结束条件, 读取到-1的时候结束
      
                  布尔表达式(len = fis.read()) != -1
                          1. fis.read(): 读取一个字节
                          2. len = fis.read(): 读取到的字节赋值给变量len
                          3. (len = fis.read()) != -1: 判断变量len是否不等于-1
               */
              int len = 0; // 计入读取到的字节
              while ((len = fis.read())!= -1) {
                  System.out.print((char)len);
              }
      
              // 3. 释放资源
              fis.close();
          }
      
      }
      
      // 运行结果
      a
      
  18. 字节输入流一次读取多个字节的方法–(IO字节流)

    • Demo18InputStream

      package cn.xiaoge.day18.demo05;
      
      /*
          字节输入流一次读取多个字节的方法:
              int read(byte[] b) 从输入流中读取一定数量的字节, 并将其存储在缓冲区数组 b 中
          明确两件事:
              1. 方法的参数byte[]的作用?
                  起到缓冲的作用, 存储每次读取到的多个字节
                  数组的长度一般定义1024(1kb)或者1024的整数倍
              2. 方法的返回值int是什么?
                  每次读取的有效字节个数
      
          String类的构造方法
              String(byte[] bytes): 把字节数组转换为字符串
              String(byte[] bytes, int offset, int length) 把字节数组的一部分转换为字符串 offset: 数组的开始索引 length: 转换的字节个数
       */
      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class Demo18InputStream {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建FileInputStream对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("./b.txt");
              // 2. 使用FileInputStream对象中的方法read, 读取文件
      
              // byte[] bytes = new byte[2];
              // int len = fis.read(bytes); // 把读取的内容存到bytes数组中
              // System.out.println(new String(bytes)); // AB
              // System.out.println(len); // 2
      
              byte[] bytes = new byte[1024];
              int len = 0;
              while((len = fis.read(bytes)) != -1) {
                  System.out.print(new String(bytes, 0, 7)); //ABCDEFG
              }
              // 3. 释放资源
              fis.close();
          }
      
      }
      
      // 运行结果--(不要看上面我写的OutputStream文件操作b.txt, b.txt类容单独被改过)
      ABCDEFG
      
  19. 复制文件

    • CopyFile

      package cn.xiaoge.day18.demo06;
      
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      /*
          文件复制练习: 一读一写
      
          明确:
              数据源: ./4.jpeg
              数据的目的地: ./day18-code/4.jpeg
      
          文件复制的步骤:
              1. 创建一个字节输入流对象, 构造方法中绑定要读取的数据源
              2. 创建一个字节输出流对象, 构造方法中绑定要写入的目的地
              3. 使用字节输入流对象中的方法read读取文件
              4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
              5. 释放资源
       */
      
      public class CopyFile {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个字节输入流对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("./4.jpeg");
              // 2. 创建一个字节输出流对象, 构造方法中绑定要写入的目的地
              FileOutputStream fos = new FileOutputStream("./day18-code/4.jpeg");
      
      
              // 3. 使用字节输入流对象中的方法read读取文件
              // 一次读取一个字节写入一个字节的方式
              /*
              int len = 0;
              while((len = fis.read()) != -1){
                  // 4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
                  fos.write(len);
              }*/
      
      
              // 3. 使用字节输入流对象中的方法read读取文件
              // 使用数组缓冲读取多个字节, 写入多个字节
              int len = 0; // 每次读取的有效字节个数
              byte[] bytes = new byte[1024];
              while((len = fis.read(bytes)) != -1) {
                  // 4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
                  fos.write(bytes, 0, len); // 写入从bytes数组 0位置开始写入的长度为len
              }
      
              // 5. 释放资源(先关写的, 后关闭读的; 如果写完了, 肯定读取完毕了)
              fos.close();
              fis.close();
      
          }
      
      }
      
  20. 使用字节读取中文文件–(IO字节流)

    • Demo20InputStream

      package cn.xiaoge.day18.demo07;
      
      import java.io.FileInputStream;
      import java.io.IOException;
      
      /*
          使用字节流读取中文文件
          1个中文
              GBK: 占用两个字节
              UTF-8: 占用3个字节
       */
      
      public class Demo20InputStream {
      
          public static void main(String[] args) throws IOException {
              FileInputStream fis = new FileInputStream("./c.txt");
              int len = 0;
              while((len = fis.read()) != -1) {
                  System.out.println((char)len);
              }
              fis.close();
          }
      
      }
      
  21. 字符输入流FileReader子类的共性方法–(IO字符流)

    • Demo21Reader

      package cn.xiaoge.day18.demo07;
      
      /*
          java.io.Reader: 字符输入流, 是字节输入流的最顶层的父类, 定义了一些共性的成员方法, 是一个抽象类
      
          共性的成员方法:
              int read() 读取单个字符并返回
              int read(char[] cbuf)一次读取多个字符, 将字符读入数组.
              void close() 关闭该流并释放与之关联的所有资源.
      
          java.io.FileReader extends InputStreamReader extends Reader
          FileReader: 文件字符输入流
          作用: 把硬盘文件中的数据以字符的方式读取到内存中
      
          构造方法:
              FileReader(String fileName)
              FileReader(File file)
              参数: 读取文件的数据源
                  String fileName: 文件的路径
                  File file: 一个文件
              FileReader构造方法的作用:
                  1. 创建一个FileReader对象
                  2. 会把FileReader对象指向要读取的文件
          字符输入流的使用步骤:
              1. 创建FileReader对象, 构造方法中绑定要读取的数据源
              2. 使用FileReader对象中的方法read读取文件
              3. 释放资源
       */
      
      import java.io.FileReader;
      import java.io.IOException;
      
      public class Demo21Reader {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建FileReader对象, 构造方法中绑定要读取的数据源
              FileReader fr = new FileReader("./c.txt");
      
              // 2. 使用FileReader对象中的方法read读取文件
              // int read() 读取单个字符并返回
              /*int len = 0;
              while((len = fr.read()) != -1) {
                  System.out.println((char)len);
              }*/
      
              // int read(char[] cbuf)一次读取多个字符, 将字符读入数组.
              char[] cs = new char[1024]; // 存储读取到的多个字符
              int len = 0; // 记录的是每次读取的有效字符个数
              while((len = fr.read(cs)) != -1){
                  /*
                      String类的构造方法
                      String(char[] value) // 把字符数组转换为字符串
                      String(char[] value, int offset, int count) / 把字符数组的一部分转换为字符串 offset数组的开始索引 count转换的个数
                   */
                  System.out.print(new String(cs, 0, len));
              }
      
              // 3. 释放资源
              fr.close();
          }
      
      }
      
      // 运行结果
      你好
      你好
      你好
      你好
      你好
      你好
      你好
      你好
      你好
      你好
      
  22. 字符输出流FileWriter子类的共性方法–(IO字符流)

    • Demo22Writer

      package cn.xiaoge.day18.demo08;
      
      /*
          java.io.Writer: 字符输出流, 是所有字符输出流的最顶层的父类, 是一个抽象类
      
          共性的成员方法:
              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() 关闭此流,但要先刷新它。
      
          java.io.FileWriter extends OutputStreamWriter extends Writer
          FileWriter: 文件字符输出流
          作用: 把内存中字符数据写入到文件中
      
          构造方法:
              FileWriter(File file)根据给定的 File 对象构造一个 FileWriter 对象.
              FileWriter(String fileName) 根据给定的文件名构造一个 FileWriter 对象.
              参数: 写入数据的目的地
                  String fileName: 文件的路径
                  File file: 是一个文件
              构造方法的作用:
                  1. 会创建一个FileWriter对象
                  2. 会根据构造方法中传递的文件/文件的路径, 创建文件
                  3. 会把FileWriter对象指向创建好的文件
      
          字符输出流的使用步骤(重点):
              1. 创建FileWriter对象, 构造方法中绑定要写入数据的目的地
              2. 使用FileWriter中的方法write, 把数据写入到内存缓冲区中(字符转换为字节的过程)
              3. 使用FileWriter中的方法flush, 把内存缓冲区的数据, 刷新到文件中
              4. 释放资源(会先把内存缓冲区中的数据刷新到文件中)
       */
      
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo22Writer {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建FileWriter对象, 构造方法中绑定要写入数据的目的地
              FileWriter fw = new FileWriter("./d.txt");
              // 2. 使用FileWriter中的方法write, 把数据写入到内存缓冲区中(字符转换为字节的过程)
              // void write(int c) 写入单个字符。
              fw.write(97);
              // 3. 使用FileWriter中的方法flush, 把内存缓冲区的数据, 刷新到文件中
              fw.flush();
              // 4. 释放资源(会先把内存缓冲区中的数据刷新到文件中)
              fw.close();
          }
      
      }
      
  23. close–And–flush–(IO字符流)

    • Demo23CloseAndFlush

      package cn.xiaoge.day18.demo08;
      
      /*
          flush :刷新缓冲区,流对象可以继续使用。
          close :先刷新缓冲区,然后通知系统释放资源。流对象不可以再被使用了
       */
      
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo23CloseAndFlush {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建FileWriter对象, 构造方法中绑定要写入数据的目的地
              FileWriter fw = new FileWriter("./e.txt");
              // 2. 使用FileWriter中的方法write, 把数据写入到内存缓冲区中(字符转换为字节的过程)
              // void write(int c) 写入单个字符
              fw.write(97);
              // 3. 使用FileWriter中的方法flush, 把内存缓冲区的数据, 刷新到文件中
              fw.flush();
              // 刷新之后流可以继续使用
              fw.write(98);
              // 4. 释放资源(会先把内存缓冲区中的数据刷新到文件中)
              fw.close();
      
              // close方法之后流已经关闭了, 已经从内存中消失了, 流就不能再使用了
              // fw.write(99); // IOException: Stream closed
          }
      
      }
      
      
  24. 字符输出流写数据的其他方法–(IO字符流)

    • Demo24Writer

      package cn.xiaoge.day18.demo08;
      
      /*
          字符输出流写数据的其他方法
              void write(char[] cbuf) 写入字符数组。
              void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
              void write(String str) 写入字符串。
              void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
       */
      
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo24Writer {
      
          public static void main(String[] args) throws IOException {
              FileWriter fw = new FileWriter("./f.txt");
      
              char[] cs = {'a', 'b', 'c', 'd', 'e'};
              // void write(char[] cbuf) 写入字符数组。
              fw.write(cs); // abcde
      
              // void write(char[] cbuf, int off, int len) 写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
              fw.write(cs, 1, 3); // bcd
      
              // void write(String str) 写入字符串。
              fw.write("全栈java"); // 全栈java
      
              // void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
              fw.write("全栈java", 2, 4); // java
      
              fw.close();
          }
      
      }
      
      
  25. 续写和换行–(IO字符流)

    • Demo25Writer

      package cn.xiaoge.day18.demo08;
      
      /*
          续写和换行
              续写: 追加写, 使用两个参数的构造方法
                      FileWriter(String fileName, boolean append)
                      FileWriter(File file, boolean append)
                      参数:
                          String fileName, File file: 写入数据的目的地
                          boolean append: 续写开关 true: 不会创建新的文件覆盖源文件, 可以续写; false: 创建新的文件覆盖源文件
              换行:
                  window: \r\n
                  linux: \n
                  mac: \r 或 \n
       */
      
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo25Writer {
      
          public static void main(String[] args) throws IOException {
              FileWriter fw = new FileWriter("./g.txt", true);
      
              for (int i = 0; i < 10; i++) {
                  fw.write("HelloWorld" + i + "\n");
              }
              fw.close();
          }
      
      }
      
      
  26. 在jdk1.7之前使用try catch finally 处理流中的异常–(IO字符流)

    • Demo26TryCatch

      package cn.xiaoge.day18.demo09;
      
      import java.io.FileWriter;
      import java.io.IOException;
      
      /*
          在jdk1.7之前使用try catch finally 处理流中的异常
          格式:
              try{
                  可能产生异常的代码
              }catch(异常类变量 变量名){
                  异常的处理逻辑
              }finally{
                  一定会指定的代码
                  资源释放
              }
       */
      
      public class Demo26TryCatch {
      
          public static void main(String[] args) {
              // 提高变量fw的作用域, 让finally可以使用
              // 变量在定义的视乎, 可以没有值, 但是使用时必须有值
              // fw = new FileWriter("./g.txt", true); 执行失败, fw没有值, close报错
              FileWriter fw = null;
      
              try{
                  // 可能会产生异常的代码
                  fw = new FileWriter("./g.txt", true);
      
                  for (int i = 0; i < 10; i++) {
                      fw.write("HelloWorld" + i + "\n");
                  }
      
              }catch(IOException e){
                  // 异常的处理逻辑
                  System.out.println(e);
              }finally{
                  // 一定会执行的代码
                  // 创建对象失败了, fw的默认值就是null, null是不能调用方法的, 会抛出NullPointerException, 需要增加一个判断, 不是null在把资源释放
                  if(fw != null) {
                      try {
                          // fw.close方法声明抛出了IOException异常对象, 所以我们就的处理这个异常对象, 要么throws, 要么try catch
                          fw.close();
                      } catch (IOException e) {
                          e.printStackTrace();
                      }
                  }
              }
      
          }
      
      }
      
      
  27. JDK7的新特性–(IO字符流)

    • Demo27JDK7

      package cn.xiaoge.day18.demo09;
      
      /*
          JDK7的新特性
              在try的后边可以增加一个(), 在括号中可以定义流对象
              那么这个流对象的作用域就在try中有效
              try中的代码执行完毕, 会自动把流对象释放, 不用写finally
      
              格式:
                  try(定义流对象; 定义流对象;...){
                      可能会产生异常的代码
                  } catch(异常类变量 变量名) {
                      异常的处理逻辑
                  }
       */
      
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo27JDK7 {
      
          public static void main(String[] args) {
      
              try(
                  // 1. 创建一个字节输入流对象, 构造方法中绑定要读取的数据源
                  FileInputStream fis = new FileInputStream("./4.jpeg");
                  // 2. 创建一个字节输出流对象, 构造方法中绑定要写入的目的地
                  FileOutputStream fos = new FileOutputStream("./day18-code/4.jpeg");
              ){
                  // 3. 使用字节输入流对象中的方法read读取文件
                  // 一次读取一个字节写入一个字节的方式
                  int len = 0;
                  while((len = fis.read()) != -1){
                      // 4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
                      fos.write(len);
                  }
              } catch(IOException e) {
                  System.out.println(e);
              }
      
      
      
      
          }
      
      }
      
      
  28. JDK9新特性–(IO字符流)

    • Demo28JDK9

      package cn.xiaoge.day18.demo09;
      
      /*
          JDK9新特性
              try的前边可以定义流对象
              在try后边的()中可以直接引入流对象的名称(变量名)
              在try代码执行完毕之后, 流对象也可以释放掉, 不用写finally
              格式: 这里用a b 代替流对象
                  A a = new A();
                  B b = new B();
                  try(a; b){
                      可能会产生异常的代码
                  } catch(异常类变量 变量名) {
                      异常的处理逻辑
                  }
      */
      
      import java.io.FileInputStream;
      import java.io.FileNotFoundException;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      public class Demo28JDK9 {
      
          public static void main(String[] args) throws FileNotFoundException {
              // 1. 创建一个字节输入流对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("./4.jpeg");
              // 2. 创建一个字节输出流对象, 构造方法中绑定要写入的目的地
              FileOutputStream fos = new FileOutputStream("./day18-code/4.jpeg");
      
              try(fis; fos){
                  // 3. 使用字节输入流对象中的方法read读取文件
                  // 一次读取一个字节写入一个字节的方式
                  int len = 0;
                  while((len = fis.read()) != -1){
                      // 4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
                      fos.write(len);
                  }
              }catch(IOException e){
                  System.out.println(e);
              }
      
      
          }
      
      }
      
      
  29. Properties集合–存储数据–遍历数据–store方法–load方法–(Properties集合)

    • Demo29Properties

      package cn.xiaoge.day18.demo10;
      
      /*
          java.util.Properties集合 extends Hashtable<k, v> implements Map<k, v>
          Properties 类表示了一个持久的属性集. Properties 可保存在流中或从流中加载.
          Properties 集合是一个唯一和IO流相结合的集合.
              可以使用Properties集合中的方法store, 把集合中的临时数据, 持久化写入到硬盘中存储
              可以使用Properties集合中的方法load, 把硬盘中保存的文件(键值对), 读取到集合中使用
      
          属性列表中每个键及其对应值都是一个字符串.
              Properties集合是一个双列集合, key和value默认都是字符串, 所以Properties可以不用写泛型
       */
      
      import java.io.FileNotFoundException;
      import java.io.FileReader;
      import java.io.FileWriter;
      import java.io.IOException;
      import java.util.Properties;
      import java.util.Set;
      
      public class Demo29Properties {
      
          public static void main(String[] args) throws IOException {
              show01();
              System.out.println("============================");
              show02();
              System.out.println("============================");
              show03();
          }
      
          /*
              可以使用Properties集合中的方法load, 把硬盘中保存的文件(键值对), 读取到集合中使用
              void load(InputStream inStream)
              void load(Reader reader)
              参数:
                  InputStream inStream: 字节输入流, 不能读取含有中文的键值对
                  Reader read: 字符输入流, 能读取含有中文的键值对
              使用步骤:
                  1. 创建Properties集合对象
                  2. 使用Properties集合对象中的方法load读取保存键值对的文件
                  3. 遍历Properties集合
              注意:
                  1. 存储键值对的文件中, 键与值默认的连接符可以使用=, 空格(其他符号)
                  2. 存储键值对的文件中, 可以使用#进行注释, 被注释的键值对不会再被读取
                  3. 存储键值对的文件中, 键与值默认都是字符串, 不用再加引号
           */
          private static void show03() throws IOException {
              // 1. 创建Properties集合对象
              Properties prop = new Properties();
              // 2. 使用Properties集合对象中的方法load读取保存键值对的文件
              prop.load(new FileReader("./prop.txt"));
              // 3. 遍历Properties集合
              Set<String> set = prop.stringPropertyNames();
              for(String key: set){
                  System.out.println(key + " = " + prop.getProperty(key));
              }
          }
      
          /*
              可以使用Properties集合中的方法store, 把集合中的临时数据, 持久化写入到硬盘存储
              void store(OutputStream out, String comments)
              void store(Writer writer, String comments)
              参数:
                  OutputStream out: 字节输出流, 不能写入中文
                  Writer writer: 字符输出流, 可以写中文
                  String comments: 注释, 用来解释说明保存的文件是做什么的
                      不能使用中文, 会产生乱码, 默认是Unicode编码
                      一般使用"空字符串"
              使用步骤:
                  1. 创建Properties集合对象, 添加数据
                  2. 创建字节输出流/字符输出流对象, 构造方法中绑定要输出的目的地
                  3. 使用Properties集合中的方法store, 把集合中的临时数据, 持久化写入到硬盘中存储
                  4. 释放资源
           */
          private static void show02() throws IOException {
              // 1. 创建Properties集合对象, 添加数据
              Properties prop = new Properties();
      
              prop.setProperty("金泫雅", "170");
              prop.setProperty("金所炫", "165");
              prop.setProperty("李智恩", "170");
      
              // 2. 创建字节输出流/字符输出流对象, 构造方法中绑定要输出的目的地
              FileWriter fw = new FileWriter("./prop.txt");
              // 3. 使用Properties集合中的方法store, 把集合中的临时数据, 持久化写入到硬盘中存储
              prop.store(fw, "save data");
              // 4. 释放资源
              fw.close();
          }
      
          /*
              使用Properties集合存储数据, 遍历取出Properties集合的数据
              Properties集合是一个双列集合, key和value默认都是字符串
              Properties集合有一些操作字符串的特有方法
                  Object setProperties(String key, String value)调用 Hashtable 的方法 put.
                  String getProperties(String key) 通过key找到value值, 此方法相当于Map集合中的get(key)方法
                  Set<String> stringPropertyNames() 返回此属性列表中的键集, 其中该键及其对应值是字符串, 此方法相当于Map集合中的keySet方法
           */
          private static void show01() {
              // 创建Properties集合对象
              Properties prop = new Properties();
              // 使用setProperties往集合中添加数据
              prop.setProperty("赵丽颖", "168");
              prop.setProperty("迪丽热巴", "165");
              prop.setProperty("古力娜扎", "160");
              // prop.put(1, true)
      
              // 使用stringPropertyNames把Properties集合中的键取出, 存储到一个set集合集合中
              Set<String> set = prop.stringPropertyNames();
      
              // 遍历得到每一个键, 通过键获取值
              for (String key : set) {
                  System.out.println(key + "=" + prop.getProperty(key));
              }
          }
      
      }
      
      // 运行结果
      赵丽颖=168
      古力娜扎=160
      迪丽热巴=165
      ============================
      ============================
      李智恩 = 170
      金泫雅 = 170
      金所炫 = 165
      
  30. 字节缓冲输出流BufferedOutputStream共性方法–(缓冲流)

    • Demo30BufferedOutputStream

      package cn.xiaoge.day18.demo11;
      
      /*
          java.io.BufferedOutputStream extends OutputStream
          BufferedOutputStream: 字节缓冲输出流
      
          继承自父类的共性成员方法:
              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) :将指定的字节输出流。
          构造方法:
              BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流, 以将数据写入指定的底层输出流.
              BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流, 以将具有指定缓冲区大小的数据写入指定的底层输出
              参数:
                  OutputStream out:字节输出流
                      我们可以传递FileOutputStream, 缓冲流会给FileOutputStream增加一个缓冲区, 提高FileOutputStream的写入效率
                  int size: 指定缓冲流内部缓冲区的大小, 不指定默认
          使用步骤(重点)
              1. 创建FileOutputStream对象, 构造方法中绑定要输出的目的地
              2. 创建BufferedOutputStream对象, 构造方法中传递FileOutputStream对象, 提高FileOutputStream对象效率
              3. 使用BufferedOutputStream对象中的方法write, 把数据写入到内部缓冲区中
              4. 使用BufferedOutputStream对象中的方法flush, 把内部缓冲区中的数据, 刷新到文件中
              5. 释放资源(会先调用flush方法刷新数据, 第4部可以省略)
       */
      
      import java.io.BufferedOutputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.util.Arrays;
      
      public class Demo30BufferedOutputStream {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建FileOutputStream对象, 构造方法中绑定要输出的目的地
              FileOutputStream fos = new FileOutputStream("./buff.txt");
      
              // 2. 创建BufferedOutputStream对象, 构造方法中传递FileOutputStream对象, 提高FileOutputStream对象效率
              BufferedOutputStream bos = new BufferedOutputStream(fos);
      
              // 3. 使用BufferedOutputStream对象中的方法write, 把数据写入到内部缓冲区中
              // getBytes("utf-8")把字符串转换为字节数组
              bos.write("我把数据写入到内部缓冲区中".getBytes("utf-8"));
      
              // 4. 使用BufferedOutputStream对象中的方法flush, 把内部缓冲区中的数据, 刷新到文件中
              bos.flush();
      
              // 5. 释放资源(会先调用flush方法刷新数据, 第4部可以省略)
              bos.close();
          }
      
      }
      
      
  31. 字节缓冲输入流BufferedInputStream共性方法–(缓冲流)

    • Demo31BufferedInputStream

      package cn.xiaoge.day18.demo11;
      
      /*
          java.io.BufferedInputStream extends InputStream
          BufferedInputStream: 字节缓冲输入流
      
          继承自父类的成员方法:
              int read()从输入流中读取数据的下一个字节
              int read(byte[] b) 从输入流中读取一定数量的字节, 并将其存储在缓冲区数组 b 中
              void close() 关闭此输入流并释放与该流关联的所有系统资源
      
          构造方法:
              BufferedInputStream(InputStream in) 创建一个 BufferedInputStream 并保存其参数, 即输入流 in, 以便将来使用.
              BufferedInputStream(InputStream in, int size) 创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数, 即输入流 in, 以便将来使用.
              参数:
                  InputStream in: 字节输入流
                      我们可以传递FileInputStream, 缓冲流会给FileInputStream增加一个缓冲区, 提高FileInputStream的读取效率
                  int size: 指定缓冲流内部缓冲去的带下, 不指定默认
          使用步骤(重点):
             1. 创建FileInputStream对象, 构造方法中绑定要读取的数据源
             2. 创建BufferedInputStream对象, 构造方法中传递FileInputStream对象, 提高FileInputStream对象的读取效率
             3. 使用BufferedInputStream对象中的方法read, 读取文件
             4. 释放资源
       */
      
      import java.io.BufferedInputStream;
      import java.io.FileInputStream;
      import java.io.IOException;
      
      public class Demo31BufferedInputStream {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建FileInputStream对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("./buff.txt");
      
              // 2. 创建BufferedInputStream对象, 构造方法中传递FileInputStream对象, 提高FileInputStream对象的读取效率
              BufferedInputStream bis = new BufferedInputStream(fis);
      
              // 3. 使用BufferedInputStream对象中的方法read, 读取文件
              /*int len = 0;
              while ((len = bis.read()) != -1){
                  System.out.println((char) len);
              }*/
      
              byte[] bytes = new byte[1024]; // 存储每次读取的数据
              int len = 0; // 记录每次读取的有效字节个数
              while ((len = bis.read(bytes)) != -1){
                  System.out.println(new String(bytes, 0, len));
              }
      
              // 4. 释放资源
              bis.close();
          }
      
      }
      
      // 运行结果
      我把数据写入到内部缓冲区中
      
  32. 字节流复制文件的速度–(IO字节流)

    • Demo32CopyFile

      package cn.xiaoge.day18.demo12;
      
      import java.io.FileInputStream;
      import java.io.FileOutputStream;
      import java.io.IOException;
      
      /*
          文件复制练习: 一读一写
      
          明确:
              数据源: ./4.jpeg
              数据的目的地: ./day18-code/4.jpeg
      
          文件复制的步骤:
              1. 创建一个字节输入流对象, 构造方法中绑定要读取的数据源
              2. 创建一个字节输出流对象, 构造方法中绑定要写入的目的地
              3. 使用字节输入流对象中的方法read读取文件
              4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
              5. 释放资源
          文件大小: 38,862 字节
          一次读取一个字节: 136毫秒
          使用数组缓冲读取多个字节, 写入多个字节: 7毫秒
       */
      
      public class Demo32CopyFile {
      
          public static void main(String[] args) throws IOException {
              long start = System.currentTimeMillis();
              // 1. 创建一个字节输入流对象, 构造方法中绑定要读取的数据源
              FileInputStream fis = new FileInputStream("./4.jpeg");
              // 2. 创建一个字节输出流对象, 构造方法中绑定要写入的目的地
              FileOutputStream fos = new FileOutputStream("./day18-code/4.jpeg");
      
      
              // 3. 使用字节输入流对象中的方法read读取文件
              // 一次读取一个字节写入一个字节的方式
              /*int len = 0;
              while((len = fis.read()) != -1){
                  // 4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
                  fos.write(len);
              }*/
      
      
              // 3. 使用字节输入流对象中的方法read读取文件
              // 使用数组缓冲读取多个字节, 写入多个字节
              int len = 0; // 每次读取的有效字节个数
              byte[] bytes = new byte[1024];
              while((len = fis.read(bytes)) != -1) {
                  // 4. 使用字节输出流中的方法write, 把读取到的字节写入到目的地的文件中
                  fos.write(bytes, 0, len); // 写入从bytes数组 0位置开始写入的长度为len
              }
      
              // 5. 释放资源(先关写的, 后关闭读的; 如果写完了, 肯定读取完毕了)
              fos.close();
              fis.close();
              long end = System.currentTimeMillis();
              System.out.println("复制文件共耗时: " + (end - start) + "毫秒");
          }
      
      }
      
      // 运行结果
      复制文件共耗时: 5毫秒
      
  33. 字节缓冲流复制文件的速度–(缓冲流)

    • Demo33CopyFile

      package cn.xiaoge.day18.demo12;
      
      /*
          文件复制练习: 一读一写
      
          明确:
              数据源: ./4.jpeg
              数据的目的地: ./day18-code/4.jpeg
      
          文件复制的步骤:
              1. 创建字节缓冲输入流对象, 构造方法中传递字节输入流
              2. 创建字节缓冲输出流对象, 构造方法中传递字节输出流
              3. 使用字节缓冲输入流对象中的方法read, 读取文件
              4. 使用字节缓冲输出流中的方法write, 把读取的数据写入到内部缓冲区中
              5. 释放资源(会先把缓冲区中的数据, 刷新到文件中)
      
          文件大小: 38,862 字节
          一次读取一个字节: 10毫秒
          使用数组缓冲读取多个字节, 写入多个字节: 1毫秒
      */
      
      import java.io.*;
      
      public class Demo33CopyFile {
      
          public static void main(String[] args) throws IOException {
              long start = System.currentTimeMillis();
      
              // 1. 创建字节缓冲输入流对象, 构造方法中传递字节输入流
              BufferedInputStream bis = new BufferedInputStream(new FileInputStream("./4.jpeg"));
      
              // 2. 创建字节缓冲输出流对象, 构造方法中传递字节输出流
              BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("./day18-code/4.jpeg"));
      
              // 3. 使用字节缓冲输入流对象中的方法read, 读取文件
              /*int len = 0;
              while((len = bis.read()) != -1) {
                  // 4. 使用字节缓冲输出流中的方法write, 把读取的数据写入到内部缓冲区中
                  bos.write(len);
              }*/
              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 end = System.currentTimeMillis();
              System.out.println("复制文件共耗时: " + (end - start) + "毫秒");
          }
      
      }
      
      // 运行结果
      复制文件共耗时: 1毫秒
      
  34. 字符缓冲输出流BufferedWriter共性方法–(缓冲流)

    • Demo34BufferedWriter

      package cn.xiaoge.day18.demo13;
      
      /*
          java.io.BufferedWriter extends Writer
          BufferedWriter: 字符缓冲输出流
      
          继承自父类的共性成员方法:
              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() 关闭此流,但要先刷新它。
      
          构造方法:
              BufferedWriter(Writer out) 创建一个使用默认大小输出缓冲区的缓冲字符输出流
              BufferedWriter(Writer out, int sz) 创建一个使用给定大小输出缓冲区的新缓冲字符输出流
              参数:
                  Writer out: 字符输出流
                      我们可以传递FileWriter, 缓冲流会给FileWriter增加一个缓冲区, 提高FileWriter的写入效率
                  int sz: 指定缓冲区的大小, 不写默认的大小
      
          特有的成员方法:
              void newLine() 写入一个行分隔符. 会根据不同的操作系统, 获取不同的行分隔符
              换行: 换行符号
              windows: \r\n
              Linux: \n
              Mac: \r 或 \n
          使用步骤:
              1. 创建字符缓冲输出流对象, 构造方法中传递字符输出流
              2. 调用字符缓冲输出流中的方法write, 把数据写入到内存缓冲区中
              3. 调用字符缓冲输出流中的方法flush, 把内存缓冲区中的数据, 刷新到文件中
              4. 释放资源
       */
      
      import java.io.BufferedWriter;
      import java.io.FileWriter;
      import java.io.IOException;
      
      public class Demo34BufferedWriter {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建字符缓冲输出流对象, 构造方法中传递字符输出流
              BufferedWriter bw = new BufferedWriter(new FileWriter("./a.txt"));
      
              // 2. 调用字符缓冲输出流中的方法write, 把数据写入到内存缓冲区中
              for (int i = 0; i < 10; i++) {
                  // 写入潇哥最帅
                  bw.write("潇哥最帅");
                  // 写入换行
                  bw.newLine();
              }
      
              // 3. 调用字符缓冲输出流中的方法flush, 把内存缓冲区中的数据, 刷新到文件中
              bw.flush();
      
              // 4. 释放资源
              bw.close();
          }
      
      }
      
      
  35. 字符缓冲输入流BufferedReader共性方法–(缓冲流)

    • Demo35BufferedReader

      package cn.xiaoge.day18.demo13;
      
      /*
          java.io.BufferedReader extends Reader
      
          继承自父类的共性成员方法:
              int read() 读取单个字符并返回
              int read(char[] cbuf)一次读取多个字符, 将字符读入数组.
              void close() 关闭该流并释放与之关联的所有资源.
      
          构造方法:
              BufferedReader(Reader in) 创建一个使用默认大小输入缓冲区的缓冲字符输入流
              BufferedReader(Reader in, int sz) 创建一个使用指定大小输入缓冲区的缓冲字符输入流
              参数:
                  Reader in: 字符输入流
                      我们可以传递FileReader, 缓冲流会给FileReader增加一个缓冲区, 提高FileReader的读取效率
              特有的成员方法:
                  String readLine() 读取一个文本行. 读取一行数据
                      行的终止符号: 通过下列字符之一即可人文某行已终止: 换行('\n') 回车('\r')或回车后直接跟着换行(\r\n).
                  返回值:
                      包含该行内容的字符串, 不包含任何行终止符, 如果已到达流末尾, 则返回null
              使用步骤:
                  1. 创建字符缓冲输入流对象, 构造方法中传递字符输入流
                  2. 使用字符缓冲输入流对象中的方法read/readLine读取文本
                  3. 释放资源
       */
      
      import java.io.BufferedReader;
      import java.io.FileReader;
      import java.io.IOException;
      
      public class Demo35BufferedReader {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建字符缓冲输入流对象, 构造方法中传递字符输入流
              BufferedReader br = new BufferedReader(new FileReader("./a.txt"));
      
              // 2. 使用字符缓冲输入流对象中的方法read/readLine读取文本
              /*
                  不知道文件有多少行的数据, 所以使用while循环
                  while的结束条件, 读取到null结束
               */
              String fileContent = "";
              // readLine()  一次读取到一个换行符
              while ((fileContent = br.readLine()) != null) {
                  System.out.println(fileContent);
              }
      
              // 3. 释放资源
              br.close();
          }
      
      }
      
      // 运行结果
      潇哥最帅
      潇哥最帅
      潇哥最帅
      潇哥最帅
      潇哥最帅
      潇哥最帅
      潇哥最帅
      潇哥最帅
      潇哥最帅
      潇哥最帅
      
  36. 对文本的内容进行排序–(缓冲流)

    • 排序前

      3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉 以咨之,然后施行,必得裨补阙漏,有所广益。
      8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其 咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
      4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之, 必能使行阵和睦,优劣得所。
      2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不 宜偏私,使内外异法也。
      1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外 者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以 塞忠谏之路也。
      9.今当远离,临表涕零,不知所言。
      6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣 以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
      7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。 今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛 下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
      5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息 痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
      
    • Demo36Test

      package cn.xiaoge.day18.demo14;
      
      /*
          练习:
              对文本的内容进行排序
              按照(1, 2, 3....)顺序排序
          分析:
              1. 创建一个HashMap集合对象, 可以: 存储每行文本的序号(1, 2, 3....); value: 存储每行的文本
              2. 创建字符缓冲输入流对象, 构造方法中绑定字符输入流
              3. 创建字符缓冲输出流对象, 构造方法中绑定字符输出流
              4. 使用字符缓冲输入流中的方法readLine, 逐行读取文本
              5. 对读取到的文本进行切割, 获取行中的序号和文本的内容
              6. 把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的, 会自动排序1, 2, 3, 4...)
              7. 遍历HashMap集合, 获取每一个键值对
              8. 把每一个键值对, 拼接为一个文本行
              9. 把拼接好的文本, 使用字符缓冲输出流中的方法write, 写入到文件中
              10. 释放资源
       */
      
      import java.io.*;
      import java.util.Arrays;
      import java.util.HashMap;
      import java.util.Set;
      
      public class Demo36Test {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建一个HashMap集合对象, 可以: 存储每行文本的序号(1, 2, 3....); value: 存储每行的文本
              HashMap<String, String> map = new HashMap<>();
      
              // 2. 创建字符缓冲输入流对象, 构造方法中绑定字符输入流
              BufferedReader br = new BufferedReader(new FileReader("./a.txt"));
      
              // 3. 创建字符缓冲输出流对象, 构造方法中绑定字符输出流
              BufferedWriter bw = new BufferedWriter(new FileWriter("./b.txt"));
      
              // 4. 使用字符缓冲输入流中的方法readLine, 逐行读取文本
              String fileContent; // 每行读取的内容
              while((fileContent = br.readLine()) != null) {
                  // 5. 对读取到的文本进行切割, 获取行中的序号和文本的内容
                  String[] arr = fileContent.split("\\.");
                  // 6. 把切割好的序号和文本的内容存储到HashMap集合中(key序号是有序的, 会自动排序1, 2, 3, 4...)
                  map.put(arr[0], arr[1]);
              }
      
              // 7. 遍历HashMap集合, 获取每一个键值对
              Set<String> set = map.keySet();
              for(String key: set) {
                  String value = map.get(key);
                  // 8. 把每一个键值对, 拼接为一个文本行
                  fileContent = key + "." + value;
                  // 9. 把拼接好的文本, 使用字符缓冲输出流中的方法write, 写入到文件中
                  bw.write(fileContent);
                  bw.newLine();
              }
      
              // 10. 释放资源
              bw.close();
              br.close();
              
          }
      
      }
      
      
    • 排序后

      1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外 者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以 塞忠谏之路也。
      2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不 宜偏私,使内外异法也。
      3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉 以咨之,然后施行,必得裨补阙漏,有所广益。
      4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之, 必能使行阵和睦,优劣得所。
      5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息 痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
      6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣 以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
      7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。 今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛 下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
      8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其 咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
      9.今当远离,临表涕零,不知所言。
      
  37. 转换流OutputStreamWriter(是字符流通向字节流的桥梁)共性方法–(转换流)

    • Demo37OutputStreamWriter

      package cn.xiaoge.day18.demo15;
      
      /*
          java.io.OutputStreamWriter extends Writer
          OutputStreamWriter: 是字符流通向字节流的桥梁:可使用指定的 charset 将要写入流中的字符编码成字节。(编码: 把能看懂的变成看不懂)
      
          继承自父类的共性成员方法:
              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() 关闭此流,但要先刷新它。
          构造方法:
              OutputStreamWriter(OutputStream out): 创建使用默认字符编码的 OutputStreamWriter。
              OutputStreamWriter(OutputStream out, String charsetName): 创建使用指定字符集的 OutputStreamWriter。
              参数:
                  OutputStream out: 字节输出流, 可以用来写转换之后的字节到文件中
                  String charsetName: 指定的编码表名称, 不区分大小写, 可以是utf-8/UTF-8, gbk/GBK,....不指定默认使用utf-8
           使用步骤:
              1. 创建OutputStreamWriter对象, 构造方法中传递字节输出流和指定的编码表名称
              2. 使用OutputStreamWriter对象中的方法Writer, 把字符转换为字节存储缓冲区中(编码)
              3. 使用OutputStreamWriter对象中的方法flush, 把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
              4. 释放资源
       */
      
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.OutputStreamWriter;
      
      public class Demo37OutputStreamWriter {
      
          public static void main(String[] args) throws IOException {
              write_utf_8();
              write_gbk();
          }
      
          /*
      
           */
          private static void write_gbk() throws IOException {
              // 1. 创建OutputStreamWriter对象, 构造方法中传递字节输出流和指定的编码表名称
              OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("./gbk.txt"), "gbk");
      
              // 2. 使用OutputStreamWriter对象中的方法Writer, 把字符转换为字节存储缓冲区中(编码)
              osw.write("哈哈");
      
              // 3. 使用OutputStreamWriter对象中的方法flush, 把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
              osw.flush();
      
              // 4. 释放资源
              osw.close();
          }
      
          /*
              使用转换流OutputStreamWriter写utf-8格式的文件
           */
          private static void write_utf_8() throws IOException {
              // 1. 创建OutputStreamWriter对象, 构造方法中传递字节输出流和指定的编码表名称
              // OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("./utf_8.txt"), "utf-8");
              OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("./utf_8.txt")); // 不指定默认使用utf-8
      
              // 2. 使用OutputStreamWriter对象中的方法Writer, 把字符转换为字节存储缓冲区中(编码)
              osw.write("哈哈");
      
              // 3. 使用OutputStreamWriter对象中的方法flush, 把内存缓冲区中的字节刷新到文件中(使用字节流写字节的过程)
              osw.flush();
      
              // 4. 释放资源
              osw.close();
          }
      
      }
      
      
  38. 转换流InputStreamReader(是字节流通向字符流的桥梁)共性方法–(转换流)

    • Demo38InputStreamReader

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

    • Demo39Test

      package cn.xiaoge.day18.demo15;
      
      /*
          练习: 转换文件编码
              将GBK编码的文件, 转化为UTF-8编码的文本文件.
      
          分析:
              1. 创建InputStreamReader对象, 构造方法中传递字节输入流和指定的gbk编码表名称
              2. 创建OutputStreamWriter对象, 构造方法中传递字节输出流和指定的utf_8编码表名称
              3. 使用InputStreamReader中的read方法读取文件
              4. 使用OutputStreamWriter中的write方法写入文件
              5. 释放资源
       */
      
      import java.io.*;
      
      public class Demo39Test {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建InputStreamReader对象, 构造方法中传递字节输入流和指定的gbk编码表名称
              InputStreamReader isr = new InputStreamReader(new FileInputStream("./gbk.txt"), "gbk");
      
              // 2. 创建OutputStreamWriter对象, 构造方法中传递字节输出流和指定的utf_8编码表名称
      
              OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("./newUtf_8.txt")); // 默认是utf_8
      
              // 3. 使用InputStreamReader中的read方法读取文件
              int len = 0;
              while((len = isr.read()) != -1) {
                  // 4. 使用OutputStreamWriter中的write方法写入文件
                  osw.write(len);
              }
              // 5. 释放资源
              osw.close();
              isr.close();
          }
      
      }
      
  40. ObjectOutputStream: 对象的序列化流: 把对象以流的方式写入到文件中保存–(序列化流)

    • Person

      package cn.xiaoge.day18.demo16;
      
      import java.io.Serializable;
      
      /*
          序列化和反序列化的时候, 会抛出NotSerializableException没有序列化异常
          类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
          Serializable接口也叫标记型接口
              要进行序列化和反序列化的类必须实现Serializable接口, 就会给类添加一个标记
              当我们进行序列化和反序列化的时候, 就会检测类上是否有这个标记
                  有: 就可以序列化和反序列化
                  没有: 就会抛出NotSerializableException异常
          去市场买肉-->肉上有一个蓝色章(检测合格)-->放心购买-->买回来怎么吃随意
      
          static关键字: 静态关键字
              静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
              被static修饰的成员变量不能被序列化的, 序列化的都是对象
              private static int age;
              oos.writeObject(new Person("金泫雅", 18));
              Object obj = ois.readObject();
              Person{name='金泫雅', age=0}
          transient关键字: 瞬态关键字
              被transient修饰成员变量, 不能被序列化
              private static int age;
              oos.writeObject(new Person("金泫雅", 18));
              Object obj = ois.readObject();
              Person{name='金泫雅', age=0}
       */
      public class Person implements Serializable {
      
          /*
              我们定义了这个类后就不能更改, 更改后就会重新生成序列化id,
              更改后在不执行序列化的情况下, 直接执行反序列化就会发生序列化id冲突异常
              直接抛出InvalidClassException
      
              想更改类里面的成员变量 或 成员方法后直接序列化想让他不报错
              解决方案就是把序列化id写死, 让它修改成员变量 或 成员方法 或类的结构后,
              不在重新生成序列化id
           */
          private static final long serialVersionUID = 1L; // 这个值可以随便写 但是这个成员变量是固定的名称
          private String name;
          // private static int age;
          private int age;
      
          public Person() {
          }
      
          public Person(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 "Person{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
      
    • Demo40ObjectOutputStream

      package cn.xiaoge.day18.demo16;
      
      /*
          java.io.ObjectOutputStream extends OutputStream
          ObjectOutputStream: 对象的序列化流
          作用: 把对象以流的方式写入到文件中保存
      
          构造方法:
              ObjectOutputStream(OutputStream out): 创建写入指定 OutputStream 的 ObjectOutputStream。
              参数:
                  OutputStream out: 字节输出流
          特有的成员方法:
              void writeObject(Object obj) 将指定的对象写入 ObjectOutputStream
      
          使用步骤:
              1. 创建ObjectOutputStream对象, 构造方法中传递字节输出流
              2. 使用ObjectOutputStream对象中的方法writeObject, 把对象写入到文件中
              3. 释放资源
       */
      
      import java.io.FileOutputStream;
      import java.io.IOException;
      import java.io.ObjectOutputStream;
      
      public class Demo40ObjectOutputStream {
      
          public static void main(String[] args) throws IOException {
              // 1. 创建ObjectOutputStream对象, 构造方法中传递字节输出流
              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./object.txt"));
      
              // 2. 使用ObjectOutputStream对象中的方法writeObject, 把对象写入到文件中
              oos.writeObject(new Person("金泫雅", 18));
      
              // 3. 释放资源
              oos.close();
          }
      
      }
      
      
  41. ObjectInputStream: 对象的反序列化流: 把文件中保存的对象, 以流的方式读取出来使用–(序列化流)

    • Person

      package cn.xiaoge.day18.demo16;
      
      import java.io.Serializable;
      
      /*
          序列化和反序列化的时候, 会抛出NotSerializableException没有序列化异常
          类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
          Serializable接口也叫标记型接口
              要进行序列化和反序列化的类必须实现Serializable接口, 就会给类添加一个标记
              当我们进行序列化和反序列化的时候, 就会检测类上是否有这个标记
                  有: 就可以序列化和反序列化
                  没有: 就会抛出NotSerializableException异常
          去市场买肉-->肉上有一个蓝色章(检测合格)-->放心购买-->买回来怎么吃随意
      
          static关键字: 静态关键字
              静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
              被static修饰的成员变量不能被序列化的, 序列化的都是对象
              private static int age;
              oos.writeObject(new Person("金泫雅", 18));
              Object obj = ois.readObject();
              Person{name='金泫雅', age=0}
          transient关键字: 瞬态关键字
              被transient修饰成员变量, 不能被序列化
              private static int age;
              oos.writeObject(new Person("金泫雅", 18));
              Object obj = ois.readObject();
              Person{name='金泫雅', age=0}
       */
      public class Person implements Serializable {
      
          /*
              我们定义了这个类后就不能更改, 更改后就会重新生成序列化id,
              更改后在不执行序列化的情况下, 直接执行反序列化就会发生序列化id冲突异常
              直接抛出InvalidClassException
      
              想更改类里面的成员变量 或 成员方法后直接序列化想让他不报错
              解决方案就是把序列化id写死, 让它修改成员变量 或 成员方法 或类的结构后,
              不在重新生成序列化id
           */
          private static final long serialVersionUID = 1L; // 这个值可以随便写 但是这个成员变量是固定的名称
          private String name;
          // private static int age;
          private int age;
      
          public Person() {
          }
      
          public Person(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 "Person{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
      
      
    • Demo41ObjectInputStream

      package cn.xiaoge.day18.demo16;
      
      /*
          java.io.ObjectInputStream extends InputStream
          ObjectInputStream: 对象的反序列化流
          作用: 把文件中保存的对象, 以流的方式读取出来使用
      
          构造方法:
              ObjectInputStream(InputStream in) 创建从指定 InputStream 读取的 ObjectInputStream
              参数:
                  InputStream in 字节输入流
          特有的成员方法:
              Object readObject() 从ObjectInputStream 读取对象
      
          使用步骤:
              1. 创建ObjectInputStream对象, 构造方法中传递字节输入流
              2. 使用ObjectInputStream中的方法readObject读取保存对象的文件
              3. 释放资源
              4. 使用读取出来的对象(打印)
      
          readObject方法声明抛出了ClassNotFoundException(class文件找不到异常)
          当不存在对象的class文件时抛出此异常
          反序列化的前提:
              1. 类必须实现Serializable
              2. 必须存在类对应的class文件
       */
      
      import java.io.FileInputStream;
      import java.io.IOException;
      import java.io.ObjectInputStream;
      
      public class Demo41ObjectInputStream {
      
          public static void main(String[] args) throws IOException, ClassNotFoundException {
              // 1. 创建ObjectInputStream对象, 构造方法中传递字节输入流
              ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./object.txt"));
      
              // 2. 使用ObjectInputStream中的方法readObject读取保存对象的文件
              Object obj = ois.readObject();
      
              // 3. 释放资源
              ois.close();
      
              // 4. 使用读取出来的对象(打印)
              System.out.println(obj);
      
              Person person = (Person)obj;
              System.out.println(person.getName() + person.getAge());
          }
      
      }
      
      
      // 运行结果
      Person{name='金泫雅', age=18}
      金泫雅18
      
  42. 序列化集合–(序列化流)

    • Person

      package cn.xiaoge.day18.demo16;
      
      import java.io.Serializable;
      
      /*
          序列化和反序列化的时候, 会抛出NotSerializableException没有序列化异常
          类通过实现 java.io.Serializable 接口以启用其序列化功能。未实现此接口的类将无法使其任何状态序列化或反序列化。
          Serializable接口也叫标记型接口
              要进行序列化和反序列化的类必须实现Serializable接口, 就会给类添加一个标记
              当我们进行序列化和反序列化的时候, 就会检测类上是否有这个标记
                  有: 就可以序列化和反序列化
                  没有: 就会抛出NotSerializableException异常
          去市场买肉-->肉上有一个蓝色章(检测合格)-->放心购买-->买回来怎么吃随意
      
          static关键字: 静态关键字
              静态优先于非静态加载到内存中(静态优先于对象进入到内存中)
              被static修饰的成员变量不能被序列化的, 序列化的都是对象
              private static int age;
              oos.writeObject(new Person("金泫雅", 18));
              Object obj = ois.readObject();
              Person{name='金泫雅', age=0}
          transient关键字: 瞬态关键字
              被transient修饰成员变量, 不能被序列化
              private static int age;
              oos.writeObject(new Person("金泫雅", 18));
              Object obj = ois.readObject();
              Person{name='金泫雅', age=0}
       */
      public class Person implements Serializable {
      
          /*
              我们定义了这个类后就不能更改, 更改后就会重新生成序列化id,
              更改后在不执行序列化的情况下, 直接执行反序列化就会发生序列化id冲突异常
              直接抛出InvalidClassException
      
              想更改类里面的成员变量 或 成员方法后直接序列化想让他不报错
              解决方案就是把序列化id写死, 让它修改成员变量 或 成员方法 或类的结构后,
              不在重新生成序列化id
           */
          private static final long serialVersionUID = 1L; // 这个值可以随便写 但是这个成员变量是固定的名称
          private String name;
          // private static int age;
          private int age;
      
          public Person() {
          }
      
          public Person(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 "Person{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      '}';
          }
      }
      
      
    • Demo42Test

      package cn.xiaoge.day18.demo16;
      
      /*
          练习: 序列化集合
              当我们想在文件中保存多个对象的时候
              可以把多个对象存储到一个集合中
              对集合进行序列化和反序列化
          分析:
              1. 定义一个存储Person对象的ArrayList集合
              2. 往ArrayList集合中存储Person对象
              3. 创建一个序列化流ObjectOutputStream对象
              4. 使用ObjectOutputStream中的方法writeObject方法, 序列化ArrayList集合
              5. 创建一个反序列化ObjectInputStream对象
              6. 使用ObjectInputStream中的方法readObject方法, 反序列化ArrayList集合
              7. 把Object类型的集合转换为ArrayList类型的集合
              8. 遍历ArrayList集合
              9. 释放资源
      
       */
      
      import java.io.*;
      import java.util.ArrayList;
      
      public class Demo42Test {
      
          public static void main(String[] args) throws IOException, ClassNotFoundException {
              // 1. 定义一个存储Person对象的ArrayList集合
              ArrayList<Person> list = new ArrayList<>();
              // 2. 往ArrayList集合中存储Person对象
              list.add(new Person("金泫雅", 18));
              list.add(new Person("金所炫", 16));
              list.add(new Person("李智恩", 17));
      
              // 3. 创建一个序列化流ObjectOutputStream对象
              ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("./person.txt"));
      
              // 4. 使用ObjectOutputStream中的方法writeObject方法, 序列化ArrayList集合
              oos.writeObject(list);
      
              // 5. 创建一个反序列化ObjectInputStream对象
              ObjectInputStream ois = new ObjectInputStream(new FileInputStream("./person.txt"));
      
              // 6. 使用ObjectInputStream中的方法readObject方法, 反序列化ArrayList集合
              Object obj = ois.readObject();
      
              // 7. 把Object类型的集合转换为ArrayList类型的集合
              ArrayList<Person> personList= (ArrayList<Person>)obj;
      
              // 8. 遍历ArrayList集合
              for(Person p: personList) {
                  System.out.println(p);
              }
              // 9. 释放资源
              oos.close();
              ois.close();
          }
      
      }
      
      
      // 运行结果
      Person{name='金泫雅', age=18}
      Person{name='金所炫', age=16}
      Person{name='李智恩', age=17}
      
  43. PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式 --(打印流)

    • Demo43PrintStream

      package cn.xiaoge.day18.demo17;
      
      /*
          java.io.PrintStream: 打印流
              PrintStream 为其他输出流添加了功能,使它们能够方便地打印各种数据值表示形式。
          PrintStream特点:
              1. 只负责数据的输出, 不负责数据读取
              2. 与其他输出流不同,PrintStream 永远不会抛出 IOException
              3. 有特有的方法, print, println
                  void print(任意类型的值)
                  void println(任意类型的值并换行)
          构造方法:
              PrintStream(File file): 输出的目的地是一个文件
              PrintStream(OutputStream out): 输出的目的地是一个字节输出流
              PrintStream(String fileName): 输出的目的地是一个文件路径
          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) :将指定的字节输出流。
          注意:
              如果使用继承自父类的write方法写数据, 那么查看数据的时候回查询编码表 97 -> a
              如果要使用字节特有的方法print/println方法写数据, 写的数据原样输出 97 -> 97
       */
      
      import java.io.FileNotFoundException;
      import java.io.PrintStream;
      
      public class Demo43PrintStream {
      
          public static void main(String[] args) throws FileNotFoundException {
              // 创建打印流PrintStream对象, 构造方法中绑定要输出的目的地
              PrintStream ps = new PrintStream("./print.txt");
      
              // 如果使用继承自父类的write方法写数据, 那么查看数据的时候回查询编码表 97 -> a
              ps.write(97);
      
              // 如果要使用字节特有的方法print/println方法写数据, 写的数据原样输出 97 -> 97
              ps.println(97);
              ps.println(8.8);
              ps.println('a');
              ps.println("Hello World");
              ps.println(true);
              ps.println(new int[5]);
      
              // 释放资源
              ps.close();
          }
      
      }
      
      
      // 运行结果
      打印在print.txt这个文件里了
      
  44. 改变输出语句的目的地(打印流的流向)

    • Demo44PrintStream

      package cn.xiaoge.day18.demo17;
      
      /*
          可以改变输出语句的目的地(打印流的流向)
          输出语句, 默认在控制台输出
          使用: System.setOut方法改变输出语句的目的地为参数中传递的打印流的目的地
              static void setOut(PrintStream out) 重新分配"标准"输出流
       */
      
      import java.io.FileNotFoundException;
      import java.io.PrintStream;
      
      public class Demo44PrintStream {
      
          public static void main(String[] args) throws FileNotFoundException {
              System.out.println("我是在控制台输出");
      
              PrintStream ps = new PrintStream("destination.txt");
              System.setOut(ps); // 把输出语句的目的地改变为打印流的目的地
      
              System.out.println("我在打印流的目的地中输出"); // 打印到了destination.txt文件里
      
              ps.close();
          }
      
      }
      
      // 运行结果
      我是在控制台输出
      我在打印流的目的地中输出---打印在destination.txt文件里了
      
      
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

只因为你温柔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值