Java里的UUID类是java.util包下的工具类,主要用于生成全局唯一标识符,在很多业务场景中都能发挥重要作用,比如数据库主键生成、分布式系统请求标识、文件唯一命名等。

UUID类的主要作用
UUID的全称是通用唯一识别码,Java的UUID类封装了UUID的生成、解析等相关操作,它的核心作用就是生成不重复的字符串标识,具体应用场景包括:
- 数据库主键生成:在分库分表场景下,自增主键容易出现冲突,使用UUID可以生成全局唯一的字符串作为主键,避免主键重复问题。
- 分布式系统标识:在微服务架构中,不同服务之间的请求需要唯一标识来追踪调用链路,UUID可以作为请求的唯一跟踪ID。
- 临时文件命名:生成临时文件时,使用UUID作为文件名可以避免文件名冲突,保证文件存储的唯一性。
- 业务对象标识:比如订单号、用户临时标识等场景,需要唯一标识来区分不同对象,UUID可以快速满足需求。
UUID的生成原理
UUID的标准格式是32个十六进制字符,以连字符分隔成五段,形式为8-4-4-4-12,总长度36位。Java中UUID的版本主要分为UUID v1、UUID v3、UUID v4、UUID v5,最常用的是UUID v4,下面分别说明不同版本的原理:
UUID v1原理
UUID v1是基于时间戳和MAC地址生成的,它包含当前时间戳、时钟序列以及生成机器的MAC地址。因为MAC地址是全球唯一的,加上时间戳的递增特性,理论上可以保证生成的UUID不重复,但会暴露机器的MAC地址,存在隐私安全问题,所以实际使用较少。
UUID v4原理
UUID v4是基于随机数生成的,Java中调用UUID.randomUUID()方法生成的就是UUID v4。它的核心是利用伪随机数生成器生成128位的随机数,其中6个固定位用来标识UUID版本为v4,另外2个固定位用来标识UUID变体,剩下的122位都是随机生成的。因为随机数的范围极大,重复的概率极低,几乎可以忽略不计,所以是最常用的UUID生成方式。
UUID v3和v5原理
UUID v3和v5是基于名字的UUID,通过指定的名字和命名空间,结合MD5(v3)或者SHA-1(v5)哈希算法生成。相同的名字和命名空间生成的UUID是完全相同的,适合需要基于固定名字生成固定唯一标识的场景。
Java中UUID类的常用用法
Java的UUID类提供了多个静态方法来生成和解析UUID,下面是最常用的操作示例:
生成UUID v4
import java.util.UUID;
public class UUIDDemo {
public static void main(String[] args) {
// 生成随机UUID(UUID v4)
UUID uuid = UUID.randomUUID();
// 输出UUID的字符串形式,格式为8-4-4-4-12
System.out.println("生成的UUID:" + uuid.toString());
// 去掉连字符的UUID字符串
String uuidWithoutHyphen = uuid.toString().replace("-", "");
System.out.println("去掉连字符的UUID:" + uuidWithoutHyphen);
}
}
解析UUID字符串
如果有一个符合UUID格式的字符串,可以使用UUID.fromString()方法将其转换为UUID对象:
import java.util.UUID;
public class UUIDParseDemo {
public static void main(String[] args) {
String uuidStr = "550e8400-e29b-41d4-a716-446655440000";
try {
UUID uuid = UUID.fromString(uuidStr);
System.out.println("解析后的UUID版本:" + (uuid.version()));
System.out.println("解析后的UUID变体:" + (uuid.variant()));
} catch (IllegalArgumentException e) {
System.out.println("字符串不符合UUID格式");
}
}
}
生成基于名字的UUID(UUID v3/v5)
Java没有直接提供生成v3和v5的静态方法,需要自己实现哈希逻辑,下面是基于MD5生成UUID v3的示例:
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.UUID;
public class NameBasedUUIDDemo {
public static void main(String[] args) {
String name = "test_name";
String namespace = "6ba7b810-9dad-11d1-80b4-00c04fd430c8";
try {
MessageDigest md = MessageDigest.getInstance("MD5");
// 拼接命名空间和名字的字节
byte[] namespaceBytes = hexStringToBytes(namespace.replace("-", ""));
byte[] nameBytes = name.getBytes(StandardCharsets.UTF_8);
byte[] data = new byte[namespaceBytes.length + nameBytes.length];
System.arraycopy(namespaceBytes, 0, data, 0, namespaceBytes.length);
System.arraycopy(nameBytes, 0, data, namespaceBytes.length, nameBytes.length);
// 计算MD5哈希
byte[] hash = md.digest(data);
// 设置UUID版本为3(版本位是第6-7位,值为0011)
hash[6] &= 0x0f;
hash[6] |= 0x30;
// 设置UUID变体为IETF(变体位是第8-9位,值为10)
hash[8] &= 0x3f;
hash[8] |= 0x80;
// 构造UUID
long mostSigBits = 0;
long leastSigBits = 0;
for (int i = 0; i < 8; i++) {
mostSigBits = (mostSigBits << 8) | (hash[i] & 0xff);
}
for (int i = 8; i < 16; i++) {
leastSigBits = (leastSigBits << 8) | (hash[i] & 0xff);
}
UUID uuid = new UUID(mostSigBits, leastSigBits);
System.out.println("基于名字生成的UUID v3:" + uuid.toString());
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
}
private static byte[] hexStringToBytes(String hexString) {
if (hexString.length() % 2 != 0) {
throw new IllegalArgumentException("十六进制字符串长度必须为偶数");
}
byte[] bytes = new byte[hexString.length() / 2];
for (int i = 0; i < hexString.length(); i += 2) {
String byteStr = hexString.substring(i, i + 2);
bytes[i / 2] = (byte) Integer.parseInt(byteStr, 16);
}
return bytes;
}
}
UUID使用的注意事项
- UUID v4的重复概率极低,但不是绝对为0,在对唯一性要求极高的场景(比如金融核心交易)需要额外校验。
- UUID生成的字符串长度较长,作为数据库主键时可能会影响索引性能,需要结合业务场景选择是否使用。
- UUID v1会暴露MAC地址,生产环境不建议使用,优先选择UUID v4。
- 如果需要基于固定输入生成固定UUID,应该选择UUID v3或者v5,而不是v4。