Java 基础(九) IO流

Java IO流核心知识点与实战总结(学习笔记)

在Java编程中,IO(Input/Output)流是实现程序与外部设备(文件、网络、控制台等)之间数据传输的核心机制。无论是读取配置文件、写入日志,还是网络通信,都离不开IO流的支持。本文结合课堂笔记和实战代码,系统梳理IO流的分类、核心类、常用方法及异常处理,补充关键细节和最佳实践,帮助你快速掌握Java IO编程。

一、IO流的基本概念与分类

1. 什么是流

流(Stream)是一组有序的数据序列,它将数据从一个地方传输到另一个地方。我们可以把流想象成"水管",数据就是水管里的水,输入流是从外部流向程序,输出流是从程序流向外部。

2. 流的核心分类

Java IO流主要按照数据类型流向两个维度进行分类:

分类维度 类型 说明 适用场景
数据类型 字节流 以8位字节为单位处理数据,处理所有类型的文件 二进制文件(图片、音频、视频、exe等)
字符流 以16位字符为单位处理数据,基于Unicode编码 文本文件(txt、java、html等)
流向 输入流 从外部数据源读取数据到程序 读取文件、接收网络数据
输出流 将程序中的数据写入到外部目标 写入文件、发送网络数据
功能 节点流 直接操作数据源的流(如FileInputStream) 基础数据读写
处理流 包装节点流,提供额外功能(如BufferedReader) 提高效率、增强功能

3. IO流核心类层次结构

Java IO流的所有类都继承自以下四个抽象基类:

复制代码
字节流                          字符流
InputStream(输入)             Reader(输入)
├─ FileInputStream             ├─ FileReader
├─ BufferedInputStream         ├─ InputStreamReader(转换流)
├─ ZipInputStream              ├─ BufferedReader(缓冲流)
└─ ObjectInputStream           └─ StringReader

OutputStream(输出)            Writer(输出)
├─ FileOutputStream            ├─ FileWriter
├─ BufferedOutputStream        ├─ OutputStreamWriter(转换流)
├─ ZipOutputStream             ├─ BufferedWriter(缓冲流)
├─ ObjectOutputStream          ├─ PrintWriter(打印流)
└─ PrintStream

二、字符流:专门处理文本数据

字符流专门用于处理文本文件,它会自动处理字符编码转换,避免乱码问题。所有字符流都继承自Reader(输入)和Writer(输出)抽象基类。

2.1 字符输入流(Reader)

字符输入流用于从文本数据源中读取字符数据,常用实现类有三个:

类名 作用 特点
FileReader 从文件中读取字符 便捷类,使用系统默认编码
InputStreamReader 字节流转字符流的桥梁 可以指定字符编码,解决乱码问题
BufferedReader 带缓冲的字符输入流 提供按行读取功能,大幅提高读取效率
实战代码:三种字符输入流的使用
java 复制代码
import java.io.*;

