第十五章 IO流
一、IO流基础概念IO流是Java中实现数据传输的核心机制位于java.io包下本质是数据在内存与外部存储设备如硬盘、网络之间传输的“数据通道”也被形象地称为“流”。1. 流的核心分类方式1按数据传输方向划分以内存为参照物• 输入流将外部设备中的数据读取到内存中对应“读操作”比如读取本地文件内容到程序中。• 输出流将内存中的数据写入到外部设备中对应“写操作”比如将程序中的字符串写入本地文件。2按数据传输单位划分• 字节流以字节1byte8bit为基本传输单位可处理所有类型的文件文本、图片、视频、音频等是Java中最基础的IO流类型。• 字符流以字符为基本传输单位仅能处理纯文本文件如.txt、.java、.html等底层本质仍是字节流编码表专门为解决文本文件的乱码问题而生。3按流的功能角色划分• 节点流直接与数据源文件、网络等对接具备最基础的读写功能也被称为“基础流”比如直接操作文件的FileInputStream。• 处理流过滤流不直接对接数据源而是在节点流的基础上进行封装增强读写功能或提供额外特性比如带缓冲功能的BufferedInputStream。二、字节流详解字节流的核心父类是两个抽象类• InputStream所有字节输入流的父类定义了read()系列读方法• OutputStream所有字节输出流的父类定义了write()系列写方法1. 常用节点字节流1文件字节输出流FileOutputStream用于将内存中的数据写入本地文件核心构造与用法• 路径形式支持绝对路径如D:/demo/test.txt和相对路径相对于项目根目录• 文件创建规则指定文件不存在时系统会自动创建但上级目录不存在时会抛出FileNotFoundException• 追加写入设置构造方法的第二个参数为true时数据会追加到文件末尾为false默认时会覆盖原文件内容• 核心方法◦ write(int b)写入单个字节数据◦ write(byte[] b)写入整个字节数组的数据◦ write(byte[] b, int off, int len)写入字节数组中从下标off开始、长度为len的部分数据◦ close()关闭流资源释放系统占用2文件字节输入流FileInputStream用于读取本地文件的数据到内存中核心构造与用法• 文件存在性要求指定文件不存在时直接抛出FileNotFoundException不会自动创建文件• 核心方法◦ read()读取单个字节返回读取到的字节值读到文件末尾时返回-1◦ read(byte[] b)读取数据到字节数组中返回实际读取的字节数读到文件末尾时返回-1◦ read(byte[] b, int off, int len)读取数据到字节数组的指定区间返回实际读取的字节数读到文件末尾时返回-12. 常用处理字节流1缓冲字节流BufferedInputStream/BufferedOutputStream• 核心优势内置缓冲区减少磁盘IO访问次数大幅提升读写效率• 关键特性缓冲区满时会自动刷新也可手动调用flush()方法强制刷新缓冲区调用close()方法时会自动刷新并关闭流资源• 典型应用大文件的复制、传输场景是文件上传下载的底层基础流2数据字节流DataInputStream/DataOutputStream• 核心功能直接读写Java的8种基本数据类型byte、short、int、long、float、double、char、boolean读写顺序需保持一致否则会出现数据解析错误• 典型应用需要按指定格式存储基本数据类型的场景比如自定义数据文件的读写3对象字节流ObjectInputStream/ObjectOutputStream• 核心功能实现Java对象的序列化与反序列化◦ 序列化将内存中的对象写入文件或网络中持久化存储◦ 反序列化将文件或网络中的对象数据读取到内存中重建对象• 关键要求参与序列化的对象对应的类必须实现java.io.Serializable接口标记接口无方法需实现否则会抛出NotSerializableException• 特殊规则被transient修饰的属性不会参与序列化自定义类的属性若包含其他自定义类型该类型也需实现Serializable接口集合类本身已实现序列化接口但存储的自定义对象仍需实现该接口• 异常处理读取到文件末尾时会抛出EOFException可通过该异常判断文件读取完成三、字符编码与字符流1. 常见字符编码方式• ISO-8859-1西欧编码仅支持单字节字符不支持中文• GB2312/GBK/GB18030中文编码体系GB2312支持常用简体中文GBK扩展支持更多汉字和繁体GB18030覆盖所有中文字符三者相互兼容• Big5繁体中文编码主要用于港台地区• UTF-8万国码采用动态字节存储英文占1字节中文占2-3字节是目前最通用的编码方式2. 编码与解码• 编码将字符字符串转换为对应编码格式的二进制数据的过程• 解码将二进制数据转换为对应编码格式的字符字符串的过程• 关键注意编码和解码必须使用相同的编码方式否则会出现乱码问题3. 字符流核心父类• Reader所有字符输入流的父类定义了read()系列读方法• Writer所有字符输出流的父类定义了write()系列写方法4. 常用节点字符流1文件字符输出流FileWriter• 核心用法与FileOutputStream类似专门处理文本文件• 核心方法◦ write(int c)写入单个字符数据◦ write(String str)直接写入字符串数据◦ write(char[] cbuf)写入整个字符数组的数据2文件字符输入流FileReader• 核心用法与FileInputStream类似专门处理文本文件• 核心方法◦ read()读取单个字符读到文件末尾返回-1◦ read(char[] cbuf)读取数据到字符数组中返回实际读取的字符数读到文件末尾返回-15. 常用处理字符流1缓冲字符流BufferedReader/BufferedWriter• 核心优势内置字符缓冲区提供按行读写的便捷方法大幅提升文本文件读写效率• 关键方法◦ BufferedReader.readLine()一次性读取一行文本读到文件末尾时返回null◦ BufferedWriter.newLine()写入一个平台无关的换行符适配不同操作系统的换行格式2打印字符流PrintWriter• 核心功能提供便捷的打印方法支持自动换行println()和不换行print()可直接输出基本数据类型、字符串和对象调用对象的toString()方法• 典型应用文本文件的格式化输出、日志文件的写入场景6. 转换流桥接流• 核心类InputStreamReader字节转字符输入流、OutputStreamWriter字节转字符输出流• 核心作用作为字节流和字符流之间的桥梁可指定编码格式解决文本文件读写的乱码问题• 使用场景当需要按指定编码方式读写文本文件时需通过转换流实现• 使用步骤1. 创建节点字节流对象如FileInputStream/FileOutputStream2. 创建转换流对象将字节流包装为字符流并指定编码格式3. 可选用处理流如BufferedReader/PrintWriter包装转换流增强读写功能4. 执行读写操作5. 关闭流资源只需关闭最外层的流内层流会自动关闭四、File类详解File类位于java.io包下专门用于对文件或目录本身进行操作与IO流的区别是IO流操作的是文件的内容而File类操作的是文件/目录的属性和状态。1. File类常用方法• getName()获取文件/目录的名称含后缀名• getAbsolutePath()获取文件/目录的绝对路径• isFile()判断当前对象是否为文件• isDirectory()判断当前对象是否为目录• listFiles()获取当前目录下所有的文件和子目录返回File数组• delete()删除文件或空目录• mkdir()/mkdirs()创建单级目录/多级目录2. 典型应用遍历目录文件通过递归遍历指定目录下的所有文件可实现批量处理文件的需求例如筛选出所有.java文件public static void listJavaFiles(File dir) {// 获取目录下所有文件和子目录File[] files dir.listFiles();if (files ! null) {for (File file : files) {if (file.isFile()) {// 判断文件是否为.java文件if (file.getName().endsWith(.java)) {System.out.println(找到Java文件 file.getAbsolutePath());}} else if (file.isDirectory()) {// 递归遍历子目录listJavaFiles(file);}}}}五、IO流核心总结1. 流的选择原则处理文本文件优先用字符流处理其他类型文件必须用字节流需要高效读写优先用缓冲流需要序列化对象必须用对象流需要指定编码格式需用转换流。2. 流的关闭规则所有IO流使用后都需调用close()方法释放资源建议在try-finally块中关闭流或使用try-with-resources语法自动关闭流。3. 序列化关键注意对象序列化必须实现Serializable接口transient修饰的属性不参与序列化反序列化时类的结构需与序列化时保持一致否则会抛出异常。