Skip to content

IO

Java IO 是 Java 核心库的重要组成部分,用于处理输入输出操作。

IO 流概述

流的分类

  • 按方向:输入流(InputStream/Reader)、输出流(OutputStream/Writer)
  • 按类型:字节流(处理二进制数据)、字符流(处理文本数据)
  • 按功能:节点流(直接操作数据源)、处理流(包装其他流)

流的继承体系

字节输入流: InputStream
├── FileInputStream
├── ByteArrayInputStream
├── BufferedInputStream
├── DataInputStream
└── ObjectInputStream

字节输出流: OutputStream
├── FileOutputStream
├── ByteArrayOutputStream
├── BufferedOutputStream
├── DataOutputStream
└── ObjectOutputStream

字符输入流: Reader
├── FileReader
├── CharArrayReader
├── BufferedReader
└── InputStreamReader

字符输出流: Writer
├── FileWriter
├── CharArrayWriter
├── BufferedWriter
└── OutputStreamWriter

字节流

FileInputStream / FileOutputStream

java
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;

// 写入文件
try (FileOutputStream fos = new FileOutputStream("output.txt")) {
    String content = "Hello, Java IO!";
    fos.write(content.getBytes());
} catch (IOException e) {
    e.printStackTrace();
}

// 读取文件
try (FileInputStream fis = new FileInputStream("output.txt")) {
    byte[] buffer = new byte[1024];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        System.out.println(new String(buffer, 0, len));
    }
} catch (IOException e) {
    e.printStackTrace();
}

// 复制文件
try (FileInputStream fis = new FileInputStream("source.txt");
     FileOutputStream fos = new FileOutputStream("dest.txt")) {

    byte[] buffer = new byte[1024];
    int len;
    while ((len = fis.read(buffer)) != -1) {
        fos.write(buffer, 0, len);
    }
} catch (IOException e) {
    e.printStackTrace();
}

ByteArrayInputStream / ByteArrayOutputStream

java
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;

// 内存中的字节流
byte[] data = "Hello".getBytes();

// 从字节数组读取
try (ByteArrayInputStream bais = new ByteArrayInputStream(data)) {
    int b;
    while ((b = bais.read()) != -1) {
        System.out.print((char) b);
    }
}

// 写入到字节数组
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
    baos.write("Hello".getBytes());
    baos.write(" World".getBytes());
    byte[] result = baos.toByteArray();
    System.out.println(new String(result));
}

BufferedInputStream / BufferedOutputStream

java
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;

// 使用缓冲流提高性能
try (BufferedInputStream bis = new BufferedInputStream(
        new FileInputStream("large.txt"));
     BufferedOutputStream bos = new BufferedOutputStream(
        new FileOutputStream("copy.txt"))) {

    byte[] buffer = new byte[8192];
    int len;
    while ((len = bis.read(buffer)) != -1) {
        bos.write(buffer, 0, len);
    }
}

DataInputStream / DataOutputStream

java
import java.io.DataInputStream;
import java.io.DataOutputStream;

// 写入基本数据类型
try (DataOutputStream dos = new DataOutputStream(
        new FileOutputStream("data.bin"))) {
    dos.writeInt(100);
    dos.writeDouble(3.14);
    dos.writeUTF("Hello");
    dos.writeBoolean(true);
}

// 读取基本数据类型
try (DataInputStream dis = new DataInputStream(
        new FileInputStream("data.bin"))) {
    int i = dis.readInt();        // 100
    double d = dis.readDouble();  // 3.14
    String s = dis.readUTF();     // "Hello"
    boolean b = dis.readBoolean();// true
}

字符流

FileReader / FileWriter

java
import java.io.FileReader;
import java.io.FileWriter;

// 写入文件
try (FileWriter fw = new FileWriter("output.txt")) {
    fw.write("Hello, Java IO!");
    fw.write("\n第二行");
} catch (IOException e) {
    e.printStackTrace();
}

// 追加写入
try (FileWriter fw = new FileWriter("output.txt", true)) {
    fw.write("\n追加的内容");
}

