一.基础概念
UUID:Universally Unique Identifier,通用唯一识别码。是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分。UUID的目的,是让分布式系统中的所有元素,都能有唯一的辨识信息,而不需要通过中央控制端来做辨识信息的指定。
历史
UUID最初被应用在Apollo Network Computing System,随后被开放软件基金会(OSF)应用在分布式计算环境领域。
后来,IETF(国际互联网工程任务组)将UUID作为一种标准发布在RFC 4122。
格式
标准的UUID格式如下:
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
除连字符-外,上面每个字符都是一个十六进制的数字,共有5个部分组成,第一部分8个,第二部分4个,第三部分4个,第四部分4个,第五部分12个,8-4-4-4-12,一共32个十六进制字符,因此一共是128位。
其中,M表示UUID的版本,N表示UUID的变体。
变体
为了能兼容过去的UUID,以及应对未来的变化,因此有了变体(Variants)这一概念。目前已知的变体有如下几种:
variant 0:N的格式为0xxx。为了向后兼容预留。
variant 1:10xx。当前正在使用的。
variant 2:11xx。为早期微软GUID预留。
variant 3:111x。为将来扩展预留。目前暂未使用。
因此,可以认为,目前正在使用的UUID都是variant1,取值是8,9,a,b中的一个。
版本
版本用于定义UUID的形成方法:
Version 1:基于时间和MAC地址。由于使用了MAC地址,因此能够确保唯一性,但是同时也暴露了MAC地址,私密性不够好。
Version 2:DCE安全的UUID。该版本在规范中并没有仔细说明,因此并没有具体的实现。
Version 3 :基于名字空间(MD5)。用户指定一个名字空间和一个字符串,通过MD5散列,生成UUID。字符串本身需要是唯一的。
Version 4 :基于随机数。虽然是基于随机数,但是重复的可能性可以忽略不计,因此该版本也是被经常使用的版本。
Version 5 : 基于名字空间(SHA1)。跟Version 3类似,但是散列函数编程了SHA1。
二.类定义
java sdk中提供了UUID的Version 3和Version 4的具体实现。我们来看一下具体的类定义:
public final class UUID
implements java.io.Serializable, Comparable
该类被定义为final的,说明不希望被继承。
类中定义了如下变量:
// 高64位
private final long mostSigBits;
// 低64位
private final long leastSigBits;
// 版本
private transient int version = -1;
// 变体
private transient int variant = -1;
// 时间戳,版本1专用
private transient volatile long timestamp = -1;
// 时钟频率,版本1专用
private transient int sequence = -1;
// mac地址,版本1专用
private transient long node = -1;
// hash值
private transient int hashCode = -1;
UUID是128位的,在Java的UUID中,是将这128分为高64位和低64位分别存储的。
三.核心方法
1.构造方法
共有两个构造方法:
// 通过字节数组来生成UUID,字节数组长度必须是16个字节
private UUID(byte[] data) {
long msb = 0;
long lsb = 0;
assert data.length == 16;
// 将前8个字节赋值到高64位
for (int i=0; i<8; i++)
msb = (msb << 8) | (data[i] & 0xff);
// 将后8个字节赋值到低64位
for (int i=8; i<16; i++)
lsb = (lsb << 8) | (data[i] & 0xff);
this.mostSigBits = msb;
this.leastSigBits = lsb;
}
// 直接指定高64位和低64位的值
public UUID(long mostSigBits, long leastSigBits) {
this.mostSigBits = mostSigBits;
this.leastSigBits = leastSigBits;
}
2.randomUUID
该方法可以生成一个版本4的UUID。
// 生成版本4UUID
public static UUID randomUUID() {
// 伪随机数生成器
SecureRandom ng = Holder.numberGenerator;
byte[] randomBytes = new byte[16];
// 生成16个字节共128位的伪随机数
ng.nextBytes(randomBytes);
// 将带有版本号的那个字节与00001111进行按位与,表示版本号的4个bit将变成0000
randomBytes[6] &= 0x0f;
// 将带有版本号的字节与01000000进行按位或,表示版本号的4个bit将变成0100,说明是版本4
randomBytes[6] |= 0x40;
// 将带有变体的字节与00111111进行按位与,表示变体的4个bit将变成00xx
randomBytes[8] &= 0x3f; /* clear variant */
// 将带有变体的字节与10000000进行按位或,表示变体的4个bit将变成10xx,说明是变体2
randomBytes[8] |= 0x80; /* set to IETF variant */
return new UUID(randomBytes);
}
3.nameUUIDFromBytes
该方法将生成一个版本3的UUID。
public static UUID nameUUIDFromBytes(byte[] name) {
// MessageDigest是信息摘要类,提供md5,sha1等算法。
MessageDigest md;
try {
// 获取提供MD5算法的MessageDigest实例
md = MessageDigest.getInstance("MD5");
} catch (NoSuchAlgorithmException nsae) {
throw new InternalError("MD5 not supported");
}
// 利用md5算法对结合name,生成md5值,md5和UUID都是16个字节
byte[] md5Bytes = md.digest(name);
// 以下操作类似于randomUUID,只是会将版本复制为3
md5Bytes[6] &= 0x0f;
md5Bytes[6] |= 0x30;
md5Bytes[8] &= 0x3f;
md5Bytes[8] |= 0x80;
return new UUID(md5Bytes);
}
参考资料:
1.维基百科:Universally unique identifier
2.:关于UUID的二三事
3.How is an UUID / GUID made
本文已迁移至我的博客:http://ipenge.com/7413.html