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类提供了三个静态方法:

  1. 生成版本3 UUID:使用.nameUUIDFromBytes(),需传入字节数组:

    UUID uuid = UUID.nameUUIDFromBytes(bytes);
    
  2. 解析字符串:从已有UUID字符串创建对象:

    UUID uuid = UUID.fromString(uuidHexDigitString);
    
  3. 生成版本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地址。

实现步骤:

  1. 生成最低64位

    private static long get64LeastSignificantBitsForVersion1() {
        Random random = new Random();
        long random63BitLong = random.nextLong() & 0x3FFFFFFFFFFFFFFFL;
        long variant3BitFlag = 0x8000000000000000L;
        return random63BitLong | variant3BitFlag;
    }
    
  2. 生成最高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;
    }
    
  3. 组合生成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仓库获取。


原始标题:Guide to UUID in Java | Baeldung