news 2026/4/16 18:22:41

字节流与字符流读写

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
字节流与字符流读写

一、字节流读写

单位字节 (byte),即 8 位二进制数。

读取方式:它不管你读的是什么文件(是图片、视频还是文本),它都把内容当成一串原始的二进制数据来搬运。

你的代码inputStream.read(bytes)读取的就是纯粹的字节。如果你用System.out.printf("0x%02X", bytes[i])打印,看到的是内存中的真实二进制数据。

适用:所有类型的文件(万能)。

使用 Java 字节流读取任何二进制数据

法一:

public class Demo7_1 { public static void main(String[] args) throws IOException { //选择合适的 InputStream 子类(如 FileInputStream),并指定数据源。 InputStream inputStream = new FileInputStream("./1.txt"); //循环读取数据 while(true) { int data = inputStream.read(); if(data == -1) { break; } //以 16 进制格式化打印读取数据 System.out.printf("0x%X\n",data); } 关闭流(释放资源) inputStream.close(); } }

以上代码就实现了从 1.txt 文件中读取字节数据的功能,我们来刨析一下:

1、为什么要 使用 InputStream inputStream = new FileInputStream("./1.txt");

“左边写父类(或接口),右边写具体的实现类” —— 这是写出高质量、可扩展 Java 代码的基本功。

(1)、语法层面:向上转型(Upcasting)

选择合适的 InputStream 子类(如 FileInputStream),并指定数据源。 InputStream inputStream = new FileInputStream("./1.txt"); 是一种典型的 “父类引用指向子类对象” 的写法,体现了 Java 中 多态(Polymorphism) 和 面向抽象编程 的核心思想FileInputStreamInputStream的子类。将子类对象赋值给父类类型的变量,称为 向上转型。这在 Java 中是自动且安全的,不需要强制类型转换。

// 向上转型(隐式) InputStream inputStream = new FileInputStream("..."); // ✅ 合法 // 向下转型(需显式强转,且有风险) FileInputStream fis = (FileInputStream) inputStream; // ⚠️ 需确保类型匹配

(2)、设计层面:面向接口/抽象类编程

虽然InputStream是一个 抽象类(不是接口),但它扮演了“通用输入流”的角色。这种写法的好处是:

  • 代码更通用、灵活

你后续调用的方法(如read()close())都是通过InputStream定义的通用接口进行的。

这意味着:将来可以轻松替换数据源,而无需修改使用逻辑。

// 今天读文件 InputStream in = new FileInputStream("data.txt"); // 明天读网络 InputStream in = new SocketInputStream(socket); // 后天读内存 InputStream in = new ByteArrayInputStream(bytes);

→ 只要右边换一个InputStream的实现类,左边和后续代码完全不用动

  • 解耦与可维护性高

你的业务逻辑只依赖于“能读字节”这个能力(InputStream抽象),而不关心底层是文件、网络还是内存。这符合 “依赖倒置原则”(DIP)。

  • 便于组合与装饰(Decorator 模式)

Java I/O 流大量使用装饰器模式,这种写法天然支持:

InputStream in = new BufferedInputStream( new FileInputStream("large.txt") );

这里BufferedInputStream包装了FileInputStream,但对外仍表现为InputStream,使用方式不变

2、int data = inputStream.read();

是 Java 字节流(InputStream)中最基础、最核心的单字节读取操作。它的作用是从输入流中读取下一个字节的数据,并以int类型返回。

(1)、返回值含义

⚠️ 注意:虽然读的是byte(有符号,-128~127),但返回int是为了能用-1表示结束,同时避免负数字节(如0xFF= -1)与结束标志混淆。

(2)为什么返回int而不是byte

如果返回byte,那么当读到字节值为-1(即0xFF)时,程序无法区分这是“有效数据”还是“流结束”。使用int后:有效字节被提升为0~255的正整数;-1专用于表示“结束”,无歧义。

3、关闭字节流

inputStream.close();是 Java I/O 操作中释放系统资源的关键一步。它的作用是关闭输入流,并释放与该流关联的所有底层系统资源(如文件句柄、网络连接、内存缓冲区等)。

为什么必须调用close()

  1. 防止资源泄漏(Resource Leak)

