当前位置: 首页 > news >正文

面试(六)——Java IO 流

       在 Java 编程中,IO 流是连接程序与外部数据载体(文件、网络、内存等)的核心桥梁。多数开发者在使用 IO 流时,常停留在 “调用 API” 的表层阶段,对其底层设计逻辑、数据传输原理及基础组件的核心作用缺乏深入理解。本文将从专业基础视角,逐层拆解 IO 流的核心概念、类结构与底层实现,帮你构建扎实的 IO 流认知体系。

一、IO 流的底层核心概念:理解数据传输的本质

       在深入代码之前,必须先明确 IO 流的三个核心底层概念,这是理解所有 IO 操作的基础。

1. 流的定义:数据的 “管道化传输”

IO 流(Input/Output Stream)本质是数据的有序传输序列,它将数据从 “源”(如文件、键盘、网络)传输到 “目的地”(如内存、文件、控制台),如同一条 “管道”:

  • 输入流(Input Stream):数据从外部载体流入程序内存,是 “读” 操作的载体;
  • 输出流(Output Stream):数据从程序内存流出到外部载体,是 “写” 操作的载体;
  • 流的方向性:IO 流是单向的 —— 输入流只能读、输出流只能写,不存在 “双向流”(需双向传输时需分别创建输入 / 输出流)。

2. 数据单位:字节与字符的底层差异

IO 流分为字节流与字符流,核心差异在于数据传输的最小单位,这直接决定了它们的适用场景:

  • 字节(byte):1 字节 = 8 位(bit),是计算机底层存储的最小单位,可表示所有二进制数据(图片、视频、文本的二进制编码等);
  • 字符(char):1 字符 = 2 字节(Java 中),是文本数据的最小单位,对应 Unicode 编码(可表示中文、英文等各类字符);
  • 关键关联:字符本质是 “字节的语义化封装”—— 文本文件的底层存储仍是字节,字符流通过 “编码表”(如 UTF-8、GBK)将字节转换为字符,避免手动处理编码逻辑。

3. 流的生命周期:从创建到关闭的 “资源管理”

       IO 流属于系统级资源(依赖操作系统的文件句柄、网络端口等),其生命周期必须严格管理,否则会导致资源泄漏:

  • 创建流:通过构造方法关联外部载体(如new FileInputStream("test.txt")关联本地文件);
  • 操作流:调用read()/write()等方法传输数据;
  • 关闭流:调用close()方法释放系统资源(必须执行,即使操作报错);
  • 自动关闭:Java 7 + 的try-with-resources语法可自动关闭实现AutoCloseable接口的流(IO 流均实现此接口),避免手动关闭遗漏。

二、字节流的底层解析:二进制数据的 “原生传输”

       字节流以byte为最小单位,是所有 IO 流的 “基础骨架”,直接操作二进制数据,不涉及编码转换。其核心父类是InputStream(输入字节流)和OutputStream(输出字节流),所有字节流实现类均继承自这两个抽象类。

1. 核心父类:InputStream 与 OutputStream 的抽象定义

       这两个类通过抽象方法定义了字节流的核心行为,子类需根据具体场景(文件、内存、网络)实现这些方法。

(1)InputStream:输入字节流的 “行为规范”

       InputStream是所有输入字节流的父类,核心抽象方法与常用功能如下:

方法签名

核心作用

底层逻辑

abstract int read()

读取 1 个字节,返回字节值(0~255);若已到流末尾,返回 - 1

从关联的外部载体(如文件)读取 1 个字节到内存,阻塞直到有数据或流结束

int read(byte[] b)

读取多个字节到字节数组b,返回实际读取的字节数;末尾返回 - 1

批量读取数据,减少 IO 次数(比单字节读取效率高 10~100 倍)

int read(byte[] b, int off, int len)

读取len个字节到数组b,从索引off开始存储

更灵活的批量读取,避免覆盖数组已有数据

long skip(long n)

跳过n个字节,返回实际跳过的字节数

移动流的 “读取指针”,不读取数据(如跳过文件头部的固定标识)

void close()

关闭流,释放系统资源

通知操作系统释放关联的文件句柄 / 端口,必须调用

关键注意点:read()方法返回的是 “字节的无符号值”(0~255),若直接强转为char可能出现乱码(需通过字符流处理)。

