Java BufferedOutputStream
在Java IO操作中,效率是一个重要的考量因素。当我们需要向文件或其他目标写入大量数据时,如果每次只写入少量字节,频繁地进行IO操作会导致程序性能下降。为了解决这个问题,Java提供了BufferedOutputStream
类,它通过添加缓冲区来提高输出流的性能。
什么是BufferedOutputStream?
BufferedOutputStream
是Java IO包中的一个类,它继承自FilterOutputStream
。它的主要作用是为其他输出流添加缓冲功能,从而提高IO操作的效率。
当我们向BufferedOutputStream
写入数据时,数据首先被写入内存中的缓冲区。只有当缓冲区满了,或者调用flush()
方法时,缓冲区中的数据才会被一次性写入底层输出流。这样就减少了实际的IO操作次数,提高了程序的性能。
BufferedOutputStream的构造方法
BufferedOutputStream
类提供了两个构造方法:
// 创建一个新的缓冲输出流,以将数据写入指定的底层输出流,使用默认缓冲区大小(8192字节)
public BufferedOutputStream(OutputStream out)
// 创建一个新的缓冲输出流,以将数据写入指定的底层输出流,使用指定的缓冲区大小
public BufferedOutputStream(OutputStream out, int size)
基本用法示例
下面是一个使用BufferedOutputStream
写入文件的基本示例:
import java.io.*;
public class BufferedOutputStreamExample {
public static void main(String[] args) {
try {
// 创建一个文件输出流
FileOutputStream fileOut = new FileOutputStream("test.txt");
// 创建一个缓冲输出流,包装文件输出流
BufferedOutputStream buffOut = new BufferedOutputStream(fileOut);
// 要写入的字符串
String data = "Hello, BufferedOutputStream!";
// 将字符串转换为字节数组并写入缓冲输出流
buffOut.write(data.getBytes());
// 刷新缓冲区,确保所有数据都写入文件
buffOut.flush();
// 关闭流
buffOut.close();
fileOut.close();
System.out.println("数据已成功写入文件!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
输出:
数据已成功写入文件!
当你关闭BufferedOutputStream
时,它会自动调用flush()
方法将缓冲区中的所有数据写入底层输出流,然后关闭底层流。
BufferedOutputStream的主要方法
-
write(int b): 将指定的字节写入缓冲输出流。
-
write(byte[] b, int off, int len): 将指定字节数组中从偏移量
off
开始的len
个字节写入缓冲输出流。 -
flush(): 刷新缓冲输出流,将缓冲区中的所有数据写入底层输出流。
-
close(): 关闭流,释放与之相关联的所有资源。
BufferedOutputStream与FileOutputStream性能对比
为了直观地展示BufferedOutputStream
的性能优势,下面是一个将大量数据写入文件的性能对比示例:
import java.io.*;
public class OutputStreamPerformanceComparison {
public static void main(String[] args) {
try {
// 准备写入的数据量(约10MB)
byte[] data = new byte[10 * 1024 * 1024]; // 10MB的字节数组
for (int i = 0; i < data.length; i++) {
data[i] = (byte) (i % 256);
}
// 使用FileOutputStream(无缓冲)
long startTime = System.currentTimeMillis();
FileOutputStream fos = new FileOutputStream("unbuffered.dat");
for (int i = 0; i < data.length; i++) {
fos.write(data[i]);
}
fos.close();
long endTime = System.currentTimeMillis();
System.out.println("FileOutputStream写入时间: " + (endTime - startTime) + " 毫秒");
// 使用BufferedOutputStream(有缓冲)
startTime = System.currentTimeMillis();
FileOutputStream fos2 = new FileOutputStream("buffered.dat");
BufferedOutputStream bos = new BufferedOutputStream(fos2);
for (int i = 0; i < data.length; i++) {
bos.write(data[i]);
}
bos.close();
endTime = System.currentTimeMillis();
System.out.println("BufferedOutputStream写入时间: " + (endTime - startTime) + " 毫秒");
} catch (IOException e) {
e.printStackTrace();
}
}
}
可能的输出:
FileOutputStream写入时间: 28647 毫秒
BufferedOutputStream写入时间: 89 毫秒
实际运行结果可能会因硬件配置和系统负载不同而有所差异,但BufferedOutputStream
通常会比FileOutputStream
快很多倍。
实际应用场景
1. 文件复制
当需要复制大文件时,使用缓冲流可以显著提高效率:
import java.io.*;
public class FileCopyWithBuffer {
public static void main(String[] args) {
try {
// 源文件和目标文件
File sourceFile = new File("source.jpg");
File targetFile = new File("target.jpg");
// 创建输入流和缓冲输入流
FileInputStream fis = new FileInputStream(sourceFile);
BufferedInputStream bis = new BufferedInputStream(fis);
// 创建输出流和缓冲输出流
FileOutputStream fos = new FileOutputStream(targetFile);
BufferedOutputStream bos = new BufferedOutputStream(fos);
// 缓冲区
byte[] buffer = new byte[1024];
int bytesRead;
// 读取和写入数据
while ((bytesRead = bis.read(buffer)) != -1) {
bos.write(buffer, 0, bytesRead);
}
// 关闭流
bis.close();
bos.close();
System.out.println("文件复制完成!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
2. 网络数据传输
在网络编程中,使用缓冲流可以减少网络请求的次数,提高传输效率:
import java.io.*;
import java.net.Socket;
public class NetworkDataTransfer {
public static void sendDataToServer(String serverAddress, int port, byte[] data) {
try {
// 创建套接字连接到服务器
Socket socket = new Socket(serverAddress, port);
// 获取输出流并包装为缓冲输出流
OutputStream out = socket.getOutputStream();
BufferedOutputStream buffOut = new BufferedOutputStream(out);
// 写入数据
buffOut.write(data);
// 刷新并关闭流
buffOut.flush();
buffOut.close();
socket.close();
System.out.println("数据成功发送到服务器!");
} catch (IOException e) {
e.printStackTrace();
}
}
}
3. 创建ZIP文件
当创建ZIP文件时,使用缓冲输出流可以提高压缩速度:
import java.io.*;
import java.util.zip.*;
public class ZipCreator {
public static void createZipFile(File fileToZip, String zipFileName) {
try {
FileOutputStream fos = new FileOutputStream(zipFileName);
BufferedOutputStream bos = new BufferedOutputStream(fos);
ZipOutputStream zos = new ZipOutputStream(bos);
FileInputStream fis = new FileInputStream(fileToZip);
BufferedInputStream bis = new BufferedInputStream(fis);
// 添加ZIP条目
ZipEntry zipEntry = new ZipEntry(fileToZip.getName());
zos.putNextEntry(zipEntry);
// 写入ZIP文件
byte[] buffer = new byte[1024];
int bytesRead;
while ((bytesRead = bis.read(buffer)) != -1) {
zos.write(buffer, 0, bytesRead);
}
// 关闭流
zos.closeEntry();
bis.close();
zos.close();
System.out.println("ZIP文件创建成功: " + zipFileName);
} catch (IOException e) {
e.printStackTrace();
}
}
}
最佳实践
- 使用try-with-resources语句:自动关闭流,防止资源泄漏。
try (
FileOutputStream fos = new FileOutputStream("file.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos)
) {
bos.write("Hello World".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
-
选择合适的缓冲区大小:默认缓冲区大小(8KB)适合大多数情况,但对于特殊需求,可以调整缓冲区大小。
-
记得调用flush()方法:当需要立即写入数据时,不要忘记调用
flush()
方法。 -
关闭最外层的流:当使用多层流包装时,只需关闭最外层的流,内部的流会自动关闭。
总结
BufferedOutputStream
是Java IO中一个重要的类,通过在内存中缓冲数据,减少实际的IO操作次数,从而显著提高输出性能。它特别适用于需要频繁写入数据的场景,如文件复制、网络数据传输等。
使用BufferedOutputStream
时需要注意:
- 确保在不需要流时正确关闭它
- 在需要立即写入数据时调用
flush()
方法 - 考虑使用try-with-resources语句自动管理资源
通过合理使用BufferedOutputStream
,你可以有效提高Java应用程序的IO性能。
练习
-
编写一个程序,使用
BufferedOutputStream
将一个包含1000个随机整数的数组写入文件。 -
创建一个简单的日志系统,使用
BufferedOutputStream
将日志信息写入文件,并确保重要的日志信息立即写入(提示:使用flush()
方法)。 -
比较使用
BufferedOutputStream
和不使用缓冲的情况下,写入10MB数据到文件的性能差异。
延伸阅读
- Java官方文档中的BufferedOutputStream
- 学习相关的
BufferedInputStream
类,了解如何提高读取效率 - 探索Java NIO(New IO)包中的缓冲区概念,例如
ByteBuffer