一、IO流概述
1.1 什么是IO流
public class IOOverview {
public static void main(String[] args) {
// IO流:Input/Output Stream
// 用于处理设备之间的数据传输
System.out.println("=== Java IO流体系结构 ===");
System.out.println("按数据流向分:");
System.out.println(" 1. 输入流(Input Stream):读取数据");
System.out.println(" 2. 输出流(Output Stream):写入数据");
System.out.println("\n按数据类型分:");
System.out.println(" 1. 字节流(Byte Stream):处理所有类型数据");
System.out.println(" InputStream / OutputStream");
System.out.println(" 2. 字符流(Character Stream):处理文本数据");
System.out.println(" Reader / Writer");
System.out.println("\n按功能分:");
System.out.println(" 1. 节点流:直接从数据源/目的地读写");
System.out.println(" 2. 处理流(包装流):对节点流进行包装,提供增强功能");
}
}
二、File类 - 文件和目录操作
2.1 File类基本操作
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
public class FileOperations {
public static void main(String[] args) {
System.out.println("=== File类操作 ===");
// 1. 创建File对象(不代表实际创建文件)
File file1 = new File("test.txt");
File file2 = new File("C:/test", "data.txt"); // 父路径 + 子路径
File file3 = new File(new File("C:/test"), "info.txt");
// 2. 文件和目录的创建
try {
// 创建文件
if (file1.createNewFile()) {
System.out.println("文件创建成功: " + file1.getName());
}
// 创建单级目录
File dir1 = new File("mydir");
if (dir1.mkdir()) {
System.out.println("目录创建成功: " + dir1.getName());
}
// 创建多级目录
File dir2 = new File("a/b/c/d");
if (dir2.mkdirs()) {
System.out.println("多级目录创建成功: " + dir2.getPath());
}
} catch (IOException e) {
e.printStackTrace();
}
// 3. 文件属性获取
System.out.println("\n=== 文件属性 ===");
System.out.println("文件名: " + file1.getName());
System.out.println("绝对路径: " + file1.getAbsolutePath());
System.out.println("规范路径: " + file1.getCanonicalPath());
System.out.println("父目录: " + file1.getParent());
System.out.println("文件大小: " + file1.length() + " bytes");
System.out.println("是否可读: " + file1.canRead());
System.out.println("是否可写: " + file1.canWrite());
System.out.println("是否可执行: " + file1.canExecute());
System.out.println("是否是文件: " + file1.isFile());
System.out.println("是否是目录: " + file1.isDirectory());
System.out.println("是否是隐藏文件: " + file1.isHidden());
long lastModified = file1.lastModified();
Date date = new Date(lastModified);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println("最后修改时间: " + sdf.format(date));
// 4. 文件操作
System.out.println("\n=== 文件操作 ===");
// 重命名
File newFile = new File("test_renamed.txt");
if (file1.renameTo(newFile)) {
System.out.println("文件重命名成功");
}
// 删除文件
if (newFile.delete()) {
System.out.println("文件删除成功");
}
// 5. 目录操作
System.out.println("\n=== 目录操作 ===");
File currentDir = new File(".");
// 列出目录内容
System.out.println("当前目录内容:");
String[] list = currentDir.list();
if (list != null) {
for (String item : list) {
System.out.println(" " + item);
}
}
// 列出文件和目录(带过滤)
System.out.println("\n所有.txt文件:");
File[] txtFiles = currentDir.listFiles((dir, name) -> name.endsWith(".txt"));
if (txtFiles != null) {
for (File f : txtFiles) {
System.out.println(" " + f.getName());
}
}
// 6. 临时文件
try {
File tempFile = File.createTempFile("temp_", ".tmp");
System.out.println("\n临时文件创建: " + tempFile.getAbsolutePath());
// 程序退出时删除临时文件
tempFile.deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.2 递归遍历目录
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class DirectoryTraversal {
/**
* 递归遍历目录,获取所有文件
*/
public static List<File> getAllFiles(File dir) {
List<File> fileList = new ArrayList<>();
traverseDirectory(dir, fileList);
return fileList;
}
private static void traverseDirectory(File dir, List<File> fileList) {
if (dir == null || !dir.exists() || !dir.isDirectory()) {
return;
}
File[] files = dir.listFiles();
if (files == null) {
return;
}
for (File file : files) {
if (file.isFile()) {
fileList.add(file);
} else if (file.isDirectory()) {
traverseDirectory(file, fileList);
}
}
}
/**
* 按文件类型统计
*/
public static void countFilesByType(File dir) {
int totalFiles = 0;
int javaFiles = 0;
int txtFiles = 0;
int otherFiles = 0;
List<File> allFiles = getAllFiles(dir);
for (File file : allFiles) {
totalFiles++;
String name = file.getName().toLowerCase();
if (name.endsWith(".java")) {
javaFiles++;
} else if (name.endsWith(".txt")) {
txtFiles++;
} else {
otherFiles++;
}
}
System.out.println("文件统计:");
System.out.println("总文件数: " + totalFiles);
System.out.println("Java文件: " + javaFiles);
System.out.println("文本文件: " + txtFiles);
System.out.println("其他文件: " + otherFiles);
}
/**
* 查找特定文件
*/
public static List<File> searchFiles(File dir, String keyword) {
List<File> result = new ArrayList<>();
List<File> allFiles = getAllFiles(dir);
for (File file : allFiles) {
if (file.getName().toLowerCase().contains(keyword.toLowerCase())) {
result.add(file);
}
}
return result;
}
/**
* 计算目录大小
*/
public static long calculateDirectorySize(File dir) {
long totalSize = 0;
List<File> allFiles = getAllFiles(dir);
for (File file : allFiles) {
totalSize += file.length();
}
return totalSize;
}
/**
* 格式化文件大小
*/
public static String formatFileSize(long size) {
if (size < 1024) {
return size + " B";
} else if (size < 1024 * 1024) {
return String.format("%.2f KB", size / 1024.0);
} else if (size < 1024 * 1024 * 1024) {
return String.format("%.2f MB", size / (1024.0 * 1024.0));
} else {
return String.format("%.2f GB", size / (1024.0 * 1024.0 * 1024.0));
}
}
public static void main(String[] args) {
System.out.println("=== 目录遍历和操作 ===");
File currentDir = new File(".");
// 获取所有文件
List<File> allFiles = getAllFiles(currentDir);
System.out.println("所有文件 (" + allFiles.size() + "个):");
for (int i = 0; i < Math.min(5, allFiles.size()); i++) {
System.out.println(" " + allFiles.get(i).getPath());
}
if (allFiles.size() > 5) {
System.out.println(" ... 还有 " + (allFiles.size() - 5) + " 个文件");
}
// 文件统计
countFilesByType(currentDir);
// 查找文件
System.out.println("\n查找包含'Test'的文件:");
List<File> searchResults = searchFiles(currentDir, "Test");
for (File file : searchResults) {
System.out.println(" " + file.getName());
}
// 计算目录大小
long dirSize = calculateDirectorySize(currentDir);
System.out.println("\n目录总大小: " + formatFileSize(dirSize));
// 文件树形展示
System.out.println("\n目录树形结构:");
printDirectoryTree(currentDir, 0);
}
private static void printDirectoryTree(File dir, int level) {
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 打印当前目录
StringBuilder prefix = new StringBuilder();
for (int i = 0; i < level; i++) {
prefix.append(" ");
}
System.out.println(prefix + "├─ " + dir.getName() + "/");
File[] files = dir.listFiles();
if (files == null) {
return;
}
for (int i = 0; i < files.length; i++) {
File file = files[i];
String indent = prefix.toString();
if (i == files.length - 1) {
indent += " └─ ";
} else {
indent += " ├─ ";
}
if (file.isDirectory()) {
System.out.println(indent + file.getName() + "/");
printDirectoryTree(file, level + 2);
} else {
System.out.println(indent + file.getName() +
" (" + formatFileSize(file.length()) + ")");
}
}
}
}
三、字节流(Byte Streams)
3.1 FileInputStream 和 FileOutputStream
import java.io.*;
public class ByteStreamDemo {
/**
* 使用FileInputStream读取文件(逐个字节)
*/
public static void readFileByteByByte(String filePath) {
System.out.println("\n=== 逐个字节读取文件 ===");
try (FileInputStream fis = new FileInputStream(filePath)) {
int byteData;
int count = 0;
while ((byteData = fis.read()) != -1) {
System.out.print((char) byteData);
count++;
if (count % 100 == 0) { // 每100个字符换行显示
System.out.println();
}
}
System.out.println("\n总字节数: " + count);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用缓冲区读取文件(提高效率)
*/
public static void readFileWithBuffer(String filePath) {
System.out.println("\n=== 使用缓冲区读取文件 ===");
try (FileInputStream fis = new FileInputStream(filePath)) {
byte[] buffer = new byte[1024]; // 1KB缓冲区
int bytesRead;
int totalBytes = 0;
while ((bytesRead = fis.read(buffer)) != -1) {
// 处理读取的数据
String content = new String(buffer, 0, bytesRead);
System.out.print(content);
totalBytes += bytesRead;
}
System.out.println("\n总字节数: " + totalBytes);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用FileOutputStream写入文件
*/
public static void writeFile(String filePath, String content) {
System.out.println("\n=== 写入文件 ===");
try (FileOutputStream fos = new FileOutputStream(filePath)) {
byte[] bytes = content.getBytes();
fos.write(bytes);
System.out.println("写入完成,字节数: " + bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 追加内容到文件
*/
public static void appendToFile(String filePath, String content) {
System.out.println("\n=== 追加内容到文件 ===");
try (FileOutputStream fos = new FileOutputStream(filePath, true)) { // true表示追加模式
byte[] bytes = content.getBytes();
fos.write(bytes);
System.out.println("追加完成,追加字节数: " + bytes.length);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件复制(字节流)
*/
public static void copyFile(String sourcePath, String targetPath) {
System.out.println("\n=== 文件复制 ===");
long startTime = System.currentTimeMillis();
try (FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath)) {
byte[] buffer = new byte[8192]; // 8KB缓冲区
int bytesRead;
long totalBytes = 0;
while ((bytesRead = fis.read(buffer)) != -1) {
fos.write(buffer, 0, bytesRead);
totalBytes += bytesRead;
}
long endTime = System.currentTimeMillis();
System.out.println("文件复制完成");
System.out.println("总字节数: " + totalBytes);
System.out.println("耗时: " + (endTime - startTime) + "ms");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读取文件前N个字节
*/
public static byte[] readFirstNBytes(String filePath, int n) {
try (FileInputStream fis = new FileInputStream(filePath)) {
byte[] result = new byte[Math.min(n, fis.available())];
int bytesRead = fis.read(result);
return result;
} catch (IOException e) {
e.printStackTrace();
return new byte[0];
}
}
/**
* 文件加密(简单的XOR加密)
*/
public static void encryptFile(String sourcePath, String targetPath, byte key) {
System.out.println("\n=== 文件加密 ===");
try (FileInputStream fis = new FileInputStream(sourcePath);
FileOutputStream fos = new FileOutputStream(targetPath)) {
int byteData;
while ((byteData = fis.read()) != -1) {
fos.write(byteData ^ key); // XOR加密
}
System.out.println("文件加密完成");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件解密
*/
public static void decryptFile(String sourcePath, String targetPath, byte key) {
System.out.println("\n=== 文件解密 ===");
// 解密和加密使用相同的操作
encryptFile(sourcePath, targetPath, key);
System.out.println("文件解密完成");
}
public static void main(String[] args) {
String testFile = "test.txt";
String copyFile = "test_copy.txt";
String encryptedFile = "test_encrypted.dat";
String decryptedFile = "test_decrypted.txt";
// 创建测试文件
String content = "Hello, Java IO Stream!\n" +
"This is a test file for demonstrating IO operations.\n" +
"字节流可以处理所有类型的数据。\n" +
"Binary data: \u0001\u0002\u0003\u0004\u0005";
writeFile(testFile, content);
// 追加内容
appendToFile(testFile, "\n\nThis is appended content.");
// 读取文件
readFileByteByByte(testFile);
readFileWithBuffer(testFile);
// 复制文件
copyFile(testFile, copyFile);
// 读取文件前N个字节
byte[] firstBytes = readFirstNBytes(testFile, 20);
System.out.println("\n前20个字节: " + new String(firstBytes));
// 文件加密解密
byte encryptionKey = 0x55; // 加密密钥
encryptFile(testFile, encryptedFile, encryptionKey);
decryptFile(encryptedFile, decryptedFile, encryptionKey);
// 清理测试文件
new File(testFile).delete();
new File(copyFile).delete();
new File(encryptedFile).delete();
new File(decryptedFile).delete();
}
}
3.2 缓冲字节流(Buffered)
import java.io.*;
public class BufferedStreamDemo {
/**
* 使用BufferedInputStream提高读取性能
*/
public static void readWithBufferedStream(String filePath) {
System.out.println("\n=== 使用BufferedInputStream读取 ===");
long startTime = System.currentTimeMillis();
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(filePath))) {
byte[] buffer = new byte[1024];
int bytesRead;
long totalBytes = 0;
while ((bytesRead = bis.read(buffer)) != -1) {
totalBytes += bytesRead;
}
long endTime = System.currentTimeMillis();
System.out.println("读取完成,总字节数: " + totalBytes);
System.out.println("耗时: " + (endTime - startTime) + "ms");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用BufferedOutputStream提高写入性能
*/
public static void writeWithBufferedStream(String filePath, String content) {
System.out.println("\n=== 使用BufferedOutputStream写入 ===");
long startTime = System.currentTimeMillis();
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(filePath))) {
byte[] bytes = content.getBytes();
bos.write(bytes);
bos.flush(); // 确保数据写入磁盘
long endTime = System.currentTimeMillis();
System.out.println("写入完成,字节数: " + bytes.length);
System.out.println("耗时: " + (endTime - startTime) + "ms");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 性能对比测试
*/
public static void performanceTest(String filePath) {
System.out.println("\n=== 性能对比测试 ===");
// 生成测试数据
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100000; i++) {
sb.append("Line ").append(i).append(": Test data for performance comparison\n");
}
String testData = sb.toString();
// 测试1:普通FileOutputStream
long start1 = System.currentTimeMillis();
try (FileOutputStream fos = new FileOutputStream(filePath + ".test1")) {
fos.write(testData.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
long end1 = System.currentTimeMillis();
// 测试2:BufferedOutputStream
long start2 = System.currentTimeMillis();
try (BufferedOutputStream bos = new BufferedOutputStream(
new FileOutputStream(filePath + ".test2"))) {
bos.write(testData.getBytes());
} catch (IOException e) {
e.printStackTrace();
}
long end2 = System.currentTimeMillis();
System.out.println("普通FileOutputStream耗时: " + (end1 - start1) + "ms");
System.out.println("BufferedOutputStream耗时: " + (end2 - start2) + "ms");
// 清理测试文件
new File(filePath + ".test1").delete();
new File(filePath + ".test2").delete();
}
/**
* 使用mark()和reset()方法
*/
public static void markAndResetDemo(String filePath) {
System.out.println("\n=== mark()和reset()演示 ===");
try (BufferedInputStream bis = new BufferedInputStream(
new FileInputStream(filePath))) {
// 标记当前位置
bis.mark(100); // 参数指定标记失效前可读取的最大字节数
// 读取10个字节
byte[] buffer1 = new byte[10];
bis.read(buffer1);
System.out.println("前10个字节: " + new String(buffer1));
// 重置到标记位置
bis.reset();
// 再次读取
byte[] buffer2 = new byte[10];
bis.read(buffer2);
System.out.println("重置后读取: " + new String(buffer2));
// 跳过部分字节
bis.skip(5);
// 查看剩余可用字节
System.out.println("剩余可用字节: " + bis.available());
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String testFile = "buffer_test.txt";
// 创建测试文件
String content = "This is a test file for buffered stream demonstration.\n" +
"Buffered streams can significantly improve IO performance.\n" +
"They use an internal buffer to reduce the number of actual IO operations.\n";
writeWithBufferedStream(testFile, content);
readWithBufferedStream(testFile);
// mark和reset演示
markAndResetDemo(testFile);
// 性能测试
performanceTest("performance_test");
// 清理测试文件
new File(testFile).delete();
}
}
四、字符流(Character Streams)
4.1 FileReader 和 FileWriter
import java.io.*;
public class CharacterStreamDemo {
/**
* 使用FileReader读取文本文件
*/
public static void readWithFileReader(String filePath) {
System.out.println("\n=== 使用FileReader读取 ===");
try (FileReader reader = new FileReader(filePath)) {
char[] buffer = new char[1024];
int charsRead;
StringBuilder content = new StringBuilder();
while ((charsRead = reader.read(buffer)) != -1) {
content.append(buffer, 0, charsRead);
}
System.out.println("文件内容:");
System.out.println(content);
System.out.println("字符数: " + content.length());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用FileWriter写入文本文件
*/
public static void writeWithFileWriter(String filePath, String content) {
System.out.println("\n=== 使用FileWriter写入 ===");
try (FileWriter writer = new FileWriter(filePath)) {
writer.write(content);
writer.flush();
System.out.println("写入完成,字符数: " + content.length());
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 按行读取文件(自定义实现)
*/
public static void readLineByLine(String filePath) {
System.out.println("\n=== 按行读取文件 ===");
try (FileReader fr = new FileReader(filePath)) {
StringBuilder line = new StringBuilder();
int charCode;
int lineNumber = 1;
while ((charCode = fr.read()) != -1) {
char ch = (char) charCode;
if (ch == '\n') {
System.out.println(lineNumber++ + ": " + line);
line.setLength(0); // 清空StringBuilder
} else if (ch != '\r') {
line.append(ch);
}
}
// 处理最后一行(如果没有换行符结尾)
if (line.length() > 0) {
System.out.println(lineNumber + ": " + line);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 处理字符编码
*/
public static void handleCharacterEncoding(String filePath) {
System.out.println("\n=== 字符编码处理 ===");
// 不同编码的文本
String text = "Hello, 世界! 这是UTF-8编码的文本。";
// 以UTF-8编码写入
try (OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream(filePath + ".utf8"), "UTF-8")) {
writer.write(text);
System.out.println("UTF-8编码文件已写入");
} catch (IOException e) {
e.printStackTrace();
}
// 以GBK编码写入
try (OutputStreamWriter writer = new OutputStreamWriter(
new FileOutputStream(filePath + ".gbk"), "GBK")) {
writer.write(text);
System.out.println("GBK编码文件已写入");
} catch (IOException e) {
e.printStackTrace();
}
// 以不同编码读取
System.out.println("\n以不同编码读取文件:");
try (InputStreamReader reader = new InputStreamReader(
new FileInputStream(filePath + ".utf8"), "UTF-8")) {
char[] buffer = new char[1024];
int charsRead = reader.read(buffer);
System.out.println("UTF-8读取: " + new String(buffer, 0, charsRead));
} catch (IOException e) {
e.printStackTrace();
}
try (InputStreamReader reader = new InputStreamReader(
new FileInputStream(filePath + ".gbk"), "GBK")) {
char[] buffer = new char[1024];
int charsRead = reader.read(buffer);
System.out.println("GBK读取: " + new String(buffer, 0, charsRead));
} catch (IOException e) {
e.printStackTrace();
}
// 清理文件
new File(filePath + ".utf8").delete();
new File(filePath + ".gbk").delete();
}
/**
* 字符流复制文本文件(保持格式)
*/
public static void copyTextFile(String sourcePath, String targetPath) {
System.out.println("\n=== 字符流复制文本文件 ===");
try (FileReader reader = new FileReader(sourcePath);
FileWriter writer = new FileWriter(targetPath)) {
char[] buffer = new char[1024];
int charsRead;
long totalChars = 0;
while ((charsRead = reader.read(buffer)) != -1) {
writer.write(buffer, 0, charsRead);
totalChars += charsRead;
}
System.out.println("文件复制完成,总字符数: " + totalChars);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 统计文本文件信息
*/
public static void analyzeTextFile(String filePath) {
System.out.println("\n=== 文本文件分析 ===");
try (FileReader reader = new FileReader(filePath)) {
int charCode;
int totalChars = 0;
int letters = 0;
int digits = 0;
int spaces = 0;
int lines = 1; // 至少一行
int chineseChars = 0;
while ((charCode = reader.read()) != -1) {
char ch = (char) charCode;
totalChars++;
if (Character.isLetter(ch)) {
letters++;
// 判断是否为中文字符
if (ch >= '\u4e00' && ch <= '\u9fa5') {
chineseChars++;
}
} else if (Character.isDigit(ch)) {
digits++;
} else if (Character.isWhitespace(ch)) {
spaces++;
if (ch == '\n') {
lines++;
}
}
}
System.out.println("总字符数: " + totalChars);
System.out.println("字母数: " + letters);
System.out.println("数字数: " + digits);
System.out.println("空格数: " + spaces);
System.out.println("行数: " + lines);
System.out.println("中文字符数: " + chineseChars);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String testFile = "char_test.txt";
String copyFile = "char_test_copy.txt";
// 创建测试文件
String content = "Hello, World!\n" +
"这是一个测试文件。\n" +
"用于演示字符流的操作。\n" +
"Line 4: 12345 67890\n" +
"最后一行。";
writeWithFileWriter(testFile, content);
// 读取文件
readWithFileReader(testFile);
// 按行读取
readLineByLine(testFile);
// 字符编码处理
handleCharacterEncoding("encoding_test");
// 复制文件
copyTextFile(testFile, copyFile);
// 分析文件
analyzeTextFile(testFile);
// 清理测试文件
new File(testFile).delete();
new File(copyFile).delete();
}
}
4.2 缓冲字符流(BufferedReader/BufferedWriter)
import java.io.*;
public class BufferedCharacterStreamDemo {
/**
* 使用BufferedReader读取文件
*/
public static void readWithBufferedReader(String filePath) {
System.out.println("\n=== 使用BufferedReader读取 ===");
try (BufferedReader reader = new BufferedReader(
new FileReader(filePath))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
System.out.println(lineNumber++ + ": " + line);
}
System.out.println("总行数: " + (lineNumber - 1));
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用BufferedWriter写入文件
*/
public static void writeWithBufferedWriter(String filePath, String[] lines) {
System.out.println("\n=== 使用BufferedWriter写入 ===");
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(filePath))) {
for (String line : lines) {
writer.write(line);
writer.newLine(); // 换行,跨平台
}
writer.flush();
System.out.println("写入完成,行数: " + lines.length);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 查找包含特定字符串的行
*/
public static void findLinesContaining(String filePath, String keyword) {
System.out.println("\n=== 查找包含 '" + keyword + "' 的行 ===");
try (BufferedReader reader = new BufferedReader(
new FileReader(filePath))) {
String line;
int lineNumber = 1;
int matchCount = 0;
while ((line = reader.readLine()) != null) {
if (line.contains(keyword)) {
System.out.println("第" + lineNumber + "行: " + line);
matchCount++;
}
lineNumber++;
}
System.out.println("找到 " + matchCount + " 行匹配内容");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 复制文件并添加行号
*/
public static void copyFileWithLineNumbers(String sourcePath, String targetPath) {
System.out.println("\n=== 复制文件并添加行号 ===");
try (BufferedReader reader = new BufferedReader(
new FileReader(sourcePath));
BufferedWriter writer = new BufferedWriter(
new FileWriter(targetPath))) {
String line;
int lineNumber = 1;
while ((line = reader.readLine()) != null) {
writer.write(String.format("%04d: %s", lineNumber, line));
writer.newLine();
lineNumber++;
}
System.out.println("复制完成,共 " + (lineNumber - 1) + " 行");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读取大文件的分页功能
*/
public static void readFileByPage(String filePath, int pageSize) {
System.out.println("\n=== 分页读取文件(每页" + pageSize + "行)===");
try (BufferedReader reader = new BufferedReader(
new FileReader(filePath))) {
String line;
int currentLine = 0;
int pageNumber = 1;
boolean continueReading = true;
while (continueReading) {
System.out.println("\n--- 第 " + pageNumber + " 页 ---");
int linesInPage = 0;
while (linesInPage < pageSize && (line = reader.readLine()) != null) {
System.out.println(line);
linesInPage++;
currentLine++;
}
if (line == null) {
System.out.println("\n已到文件末尾,总行数: " + currentLine);
break;
}
// 模拟用户输入
System.out.print("\n按Enter键继续下一页,输入q退出: ");
BufferedReader consoleReader = new BufferedReader(
new InputStreamReader(System.in));
String input = consoleReader.readLine();
if ("q".equalsIgnoreCase(input.trim())) {
continueReading = false;
}
pageNumber++;
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 合并多个文本文件
*/
public static void mergeTextFiles(String[] sourceFiles, String targetFile) {
System.out.println("\n=== 合并多个文本文件 ===");
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(targetFile))) {
for (String sourceFile : sourceFiles) {
System.out.println("正在合并: " + sourceFile);
try (BufferedReader reader = new BufferedReader(
new FileReader(sourceFile))) {
writer.write("=== " + sourceFile + " ===");
writer.newLine();
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
writer.newLine(); // 文件间空行
}
}
System.out.println("文件合并完成: " + targetFile);
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
String testFile = "buffered_char_test.txt";
String numberedFile = "buffered_char_test_numbered.txt";
String[] sourceFiles = {"file1.txt", "file2.txt", "file3.txt"};
String mergedFile = "merged.txt";
// 创建测试文件
String[] lines = {
"第一行: Java IO 流操作",
"第二行: BufferedReader 和 BufferedWriter",
"第三行: 提供缓冲功能,提高效率",
"第四行: 支持 readLine() 方法",
"第五行: 可以按行读取文本文件",
"第六行: 这是最后一行"
};
writeWithBufferedWriter(testFile, lines);
// 读取文件
readWithBufferedReader(testFile);
// 查找包含特定字符串的行
findLinesContaining(testFile, "Buffered");
// 复制文件并添加行号
copyFileWithLineNumbers(testFile, numberedFile);
// 创建用于合并的文件
for (int i = 0; i < sourceFiles.length; i++) {
String[] fileLines = {"文件 " + (i + 1) + " 的内容", "这是第 " + (i + 1) + " 个文件"};
writeWithBufferedWriter(sourceFiles[i], fileLines);
}
// 合并文件
mergeTextFiles(sourceFiles, mergedFile);
// 分页读取演示
System.out.println("\n=== 分页读取演示 ===");
readFileByPage(testFile, 3);
// 清理测试文件
new File(testFile).delete();
new File(numberedFile).delete();
new File(mergedFile).delete();
for (String sourceFile : sourceFiles) {
new File(sourceFile).delete();
}
}
}
五、高级IO操作
5.1 数据流(DataInputStream/DataOutputStream)
import java.io.*;
public class DataStreamDemo {
/**
* 使用DataOutputStream写入各种数据类型
*/
public static void writeWithDataStream(String filePath) {
System.out.println("\n=== 使用DataOutputStream写入 ===");
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream(filePath))) {
// 写入各种数据类型
dos.writeBoolean(true);
dos.writeByte(65); // 'A'
dos.writeChar('中');
dos.writeShort(1000);
dos.writeInt(123456);
dos.writeLong(9876543210L);
dos.writeFloat(3.14159f);
dos.writeDouble(2.71828);
dos.writeUTF("Hello, 世界!"); // UTF-8编码字符串
System.out.println("数据写入完成");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 使用DataInputStream读取各种数据类型
*/
public static void readWithDataStream(String filePath) {
System.out.println("\n=== 使用DataInputStream读取 ===");
try (DataInputStream dis = new DataInputStream(
new FileInputStream(filePath))) {
// 读取顺序必须与写入顺序一致
boolean boolValue = dis.readBoolean();
byte byteValue = dis.readByte();
char charValue = dis.readChar();
short shortValue = dis.readShort();
int intValue = dis.readInt();
long longValue = dis.readLong();
float floatValue = dis.readFloat();
double doubleValue = dis.readDouble();
String stringValue = dis.readUTF();
System.out.println("读取的数据:");
System.out.println("boolean: " + boolValue);
System.out.println("byte: " + byteValue + " (char: " + (char)byteValue + ")");
System.out.println("char: " + charValue);
System.out.println("short: " + shortValue);
System.out.println("int: " + intValue);
System.out.println("long: " + longValue);
System.out.println("float: " + floatValue);
System.out.println("double: " + doubleValue);
System.out.println("String: " + stringValue);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 读写学生信息
*/
public static void writeStudentInfo(String filePath) {
System.out.println("\n=== 写入学生信息 ===");
try (DataOutputStream dos = new DataOutputStream(
new FileOutputStream(filePath))) {
// 写入学生数量
dos.writeInt(3);
// 学生1
dos.writeUTF("张三");
dos.writeInt(20);
dos.writeDouble(89.5);
dos.writeBoolean(true); // 是否及格
// 学生2
dos.writeUTF("李四");
dos.writeInt(22);
dos.writeDouble(76.0);
dos.writeBoolean(true);
// 学生3
dos.writeUTF("王五");
dos.writeInt(19);
dos.writeDouble(59.5);
dos.writeBoolean(false);
System.out.println("学生信息写入完成");
} catch (IOException e) {
e.printStackTrace();
}
}
public static void readStudentInfo(String filePath) {
System.out.println("\n=== 读取学生信息 ===");
try (DataInputStream dis = new DataInputStream(
new FileInputStream(filePath))) {
int studentCount = dis.readInt();
System.out.println("学生人数: " + studentCount);
System.out.println("\n学生信息:");
System.out.println("姓名\t年龄\t成绩\t是否及格");
System.out.println("---------------------------");
for (int i = 0; i < studentCount; i++) {
String name = dis.readUTF();
int age = dis.readInt();
double score = dis.readDouble();
boolean passed = dis.readBoolean();
System.out.printf("%s\t%d\t%.1f\t%s\n",
name, age, score, passed ? "是" : "否");
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 处理大端序和小端序
*/
public static void endianDemo() {
System.out.println("\n=== 字节序演示 ===");
int value = 0x12345678;
System.out.println("原始值: 0x" + Integer.toHexString(value));
// Java默认使用大端序(Big-Endian)
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream dos = new DataOutputStream(baos)) {
dos.writeInt(value);
} catch (IOException e) {
e.printStackTrace();
}
byte[] bytes = baos.toByteArray();
System.out.print("大端序字节: ");
for (byte b : bytes) {
System.out.printf("%02X ", b);
}
System.out.println();
// 手动转换为小端序
byte[] littleEndian = new byte[4];
for (int i = 0; i < 4; i++) {
littleEndian[i] = bytes[3 - i];
}
System.out.print("小端序字节: ");
for (byte b : littleEndian) {
System.out.printf("%02X ", b);
}
System.out.println();
}
public static void main(String[] args) {
String dataFile = "data_stream_test.dat";
String studentFile = "student_info.dat";
// DataStream读写演示
writeWithDataStream(dataFile);
readWithDataStream(dataFile);
// 学生信息读写
writeStudentInfo(studentFile);
readStudentInfo(studentFile);
// 字节序演示
endianDemo();
// 清理测试文件
new File(dataFile).delete();
new File(studentFile).delete();
}
}
5.2 对象序列化(Object Streams)
import java.io.*;
import java.util.Date;
public class ObjectStreamDemo {
// 可序列化的学生类
static class Student implements Serializable {
// 序列化版本ID,确保序列化兼容性
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient double score; // transient关键字表示不序列化
private Date birthDate;
public Student(String name, int age, double score, Date birthDate) {
this.name = name;
this.age = age;
this.score = score;
this.birthDate = birthDate;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", score=" + score +
", birthDate=" + birthDate +
'}';
}
}
/**
* 序列化对象到文件
*/
public static void serializeObject(String filePath, Object obj) {
System.out.println("\n=== 序列化对象 ===");
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filePath))) {
oos.writeObject(obj);
System.out.println("对象序列化完成: " + obj);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从文件反序列化对象
*/
public static Object deserializeObject(String filePath) {
System.out.println("\n=== 反序列化对象 ===");
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filePath))) {
Object obj = ois.readObject();
System.out.println("对象反序列化完成: " + obj);
return obj;
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
return null;
}
}
/**
* 序列化多个对象
*/
public static void serializeMultipleObjects(String filePath) {
System.out.println("\n=== 序列化多个对象 ===");
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filePath))) {
// 写入多个对象
oos.writeObject(new Student("张三", 20, 89.5, new Date()));
oos.writeObject(new Student("李四", 22, 76.0, new Date()));
oos.writeObject(new Student("王五", 19, 59.5, new Date()));
System.out.println("多个对象序列化完成");
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 反序列化多个对象
*/
public static void deserializeMultipleObjects(String filePath) {
System.out.println("\n=== 反序列化多个对象 ===");
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filePath))) {
System.out.println("读取的对象:");
try {
while (true) {
Object obj = ois.readObject();
System.out.println(" " + obj);
}
} catch (EOFException e) {
// 到达文件末尾
System.out.println("所有对象读取完成");
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
/**
* 自定义序列化
*/
static class CustomSerializable implements Serializable {
private static final long serialVersionUID = 1L;
private String data;
private transient String secret; // 不自动序列化
public CustomSerializable(String data, String secret) {
this.data = data;
this.secret = secret;
}
// 自定义序列化逻辑
private void writeObject(ObjectOutputStream oos) throws IOException {
oos.defaultWriteObject(); // 默认序列化
// 对秘密数据加密后再序列化
String encrypted = encrypt(secret);
oos.writeObject(encrypted);
}
// 自定义反序列化逻辑
private void readObject(ObjectInputStream ois)
throws IOException, ClassNotFoundException {
ois.defaultReadObject(); // 默认反序列化
// 解密秘密数据
String encrypted = (String) ois.readObject();
this.secret = decrypt(encrypted);
}
private String encrypt(String text) {
// 简单的加密:字符后移一位
char[] chars = text.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] = (char)(chars[i] + 1);
}
return new String(chars);
}
private String decrypt(String text) {
// 解密:字符前移一位
char[] chars = text.toCharArray();
for (int i = 0; i < chars.length; i++) {
chars[i] = (char)(chars[i] - 1);
}
return new String(chars);
}
@Override
public String toString() {
return "CustomSerializable{" +
"data='" + data + '\'' +
", secret='" + secret + '\'' +
'}';
}
}
/**
* 序列化演示
*/
public static void testCustomSerializable() {
System.out.println("\n=== 自定义序列化演示 ===");
String filePath = "custom_serializable.dat";
CustomSerializable obj = new CustomSerializable("公开数据", "秘密信息");
// 序列化
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(filePath))) {
oos.writeObject(obj);
System.out.println("对象序列化完成: " + obj);
} catch (IOException e) {
e.printStackTrace();
}
// 反序列化
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(filePath))) {
CustomSerializable restored = (CustomSerializable) ois.readObject();
System.out.println("对象反序列化完成: " + restored);
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
// 清理文件
new File(filePath).delete();
}
/**
* 序列化版本控制
*/
static class VersionedClass implements Serializable {
// 修改类时更新这个版本号
private static final long serialVersionUID = 2L; // 从1L改为2L
private String name;
private int age;
// 新版本添加的字段
private String email; // 这个字段在旧版本中不存在
public VersionedClass(String name, int age, String email) {
this.name = name;
this.age = age;
this.email = email;
}
@Override
public String toString() {
return "VersionedClass{" +
"name='" + name + '\'' +
", age=" + age +
", email='" + email + '\'' +
'}';
}
}
public static void main(String[] args) {
String studentFile = "student.ser";
String multiFile = "multi_objects.ser";
// 单个对象序列化/反序列化
Student student = new Student("张三", 20, 89.5, new Date());
serializeObject(studentFile, student);
Student restoredStudent = (Student) deserializeObject(studentFile);
// 注意:score字段被标记为transient,反序列化后为默认值0.0
System.out.println("原对象: " + student);
System.out.println("恢复的对象: " + restoredStudent);
// 多个对象序列化/反序列化
serializeMultipleObjects(multiFile);
deserializeMultipleObjects(multiFile);
// 自定义序列化演示
testCustomSerializable();
// 清理测试文件
new File(studentFile).delete();
new File(multiFile).delete();
}
}
六、NIO文件操作(Java 7+)
6.1 NIO.2 Files和Paths类
import java.io.IOException;
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.attribute.*;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
public class NIO2Demo {
/**
* 基本文件操作
*/
public static void basicFileOperations() {
System.out.println("=== NIO.2 基本文件操作 ===");
// 1. 创建Path对象
Path path1 = Paths.get("nio2_test.txt");
Path path2 = Paths.get(".", "subdir", "file.txt");
Path path3 = Paths.get("/Users", "username", "documents", "test.txt");
System.out.println("path1: " + path1);
System.out.println("path2: " + path2.toAbsolutePath());
System.out.println("path2规范化: " + path2.normalize());
// 2. 文件操作
try {
// 创建文件
if (!Files.exists(path1)) {
Files.createFile(path1);
System.out.println("文件创建成功");
}
// 写入文件
String content = "Hello, NIO.2!\n这是第二行。";
Files.write(path1, content.getBytes(StandardCharsets.UTF_8));
// 追加内容
String appendContent = "\n这是追加的内容。";
Files.write(path1, appendContent.getBytes(StandardCharsets.UTF_8),
StandardOpenOption.APPEND);
// 读取文件
byte[] bytes = Files.readAllBytes(path1);
System.out.println("\n文件内容:\n" + new String(bytes));
// 按行读取
List<String> lines = Files.readAllLines(path1, StandardCharsets.UTF_8);
System.out.println("\n按行读取:");
for (int i = 0; i < lines.size(); i++) {
System.out.println("第" + (i + 1) + "行: " + lines.get(i));
}
// 使用Stream API读取
System.out.println("\n使用Stream读取:");
try (Stream<String> stream = Files.lines(path1, StandardCharsets.UTF_8)) {
stream.forEach(System.out::println);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 目录操作
*/
public static void directoryOperations() {
System.out.println("\n=== NIO.2 目录操作 ===");
try {
// 创建目录
Path dir1 = Paths.get("nio2_dir");
if (!Files.exists(dir1)) {
Files.createDirectory(dir1);
System.out.println("目录创建成功: " + dir1);
}
// 创建多级目录
Path multiDir = Paths.get("a", "b", "c");
Files.createDirectories(multiDir);
System.out.println("多级目录创建成功: " + multiDir);
// 遍历目录
System.out.println("\n遍历目录内容:");
try (Stream<Path> stream = Files.list(Paths.get("."))) {
stream.forEach(p -> System.out.println(" " + p.getFileName()));
}
// 深度遍历
System.out.println("\n深度遍历目录:");
try (Stream<Path> stream = Files.walk(Paths.get("."), 2)) {
stream.forEach(p -> {
try {
String type = Files.isDirectory(p) ? "目录" : "文件";
long size = Files.size(p);
System.out.printf(" %s [%s, %d字节]\n",
p, type, size);
} catch (IOException e) {
e.printStackTrace();
}
});
}
// 查找文件
System.out.println("\n查找.java文件:");
try (Stream<Path> stream = Files.find(Paths.get("."),
2,
(path, attrs) ->
attrs.isRegularFile() &&
path.toString().endsWith(".java"))) {
stream.forEach(p -> System.out.println(" " + p));
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件复制和移动
*/
public static void fileCopyAndMove() {
System.out.println("\n=== 文件复制和移动 ===");
try {
Path source = Paths.get("nio2_test.txt");
Path copy = Paths.get("nio2_test_copy.txt");
Path move = Paths.get("nio2_test_moved.txt");
// 复制文件
Files.copy(source, copy, StandardCopyOption.REPLACE_EXISTING);
System.out.println("文件复制成功");
// 移动文件
Files.move(copy, move, StandardCopyOption.REPLACE_EXISTING);
System.out.println("文件移动成功");
// 比较文件内容
long mismatch = Files.mismatch(source, move);
if (mismatch == -1) {
System.out.println("两个文件内容相同");
} else {
System.out.println("文件内容不同,第一个不同处位置: " + mismatch);
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件属性操作
*/
public static void fileAttributes() {
System.out.println("\n=== 文件属性操作 ===");
try {
Path path = Paths.get("nio2_test.txt");
// 基本属性
System.out.println("基本文件信息:");
System.out.println("文件大小: " + Files.size(path) + "字节");
System.out.println("是否可读: " + Files.isReadable(path));
System.out.println("是否可写: " + Files.isWritable(path));
System.out.println("是否可执行: " + Files.isExecutable(path));
System.out.println("是否隐藏: " + Files.isHidden(path));
System.out.println("是否符号链接: " + Files.isSymbolicLink(path));
System.out.println("最后修改时间: " + Files.getLastModifiedTime(path));
// 文件所有者
FileOwnerAttributeView ownerView = Files.getFileAttributeView(
path, FileOwnerAttributeView.class);
if (ownerView != null) {
System.out.println("文件所有者: " + ownerView.getOwner().getName());
}
// 文件权限
Set<PosixFilePermission> permissions = null;
try {
permissions = Files.getPosixFilePermissions(path);
System.out.println("文件权限: " + permissions);
} catch (UnsupportedOperationException e) {
System.out.println("不支持POSIX文件权限");
}
// 设置文件属性
Files.setAttribute(path, "dos:hidden", true);
System.out.println("已设置隐藏属性");
// 获取文件类型
String contentType = Files.probeContentType(path);
System.out.println("文件类型: " + contentType);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 文件监控
*/
public static void fileWatchService() {
System.out.println("\n=== 文件监控 ===");
try {
WatchService watchService = FileSystems.getDefault().newWatchService();
Path dir = Paths.get(".");
// 注册要监控的事件
dir.register(watchService,
StandardWatchEventKinds.ENTRY_CREATE,
StandardWatchEventKinds.ENTRY_MODIFY,
StandardWatchEventKinds.ENTRY_DELETE);
System.out.println("开始监控目录变化(监控5秒)...");
long startTime = System.currentTimeMillis();
while (System.currentTimeMillis() - startTime < 5000) {
WatchKey key = watchService.poll(1000, java.util.concurrent.TimeUnit.MILLISECONDS);
if (key != null) {
for (WatchEvent<?> event : key.pollEvents()) {
WatchEvent.Kind<?> kind = event.kind();
Path fileName = (Path) event.context();
System.out.println("事件: " + kind.name() + ", 文件: " + fileName);
}
// 重置key,继续接收事件
boolean valid = key.reset();
if (!valid) {
System.out.println("监控key失效");
break;
}
}
}
watchService.close();
System.out.println("监控结束");
} catch (IOException | InterruptedException e) {
e.printStackTrace();
}
}
/**
* 临时文件和目录
*/
public static void tempFilesAndDirs() {
System.out.println("\n=== 临时文件和目录 ===");
try {
// 创建临时文件
Path tempFile = Files.createTempFile("nio2_temp_", ".txt");
System.out.println("临时文件: " + tempFile.toAbsolutePath());
// 写入临时文件
Files.write(tempFile, "临时文件内容".getBytes(StandardCharsets.UTF_8));
// 创建临时目录
Path tempDir = Files.createTempDirectory("nio2_temp_dir_");
System.out.println("临时目录: " + tempDir.toAbsolutePath());
// 在临时目录中创建文件
Path fileInTempDir = tempDir.resolve("test.txt");
Files.write(fileInTempDir, "临时目录中的文件".getBytes(StandardCharsets.UTF_8));
// 程序退出时删除临时文件
tempFile.toFile().deleteOnExit();
fileInTempDir.toFile().deleteOnExit();
tempDir.toFile().deleteOnExit();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
// 基本文件操作
basicFileOperations();
// 目录操作
directoryOperations();
// 文件复制和移动
fileCopyAndMove();
// 文件属性
fileAttributes();
// 文件监控
fileWatchService();
// 临时文件
tempFilesAndDirs();
// 清理测试文件
try {
Files.deleteIfExists(Paths.get("nio2_test.txt"));
Files.deleteIfExists(Paths.get("nio2_test_moved.txt"));
Files.deleteIfExists(Paths.get("nio2_dir"));
// 递归删除目录
Files.walk(Paths.get("a"))
.sorted((a, b) -> -a.compareTo(b)) // 先删除文件,再删除目录
.forEach(p -> {
try {
Files.delete(p);
} catch (IOException e) {
// 忽略删除错误
}
});
} catch (IOException e) {
e.printStackTrace();
}
}
}
七、文件操作工具类
7.1 文件工具类实现
import java.io.*;
import java.nio.file.*;
import java.nio.file.attribute.BasicFileAttributes;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.*;
import java.util.zip.*;
public class FileUtils {
/**
* 复制文件或目录
*/
public static void copy(String source, String target) throws IOException {
Path sourcePath = Paths.get(source);
Path targetPath = Paths.get(target);
if (Files.isDirectory(sourcePath)) {
copyDirectory(sourcePath, targetPath);
} else {
copyFile(sourcePath, targetPath);
}
}
private static void copyFile(Path source, Path target) throws IOException {
Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
}
private static void copyDirectory(Path source, Path target) throws IOException {
if (!Files.exists(target)) {
Files.createDirectories(target);
}
try (Stream<Path> stream = Files.walk(source)) {
stream.forEach(src -> {
try {
Path dest = target.resolve(source.relativize(src));
if (Files.isDirectory(src)) {
if (!Files.exists(dest)) {
Files.createDirectory(dest);
}
} else {
Files.copy(src, dest, StandardCopyOption.REPLACE_EXISTING);
}
} catch (IOException e) {
throw new RuntimeException(e);
}
});
}
}
/**
* 删除文件或目录(递归删除)
*/
public static void delete(String path) throws IOException {
Path filePath = Paths.get(path);
if (Files.isDirectory(filePath)) {
Files.walk(filePath)
.sorted((a, b) -> -a.compareTo(b)) // 先删除文件,再删除目录
.forEach(p -> {
try {
Files.delete(p);
} catch (IOException e) {
// 忽略删除错误
}
});
} else {
Files.deleteIfExists(filePath);
}
}
/**
* 计算文件MD5哈希值
*/
public static String calculateMD5(String filePath) throws IOException, NoSuchAlgorithmException {
MessageDigest md = MessageDigest.getInstance("MD5");
try (InputStream is = Files.newInputStream(Paths.get(filePath))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
md.update(buffer, 0, bytesRead);
}
}
byte[] digest = md.digest();
StringBuilder sb = new StringBuilder();
for (byte b : digest) {
sb.append(String.format("%02x", b));
}
return sb.toString();
}
/**
* 比较两个文件是否相同
*/
public static boolean compareFiles(String file1, String file2) throws IOException {
Path path1 = Paths.get(file1);
Path path2 = Paths.get(file2);
// 先比较文件大小
if (Files.size(path1) != Files.size(path2)) {
return false;
}
// 比较内容
return Files.mismatch(path1, path2) == -1;
}
/**
* 文件分割
*/
public static void splitFile(String sourceFile, long partSize) throws IOException {
Path source = Paths.get(sourceFile);
long fileSize = Files.size(source);
long parts = (fileSize + partSize - 1) / partSize;
try (InputStream is = Files.newInputStream(source)) {
byte[] buffer = new byte[8192];
for (int i = 1; i <= parts; i++) {
String partName = sourceFile + ".part" + i;
try (OutputStream os = Files.newOutputStream(Paths.get(partName))) {
long bytesRemaining = Math.min(partSize, fileSize - (i - 1) * partSize);
while (bytesRemaining > 0) {
int bytesToRead = (int) Math.min(buffer.length, bytesRemaining);
int bytesRead = is.read(buffer, 0, bytesToRead);
if (bytesRead == -1) {
break;
}
os.write(buffer, 0, bytesRead);
bytesRemaining -= bytesRead;
}
}
System.out.println("创建分片: " + partName);
}
}
}
/**
* 文件合并
*/
public static void mergeFiles(String[] partFiles, String targetFile) throws IOException {
try (OutputStream os = Files.newOutputStream(Paths.get(targetFile))) {
for (String partFile : partFiles) {
try (InputStream is = Files.newInputStream(Paths.get(partFile))) {
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
}
}
}
}
/**
* 文件压缩(ZIP格式)
*/
public static void compressToZip(String source, String zipFile) throws IOException {
Path sourcePath = Paths.get(source);
try (ZipOutputStream zos = new ZipOutputStream(
new FileOutputStream(zipFile))) {
if (Files.isDirectory(sourcePath)) {
Files.walk(sourcePath)
.filter(path -> !Files.isDirectory(path))
.forEach(path -> {
try {
String entryName = sourcePath.relativize(path).toString();
ZipEntry entry = new ZipEntry(entryName);
zos.putNextEntry(entry);
Files.copy(path, zos);
zos.closeEntry();
} catch (IOException e) {
throw new RuntimeException(e);
}
});
} else {
ZipEntry entry = new ZipEntry(sourcePath.getFileName().toString());
zos.putNextEntry(entry);
Files.copy(sourcePath, zos);
zos.closeEntry();
}
}
}
/**
* 文件解压缩
*/
public static void extractZip(String zipFile, String targetDir) throws IOException {
Path targetPath = Paths.get(targetDir);
try (ZipInputStream zis = new ZipInputStream(
new FileInputStream(zipFile))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
Path entryPath = targetPath.resolve(entry.getName());
// 防止路径遍历攻击
if (!entryPath.normalize().startsWith(targetPath.normalize())) {
throw new IOException("无效的ZIP条目: " + entry.getName());
}
if (entry.isDirectory()) {
Files.createDirectories(entryPath);
} else {
Files.createDirectories(entryPath.getParent());
Files.copy(zis, entryPath, StandardCopyOption.REPLACE_EXISTING);
}
zis.closeEntry();
}
}
}
/**
* 文件搜索
*/
public static List<Path> searchFiles(String directory, String pattern) throws IOException {
List<Path> results = new ArrayList<>();
Path dir = Paths.get(directory);
if (!Files.exists(dir) || !Files.isDirectory(dir)) {
return results;
}
// 使用Files.walk搜索文件
try (Stream<Path> stream = Files.walk(dir)) {
stream.filter(path -> {
String fileName = path.getFileName().toString();
return fileName.toLowerCase().contains(pattern.toLowerCase());
}).forEach(results::add);
}
return results;
}
/**
* 文件编码转换
*/
public static void convertEncoding(String sourceFile, String targetFile,
String sourceCharset, String targetCharset) throws IOException {
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(new FileInputStream(sourceFile), sourceCharset));
BufferedWriter writer = new BufferedWriter(
new OutputStreamWriter(new FileOutputStream(targetFile), targetCharset))) {
String line;
while ((line = reader.readLine()) != null) {
writer.write(line);
writer.newLine();
}
}
}
/**
* 统计目录信息
*/
public static class DirectoryInfo {
public long fileCount;
public long directoryCount;
public long totalSize;
public Map<String, Long> sizeByExtension = new HashMap<>();
}
public static DirectoryInfo getDirectoryInfo(String directory) throws IOException {
DirectoryInfo info = new DirectoryInfo();
Path dir = Paths.get(directory);
if (!Files.exists(dir) || !Files.isDirectory(dir)) {
return info;
}
Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {
if (!dir.equals(DirectoryInfo.this)) {
info.directoryCount++;
}
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) {
info.fileCount++;
info.totalSize += attrs.size();
// 统计文件扩展名
String fileName = file.getFileName().toString();
int dotIndex = fileName.lastIndexOf('.');
if (dotIndex > 0) {
String extension = fileName.substring(dotIndex + 1).toLowerCase();
info.sizeByExtension.merge(extension, attrs.size(), Long::sum);
}
return FileVisitResult.CONTINUE;
}
});
return info;
}
/**
* 监控文件变化
*/
public static void monitorFile(String filePath, FileChangeListener listener) {
Path path = Paths.get(filePath);
if (!Files.exists(path)) {
System.out.println("文件不存在: " + filePath);
return;
}
Thread monitorThread = new Thread(() -> {
try {
long lastModified = Files.getLastModifiedTime(path).toMillis();
long lastSize = Files.size(path);
while (!Thread.currentThread().isInterrupted()) {
Thread.sleep(1000); // 每秒检查一次
if (!Files.exists(path)) {
listener.onFileDeleted();
break;
}
long currentModified = Files.getLastModifiedTime(path).toMillis();
long currentSize = Files.size(path);
if (currentModified != lastModified) {
if (currentSize != lastSize) {
listener.onFileModified();
} else {
listener.onFileTouched();
}
lastModified = currentModified;
lastSize = currentSize;
}
}
} catch (IOException | InterruptedException e) {
// 线程被中断
}
});
monitorThread.setDaemon(true);
monitorThread.start();
}
interface FileChangeListener {
void onFileModified();
void onFileTouched();
void onFileDeleted();
}
public static void main(String[] args) {
try {
// 创建测试文件
String testFile = "test_utils.txt";
Files.write(Paths.get(testFile), "测试内容".getBytes(StandardCharsets.UTF_8));
// 计算MD5
String md5 = calculateMD5(testFile);
System.out.println("文件MD5: " + md5);
// 文件分割
splitFile(testFile, 1024); // 1KB分片
// 搜索文件
List<Path> searchResults = searchFiles(".", "utils");
System.out.println("搜索结果: " + searchResults.size());
// 获取目录信息
DirectoryInfo info = getDirectoryInfo(".");
System.out.println("目录信息:");
System.out.println(" 文件数: " + info.fileCount);
System.out.println(" 目录数: " + info.directoryCount);
System.out.println(" 总大小: " + info.totalSize);
// 监控文件变化
monitorFile(testFile, new FileChangeListener() {
@Override
public void onFileModified() {
System.out.println("文件被修改");
}
@Override
public void onFileTouched() {
System.out.println("文件被访问");
}
@Override
public void onFileDeleted() {
System.out.println("文件被删除");
}
});
// 等待一会儿以便观察监控效果
Thread.sleep(3000);
// 修改文件
Files.write(Paths.get(testFile),
"修改后的内容".getBytes(StandardCharsets.UTF_8),
StandardOpenOption.TRUNCATE_EXISTING);
Thread.sleep(1000);
// 清理测试文件
delete(testFile);
// 删除分片文件
for (int i = 1; ; i++) {
String partFile = testFile + ".part" + i;
if (!Files.exists(Paths.get(partFile))) {
break;
}
delete(partFile);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
八、最佳实践和性能优化
8.1 IO操作最佳实践
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
public class IOBestPractices {
/**
* 1. 使用try-with-resources确保资源关闭
*/
public static void practice1_tryWithResources() {
System.out.println("=== 最佳实践1:使用try-with-resources ===");
// 正确做法
try (BufferedReader reader = new BufferedReader(
new FileReader("test.txt"))) {
String line = reader.readLine();
System.out.println("读取内容: " + line);
} catch (IOException e) {
e.printStackTrace();
}
// 错误做法(可能忘记关闭资源)
/*
BufferedReader reader = null;
try {
reader = new BufferedReader(new FileReader("test.txt"));
String line = reader.readLine();
System.out.println("读取内容: " + line);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
*/
}
/**
* 2. 选择合适的缓冲区大小
*/
public static void practice2_bufferSize() throws IOException {
System.out.println("\n=== 最佳实践2:选择合适的缓冲区大小 ===");
String testFile = "large_file.txt";
createLargeFile(testFile, 10 * 1024 * 1024); // 创建10MB文件
long[] times = new long[5];
int[] bufferSizes = {512, 1024, 4096, 8192, 16384};
for (int i = 0; i < bufferSizes.length; i++) {
long startTime = System.currentTimeMillis();
copyFileWithBuffer(testFile, "copy_" + i + ".txt", bufferSizes[i]);
long endTime = System.currentTimeMillis();
times[i] = endTime - startTime;
System.out.printf("缓冲区 %5d bytes: %4d ms\n", bufferSizes[i], times[i]);
}
// 清理测试文件
Files.deleteIfExists(Paths.get(testFile));
for (int i = 0; i < bufferSizes.length; i++) {
Files.deleteIfExists(Paths.get("copy_" + i + ".txt"));
}
}
private static void createLargeFile(String filePath, long size) throws IOException {
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(filePath))) {
for (long i = 0; i < size / 100; i++) {
writer.write("01234567890123456789012345678901234567890123456789");
writer.write("01234567890123456789012345678901234567890123456789\n");
}
}
}
private static void copyFileWithBuffer(String source, String target, int bufferSize)
throws IOException {
try (InputStream is = new FileInputStream(source);
OutputStream os = new FileOutputStream(target)) {
byte[] buffer = new byte[bufferSize];
int bytesRead;
while ((bytesRead = is.read(buffer)) != -1) {
os.write(buffer, 0, bytesRead);
}
}
}
/**
* 3. 使用NIO Files类简化操作
*/
public static void practice3_useNIOFiles() throws IOException {
System.out.println("\n=== 最佳实践3:使用NIO Files类 ===");
String source = "nio_source.txt";
String target = "nio_target.txt";
// 写入文件(一行代码)
Files.writeString(Paths.get(source),
"使用NIO Files类简化文件操作\n第二行内容",
StandardOpenOption.CREATE);
// 读取文件(一行代码)
String content = Files.readString(Paths.get(source));
System.out.println("读取内容:\n" + content);
// 复制文件(一行代码)
Files.copy(Paths.get(source), Paths.get(target));
// 删除文件
Files.deleteIfExists(Paths.get(source));
Files.deleteIfExists(Paths.get(target));
}
/**
* 4. 正确处理字符编码
*/
public static void practice4_characterEncoding() {
System.out.println("\n=== 最佳实践4:正确处理字符编码 ===");
String testFile = "encoding_test.txt";
String content = "Hello, 世界! 中文测试。";
try {
// 错误做法:使用平台默认编码
// Files.writeString(Paths.get(testFile), content);
// 正确做法:明确指定编码
Files.writeString(Paths.get(testFile), content,
java.nio.charset.StandardCharsets.UTF_8);
// 读取时也要指定相同的编码
String readContent = Files.readString(Paths.get(testFile),
java.nio.charset.StandardCharsets.UTF_8);
System.out.println("写入和读取的内容: " + readContent);
// 检测文件编码
String detectedEncoding = detectFileEncoding(testFile);
System.out.println("检测到的编码: " + detectedEncoding);
Files.deleteIfExists(Paths.get(testFile));
} catch (IOException e) {
e.printStackTrace();
}
}
private static String detectFileEncoding(String filePath) throws IOException {
// 简单检测UTF-8 BOM
try (InputStream is = new FileInputStream(filePath)) {
byte[] bom = new byte[3];
int bytesRead = is.read(bom);
if (bytesRead >= 3 &&
bom[0] == (byte)0xEF &&
bom[1] == (byte)0xBB &&
bom[2] == (byte)0xBF) {
return "UTF-8 with BOM";
}
return "UTF-8 (without BOM)";
}
}
/**
* 5. 处理大文件时使用流式处理
*/
public static void practice5_streamProcessing() throws IOException {
System.out.println("\n=== 最佳实践5:流式处理大文件 ===");
String largeFile = "large_data.txt";
// 生成大文件
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(largeFile))) {
for (int i = 0; i < 100000; i++) {
writer.write("Line " + i + ": " +
"This is some data for line " + i + "\n");
}
}
// 流式处理(避免一次性加载到内存)
long lineCount = 0;
long wordCount = 0;
try (BufferedReader reader = new BufferedReader(
new FileReader(largeFile))) {
String line;
while ((line = reader.readLine()) != null) {
lineCount++;
wordCount += line.split("\\s+").length;
// 可以在这里处理每一行,而不需要整个文件内容
if (lineCount % 10000 == 0) {
System.out.println("已处理 " + lineCount + " 行");
}
}
}
System.out.println("总行数: " + lineCount);
System.out.println("总单词数: " + wordCount);
Files.deleteIfExists(Paths.get(largeFile));
}
/**
* 6. 使用合适的异常处理
*/
public static void practice6_exceptionHandling() {
System.out.println("\n=== 最佳实践6:合适的异常处理 ===");
String filePath = "nonexistent.txt";
try {
// 尝试读取不存在的文件
String content = Files.readString(Paths.get(filePath));
System.out.println(content);
} catch (java.nio.file.NoSuchFileException e) {
System.out.println("文件不存在: " + e.getFile());
// 创建文件或提供默认值
} catch (java.nio.file.AccessDeniedException e) {
System.out.println("没有访问权限: " + e.getFile());
} catch (IOException e) {
System.out.println("IO错误: " + e.getMessage());
e.printStackTrace();
} finally {
System.out.println("异常处理完成");
}
}
/**
* 7. 文件操作事务性处理
*/
public static void practice7_transactionalFileOperation() {
System.out.println("\n=== 最佳实践7:事务性文件操作 ===");
String tempFile = "data.tmp";
String finalFile = "data.txt";
try {
// 1. 写入临时文件
Files.writeString(Paths.get(tempFile),
"这是要写入的数据\n第二行",
StandardOpenOption.CREATE);
// 2. 验证数据(可以添加校验逻辑)
String tempContent = Files.readString(Paths.get(tempFile));
if (!tempContent.contains("数据")) {
throw new IOException("数据验证失败");
}
// 3. 原子性地重命名为最终文件
Files.move(Paths.get(tempFile), Paths.get(finalFile),
java.nio.file.StandardCopyOption.ATOMIC_MOVE,
java.nio.file.StandardCopyOption.REPLACE_EXISTING);
System.out.println("文件操作成功完成");
// 读取最终文件验证
String finalContent = Files.readString(Paths.get(finalFile));
System.out.println("最终文件内容:\n" + finalContent);
// 清理
Files.deleteIfExists(Paths.get(finalFile));
} catch (IOException e) {
System.out.println("操作失败: " + e.getMessage());
// 清理临时文件
try {
Files.deleteIfExists(Paths.get(tempFile));
} catch (IOException ex) {
// 忽略清理错误
}
}
}
public static void main(String[] args) throws IOException {
practice1_tryWithResources();
practice2_bufferSize();
practice3_useNIOFiles();
practice4_characterEncoding();
practice5_streamProcessing();
practice6_exceptionHandling();
practice7_transactionalFileOperation();
}
}
总结
核心知识点:
-
File类:文件和目录的基本操作
-
字节流:FileInputStream/FileOutputStream,处理二进制数据
-
字符流:FileReader/FileWriter,处理文本数据
-
缓冲流:BufferedInputStream/BufferedOutputStream/BufferedReader/BufferedWriter,提高IO性能
-
数据流:DataInputStream/DataOutputStream,读写基本数据类型
-
对象流:ObjectInputStream/ObjectOutputStream,对象序列化
-
NIO.2:Files和Paths类,更现代的文件操作API
最佳实践:
-
使用try-with-resources:确保资源正确关闭
-
合理选择缓冲区大小:通常8KB是最佳选择
-
明确指定字符编码:避免平台依赖问题
-
使用NIO Files类:简化常见文件操作
-
流式处理大文件:避免内存溢出
-
事务性文件操作:使用临时文件和原子操作
-
适当的异常处理:提供用户友好的错误信息
性能优化建议:
-
使用缓冲流:显著提高IO性能
-
批量读写:避免单字节操作
-
选择合适的流类型:文本用字符流,二进制用字节流
-
使用NIO通道:对于大文件或高性能要求场景
-
异步IO:Java NIO.2支持异步文件操作