程序世界的"输水管道"
想象你家的自来水系统:
🚰 水源 (文件/网络/内存)→ 管道 (流)→ 水龙头 (程序)
🔄 双向控制:既能接水(输入),也能排水(输出)
Java的I/O流正是这样的"管道系统",让数据在不同设备间安全高效 地流动!
一、I/O流基础概念
1. 流的本质
2. 主要流类型对比
分类维度 类型 特点 典型实现类 数据单位 字节流 8位二进制处理 FileInputStream 字符流 16位Unicode处理 FileReader 流向 输入流 数据读取 InputStream 输出流 数据写入 OutputStream 功能 节点流 直接连接数据源/目的地 FileWriter 处理流 对现有流增强功能 BufferedInputStream
二、四大基础流家族
1. 字节输入流:InputStream
try ( InputStream is = new FileInputStream ( "test.txt" ) ) {
byte [ ] buffer = new byte [ 1024 ] ;
int len;
while ( ( len = is. read ( buffer) ) != - 1 ) {
System . out. write ( buffer, 0 , len) ;
}
}
2. 字节输出流:OutputStream
try ( OutputStream os = new FileOutputStream ( "log.txt" ) ) {
String data = "Hello IO" ;
os. write ( data. getBytes ( ) ) ;
}
3. 字符输入流:Reader
try ( Reader reader = new FileReader ( "note.txt" , StandardCharsets . UTF_8 ) ) {
char [ ] buffer = new char [ 1024 ] ;
int len;
while ( ( len = reader. read ( buffer) ) != - 1 ) {
System . out. print ( new String ( buffer, 0 , len) ) ;
}
}
4. 字符输出流:Writer
try ( Writer writer = new FileWriter ( "report.txt" ) ) {
writer. write ( "用户日志\n" ) ;
writer. append ( "第二行内容" ) ;
}
三、性能升级:缓冲流的使用
1. 缓冲流工作原理
App BufferedStream RawStream read() 批量读取(如8KB) 数据块 返回单个字节/字符 App BufferedStream RawStream
2. 缓冲大小性能测试
缓冲大小 1GB文件复制耗时 无缓冲 14.2秒 1KB 3.8秒 8KB 2.1秒 64KB 1.9秒
3. 代码示例
try (
BufferedInputStream bis = new BufferedInputStream ( new FileInputStream ( "src.zip" ) ) ;
BufferedOutputStream bos = new BufferedOutputStream ( new FileOutputStream ( "copy.zip" ) )
) {
byte [ ] buffer = new byte [ 8192 ] ;
int len;
while ( ( len = bis. read ( buffer) ) != - 1 ) {
bos. write ( buffer, 0 , len) ;
}
}
四、高级流:装饰器模式的应用
1. 常见装饰流
装饰流 功能 示例 ObjectInputStream
对象序列化读取 读取Java对象 DataOutputStream
基本数据类型写入 写int/double等 ZipOutputStream
ZIP压缩 生成压缩文件 PrintWriter
格式化输出 System.out的增强版
2. 对象序列化流
try ( ObjectOutputStream oos = new ObjectOutputStream (
new FileOutputStream ( "user.dat" ) ) ) {
oos. writeObject ( new User ( "Alice" , 25 ) ) ;
}
try ( ObjectInputStream ois = new ObjectInputStream (
new FileInputStream ( "user.dat" ) ) ) {
User user = ( User ) ois. readObject ( ) ;
}
3. 组合使用示例
try (
InputStream fis = new FileInputStream ( "data.gz" ) ;
InputStream gzis = new GZIPInputStream ( fis) ;
Reader reader = new InputStreamReader ( gzis, "UTF-8" ) ;
BufferedReader br = new BufferedReader ( reader)
) {
String line;
while ( ( line = br. readLine ( ) ) != null ) {
System . out. println ( line) ;
}
}
五、NIO:新一代I/O系统
1. 传统IO vs NIO
特性 传统IO NIO 数据单位 流(Stream) 块(Channel & Buffer) 传输模式 阻塞 非阻塞 性能 较低 较高 复杂度 简单 较复杂
2. NIO核心组件
try (
FileChannel src = new FileInputStream ( "a.txt" ) . getChannel ( ) ;
FileChannel dest = new FileOutputStream ( "b.txt" ) . getChannel ( )
) {
ByteBuffer buffer = ByteBuffer . allocate ( 8192 ) ;
while ( src. read ( buffer) != - 1 ) {
buffer. flip ( ) ;
dest. write ( buffer) ;
buffer. clear ( ) ;
}
}
3. Channel与Buffer配合
try ( FileChannel src = FileChannel . open ( Paths . get ( "src.mp4" ) ) ;
FileChannel dest = FileChannel . open ( Paths . get ( "copy.mp4" ) ,
StandardOpenOption . CREATE , StandardOpenOption . WRITE ) ) {
ByteBuffer buffer = ByteBuffer . allocateDirect ( 8192 ) ;
while ( src. read ( buffer) != - 1 ) {
buffer. flip ( ) ;
dest. write ( buffer) ;
buffer. clear ( ) ;
}
}
4. 内存映射文件
try ( RandomAccessFile raf = new RandomAccessFile ( "huge.data" , "r" ) ;
FileChannel channel = raf. getChannel ( ) ) {
MappedByteBuffer buffer = channel. map (
FileChannel. MapMode . READ_ONLY , 0 , channel. size ( ) ) ;
}
六、I/O最佳实践
1. 资源关闭黄金法则
try ( InputStream is = new FileInputStream ( "file" ) ) {
}
InputStream is = null ;
try {
is = new FileInputStream ( "file" ) ;
} finally {
if ( is != null ) is. close ( ) ;
}
2. 缓冲大小选择
小文件:4KB-8KB 大文件:8KB-64KB 网络IO:1KB-4KB
3. 字符编码统一
Reader reader = new InputStreamReader (
new FileInputStream ( "text.txt" ) , StandardCharsets . UTF_8 ) ;
4. 异常处理模板
try {
} catch ( FileNotFoundException e) {
System . err. println ( "文件不存在: " + e. getMessage ( ) ) ;
} catch ( IOException e) {
System . err. println ( "I/O错误: " + e. getClass ( ) . getSimpleName ( ) ) ;
} catch ( Exception e) {
System . err. println ( "未知错误: " + e. getMessage ( ) ) ;
}
七、性能优化实战
1. 多线程文件处理
ExecutorService executor = Executors . newFixedThreadPool ( 4 ) ;
try ( BufferedReader br = Files . newBufferedReader ( Paths . get ( "bigfile.txt" ) ) ) {
br. lines ( ) . forEach ( line ->
executor. submit ( ( ) -> processLine ( line) ) ) ;
}
executor. shutdown ( ) ;
2. 零拷贝技术示例
try ( FileChannel src = FileChannel . open ( Paths . get ( "src.iso" ) ) ;
FileChannel dest = FileChannel . open ( Paths . get ( "copy.iso" ) ,
StandardOpenOption . CREATE , StandardOpenOption . WRITE ) ) {
src. transferTo ( 0 , src. size ( ) , dest) ;
}
八、常见问题解决方案
1. 大文件读取内存溢出
try ( RandomAccessFile raf = new RandomAccessFile ( "huge.log" , "r" ) ) {
byte [ ] buffer = new byte [ 8192 ] ;
for ( long pos = 0 ; pos < raf. length ( ) ; pos += buffer. length) {
raf. seek ( pos) ;
int read = raf. read ( buffer) ;
}
}
2. 文件复制性能对比
方法 1GB文件耗时 基本字节流 12.8s 缓冲流(8KB) 2.3s NIO传输 1.7s Files.copy() 1.5s
3. 网络IO超时设置
Socket socket = new Socket ( ) ;
socket. connect ( new InetSocketAddress ( "example.com" , 80 ) , 3000 ) ;
socket. setSoTimeout ( 5000 ) ;
结语:掌握数据流动的艺术
🔧 I/O操作黄金法则 :
始终关闭资源(用try-with-resources) 合理使用缓冲(性能提升明显) 统一字符编码(避免乱码) 根据场景选技术(传统IO/NIO/Java7+的Files)
💡 记住这个口诀:
数据流动像水流,字节字符分清楚;
缓冲装饰效率高,资源关闭不能漏;
NIO性能虽然好,场景合适最重要;
编码统一记心头,乱码问题不再愁!