Java 8 新特性—全新的、标准的 Base64 API

作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO

联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬

学习必须往深处挖,挖的越深,基础越扎实!

阶段1、深入多线程

阶段2、深入多线程设计模式

阶段3、深入juc源码解析


阶段4、深入jdk其余源码解析


阶段5、深入jvm源码解析

码哥源码部分

码哥讲源码-原理源码篇【2024年最新大厂关于线程池使用的场景题】

码哥讲源码【炸雷啦!炸雷啦!黄光头他终于跑路啦!】

码哥讲源码-【jvm课程前置知识及c/c++调试环境搭建】

​​​​​​码哥讲源码-原理源码篇【揭秘join方法的唤醒本质上决定于jvm的底层析构函数】

码哥源码-原理源码篇【Doug Lea为什么要将成员变量赋值给局部变量后再操作?】

码哥讲源码【你水不是你的错,但是你胡说八道就是你不对了!】

码哥讲源码【谁再说Spring不支持多线程事务,你给我抽他!】

终结B站没人能讲清楚红黑树的历史,不服等你来踢馆!

打脸系列【020-3小时讲解MESI协议和volatile之间的关系,那些将x86下的验证结果当作最终结果的水货们请闭嘴】

引言

Base64编码是一种用64个字符表示二进制数据的方法,它使用一组64个可打印字符来表示二进制数据,每6个比特位为一个单元,对应某个可打印字符。注意它并不是一种加密算法,所以Base64常用于在不支持二进制数据的系统间传输二进制数据。

但是,Java 8 之前并不支持 Base64,我们需要依赖第三方库如Apache Commons Codec或者在JDK内部类sun.misc.BASE64Encodersun.misc.BASE64Decoder等不推荐使用的方式来实现Base64编码解码。

为了能够提供一个更加标准的、更加安全的方法来进行Base64的编码和解码操作,使得开发者们不再需要依赖外部库,Java 8 引入全新的 Base64,同时为了提供更好的性能,Java 为 Base64 的实现做了专门的性能优化。

Base64 的核心原理

在了解 Java 8 中 Base64 API 之前,我们先看 Base64 的实现原理。

Base64 的核心思想是将数据流的每三个字节划分为一组,总共24位,再将这24位分为4组,每组6位。由于每组现在只有6位,因此它可以表示的最大数值是 2^6 - 1 = 63,Base64编码正是利用这64个数字(从0到63)对应到可打印字符的映射关系来工作的。

Base64 编码的步骤如下:

  1. 分组:输入数据被分成每组3字节(24位)。如果最后一组不足3字节,则用0填充至3字节。
  2. 映射:每组24位被进一步划分为4个6位的小组。每个6位小组将被映射为一个0-63之间的数字。
  3. 编码表:这些数字用作Base64编码表中的索引,该表由64个字符组成,包括大小写英文字母、数字和两个额外符号(通常是+/),还有一个用于填充的=符号,以确保输出的字符数为4的倍数。
  4. 转换:每个6位的分组对应的数字转换成相应的Base64字符。
  5. 填充:如果原始数据字节长度不是3的倍数,最终的编码可能会用=字符填充至4的倍数长度,这样接收方在解码时能够恢复原始数据。

编码表如下:

数值字符数值字符数值字符数值字符
0A16Q32g48w
1B17R33h49x
2C18S34i50y
3D19T35j51z
4E20U36k520
5F21V37l531
6G22W38m542
7H23X39n553
8I24Y40o564
9J25Z41p575
10K26a42q586
11L27b43r597
12M28c44s608
13N29d45t619
14O30e46u62+
15P31f47v63/

有了这个映射表我们就把任意的二进制转换成Base64的编码了,下面大明哥举个例子给大家演示下转换过程。我们将 sikejava 字符串转换为 Base64 编码

步骤1:将字符转换为ASCII值

将每个字符转换为对应一个ASCII值。

  • s -> 115
  • i -> 105
  • k -> 107
  • e -> 101
  • j -> 106
  • a -> 97
  • v -> 118
  • a -> 97

步骤2:将ASCII值转换为二进制

将每个ASCII值转换为8位二进制数。

  • 115 -> 01110011
  • 105 -> 01101001
  • 107 -> 01101011
  • 101 -> 01100101
  • 106 -> 01101010
  • 97 -> 01100001
  • 118 -> 01110110
  • 97 -> 01100001

串连起来就是:01110011 01101001 01101011 01100101 01101010 01100001 01110110 01100001

步骤3:将二进制数据划分为6位一组

将连续的二进制位分成6位一组的小块。如果最后一组不足6位,需要用0填充。

上面二进制分为 6 位一组:011100 110110 100101 101011 011001 010110 101001 100001 011101 100110 000100

步骤 4:将6位二进制数转换为十进制

每组6位的二进制数将被转换成十进制数。

  • 011100 -> 28
  • 110110 -> 54
  • 100101 -> 37
  • 101011 -> 43
  • 011001 -> 25
  • 010110 -> 22
  • 101001 -> 41
  • 100001 -> 33
  • 011101 -> 29
  • 100110 -> 38
  • 000100 -> 4