    • 每打开一个文件,操作系统会分配一个 文件描述符(File Descriptor)。

    • 如果不关闭流,这些描述符会一直被占用,直到程序退出。

    • 操作系统对单个进程能打开的文件数有限制(如 Linux 默认 1024),资源耗尽会导致程序崩溃(抛出IOException: Too many open files)。

  2. 确保数据完整性(对输出流更重要)

    • 虽然InputStream主要是读取,但某些流(如带缓冲的BufferedInputStream)可能在内部维护状态,及时关闭有助于清理。

  3. 符合“谁打开,谁关闭”原则

    • 良好的编程习惯:打开资源后,必须显式或隐式地关闭它。

法二

public class Demo7_2 { public static void main(String[] args) throws IOException { InputStream inputStream = new FileInputStream("./1.txt"); //通过 read 读取数据,一次就读一个字节数组 while(true) { //长度随便定,无所谓 byte[] bytes = new byte[1024]; //read 方法会尽可能的把参数的数组填满 //返回值表示实际读取到的字节数 int n = inputStream.read(bytes); if(n == -1) { break; } for (int i = 0; i < n; i++) { System.out.printf("0x%X\n",bytes[i]); } } //若上面代码出现异常,close() 可能执行不到 inputStream.close(); } }

法二与法一的区别是,这使用字节流逐块读取文件并以十六进制形式打印每个字节

4、int n = inputStream.read(bytes);

是 Java 字节流(InputStream)中“批量读取”数据的标准写法,属于 I/O 编程中最核心、最常用的模式之一。