// 读取文件
try (FileReader fr = new FileReader("output.txt")) {
    char[] buffer = new char[1024];
    int len;
    while ((len = fr.read(buffer)) != -1) {
        System.out.print(new String(buffer, 0, len));
    }
}

BufferedReader / BufferedWriter

java
import java.io.BufferedReader;
import java.io.BufferedWriter;

// 写入文件
try (BufferedWriter bw = new BufferedWriter(
        new FileWriter("output.txt"))) {
    bw.write("第一行");
    bw.newLine();  // 写入换行符
    bw.write("第二行");
}

// 读取文件(按行)
try (BufferedReader br = new BufferedReader(
        new FileReader("output.txt"))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}

InputStreamReader / OutputStreamWriter

java
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;

// 字节流转字符流(指定编码)
try (BufferedReader br = new BufferedReader(
        new InputStreamReader(
            new FileInputStream("utf8.txt"), StandardCharsets.UTF_8))) {
    String line;
    while ((line = br.readLine()) != null) {
        System.out.println(line);
    }
}

// 写入时指定编码
try (BufferedWriter bw = new BufferedWriter(
        new OutputStreamWriter(
            new FileOutputStream("gbk.txt"), "GBK"))) {
    bw.write("中文内容");
}

文件操作

File 类

java
import java.io.File;

// 创建 File 对象
File file = new File("test.txt");
File dir = new File("D:/projects");
File file2 = new File(dir, "test.txt");

// 获取文件信息
String name = file.getName();           // 文件名
String path = file.getPath();           // 路径
String absPath = file.getAbsolutePath();// 绝对路径
String parent = file.getParent();       // 父目录
long length = file.length();            // 文件大小(字节)
long lastModified = file.lastModified();// 最后修改时间

// 判断
boolean exists = file.exists();         // 是否存在
boolean isFile = file.isFile();         // 是否为文件
boolean isDir = file.isDirectory();     // 是否为目录
boolean canRead = file.canRead();       // 是否可读
boolean canWrite = file.canWrite();     // 是否可写
boolean isHidden = file.isHidden();     // 是否隐藏

// 创建
boolean created = file.createNewFile(); // 创建文件
boolean mkdir = dir.mkdir();            // 创建目录(单级)
boolean mkdirs = dir.mkdirs();          // 创建目录(多级)

// 删除
boolean deleted = file.delete();        // 删除文件或空目录

// 重命名
boolean renamed = file.renameTo(new File("newname.txt"));

// 列出目录内容
String[] list = dir.list();             // 文件名数组
File[] files = dir.listFiles();         // File 对象数组

// 过滤器
File[] txtFiles = dir.listFiles((d, name) -> name.endsWith(".txt"));

文件遍历

java
import java.io.File;

public class FileTraversal {
    public static void main(String[] args) {
        File dir = new File("D:/projects");
        listFiles(dir, 0);
    }

    public static void listFiles(File file, int level) {
        if (file.exists()) {
            String indent = "  ".repeat(level);
            if (file.isDirectory()) {
                System.out.println(indent + "📁 " + file.getName());
                File[] files = file.listFiles();
                if (files != null) {
                    for (File f : files) {
                        listFiles(f, level + 1);
                    }
                }
            } else {
                System.out.println(indent + "📄 " + file.getName() +
                    " (" + file.length() + " bytes)");
            }
        }
    }
}

NIO

Path 和 Files

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

// Path 对象
Path path = Paths.get("D:/projects/test.txt");
Path path2 = Path.of("D:/projects", "subdir", "file.txt");

// 路径信息
Path fileName = path.getFileName();
Path parent = path.getParent();
Path root = path.getRoot();
int count = path.getNameCount();

// 路径操作
Path resolved = path.resolve("subdir/file.txt");  // 解析路径
Path normalized = path.normalize();               // 规范化
Path relativized = path.relativize(path2);        // 相对路径

// Files 工具类
// 创建
Files.createFile(path);
Files.createDirectory(Paths.get("newdir"));
Files.createDirectories(Paths.get("a/b/c"));

// 复制
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);

// 移动
Files.move(source, target, StandardCopyOption.REPLACE_EXISTING);

