JavaSE🔟IO 流

发布时间 2023-03-25 16:45:33作者: Jaywee

前言

  1. 字节流和字符流体系相同,仅操作的数据类型不同。
  2. 节点流和过滤流的关系,是装饰者模式的体现。
  3. 转换流,是适配器模式的体现。
  4. I/O 对磁盘文件的读写,针对的是 File 类。

1、流

流(Stream)内存与存储设备之间传输数据的通道

分类

  1. 按方向:以内存为基准。
    • 输入流:读操作,存储设备 → 内存。
    • 输出流:写操作,内存 → 存储设备。
  2. 按处理单位
    • 字节流:字节,可以读写任意类型数据。
    • 字符流:字符,只能读写文本类型数据。
  3. 按功能
    • 节点流实际具有传输数据的功能(具体构件)。
    • 过滤流:在节点流的基础之上增强功能(装饰者)

2、字节流

2.1、抽象类

InputStream

字节输入流

image-20220317235716205
  1. read():从输入流中读取数据的下一个字节并返回。
  2. read(byte[]):从输入流中读取数据的一部分字节,存储到缓冲区并返回读取字节数量。
  3. read(byte[], off, len):从输入流中的偏移量 off 开始读取 len 个字节,存储到缓冲区并返回读取字节数量。
  4. close():关闭输入流,释放资源。

OutputStream

字节输出流

image-20220317235732262
  1. write(int):将指定字节(ASCII 码)写入输出流。
  2. write(byte[]):将指定字节数组写入输出流。
  3. write(byte[], off, len):将指定字节数组中偏移量 off 开始的 len 个字节,写入到输出流。
  4. flush():刷新输出流,并将任何缓冲的字节写出。
  5. close():先调用 flush(),再关闭输出流,释放资源。

Hint

  • write() 仅将数据写入流中,此时位于内存,还未写到磁盘文件。
  • flush() 才将数据写到磁盘文件。

2.2、节点流

FileInputStream

使用步骤

  1. 创建流:参数为文件名

  2. 创建缓冲区:减少磁盘 I/O,提高效率

  3. 读取数据:直接读取磁盘文件

  4. 关闭流

    public void readFile(String fileName) throws IOException {
        // 1、创建流
        FileInputStream fis = new FileInputStream(fileName);
        // 2、缓冲区
        byte[] buf = new byte[1024];
        int len = 0;
        // 3、读取
        while ((len = fis.read(buf)) != -1) {
            System.out.println(new String(buf, 0, len));
        }
        // 4、关闭流
        fis.close();
    }
    

FileOutputStream

使用步骤

  1. 创建流:参数 1 为文件名,参数 2 表示是否写入方式。

    1. true:追加内容到文件中。
    2. false:覆盖文件。
  2. 写数据

  3. 刷新到磁盘

  4. 关闭流

    public void writeFile(String location) throws IOException {
        // 1、创建流
        FileOutputStream fos = new FileOutputStream(location, true);
        // 2、写
        String str = "Hello, I/O world!";
        fos.write(str.getBytes());
        // 3、刷新
        fos.flush();
        // 4、关闭流
        fos.close();
    }
    

case:文件复制

思路:输入流读取文件数据,输出流写数据。

  1. 创建流

  2. 创建缓冲区:减少磁盘 I/O,提高效率

  3. 读写数据

  4. 关闭流先开后关

    public void copyFile(String source, String dest) throws IOException {
        // 1、创建流
        FileInputStream fis = new FileInputStream(source);
        FileOutputStream fos = new FileOutputStream(dest);
        // 2、缓冲区
        byte[] buf = new byte[1024];
        int len = 0;
        // 3、读写
        while ((len = fis.read(buf)) != -1) {
            fos.write(buf, 0, len);
        }
        // 4、关闭流:先开后关
        fos.close();
        fis.close();
    }
    

2.3、缓冲流

Buffered Stream

属于过滤流(装饰者)

  1. 优点提高 I/O 效率,减少磁盘访问次数
  2. 内置缓冲区:在节点流的基础上增强的功能。
    1. Buffered Stream内置了一个缓冲区(默认大小 8K)。
    2. 调用者对 Buffered Stream 的操作,都是在内置缓冲区的操作。
  3. 使用
    1. 创建 Buffered Stream 时,需要传入节点流。
    2. 关闭 Buffered Stream 时,自动关闭节点流。

