Java CharsetDecoder 怎样处理不完整多字节输入
在流式 UTF-8 输入里,一个汉字可能被拆到两个 ByteBuffer 中。此时 CharsetDecoder.decode(in, out, false) 返回 UNDERFLOW 并不表示数据损坏,而是说明当前输入块还不够,剩余字节必须保留下来,等下一块到达后继续解码。真正结束时才把最后一次 decode 的 endOfInput 设为 true,随后调用 flush 完成收尾。
官方地址:https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/nio/charset/CharsetDecoder.html
先区分 UNDERFLOW、OVERFLOW 和错误
CharsetDecoder 把输入字节转换为字符,并通过 CoderResult 告诉调用方为什么暂时停下。UNDERFLOW 可能意味着输入已经读完,也可能意味着解码器需要更多字节;OVERFLOW 表示输出 CharBuffer 空间不足;malformed input 和 unmappable character 才是需要按错误策略处理的数据问题。
分块解码时最容易犯的错,是把每个块单独交给便捷方法 decode(ByteBuffer)。这个方法会自己完成一次完整的 reset、decode 和 flush,不适合跨块保存解码状态。跨块场景应使用三参数 decode,并自行维护输入和输出缓冲区。

用 compact 保留半个字符
下面用 UTF-8 的“中”作为示意:它的三个字节被拆成第一块的两个字节和第二块的一个字节。第一次解码结束时,输入缓冲区里没有完成的字节仍然属于当前解码状态;调用 compact() 会把这些剩余字节移到缓冲区开头,下一次 put 才能把新数据接在后面。
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.charset.CoderResult;
import java.nio.charset.CodingErrorAction;
import java.nio.charset.StandardCharsets;
public class StreamingDecode {
public static void main(String[] args) throws Exception {
var decoder = StandardCharsets.UTF_8.newDecoder()
// 流式输入遇到非法字节时报告,避免静默替换掩盖数据问题。
.onMalformedInput(CodingErrorAction.REPORT)
.onUnmappableCharacter(CodingErrorAction.REPORT);
var input = ByteBuffer.allocate(8);
var output = CharBuffer.allocate(32);
byte[][] chunks = {
{0x41, (byte) 0xE4, (byte) 0xB8}, // A 加上“中”的前两个字节
{(byte) 0xAD, 0x42} // 补齐“中”,再接 B
};
for (int i = 0; i
这里的关键不是把字节数组写成固定两块,而是保留“未消费输入”。第一次 decode 以 false 结束后,如果返回 UNDERFLOW,input.compact() 会把尚未组成完整字符的前缀留下;第二块写入后,解码器才能看到完整的 UTF-8 序列。

最后一次 decode 必须传 true
endOfInput 表示调用方是否还能提供更多输入。只要后面还有数据,就传 false;确认输入流结束后,最后一次调用必须传 true。如果此时仍有半个 UTF-8 字符,解码器才会把它按 malformed input 处理,而不是继续等待一个永远不会到来的字节。
状态顺序也不能跳过:一次解码操作通常是先 reset(),再多次 decode(false),然后一次 decode(true),最后 flush()。尚未结束时直接调用 flush,或者在没有完成最终 decode 的情况下重复进入下一阶段,都会触发 IllegalStateException。
按返回结果定位故障
| 结果 | 含义 | 处理方式 |
|---|---|---|
UNDERFLOW | 输入已消费完,或还需要更多输入 | 非末块保留 remaining 字节;末块继续检查是否有错误 |
OVERFLOW | 输出 CharBuffer 没有足够空间 | 先 flip 读取或排空输出,再用同一输入位置继续 decode |
| malformed input | 字节序列不符合当前字符集 | 按 REPORT、IGNORE 或 REPLACE 策略处理,生产默认建议显式选择 |
| unmappable character | 合法输入不能映射为目标字符 | 检查目标字符集和 onUnmappableCharacter 设置 |
生产代码还要注意输出缓冲区的生命周期:OVERFLOW 不代表输入失败,输入位置应保持在未完成的位置,排空输出后继续调用。若只是一次性完整字节数组,才适合使用便捷的 decode(ByteBuffer)。
速查结论
分块输入遇到半个多字节字符时,先把 UNDERFLOW 当作“需要更多输入”,用 compact() 保留剩余字节;结束时用 decode(true) 明确封口,再调用 flush()。只有这样,输入不足、输出不足和数据损坏才会被分开处理。
相关问题
UNDERFLOW 一定表示 UTF-8 字节不完整吗?
不一定。它也可能只是当前输入块已经读完;调用方要结合是否还有下一块和输入缓冲区的 remaining 判断。
为什么不能每块都调用 decode(ByteBuffer)?
便捷方法会把每次调用视为独立解码操作,并自行 reset 与 flush;跨块输入需要保留 decoder 和剩余字节,所以应使用三参数 decode。
Java ZGC 堆容量参数的延迟目标取舍
- 上一篇
- Java ZGC 堆容量参数的延迟目标取舍
- 下一篇
- Java Time Chronology 非 ISO 日历的格式化
-
- 文章 · java教程 | 50分钟前 |
- Java Pattern quoteReplacement 怎样处理字面量替换文本
- 191浏览 收藏
-
- 文章 · java教程 | 52分钟前 | Java教程 · java DateTimeFormatter java.time.chrono Chronology 日本历 非ISO日历
- Java Time Chronology 非 ISO 日历的格式化
- 117浏览 收藏
-
- 文章 · java教程 | 57分钟前 | 垃圾回收 · Java教程 · java Xmx ZGC SoftMaxHeapSize ZUncommit AlwaysPreTouch
- Java ZGC 堆容量参数的延迟目标取舍
- 380浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java Matcher usePattern 切换模式后怎样继续扫描
- 500浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java FFM FunctionDescriptor 映射 C 结构体参数的布局方法
- 437浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java sealed class 与反射检查的类型边界
- 164浏览 收藏
-
- 文章 · java教程 | 1小时前 | 网络编程 · Java教程 · java httpclient 连接复用 多路复用 http/2
- Java HttpClient HTTP2 连接复用的配置方法
- 294浏览 收藏
-
- 文章 · java教程 | 1小时前 | Java教程 · 语言特性 · java 模式匹配 嵌套字段 record record pattern
- Java record 模式匹配的嵌套字段提取
- 475浏览 收藏
-
- 文章 · java教程 | 1小时前 | 反射 · 标准库 · 缓存 · Java教程 · java get remove ClassValue computeValue 类级缓存
- Java ClassValue 怎样缓存与类关联的计算结果
- 100浏览 收藏
-
- 文章 · java教程 | 1小时前 |
- Java virtual thread 阻塞调用的线程池迁移
- 141浏览 收藏
-
- 文章 · java教程 | 1小时前 | 虚拟线程 ·
- Java 结构化并发任务作用域的取消传播
- 460浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 755次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 752次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 833次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 799次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 633次使用
-
- Java 时间格式兼容多个输入格式的方法
- 2026-10-18 501浏览
-
- Java try-with-resources 多个资源关闭顺序是什么
- 2026-09-10 501浏览
-
- 矩阵主副对角线快速定位技巧
- 2026-05-31 501浏览
-
- Java多态优化流程代码与行为分发改进
- 2026-05-26 501浏览
-
- JVM 类元数据双亲委派链表深度解析
- 2026-05-21 501浏览

