2025-8-21
IO 即
Input/Output,输入和输出。数据输入到计算机内存的过程即输入,反之输出到外部存储(比如数据库,文件,远程主机)的过程即输出。数据传输过程类似于水流,因此称为 IO 流。IO 流在 Java 中分为输入流和输出流,而根据数据的处理方式又分为字节流和字符流。Java IO 流的 40 多个类都是从如下 4 个抽象类基类中派生出来的。
InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。
1. I/O流分类
字节流:
InputStream/OutputStream字符流:
Reader/Writer桥接:
InputStreamReader/OutputStreamWriter(需指定编码)
2. 常用流
缓冲流:
BufferedInputStream/BufferedReader(提高性能)数据流:
DataInputStream/DataOutputStream(读写基本类型)对象流:
ObjectInputStream/ObjectOutputStream(序列化)打印流:
PrintWriter(支持println/printf)随机访问:
RandomAccessFile
3. Java文件 API
旧版:
File(路径操作有限)新版:
Path/Files(推荐)Files.exists/size/copy/move/deleteFiles.readString/writeString(Java 11+)Files.lines(流式逐行读取)Files.list/walk/find(遍历)
4. 代码示例
逐行读取文本
try (BufferedReader br = Files.newBufferedReader(
Path.of("a.txt"), StandardCharsets.UTF_8)) {
for (String line; (line = br.readLine()) != null; ) {
System.out.println(line);
}
}文本写入
try (BufferedWriter bw = Files.newBufferedWriter(
Path.of("out.txt"), StandardCharsets.UTF_8)) {
bw.write("Hello\n");
}文件复制(字节流)
try (InputStream in = new BufferedInputStream(Files.newInputStream(Path.of("a.bin")));
OutputStream out = new BufferedOutputStream(Files.newOutputStream(Path.of("b.bin")))) {
byte[] buf = new byte[8192];
int n;
while ((n = in.read(buf)) != -1) out.write(buf, 0, n);
}大文件复制(高效)
try (FileChannel in = FileChannel.open(Path.of("a.iso"), StandardOpenOption.READ);
FileChannel out = FileChannel.open(Path.of("b.iso"),
StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
in.transferTo(0, in.size(), out);
}对象序列化
try (ObjectOutputStream oos = new ObjectOutputStream(
Files.newOutputStream(Path.of("obj.ser")))) {
oos.writeObject(obj);
}
try (ObjectInputStream ois = new ObjectInputStream(
Files.newInputStream(Path.of("obj.ser")))) {
Object o = ois.readObject();
}5. NIO 特性
FileChannel:
transferTo/transferFrom(零拷贝)MappedByteBuffer:内存映射文件,支持随机访问
Scatter/Gather:分散读/聚集写
AsynchronousFileChannel:异步文件 I/O
WatchService:目录监听
6. 文件属性
BasicFileAttributes attr = Files.readAttributes(path, BasicFileAttributes.class);
Files.isHidden(path);
Files.size(path);7. 随机访问与文件锁
RandomAccessFile raf = new RandomAccessFile("data.bin", "rw");
raf.seek(1024);
int v = raf.readInt();
raf.writeLong(123L);
try (FileChannel ch = FileChannel.open(Path.of("data.bin"), StandardOpenOption.WRITE);
FileLock lock = ch.lock()) {
// 文件锁
}补充解释
1. 文本流必须显式指定编码
默认编码依赖操作系统(Windows 可能是 GBK,Linux 常见 UTF-8)。
如果不指定,跨平台读写时可能出现乱码。
建议始终显式传入
StandardCharsets.UTF_8。
new BufferedReader(new InputStreamReader(
new FileInputStream("a.txt"), StandardCharsets.UTF_8));2. 大文件不要使用 readAllLines / readAllBytes
Files.readAllLines(path)会一次性把文件所有内容读入内存。对几百 MB 或更大文件可能导致 内存溢出。
替代:逐行/分块读取。
try (Stream<String> lines = Files.lines(path)) {
lines.forEach(System.out::println);
}3. available() 不能当作文件大小
InputStream.available()只保证返回“当前不阻塞情况下可读的字节数”。并不等于文件剩余长度,也可能比实际可读的少。
获取文件大小应使用:
long size = Files.size(path);4. flush() vs close()
flush():立即把缓冲区数据写入目标(文件/网络)。close():关闭流前会自动调用一次flush()。场景:写日志、网络通信时,必须及时写出,而不是等缓冲区满。
5. try-with-resources
Java 7+ 语法,确保流在 作用域结束时自动关闭,无需手动
finally。自动调用
close(),即使中途抛出异常也能安全关闭。
try (BufferedWriter bw = Files.newBufferedWriter(path)) {
bw.write("Hello");
} // 这里自动 close()