Java實(shí)現(xiàn)BASE64加解密算法的示例代碼
1. 項(xiàng)目背景詳細(xì)介紹
在網(wǎng)絡(luò)通信、數(shù)據(jù)存儲(chǔ)與傳輸以及各種協(xié)議交互中,二進(jìn)制數(shù)據(jù)常常需要以文本形式表現(xiàn),以便在 HTTP、SMTP、JSON 等純文本協(xié)議中安全傳遞。Base64 編碼便是一種常用的方法,它將任意二進(jìn)制數(shù)據(jù)編碼為可打印的 ASCII 字符,保證在文本環(huán)境下不被破壞。
Java 標(biāo)準(zhǔn)庫(kù)中已有 java.util.Base64 實(shí)現(xiàn),但手寫(xiě)一套基于查表法(Table-Driven)的 Base64 編解碼算法,可以幫助我們:
- 深入理解 Base64 的編碼原理與字符映射規(guī)則;
- 掌握位運(yùn)算與字節(jié)處理技巧;
- 在不依賴(lài)庫(kù)的環(huán)境中靈活集成到自定義框架或受限平臺(tái)(如 Android 早期版本)中。
本項(xiàng)目將使用 Java 語(yǔ)言,從頭實(shí)現(xiàn)基于查表法的 Base64 編碼與解碼工具,支持標(biāo)準(zhǔn) Base64(含 +、/、= 填充)及 URL 安全變體(-、_、無(wú)填充)。
2. 項(xiàng)目需求詳細(xì)介紹
功能需求
Base64 編碼
- 輸入任意
byte[],返回標(biāo)準(zhǔn) Base64 字符串; - 支持 URL 安全模式:
+→-、/→_、省略=;
Base64 解碼
- 輸入 Base64 編碼字符串,恢復(fù)原始
byte[]; - 自動(dòng)識(shí)別并兼容標(biāo)準(zhǔn)與 URL 安全字符;
- 處理缺失或多余的填充字符,保證健壯解析;
易用 API
- 提供靜態(tài)方法:
public static String encode(byte[] data, boolean urlSafe); public static byte[] decode(String b64);
- 支持對(duì)
String與byte[]互轉(zhuǎn)的簡(jiǎn)便調(diào)用;
邊界與異常處理
- 對(duì)
null或空輸入返回空結(jié)果; - 對(duì)非法字符或格式拋出自定義
Base64Exception,并帶有錯(cuò)誤位置信息。
非功能需求
性能
- 對(duì)大數(shù)據(jù)(如圖片、視頻片段)編碼/解碼時(shí),避免頻繁擴(kuò)容,整體時(shí)空效率與
java.util.Base64相當(dāng);
可擴(kuò)展性
- 查表數(shù)組與位移邏輯解耦,后續(xù)可支持自定義字符集;
易測(cè)試
- 附帶 JUnit 單元測(cè)試,覆蓋標(biāo)準(zhǔn)用例、URL 變體、邊界、非法輸入等;
多線(xiàn)程安全
- 算法方法無(wú)共享可變狀態(tài),可并發(fā)調(diào)用。
3. 相關(guān)技術(shù)詳細(xì)介紹
Base64 編碼原理
- 將每 3 個(gè)字節(jié)(24 位)劃分為 4 個(gè) 6 位單元;
- 每個(gè) 6 位取值映射到字符表
A–Z, a–z, 0–9, +, /; - 當(dāng)輸入長(zhǎng)度非 3 的倍數(shù)時(shí),使用
=填充保證輸出長(zhǎng)度為 4 的倍數(shù);
查表法實(shí)現(xiàn)
- 預(yù)先構(gòu)造長(zhǎng)度為 64 的字符表
char[] ENC = {...}; - 解碼時(shí)構(gòu)造大小為 128 或 256 的反向查表
byte[] DEC,映射字符到 6 位值;
位運(yùn)算與字節(jié)處理
- 使用位移與掩碼操作:
int b0 = data[i] & 0xFF; int b1 = data[i+1] & 0xFF; int b2 = data[i+2] & 0xFF; // 組合為 24 位:(b0<<16)|(b1<<8)|b2 // 依次提取 6 位輸出
字符編碼
- 將
byte[]與 JavaString互轉(zhuǎn)需指定字符集(如 UTF-8);
異常設(shè)計(jì)
- 自定義運(yùn)行時(shí)
Base64Exception,區(qū)分填充錯(cuò)誤、非法字符、長(zhǎng)度不匹配。
4. 實(shí)現(xiàn)思路詳細(xì)介紹
4.1 數(shù)據(jù)結(jié)構(gòu)設(shè)計(jì)
編碼表
private static final char[] ENC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray(); private static final char[] ENC_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray();
private static final byte[] DEC = new byte[128]; // 初始化為 -1 // 遍歷 ENC,DEC[ENC[i]] = (byte)i // 同時(shí)為 URL 變體字符賦值
4.2 編碼流程
初始化:選擇 ENC 或 ENC_URL;
主循環(huán):每次處理 3 字節(jié):
- 組合 24 位臨時(shí)值;
- 右移提取 4 個(gè) 6 位索引,訪(fǎng)查表寫(xiě)入輸出;
尾部處理:剩余 1 或 2 字節(jié)時(shí),補(bǔ)零組合并輸出相應(yīng)字符,最后添加 =(標(biāo)準(zhǔn)模式);
結(jié)果拼接:使用 StringBuilder 或預(yù)估長(zhǎng)度的 char[] 直接寫(xiě)入,避免擴(kuò)容。
4.3 解碼流程
預(yù)處理:去掉所有非 Base64 字符(如換行、空格);
填充檢測(cè):記錄末尾 = 數(shù)量,驗(yàn)證長(zhǎng)度對(duì) 4 的整除;
主循環(huán):每次讀 4 個(gè)字符:
- 通過(guò)
DEC查出 4 個(gè) 6 位值,組合成 24 位; - 拆分為最多 3 字節(jié),依據(jù)填充數(shù)量控制輸出長(zhǎng)度;
結(jié)果返回:收集到 byte[],或使用 ByteArrayOutputStream 緩沖。
4.4 API 設(shè)計(jì)
public class Base64Util {
public static String encode(byte[] data);
public static String encodeUrlSafe(byte[] data);
public static byte[] decode(String b64) throws Base64Exception;
}encode:標(biāo)準(zhǔn)模式;encodeUrlSafe:URL 安全模式;decode:自動(dòng)識(shí)別兩種模式。
4.5 擴(kuò)展與優(yōu)化
- 自定義字符集:支持用戶(hù)傳入自定義
char[]; - 無(wú)填充模式:為極端場(chǎng)景去掉
=; - 流式 API:對(duì)大文件使用輸入流/輸出流分塊處理;
- SIMD 優(yōu)化:在性能敏感場(chǎng)景,使用 Java 9+ 的
sun.misc.Unsafe或 JNI 調(diào)用底層指令加速。
// ==================== 文件:Base64Exception.java ====================
package com.example.base64;
/**
* Base64 編解碼異常
*/
public class Base64Exception extends RuntimeException {
public Base64Exception(String message) {
super(message);
}
public Base64Exception(String message, Throwable cause) {
super(message, cause);
}
}
// ==================== 文件:Base64Util.java ====================
package com.example.base64;
import java.util.Arrays;
/**
* 基于查表法的 Base64 編解碼工具,支持標(biāo)準(zhǔn)與 URL 安全模式
*/
public class Base64Util {
// 標(biāo)準(zhǔn) Base64 編碼表
private static final char[] ENC = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/".toCharArray();
// URL 安全 Base64 編碼表
private static final char[] ENC_URL = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_".toCharArray();
// 解碼查表,-1 表示非法字符
private static final byte[] DEC = new byte[128];
static {
Arrays.fill(DEC, (byte)-1);
for (int i = 0; i < ENC.length; i++) {
DEC[ENC[i]] = (byte)i;
}
for (int i = 0; i < ENC_URL.length; i++) {
DEC[ENC_URL[i]] = (byte)i;
}
DEC['='] = 0;
}
/**
* 標(biāo)準(zhǔn) Base64 編碼
*/
public static String encode(byte[] data) {
return encode(data, false);
}
/**
* URL 安全 Base64 編碼(無(wú)填充)
*/
public static String encodeUrlSafe(byte[] data) {
return encode(data, true);
}
private static String encode(byte[] data, boolean urlSafe) {
if (data == null || data.length == 0) return "";
char[] table = urlSafe ? ENC_URL : ENC;
int len = data.length;
int fullGroups = len / 3;
int remainder = len - 3 * fullGroups;
int outLen = 4 * ((len + 2) / 3);
StringBuilder sb = new StringBuilder(outLen);
int idx = 0;
// 主循環(huán),每次處理 3 字節(jié)
for (int i = 0; i < fullGroups; i++) {
int b0 = data[idx++] & 0xFF;
int b1 = data[idx++] & 0xFF;
int b2 = data[idx++] & 0xFF;
sb.append(table[b0 >>> 2]);
sb.append(table[((b0 & 0x3) << 4) | (b1 >>> 4)]);
sb.append(table[((b1 & 0xF) << 2) | (b2 >>> 6)]);
sb.append(table[b2 & 0x3F]);
}
// 處理尾部
if (remainder == 1) {
int b0 = data[idx++] & 0xFF;
sb.append(table[b0 >>> 2]);
sb.append(table[(b0 & 0x3) << 4]);
if (!urlSafe) {
sb.append("==");
}
} else if (remainder == 2) {
int b0 = data[idx++] & 0xFF;
int b1 = data[idx++] & 0xFF;
sb.append(table[b0 >>> 2]);
sb.append(table[((b0 & 0x3) << 4) | (b1 >>> 4)]);
sb.append(table[(b1 & 0xF) << 2]);
if (!urlSafe) {
sb.append('=');
}
}
return sb.toString();
}
/**
* 自動(dòng)識(shí)別標(biāo)準(zhǔn)或 URL 安全 Base64,解碼為原始字節(jié)
*/
public static byte[] decode(String b64) {
if (b64 == null || b64.isEmpty()) return new byte[0];
// 去除空白
String s = b64.trim().replaceAll("\\s", "");
int len = s.length();
if ((len & 3) != 0) {
throw new Base64Exception("Base64 長(zhǎng)度非 4 的倍數(shù): " + len);
}
// 計(jì)算填充數(shù)量
int pad = 0;
if (len > 0 && s.charAt(len - 1) == '=') pad++;
if (len > 1 && s.charAt(len - 2) == '=') pad++;
int outLen = (len * 3) / 4 - pad;
byte[] out = new byte[outLen];
int outIdx = 0;
int inIdx = 0;
// 主循環(huán),每次處理 4 字符
for (int i = 0; i < len; i += 4) {
int c0 = charToValue(s.charAt(i));
int c1 = charToValue(s.charAt(i+1));
int c2 = charToValue(s.charAt(i+2));
int c3 = charToValue(s.charAt(i+3));
int triple = (c0 << 18) | (c1 << 12) | (c2 << 6) | c3;
if (outIdx < outLen) out[outIdx++] = (byte)(triple >> 16);
if (outIdx < outLen) out[outIdx++] = (byte)(triple >> 8);
if (outIdx < outLen) out[outIdx++] = (byte)triple;
}
return out;
}
private static int charToValue(char c) {
if (c >= DEC.length || DEC[c] < 0) {
throw new Base64Exception("非法 Base64 字符: " + c);
}
return DEC[c];
}
}
// ==================== 文件:TestBase64Util.java ====================
package com.example.base64;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
/**
* JUnit 單元測(cè)試:驗(yàn)證 Base64 編解碼功能
*/
public class TestBase64Util {
@Test
public void testStandardEncodeDecode() {
String text = "Hello, 世界!";
byte[] raw = text.getBytes(java.nio.charset.StandardCharsets.UTF_8);
String enc = Base64Util.encode(raw);
byte[] dec = Base64Util.decode(enc);
Assertions.assertEquals(text, new String(dec, java.nio.charset.StandardCharsets.UTF_8));
}
@Test
public void testUrlSafe() {
String text = "abc+/";
byte[] raw = text.getBytes(java.nio.charset.StandardCharsets.UTF_8);
String encUrl = Base64Util.encodeUrlSafe(raw);
Assertions.assertFalse(encUrl.contains("+") || encUrl.contains("/"));
byte[] dec = Base64Util.decode(encUrl);
Assertions.assertEquals(text, new String(dec, java.nio.charset.StandardCharsets.UTF_8));
}
@Test
public void testEmpty() {
Assertions.assertArrayEquals(new byte[0], Base64Util.decode(""));
Assertions.assertEquals("", Base64Util.encode(new byte[0]));
}
@Test
public void testInvalid() {
Assertions.assertThrows(Base64Exception.class, () -> Base64Util.decode("abcd*"));
Assertions.assertThrows(Base64Exception.class, () -> Base64Util.decode("abc"));
}
}5. 代碼詳細(xì)解讀
Base64Exception.java
- 自定義運(yùn)行時(shí)異常,用于標(biāo)識(shí)非法格式或字符錯(cuò)誤。
Base64Util.java
- 查表初始化:
ENC/ENC_URL分別定義標(biāo)準(zhǔn)與 URL 安全字符表;DEC長(zhǎng)度 128,預(yù)設(shè)為 -1,再給有效字符賦值;
- 編碼邏輯:
- 按 3 字節(jié)一組組合 24 位,依次右移提取 6 位索引訪(fǎng)問(wèn)
ENC; - 對(duì)尾部剩余 1 或 2 字節(jié)做特殊處理并添加
=(僅標(biāo)準(zhǔn)模式); - 使用
StringBuilder預(yù)估長(zhǎng)度,避免動(dòng)態(tài)擴(kuò)容。
- 按 3 字節(jié)一組組合 24 位,依次右移提取 6 位索引訪(fǎng)問(wèn)
- 解碼邏輯:
- 去掉空白和換行;
- 驗(yàn)證長(zhǎng)度為 4 的倍數(shù)并統(tǒng)計(jì)填充數(shù);
- 每 4 字符通過(guò)
DEC查表得 4×6=24 位,拆分出至多 3 字節(jié); - 非法字符或不匹配拋
Base64Exception。
- TestBase64Util.java
- 覆蓋標(biāo)準(zhǔn)模式、URL 安全模式、空輸入和非法輸入四類(lèi)測(cè)試場(chǎng)景,確保正確性與健壯性。
6. 項(xiàng)目詳細(xì)總結(jié)
本項(xiàng)目從底層位運(yùn)算與查表法出發(fā),完整實(shí)現(xiàn)了 Base64 編解碼功能,具有以下特點(diǎn):
- 深入原理:手寫(xiě)查表法幫助理解編碼映射與填充機(jī)制;
- 雙模式支持:同時(shí)提供標(biāo)準(zhǔn)與 URL 安全兩種變體;
- 高效:預(yù)分配輸出緩沖、位運(yùn)算提取、查表訪(fǎng)問(wèn),性能可與
java.util.Base64媲美; - 健壯:對(duì)非法長(zhǎng)度、非法字符、空白干擾等場(chǎng)景做嚴(yán)格校驗(yàn)并拋出友好異常;
- 線(xiàn)程安全:所有方法使用局部變量,無(wú)共享可變狀態(tài),可安全并發(fā)調(diào)用;
- 易擴(kuò)展:字符表與查表邏輯解耦,可替換為自定義 Base64 變體。
7. 項(xiàng)目常見(jiàn)問(wèn)題及解答
Q1:為什么要手寫(xiě)而不直接用 java.util.Base64?
A:手寫(xiě)實(shí)現(xiàn)有助于深入理解 Base64 原理,并可在受限環(huán)境(無(wú)庫(kù)支持)中使用。
Q2:URL 安全模式為什么不加填充?
A:JWT 等場(chǎng)景中常省略填充以縮短長(zhǎng)度,讀取時(shí)可自動(dòng)補(bǔ)齊。
Q3:如何支持其他字符集的 Base64(如 Base64Url+Padding)?
A:可在 encode 方法中傳入自定義字符表,并在解碼時(shí)提供相應(yīng)反向查表。
Q4:如何對(duì)大文件做流式處理?
A:可將 encode/decode 分塊調(diào)用,使用 InputStream/OutputStream,在每次塊結(jié)束時(shí)維護(hù)少量狀態(tài)。
8. 擴(kuò)展方向與性能優(yōu)化
SIMD 加速
- 利用 Java 16+ 的
Vector API對(duì)大塊內(nèi)存做并行位運(yùn)算,提升吞吐。
JNI 本地庫(kù)
- 調(diào)用 C/C++ 高性能實(shí)現(xiàn),適合超大數(shù)據(jù)場(chǎng)景。
緩存優(yōu)化
- 對(duì)頻繁相同輸入做 LRU 緩存,減少重復(fù)計(jì)算。
自定義變體
- 支持 Base64 Feng、Radix-64 等其他變體或自定義映射。
并行流水線(xiàn)
- 對(duì)超大流分段并發(fā)處理,再合并結(jié)果,可充分利用多核優(yōu)勢(shì)。
以上就是Java實(shí)現(xiàn)BASE64加解密算法的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Java BASE64加解密算法的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java多線(xiàn)程和并發(fā)基礎(chǔ)面試題(問(wèn)答形式)
多線(xiàn)程和并發(fā)問(wèn)題是Java技術(shù)面試中面試官比較喜歡問(wèn)的問(wèn)題之一。在這里,從面試的角度列出了大部分重要的問(wèn)題,感興趣的小伙伴們可以參考一下2016-06-06
利用logback filter過(guò)濾某個(gè)類(lèi) 屏蔽某個(gè)類(lèi)
這篇文章主要介紹了利用logback filter過(guò)濾某個(gè)類(lèi) 屏蔽某個(gè)類(lèi)的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-07-07
Java中的for循環(huán)結(jié)構(gòu)及實(shí)例
這篇文章主要介紹了Java中的for循環(huán)結(jié)構(gòu)及實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-01-01
深入解析Java實(shí)現(xiàn)文件寫(xiě)入磁盤(pán)的全鏈路過(guò)程
寫(xiě)一行簡(jiǎn)單的 Java 文件操作代碼,數(shù)據(jù)就能順利保存到磁盤(pán),這背后到底經(jīng)歷了什么,本文將從源碼到硬件,全方位拆解這個(gè)過(guò)程,有需要的可以了解下2025-05-05
Java深入了解數(shù)據(jù)結(jié)構(gòu)之二叉搜索樹(shù)增 插 刪 創(chuàng)詳解
二叉搜索樹(shù)是以一棵二叉樹(shù)來(lái)組織的。每個(gè)節(jié)點(diǎn)是一個(gè)對(duì)象,包含的屬性有l(wèi)eft,right,p和key,其中,left指向該節(jié)點(diǎn)的左孩子,right指向該節(jié)點(diǎn)的右孩子,p指向該節(jié)點(diǎn)的父節(jié)點(diǎn),key是它的值2022-01-01
IDEA運(yùn)行導(dǎo)入的javaweb項(xiàng)目tomcat正常,但是運(yùn)行失敗404問(wèn)題
這篇文章主要介紹了IDEA運(yùn)行導(dǎo)入的javaweb項(xiàng)目tomcat正常但是運(yùn)行失敗404問(wèn)題,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-07-07
mybatis-plus內(nèi)置雪花算法主鍵重復(fù)問(wèn)題解決
本文主要介紹了mybatis-plus內(nèi)置雪花算法主鍵重復(fù)問(wèn)題解決,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-09-09

