Java 流(Stream)、文件(File)和IO

张开发
2026/5/17 21:03:56 15 分钟阅读
Java 流(Stream)、文件(File)和IO
Java 流 (Stream)、文件 (File) 和 IO 学习笔记详细版Java 的输入/输出I/O系统非常强大且灵活涵盖了从文件读写、网络通信到内存数据处理的各种场景。Java 8 引入的Stream API更是将函数式编程思想引入集合与 IO 操作极大地简化了数据处理流程。一、Java IO 体系概览Java IO 基于字节流和字符流两大体系核心区别在于处理的数据单位不同。1. 核心分类类型基类处理单位适用场景典型类字节流InputStream/OutputStream字节 (8-bit)图片、音频、视频、二进制文件FileInputStream,FileOutputStream,BufferedInputStream字符流Reader/Writer字符 (16-bit Unicode)文本文件.txt, .java, .xml, .jsonFileReader,FileWriter,BufferedReader,BufferedWriter原则处理二进制数据图片、视频、压缩包必须用字节流。处理文本数据优先用字符流自动处理编码避免乱码。如果不确定字节流是万能的字符流底层也是字节流 编码转换。2. 节点流 vs 处理流节点流直接连接数据源如文件、内存数组。例FileInputStream,FileReader处理流包装流包装在节点流或其他处理流之上提供缓冲、转换、格式化等功能。例BufferedInputStream,DataInputStream,InputStreamReader二、核心 IO 操作详解1. 字节流操作 (Binary Data)A. 读取文件importjava.io.*;publicclassReadBinary{publicstaticvoidmain(String[]args){// 使用 try-with-resources 自动关闭流try(FileInputStreamfisnewFileInputStream(input.jpg);FileOutputStreamfosnewFileOutputStream(output_copy.jpg)){byte[]buffernewbyte[1024];// 缓冲区intlen;while((lenfis.read(buffer))!-1){fos.write(buffer,0,len);}System.out.println(复制完成);}catch(IOExceptione){e.printStackTrace();}}}B. 写入文件try(FileOutputStreamfosnewFileOutputStream(data.bin)){fos.write(65);// 写入 Afos.write(newbyte[]{1,2,3});}2. 字符流操作 (Text Data)A. 读取文本 (推荐 BufferedReader)importjava.io.*;publicclassReadText{publicstaticvoidmain(String[]args){// 指定编码防止乱码 (Java 7 推荐)try(BufferedReaderbrnewBufferedReader(newInputStreamReader(newFileInputStream(log.txt),UTF-8))){Stringline;while((linebr.readLine())!null){System.out.println(line);}}catch(IOExceptione){e.printStackTrace();}}}B. 写入文本 (推荐 BufferedWriter)try(BufferedWriterbwnewBufferedWriter(newOutputStreamWriter(newFileOutputStream(out.txt),UTF-8))){bw.write(Hello World);bw.newLine();// 写入换行符bw.write(Java IO is powerful);}3. 关键类File与Files/Paths(NIO.2)Java 7 引入了NIO.2 (New IO)提供了更简洁的java.nio.file包。A.File类 (传统)FilefilenewFile(src/test.txt);if(file.exists()){System.out.println(长度file.length());System.out.println(是否文件file.isFile());System.out.println(是否目录file.isDirectory());String[]listfile.list();// 列出目录内容}B.Files和Paths(现代推荐)Files提供了静态方法一行代码完成读写。importjava.nio.file.*;importjava.nio.charset.StandardCharsets;importjava.util.List;// 1. 读取所有行 (ListString)ListStringlinesFiles.readAllLines(Paths.get(log.txt),StandardCharsets.UTF_8);// 2. 写入所有行Files.write(Paths.get(out.txt),lines,StandardCharsets.UTF_8);// 3. 复制文件Files.copy(Paths.get(src.txt),Paths.get(dst.txt),StandardCopyOption.REPLACE_EXISTING);// 4. 移动/重命名Files.move(Paths.get(old.txt),Paths.get(new.txt),StandardCopyOption.REPLACE_EXISTING);// 5. 删除文件Files.delete(Paths.get(temp.txt));// 6. 创建目录Files.createDirectories(Paths.get(a/b/c));// 自动创建多级目录三、Java Stream API (流)注意这里的 Stream 是Java 8 引入的java.util.stream用于处理集合和数组数据不是IO 流。它支持函数式操作filter, map, reduce 等。1. 创建 Streamimportjava.util.*;importjava.util.stream.*;ListStringlistArrays.asList(Java,Python,C,Go);// 从集合创建StreamStringstream1list.stream();// 从数组创建StreamStringstream2Arrays.stream(newString[]{A,B});// 从值创建StreamIntegerstream3Stream.of(1,2,3);// 生成无限流StreamDoublerandomsStream.generate(Math::random);StreamIntegerevensStream.iterate(0,n-n2);2. 中间操作 (Intermediate Operations)特点惰性求值Lazy返回新的 Stream可以链式调用。方法功能示例filter(Predicate)过滤list.stream().filter(s - s.startsWith(J))map(Function)转换list.stream().map(String::toUpperCase)sorted()排序list.stream().sorted()distinct()去重list.stream().distinct()limit(n)截取前 n 个list.stream().limit(3)skip(n)跳过前 n 个list.stream().skip(2)peek(Consumer)调试/中间处理stream.peek(System.out::println)3. 终端操作 (Terminal Operations)特点触发执行返回结果或 voidStream 被消耗后不可再用。方法功能返回值forEach(Consumer)遍历voidcollect(Collector)收集结果List, Set, Map 等count()计数longanyMatch(Predicate)是否存在匹配booleanallMatch(Predicate)是否全部匹配booleannoneMatch(Predicate)是否都不匹配booleanfindFirst()找第一个Optionalreduce(BinaryOperator)归约Optional / T4. 实战示例ListStringnamesArrays.asList(Alice,Bob,Charlie,David,Eve);// 需求找出长度大于 3 的名字转为大写排序收集为 ListListStringresultnames.stream().filter(name-name.length()3)// 过滤.map(String::toUpperCase)// 转换.sorted()// 排序.collect(Collectors.toList());// 收集System.out.println(result);// [ALICE, CHARLIE, DAVID, EVE]// 并行流 (Parallel Stream)利用多核 CPUlongcountnames.parallelStream().filter(n-n.length()3).count();5. Stream 与 IO 的结合Java 8 的Files类返回StreamString可以无缝结合 Stream API 处理文件。// 统计文件中包含 error 的行数longerrorCountFiles.lines(Paths.get(log.txt),StandardCharsets.UTF_8).filter(line-line.contains(error)).count();四、序列化 (Serialization)将对象转换为字节序列保存到文件或网络传输反之亦然。1. 实现条件类必须实现java.io.Serializable接口标记接口无方法。所有非transient和非static的成员变量也必须可序列化。2. 示例importjava.io.*;classUserimplementsSerializable{privatestaticfinallongserialVersionUID1L;// 版本控制建议添加privateStringname;privatetransientStringpassword;// transient: 不序列化publicUser(Stringname,Stringpassword){this.namename;this.passwordpassword;}}publicclassSerializeDemo{publicstaticvoidmain(String[]args){UserusernewUser(Alice,123456);// 序列化try(ObjectOutputStreamoosnewObjectOutputStream(newFileOutputStream(user.obj))){oos.writeObject(user);}catch(IOExceptione){e.printStackTrace();}// 反序列化try(ObjectInputStreamoisnewObjectInputStream(newFileInputStream(user.obj))){UserloadedUser(User)ois.readObject();System.out.println(loadedUser.name);// AliceSystem.out.println(loadedUser.password);// null (因为 transient)}catch(IOException|ClassNotFoundExceptione){e.printStackTrace();}}}五、最佳实践与注意事项1. 资源关闭 (Try-with-Resources)必须使用try-with-resources语法确保流在使用后自动关闭防止资源泄露。// ✅ 推荐try(BufferedReaderbrnewBufferedReader(newFileReader(file.txt))){// 使用 br}// 自动关闭// ❌ 不推荐 (容易忘记 close)BufferedReaderbrnewBufferedReader(newFileReader(file.txt));try{// 使用}finally{br.close();}2. 编码问题处理文本文件时务必指定字符集如StandardCharsets.UTF_8。不要依赖系统默认编码new FileReader(file)因为不同操作系统默认编码可能不同导致乱码。3. 缓冲流 (Buffered Stream)读写文件时务必使用缓冲流BufferedInputStream,BufferedReader等。直接读写字节/字符效率极低缓冲流能显著减少磁盘 I/O 次数。4. 大文件处理不要使用Files.readAllBytes()或Files.readAllLines()读取超大文件会导致OutOfMemoryError。应使用流式处理InputStream,BufferedReader或Files.lines()逐行/逐块处理。5. Stream 的陷阱Stream 只能消费一次调用终端操作后Stream 关闭不能再使用。并行流慎用对于小数据量并行流开销可能大于收益对于有状态的操作如sorted并行流可能变慢。不要修改源集合在 Stream 处理过程中不要直接修改源集合ConcurrentModificationException。六、总结对比特性IO 流 (InputStream/Reader)Stream API (java.util.stream)用途数据输入/输出文件、网络、内存集合/数组数据的函数式处理数据源文件、Socket、字节数组List, Set, Array, IO 流执行方式主动读取/写入惰性求值终端操作触发数据修改通常不修改源数据生成新文件不修改源集合生成新结果核心方法read(),write(),close()filter,map,collect,reduce关系Files.lines()返回 Stream可结合 IO 流处理文件内容核心口诀读写文件用IO文本字符用Reader/Writer。处理集合用Stream链式调用效率高。资源关闭用Try-with-Resources。编码问题要显式指定。大文件处理用流式别一次性加载。掌握 IO 和 Stream 是 Java 后端开发、数据处理和文件操作的基础务必熟练运用。

更多文章