// 删除
Files.delete(path);
Files.deleteIfExists(path);

// 读取文件
List<String> lines = Files.readAllLines(path, StandardCharsets.UTF_8);
byte[] bytes = Files.readAllBytes(path);

// 写入文件
Files.write(path, "内容".getBytes(StandardCharsets.UTF_8));
Files.write(path, lines, StandardCharsets.UTF_8);

// 追加写入
Files.write(path, "追加内容".getBytes(), StandardOpenOption.APPEND);

// 判断
boolean exists = Files.exists(path);
boolean isRegularFile = Files.isRegularFile(path);
boolean isDirectory = Files.isDirectory(path);
boolean isReadable = Files.isReadable(path);
boolean isWritable = Files.isWritable(path);

// 文件属性
long size = Files.size(path);
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);

文件遍历

java
import java.nio.file.*;
import java.io.IOException;

// 使用 Files.walk
try (Stream<Path> stream = Files.walk(Paths.get("D:/projects"))) {
    stream.filter(Files::isRegularFile)
          .filter(p -> p.toString().endsWith(".java"))
          .forEach(System.out::println);
}

// 使用 Files.list(只列出直接子项)
try (Stream<Path> stream = Files.list(Paths.get("D:/projects"))) {
    stream.forEach(System.out::println);
}

// 使用 Files.find
try (Stream<Path> stream = Files.find(
        Paths.get("D:/projects"),
        10,  // 最大深度
        (path, attrs) -> attrs.isRegularFile() &&
                        path.toString().endsWith(".java"))) {
    stream.forEach(System.out::println);
}

// 使用 FileVisitor
Files.walkFileTree(Paths.get("D:/projects"), new SimpleFileVisitor<Path>() {
    @Override
    public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
        System.out.println("访问文件: " + file);
        return FileVisitResult.CONTINUE;
    }

    @Override
    public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
        System.out.println("进入目录: " + dir);
        return FileVisitResult.CONTINUE;
    }
});

文件监视

java
import java.nio.file.*;

WatchService watchService = FileSystems.getDefault().newWatchService();
Path dir = Paths.get("D:/projects");
dir.register(watchService,
    StandardWatchEventKinds.ENTRY_CREATE,
    StandardWatchEventKinds.ENTRY_DELETE,
    StandardWatchEventKinds.ENTRY_MODIFY);

while (true) {
    WatchKey key = watchService.take();

    for (WatchEvent<?> event : key.pollEvents()) {
        WatchEvent.Kind<?> kind = event.kind();
        Path fileName = (Path) event.context();

        if (kind == StandardWatchEventKinds.ENTRY_CREATE) {
            System.out.println("创建: " + fileName);
        } else if (kind == StandardWatchEventKinds.ENTRY_DELETE) {
            System.out.println("删除: " + fileName);
        } else if (kind == StandardWatchEventKinds.ENTRY_MODIFY) {
            System.out.println("修改: " + fileName);
        }
    }

    boolean valid = key.reset();
    if (!valid) break;
}

对象序列化

序列化与反序列化

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;  // 不序列化

    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 + "}";
    }
}

// 序列化
Person person = new Person("张三", 25, "123456");
try (ObjectOutputStream oos = new ObjectOutputStream(
        new FileOutputStream("person.obj"))) {
    oos.writeObject(person);
}

// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
        new FileInputStream("person.obj"))) {
    Person p = (Person) ois.readObject();
    System.out.println(p);  // Person{name='张三', age=25}
}

自定义序列化

java
class User implements Serializable {
    private String username;
    private String password;

    private void writeObject(ObjectOutputStream oos) throws IOException {
        oos.defaultWriteObject();
        // 自定义加密
        oos.writeObject(Base64.getEncoder().encodeToString(password.getBytes()));
    }

    private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException {
        ois.defaultReadObject();
        // 自定义解密
        String encoded = (String) ois.readObject();
        password = new String(Base64.getDecoder().decode(encoded));
    }
}

实践示例

文件复制工具

java
import java.io.*;
import java.nio.file.*;

public class FileCopyUtil {

