java

关注公众号 jb51net

关闭
首页 > 软件编程 > java > java stream流与io流

Java中的Stream流与IO流完整实战指南(从零掌握)

作者:努力中的马喽

本文详细介绍了Java中的Stream流和IO流的使用方法,包括它们的基本概念、核心API、适用场景以及如何正确关闭资源,通过多个实战案例,帮助读者掌握这些核心技能,提高Java开发效率,感兴趣的朋友跟随小编一起看看吧

Java新手必会:Stream流与IO流完整实战指南

核心观点:掌握Java的Stream流和IO流,就像学会了两把"数据处理神器"——Stream流让集合操作变得优雅高效,IO流让文件读写不再头疼。本文将用最接地气的方式,带你从零到一掌握这两大核心技能。

1. 什么是Stream流和IO流?一句话说清楚

Stream流(集合流)

一句话理解:把集合中的数据想象成流水线上的产品,Stream流就是这条流水线,可以对数据进行筛选、加工、组装。

生活例子:就像奶茶店制作奶茶的过程——原料(集合)→ 筛选茶叶(filter)→ 加糖(map)→ 打包(collect),一气呵成!

IO流(输入输出流)

一句话理解:IO流是Java读写文件、网络传输数据的通道,就像水管输送水一样。

生活例子:你用U盘拷贝文件,IO流就是那根"看不见的数据线",负责把数据从硬盘"运"到U盘。

2. Stream流:让集合操作像流水线一样顺畅

2.1 Stream流的三大核心优势

  1. 代码简洁:一行代码搞定原本需要好几个for循环的操作
  2. 链式调用:filter、map、sorted一气呵成,逻辑清晰
  3. 延迟执行:只有调用终止操作(如collect)时才真正执行,性能更优

2.2 核心API实战

案例1:筛选出成绩大于60分的学生
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDemo1 {
    public static void main(String[] args) {
        // 学生成绩列表
        List<Integer> scores = new ArrayList<>();
        scores.add(85);
        scores.add(45);
        scores.add(92);
        scores.add(58);
        scores.add(73);
        // 使用Stream流筛选及格的成绩
        List<Integer> passedScores = scores.stream()
                .filter(score -> score >= 60)  // 筛选:保留>=60的成绩
                .collect(Collectors.toList()); // 收集结果
        System.out.println("及格成绩:" + passedScores);
        // 输出:及格成绩:[85, 92, 73]
    }
}

代码讲解

案例2:将所有学生姓名转为大写
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDemo2 {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("zhangsan", "lisi", "wangwu");
        // 将姓名转为大写
        List<String> upperNames = names.stream()
                .map(String::toUpperCase)  // 转换:每个元素调用toUpperCase()
                .collect(Collectors.toList());
        System.out.println("大写姓名:" + upperNames);
        // 输出:大写姓名:[ZHANGSAN, LISI, WANGWU]
    }
}

代码讲解

案例3:统计平均分
import java.util.Arrays;
import java.util.List;
public class StreamDemo3 {
    public static void main(String[] args) {
        List<Integer> scores = Arrays.asList(85, 92, 78, 90, 88);
        // 计算平均分
        double average = scores.stream()
                .mapToInt(Integer::intValue)  // 转换为IntStream
                .average()                     // 求平均值
                .orElse(0.0);                 // 如果流为空,返回0.0
        System.out.println("平均分:" + average);
        // 输出:平均分:86.6
    }
}

代码讲解

案例4:去重并排序
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
public class StreamDemo4 {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(5, 2, 8, 2, 9, 1, 5, 8);
        // 去重、排序、收集
        List<Integer> result = numbers.stream()
                .distinct()                    // 去重
                .sorted()                      // 排序(默认升序)
                .collect(Collectors.toList());
        System.out.println("去重排序后:" + result);
        // 输出:去重排序后:[1, 2, 5, 8, 9]
    }
}

3. 字节流 vs 字符流:该用哪个?看这里就够了

3.1 核心区别一览表