(2)OutputStream:输出字节流的 “行为规范”

       OutputStream是所有输出字节流的父类,核心抽象方法与常用功能如下:

方法签名

核心作用

底层逻辑

abstract void write(int b)

写入 1 个字节(仅取int的低 8 位,高 24 位忽略)

将内存中的 1 个字节写入外部载体,阻塞直到写入完成

void write(byte[] b)

将字节数组b的所有字节写入

批量写入,减少 IO 次数

void write(byte[] b, int off, int len)

将数组b中从off开始的len个字节写入

灵活的批量写入(如只写入数组的部分数据)

void flush()

强制刷新缓冲区,将缓冲中的数据写入外部载体

若流有缓冲区(如BufferedOutputStream),需调用此方法确保数据不滞留

void close()

关闭流,释放系统资源

关闭前会自动调用flush(),确保缓冲数据写入

关键注意点:write(int b)方法接收int类型参数,但仅使用低 8 位(因为 1 字节 = 8 位),例如write(0x1234)实际写入的是0x34(低 8 位)。

2. 基础实现类:FileInputStream 与 FileOutputStream

       这两个类是字节流中最常用的实现,直接关联本地文件,实现文件的二进制读写,是理解 “文件 IO” 的基础。

(1)FileInputStream:读取本地文件的字节流

构造方法(核心):

  • FileInputStream(String name):通过文件路径关联文件(如new FileInputStream("D:/test.dat"));
  • FileInputStream(File file):通过File对象关联文件(更灵活,可先判断文件是否存在)。

底层原理:创建FileInputStream时,会调用操作系统的 “打开文件” 接口(如 Windows 的CreateFile、Linux 的open),获取文件句柄(一个标识文件的整数),后续的read()操作均通过文件句柄与操作系统交互,读取文件的二进制数据到内存。

实战:单字节读取与批量读取的效率对比

public class FileInputStreamDemo {public static void main(String[] args) throws IOException {String filePath = "large_file.bin"; // 100MB的二进制文件// 1. 单字节读取(效率极低)long start1 = System.currentTimeMillis();try (FileInputStream fis = new FileInputStream(filePath)) {int b;while ((b = fis.read()) != -1) {// 仅读取,不处理(模拟空操作)}}System.out.println("单字节读取耗时:" + (System.currentTimeMillis() - start1) + "ms");// 2. 批量读取(效率高)long start2 = System.currentTimeMillis();try (FileInputStream fis = new FileInputStream(filePath)) {byte[] buffer = new byte[8192]; // 8KB缓冲区(推荐大小:4KB~64KB)int len;while ((len = fis.read(buffer)) != -1) {// 仅读取,不处理}}System.out.println("批量读取耗时:" + (System.currentTimeMillis() - start2) + "ms");}
}

运行结果(参考):单字节读取耗时约 5000ms,批量读取耗时约 20ms—— 批量读取通过减少 “用户态与内核态的切换次数”(IO 操作需从用户程序切换到操作系统内核),大幅提升效率。

(2)FileOutputStream:写入本地文件的字节流

构造方法(核心):

  • FileOutputStream(String name):通过路径关联文件,默认 “覆盖写入”(若文件已存在,清空原有内容);
  • FileOutputStream(String name, boolean append):append为true时 “追加写入”(在文件末尾添加数据,不覆盖原有内容);
  • FileOutputStream(File file)/FileOutputStream(File file, boolean append):通过File对象关联文件。

底层原理:创建时同样会获取文件句柄,write()操作通过文件句柄将内存中的字节写入操作系统的 “文件缓冲区”,最终由操作系统异步写入磁盘(若需立即写入磁盘,需调用flush()或使用RandomAccessFile的 “同步写入” 模式)。

实战:追加写入日志文件

public class FileOutputStreamDemo {public static void writeLog(String logContent) throws IOException {// 追加写入日志,避免覆盖历史日志try (FileOutputStream fos = new FileOutputStream("app.log", true)) {// 拼接日志时间与内容String log = LocalDateTime.now() + " - " + logContent + "\n";fos.write(log.getBytes(StandardCharsets.UTF_8)); // 显式指定编码,避免平台默认编码问题}}public static void main(String[] args) throws IOException {writeLog("用户[123]登录成功");writeLog("用户[456]查询数据");}
}

关键细节:getBytes(StandardCharsets.UTF_8)显式指定编码为 UTF-8,避免依赖操作系统的默认编码(如 Windows 默认 GBK、Linux 默认 UTF-8),确保日志在不同平台下读取无乱码。

3. 缓冲优化类:BufferedInputStream 与 BufferedOutputStream

