1. 概述
本文将深入探讨 UUID(Universally Unique Identifier,通用唯一标识符),有时也称为 GUID(Globally Unique Identifier)。简单来说,它是一个由十六进制字符组成的128位长数字,并用“-”分隔:
e58ed763-928c-4155-bee9-fdbaaadc15f3
标准UUID包含32个十六进制数字和4个“-”符号,总长度为36字符。此外还有一种全零的 Nil UUID。
本文将聚焦Java中的UUID
类。首先介绍基本用法,然后解析不同类型UUID及其在Java中的生成方式。
2. UUID类详解
UUID
类只有一个构造函数,需要两个long
参数分别表示最高64位和最低64位:
UUID uuid = new UUID(mostSignificant64Bits, leastSignificant64Bits);
直接使用构造函数的缺点是需要手动构建UUID的位模式——这在需要重建特定UUID时很有用。但多数情况下我们只是需要随机值,因此UUID
类提供了三个静态方法:
✅ 生成版本3 UUID:使用
.nameUUIDFromBytes()
,需传入字节数组:UUID uuid = UUID.nameUUIDFromBytes(bytes);
✅ 解析字符串:从已有UUID字符串创建对象:
UUID uuid = UUID.fromString(uuidHexDigitString);
✅ 生成版本4 UUID:最简单的方式,无需参数:
UUID uuid = UUID.randomUUID();
接下来我们深入UUID的结构。
3. UUID结构解析
观察以下UUID及其对应的位掩码:
123e4567-e89b-42d3-a456-556642440000
xxxxxxxx-xxxx-Bxxx-Axxx-xxxxxxxxxxxx
3.1 变体(Variant)
上例中 A 表示变体,定义UUID的布局结构。其他所有位都依赖此布局。变体字段由A的最高3位表示:
MSB1 MSB2 MSB3
0 X X 保留 (0)
1 0 X 当前变体 (2)
1 1 0 Microsoft保留 (6)
1 1 1 未来保留 (7)
示例UUID中A值为“a”(二进制10xx),因此变体为2。
3.2 版本(Version)
B 表示版本号。示例中B值为4,即版本4。
在Java中可通过以下方法获取变体和版本:
UUID uuid = UUID.randomUUID();
int variant = uuid.variant();
int version = uuid.version();
变体2的UUID有五个版本:
- 时间戳版本(UUIDv1)
- DCE安全版本(UUIDv2)
- 名称哈希版本(UUIDv3和UUIDv5)
- 随机版本(UUIDv4)
⚠️ 注意:Java仅实现了v3和v4,其他版本需手动构造。
4. 各版本UUID详解
4.1 版本1
版本1使用当前时间戳和生成设备的MAC地址。时间戳以100纳秒为单位,从1582年10月15日开始计算。若需隐私保护,可用随机48位数替代MAC地址。
实现步骤:
生成最低64位:
private static long get64LeastSignificantBitsForVersion1() { Random random = new Random(); long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL; long variant3BitFlag = 0x8000000000000000L; return random63BitLong | variant3BitFlag; }
生成最高64位:
private static long get64MostSignificantBitsForVersion1() { final long currentTimeMillis = System.currentTimeMillis(); final long time_low = (currentTimeMillis & 0x0000_0000_FFFF_FFFFL) << 32; final long time_mid = ((currentTimeMillis >> 32) & 0xFFFF) << 16; final long version = 1 << 12; final long time_hi = ((currentTimeMillis >> 48) & 0x0FFF); return time_low | time_mid | version | time_hi; }
组合生成UUID:
public static UUID generateType1UUID() { long most64SigBits = get64MostSignificantBitsForVersion1(); long least64SigBits = get64LeastSignificantBitsForVersion1(); return new UUID(most64SigBits, least64SigBits); }
4.2 版本2
版本2同样使用时间戳和MAC地址,但RFC 4122未规定具体实现细节,本文不展开讨论。
4.3 版本3和5
版本3和5基于唯一命名空间的哈希值生成。命名空间不限于文本,DNS、OID、URL等都有效:
UUID = hash(NAMESPACE_IDENTIFIER + NAME)
核心区别在于哈希算法:
- v3使用MD5(128位)
- v5使用SHA-1(截断为128位)
生成v3 UUID示例:
byte[] nameSpaceBytes = bytesFromUUID(namespace);
byte[] nameBytes = name.getBytes("UTF-8");
byte[] result = joinBytes(nameSpaceBytes, nameBytes);
UUID uuid = UUID.nameUUIDFromBytes(result);
⚠️ 踩坑提醒:Java未实现v5版本,需手动实现类似逻辑。
4.4 版本4
最简单粗暴的方式——直接调用randomUUID()
:
UUID uuid = UUID.randomUUID();
5. 总结
本文系统解析了UUID的结构与各版本特性:
- 掌握了Java中创建UUID的基础方法
- 深入理解了v1/v3/v4的实现细节
- 提供了手动生成特定版本UUID的代码示例
完整实现代码可在GitHub仓库获取。