JAVA流
流的基本概念
流(Stream)在Java中是对数据序列的抽象,用于处理输入输出(I/O)操作。它代表了从一个源(如文件、网络套接字、内存缓冲区)到另一个目的地的数据流动。流不是特指网络套接字,而是涵盖了所有I/O操作,包括文件、控制台、网络等。
在计算机体系中,数据在磁盘、内存和CPU之间流动时,流充当了桥梁角色。但更准确地说,流操作通常涉及:
- 源(如磁盘文件)→ 内存(缓冲区)→ 程序(CPU处理)
- 程序 → 内存(缓冲区)→ 目的(如磁盘文件)
常见的流包括:
- 标准输入/输出流:
System.in
(标准输入),System.out
(标准输出),System.err
(标准错误输出)。例如 System.out.println
使用了标准输出流。
- 文件流:用于读写文件,如
FileInputStream
, FileOutputStream
, FileReader
, FileWriter
。
- 缓冲流:在基础流之上增加缓冲功能,提高效率,如
BufferedReader
, BufferedWriter
。
- 数据流:用于读写基本数据类型,如
DataInputStream
, DataOutputStream
。
- 对象流:用于序列化对象,如
ObjectInputStream
, ObjectOutputStream
。
- 网络流:用于网络通信,如
Socket
相关的流。
文件流操作示例
文件流主要分为字节流(用于处理二进制数据)和字符流(用于处理文本数据,处理字符编码)。
字节流文件复制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| import java.io.*;
public class FileCopyByteStream { public static void main(String[] args) { String sourceFile = "1.txt"; String targetFile = "2.txt"; try { copyByStream(sourceFile, targetFile); } catch (IOException e) { System.err.println("文件复制失败: " + e.getMessage()); e.printStackTrace(); } }
public static void copyByStream(String sourceDir, String targetDir) throws IOException { try (FileInputStream fis = new FileInputStream(sourceDir); FileOutputStream fos = new FileOutputStream(targetDir)) { byte[] buffer = new byte[8192]; int length; while ((length = fis.read(buffer)) != -1) { fos.write(buffer, 0, length); } System.out.println("文件复制完成!"); } } }
|
字符流文件复制(改进版)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38
| import java.io.*; import java.nio.charset.StandardCharsets;
public class FileCopyCharStream { public static void main(String[] args) { String sourceFile = "1.txt"; String targetFile = "2.txt"; try { copyByCharStream(sourceFile, targetFile); } catch (IOException e) { System.err.println("文件复制失败: " + e.getMessage()); e.printStackTrace(); } }
public static void copyByCharStream(String sourceDir, String targetDir) throws IOException { try (FileReader fis = new FileReader(sourceDir, StandardCharsets.UTF_8); FileWriter fos = new FileWriter(targetDir, StandardCharsets.UTF_8)) { char[] buffer = new char[8192]; int length; while ((length = fis.read(buffer)) != -1) { fos.write(buffer, 0, length); } System.out.println("文件复制完成!"); } } }
|
缓冲流的使用(改进版)
缓冲流(Buffered Stream)通过减少底层系统的调用次数来提高I/O效率。它们确实在基础流之上添加了缓冲层,但更准确的说法是:缓冲流在内存中创建缓冲区,减少了对物理设备(如硬盘)的直接访问次数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| import java.io.*; import java.nio.charset.StandardCharsets;
public class FileCopyBufferedStream { public static void main(String[] args) { String sourceFile = "1.txt"; String targetFile = "2.txt"; try { copyByBufferedStream(sourceFile, targetFile); } catch (IOException e) { System.err.println("文件复制失败: " + e.getMessage()); e.printStackTrace(); } }
public static void copyByBufferedStream(String sourceDir, String targetDir) throws IOException { try (FileInputStream fis = new FileInputStream(sourceDir); FileOutputStream fos = new FileOutputStream(targetDir); BufferedInputStream bis = new BufferedInputStream(fis); BufferedOutputStream bos = new BufferedOutputStream(fos)) { byte[] buffer = new byte[8192]; int length; while ((length = bis.read(buffer)) != -1) { bos.write(buffer, 0, length); } System.out.println("文件复制完成!"); } } }
|
某些缓冲流(如 BufferedReader
)支持标记位置和重置功能。
使用NIO API进行文件复制(现代方法)
Java NIO (New I/O) 提供了更简洁高效的文件操作方式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| import java.io.IOException; import java.nio.file.*;
public class FileCopyNIO { public static void main(String[] args) { Path sourcePath = Paths.get("1.txt"); Path targetPath = Paths.get("2.txt"); try { Files.copy(sourcePath, targetPath, StandardCopyOption.REPLACE_EXISTING); System.out.println("文件复制完成!"); } catch (IOException e) { System.err.println("文件复制失败: " + e.getMessage()); e.printStackTrace(); } } }
|
NIO是新型的封装好的文件操作方法。
重要注意事项与最佳实践
资源管理:总是使用try-with-resources来自动关闭流,避免资源泄漏。
不要依赖手动调用 close()
,因为异常可能导致资源无法关闭。
字符编码: 处理文本文件时总是明确指定字符编码(如UTF-8)。
避免使用 FileReader
和 FileWriter
的默认构造函数,因为它们使用平台默认编码,可能导致跨环境问题。
缓冲区大小: 缓冲区大小(如8KB)通常比小缓冲区(如1KB)性能更好,但应通过测试确定最佳大小。
对于大文件,可以考虑使用更大的缓冲区(如64KB或128KB)。
flush操作: 输出流在关闭前会自动flush,通常不需要显式调用 flush()
。
如果需要确保数据立即写入(如实时日志),可以显式调用 flush()
。
性能考量: 对于大文件,NIO的 Files.copy()
通常是最佳选择。
缓冲流对于多次小量读写操作最有效。