1. 概述
在本文中,我们将深入探讨 使用 Java 向文件写入数据的多种方式。我们会使用到如下几个核心类和工具:
BufferedWriter
PrintWriter
FileOutputStream
DataOutputStream
RandomAccessFile
FileChannel
- Java 7 中引入的
Files
工具类
此外,我们还会介绍如何在写入时对文件进行加锁,并总结一些实用的经验。
本教程属于 Baeldung Java “Back to Basics” 系列 的一部分。
2. 使用 BufferedWriter 写入文件
最基础的方式是使用 BufferedWriter
将字符串写入文件:
public void whenWriteStringUsingBufferedWritter_thenCorrect()
throws IOException {
String str = "Hello";
BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
writer.write(str);
writer.close();
}
最终文件内容为:
Hello
✅ 追加写入也很简单,只需将 FileWriter
构造函数的第二个参数设为 true
:
@Test
public void whenAppendStringUsingBufferedWritter_thenOldContentShouldExistToo()
throws IOException {
String str = "World";
BufferedWriter writer = new BufferedWriter(new FileWriter(fileName, true));
writer.append(' ');
writer.append(str);
writer.close();
}
文件内容变为:
Hello World
3. 使用 PrintWriter 格式化写入
PrintWriter
更适合用于格式化输出,比如写入日志或结构化数据:
@Test
public void givenWritingStringToFile_whenUsingPrintWriter_thenCorrect()
throws IOException {
FileWriter fileWriter = new FileWriter(fileName);
PrintWriter printWriter = new PrintWriter(fileWriter);
printWriter.print("Some String");
printWriter.printf("Product name is %s and its price is %d $", "iPhone", 1000);
printWriter.close();
}
文件输出如下:
Some String
Product name is iPhone and its price is 1000$
⚠️ 注意:PrintWriter
支持 print
和 printf
,可以灵活处理字符串和格式化内容。
4. 使用 FileOutputStream 写入二进制数据
如果你需要写入的是字节数据,推荐使用 FileOutputStream
:
@Test
public void givenWritingStringToFile_whenUsingFileOutputStream_thenCorrect()
throws IOException {
String str = "Hello";
FileOutputStream outputStream = new FileOutputStream(fileName);
byte[] strToBytes = str.getBytes();
outputStream.write(strToBytes);
outputStream.close();
}
文件内容:
Hello
5. 使用 DataOutputStream 写入原始数据类型
DataOutputStream
适合写入 Java 原始数据类型(如 int、double 等),常用于序列化:
@Test
public void givenWritingToFile_whenUsingDataOutputStream_thenCorrect()
throws IOException {
String value = "Hello";
FileOutputStream fos = new FileOutputStream(fileName);
DataOutputStream outStream = new DataOutputStream(new BufferedOutputStream(fos));
outStream.writeUTF(value);
outStream.close();
// verify the results
String result;
FileInputStream fis = new FileInputStream(fileName);
DataInputStream reader = new DataInputStream(fis);
result = reader.readUTF();
reader.close();
assertEquals(value, result);
}
6. 使用 RandomAccessFile 随机位置写入
当你需要在文件的某个特定位置写入或修改数据时,RandomAccessFile
是不二之选:
private void writeToPosition(String filename, int data, long position)
throws IOException {
RandomAccessFile writer = new RandomAccessFile(filename, "rw");
writer.seek(position);
writer.writeInt(data);
writer.close();
}
读取指定位置的 int
值:
private int readFromPosition(String filename, long position)
throws IOException {
int result = 0;
RandomAccessFile reader = new RandomAccessFile(filename, "r");
reader.seek(position);
result = reader.readInt();
reader.close();
return result;
}
测试写入和读取:
@Test
public void whenWritingToSpecificPositionInFile_thenCorrect()
throws IOException {
int data1 = 2014;
int data2 = 1500;
writeToPosition(fileName, data1, 4);
assertEquals(data1, readFromPosition(fileName, 4));
writeToPosition(fileName2, data2, 4);
assertEquals(data2, readFromPosition(fileName, 4));
}
7. 使用 FileChannel 提升大文件写入性能
对于大文件操作,FileChannel
比传统 IO 更高效:
@Test
public void givenWritingToFile_whenUsingFileChannel_thenCorrect()
throws IOException {
RandomAccessFile stream = new RandomAccessFile(fileName, "rw");
FileChannel channel = stream.getChannel();
String value = "Hello";
byte[] strBytes = value.getBytes();
ByteBuffer buffer = ByteBuffer.allocate(strBytes.length);
buffer.put(strBytes);
buffer.flip();
channel.write(buffer);
stream.close();
channel.close();
// verify
RandomAccessFile reader = new RandomAccessFile(fileName, "r");
assertEquals(value, reader.readLine());
reader.close();
}
8. 使用 Java 7 的 Files 工具类
Java 7 引入了新的文件操作方式,其中 Files
类非常强大:
@Test
public void givenUsingJava7_whenWritingToFile_thenCorrect()
throws IOException {
String str = "Hello";
Path path = Paths.get(fileName);
byte[] strToBytes = str.getBytes();
Files.write(path, strToBytes);
String read = Files.readAllLines(path).get(0);
assertEquals(str, read);
}
9. 写入临时文件
临时文件的创建也很常见,例如用于缓存或测试:
@Test
public void whenWriteToTmpFile_thenCorrect() throws IOException {
String toWrite = "Hello";
File tmpFile = File.createTempFile("test", ".tmp");
FileWriter writer = new FileWriter(tmpFile);
writer.write(toWrite);
writer.close();
BufferedReader reader = new BufferedReader(new FileReader(tmpFile));
assertEquals(toWrite, reader.readLine());
reader.close();
}
⚠️ 创建临时文件是关键点,写入操作与其他方式一致。
10. 写入前加锁文件
为了防止多个线程或进程同时写入同一文件,我们可以使用 FileChannel
加锁:
@Test
public void whenTryToLockFile_thenItShouldBeLocked()
throws IOException {
RandomAccessFile stream = new RandomAccessFile(fileName, "rw");
FileChannel channel = stream.getChannel();
FileLock lock = null;
try {
lock = channel.tryLock();
} catch (final OverlappingFileLockException e) {
stream.close();
channel.close();
}
stream.writeChars("test lock");
lock.release();
stream.close();
channel.close();
}
⚠️ 如果文件已被锁定,将抛出 OverlappingFileLockException
。
11. 注意事项
在使用这些文件写入方式时,有几点值得注意:
- ❌ 尝试读取不存在的文件会抛出
FileNotFoundException
- ✅ 写入不存在的文件时,Java 会自动创建文件
- ⚠️ 务必手动调用
close()
方法释放资源,尤其是输出流 - ⚠️
close()
方法内部会调用flush()
,确保缓冲区数据被写入
不同类的使用场景总结如下:
类名 | 使用场景 |
---|---|
PrintWriter |
格式化文本输出 |
FileOutputStream |
写入二进制数据 |
DataOutputStream |
写入原始数据类型 |
RandomAccessFile |
随机访问写入 |
FileChannel |
大文件高性能写入 |
12. 总结
本文全面介绍了 Java 中向文件写入数据的多种方式,包括基础的文本写入、二进制数据处理、随机位置写入、文件加锁等高级操作。
所有示例代码均可在 GitHub 项目中找到。