public class ReaderExample {
    public static void main(String[] args) {
        // 1. FileReader:逐个字符读取(简单但效率低)
        try (FileReader reader = new FileReader("D:\\test\\a.txt")) {
            int data;
            // read()返回-1表示到达流末尾
            while ((data = reader.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 2. InputStreamReader:指定编码读取(解决乱码)
        try (InputStreamReader reader = new InputStreamReader(
                new FileInputStream("D:\\test\\a.txt"), "UTF-8")) {
            int data;
            while ((data = reader.read()) != -1) {
                System.out.print((char) data);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. BufferedReader:按行读取(推荐,效率最高)
        try (BufferedReader br = new BufferedReader(
                new FileReader("D:\\test\\a.txt"))) {
            String line;
            // readLine()返回null表示到达流末尾
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

2.2 字符输出流(Writer)

字符输出流用于将字符数据写入文本目标,常用实现类与输入流对应:

类名 作用 特点
FileWriter 向文件中写入字符 便捷类,使用系统默认编码
OutputStreamWriter 字符流转字节流的桥梁 可以指定字符编码
BufferedWriter 带缓冲的字符输出流 提供换行方法,提高写入效率
Writer的五种写入方法

Writer类提供了五种重载的write方法,满足不同的写入需求:

方法签名 功能说明
write(int c) 写入单个字符(低16位有效)
write(char[] cbuf) 写入整个字符数组
write(char[] cbuf, int off, int len) 写入字符数组的一部分(从off开始,共len个字符)
write(String str) 写入整个字符串
write(String str, int off, int len) 写入字符串的一部分(从off开始,共len个字符)
实战代码:字符输出流的使用
java 复制代码
import java.io.*;

public class WriterExample {
    public static void main(String[] args) {
        // 1. FileWriter:基础写入
        try (FileWriter writer = new FileWriter("output.txt")) {
            writer.write("Hello, World!");
            writer.write("\n这是新的一行"); // \n是换行符
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 2. BufferedWriter:带缓冲写入(推荐)
        try (BufferedWriter bw = new BufferedWriter(
                new FileWriter("output.txt", true))) { // true表示追加写入
            bw.write("Hello, Java IO!");
            bw.newLine(); // 跨平台换行符(推荐使用)
            bw.write("这是追加的内容");
        } catch (IOException e) {
            e.printStackTrace();
        }

        // 3. 五种写入方法演示
        try (Writer writer = new FileWriter("writer_demo.txt")) {
            writer.write('H'); // 单个字符
            char[] chars = {'e', 'l', 'l', 'o'};
            writer.write(chars); // 整个字符数组
            writer.write(chars, 1, 2); // 字符数组的一部分(ll)
            writer.write(", World!"); // 整个字符串
            writer.write("\nThis is Java IO", 0, 10); // 字符串的一部分(This is Ja)
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

重要提示

  • FileWriter的构造方法第二个参数append设为true时,会在文件末尾追加内容,否则会覆盖原有内容。
  • 使用newLine()方法代替\n,可以实现跨平台的换行(Windows是\r\n,Linux是\n)。

三、字节流:处理所有类型的数据

字节流是最基础的流,它以字节为单位处理数据,可以处理任何类型的文件(文本和二进制)。所有字节流都继承自InputStream(输入)和OutputStream(输出)抽象基类。

3.1 字节输入流(InputStream)

InputStream定义了字节输入流的基本方法:

  • int read():读取一个字节,返回0-255的整数,到达末尾返回-1
  • int read(byte[] b):读取最多b.length个字节到数组,返回实际读取的字节数
  • int read(byte[] b, int off, int len):读取最多len个字节到数组,从off位置开始存储
  • void close():关闭流,释放资源

最常用的实现类是FileInputStream,用于从文件中读取字节数据。

实战代码:字节输入流读取文件
java 复制代码
import java.io.*;

public class FileInputStreamExample {
    public static void main(String[] args) {
        // 推荐使用try-with-resources自动关闭流
        try (FileInputStream fis = new FileInputStream("D:\\test\\test.jpg")) {
            byte[] buffer = new byte[1024]; // 1KB缓冲区
            int len;
            while ((len = fis.read(buffer)) != -1) {
                // 处理读取到的字节数据(这里只是打印长度)
                System.out.println("读取了" + len + "个字节");
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

最佳实践:使用字节数组作为缓冲区(通常大小为1024或其倍数),可以大幅提高读取效率,避免频繁的磁盘IO操作。

3.2 字节输出流(OutputStream)

OutputStream定义了字节输出流的基本方法:

  • void write(int b):写入一个字节
  • void write(byte[] b):写入整个字节数组
  • void write(byte[] b, int off, int len):写入字节数组的一部分
  • void close():关闭流,释放资源
  • void flush():刷新缓冲区,将数据立即写入目标

最常用的实现类是FileOutputStream,用于向文件中写入字节数据。

实战代码:字节输出流写入文件
java 复制代码
import java.io.*;

public class FileOutputStreamExample {
    public static void main(String[] args) {
        try (FileOutputStream fos = new FileOutputStream("byte_output.txt")) {
            fos.write(97); // 写入字节'a'
            fos.write("Hello, 字节流!".getBytes()); // 写入字符串的字节数组
            fos.write("ABCDEFG".getBytes(), 2, 3); // 写入"CDE"
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

四、IO流中的异常处理

IO操作几乎都会抛出受检异常(IOException及其子类),必须显式处理。Java提供了两种异常处理方式:

4.1 传统方式:try-catch-finally

finally块中关闭流,确保无论是否发生异常,资源都会被释放。

java 复制代码
import java.io.*;

public class TraditionalExceptionHandling {
    public static void main(String[] args) {
        FileInputStream fis = null;
        try {
            fis = new FileInputStream("test.txt");
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                System.out.println(new String(buffer, 0, len));
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关闭流前必须判断是否为null
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

4.2 推荐方式:try-with-resources(Java 7+)

try-with-resources语法会自动关闭实现了AutoCloseable接口的资源(所有IO流都实现了该接口),代码更简洁、更安全。

java 复制代码
import java.io.*;

public class TryWithResourcesExample {
    public static void main(String[] args) {
        // 资源在try括号中声明,会自动关闭
        try (FileInputStream fis = new FileInputStream("test.txt");
             FileOutputStream fos = new FileOutputStream("copy.txt")) {
            
            byte[] buffer = new byte[1024];
            int len;
            while ((len = fis.read(buffer)) != -1) {
                fos.write(buffer, 0, len);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

重要提示

  • 多个资源之间用分号分隔,会按照声明的逆序自动关闭。
  • 这是Java IO编程的最佳实践,强烈推荐使用。

五、关键补充知识点

5.1 字符编码与乱码问题

乱码的根本原因是编码和解码使用的字符集不一致。解决方法:

  • 使用InputStreamReaderOutputStreamWriter显式指定编码(如UTF-8)。
  • 避免使用FileReaderFileWriter处理跨平台的文本文件,因为它们使用系统默认编码。

5.2 缓冲流的工作原理

缓冲流内部维护了一个缓冲区(默认大小8KB),读写数据时先操作缓冲区,当缓冲区满了或流关闭时,才会进行实际的磁盘IO操作。这大大减少了IO次数,提高了性能。

性能对比:使用缓冲流读写文件的速度通常是普通流的10倍以上。

5.3 字节流与字符流的选择原则

  • 处理文本文件 :优先使用字符流(Reader/Writer),自动处理编码。
  • 处理二进制文件 (图片、音频、视频等):必须使用字节流(InputStream/OutputStream),否则会损坏文件。
  • 不确定文件类型:使用字节流,它可以处理所有类型的数据。

5.4 其他常用流简介

  • 打印流PrintStreamPrintWriter,提供了丰富的打印方法(print()println()),System.out就是PrintStream的实例。
  • 对象流ObjectInputStreamObjectOutputStream,用于实现对象的序列化和反序列化。
  • 压缩流ZipInputStreamZipOutputStream,用于处理ZIP压缩文件。

六、总结

Java IO流是编程中不可或缺的部分,掌握它的核心要点:

  1. 分类清晰:按数据类型分为字节流和字符流,按流向分为输入流和输出流。
  2. 抽象基类 :所有流都继承自InputStreamOutputStreamReaderWriter
  3. 缓冲优先 :使用缓冲流(BufferedXxx)可以大幅提高IO性能。
  4. 异常处理 :优先使用try-with-resources自动关闭资源,避免资源泄漏。
  5. 编码问题:处理文本文件时显式指定编码,避免乱码。
相关推荐
_Evan_Yao2 小时前
缓存金字塔上的红色闪电:Redis 如何借力 CPU 的 L1/L2/L3 与 TLB 飞驰
java·数据库·redis·后端·缓存
Teable任意门互动2 小时前
多维表格哪家最好用最容易上手?国产开源 Teable 测评
开发语言·数据库·开源·excel·飞书·开源软件
独隅2 小时前
此电脑网络位置异常的AD域排错指南
开发语言·php
陈天伟教授2 小时前
GPT Image 2
开发语言·人工智能·架构
他是龙5512 小时前
68:Java 原生反序列化 & SpringBoot 攻防
java·开发语言·spring boot
西岭千秋雪_2 小时前
终战诏书.
java
嘻嘻哈哈樱桃2 小时前
牛客经典101题题解集--二叉树
java·数据结构·python·算法·leetcode·职场和发展
cen__y2 小时前
Linux05(管道)
linux·运维·服务器·c语言·开发语言·文件流
Frank学习路上2 小时前
【Python】应用:发布pyproject.toml格式包到 PyPI
开发语言·chrome·python