       基础字节流(如FileInputStream)的每次read()/write()都会直接触发系统 IO 操作,而缓冲字节流通过在内存中开辟独立的缓冲区,减少系统 IO 次数,是 “高性能 IO” 的基础。

(1)核心原理:内存缓冲区的 “批量转发”
  • BufferedInputStream:创建时默认分配 8KB 的内存缓冲区,调用read()时,先从缓冲区读取数据;若缓冲区为空,一次性从底层流(如FileInputStream)读取 8KB 数据到缓冲区,再从缓冲区返回数据 —— 原本 1000 次read()操作,只需 1 次系统 IO(若每次读 1 字节,8KB 缓冲区可减少 7999 次系统 IO)。
  • BufferedOutputStream:创建时默认分配 8KB 缓冲区,调用write()时,先将数据写入缓冲区;若缓冲区满,一次性将 8KB 数据写入底层流 —— 同样减少系统 IO 次数。
(2)构造方法与关键方法
  • 构造方法:BufferedInputStream(InputStream in)(默认 8KB 缓冲区)、BufferedInputStream(InputStream in, int size)(自定义缓冲区大小,如new BufferedInputStream(fis, 65536)设置 64KB 缓冲区);
  • 关键方法:flush()(仅BufferedOutputStream需调用,强制将缓冲区数据写入底层流,避免数据滞留)、close()(关闭时会自动调用flush(),无需手动调用,但主动调用更安全)。

实战:缓冲流与基础流的效率对比(复制大文件)

public class BufferedStreamDemo {public static void copyFile(String sourcePath, String targetPath, boolean useBuffer) throws IOException {long start = System.currentTimeMillis();if (useBuffer) {// 使用缓冲流复制try (BufferedInputStream bis = new BufferedInputStream(new FileInputStream(sourcePath));BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(targetPath))) {byte[] buffer = new byte[8192];int len;while ((len = bis.read(buffer)) != -1) {bos.write(buffer, 0, len);}}} else {// 使用基础流复制try (FileInputStream fis = new FileInputStream(sourcePath);FileOutputStream fos = new FileOutputStream(targetPath)) {byte[] buffer = new byte[8192];int len;while ((len = fis.read(buffer)) != -1) {fos.write(buffer, 0, len);}}}System.out.println((useBuffer ? "缓冲流" : "基础流") + "复制耗时:" + (System.currentTimeMillis() - start) + "ms");}public static void main(String[] args) throws IOException {String source = "large_video.mp4"; // 500MB视频文件String target1 = "target1.mp4";String target2 = "target2.mp4";copyFile(source, target1, false); // 基础流复制copyFile(source, target2, true);  // 缓冲流复制}
}

运行结果(参考):基础流复制耗时约 1500ms,缓冲流复制耗时约 100ms—— 缓冲流通过减少系统 IO 次数,将效率提升一个数量级,是处理大文件的 “必备工具”。

(3)缓冲区大小的选择原则
  • 默认 8KB:适用于多数场景(如文本文件、中小型二进制文件);
  • 大文件(100MB+):可将缓冲区调至 64KB~256KB(如new BufferedInputStream(in, 262144)),减少缓冲区满的频率,但需避免过大(如超过 1MB)导致内存浪费;
  • 小文件(1KB 以下):无需自定义缓冲区,默认 8KB 足够(缓冲区过大反而会占用多余内存)。

三、字符流的底层解析:文本数据的 “语义化传输”

       字符流以char为最小单位,专为文本处理设计,核心解决 “字节与字符的编码转换” 问题。其核心父类是Reader(输入字符流)和Writer(输出字符流),所有字符流实现类均继承自这两个抽象类。

1. 核心父类:Reader 与 Writer 的抽象定义

