一、Java IO 文件流
1. 文件流基础概念
文件流是用于读写文件数据的流,主要分为字节流和字符流。
字节流类体系
InputStream (抽象类)
├── FileInputStream (文件字节输入流)
├── FilterInputStream
└── ObjectInputStream
OutputStream (抽象类)
├── FileOutputStream (文件字节输出流)
├── FilterOutputStream
└── ObjectOutputStream
字符流类体系
Reader (抽象类)
├── FileReader (文件字符输入流)
├── BufferedReader
└── InputStreamReader
Writer (抽象类)
├── FileWriter (文件字符输出流)
├── BufferedWriter
└── OutputStreamWriter
2. 文件字节流示例
import java.io.*;
public class FileStreamExample {
// 使用 FileInputStream 和 FileOutputStream
public static void byteStreamExample() {
String sourceFile = "source.txt";
String targetFile = "target.txt";
try (FileInputStream fis = new FileInputStream(sourceFile);
FileOutputStream fos = new FileOutputStream(targetFile)) {
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
}
System.out.println("文件复制完成");
} catch (IOException e) {
e.printStackTrace();
}
}
// 读取文件内容到字符串
public static String readFileToString(String filename) {
try (FileInputStream fis = new FileInputStream(filename)) {
byte[] data = new byte[fis.available()];
fis.read(data);
return new String(data);
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
3. 文件字符流示例
import java.io.*;
public class CharacterStreamExample {
// 使用 FileReader 和 FileWriter
public static void characterStreamExample() {
String sourceFile = "source.txt";
String targetFile = "target.txt";
try (FileReader reader = new FileReader(sourceFile);
FileWriter writer = new FileWriter(targetFile)) {
char[] buffer = new char[1024];
int charsRead;
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
}
System.out.println("字符流文件复制完成");
} catch (IOException e) {
e.printStackTrace();
}
}
// 逐行读取文本文件
public static void readFileLineByLine(String filename) {
try (FileReader fr = new FileReader(filename);
BufferedReader br = new BufferedReader(fr)) {
String line;
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
二、Java IO 缓冲
1. 缓冲流的作用
缓冲流通过在内存中创建缓冲区来提高 IO 效率:
- 减少实际的物理读写次数
- 批量处理数据,提高性能
- 提供额外的便捷方法
2. 缓冲字节流
import java.io.*;
public class BufferedStreamExample {
// 使用 BufferedInputStream 和 BufferedOutputStream
public static void bufferedByteStream() {
String sourceFile = "largefile.dat";
String targetFile = "copy.dat";
try (FileInputStream fis = new FileInputStream(sourceFile);
BufferedInputStream bis = new BufferedInputStream(fis);
FileOutputStream fos = new FileOutputStream(targetFile);
BufferedOutputStream bos = new BufferedOutputStream(fos)) {
byte[] buffer = new byte[8192]; // 8KB 缓冲区
int bytesRead;
long startTime = System.currentTimeMillis();
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
long endTime = System.currentTimeMillis();
System.out.println("缓冲流复制耗时: " + (endTime - startTime) + "ms");
} catch (IOException e) {
e.printStackTrace();
}
}
// 比较有缓冲和无缓冲的性能差异
public static void comparePerformance() {
String testFile = "testfile.dat";
createTestFile(testFile, 100 * 1024 * 1024); // 创建 100MB 测试文件
// 无缓冲复制
long start1 = System.currentTimeMillis();
copyWithoutBuffer(testFile, "without_buffer.dat");
long end1 = System.currentTimeMillis();
// 有缓冲复制
long start2 = System.currentTimeMillis();
copyWithBuffer(testFile, "with_buffer.dat");
long end2 = System.currentTimeMillis();
System.out.println("无缓冲复制耗时: " + (end1 - start1) + "ms");
System.out.println("有缓冲复制耗时: " + (end2 - start2) + "ms");
}
private static void createTestFile(String filename, long size) {
try (RandomAccessFile raf = new RandomAccessFile(filename, "rw")) {
raf.setLength(size);
} catch (IOException e) {
e.printStackTrace();
}
}
private static void copyWithoutBuffer(String source, String target) {
try (FileInputStream fis = new FileInputStream(source);
FileOutputStream fos = new FileOutputStream(target)) {
int data;
while ((data = fis.read()) != -1) { // 逐字节读取,性能较差
fos.write(data);
}
} catch (IOException e) {
e.printStackTrace();
}
}
private static void copyWithBuffer(String source, String target) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 缓冲字符流
import java.io.*;
public class BufferedCharacterStream {
// 使用 BufferedReader 和 BufferedWriter
public static void bufferedCharacterExample() {
String inputFile = "input.txt";
String outputFile = "output.txt";
try (BufferedReader reader = new BufferedReader(new FileReader(inputFile));
BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile))) {
String line;
while ((line = reader.readLine()) != null) {
// 处理每一行数据
String processedLine = processLine(line);
writer.write(processedLine);
writer.newLine(); // 写入换行符
}
System.out.println("文件处理完成");
} catch (IOException e) {
e.printStackTrace();
}
}
private static String processLine(String line) {
// 示例处理逻辑:转换为大写并添加前缀
return "PROCESSED: " + line.toUpperCase();
}
// 使用 mark() 和 reset() 方法
public static void markResetExample() {
String text = "Hello World! This is a test.";
try (BufferedReader reader = new BufferedReader(new StringReader(text))) {
// 标记当前位置
reader.mark(100);
// 读取前5个字符
char[] buffer = new char[5];
reader.read(buffer);
System.out.println("第一次读取: " + new String(buffer));
// 重置到标记位置
reader.reset();
// 再次读取
reader.read(buffer);
System.out.println("重置后读取: " + new String(buffer));
} catch (IOException e) {
e.printStackTrace();
}
}
}
4. 缓冲流的实用方法
import java.io.*;
import java.util.ArrayList;
import java.util.List;
public class BufferedStreamUtilities {
// 读取文件所有行到列表
public static List<String> readAllLines(String filename) {
List<String> lines = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(new FileReader(filename))) {
String line;
while ((line = reader.readLine()) != null) {
lines.add(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return lines;
}
// 批量写入行到文件
public static void writeLines(List<String> lines, String filename) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(filename))) {
for (String line : lines) {
writer.write(line);
writer.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
}
// 使用 PrintWriter 进行格式化输出
public static void printWriterExample() {
try (PrintWriter pw = new PrintWriter(new BufferedWriter(new FileWriter("output.txt")))) {
pw.printf("姓名: %s, 年龄: %d%n", "张三", 25);
pw.println("这是一行文本");
pw.format("当前时间: %tF %tT%n", System.currentTimeMillis(), System.currentTimeMillis());
} catch (IOException e) {
e.printStackTrace();
}
}
}
5. 综合示例:文件加密工具
import java.io.*;
public class FileEncryptionTool {
// 使用缓冲流进行文件加密/解密
public static void encryptFile(String sourceFile, String targetFile, int key) {
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourceFile));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetFile))) {
int data;
while ((data = bis.read()) != -1) {
// 简单的异或加密
bos.write(data ^ key);
}
System.out.println("文件加密完成");
} catch (IOException e) {
e.printStackTrace();
}
}
// 解密文件(加密的逆过程)
public static void decryptFile(String encryptedFile, String decryptedFile, int key) {
encryptFile(encryptedFile, decryptedFile, key); // 异或加密的解密是同样的操作
System.out.println("文件解密完成");
}
// 带进度显示的文件复制
public static void copyWithProgress(String source, String target) {
File sourceFile = new File(source);
long totalBytes = sourceFile.length();
long copiedBytes = 0;
try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(source));
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(target))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
copiedBytes += bytesRead;
// 显示进度
int progress = (int) ((copiedBytes * 100) / totalBytes);
System.out.printf("\r复制进度: %d%% [%d/%d bytes]",
progress, copiedBytes, totalBytes);
}
System.out.println("\n文件复制完成");
} catch (IOException e) {
e.printStackTrace();
}
}
}
三、最佳实践和注意事项
1. 资源管理
// 推荐使用 try-with-resources
try (BufferedReader reader = new BufferedReader(new FileReader("file.txt"))) {
// 使用资源
} catch (IOException e) {
// 异常处理
}
// 不推荐的方式(需要手动关闭)
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("file.txt"));
// 使用资源
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 缓冲区大小选择
public class BufferSizeRecommendation {
// 常见的缓冲区大小
private static final int[] BUFFER_SIZES = {
1024, // 1KB - 小文件
8192, // 8KB - 通用
32768, // 32KB - 大文件
65536 // 64KB - 超大文件
};
public static int getOptimalBufferSize(long fileSize) {
if (fileSize < 1024 * 1024) { // < 1MB
return BUFFER_SIZES[0];
} else if (fileSize < 10 * 1024 * 1024) { // < 10MB
return BUFFER_SIZES[1];
} else if (fileSize < 100 * 1024 * 1024) { // < 100MB
return BUFFER_SIZES[2];
} else {
return BUFFER_SIZES[3];
}
}
}
总结
- 文件流:提供了基本的文件读写能力,分为字节流和字符流
- 缓冲流:通过内存缓冲区提高 IO 性能,减少物理读写次数
- 性能优化:合理选择缓冲区大小,使用 try-with-resources 管理资源
- 适用场景:
- 文本文件处理:使用字符缓冲流
- 二进制文件:使用字节缓冲流
- 大文件操作:必须使用缓冲流
- 逐行处理:使用 BufferedReader 的 readLine() 方法
掌握这些概念和方法能够帮助你编写更高效、更健壮的 Java IO 程序。