Skip to content

IO流

IO流用于处理数据的输入和输出操作。Java 提供了丰富的 IO 流类来处理文件、网络等数据传输。

IO流概述

流的分类

text
┌─────────────────────────────────────────────────────────────────┐
│                       IO流分类                                  │
├─────────────────────────────────────────────────────────────────┤
│  按数据流向:                                                    │
│    - 输入流(InputStream/Reader):读取数据                      │
│    - 输出流(OutputStream/Writer):写出数据                     │
│                                                                 │
│  按数据类型:                                                    │
│    - 字节流:处理所有类型数据(图片、视频、文本等)               │
│    - 字符流:处理文本数据(自动处理编码问题)                     │
│                                                                 │
│  按功能:                                                        │
│    - 节点流:直接操作数据源                                      │
│    - 处理流:包装节点流,增强功能                                │
└─────────────────────────────────────────────────────────────────┘

流的体系

text
┌─────────────────────────────────────────────────────────────────┐
│                        IO流体系                                 │
├─────────────────────────────────────────────────────────────────┤
│  字节流:                                                        │
│    InputStream          OutputStream                            │
│    ├── FileInputStream  ├── FileOutputStream                   │
│    ├── BufferedInputStream ├── BufferedOutputStream             │
│    ├── ObjectInputStream ├── ObjectOutputStream                 │
│    └── ByteArrayInputStream └── ByteArrayOutputStream          │
│                                                                 │
│  字符流:                                                        │
│    Reader               Writer                                  │
│    ├── FileReader       ├── FileWriter                          │
│    ├── BufferedReader   ├── BufferedWriter                      │
│    ├── InputStreamReader ├── OutputStreamWriter                  │
│    └── StringReader     └── StringWriter                        │
└─────────────────────────────────────────────────────────────────┘

文件操作

File 类

File 类表示文件或目录的路径。

java
import java.io.*;

public class FileDemo {
    public static void main(String[] args) throws IOException {
        // 创建 File 对象
        File file = new File("test.txt");
        File dir = new File("mydir");
        
        // 文件信息
        System.out.println("文件名:" + file.getName());
        System.out.println("绝对路径:" + file.getAbsolutePath());
        System.out.println("父目录:" + file.getParent());
        System.out.println("是否存在:" + file.exists());
        System.out.println("是否文件:" + file.isFile());
        System.out.println("是否目录:" + file.isDirectory());
        System.out.println("文件大小:" + file.length() + " 字节");
        System.out.println("最后修改:" + file.lastModified());
        
        // 创建文件
        if (!file.exists()) {
            boolean created = file.createNewFile();
            System.out.println("文件创建:" + (created ? "成功" : "失败"));
        }
        
        // 创建目录
        if (!dir.exists()) {
            boolean created = dir.mkdirs();  // 创建多级目录
            System.out.println("目录创建:" + (created ? "成功" : "失败"));
        }
        
        // 删除文件
        // boolean deleted = file.delete();
        // System.out.println("文件删除:" + (deleted ? "成功" : "失败"));
        
        // 重命名
        File newFile = new File("renamed.txt");
        // file.renameTo(newFile);
        
        // 遍历目录
        File root = new File(".");
        String[] list = root.list();
        if (list != null) {
            System.out.println("\n当前目录内容:");
            for (String name : list) {
                System.out.println(name);
            }
        }
        
        // 过滤文件
        File[] javaFiles = root.listFiles((d, name) -> name.endsWith(".java"));
        System.out.println("\nJava 文件:");
        if (javaFiles != null) {
            for (File f : javaFiles) {
                System.out.println(f.getName());
            }
        }
    }
}

字节流

FileInputStream 和 FileOutputStream

java
import java.io.*;