       字符流的父类与字节流结构相似,但方法参数和返回值以char(字符)和char[](字符数组)为主,内置编码转换逻辑。

(1)Reader:输入字符流的 “行为规范”

Reader是所有输入字符流的父类,核心抽象方法与常用功能如下:

方法签名

核心作用

底层逻辑

abstract int read()

读取 1 个字符,返回字符的 Unicode 值(0~65535);末尾返回 - 1

从底层字节流读取字节,通过编码表转换为字符(如 UTF-8 编码:1~4 字节对应 1 个字符)

int read(char[] cbuf)

读取多个字符到字符数组cbuf,返回实际读取的字符数;末尾返回 - 1

批量读取字符,减少编码转换次数

int read(char[] cbuf, int off, int len)

读取len个字符到数组cbuf,从索引off开始存储

灵活的批量读取

long skip(long n)

跳过n个字符,返回实际跳过的字符数

移动字符流的 “读取指针”(需先将字节转换为字符,再跳过)

void close()

关闭流,释放资源

关闭底层字节流,释放编码转换相关的缓冲区

关键差异:与InputStream的read()返回 “字节值(0~255)” 不同,Reader的read()返回 “字符的 Unicode 值(0~65535)”,可直接强转为char使用(如char c = (char) reader.read())。

(2)Writer:输出字符流的 “行为规范”

       Writer 是所有输出字符流的父类,核心抽象方法与常用功能如下:

方法签名

核心作用

底层逻辑

abstract void write(int c)

写入 1 个字符(仅取 int 的低 16 位,因为 1 个 char=16 位)

将字符的 Unicode 编码,通过指定编码表(如 UTF-8)转换为字节数组,再写入底层字节流

void write(char[] cbuf)

将字符数组 cbuf 的所有字符写入

批量转换字符为字节,减少编码转换次数

void write(char[] cbuf, int off, int len)

将数组 cbuf 中从 off 开始的 len 个字符写入

灵活的批量写入(如只写入字符数组的部分数据)

void write(String str)

直接写入字符串(字符流的核心便利)

先将字符串转换为字符数组,再按字符数组写入逻辑处理

void write(String str, int off, int len)

写入字符串 str 从 off 开始的 len 个字符

避免创建完整字符数组,减少内存占用

void flush()

强制刷新缓冲区,将缓冲的字符数据写入底层流

若流有缓冲区(如 BufferedWriter),需调用此方法确保字符数据不滞留

void close()

关闭流,释放资源

关闭前自动调用 flush (),并释放编码转换缓冲区与底层字节流

       关键注意点:write(int c) 接收 int 类型参数,但仅使用低 16 位(对应 1 个 char 的长度),例如 write(0x123456) 实际写入的是 0x3456(低 16 位)对应的字符。

2. 字符流的核心:编码转换原理(字节→字符 / 字符→字节)

       字符流的本质是 “字节流 + 编码表” 的封装,其核心解决的问题是:如何将底层二进制的字节数据,正确转换为人类可理解的文本字符(输入流),以及如何将文本字符转换为二进制字节数据存储 / 传输(输出流)。

(1)编码转换的核心组件:Charset(字符集)

       Java 中通过 java.nio.charset.Charset 类管理编码表,常用字符集包括:

  • UTF-8:国际通用字符集,1 个英文占 1 字节,1 个中文占 3 字节,支持所有 Unicode 字符;
  • GBK:中文专用字符集,1 个英文占 1 字节,1 个中文占 2 字节,不支持其他语言;
  • ISO-8859-1:西欧字符集,仅支持英文等西欧语言,1 个字符占 1 字节,不支持中文(中文会被转为 ?)。

字符流的编码转换逻辑如下:

  • 输入字符流(如 InputStreamReader):底层字节流读取字节数组 → 通过指定 Charset 将字节数组解码为字符数组 → 提供给上层读取;
  • 输出字符流(如 OutputStreamWriter):上层写入字符 / 字符串 → 通过指定 Charset 将字符数组编码为字节数组 → 底层字节流写入外部载体。

(2)编码转换的常见问题:乱码的根源与解决

       乱码的本质是 “编码与解码使用的字符集不统一”,例如:

  • 用 GBK 编码写入的文本,用 UTF-8 解码读取 → 中文会显示为乱码(如 “锘胯揪”);
  • 用 UTF-8 编码写入的文本,用 ISO-8859-1 解码读取 → 中文会显示为 ?。

