Java IO 实战学习指南:从基础到进阶(附代码实操)
Java IO(输入/输出)是Java开发的核心基础,负责程序与外部设备(文件、网络、键盘等)的数据交互。很多初学者觉得IO类繁多、流概念抽象,其实掌握“先理解模型,再分场景实操”的思路就能快速上手。本文从核心概念到实战案例,带你一步步学会Java IO编程。
一、先搞懂3个核心前提(避免踩坑)
在写代码前,必须明确IO的核心逻辑,否则容易陷入“流用错、资源漏”的坑:
- IO的核心是“流”:数据像水流一样从源头(输入流)流向目的地(输出流),不可回退(除非用缓冲流);
- 按数据类型分2大类:
- 字节流(
InputStream/OutputStream):处理所有数据(图片、视频、文本等),最小单位是字节(1byte=8bit); - 字符流(
Reader/Writer):仅处理文本数据(.txt、.java等),最小单位是字符(依赖编码,如UTF-8中1个中文=3字节);
- 字节流(
- 资源必须关闭:IO流是“稀缺资源”,不用时必须关闭,否则会导致文件占用、内存泄露(推荐用Java 7+的
try-with-resources自动关闭)。
二、入门实操:从最常用的“文件IO”开始
文件操作是IO最基础的场景,先掌握字节流和字符流的文件读写,再扩展到其他场景。
场景1:字节流读写文件(万能场景,推荐优先掌握)
适合读写任意文件(文本、图片、视频等),核心类:FileInputStream(读)、FileOutputStream(写)。
案例1:读取文件内容(字节流)
需求:读取本地test.txt文件的内容,打印到控制台。
importjava.io.FileInputStream;importjava.io.IOException;publicclassFileByteReadDemo{publicstaticvoidmain(String[]args){// 关键:用try-with-resources声明流,自动关闭(无需手动close())try(FileInputStreamfis=newFileInputStream("test.txt")){byte[]buffer=newbyte[1024];// 缓冲区(一次读1024字节,提升效率)intlen;// 记录每次实际读取的字节数// 循环读取:read()返回-1表示读取完毕while((len=fis.read(buffer))!=-1){// 字节数组转字符串(注意编码一致,避免乱码)Stringcontent=newString(buffer,0,len,"UTF-8");System.out.print(content);}}catch(IOExceptione){// 捕获IO异常(文件不存在、权限不足等)e.printStackTrace();}}}代码说明:
byte[] buffer = new byte[1024]:缓冲区是IO高效读写的关键,避免每次只读1字节(频繁IO操作耗时);new String(buffer, 0, len, "UTF-8"):必须指定编码,否则默认使用系统编码(Windows是GBK,Linux是UTF-8,容易乱码);try-with-resources:流对象在try括号内声明,代码执行完自动关闭,无需手动写fis.close()。
案例2:写入文件内容(字节流)
需求:将字符串“Java IO 实战”写入output.txt,若文件不存在则创建,存在则覆盖。
importjava.io.FileOutputStream;importjava.io.IOException;publicclassFileByteWriteDemo{publicstaticvoidmain(String[]args){Stringcontent="Java IO 实战";// 追加内容:在构造方法加true(默认false=覆盖)try(FileOutputStreamfos=newFileOutputStream("output.txt",true)){// 字符串转字节数组(指定UTF-8编码)byte[]data=content.getBytes("UTF-8");fos.write(data);// 写入字节数组fos.flush();// 强制刷新缓冲区(确保数据立即写入文件)System.out.println("写入成功!");}catch(IOExceptione){e.printStackTrace();}}}代码说明:
FileOutputStream("output.txt", true):第二个参数true表示“追加模式”,不会覆盖原有内容;fos.flush():字节流缓冲区满了才会自动写入,flush()强制清空缓冲区,避免数据残留(字符流同理)。
场景2:字符流读写文本文件(专门处理文本,避免乱码)
字符流是字节流的“包装”,自动处理编码转换,适合读写文本文件(.txt、.md等),核心类:FileReader(读)、FileWriter(写)。
案例3:字符流读取文本文件
需求:读取test.txt(UTF-8编码)的文本内容,按行打印。
importjava.io.FileReader;importjava.io.BufferedReader;importjava.io.IOException;publicclassFileCharReadDemo{publicstaticvoidmain(String[]args){// 包装流:BufferedReader(缓冲字符流,支持按行读取)try(BufferedReaderbr=newBufferedReader(newFileReader("test.txt"))){Stringline;// 记录每行内容// readLine():读取一行文本,返回null表示结束while((line=br.readLine())!=null){System.out.println("行内容:"+line);}}catch(IOExceptione){e.printStackTrace();}}}代码说明:
BufferedReader是“缓冲包装流”,基于FileReader增强,提供readLine()按行读取,效率比直接用FileReader高;FileReader默认使用系统编码,若文件是UTF-8编码且系统编码不是UTF-8(如Windows),会乱码!解决方案:用InputStreamReader指定编码:// 解决乱码:FileInputStream + InputStreamReader(手动指定编码)BufferedReaderbr=newBufferedReader(newInputStreamReader(newFileInputStream("test.txt"),"UTF-8"));
案例4:字符流写入文本文件
需求:将多行文本写入note.txt,按行分隔。
importjava.io.FileWriter;importjava.io.BufferedWriter;importjava.io.IOException;publicclassFileCharWriteDemo{publicstaticvoidmain(String[]args){// BufferedWriter:缓冲字符流,支持newLine()换行try(BufferedWriterbw=newBufferedWriter(newFileWriter("note.txt"))){bw.write("第一行:Java IO 字符流");bw.newLine();// 跨平台换行(Windows是\r\n,Linux是\n,自动适配)bw.write("第二行:BufferedWriter 实战");bw.flush();// 刷新缓冲区System.out.println("写入完成!");}catch(IOExceptione){e.printStackTrace();}}}代码说明:
bw.newLine():比手动写\n更通用,自动适配操作系统的换行符;- 若需指定编码,用
OutputStreamWriter包装FileOutputStream:BufferedWriterbw=newBufferedWriter(newOutputStreamWriter(newFileOutputStream("note.txt"),"UTF-8"));
三、进阶实操:IO流的“包装思想”(核心技巧)
Java IO的设计核心是“装饰器模式”——通过“基础流+包装流”的组合,实现功能增强。比如:
- 基础流:
FileInputStream(读文件字节)、FileOutputStream(写文件字节); - 包装流:
- 缓冲流:
BufferedInputStream/BufferedOutputStream(提升读写效率); - 转换流:
InputStreamReader/OutputStreamWriter(字节流转字符流,指定编码); - 数据流:
DataInputStream/DataOutputStream(读写基本数据类型,如int、double)。
- 缓冲流:
场景3:用数据流读写基本数据类型
需求:将int、double类型的数据写入文件,再读取出来(适合存储配置、数值数据)。
importjava.io.DataInputStream;importjava.io.DataOutputStream;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;publicclassDataStreamDemo{publicstaticvoidmain(String[]args){// 写入基本数据类型try(DataOutputStreamdos=newDataOutputStream(newFileOutputStream("data.dat"))){dos.writeInt(100);// 写int类型dos.writeDouble(3.14159);// 写double类型dos.writeUTF("Java IO 数据流");// 写UTF-8字符串System.out.println("数据写入成功!");}catch(IOExceptione){e.printStackTrace();}// 读取基本数据类型(注意:读取顺序必须和写入顺序一致!)try(DataInputStreamdis=newDataInputStream(newFileInputStream("data.dat"))){intnum=dis.readInt();doublepi=dis.readDouble();Stringstr=dis.readUTF();System.out.println("读取结果:num="+num+", pi="+pi+", str="+str);}catch(IOExceptione){e.printStackTrace();}}}代码说明:
- 数据流的核心优势:能直接读写
int、double等基本类型,无需手动转换; - 关键注意:读取顺序必须和写入顺序完全一致(如先写int再写double,读取时也必须先读int再读double),否则会报
EOFException或数据错乱。
场景4:文件复制(IO流综合实战)
需求:复制一张图片(或视频、文档),从source.jpg到target.jpg(字节流万能复制,适合任意文件)。
importjava.io.BufferedInputStream;importjava.io.BufferedOutputStream;importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;publicclassFileCopyDemo{publicstaticvoidmain(String[]args){// 源文件路径和目标文件路径StringsourcePath="source.jpg";StringtargetPath="target.jpg";longstartTime=System.currentTimeMillis();// 记录开始时间// 用缓冲流包装基础流,提升复制效率try(BufferedInputStreambis=newBufferedInputStream(newFileInputStream(sourcePath));BufferedOutputStreambos=newBufferedOutputStream(newFileOutputStream(targetPath))){byte[]buffer=newbyte[4096];// 4KB缓冲区(常用最优大小)intlen;while((len=bis.read(buffer))!=-1){bos.write(buffer,0,len);// 写入实际读取的字节数}bos.flush();// 确保最后一批数据写入longendTime=System.currentTimeMillis();System.out.println("复制完成!耗时:"+(endTime-startTime)+"ms");}catch(IOExceptione){e.printStackTrace();}}}代码说明:
- 复制文件必须用字节流(字符流会损坏非文本文件,如图片、视频);
byte[] buffer = new byte[4096]:缓冲区大小建议设为2^n(如1024、4096、8192),平衡内存占用和效率;- 缓冲流(
BufferedInputStream/BufferedOutputStream)能减少磁盘IO次数,比直接用基础流快10倍以上。
四、Java 8+ 新特性:NIO.2(Files工具类,简化IO操作)
Java 7引入的NIO.2(java.nio.file包)提供了更简洁的IO API,Files工具类封装了常用操作(读文件、写文件、复制、删除等),无需手动处理流,代码更简洁。
场景5:用Files工具类快速读写文本文件
importjava.nio.file.Files;importjava.nio.file.Paths;importjava.util.List;importjava.io.IOException;publicclassFilesDemo{publicstaticvoidmain(String[]args){StringfilePath="test.txt";try{// 1. 读取所有行(自动按行分割,返回List<String>)List<String>lines=Files.readAllLines(Paths.get(filePath),java.nio.charset.StandardCharsets.UTF_8);System.out.println("文件内容:");for(Stringline:lines){System.out.println(line);}// 2. 写入文件(覆盖原有内容)Stringcontent="Files工具类实战:简化IO操作";Files.write(Paths.get("newFile.txt"),content.getBytes(StandardCharsets.UTF_8));// 3. 复制文件(一行代码搞定)Files.copy(Paths.get("source.jpg"),Paths.get("target_nio.jpg"));System.out.println("操作完成!");}catch(IOExceptione){e.printStackTrace();}}}代码说明:
Files.readAllLines():一次性读取所有行,适合小文件(大文件不推荐,会占用大量内存);Files.write():自动创建文件(父目录不存在会抛异常),支持字节数组、集合等参数;- 优势:代码简洁,无需手动管理流,自动处理资源关闭,是Java 8+推荐的简洁IO方式。
五、常见错误与避坑指南(新手必看)
乱码问题:
- 原因:字节流读文本时编码不匹配,或字符流未指定编码;
- 解决方案:始终用
InputStreamReader/OutputStreamWriter指定UTF-8编码,或用Files工具类显式指定编码。
资源泄露:
- 原因:忘记关闭流,导致文件被占用、内存泄露;
- 解决方案:强制使用
try-with-resources声明流(自动关闭),避免手动close()(容易遗漏)。
文件路径错误:
- 原因:相对路径写错(如
test.txt实际在src/main/resources下,而非项目根目录); - 解决方案:用绝对路径测试(如
C:/test.txt),或通过ClassLoader获取资源路径:// 获取resources目录下的文件Stringpath=FileCopyDemo.class.getClassLoader().getResource("test.txt").getPath();
- 原因:相对路径写错(如
缓冲区未刷新:
- 原因:写入流后未调用
flush(),缓冲区数据未写入文件; - 解决方案:写入完成后调用
flush(),或依赖try-with-resources自动刷新。
- 原因:写入流后未调用
六、学习路线建议(从易到难)
- 第一步:掌握字节流基础(
FileInputStream/FileOutputStream),实现文件读写、复制; - 第二步:学习字符流(
FileReader/FileWriter)+ 缓冲流(BufferedReader/BufferedWriter),处理文本文件; - 第三步:理解包装思想,学习转换流、数据流,掌握“基础流+包装流”的组合用法;
- 第四步:学习NIO.2(
Files工具类),简化日常IO操作; - 第五步:进阶学习NIO(通道、缓冲区),应对高并发IO场景(如网络编程)。
总结
Java IO学习的核心是“先理解流的模型,再分场景实操”。新手建议从文件读写、复制等简单场景入手,先能用起来,再深入理解“装饰器模式”“缓冲区原理”等底层逻辑。记住:IO编程的关键是“选对流、关对资源、避乱码”,多动手写案例(如复制文件、读写配置),很快就能熟练掌握。