21_Java IO流体系详解

21_Java IO流体系详解 Java IO流体系详解文章目录Java IO流体系详解前言一、IO流的分类二、File类详解三、字节流InputStream与OutputStream3.1 字节输入流 InputStream3.2 字节输出流 OutputStream3.3 文件拷贝示例四、字符流Reader与Writer4.1 FileReader与FileWriter4.2 字节流与字符流的桥接五、try-with-resources 自动关闭资源六、IO流使用的最佳实践总结✅ 亮点总结适用场景扩展方向前言在Java开发中IOInput/Output流是处理数据输入输出的核心机制。无论是文件读写、网络通信还是内存数据处理都离不开IO流的支持。Java的IO流体系设计精良采用装饰器模式构建了一套灵活、可扩展的流处理框架。Java IO流体系是初学者最容易产生学完就忘的知识点之一原因在于种类太多、API繁杂。但实际上如果你理解了它的设计模式——装饰器模式——整个体系就变得有条理了。就像乐高积木一样基础流负责连接数据源功能流缓冲、转换、编码负责增强能力通过层层嵌套组合出任意你需要的功能。本文将从零开始系统讲解Java IO流的分类、核心类及其使用方法帮助你建立清晰的IO流心智模型。一、IO流的分类Java IO流可以从两个维度进行分类按数据流向分输入流InputStream/Reader和输出流OutputStream/Writer按处理单位分字节流以byte为单位和字符流以char为单位分类字节输入流字节输出流字符输入流字符输出流抽象基类InputStreamOutputStreamReaderWriter文件操作FileInputStreamFileOutputStreamFileReaderFileWriter缓冲操作BufferedInputStreamBufferedOutputStreamBufferedReaderBufferedWriter设计理念字节流适合处理二进制数据图片、视频、音频字符流适合处理文本数据内置了字符编码处理。二、File类详解File类是java.io包中唯一代表磁盘文件本身的对象它既可以表示文件也可以表示目录。需要注意的是File类只用于表示文件目录的信息名称、大小、路径等不能用于文件内容的读写。常见的混淆点很多初学者误以为new File(test.txt)会创建文件。实际上构造File对象只是在内存中创建一个代表路径的对象并不会在磁盘上创建任何东西。磁盘上的文件创建需要通过createNewFile()方法显式完成。此外File类既可以表示文件也可以表示目录需要通过isDirectory()和isFile()方法来判断。importjava.io.File;importjava.io.IOException;importjava.util.Date;publicclassFileDemo{publicstaticvoidmain(String[]args)throwsIOException{// 创建File对象三种方式Filefile1newFile(D:/test/hello.txt);// 绝对路径Filefile2newFile(D:/test,hello.txt);// 父目录 子路径FileparentnewFile(D:/test);Filefile3newFile(parent,hello.txt);// 父File对象 子路径System.out.println(文件是否存在file1.exists());System.out.println(文件名file1.getName());System.out.println(父路径file1.getParent());System.out.println(绝对路径file1.getAbsolutePath());System.out.println(文件大小字节file1.length());System.out.println(最后修改时间newDate(file1.lastModified()));// 创建文件FilenewFilenewFile(D:/test/newfile.txt);if(!newFile.exists()){booleancreatednewFile.createNewFile();System.out.println(文件创建(created?成功:失败));}// 创建目录FiledirnewFile(D:/test/subdir);dir.mkdir();// 创建单级目录dir.mkdirs();// 创建多级目录父目录不存在也会创建// 遍历目录FiletestDirnewFile(D:/test);String[]fileNamestestDir.list();// 返回文件名数组File[]filestestDir.listFiles();// 返回File对象数组if(files!null){for(Filef:files){Stringtypef.isDirectory()?[目录]:[文件];System.out.println(type f.getName());}}// 删除文件或空目录newFile.delete();}}File类的核心方法总结exists()— 判断文件或目录是否存在createNewFile()— 创建新文件mkdir()/mkdirs()— 创建目录delete()— 删除文件或空目录list()/listFiles()— 列出目录内容getName()/getPath()/getAbsolutePath()— 获取路径信息三、字节流InputStream与OutputStream3.1 字节输入流 InputStreamInputStream是字节输入流的抽象基类定义了读取字节数据的基本方法。作为整个字节输入体系的顶层抽象它提供了三个层次的读取能力单个字节读取、批量字节数组读取、以及指定偏移量的读取。实际开发中建议使用后两种因为批量读取可以减少系统调用次数。publicabstractclassInputStream{publicabstractintread()throwsIOException;// 读取单个字节publicintread(byte[]b)throwsIOException;// 读取到字节数组publicintread(byte[]b,intoff,intlen);// 读取指定长度publicvoidclose()throwsIOException;// 关闭流}开发中常见的困惑read()返回int类型而不是byte类型这是为什么因为byte的取值范围是-128到127而read()需要用-1来表示已读到末尾。如果返回byte就没法区分读到了值为255的字节和流结束了。使用int0到255返回-1就能完美解决这个二义性问题。FileInputStream是InputStream的常用子类用于从文件中读取字节数据importjava.io.FileInputStream;importjava.io.IOException;publicclassFileInputStreamDemo{publicstaticvoidmain(String[]args){FileInputStreamfisnull;try{// 创建文件输入流fisnewFileInputStream(D:/test/hello.txt);// 方式一逐字节读取intdata;while((datafis.read())!-1){System.out.print((char)data);}// 方式二批量读取到字节数组效率更高byte[]buffernewbyte[1024];intlen;while((lenfis.read(buffer))!-1){StringcontentnewString(buffer,0,len);System.out.print(content);}}catch(IOExceptione){e.printStackTrace();}finally{// 必须在finally中关闭流if(fis!null){try{fis.close();}catch(IOExceptione){e.printStackTrace();}}}}}3.2 字节输出流 OutputStreamOutputStream是字节输出流的抽象基类importjava.io.FileOutputStream;importjava.io.IOException;publicclassFileOutputStreamDemo{publicstaticvoidmain(String[]args){FileOutputStreamfosnull;try{// true表示追加模式不传或false表示覆盖模式fosnewFileOutputStream(D:/test/output.txt,true);// 写入字节数据StringtextHello, Java IO!\r\n;fos.write(text.getBytes());// 写入字节数组fos.write(A);// 写入单个字节// 强制刷出缓冲区到磁盘fos.flush();System.out.println(写入成功);}catch(IOExceptione){e.printStackTrace();}finally{if(fos!null){try{fos.close();}catch(IOExceptione){e.printStackTrace();}}}}}3.3 文件拷贝示例结合输入流和输出流实现文件拷贝importjava.io.FileInputStream;importjava.io.FileOutputStream;importjava.io.IOException;publicclassFileCopyDemo{publicstaticvoidcopyFile(StringsrcPath,StringdestPath){try(FileInputStreamfisnewFileInputStream(srcPath);FileOutputStreamfosnewFileOutputStream(destPath)){byte[]buffernewbyte[4096];intlen;while((lenfis.read(buffer))!-1){fos.write(buffer,0,len);}System.out.println(文件拷贝完成);}catch(IOExceptione){e.printStackTrace();}}publicstaticvoidmain(String[]args){copyFile(D:/test/source.jpg,D:/test/dest.jpg);}}小提示上面使用了try-with-resources语法Java 7实现了流的自动关闭无需手动编写finally块。四、字符流Reader与Writer字符流用于处理文本数据自动处理字符编码问题。4.1 FileReader与FileWriterimportjava.io.FileReader;importjava.io.FileWriter;importjava.io.IOException;publicclassFileReaderWriterDemo{publicstaticvoidmain(String[]args){// 写入文本文件try(FileWriterfwnewFileWriter(D:/test/poem.txt)){fw.write(床前明月光\r\n);fw.write(疑是地上霜。\r\n);fw.write(举头望明月\r\n);fw.write(低头思故乡。\r\n);System.out.println(文件写入成功);}catch(IOExceptione){e.printStackTrace();}// 读取文本文件try(FileReaderfrnewFileReader(D:/test/poem.txt)){char[]buffernewchar[1024];intlen;while((lenfr.read(buffer))!-1){System.out.print(newString(buffer,0,len));}}catch(IOExceptione){e.printStackTrace();}}}4.2 字节流与字符流的桥接当需要处理字节流但想要字符流特性时可以使用InputStreamReader和OutputStreamWriter这两个转换流将在后续文章详细讲解。五、try-with-resources 自动关闭资源Java 7引入的try-with-resources机制极大简化了流的关闭操作。任何实现了java.lang.AutoCloseable接口的类都可以使用此语法// 传统写法繁琐FileInputStreamfisnull;try{fisnewFileInputStream(test.txt);// ... 处理数据}finally{if(fis!null)fis.close();}// try-with-resources 写法简洁try(FileInputStreamfisnewFileInputStream(test.txt);FileOutputStreamfosnewFileOutputStream(out.txt)){// ... 处理数据资源会自动关闭}catch(IOExceptione){e.printStackTrace();}执行顺序后声明的资源先关闭先声明的资源后关闭类似栈的先进后出。六、IO流使用的最佳实践以下几条实践原则是在实际开发中被反复验证过的经验总结选择合适的流类型二进制数据用字节流Stream文本数据用字符流Reader/Writer。这里的二进制包括但不限于图片、视频、压缩包、序列化对象等任何非纯文本格式。如果用字符流处理二进制数据编码转换会破坏原始字节。始终关闭流使用try-with-resources自动管理资源。未关闭的流不仅造成内存泄漏还可能导致文件句柄耗尽——在Linux系统上每个进程可打开的文件数有上限满后无法再打开任何文件。使用缓冲区使用byte[]或char[]缓冲区批量读写显著提升性能。一个4KB的缓冲区就能将读写效率提升数十倍因为系统调用的开销远大于内存复制。编码问题读写文本时注意指定字符编码避免乱码。这是跨平台部署时最常踩的坑——Windows默认GBKLinux默认UTF-8不显式指定编码就会在上线后出现乱码。路径处理使用File.separator代替硬编码的路径分隔符增强跨平台兼容性。或者在Java 7中使用Paths.get()统一处理。总结本文详细介绍了Java IO流体系的核心概念File类用于表示文件和目录的元信息是操作文件系统的入口字节流InputStream/OutputStream处理二进制数据适合图片、视频等文件字符流Reader/Writer处理文本数据内置字符编码支持try-with-resources自动关闭资源的现代写法推荐在所有IO操作中使用掌握IO流体系是Java开发的基础技能后续文章将继续深入讲解缓冲流、转换流以及NIO等高级IO技术。✅ 亮点总结IO流按流向输入/输出和处理单位字节/字符两个维度的二维分类体系形成四象限架构File类作为文件系统入口封装了创建/删除/遍历/属性读取等完整的元信息操作字节流FileInputStream/FileOutputStream处理二进制数据字符流FileReader/FileWriter处理文本并内置编码文件拷贝的完整实现4KB缓冲区批量读写 try-with-resources自动关闭兼顾性能与安全性try-with-resources语法Java 7的资源自动管理机制后声明先关闭的栈式释放顺序适用场景配置文件读取与解析如properties、json、yaml等格式文件的加载日志文件的批量写入与按日期归档组合File类创建目录和字符流写入内容文件上传下载功能的底层实现字节流处理任意格式、字符流处理文本内容扩展方向深入学习缓冲流BufferedInputStream/BufferedReader的性能优化原理与字符编码转换研究Apache Commons IO和Google Guava的高效IO工具类封装推荐阅读22_Java缓冲流与转换流下一篇Java缓冲流与转换流