       解决原则:编码与解码必须使用相同的字符集,且优先使用 UTF-8(跨平台、兼容性强)。

3. 基础实现类:InputStreamReader 与 OutputStreamWriter(字节流→字符流的桥梁)

       FileReader 与 FileWriter 是字符流的常用实现,但它们本质是 InputStreamReader 与 OutputStreamWriter 的 “简化版”(默认使用系统编码)。而 InputStreamReader 与 OutputStreamWriter 是真正的 “字节流→字符流桥梁”,支持显式指定字符集,是专业开发的首选。

(1)InputStreamReader:字节输入流→字符输入流的转换

  • 核心作用:将底层字节输入流(如 FileInputStream)的字节数据,通过指定字符集解码为字符数据,供上层按字符读取;
  • 构造方法
    • InputStreamReader(InputStream in):默认使用系统编码(不推荐,易导致跨平台乱码);
    • InputStreamReader(InputStream in, Charset cs):显式指定字符集(推荐,如 Charset.forName("UTF-8"));
    • InputStreamReader(InputStream in, String charsetName):通过字符集名称指定(如 "UTF-8")。

实战:指定 UTF-8 编码读取 GBK 文本(模拟乱码与解决)

public class InputStreamReaderDemo {public static void main(String[] args) throws IOException {String gbkFilePath = "gbk_text.txt"; // 用 GBK 编码保存的文本文件(内容:"你好,Java")// 1. 错误示例:用 UTF-8 解码 GBK 文本(导致乱码)try (InputStreamReader isrError = new InputStreamReader(new FileInputStream(gbkFilePath), StandardCharsets.UTF_8)) {char[] buffer = new char[1024];int len = isrError.read(buffer);System.out.println("UTF-8 解码 GBK 文本(乱码):" + new String(buffer, 0, len));// 输出:UTF-8 解码 GBK 文本(乱码):浣犲ソ锛孒ava}// 2. 正确示例:用 GBK 解码 GBK 文本(正常显示)try (InputStreamReader isrCorrect = new InputStreamReader(new FileInputStream(gbkFilePath), "GBK")) {char[] buffer = new char[1024];int len = isrCorrect.read(buffer);System.out.println("GBK 解码 GBK 文本(正常):" + new String(buffer, 0, len));// 输出:GBK 解码 GBK 文本(正常):你好,Java}}
}

(2)OutputStreamWriter:字符输出流→字节输出流的转换

  • 核心作用:将上层写入的字符数据,通过指定字符集编码为字节数据,再交给底层字节输出流(如 FileOutputStream)写入外部载体;
  • 构造方法:与 InputStreamReader 对应,支持显式指定字符集(推荐 Charset 或字符集名称)。

实战:用 UTF-8 编码写入多语言文本(确保跨平台兼容)

public class OutputStreamWriterDemo {public static void writeMultiLangText(String filePath) throws IOException {// 显式指定 UTF-8 编码,支持中文、英文、日文try (OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(filePath), StandardCharsets.UTF_8)) {osw.write("中文:你好,Java IO 流\n");osw.write("English: Hello, Java IO Stream\n");osw.write("日文:こんにちは、Java IO ストリーム\n");osw.flush(); // 缓冲数据写入,确保即时生效}}public static void main(String[] args) throws IOException {String multiLangPath = "multi_lang.txt";writeMultiLangText(multiLangPath);// 验证读取(同样用 UTF-8 解码)try (InputStreamReader isr = new InputStreamReader(new FileInputStream(multiLangPath), StandardCharsets.UTF_8)) {char[] buffer = new char[1024];int len = isr.read(buffer);System.out.println("读取多语言文本:\n" + new String(buffer, 0, len));}}
}

       关键细节:StandardCharsets.UTF_8 是 Java 7+ 提供的常量,比手动写字符串 "UTF-8" 更安全(避免拼写错误,如 "UTF8" 或 "utf-8")。

4. 简化实现类:FileReader 与 FileWriter(默认系统编码的便捷类)

       FileReader 继承自 InputStreamReader,FileWriter 继承自 OutputStreamWriter,它们的核心特点是 “默认使用系统编码”,简化了文本文件的读写代码,但存在跨平台乱码风险。

(1)核心局限性