对比项字节流字符流
处理单位字节(byte,8位)字符(char,16位)
适用场景图片、视频、音频等二进制文件文本文件(.txt、.java等)
核心类InputStream / OutputStreamReader / Writer
是否有缓冲无(需手动包装)无(需手动包装)

3.2 字节流实战:复制图片

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class ByteStreamDemo {
    public static void main(String[] args) {
        // 使用 try-with-resources 自动关闭资源
        try (
            FileInputStream fis = new FileInputStream("source.jpg");
            FileOutputStream fos = new FileOutputStream("target.jpg")
        ) {
            byte[] buffer = new byte[1024];  // 缓冲区
            int len;
            // 循环读取并写入
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
            System.out.println("图片复制成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码讲解

3.3 字符流实战:读写文本文件

import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class CharStreamDemo {
    public static void main(String[] args) {
        // 写入文本
        try (FileWriter fw = new FileWriter("hello.txt")) {
            fw.write("你好,Java IO流!\n");
            fw.write("这是第二行内容。");
            System.out.println("文件写入成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 读取文本
        try (FileReader fr = new FileReader("hello.txt")) {
            int ch;
            while ((ch = fr.read()) != -1) {
                System.out.print((char) ch);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码讲解

4. 缓冲流:性能提升10倍的秘密武器

4.1 为什么需要缓冲流?

问题:每次读写都直接访问硬盘,就像你去超市买菜,买一根葱就跑一趟,效率极低!

解决方案:缓冲流内置一个缓冲区(默认8KB),相当于带个购物车,攒够一车再去收银台,大大减少IO次数。

4.2 缓冲字节流实战

import java.io.*;
public class BufferedByteStreamDemo {
    public static void main(String[] args) {
        long startTime = System.currentTimeMillis();
        try (
            BufferedInputStream bis = new BufferedInputStream(
                new FileInputStream("large_file.zip")
            );
            BufferedOutputStream bos = new BufferedOutputStream(
                new FileOutputStream("large_file_copy.zip")
            )
        ) {
            byte[] buffer = new byte[8192];  // 8KB缓冲区
            int len;
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
            }
            long endTime = System.currentTimeMillis();
            System.out.println("复制完成,耗时:" + (endTime - startTime) + "ms");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

性能对比

4.3 缓冲字符流实战:按行读写

import java.io.*;
public class BufferedCharStreamDemo {
    public static void main(String[] args) {
        // 按行写入
        try (BufferedWriter bw = new BufferedWriter(
                new FileWriter("students.txt")
        )) {
            bw.write("张三,85");
            bw.newLine();  // 写入换行符(跨平台)
            bw.write("李四,92");
            bw.newLine();
            bw.write("王五,78");
            System.out.println("学生信息写入成功!");
        } catch (IOException e) {
            e.printStackTrace();
        }
        // 按行读取
        try (BufferedReader br = new BufferedReader(
                new FileReader("students.txt")
        )) {
            String line;
            while ((line = br.readLine()) != null) {  // 按行读取
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

代码讲解

5. 资源释放:不注意这点会出大问题 ⚠️

5.1 为什么必须关闭流?

后果

  1. 内存泄漏:流对象一直占用内存
  2. 文件锁定:文件被占用,无法删除或重命名
  3. 数据丢失:缓冲区数据可能未写入文件

5.2 正确的资源释放方式

❌ 错误示范(JDK7之前)
FileInputStream fis = null;
try {
    fis = new FileInputStream("data.txt");
    // 读取操作...
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if (fis != null) {
        try {
            fis.close();  // 繁琐!
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
✅ 正确示范(JDK7+)
try (FileInputStream fis = new FileInputStream("data.txt")) {
    // 读取操作...
} catch (IOException e) {
    e.printStackTrace();
}
// 无需手动关闭,try-with-resources自动处理

原理:实现了AutoCloseable接口的类,在try代码块结束时会自动调用close()方法。

6. 实战案例与常见避坑指南

案例1:读取配置文件并过滤有效配置

import java.io.*;
import java.util.List;
import java.util.stream.Collectors;
public class ConfigReaderDemo {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(
                new FileReader("config.properties")
        )) {
            // 使用Stream流过滤空行和注释行
            List<String> validConfigs = br.lines()  // 将所有行转为Stream
                    .map(String::trim)               // 去除首尾空格
                    .filter(line -> !line.isEmpty()) // 过滤空行
                    .filter(line -> !line.startsWith("#")) // 过滤注释
                    .collect(Collectors.toList());
            System.out.println("有效配置:");
            validConfigs.forEach(System.out::println);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

亮点:将IO流和Stream流结合,代码简洁优雅!

案例2:统计文本文件中每个单词的出现次数

import java.io.*;
import java.util.*;
import java.util.stream.Collectors;
public class WordCountDemo {
    public static void main(String[] args) {
        try (BufferedReader br = new BufferedReader(
                new FileReader("article.txt")
        )) {
            Map<String, Long> wordCount = br.lines()
                    .flatMap(line -> Arrays.stream(line.split("\\s+"))) // 按空格分词
                    .map(String::toLowerCase)  // 转小写
                    .filter(word -> !word.isEmpty())  // 过滤空字符串
                    .collect(Collectors.groupingBy(
                            word -> word,           // 按单词分组
                            Collectors.counting()   // 统计数量
                    ));
            // 打印词频前10的单词
            wordCount.entrySet().stream()
                    .sorted(Map.Entry.<String, Long>comparingByValue().reversed())
                    .limit(10)
                    .forEach(entry -> 
                        System.out.println(entry.getKey() + ": " + entry.getValue())
                    );
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

知识点整合

常见避坑指南 🚨

坑1:忘记关闭流导致文件锁定

现象:程序运行后无法删除文件,提示"文件正在使用"

解决:始终使用try-with-resources

坑2:字节流读取文本出现乱码

原因:字节流不处理字符编码

解决

// ❌ 错误:用字节流读中文
FileInputStream fis = new FileInputStream("中文.txt");
// ✅ 正确:用字符流或指定编码
BufferedReader br = new BufferedReader(
    new InputStreamReader(new FileInputStream("中文.txt"), "UTF-8")
);
坑3:缓冲流未flush导致数据丢失
BufferedWriter bw = new BufferedWriter(new FileWriter("data.txt"));
bw.write("重要数据");
// 如果程序崩溃,数据可能丢失!
// 解决:手动flush或关闭流
bw.flush();  // 强制刷新缓冲区
坑4:Stream流多次消费
Stream<String> stream = list.stream();
stream.forEach(System.out::println);  // 第一次消费
stream.forEach(System.out::println);  // ❌ 报错:stream已被消费

解决:Stream只能消费一次,需要重新创建。

7. 核心要点总结 📝

Stream流(集合流)核心要点

记住这三步:创建流 → 中间操作(filter/map/sorted)→ 终止操作(collect)

常用API速查

注意事项:Stream只能消费一次,链式调用更高效

IO流核心要点

选择流的黄金法则

四大核心类记忆

场景输入输出
字节流FileInputStreamFileOutputStream
字符流FileReaderFileWriter
缓冲字节流BufferedInputStreamBufferedOutputStream
缓冲字符流BufferedReaderBufferedWriter

资源管理铁律:永远使用try-with-resources,避免内存泄漏

实践建议

  1. 读写文本优先用缓冲字符流,性能好且支持按行操作
  2. 复制文件优先用缓冲字节流,通用性强
  3. 处理集合数据优先用Stream流,代码简洁
  4. 记得指定字符编码,避免中文乱码(推荐UTF-8)
  5. 大文件分块读取,避免内存溢出

结语

掌握Stream流和IO流,你就解锁了Java开发的两大核心技能:

记住:多动手实践,把本文的代码全部运行一遍,你会发现原来流的世界如此简单!

到此这篇关于Java中的Stream流与IO流完整实战指南(从零掌握)的文章就介绍到这了,更多相关java stream流与io流内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文