文件与 IO
Path、Files (NIO.2)、字节流 vs 字符流、目录遍历
IO 在 Java 中的应用场景
几乎所有非纯内存程序都要做 IO:读配置 / 写日志 / 处理上传文件 / 导出 CSV / 解析大数据。Java 的 IO API 分两代:传统 java.io(File/FileInputStream/Reader)与 NIO.2 java.nio.file(Path/Files),后者从 Java 7 开始是首选——更简洁、错误信息更好、跨平台路径处理更稳。
- 小文件一次性读写:Files.readString / writeString
- 大文件按行流式处理:Files.lines(结合 try-with-resources)
- 二进制读写:Files.newInputStream / newOutputStream + BufferedStream
- 目录遍历:Files.walk / Files.list
- 字符 vs 字节:文本用 Reader/Writer(自动处理编码),二进制用 InputStream/OutputStream
Path:跨平台路径
Path 是 java.nio.file 的核心类,表示文件系统路径。Path.of(...) 是创建入口,能自动处理 Windows / Linux 分隔符差异。
// PathDemo.java
import java.nio.file.Path;
import java.nio.file.Paths;
public class PathDemo {
public static void main(String[] args) {
Path p = Path.of("data", "users", "list.json");
System.out.println(p); // data/users/list.json (或 data\\users\\list.json on Windows)
System.out.println(p.toAbsolutePath());
System.out.println(p.getFileName()); // list.json
System.out.println(p.getParent()); // data/users
System.out.println(p.getName(0)); // data
System.out.println(p.getNameCount()); // 3
// 拼接
Path base = Path.of("/var/log");
Path full = base.resolve("app.log");
System.out.println(full); // /var/log/app.log
// 规范化(消除 . 和 ..)
System.out.println(Path.of("a/./b/../c").normalize()); // a/c
}
}小文件:一次性读写
适合配置文件、模板、小 JSON——通常几 KB ~ 几 MB。Files.readString / writeString 自动用 UTF-8(也可显式传字符集)。
// SmallFile.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
public class SmallFile {
public static void main(String[] args) throws IOException {
Path p = Path.of("hello.txt");
// 写(默认覆盖)
Files.writeString(p, "hello\nworld\n");
// 追加
Files.writeString(p, "more\n", StandardOpenOption.APPEND);
// 读字符串
String content = Files.readString(p);
System.out.println(content);
// 读全部字节(二进制)
byte[] bytes = Files.readAllBytes(p);
System.out.println("size=" + bytes.length);
Files.delete(p);
}
}大文件:按行流式处理
应用场景:分析几百 MB 的日志文件、读取 CSV 数据。Files.lines 返回一个 Stream<String>,**懒**地按行读,配合 try-with-resources 自动关闭底层文件句柄。
// BigFile.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
public class BigFile {
public static void main(String[] args) throws IOException {
Path p = Files.createTempFile("log", ".txt");
Files.writeString(p, """
INFO user login id=1
ERROR db down
INFO user login id=2
WARN slow query 1200ms
ERROR connection reset
""");
// 流式按行处理,自动关闭流(关键:try-with-resources)
try (Stream<String> lines = Files.lines(p)) {
long errCount = lines
.filter(line -> line.startsWith("ERROR"))
.peek(System.out::println)
.count();
System.out.println("errors = " + errCount);
}
Files.delete(p);
}
}字节流 vs 字符流
InputStream / OutputStream 处理字节,适合二进制(图片、PDF、压缩文件);Reader / Writer 处理字符,自动按指定编码解码字节,适合文本。BufferedReader / BufferedWriter 加缓冲,能大幅提速。
// Streams.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
public class Streams {
public static void main(String[] args) throws IOException {
Path p = Files.createTempFile("demo", ".txt");
Files.writeString(p, "line1\nline2\nline3\n");
// 字节流:读原始字节
try (InputStream in = Files.newInputStream(p)) {
int b;
int count = 0;
while ((b = in.read()) != -1) count++;
System.out.println("bytes = " + count);
}
// 字符流 + 缓冲:按行高效读
try (BufferedReader reader = Files.newBufferedReader(p, StandardCharsets.UTF_8)) {
String line;
while ((line = reader.readLine()) != null) {
System.out.println(line);
}
}
Files.delete(p);
}
}目录与文件操作
// FileOps.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
public class FileOps {
public static void main(String[] args) throws IOException {
Path dir = Files.createTempDirectory("demo-");
Path src = dir.resolve("a.txt");
Path dst = dir.resolve("b.txt");
Files.writeString(src, "hello");
System.out.println(Files.exists(src)); // true
System.out.println(Files.size(src)); // 5
// 复制
Files.copy(src, dst, StandardCopyOption.REPLACE_EXISTING);
// 移动 / 重命名
Path moved = dir.resolve("c.txt");
Files.move(dst, moved, StandardCopyOption.REPLACE_EXISTING);
// 删除
Files.delete(src);
Files.delete(moved);
Files.delete(dir);
}
}目录遍历:Files.walk / Files.list
应用场景:扫描某个目录下所有 .java 文件统计代码行数、查找特定后缀文件、构建索引。walk 递归遍历,list 只看一层。两者都返回 Stream<Path>,**必须 try-with-resources**。
// WalkDemo.java
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.stream.Stream;
public class WalkDemo {
public static void main(String[] args) throws IOException {
Path root = Path.of(".");
// 只列当前目录
try (Stream<Path> top = Files.list(root)) {
top.forEach(System.out::println);
}
System.out.println("--- recursive ---");
// 递归找所有 .java 文件
try (Stream<Path> walk = Files.walk(root)) {
walk.filter(Files::isRegularFile)
.filter(p -> p.toString().endsWith(".java"))
.limit(10)
.forEach(System.out::println);
}
}
}下载/上传:传统流写入
应用场景:HTTP 下载、网络读字节落盘、压缩文件读写。使用 InputStream.transferTo(Java 9+)一行搞定。
// Transfer.java
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
public class Transfer {
public static void main(String[] args) throws IOException {
// 模拟一个网络流
InputStream in = new ByteArrayInputStream("streaming payload".getBytes());
Path dst = Files.createTempFile("out", ".bin");
try (InputStream src = in; OutputStream out = Files.newOutputStream(dst)) {
long bytes = src.transferTo(out); // Java 9+
System.out.println("transferred " + bytes + " bytes");
}
System.out.println(Files.readString(dst));
Files.delete(dst);
}
}