  • 编码不可控:默认使用 Charset.defaultCharset()(系统编码),例如 Windows 系统默认 GBK,Linux/macOS 系统默认 UTF-8;
  • 乱码风险高:在 Windows 用 FileWriter 写入的文本,复制到 Linux 用 FileReader 读取,会因编码不统一导致乱码。

(2)适用场景

       仅适用于 “本地单机、无需跨平台” 的简单文本处理(如临时日志、本地配置文件),专业开发中优先使用 InputStreamReader/OutputStreamWriter 并显式指定字符集。

示例:FileReader 读取本地文本(简单场景)

public class FileReaderDemo {public static void main(String[] args) throws IOException {// 本地临时文本文件(仅在当前系统使用)try (FileReader fr = new FileReader("local_temp.txt");BufferedReader br = new BufferedReader(fr)) { // 结合缓冲流提升效率String line;while ((line = br.readLine()) != null) {System.out.println("本地文本内容:" + line);}}}
}

5. 缓冲优化类:BufferedReader 与 BufferedWriter(字符流的性能加速器)

       与字节流的缓冲类类似,BufferedReader 与 BufferedWriter 通过在内存中开辟字符缓冲区,减少编码转换与系统 IO 次数,同时提供了文本处理的便捷方法(如 readLine() 读取整行文本)。

(1)核心原理

  • BufferedReader:默认分配 8KB 字符缓冲区,调用 read() 时先从缓冲区读取字符;若缓冲区为空,一次性从底层字符流(如 InputStreamReader)读取批量字符到缓冲区,减少编码转换次数;
  • BufferedWriter:默认分配 8KB 字符缓冲区,调用 write() 时先将字符写入缓冲区;若缓冲区满,一次性将字符编码为字节并写入底层流,减少系统 IO 次数。

(2)核心便捷方法

  • BufferedReader.readLine():读取整行文本(以 \n、\r\n 或流末尾为换行标识),返回该行字符串(不含换行符);若已到流末尾,返回 null(文本处理的核心高效方法);
  • BufferedWriter.newLine():写入与平台无关的换行符(Windows 写入 \r\n,Linux/macOS 写入 \n),避免手动处理跨平台换行问题。

实战:用缓冲字符流处理大文本文件(按行读取并过滤内容)

public class BufferedCharStreamDemo {// 读取大文本文件,过滤包含 "ERROR" 的日志行并保存public static void filterErrorLogs(String sourcePath, String targetPath) throws IOException {try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(sourcePath), StandardCharsets.UTF_8));BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetPath), StandardCharsets.UTF_8))) {String line;// 按行读取(效率远高于单字符读取)while ((line = br.readLine()) != null) {// 过滤包含 "ERROR" 的行if (line.contains("ERROR")) {bw.write(line);bw.newLine(); // 跨平台换行}}bw.flush(); // 确保缓冲数据写入目标文件}}public static void main(String[] args) throws IOException {String largeLogPath = "app_large.log"; // 1GB 大日志文件String errorLogPath = "error_only.log";long start = System.currentTimeMillis();filterErrorLogs(largeLogPath, errorLogPath);System.out.println("过滤完成,耗时:" + (System.currentTimeMillis() - start) + "ms");// 输出:过滤完成,耗时:约 2000ms(缓冲流高效处理大文本)}
}

(3)性能优化建议

  • 缓冲区大小:默认 8KB 适用于多数文本场景,处理超大型文本(10GB+)可将缓冲区调至 64KB~256KB(如 new BufferedReader(reader, 65536));
  • 避免频繁转换:尽量使用 readLine() 按行处理,避免将 char[] 频繁转换为 String(减少内存开销);
  • 批量写入:若需写入大量文本,可先拼接为 StringBuilder,再一次性 write()(减少 write() 调用次数)。

四、字节流与字符流的核心区别与选择原则

