Java IO 流-详解

发布于:2025-08-31 ⋅ 阅读:(23) ⋅ 点赞:(0)

介 绍

Java 的 IO(输入 / 输出)流是处理设备间数据传输的核心机制,主要用于读取和写入数据。

IO 流根据不同的分类方式可以分为多种类型:

  1. 按流向划分输入流(InputStream/Reader)和输出流(OutputStream/Writer)
  2. 按数据类型划分字节流(InputStream/OutputStream)和字符流(Reader/Writer)
  3. 按功能划分节点流(直接操作数据源)和处理流(对节点流进行包装)

核心用法、区别及适用场景

一、字节流(InputStream/OutputStream)

特点:以字节为单位处理数据,可操作所有类型文件(文本、图片、音频等)。

1. 节点字节流
  • FileInputStream/FileOutputStream(文件字节流)
    // 读取文件
    try (FileInputStream fis = new FileInputStream("input.txt")) {
        byte[] buffer = new byte[1024];
        int len;
        while ((len = fis.read(buffer)) != -1) {
            // 处理读取的字节数据
        }
    }
    
    // 写入文件
    try (FileOutputStream fos = new FileOutputStream("output.txt")) {
        byte[] data = "Hello".getBytes();
        fos.write(data); // 写入字节数组
    }
    
    适用场景:读写二进制文件(图片、视频)或简单文本文件。
2. 处理字节流
  • BufferedInputStream/BufferedOutputStream(缓冲字节流)

    // 带缓冲区的文件读写(性能更优)
    try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.jpg"));
         BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("b.jpg"))) {
        
        byte[] buffer = new byte[8192];
        int len;
        while ((len = bis.read(buffer)) != -1) {
            bos.write(buffer, 0, len); // 批量写入,减少IO次数
        }
    }
    
    核心优势:内部维护缓冲区,减少磁盘 IO 次数,大幅提升大文件处理效率。
  • DataInputStream/DataOutputStream(数据字节流)

    // 读写基本数据类型(保留数据类型信息)
    try (DataOutputStream dos = new DataOutputStream(new FileOutputStream("data.dat"))) {
        dos.writeInt(100);    // 写入int
        dos.writeDouble(3.14); // 写入double
        dos.writeUTF("字符串"); // 写入UTF字符串
    }
    
    try (DataInputStream dis = new DataInputStream(new FileInputStream("data.dat"))) {
        int num = dis.readInt();
        double d = dis.readDouble();
    }
    
    适用场景:需要按数据类型读写时(如保存配置文件、网络传输基本类型)。
  • ObjectInputStream/ObjectOutputStream(对象流)

    // 序列化对象(需实现Serializable接口)
    class User implements Serializable {
        private static final long serialVersionUID = 1L; // 版本控制
        private String name;
        // get/set...
    }
    
    // 序列化
    try (ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("user.obj"))) {
        oos.writeObject(new User("张三"));
    }
    
    // 反序列化
    try (ObjectInputStream ois = new ObjectInputStream(new FileInputStream("user.obj"))) {
        User user = (User) ois.readObject();
    }
    

    核心特点:实现对象的持久化存储或网络传输,transient修饰的字段不参与序列化。

二、字符流(Reader/Writer)

特点:以字符为单位处理数据,自动处理编码转换,仅适用于文本文件。

1. 节点字符流
  • FileReader/FileWriter(文件字符流)
    // 读取文本(默认编码)
    try (FileReader fr = new FileReader("text.txt")) {
        char[] buffer = new char[1024];
        int len;
        while ((len = fr.read(buffer)) != -1) {
            System.out.print(new String(buffer, 0, len));
        }
    }
    
    // 写入文本
    try (FileWriter fw = new FileWriter("output.txt")) {
        fw.write("中文文本"); // 直接写入字符
    }
    
    局限性:依赖系统默认编码,可能导致跨平台乱码。
