Java基础-IO流全解析

张开发
2026/6/21 17:17:16 15 分钟阅读
Java基础-IO流全解析
一、IO流的核心分类3个维度无死角覆盖Java IO流的分类是基础也是高频面试题记住3个维度就能理清所有流的关系再也不混乱。所有IO流都围绕这3个维度划分没有例外。1. 按“数据传输单位”分最核心必记这是最根本的分类直接决定了流的用途——不同数据类型用不同的流。字节流以“字节byte”为单位传输数据1字节8位能处理所有类型的数据文本、图片、音频、视频、压缩包等因为所有数据在计算机中本质都是字节。核心基类抽象类不能直接实例化InputStream字节输入流、OutputStream字节输出流所有字节流都直接/间接继承这两个类。字符流以“字符char”为单位传输数据1字符2字节对应Unicode编码只能处理文本数据.txt、.java文件等会自动处理字符编码比如UTF-8、GBK避免中文乱码。核心基类抽象类Reader字符输入流、Writer字符输出流所有字符流都直接/间接继承这两个类。通俗类比字节流是“万能水管”能输送任何液体水、油、饮料字符流是“专用水管”只能输送水文本但输送水时更高效、更省心自动处理编码。2. 按“数据流向”分相对简单结合程序判断流向的判断标准以程序为中心数据从外部到程序就是输入流数据从程序到外部就是输出流。输入流读数据外部 → 程序比如从文件读取内容到程序、从键盘输入内容到程序。对应基类InputStream、Reader。输出流写数据程序 → 外部比如将程序中的内容写入文件、将内容打印到控制台。对应基类OutputStream、Writer。注意没有“既输入又输出”的流一个流只能负责一个方向比如读文件的流不能写文件写文件的流不能读文件。3. 按“功能角色”分理解流的作用避免用错这个分类容易被新手忽略但却是开发中“选对流”的关键——不同功能的流搭配使用才能高效开发。节点流基础流、底层流直接和数据源/目标对接是“直接输送数据的水管”没有依赖能独立工作。比如直接连接文件的流、直接连接内存的流。示例FileInputStream直接读文件、FileReader直接读文本文件、ByteArrayInputStream直接读内存中的字节数组。处理流包装流、高级流不直接对接数据源而是“包装”节点流或其他处理流增强节点流的功能比如缓冲、编码转换、对象序列化不能独立工作。示例BufferedInputStream给字节流加缓冲提升效率、InputStreamReader将字节流转成字符流处理编码、ObjectOutputStream实现对象序列化。通俗类比节点流是“原始水管”直接接水龙头数据源处理流是“水管配件”比如过滤器、增压泵套在原始水管上让水流更顺畅、更干净。开发中我们通常会“节点流处理流”搭配使用装饰器模式比如用BufferedInputStream包装FileInputStream提升文件读取效率。二、IO流核心类详解全类覆盖通俗用法实战代码掌握分类后我们逐个拆解所有核心IO流类——从字节流到字符流从节点流到处理流每个类的“作用核心方法实战代码”都讲透保证你看完就能用。先记住一个原则所有IO流类都要遵循“打开流 → 操作流读/写 → 关闭流”的流程关闭流是重中之重否则会导致资源泄漏文件被占用、内存浪费。第一部分字节流万能流处理所有数据字节流是基础所有字符流底层其实都是字节流只是做了字符编码的转换。先掌握字节流再学字符流会更轻松。1. 字节节点流直接对接数据源基础中的基础1FileInputStream文件字节输入流作用直接从文件中读取字节数据适用于所有文件图片、文本、音频等是最常用的字节输入节点流。核心构造方法2个常用FileInputStream(String filePath)传入文件路径创建流对象路径错误会报FileNotFoundException。FileInputStream(File file)传入File对象创建流对象更灵活可先判断文件是否存在。核心方法3个必记int read()读取1个字节返回读取的字节值0-255读取到文件末尾返回-1判断是否读完的关键。int read(byte[] buffer)读取多个字节存入字节数组buffer返回“实际读取的字节数”读完返回-1推荐用这个方法效率比单字节读取高。void close()关闭流释放资源必须调用最好放在finally中或用try-with-resources自动关闭。实战代码读取文件内容推荐try-with-resourcesJDK7可用自动关闭流import java.io.FileInputStream; import java.io.IOException; public class FileInputStreamDemo { public static void main(String[] args) { // 1. 定义文件路径注意相对路径是相对于项目根目录 String filePath test.txt; // 2. try-with-resources自动关闭流括号内创建流对象退出try块自动关闭 try (FileInputStream fis new FileInputStream(filePath)) { // 定义字节数组缓冲区一次读1024字节提升效率 byte[] buffer new byte[1024]; // 记录实际读取的字节数 int len; // 3. 循环读取直到读完len -1表示读完 while ((len fis.read(buffer)) ! -1) { // 将字节数组转成字符串避免乱码文本文件建议用字符流这里仅演示 System.out.print(new String(buffer, 0, len)); } } catch (IOException e) { // 处理异常实际开发中不要只打印要做日志记录 e.printStackTrace(); } // 无需手动close()try-with-resources会自动处理 } }注意单字节读取read()效率极低因为每次读取都要和磁盘交互开发中绝对不要用一定要用字节数组读取。2FileOutputStream文件字节输出流作用直接将字节数据写入文件适用于所有文件可覆盖写入或追加写入。核心构造方法2个常用FileOutputStream(String filePath)传入文件路径创建流对象默认“覆盖写入”写入前清空文件原有内容。FileOutputStream(String filePath, boolean append)append为true时“追加写入”在文件末尾添加内容为false时覆盖写入默认。核心方法3个必记void write(int b)写入1个字节注意传入的int值实际只取低8位作为字节。void write(byte[] buffer)将整个字节数组的内容写入文件。void write(byte[] buffer, int off, int len)将字节数组中“从off索引开始长度为len”的内容写入文件常用避免写入无用数据。void close()关闭流释放资源写入流关闭前会自动刷新缓冲区将数据写入文件。实战代码写入内容到文件追加写入import java.io.FileOutputStream; import java.io.IOException; public class FileOutputStreamDemo { public static void main(String[] args) { String filePath test.txt; // 追加写入appendtrue try (FileOutputStream fos new FileOutputStream(filePath, true)) { String content Java IO流学习笔记\n; // 将字符串转成字节数组文本转字节默认用系统编码可能乱码后续用字符流解决 byte[] buffer content.getBytes(); // 写入字节数组 fos.write(buffer); // 手动刷新可选close()会自动刷新 fos.flush(); System.out.println(写入成功); } catch (IOException e) { e.printStackTrace(); } } }注意如果写入的是文本内容用getBytes()可能会因系统编码不同导致乱码比如Windows默认GBKLinux默认UTF-8后续用字符流可解决这个问题。3ByteArrayInputStream / ByteArrayOutputStream内存字节流作用不操作文件只操作内存中的字节数组相当于“内存中的数据传输”常用于临时数据处理比如数据缓存、转换。核心特点ByteArrayInputStream从字节数组中读取数据数据源是内存中的字节数组。ByteArrayOutputStream将数据写入内存中的字节数组目标是内存无需关闭流关闭也没用不会占用资源。实战代码内存中读写数据import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; public class ByteArrayStreamDemo { public static void main(String[] args) { try { // 1. 内存输出流将数据写入内存字节数组 ByteArrayOutputStream baos new ByteArrayOutputStream(); String content 内存流测试; baos.write(content.getBytes()); // 获取内存中的字节数组 byte[] bytes baos.toByteArray(); System.out.println(内存中存储的字节数组 new String(bytes)); // 2. 内存输入流从字节数组中读取数据 ByteArrayInputStream bais new ByteArrayInputStream(bytes); byte[] buffer new byte[1024]; int len; while ((len bais.read(buffer)) ! -1) { System.out.println(读取到的内容 new String(buffer, 0, len)); } // 内存流可以不用关闭无资源泄漏但养成关闭习惯也可以 baos.close(); bais.close(); } catch (IOException e) { e.printStackTrace(); } } }2. 字节处理流包装节点流增强功能处理流不能独立工作必须包装一个节点流或其他处理流核心作用是提升效率、增加功能。1BufferedInputStream / BufferedOutputStream缓冲字节流作用给字节流添加“缓冲区”默认8KB减少磁盘IO次数大幅提升读写效率——这是开发中最常用的处理流几乎所有字节流操作都会搭配它使用。核心原理没有缓冲时每次读写1个字节都要和磁盘交互一次有缓冲后先将数据读到缓冲区内存缓冲区满了再一次性写入磁盘或从磁盘读取减少磁盘交互次数磁盘IO是程序性能瓶颈之一。核心构造方法BufferedInputStream(InputStream in)包装一个字节输入流如FileInputStream。BufferedOutputStream(OutputStream out)包装一个字节输出流如FileOutputStream。核心方法和包装的节点流方法一致read()、write()无需额外记新方法重点是“搭配使用”。实战代码缓冲流复制文件高效开发中复制文件的标准写法import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class BufferedStreamDemo { public static void main(String[] args) { // 源文件路径要复制的文件 String srcPath test.jpg; // 目标文件路径复制后的文件 String destPath test_copy.jpg; // 包装流BufferedInputStream包装FileInputStreamBufferedOutputStream包装FileOutputStream try (BufferedInputStream bis new BufferedInputStream(new FileInputStream(srcPath)); BufferedOutputStream bos new BufferedOutputStream(new FileOutputStream(destPath))) { byte[] buffer new byte[1024]; int len; // 循环读写效率比单纯用FileInputStream高很多 while ((len bis.read(buffer)) ! -1) { bos.write(buffer, 0, len); } // 缓冲输出流建议手动flush确保缓冲区数据全部写入文件 bos.flush(); System.out.println(文件复制成功); } catch (IOException e) { e.printStackTrace(); } } }注意缓冲输出流BufferedOutputStream的flush()方法用于将缓冲区中的数据一次性写入目标设备如果不调用close()方法会自动调用flush()但手动调用更安全避免程序异常退出时缓冲区数据丢失。2DataInputStream / DataOutputStream数据字节流作用包装字节流实现“基本数据类型”的读写如int、double、boolean等无需手动转换字节数组——比如直接写入int值直接读取int值不用再转成字节。核心方法常用DataOutputStreamwriteInt(int i)、writeDouble(double d)、writeBoolean(boolean b)。DataInputStreamreadInt()、readDouble()、readBoolean()。实战代码读写基本数据类型import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class DataStreamDemo { public static void main(String[] args) { String filePath data.txt; // 写入基本数据类型 try (DataOutputStream dos new DataOutputStream(new FileOutputStream(filePath))) { dos.writeInt(100); // 写入int dos.writeDouble(3.14); // 写入double dos.writeBoolean(true); // 写入boolean dos.writeUTF(Java IO); // 写入字符串UTF-8编码 System.out.println(基本数据类型写入成功); } catch (IOException e) { e.printStackTrace(); } // 读取基本数据类型注意读取顺序必须和写入顺序一致否则会读错 try (DataInputStream dis new DataInputStream(new FileInputStream(filePath))) { int num dis.readInt(); double d dis.readDouble(); boolean b dis.readBoolean(); String str dis.readUTF(); System.out.println(读取到的int num); System.out.println(读取到的double d); System.out.println(读取到的boolean b); System.out.println(读取到的字符串 str); } catch (IOException e) { e.printStackTrace(); } } }关键注意点读取顺序必须和写入顺序完全一致比如先写int再写double读取时也要先读int再读double否则会出现数据错乱、读取失败。3ObjectInputStream / ObjectOutputStream对象字节流序列化流作用包装字节流实现“Java对象”的读写序列化与反序列化——将对象转成字节数组序列化写入文件/网络再从字节数组中恢复成对象反序列化常用于对象持久化、网络传输比如RPC调用。核心要求必记否则序列化失败要序列化的对象必须实现Serializable接口标记接口无需实现任何方法仅表示“该对象可序列化”。对象中的所有成员变量也必须可序列化基本数据类型默认可序列化引用类型需实现Serializable接口。可通过transient关键字标记“不需要序列化”的成员变量序列化时会忽略该变量反序列化时该变量为默认值比如int为0String为null。序列化与反序列化的对象必须是同一个类类名、包名完全一致且类的serialVersionUID一致建议手动指定避免类修改后序列化失败。核心方法ObjectOutputStreamwriteObject(Object obj)序列化对象。ObjectInputStreamreadObject()反序列化对象返回Object类型需强制转换。实战代码对象序列化与反序列化import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; // 1. 定义可序列化的类实现Serializable接口 class User implements Serializable { // 手动指定serialVersionUID避免类修改后序列化失败 private static final long serialVersionUID 1L; private String name; private int age; // transient标记的变量不参与序列化 private transient String password; // 构造器、getter/setter public User(String name, int age, String password) { this.name name; this.age age; this.password password; } Override public String toString() { return User{name name , age age , password password }; } } public class ObjectStreamDemo { public static void main(String[] args) { String filePath user.txt; User user new User(张三, 25, 123456); // 序列化将对象写入文件 try (ObjectOutputStream oos new ObjectOutputStream(new FileOutputStream(filePath))) { oos.writeObject(user); System.out.println(对象序列化成功); } catch (IOException e) { e.printStackTrace(); } // 反序列化从文件中读取对象 try (ObjectInputStream ois new ObjectInputStream(new FileInputStream(filePath))) { User readUser (User) ois.readObject(); System.out.println(对象反序列化成功 readUser); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } }运行结果反序列化后的User对象password为null因为被transient标记未参与序列化其他属性正常恢复。第二部分字符流专用流处理文本数据避免乱码字符流是基于字节流封装的核心优势是“自动处理字符编码”解决文本读写的乱码问题。记住只要处理的是文本文件.txt、.java、.xml等就用字符流处理非文本文件图片、音频等必须用字节流。1. 字符节点流直接对接文本数据源1FileReader文件字符输入流作用直接从文本文件中读取字符数据自动适配系统编码默认编码比如Windows GBK、Linux UTF-8但无法手动指定编码这是它的缺点后续用转换流解决。核心构造方法FileReader(String filePath)传入文本文件路径。FileReader(File file)传入文本文件对象。核心方法和字节流类似只是单位是字符int read()读取1个字符返回字符的Unicode值读完返回-1。int read(char[] cbuf)读取多个字符存入字符数组返回实际读取的字符数读完返回-1。void close()关闭流释放资源。实战代码读取文本文件import java.io.FileReader; import java.io.IOException; public class FileReaderDemo { public static void main(String[] args) { String filePath test.txt; try (FileReader fr new FileReader(filePath)) { // 字符数组缓冲区一次读1024个字符 char[] cbuf new char[1024]; int len; while ((len fr.read(cbuf)) ! -1) { // 直接将字符数组转成字符串无乱码适配系统编码 System.out.print(new String(cbuf, 0, len)); } } catch (IOException e) { e.printStackTrace(); } } }2FileWriter文件字符输出流作用直接将字符数据写入文本文件自动适配系统编码支持覆盖写入和追加写入同样无法手动指定编码。核心构造方法FileWriter(String filePath)覆盖写入。FileWriter(String filePath, boolean append)appendtrue追加写入false覆盖写入。核心方法void write(int c)写入1个字符。void write(char[] cbuf)写入字符数组。void write(char[] cbuf, int off, int len)写入字符数组的指定部分。void write(String str)直接写入字符串最常用无需转字符数组。void write(String str, int off, int len)写入字符串的指定部分。实战代码写入文本文件追加写入import java.io.FileWriter; import java.io.IOException; public class FileWriterDemo { public static void main(String[] args) { String filePath test.txt; try (FileWriter fw new FileWriter(filePath, true)) { String content 字符流写入文本不会乱码\n; // 直接写入字符串非常方便 fw.write(content); // 手动刷新避免缓冲区数据未写入 fw.flush(); System.out.println(写入成功); } catch (IOException e) { e.printStackTrace(); } } }注意FileReader和FileWriter的缺点是“无法手动指定编码”如果文本文件的编码和系统编码不一致比如文件是UTF-8系统是GBK就会出现乱码此时需要用“转换流”解决。3CharArrayReader / CharArrayWriter内存字符流作用和字节流的ByteArrayInputStream/ByteArrayOutputStream类似操作内存中的字符数组用于临时文本数据处理无需关闭流。实战代码内存中读写文本import java.io.CharArrayReader; import java.io.CharArrayWriter; import java.io.IOException; public class CharArrayStreamDemo { public static void main(String[] args) { try { // 内存字符输出流写入字符到内存字符数组 CharArrayWriter caw new CharArrayWriter(); caw.write(内存字符流测试); caw.write(\n); // 写入单个字符 // 获取内存中的字符数组 char[] chars caw.toCharArray(); System.out.println(内存中的字符数组 new String(chars)); // 内存字符输入流从字符数组中读取 CharArrayReader car new CharArrayReader(chars); char[] cbuf new char[1024]; int len; while ((len car.read(cbuf)) ! -1) { System.out.println(读取到的内容 new String(cbuf, 0, len)); } // 无需关闭 caw.close(); car.close(); } catch (IOException e) { e.printStackTrace(); } } }2. 字符处理流包装节点流增强功能解决编码问题1InputStreamReader / OutputStreamWriter转换流核心解决乱码作用字节流和字符流的桥梁可以手动指定字符编码如UTF-8、GBK彻底解决文本读写的乱码问题——这是开发中处理文本文件的“首选流”因为它能控制编码。核心原理InputStreamReader将字节输入流如FileInputStream转换成字符输入流读取字节时按指定编码转成字符。OutputStreamWriter将字符输出流转换成字节输出流写入字符时按指定编码转成字节。核心构造方法必记指定编码InputStreamReaderInputStreamReader(InputStream in, String charsetName)charsetName为编码如UTF-8、GBK。OutputStreamWriterOutputStreamWriter(OutputStream out, String charsetName)。实战代码指定UTF-8编码读写文本文件避免乱码import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.IOException; public class ConvertStreamDemo { public static void main(String[] args) { String filePath test.txt; // 写入指定UTF-8编码避免乱码 try (OutputStreamWriter osw new OutputStreamWriter( new FileOutputStream(filePath, true), UTF-8)) { osw.write(指定UTF-8编码写入中文不会乱码\n); osw.flush(); } catch (IOException e) { e.printStackTrace(); } // 读取指定UTF-8编码和写入编码一致 try (InputStreamReader isr new InputStreamReader( new FileInputStream(filePath), UTF-8)) { char[] cbuf new char[1024]; int len; while ((len isr.read(cbuf)) ! -1) { System.out.print(new String(cbuf, 0, len)); } } catch (IOException e) { e.printStackTrace(); } } }关键注意点读取和写入的编码必须一致比如都用UTF-8否则会出现乱码开发中建议统一用UTF-8编码避免编码混乱。2BufferedReader / BufferedWriter缓冲字符流高效读写文本作用给字符流添加缓冲区提升文本读写效率同时BufferedReader提供了readLine()方法支持“按行读取文本”BufferedWriter提供了newLine()方法支持“写入换行符”跨平台兼容Windows是\n\rLinux是\n自动适配。核心构造方法BufferedReaderBufferedReader(Reader in)包装一个字符输入流如FileReader、InputStreamReader。BufferedWriterBufferedWriter(Writer out)包装一个字符输出流如FileWriter、OutputStreamWriter。核心方法重点记特有方法BufferedReaderString readLine()读取一行文本不包含换行符读完返回null判断是否读完的关键。BufferedWritervoid newLine()写入一个跨平台的换行符比手动写\n更规范。实战代码按行读写文本开发中最常用的文本处理方式import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.IOException; public class BufferedCharStreamDemo { public static void main(String[] args) { String srcPath test.txt; String destPath test_copy.txt; // 搭配转换流指定UTF-8编码同时用缓冲流提升效率 try (BufferedReader br new BufferedReader( new InputStreamReader(new FileInputStream(srcPath), UTF-8)); BufferedWriter bw new BufferedWriter( new OutputStreamWriter(new FileOutputStream(destPath), UTF-8))) { String line; // 按行读取直到读完line null表示读完 while ((line br.readLine()) ! null) { // 写入一行文本 bw.write(line); // 写入换行符跨平台兼容 bw.newLine(); } // 手动刷新 bw.flush(); System.out.println(文本文件复制成功); } catch (IOException e) { e.printStackTrace(); } } }说明这是开发中“文本文件读写”的标准写法——转换流指定编码 缓冲流高效按行读写兼顾了“无乱码”和“高效率”。3PrintWriter打印字符流简化写入操作作用包装字符流提供了更简洁的写入方法如print()、println()支持自动刷新无需手动调用flush()构造器中指定autoFlushtrue即可。核心特点支持写入基本数据类型、字符串无需转换如print(100)、print(abc)。println()方法会自动换行比BufferedWriter的newLine()更方便。可直接传入文件路径无需手动创建节点流简化代码。实战代码简化文本写入import java.io.PrintWriter; import java.io.IOException; public class PrintWriterDemo { public static void main(String[] args) { String filePath test.txt; // 构造器传入文件路径指定UTF-8编码自动刷新autoFlushtrue try (PrintWriter pw new PrintWriter(filePath, UTF-8)) { pw.println(PrintWriter写入简化操作); pw.println(100); // 写入int pw.println(3.14); // 写入double pw.print(不换行写入); // 无需手动flush()autoFlushtrueprintln()会自动刷新 } catch (IOException e) { e.printStackTrace(); } } }三、IO流的核心规律与使用技巧学到这里你可能会觉得IO流类太多不好记。其实只要记住以下规律就能轻松应对所有IO场景避免踩坑。1. 流的命名规律一眼分清流的类型名字带“InputStream/OutputStream”字节流带“Reader/Writer”字符流。名字带“File”节点流直接操作文件带“Buffered”缓冲处理流带“Data”数据处理流带“Object”对象处理流带“InputStreamReader/OutputStreamWriter”转换流。输入流名字带“Input/Reader”输出流名字带“Output/Writer”。2. 流的搭配规律开发中必用处理非文本文件图片、音频、视频字节节点流FileInputStream/FileOutputStream 缓冲字节流BufferedInputStream/BufferedOutputStream。处理文本文件无乱码字节节点流 转换流指定编码 缓冲字符流BufferedReader/BufferedWriter。处理基本数据类型字节节点流 数据处理流DataInputStream/DataOutputStream。处理对象序列化字节节点流 对象处理流ObjectInputStream/ObjectOutputStream。3. 资源释放的正确方式避坑重点IO流是“资源密集型”对象不关闭会导致资源泄漏文件被占用、内存浪费甚至程序崩溃。正确的释放方式有3种优先级从高到低try-with-resourcesJDK7推荐将流对象放在try括号内退出try块无论正常执行还是异常会自动关闭流无需手动调用close()。所有实现了AutoCloseable接口的流对象都支持这种方式Java所有IO流都实现了该接口。try-finallyJDK7之前在try块中操作流在finally块中手动关闭流确保无论是否异常都能关闭。注意关闭多个流时要嵌套try-catch避免某个流关闭失败导致后续流无法关闭。手动关闭不推荐直接在代码末尾调用close()如果中间出现异常流无法关闭会导致资源泄漏。示例try-finally方式关闭多个流import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; public class TryFinallyCloseDemo { public static void main(String[] args) { BufferedInputStream bis null; BufferedOutputStream bos null; try { bis new BufferedInputStream(new FileInputStream(test.jpg)); bos new BufferedOutputStream(new FileOutputStream(test_copy.jpg)); byte[] buffer new byte[1024]; int len; while ((len bis.read(buffer)) ! -1) { bos.write(buffer, 0, len); } bos.flush(); } catch (IOException e) { e.printStackTrace(); } finally { // 关闭多个流嵌套try-catch避免一个关闭失败影响另一个 try { if (bos ! null) bos.close(); } catch (IOException e) { e.printStackTrace(); } try { if (bis ! null) bis.close(); } catch (IOException e) { e.printStackTrace(); } } } }4. 常见坑点与解决方案坑点1文本读写乱码原因编码不一致写入和读取编码不同或未指定编码用默认系统编码。 解决方案用转换流InputStreamReader/OutputStreamWriter手动指定编码统一用UTF-8。坑点2流未关闭导致资源泄漏原因忘记关闭流或异常导致流无法关闭。 解决方案优先用try-with-resourcesJDK7之前用try-finally确保流一定被关闭。坑点3对象序列化失败原因未实现Serializable接口、成员变量不可序列化、serialVersionUID不一致、transient使用不当。 解决方案给需要序列化的类实现Serializable接口手动指定serialVersionUID确保所有成员变量可序列化无需序列化的用transient。坑点4缓冲流未flush导致数据丢失原因缓冲流的缓冲区未写满数据留在缓冲区未写入目标设备程序异常退出时数据丢失。 解决方案写入操作完成后手动调用flush()方法或确保close()方法被调用close()会自动flush()。坑点5路径错误报FileNotFoundException原因文件路径写错相对路径相对于项目根目录不是src目录或文件不存在。 解决方案用File对象判断文件是否存在file.exists()检查路径是否正确避免硬编码路径。四、IO流总结一张图吃透所有知识点最后用一张思维导图的逻辑总结Java IO流的所有核心知识点方便你记忆和回顾IO流本质数据传输通道程序与外部设备的桥梁。3大分类按数据单位字节流/字符流、按流向输入流/输出流、按功能节点流/处理流。核心基类字节流InputStream/OutputStream、字符流Reader/Writer。常用流组合 - 非文本文件FileInputStream/FileOutputStream BufferedInputStream/BufferedOutputStream - 文本文件FileInputStream/FileOutputStream 转换流 BufferedReader/BufferedWriter - 基本数据类型字节流 DataStream - 对象序列化字节流 ObjectStream核心技巧资源释放try-with-resources、编码统一UTF-8、缓冲流提升效率、按场景选对流。

更多文章