       通过前面的底层解析,我们可以总结出字节流与字符流的核心差异,并明确不同场景下的选择依据。

1. 核心区别对比

对比维度

字节流(InputStream/OutputStream)

字符流(Reader/Writer)

数据单位

字节(byte,8 位)

字符(char,16 位,Unicode 编码)

编码处理

不处理编码,直接传输字节

内置编码转换(需指定字符集)

核心作用

处理所有二进制数据(图片、视频、文本的二进制形式)

仅处理文本数据(.txt、.log、.properties 等)

关键方法

read(byte[])、write(byte[])

read(char[])、write(char[])、readLine()

底层依赖

直接依赖操作系统的字节 IO 接口

依赖字节流 + 字符集编码表

2. 选择原则(开发实战指南)

(1)优先判断数据类型

  • 若处理二进制数据(图片、视频、音频、可执行文件)→ 必须用字节流;
  • 若处理文本数据(无论何种语言)→ 必须用字符流(避免手动编码转换,减少乱码);

(2)字符流必须显式指定编码

  • 禁止使用 FileReader/FileWriter(默认系统编码),必须用 InputStreamReader/OutputStreamWriter 并指定 UTF-8;

(3)大文件必须用缓冲流

  • 字节流用 BufferedInputStream/BufferedOutputStream;
  • 字符流用 BufferedReader/BufferedWriter;

(4)避免混合使用

  • 同一文件不建议同时用字节流和字符流操作(可能导致文件指针混乱,数据读写异常)。

五、总结:IO 流的底层逻辑与实践闭环

Java IO 流的设计本质是 “分层封装”:

  • 底层:字节流直接操作二进制数据,对接操作系统 IO 接口,是所有 IO 操作的基础;
  • 上层:字符流封装字节流 + 编码表,解决文本处理的语义化问题;
  • 优化层:缓冲流通过内存缓冲区,减少系统 IO 与编码转换次数,提升性能。

掌握 IO 流的核心在于:

  • 明确数据类型:二进制用字节流,文本用字符流;
  • 控制编码统一:字符流必须显式指定 UTF-8,避免乱码;
  • 优先使用缓冲:大文件 / 频繁读写场景,缓冲流是性能关键;
  • 规范资源管理:始终用 try-with-resources 自动关闭流,避免资源泄漏。

       通过本文的底层解析与实战案例,希望你能跳出 “API 调用” 的表层认知,建立起 IO 流的 “原理→实践→优化” 完整认知体系,在实际开发中能根据场景灵活选择流的组合,写出高效、健壮的 IO 操作代码。

http://www.dtcms.com/a/512324.html

相关文章:

  • 怎么做视频网站教程php彩票网站建设教程
  • 大模型(Large Language Model, LLM)——什么是大模型,大模型的基本原理、架构、流程
  • 长春网站建设排名怎样用自己电脑做网站
  • 基于 Redis 的基数统计:高效的大规模去重与计数
  • 机械外贸网站站长网站工具
  • 广州企业建站素材安徽禹尧工程建设有限公司网站
  • MySQL if函数
  • Promise.all怎么用
  • 成都网站建设开发价玉环哪里有做网站
  • 01)mysql数据误删恢复相关-mysql5.7 开启 binlog、设置binlog 保留时间
  • 电力电子技术 第五章——非连续导电模式
  • Django 项目 .gitignore 模板
  • MySQL 中文排序(拼音排序)不生效问题全解析
  • 建站网络公司云南网站备案难吗
  • 深度学习(8)- PyTorch 数据处理与加载
  • JAVA:Spring Boot 集成 Jackson 实现高效 JSON 处理
  • 深度学习之YOLO系列YOLOv4
  • 江西移动网站建站推广外包
  • 张家口网站建设zjktao温州公司网址公司
  • Cef笔记:Cef消息循环的集成
  • 第十六篇:Lambda表达式:匿名函数对象的艺术
  • 织梦cms通用蓝白简介大气企业网站环保科技公司源码汕头网站制作全过程
  • xss-labs pass-06
  • 解决selenium提示chrome版本过低问题
  • 重庆做网站电话深圳做装修网站费用多少
  • 做网站的免费空间商品房交易网
  • 鸡蛋质量识别数据集,可识别染血的鸡蛋,棕色鸡蛋,钙沉积鸡蛋,污垢染色的鸡蛋,白鸡蛋,平均正确识别率可达89%,支持yolo, json, xml格式的标注
  • YOLOv4简单基础学习
  • 网站的域名每年都要续费南通网络科技有限公司
  • LLAMA-Factory Qwen3-1.7b模型微调