  • 参数byte[] b—— 一个字节数组,作为缓冲区(buffer),用于接收读取到的数据。
  • 返回值int—— 表示实际读取到的字节数(不是数组长度!)。

✅ 所以int n = inputStream.read(bytes);的意思是:
“尝试从输入流中读取最多bytes.length个字节,存入bytes数组,并把实际读到的字节数赋给变量n。”

✅ 具体过程(以文件大小 > 1024 字节为例)

假设文件总长度为2500 字节,你使用byte[1024]缓冲区:

循环次数调用read(bytes)实际返回值n读取的字节范围流内部指针位置
第 1 次读取前 1024 字节n = 1024字节 0 ~ 1023指向第1024字节(即下一次从 1024 开始)
第 2 次读取接下来的 1024 字节n = 1024字节 1024 ~ 2047指向第2048字节
第 3 次读取剩余 452 字节n = 452字节 2048 ~ 2499指向文件末尾(EOF)
第 4 次尝试再读n = -1无数据已到末尾,返回 -1

法三:

public class Demo7_3 { public static void main(String[] args) throws IOException { InputStream inputStream = null; try { inputStream = new FileInputStream("./1.txt"); //通过 read 读取数据,一次就读一个字节数组 while(true) { //长度随便定,无所谓 byte[] bytes = new byte[1024]; //read 方法会尽可能的把参数的数组填满 //返回值表示实际读取到的字节数 int n = inputStream.read(bytes); if(n == -1) { break; } for (int i = 0; i < n; i++) { System.out.printf("0x%X\n",bytes[i]); } } } finally { //关闭文件 inputStream.close(); } } }

法三保证了inputStream.close()被执行

5、保证inputStream.close()被执行

在法二中,虽然方法声明了throws IOException,但read()printf()在循环中仍可能抛出异常。一旦异常抛出,程序会直接跳转到调用栈上层,跳过close()语句。结果:文件句柄未释放 → 资源泄漏(Resource Leak)。

法四:

public class Demo8 { // try with resource public static void main(String[] args) { try(InputStream inputStream = new FileInputStream("./1.txt")) { while(true) { byte[] bytes = new byte[1024]; int n = inputStream.read(bytes); if(n == -1) { break; } for (int i = 0; i < n; i++) { System.out.printf("0x%X\n",bytes[i]); } } // close 不必写了 //会在 try 结束的时候,自动调用 close() }catch (IOException e) { e.printStackTrace(); } } }

6、使用try-with-resources确保资源释放

try-with-resources是 Java 7 引入的一种自动资源管理(Automatic Resource Management, ARM) 语法,用于确保每个打开的资源在使用完毕后都能被正确关闭,即使发生异常也不会泄漏资源。

“任何实现了Closeable/AutoCloseable的资源,都必须用try-with-resources管理!

✅ 基本语法

try (ResourceType resource = new ResourceType(...)) { // 使用 resource 的代码 } catch (ExceptionType e) { // 处理异常 } // resource 在此处自动关闭(无论是否抛出异常)

前提:资源类必须实现java.lang.AutoCloseable接口(Closeable是其子接口)。
Java I/O 中的所有流(如FileInputStreamFileReader)都实现了该接口。

7、3 种read方法

1、int read()

  • 读取单个字节(8 位)。

  • 返回该字节的 无符号整数值(0 ~ 255)。

  • 如果已到达流末尾(EOF),返回-1

2、int read(byte[] b)

  • 尝试读取 最多b.length个字节 到数组b中。

  • 返回 实际读取的字节数(0 ≤ n ≤ b.length)。

  • 如果已到流末尾,返回-1

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

  • 从输入流中读取 最多len个字节,存入数组b的 从索引off开始的位置。

  • 返回 实际读取的字节数。

  • 流结束时返回-1

使用 Java 字节流写任何二进制数据

法一:

public class Demo9_1 { public static void main(String[] args) { //如果没有 true 会把原来的内容清空再写 try(OutputStream outputStream = new FileOutputStream("./1.txt")) { // 97 十进制,表示 a outputStream.write(97); outputStream.write(98); outputStream.write(99); } catch (IOException e) { e.printStackTrace(); } } }

法二:

public class Demo9_2 { public static void main(String[] args) { //加了 true, 实现了追加写的作业,原来的内容不消失 try(OutputStream outputStream = new FileOutputStream("./1.txt",true)) { // 97 十进制,表示 a outputStream.write(97); outputStream.write(98); outputStream.write(99); } catch (IOException e) { e.printStackTrace(); } } }

法三:

public class Demo9_3 { public static void main(String[] args) { try(OutputStream outputStream = new FileOutputStream("./1.txt",true)) { byte[] bytes = {97,98,99}; outputStream.write(bytes); }catch (IOException e) { e.printStackTrace(); } } }

法四:

ublic class Demo9_4 { public static void main(String[] args) { try (OutputStream outputStream = new FileOutputStream("./1.txt",true)) { byte[] bytes = {97,98,99,100,101,102,103,104,105,106}; outputStream.write(bytes,2,3); } catch (IOException e) { e.printStackTrace(); } } }

二、字符流读写

单位字符 (char),即 16 位 Unicode 字符。

读取方式:它专门用于读取文本。它在底层其实也是读取字节,但它会根据指定的编码规则(如 UTF-8、GBK),自动将“字节”翻译成“人类可读的字符”。

适用:纯文本文件(.txt, .java, .html 等)。

使用字符流读取数据

public class Demo10 { public static void main(String[] args) { try(Reader reader = new FileReader("./1.txt")) { //使用 read 方法读取数据 // while(true) { // int data = reader.read(); // if(data == -1) { // break; // } // char c = (char)data; // System.out.println(c); // } while(true) { char[] chars = new char[1024]; int n = reader.read(chars); if(n == -1) { break; } for (int i = 0; i < n; i++) { System.out.println(chars[i]); } } } catch(IOException e) { e.printStackTrace(); } } }

1.int read()

这是最基本的方法,用于逐个字符读取。

2.int read(char[] cbuf)

此方法通过字符数组进行批量读取,能显著提升读取效率。

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

这是最灵活的重载方法,允许将字符读入数组的指定部分。

使用字符流写数据

public class Demo11 { public static void main(String[] args) { try(Writer writer = new FileWriter("./1.txt",true)) { writer.write('我'); writer.write('真'); writer.write('美'); char[] chars = {'我','真','美'}; writer.write(chars); String s = "你也挺美的"; writer.write(s); }catch (IOException e) { e.printStackTrace(); } } }

1、void write(int c)

  • 功能:写入单个字符。参数c是一个表示字符的整数(0-65535,对应Unicode字符)。

  • 示例

    writer.write(65); // 写入字符 'A' writer.write('H'); // 直接写入字符 'H'

2、void write(char[] cbuf)

  • 功能:写入整个字符数组cbuf中的所有字符。
  • 示例
    char[] chars = {'H', 'e', 'l', 'l', 'o'}; writer.write(chars);

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

  • 功能:写入字符数组cbuf中从索引off开始的len个字符。这是对字符数组进行部分写入的精确控制方法。
  • 示例
    char[] chars = {'H', 'e', 'l', 'l', 'o'}; writer.write(chars, 0, 3); // 只写入 "Hel"

4、void write(String str)

  • 功能:写入整个字符串str
  • 示例
    writer.write("Hello, World!");

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

  • 功能:写入字符串str中从索引off开始的len个字符。这是对字符串进行部分写入的精确控制方法。
  • 参数
    • str:要写入的字符串。
    • startingIndex(off):获取字符部分的起始索引。
    • lengthOfstring(len):要写入的字符串长度。
  • 示例
    String str = "GeeksForGeeks"; writer.write(str, 0, 5); // 写入 "Geeks"[11](@ref)

三、字节流与字符流的区别

假设文件里存的是中文字符“中”

  • 在磁盘上(UTF-8编码):它实际上占用了 3 个字节,数据可能是0xE4 0xB8 0xAD

  • 字节流读取

    • 它会读出 3 个独立的字节:E4B8AD

    • 如果你直接把这些字节转成字符串打印,可能会显示乱码(比如),因为它不知道这 3 个字节合起来才表示一个“中”字。

  • 字符流读取

    • 它会根据编码规则(UTF-8),把这 3 个字节自动拼装、翻译,最终给你返回一个字符'中'

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/15 23:53:35

揭秘Open-AutoGLM进程调度机制:如何实现资源利用率提升90%?

第一章&#xff1a;揭秘Open-AutoGLM进程调度机制的核心价值 Open-AutoGLM 作为面向大规模语言模型推理任务的自动化调度框架&#xff0c;其核心竞争力之一在于高效、智能的进程调度机制。该机制不仅优化了计算资源的利用率&#xff0c;还显著降低了多任务并发执行时的延迟与冲…

作者头像 李华
网站建设 2026/4/15 23:08:27

Open-AutoGLM报错代码清单曝光(仅限内部流传的调试秘籍)

第一章&#xff1a;Open-AutoGLM 报错代码查询在使用 Open-AutoGLM 框架进行自动化推理任务时&#xff0c;开发者常会遇到各类运行时错误。准确识别并解析报错代码是提升调试效率的关键环节。本章将介绍常见错误类型、其成因及快速定位方法。常见报错代码与含义 以下为 Open-Au…

作者头像 李华
网站建设 2026/4/16 11:02:30

为什么你的Open-AutoGLM总是启动失败:资深架构师还原真实故障场景

第一章&#xff1a;Open-AutoGLM 启动异常排查 在部署 Open-AutoGLM 服务时&#xff0c;部分用户反馈启动过程中出现异常&#xff0c;导致服务无法正常加载。常见问题包括依赖缺失、环境变量未配置以及端口冲突等。为快速定位并解决问题&#xff0c;需系统性地检查运行环境与配…

作者头像 李华
网站建设 2026/4/16 9:12:12

高效低成本!Linly-Talker助力教育类视频批量生产

高效低成本&#xff01;Linly-Talker助力教育类视频批量生产 在知识内容爆炸式增长的今天&#xff0c;教育机构正面临一个共同难题&#xff1a;如何以有限的人力和预算&#xff0c;持续产出高质量、具有一致风格的教学视频&#xff1f;传统模式下&#xff0c;每一条讲解视频都需…

作者头像 李华
网站建设 2026/4/16 11:00:47

无需动作捕捉!Linly-Talker通过语音自动驱动面部表情

无需动作捕捉&#xff01;Linly-Talker通过语音自动驱动面部表情 在虚拟主播24小时不间断直播、AI讲师批量生成教学视频的今天&#xff0c;数字人早已不再是影视特效的专属。然而&#xff0c;传统数字人制作动辄需要动捕设备、动画师调参和数小时后期处理&#xff0c;成本高、周…

作者头像 李华