读写流程

  1. :缓冲输入流
    1. Buffered Stream 先使用节点流,读取一部分字节数到内置缓冲区。
    2. 在内置缓冲区的数据读完之前,都是从内置缓冲区中读取。
  2. :缓冲输出流
    1. Buffered Stream 先使用节点流,写入一部分字节数到内置缓冲区。
    2. 在内置缓冲区的数据写完之前,都是从内置缓冲区中写出。

BufferedInputStream

使用步骤

Hint:此处缓冲区是节点流用于批量读取字节的缓冲区,不是缓冲流的内置缓冲区。

public void readFile(String filename) throws IOException {
    // 1、创建流
    FileInputStream fis = new FileInputStream(filename);
    BufferedInputStream bis = new BufferedInputStream(fis);
    // 2、缓冲区(Hint)
    byte[] buf = new byte[1024];
    int len = 0;
    // 3、读取
    while ((len = bis.read(buf)) != -1) {
        System.out.print(new String(buf, 0, len));
    }
    // 3、关闭流
    bis.close();
}

BufferedOutputStream

使用步骤

public void writeFile(String location) throws IOException {
    // 1、创建流
    FileOutputStream fos = new FileOutputStream(location);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    // 2、写
    String str = "Hello, buffer I/O";
    for (int i = 0; i < 10; i++) {
        bos.write(str.getBytes());
        // 3、刷新
        bos.flush();
    }
    // 4、关闭流
    bos.close();
}

case:文件复制

思路:输入流读取文件数据,输出流写数据。

  1. 创建流:节点流、过滤流

  2. 创建缓冲区

  3. 读写数据

  4. 关闭流:只需关闭过滤流,内部自动关闭节点流。

    public void copyFile(String source, String dest) throws IOException {
        // 1、 创建流
        FileInputStream fis = new FileInputStream(source);
        FileOutputStream fos = new FileOutputStream(dest);
        BufferedInputStream bis = new BufferedInputStream(fis);
        BufferedOutputStream bos = new BufferedOutputStream(fos);
        // 2、缓冲区
        byte[] buf = new byte[1024];
        int len = 0;
        // 3、读写
        while ((len = bis.read(buf)) != -1) {
            bos.write(buf, 0, len);
            bos.flush();
        }
        // 4、关闭连接
        bos.close();
        bis.close();
    }
    

2.4、对象流

ObjectStream

字节流的子类,属于过滤流

增强功能

  1. 内置缓冲区
  2. 读写对象
    1. 序列化:向流中写入一个对象
    2. 反序列化:从流中读取一个对象

? 对象序列化

序列化 - ObjectOutputStream

使用步骤

  1. 创建流

  2. 写数据:即序列化对象

  3. 关闭流

    public void write(String location) throws IOException {
        // 1、创建流
        FileOutputStream fos = new FileOutputStream(location);
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        // 2、序列化对象
        Person p1 = new Person("Jaywee", 17);
        oos.writeObject(p1);
        oos.flush();
        // 3、关闭流
        oos.close();
    }
    

反序列化 - ObjectInputStream

使用步骤

  1. 创建流

  2. 读数据:即反序列化对象

  3. 关闭流

    public Person read(String name) throws IOException, ClassNotFoundException {
        // 1、创建流
        FileInputStream fis = new FileInputStream(name);
        ObjectInputStream ois = new ObjectInputStream(fis);
        // 2、反序列化对象
        Person p = (Person) ois.readObject();
        // 3、关闭
        ois.close();
    
        return p;
    }
    

3、字符流

? 计算机基础:编码字符集

当字符的编码格式和解码格式不一致时,会出现乱码。

  1. 计算机中有多种编码字符集,使用的字节数也不同。
  2. 若使用字节流,将字符拆分为多个字节进行读写,可能导致乱码。

使用字符流,将字符作为整体进行读写。

(操作与字节流相同,区别是数据单位)

3.1、抽象类

Reader

字符输入流

image-20220318163024746

Writer

字符输出流

image-20230325155222758

3.2、节点流

FileReader

public void readFile(String fileName) throws IOException {
    // 1、创建流
    FileReader fr = new FileReader(fileName);
    // 2、逐个字符读取
    int data = 0;
    // fr.read()返回数据的下一个字符
    while ((data = fr.read()) != -1) {
        System.out.print((char) data);
    }
    // 3、关闭
    fr.close();
}