2. 处理字符流
  • InputStreamReader/OutputStreamWriter(转换流)

    // 指定编码读写(解决乱码问题)
    try (InputStreamReader isr = new InputStreamReader(
             new FileInputStream("text.txt"), StandardCharsets.UTF_8);
         OutputStreamWriter osw = new OutputStreamWriter(
             new FileOutputStream("output.txt"), StandardCharsets.UTF_8)) {
        
        char[] buffer = new char[1024];
        int len;
        while ((len = isr.read(buffer)) != -1) {
            osw.write(buffer, 0, len);
        }
    }
    
    核心作用:字节流与字符流的桥梁,可指定编码(如 UTF-8),彻底解决乱码问题。
  • BufferedReader/BufferedWriter(缓冲字符流)

    // 高效读写文本,支持按行操作
    try (BufferedReader br = new BufferedReader(
             new InputStreamReader(new FileInputStream("text.txt"), "UTF-8"));
         BufferedWriter bw = new BufferedWriter(
             new OutputStreamWriter(new FileOutputStream("out.txt"), "UTF-8"))) {
        
        String line;
        while ((line = br.readLine()) != null) { // 按行读取
            bw.write(line);
            bw.newLine(); // 跨平台换行
        }
    }
    
    核心优势:提供readLine()newLine()方法,适合处理换行符敏感的文本(如 CSV、配置文件)。
  • PrintWriter(打印流)

    // 方便的文本输出(支持自动刷新)
    try (PrintWriter pw = new PrintWriter(
             new OutputStreamWriter(new FileOutputStream("log.txt"), "UTF-8"), true)) {
        
        pw.println("日志信息1"); // 自动换行
        pw.printf("用户%s登录成功", "张三"); // 格式化输出
    }
    
    适用场景:日志输出、格式化文本写入(如报表生成)。
三、特殊流
  1. ByteArrayInputStream/ByteArrayOutputStream(字节数组流)

    // 内存中处理数据(无磁盘IO)
    byte[] data = "内存数据".getBytes();
    try (ByteArrayInputStream bais = new ByteArrayInputStream(data);
         ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
        
        byte[] buffer = new byte[1024];
        int len;
        while ((len = bais.read(buffer)) != -1) {
            baos.write(buffer, 0, len); // 数据在内存中流转
        }
        System.out.println(baos.toString());
    }
    
    适用场景临时数据处理(如数据加密、压缩前的内存缓存)。
  2. PipedInputStream/PipedOutputStream(管道流)

    // 线程间通信
    PipedInputStream pis = new PipedInputStream();
    PipedOutputStream pos = new PipedOutputStream(pis); // 绑定输入输出流
    
    // 线程1:写入数据
    new Thread(() -> {
        try { pos.write("线程间通信".getBytes()); } 
        catch (IOException e) { e.printStackTrace(); }
    }).start();
    
    // 线程2:读取数据
    new Thread(() -> {
        try { 
            byte[] buf = new byte[1024];
            pis.read(buf); 
        } catch (IOException e) { e.printStackTrace(); }
    }).start();
    
    适用场景同一进程内的线程间数据传递。
四、核心区别与选择指南
维度 字节流 字符流
处理单位 字节(8 位) 字符(根据编码,如 UTF-8 为 1-4 字节)
处理对象 所有文件(文本 + 二进制) 仅文本文件
编码依赖 不依赖编码 依赖编码(需注意乱码)
核心类 InputStream/OutputStream Reader/Writer

选择原则

  • 二进制文件(图片、视频)→ 字节流 + 缓冲流
  • 文本文件 → 字符流(优先用转换流指定编码)+ 缓冲流
  • 基本数据类型 → Data 流
  • 对象持久化 → 对象流
  • 内存数据处理 → 字节数组流
  • 线程通信 → 管道流
五、最佳实践
  1. 资源自动关闭始终使用try-with-resources语法(JDK7+),无需手动调用close()
  2. 缓冲流优先处理大文件时,用缓冲流包装节点流提升性能。
  3. 编码显式化文本处理时通过InputStreamReader/OutputStreamWriter指定编码(如 UTF-8)。
  4. 序列化版本控制实现Serializable的类必须定义serialVersionUID,避免版本冲突。

网站公告

今日签到

点亮在社区的每一天
去签到