public class FileStreamDemo {
    public static void main(String[] args) {
        // 写入文件
        try (FileOutputStream fos = new FileOutputStream("output.txt")) {
            String content = "Hello, Java IO!\n你好,Java IO!";
            fos.write(content.getBytes());
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 读取文件(逐字节)
        try (FileInputStream fis = new FileInputStream("output.txt")) {
            int data;
            System.out.println("\n逐字节读取:");
            while ((data = fis.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 读取文件(字节数组)
        try (FileInputStream fis = new FileInputStream("output.txt")) {
            byte[] buffer = new byte[1024];
            int len;
            System.out.println("\n\n字节数组读取:");
            while ((len = fis.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 文件复制
        copyFile("output.txt", "copy.txt");
    }
    
    /**
     * 文件复制方法
     */
    public static void copyFile(String src, String dest) {
        try (FileInputStream fis = new FileInputStream(src);
             FileOutputStream fos = new FileOutputStream(dest)) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            System.out.println("\n\n复制成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BufferedInputStream 和 BufferedOutputStream

java
import java.io.*;

public class BufferedStreamDemo {
    public static void main(String[] args) {
        // 使用缓冲流提高效率
        long start, end;
        
        // 不使用缓冲流
        start = System.currentTimeMillis();
        copyWithoutBuffer("largefile.zip", "copy1.zip");
        end = System.currentTimeMillis();
        System.out.println("不使用缓冲流耗时:" + (end - start) + " ms");
        
        // 使用缓冲流
        start = System.currentTimeMillis();
        copyWithBuffer("largefile.zip", "copy2.zip");
        end = System.currentTimeMillis();
        System.out.println("使用缓冲流耗时:" + (end - start) + " ms");
    }
    
    // 不使用缓冲流
    public static void copyWithoutBuffer(String src, String dest) {
        try (FileInputStream fis = new FileInputStream(src);
             FileOutputStream fos = new FileOutputStream(dest)) {
            int data;
            while ((data = fis.read()) != -1) {
                fos.write(data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    
    // 使用缓冲流
    public static void copyWithBuffer(String src, String dest) {
        try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(src));
             BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(dest))) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

字符流

FileReader 和 FileWriter

java
import java.io.*;

public class FileReaderWriterDemo {
    public static void main(String[] args) {
        // 写入文件
        try (FileWriter fw = new FileWriter("chars.txt")) {
            fw.write("Hello, Java!\n");
            fw.write("你好,Java!\n");
            fw.write("字符流处理文本更方便");
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 读取文件(逐字符)
        try (FileReader fr = new FileReader("chars.txt")) {
            int ch;
            System.out.println("\n逐字符读取:");
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 读取文件(字符数组)
        try (FileReader fr = new FileReader("chars.txt")) {
            char[] buffer = new char[1024];
            int len;
            System.out.println("\n\n字符数组读取:");
            while ((len = fr.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

BufferedReader 和 BufferedWriter

java
import java.io.*;

public class BufferedReadWriteDemo {
    public static void main(String[] args) {
        // 写入文件
        try (BufferedWriter bw = new BufferedWriter(new FileWriter("lines.txt"))) {
            bw.write("第一行");
            bw.newLine();  // 换行
            bw.write("第二行");
            bw.newLine();
            bw.write("第三行");
            System.out.println("写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 读取文件(按行)
        try (BufferedReader br = new BufferedReader(new FileReader("lines.txt"))) {
            String line;
            System.out.println("\n按行读取:");
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

InputStreamReader 和 OutputStreamWriter

java
import java.io.*;

public class StreamReaderWriterDemo {
    public static void main(String[] args) {
        // 指定编码写入
        try (OutputStreamWriter osw = new OutputStreamWriter(
                new FileOutputStream("utf8.txt"), "UTF-8")) {
            osw.write("UTF-8 编码的文本\n");
            osw.write("你好,世界!");
            System.out.println("UTF-8 写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 指定编码读取
        try (InputStreamReader isr = new InputStreamReader(
                new FileInputStream("utf8.txt"), "UTF-8")) {
            char[] buffer = new char[1024];
            int len;
            System.out.println("\nUTF-8 读取:");
            while ((len = isr.read(buffer)) != -1) {
                System.out.print(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // GBK 编码
        try (OutputStreamWriter osw = new OutputStreamWriter(
                new FileOutputStream("gbk.txt"), "GBK")) {
            osw.write("GBK 编码的文本");
            System.out.println("\n\nGBK 写入成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

对象流

序列化和反序列化

java
import java.io.*;

// 实现 Serializable 接口的类可以被序列化
class Person implements Serializable {
    private static final long serialVersionUID = 1L;  // 序列化版本号
    
    private String name;
    private int age;
    private transient String password;  // transient 表示不参与序列化
    
    public Person(String name, int age, String password) {
        this.name = name;
        this.age = age;
        this.password = password;
    }
    
    @Override
    public String toString() {
        return "Person{name='" + name + "', age=" + age + 
               ", password='" + password + "'}";
    }
}

public class ObjectStreamDemo {
    public static void main(String[] args) {
        // 序列化:对象 → 文件
        Person person = new Person("张三", 25, "123456");
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("person.obj"))) {
            oos.writeObject(person);
            System.out.println("序列化成功:" + person);
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 反序列化:文件 → 对象
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("person.obj"))) {
            Person p = (Person) ois.readObject();
            System.out.println("反序列化成功:" + p);
            // password 为 null(transient 字段不序列化)
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        // 序列化多个对象
        try (ObjectOutputStream oos = new ObjectOutputStream(
                new FileOutputStream("persons.obj"))) {
            oos.writeObject(new Person("张三", 25, "111"));
            oos.writeObject(new Person("李四", 30, "222"));
            oos.writeObject(new Person("王五", 28, "333"));
            System.out.println("\n序列化多个对象成功");
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 反序列化多个对象
        try (ObjectInputStream ois = new ObjectInputStream(
                new FileInputStream("persons.obj"))) {
            System.out.println("反序列化多个对象:");
            while (true) {
                try {
                    Person p = (Person) ois.readObject();
                    System.out.println(p);
                } catch (EOFException e) {
                    break;  // 文件结束
                }
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}

打印流

java
import java.io.*;

public class PrintStreamDemo {
    public static void main(String[] args) throws FileNotFoundException {
        // PrintStream:字节打印流
        try (PrintStream ps = new PrintStream("print.txt")) {
            ps.println("Hello, PrintStream!");
            ps.println(123);
            ps.println(3.14);
            ps.printf("格式化输出:%s, %d, %.2f%n", "Java", 100, 3.14159);
            System.out.println("PrintStream 写入成功");
        }
        
        // PrintWriter:字符打印流
        try (PrintWriter pw = new PrintWriter(new FileWriter("printwriter.txt"))) {
            pw.println("Hello, PrintWriter!");
            pw.println("支持自动刷新");
            pw.format("格式化:%d + %d = %d%n", 1, 2, 3);
            System.out.println("PrintWriter 写入成功");
        }
        
        // 重定向标准输出
        PrintStream originalOut = System.out;
        System.setOut(new PrintStream("console.txt"));
        System.out.println("这行会写入文件");
        System.setOut(originalOut);
        System.out.println("这行输出到控制台");
    }
}

标准输入输出

java
import java.io.*;
import java.util.Scanner;

public class StandardIODemo {
    public static void main(String[] args) {
        // 标准输入流
        System.out.println("请输入内容(输入 exit 退出):");
        
        // 方式一:使用 BufferedReader
        try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
            String line;
            while (!(line = br.readLine()).equals("exit")) {
                System.out.println("你输入了:" + line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        // 方式二:使用 Scanner(推荐)
        Scanner scanner = new Scanner(System.in);
        System.out.println("\n使用 Scanner:");
        System.out.print("请输入姓名:");
        String name = scanner.nextLine();
        System.out.print("请输入年龄:");
        int age = scanner.nextInt();
        System.out.println("姓名:" + name + ",年龄:" + age);
        scanner.close();
        
        // 标准错误流
        System.err.println("\n这是错误输出");
    }
}

NIO 文件操作

Java 7+ 提供了 NIO.2 文件 API,更简洁高效。

java
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.io.IOException;
import java.util.List;

public class NIOFileDemo {
    public static void main(String[] args) throws IOException {
        Path path = Paths.get("nio_test.txt");
        
        // 写入文件
        Files.write(path, "Hello, NIO!\n你好,NIO!".getBytes(StandardCharsets.UTF_8));
        System.out.println("写入成功");
        
        // 读取所有内容
        String content = Files.readString(path);
        System.out.println("\n读取全部内容:\n" + content);
        
        // 读取所有行
        List<String> lines = Files.readAllLines(path);
        System.out.println("\n按行读取:");
        lines.forEach(System.out::println);
        
        // 追加内容
        Files.write(path, "\n追加的内容".getBytes(StandardCharsets.UTF_8), 
                    StandardOpenOption.APPEND);
        
        // 文件信息
        System.out.println("\n文件信息:");
        System.out.println("文件名:" + path.getFileName());
        System.out.println("文件大小:" + Files.size(path) + " 字节");
        System.out.println("是否可读:" + Files.isReadable(path));
        System.out.println("是否可写:" + Files.isWritable(path));
        
        // 创建目录
        Path dir = Paths.get("nio_dir");
        Files.createDirectories(dir);
        
        // 复制文件
        Path copy = Paths.get("nio_copy.txt");
        Files.copy(path, copy, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("\n复制成功");
        
        // 移动/重命名文件
        Path moved = Paths.get("nio_moved.txt");
        Files.move(copy, moved, StandardCopyOption.REPLACE_EXISTING);
        System.out.println("移动成功");
        
        // 删除文件
        Files.deleteIfExists(moved);
        System.out.println("删除成功");
        
        // 遍历目录
        System.out.println("\n遍历当前目录:");
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(Paths.get("."))) {
            for (Path p : stream) {
                System.out.println(p.getFileName() + 
                    (Files.isDirectory(p) ? "/" : ""));
            }
        }
        
        // 递归遍历(深度优先)
        System.out.println("\n递归遍历:");
        Files.walk(Paths.get("."))
             .filter(p -> p.toString().endsWith(".java"))
             .limit(5)
             .forEach(System.out::println);
    }
}

小结

本章我们学习了:

  • File 类:文件和目录的操作
  • 字节流:FileInputStream、FileOutputStream、BufferedInputStream、BufferedOutputStream
  • 字符流:FileReader、FileWriter、BufferedReader、BufferedWriter
  • 转换流:InputStreamReader、OutputStreamWriter
  • 对象流:ObjectInputStream、ObjectOutputStream
  • 打印流:PrintStream、PrintWriter
  • NIO 文件操作:Files、Path 类

下一章,我们将学习 多线程,了解 Java 的并发编程。