Java常用编码解码类库与Base64

Java 8+ 应优先使用 java.util.Base64,它性能更好、无依赖、线程安全;Commons Codec 的 Base64 已冗余,除非维护 JDK 7;需注意编码变种、填充规则及显式字符集指定。

Java里用java.util.Base64就够了,别再引Apache Commons Codec了

Java 8+ 自带的 java.util.Base64 已覆盖绝大多数 Base64 编解码需求,性能更好、无额外依赖、线程安全。Apache Commons Codec 的 Base64 类在 Java 8 后基本属于冗余——除非你还在维护 JDK 7 项目。

常见误用是直接 new Base64()(Commons Codec)或调用静态方法时传错参数类型(比如把 byte[] 当 String 传)。实际只需:

String en

coded = Base64.getEncoder().encodeToString("hello".getBytes(StandardCharsets.UTF_8)); byte[] decoded = Base64.getDecoder().decode(encoded);
  • getEncoder()getDecoder() 返回的是无状态实例,可复用,无需缓存
  • 编码结果默认不含换行符(MIME 模式才加 \r\n),避免 JSON 或 URL 场景出错
  • 若需 URL 安全变种(不使用 +/,改用 -_),用 getUrlEncoder()getUrlDecoder()

Base64 编码后字符串末尾的 = 是什么?能删吗?

等号是 Base64 的填充字符(padding),用于对齐 4 字节边界。它不是“可有可无”的装饰——解码时缺失填充会导致 IllegalArgumentException: Illegal base64 character 或截断数据。

但某些场景(如 JWT payload、URL 参数)明确要求去掉 =。这时不能手动 replaceAll("=", ""),而应使用 URL 安全编码器:

String urlSafe = Base64.getUrlEncoder().withoutPadding().encodeToString(data);
  • withoutPadding() 是关键,它跳过填充逻辑,生成无 = 的字符串
  • 对应解码必须用 getUrlDecoder(),普通 getDecoder() 不接受 -/_ 字符
  • 手动删等号再解码 → 必然失败;手动补等号再解码 → 可能成功但不可靠(长度不对时补错位)

为什么 new String(decodedBytes) 解出来的中文是乱码?

Base64 解码得到的是原始字节数组,直接用 new String(byte[]) 会走平台默认编码(如 Windows 上是 GBK),而非原始编码(通常是 UTF-8)。这不是 Base64 的问题,而是字符串重建环节的编码错配。

正确做法始终显式指定字符集:

String original = new String(decoded, StandardCharsets.UTF_8);
  • 编码时也必须一致:"文本".getBytes(StandardCharsets.UTF_8)
  • 如果原始数据不是文本(比如图片二进制),就根本不要转成 String —— Base64 本质是二进制到 ASCII 的映射,中间不该插入字符语义
  • String.getBytes()(无参)或 new String(bytes)(无参)在跨环境时必然翻车

Spring Boot 里自动配置的 Base64 Bean 是什么?

Spring Boot 没有、也不会自动配置任何 Base64 相关 Bean。如果你在项目里看到类似 @Bean public Base64 base64() { ... },那一定是团队自己写的冗余封装。

真正需要关注的是 Spring Security 或 JWT 库中隐含的 Base64 使用点:

  • JWT 的 header/payload 解析会内部调用 Base64.getUrlDecoder(),你只需确保传入的是标准 URL 安全 Base64 字符串
  • Spring 的 HttpMessageConverter 对 Base64 无特殊处理;JSON 中的 Base64 字段就是普通字符串字段
  • 写工具类时,别包装 Base64.getEncoder() 成单例 Bean——它本身已是轻量且线程安全的

最易被忽略的点:不同 Base64 变种(standard / URL-safe / MIME)之间不可混用,且 padding 策略必须两端一致。一个接口用 getUrlEncoder().withoutPadding(),另一端却用 getDecoder(),错误不会立刻暴露,而是解出错乱的二进制。