步骤5:将十进制数映射到Base64字符

  • 28 -> c
  • 54 -> 2
  • 37 -> l
  • 43 -> r
  • 25 -> Z
  • 22 -> W
  • 41 -> p
  • 33 -> h
  • 29 -> d
  • 38 -> m
  • 4 -> E

所以,"sikejava"对应的Base64编码是 c2lrZWphdmE=。最后的=符号用于填充,因为Base64编码的输出应该是4的倍数。

我们用代码验证下 :

System.out.println(Base64.getEncoder().encodeToString("sikejava".getBytes()));
// 结果......
c2lrZWphdmE=

Java 8 中的Base64 API

Java 8 中的 Base64 API 提供了三种主要类型的 Base64 编码和解码,他们分别适用于不同的场景和需求。

基本 Base64 编码和解码

  • 编码器: 使用 Base64.getEncoder() 获取。
  • 解码器: 使用 Base64.getDecoder() 获取。

它们提供了基本的 Base64 编码和解码功能,适用于所有需要 Base64 编码的场景。其特点是输出编码字符串不会包含用于换行的字符。

URL 和文件名安全 Base64 编码和解码

  • 编码器: 使用 Base64.getUrlEncoder() 获取。
  • 解码器: 使用 Base64.getUrlDecoder() 获取。

该编解码器适用于 URL 和文件名的 Base64 编码。由于 URL 中的某些字符(如 + 和 /)有特殊含义,所以需要使用这种编码方式来替换这些字符。使用该编码器,编码输出中的 + 和 / 字符分别被替换为 - 和 _,使得编码后的字符串可以安全地用在 URL 和文件名中。

MIME 类型 Base64 编码和解码

  • 编码器: 使用 Base64.getMimeEncoder() 获取。
  • 解码器: 使用 Base64.getMimeDecoder() 获取。

该编码器适用于 MIME 类型(如电子邮件)的内容,其中可能需要支持多行输出。特点是持按照 MIME 类型的要求将输出格式化为每行固定长度的多行字符串。默认每行长度不超过 76 个字符,并在每行后插入 \r\n

Base64 提供了多种编解码的方法,大明哥这里列举几个最常用的:

  • encode(byte[] src): 将给定的字节数组编码为 Base64 字符串。
  • encodeToString(byte[] src): 将给定的字节数组编码为一个 Base64 字符串,并将其直接转换为字符串格式。
  • decode(String src): 将给定的 Base64 字符串解码为字节数组。
  • decode(byte[] src): 将给定的 Base64 编码的字节数组解码为原始字节数组。

Base64示例

基本 Base64 编码

基本编码是最常见的类型,适用于大多数需要 Base64 编码的场景。

    @Test
    public void base64BasicTest() {
        String skStr = "skjava";

        // 编码
        String encodingStr = Base64.getEncoder().encodeToString(skStr.getBytes());
        System.out.println("encodingStr = " + encodingStr);

        // 解码
        String decodeStr = new String(Base64.getDecoder().decode(encodingStr));
        System.out.println("decodeStr = " + decodeStr);
    }
// 结果......
encodingStr = c2tqYXZh
decodeStr = skjava

URL 和文件名安全 Base64 编码

URL和文件名安全编码会替换掉一些在 URL 中可能会引起问题的字符,比如 + 和 /

    @Test
    public void base64UrlTest() {
        String skStr = "https://skjava.com/?series=skjava";

        // 编码
        String encodingStr = Base64.getUrlEncoder().encodeToString(skStr.getBytes());
        System.out.println("encodingStr = " + encodingStr);

        // 解码
        String decodeStr = new String(Base64.getUrlDecoder().decode(encodingStr));
        System.out.println("decodeStr = " + decodeStr);
    }
// 结果......
encodingStr = aHR0cHM6Ly9za2phdmEuY29tLz9zZXJpZXM9c2tqYXZh
decodeStr = https://skjava.com/?series=skjava

MIME 类型 Base64 编码

MIME 类型编码适用于电子邮件或其他 MIME 类型的内容,它支持多行输出。

    @Test
    public void base64MimeTest() {
        String skStr = "Hello, MIME-Type Example。\r\n" +
                "sike-java,sike-java-feature;" +
                "sike-javanio,sike-netty";

        // 编码
        String encodingStr = Base64.getMimeEncoder().encodeToString(skStr.getBytes());
        System.out.println("encodingStr = " + encodingStr);

        // 解码
        String decodeStr = new String(Base64.getMimeDecoder().decode(encodingStr));
        System.out.println("decodeStr = " + decodeStr);
    }
// 结果......
encodingStr = SGVsbG8sIE1JTUUtVHlwZSBFeGFtcGxl44CCDQpzaWtlLWphdmEsc2lrZS1qYXZhLWZlYXR1cmU7
c2lrZS1qYXZhbmlvLHNpa2UtbmV0dHk=
decodeStr = Hello, MIME-Type Example。
sike-java,sike-java-feature;sike-javanio,sike-netty
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值