    public static void copyFile(String source, String target) throws IOException {
        try (InputStream is = new FileInputStream(source);
             OutputStream os = new FileOutputStream(target)) {
            byte[] buffer = new byte[8192];
            int len;
            while ((len = is.read(buffer)) != -1) {
                os.write(buffer, 0, len);
            }
        }
    }

    public static void copyFileNIO(String source, String target) throws IOException {
        Files.copy(Paths.get(source), Paths.get(target),
            StandardCopyOption.REPLACE_EXISTING);
    }

    public static void copyDirectory(String source, String target) throws IOException {
        Path sourcePath = Paths.get(source);
        Path targetPath = Paths.get(target);

        Files.walk(sourcePath).forEach(sourceFile -> {
            Path targetFile = targetPath.resolve(sourcePath.relativize(sourceFile));
            try {
                if (Files.isDirectory(sourceFile)) {
                    Files.createDirectories(targetFile);
                } else {
                    Files.copy(sourceFile, targetFile,
                        StandardCopyOption.REPLACE_EXISTING);
                }
            } catch (IOException e) {
                e.printStackTrace();
            }
        });
    }

    public static void main(String[] args) throws IOException {
        copyFile("source.txt", "target.txt");
        copyDirectory("D:/source", "D:/target");
    }
}

配置文件读取器

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

public class ConfigReader {
    private Properties properties = new Properties();

    public void load(String filePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            properties.load(fis);
        }
    }

    public void loadFromXML(String filePath) throws IOException {
        try (FileInputStream fis = new FileInputStream(filePath)) {
            properties.loadFromXML(fis);
        }
    }

    public String get(String key) {
        return properties.getProperty(key);
    }

    public String get(String key, String defaultValue) {
        return properties.getProperty(key, defaultValue);
    }

    public void set(String key, String value) {
        properties.setProperty(key, value);
    }

    public void save(String filePath, String comment) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            properties.store(fos, comment);
        }
    }

    public void saveToXML(String filePath, String comment) throws IOException {
        try (FileOutputStream fos = new FileOutputStream(filePath)) {
            properties.storeToXML(fos, comment);
        }
    }

    public static void main(String[] args) throws IOException {
        ConfigReader config = new ConfigReader();
        config.load("config.properties");

        System.out.println("数据库: " + config.get("db.url"));
        System.out.println("用户名: " + config.get("db.username"));

        config.set("new.key", "new value");
        config.save("config.properties", "Updated configuration");
    }
}

日志文件分析器

java
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.regex.*;
import java.util.stream.*;

public class LogAnalyzer {

    public static void analyzeLogFile(String filePath) throws IOException {
        Map<String, Long> levelCounts = new HashMap<>();
        Map<String, Long> hourCounts = new HashMap<>();
        List<String> errors = new ArrayList<>();

        Pattern pattern = Pattern.compile(
            "\\[(\\d{4}-\\d{2}-\\d{2} (\\d{2}):\\d{2}:\\d{2})\\] \\[(\\w+)\\] (.+)"
        );

        try (BufferedReader reader = Files.newBufferedReader(Paths.get(filePath))) {
            String line;
            while ((line = reader.readLine()) != null) {
                Matcher matcher = pattern.matcher(line);
                if (matcher.find()) {
                    String hour = matcher.group(2);
                    String level = matcher.group(3);
                    String message = matcher.group(4);

                    levelCounts.merge(level, 1L, Long::sum);
                    hourCounts.merge(hour, 1L, Long::sum);

                    if ("ERROR".equals(level)) {
                        errors.add(line);
                    }
                }
            }
        }

        System.out.println("=== 日志级别统计 ===");
        levelCounts.forEach((k, v) -> System.out.println(k + ": " + v));

        System.out.println("\n=== 每小时日志量 ===");
        hourCounts.entrySet().stream()
            .sorted(Map.Entry.comparingByKey())
            .forEach(e -> System.out.println(e.getKey() + "时: " + e.getValue()));

        System.out.println("\n=== 错误日志 ===");
        errors.forEach(System.out::println);
    }

    public static void main(String[] args) throws IOException {
        analyzeLogFile("application.log");
    }
}