可以验证一下不同静态工厂方法的版本和变体号:
UUID uuid = UUID.randomUUID(); int version = uuid.version(); int variant = uuid.variant(); System.out.println(String.format("version:%d,variant:%d", version, variant)); uuid = UUID.nameUUIDFromBytes(new byte[0]); version = uuid.version(); variant = uuid.variant(); System.out.println(String.format("version:%d,variant:%d", version, variant)); // 输出结果 version:4,variant:2 version:3,variant:2 探究JDK中UUID源码实现java.util.UUID被final修饰,实现了Serializable和Comparable接口,从一般理解上看,有下面的特定:
不可变,一般来说工具类都是这样定义的
可序列化和反序列化
不同的对象之间可以进行比较,比较方法后面会分析
下面会从不同的方面分析一下java.util.UUID的源码实现:
属性和构造函数
随机数版本实现
namespace name-based MD5版本实现
其他实现
格式化输出
比较相关的方法
属性和构造函数前面反复提到JDK中只提供了版本3和版本4的实现,但是java.util.UUID的布局采用了UUID规范中的字段定义,长度一共128比特,刚好可以存放在两个long类型的整数中,所以看到了UUID类中存在两个long类型的整型数值:
public final class UUID implements java.io.Serializable, Comparable<UUID> { // 暂时省略其他代码 /* * The most significant 64 bits of this UUID. * UUID中有效的高64比特 * * @serial */ private final long mostSigBits; /* * The least significant 64 bits of this UUID. * UUID中有效的低64比特 * * @serial */ private final long leastSigBits; // 暂时省略其他代码 }从UUID类注释中可以看到具体的字段布局如下:
高64比特mostSigBits的布局
字段 bit长度 16进制字符长度time_low 32 8
time_mid 16 4
version 4 1
time_hi 12 3
低64比特leastSigBits的布局
字段 bit长度 16进制字符长度variant 2 小于1
clock_seq 14 variant和clock_seq加起来等于4
node 48 12
接着看UUID的其他成员属性和构造函数:
public final class UUID implements java.io.Serializable, Comparable<UUID> { // 暂时省略其他代码 // Java语言访问类,里面存放了很多底层相关的访问或者转换方法,在UUID中主要是toString()实例方法用来格式化成8-4-4-4-12的形式,委托到Long.fastUUID()方法 private static final JavaLangAccess jla = SharedSecrets.getJavaLangAccess(); // 静态内部类确保SecureRandom初始化,用于版本4的随机数UUID版本生成安全随机数 private static class Holder { static final SecureRandom numberGenerator = new SecureRandom(); } // 通过长度为16的字节数组,计算mostSigBits和leastSigBits的值初始化UUID实例 private UUID(byte[] data) { long msb = 0; long lsb = 0; assert data.length == 16 : "data must be 16 bytes in length"; for (int i=0; i<8; i++) msb = (msb << 8) | (data[i] & 0xff); for (int i=8; i<16; i++) lsb = (lsb << 8) | (data[i] & 0xff); this.mostSigBits = msb; this.leastSigBits = lsb; } // 直接指定mostSigBits和leastSigBits构造UUID实例 public UUID(long mostSigBits, long leastSigBits) { this.mostSigBits = mostSigBits; this.leastSigBits = leastSigBits; } // 暂时省略其他代码 }私有构造private UUID(byte[] data)中有一些位运算技巧:
long msb = 0; long lsb = 0; assert data.length == 16 : "data must be 16 bytes in length"; for (int i=0; i<8; i++) msb = (msb << 8) | (data[i] & 0xff); for (int i=8; i<16; i++) lsb = (lsb << 8) | (data[i] & 0xff); this.mostSigBits = msb; this.leastSigBits = lsb;输入的字节数组长度为16,mostSigBits由字节数组的前8个字节转换而来,而leastSigBits由字节数组的后8个字节转换而来。中间变量msb或者lsb在提取字节位进行计算的时候:
先进行左移8位确保需要计算的位为0,已经计算好的位移动到左边
然后右边需要提取的字节data[i]的8位会先和0xff(补码1111 1111)进行或运算,确保不足8位的高位被补充为0,超过8位的高位会被截断为低8位,也就是data[i] & 0xff确保得到的补码为8位
前面两步的结果再进行或运算