Java IO流基础与Guava Files工具类实战指南
在Java开发中,文件与数据的IO传输是核心功能之一。Java原生提供了丰富的InputStream和OutputStream接口,但代码往往较为冗长。本文将解析Java IO流的分类与典型操作,并对比展示Google Guava库如何通过Files类简化文件读写、移动及高级内存映射等操作。
Java原生IO流体系解析
IO(Input/Output)指数据相对于程序的流入与流出。Java在java.io包中提供了庞大的接口体系,理解其分类是正确使用的前提。
流的分类维度
根据操作对象和功能的不同,Java IO流主要分为以下几类:
- 按数据流向:输入流(
InputStream,读入程序)与输出流(OutputStream,写出程序)。 - 按操作单位:字节流以
byte为单位,适用于所有类型文件;字符流以char为单位,基于Reader和Writer,需关注字符编码。 - 按功能封装:包括缓冲流(提高读写效率)、转换流(字节与字符互转)、数据流(按基本数据类型读写)及对象序列化流。
区分技巧:通常以Stream结尾的是字节流,以Reader或Writer结尾的是字符流。
常见原生操作示例
原生IO API功能强大但样板代码较多,以下是几种典型场景的实现方式。
第1步:使用字节流进行文件复制
目的:将源文件数据逐块读取并写入目标文件。需注意手动关闭资源或使用Try-with-resources语句。
@Test
public void copyByBytes() throws IOException {
String root = FileTests.class.getResource("/").getPath();
// 建议在实际生产环境中使用 try-with-resources 自动管理资源
FileInputStream fis = new FileInputStream(new File(root, "start.bat"));
FileOutputStream fos = new FileOutputStream(root + "/out2.bat");
byte[] buff = new byte[100];
int b;
while ((b = fis.read(buff)) != -1) {
fos.write(buff, 0, b);
}
// 此处应调用 fis.close(); fos.close();
}
第2步:合并多个文件内容
目的:通过SequenceInputStream串联多个输入流,将它们连续读取后写入单一输出流。
@Test
public void mergeFiles() throws IOException {
File file1 = new File("E:\\_projects\\...\\FileTests.java");
File file2 = new File("E:\\_projects\\...\\StreamTests.java");
Enumeration<InputStream> ins = Collections.enumeration(Arrays.asList(
new FileInputStream(file1),
new FileInputStream(file2)
));
SequenceInputStream sequenceInputStream = new SequenceInputStream(ins);
FileOutputStream fos = new FileOutputStream(root + "/out4");
byte[] buff = new byte[100];
int read;
while ((read = sequenceInputStream.read(buff)) != -1) {
fos.write(buff, 0, read);
}
fos.close();
}
第3步:读取文件为字符串
目的:使用FileReader配合StringBuilder构建完整文本内容。注意处理编码问题,避免乱码。
@Test
public void readStringFromFile() throws IOException {
FileReader fileReader = new FileReader(new File(this.getClass().getResource("/").getPath(), "start.bat"));
StringBuilder stringBuilder = new StringBuilder();
int i;
while ((i = fileReader.read()) != -1) {
stringBuilder.append((char) i);
}
System.out.println(stringBuilder);
}
第4步:对象序列化与反序列化
目的:将Java对象转换为字节序列存储到文件,或从文件还原对象。对象必须实现Serializable接口。
@Test
public void objectToFile() throws IOException {
Person person = new Person();
person.setName("张三").setAge(25);
String root = FileTests.class.getResource("/").getPath();
FileOutputStream fos = new FileOutputStream(new File(root, "/person"));
ObjectOutputStream oos = new ObjectOutputStream(fos);
oos.writeObject(person);
oos.close();
}
@Test
public void fileToObject() throws IOException, ClassNotFoundException {
String root = FileTests.class.getResource("/").getPath();
FileInputStream fis = new FileInputStream(new File(root, "/person"));
ObjectInputStream ois = new ObjectInputStream(fis);
Person person = (Person) ois.readObject();
System.out.println(person);
}
Guava Files工具类的高效实践
Apache Commons IO和Google Guava都提供了更简洁的文件操作API。Guava的Files类不仅简化了常规读写,还引入了ByteSource/ByteSink抽象和内存映射技术。
基础文件操作
Guava封装了复制、移动、创建空文件等高频操作,且支持异常的统一抛出(IOException),无需繁琐的资源关闭代码。
/**
* 文件复制
*/
@Test
public void copy() throws IOException {
File from = new File(root,"from");
File to = new File(root,"to");
Files.copy(from,to);
}
/**
* 文件移动
*/
@Test
public void move() throws IOException {
File from = new File(root,"from");
File to = new File(root,"to");
Files.move(from,to);
}
/**
* 按行读取文件
* @throws IOException
*/
@Test
public void readLines() throws IOException {
File dest = new File(root,"start.bat");
List<String> lines = Files.readLines(dest, Charset.defaultCharset());
lines.forEach(System.out::println);
}
/**
* 写入文件
* @throws IOException
*/
@Test
public void writeToFile() throws IOException {
File dest = new File(root,"demo.txt");
Files.write("hello world!".getBytes(Charset.defaultCharset()), dest);
}
/**
* 修改文件更新时间(类似touch命令)
* @throws IOException
*/
@Test
public void touch() throws IOException {
File dest = new File(root,"demo.txt");
Files.touch(dest);
}高级IO:ByteSource与ByteSink
Guava引入ByteSource和ByteSink作为字节读写源的抽象。它们不仅代表文件,还可以代表网络流或数组,增强了组合能力。
文件转为字节数组:
@Test
public void fileAndBytes() throws IOException {
File dest = new File(root,"start.bat");
ByteSource byteSource = Files.asByteSource(dest);
byte[] bytes = byteSource.read();
System.out.println(bytes);
// 字节写入文件,实现复制
File target = new File(root, "start2.bat");
ByteSink byteSink = Files.asByteSink(target);
byteSink.write(bytes);
}
高性能操作:文件内存映射(零拷贝概念)
对于大文件处理,可以使用Files.map获取MappedByteBuffer,利用操作系统的虚拟内存机制减少用户态与内核态的数据拷贝次数。
/**
* 文件的零拷贝(基于MappedByteBuffer)
* @throws IOException
*/
@Test
public void map() throws IOException, URISyntaxException {
File from = new File(root,"from");
File to = new File(root,"to");
Files.touch(to);
MappedByteBuffer fromBuff = Files.map(from, MapMode.READ_ONLY, 1024);
// => 这里演示将mapped buffer写入文件通道
FileChannel channel = FileChannel.open(Paths.get(to.toURI()), StandardOpenOption.WRITE);
channel.write(fromBuff);
channel.close();
}管道流通信示例
PipedInputStream和PipedOutputStream可用于线程间数据传输,模拟生产者-消费者模式。
@Test
public void pipe() throws IOException {
PipedOutputStream pipedOutputStream = new PipedOutputStream();
PipedInputStream pipedInputStream = new PipedInputStream();
pipedOutputStream.connect(pipedInputStream);
new Thread(()->{
while (true){
String date = new Date().toString();
try {
pipedOutputStream.write( date.getBytes(StandardCharsets.UTF_8) );
pipedOutputStream.flush();
TimeUnit.SECONDS.sleep(2);
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
new Thread(()->{
while (true){
byte [] buff = new byte[1024];
try {
int read = pipedInputStream.read(buff);
if(read > 0) {
System.out.println( new String(buff, 0, read) );
}
TimeUnit.SECONDS.sleep(4);
} catch (IOException | InterruptedException e) {
throw new RuntimeException(e);
}
}
}).start();
}总结
Java原生IO流虽然灵活,但在处理常规文件读写时显得冗余。Guava提供的Files工具类通过ByteSource/Sink抽象和极简API(如copy, move, readLines),大幅降低了代码复杂度。对于高吞吐场景,其内置的内存映射功能也是优化IO性能的有效手段。以上就是Java中IO流与Guava使用的详细内容,更多关于Java基础与工具类优化的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