FileWriter

public void writeFile(String location) throws IOException {
    // 1、创建流
    FileWriter fw = new FileWriter(location);
    // 2、写
    String str = "Hello, I/O world!";
    fw.write(str);
    // 3、刷新
    fw.flush();
    // 4、关闭流
    fw.close();
}

case:文件复制

对比字节流

  1. 缓冲区类型

    1. 字节流缓冲区:byte
    2. 字符流缓冲区:char
  2. 文件类型

    • 字节流:任意类型文件。

    • 字符流:文本类型文件。

      public void copyFile(String source, String dest) throws IOException {
          // 1、创建流
          FileReader fr = new FileReader(source);
          FileWriter fw = new FileWriter(dest);
          // 2、缓冲区
          char[] buf = new char[1024];
          int len = 0;
          // 3、读取
          while ((len = fr.read(buf)) != -1) {
              fw.write(buf, 0, len);
          }
          // 4、关闭流:先开后关
          fw.close();
          fr.close();
      }
      

3.3、缓冲流

Buffered Stream

(属于过滤流,装饰者)

对比字节缓冲流

  1. 支持输入换行符
    • 字节流:需手动输入换行符(Linux \n,Windows \r\n
    • 字符缓冲输出流:提供 newLine(),返回操作系统对应的换行符。
  2. 支持读一行readLine()

BufferedReader

示例:逐行读取

public void read(String name) throws IOException {
    // 1、创建流
    FileReader fr = new FileReader(name);
    BufferedReader br = new BufferedReader(fr);
    // 2、逐行读取
    String line = null;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
    // 3、关闭
    br.close();
}

BufferedWriter

public void write(String location) throws IOException {
    // 1、创建流
    FileWriter fw = new FileWriter(location);
    BufferedWriter bw = new BufferedWriter(fw);
    // 2、写
    String str = "Hello, buffer I/O";
    for (int i = 0; i < 10; i++) {
        bw.write(str);
        // 换行符
        bw.newLine();
        bw.flush();
    }
    // 3、关闭
    bw.close();
}

3.4、打印流

3.4.1、说明

PrintWriter

Writer 子类,属于过滤流

增强功能

  1. 封装打印方法print()println()

  2. 数据原样打印:如不会将数字转换为字符。

    // print()部分重载方法
    public void print(boolean b) { write(b ? "true" : "false"); }
    public void print(char c) { write(c); }
    public void print(int i) { write(String.valueOf(i)); }
    
    // println(String)
    public void println(String x) {
        synchronized (lock) {
            print(x);
            println();
        }
    }
    

3.4.2、使用

public void write(String location) throws IOException {
    // 1、创建流
    FileWriter fw = new FileWriter(location);
    PrintWriter pw = new PrintWriter(fw);
    // 2、打印
    pw.print(97);
    pw.print(7.2);
    pw.print('c');
    pw.println("Jaywee");
    pw.print(true);
    // 3、刷新
    pw.flush();
    // 4、关闭
    pw.close();
}

4、转换流

适配器模式:字节流 → 字符流,

可显式设置默认字符编码(解码)方式。

  1. 被适配者:字节流(InputStream/InputStream)
  2. 适配者:字符流(Reader/Writer)
  3. 适配器类:转换流(InputStreamReader/InputStreamWriter)

InputStreamReader

  1. 将字节输入流转换为字符输入流

  2. 可显式设置解码方式(Decoder)

    public void read(String name) throws IOException {
        // 1、创建流
        FileInputStream fis = new FileInputStream(name);
        // 可指定解码方式:CharsetDecoder
        InputStreamReader reader = new InputStreamReader(fis);
        // 2、读
        int data = 0;
        while ((data = reader.read()) != -1) {
            System.out.print((char) data);
        }
        // 3、关闭
        reader.close();
    }
    

OutputStreamWriter

  1. 将字节输出流转换为字符输出流

  2. 可显式设置解码方式(Encoder)

    public void write(String location) throws IOException {
        // 1、创建流
        FileOutputStream fos = new FileOutputStream(location);
        // 可指定解码方式:CharsetEncoder
        OutputStreamWriter writer = new OutputStreamWriter(fos);
        // 2、写
        String str = "Hello, the bridge from byte streams to character stream";
        writer.write(str);
        writer.flush();
        // 3、关闭
        writer.close();
    }