java类uuid源码分析


一、解释

维基百科
UUID 是有一定格式的,满足8-4-4-4-12这种格式,如下面这个 UUID :

6b349832-0470-4692-befd-6037b280bbc5

UUID由32 个字母数字字符组成(没有包括连字符),每一个字符是一个16进制的数字(0-f)。UUID 有一定的结构:

UUID 结构
Name 字节 半字节 Contents
time_low 4 8 整数:低位 32 bits 时间
time_mid 2 4 整数:中间位 16 bits 时间
time_hi_and_version 2 4 最高有效位中的 4 bits“版本”,后面是高 12 bits 的时间
clock_seq_hi_and_res clock_seq_low 2 4 最高有效位为 1-3 bits“变体”,后跟13-15 bits 时钟序列
node 6 12 48 bits 节点 ID
注意:16进制用 半个字节 表示,1个字节等于两个半字节,1个字节等于8位,半字节等于4个位。

UUID产生方式:

  1. “版本1” UUID 是根据时间和节点 ID(通常是MAC地址)生成;

  2. “版本2” UUID是根据标识符(通常是组或用户ID)、时间和节点ID生成;

  3. “版本3” 和 “版本5” 确定性UUID 通过散列 (hashing) 名字空间 (namespace) 标识符和名称生成;

  4. “版本4” UUID 使用随机性或伪随机性生成。

二、使用示例

 public static void main(String[] args) {
        String uuid="";
        uuid=UUID.randomUUID().toString();
        System.out.println(uuid);
    }

结果:

60cc1ff0-4b30-4e35-a0a6-940934ac756b

对,在java中产生 UUID 就是这么简单。

三、原理概述

 public static void main(String[] args) {
        byte[] randomBytes = new byte[16];//数组每一个元素都为0,所以需要产生随机的16字节
        SecureRandom secureRandom=new SecureRandom();
        secureRandom.engineNextBytes(randomBytes);
        System.out.println(DatatypeConverter.printHexBinary(randomBytes));


        /**
         *为了满足RFC 4122规范,需要对参数这个16个字节的随机数据进行一些设置。这里我们拿用随机方式产生UUID
         * 随机生成 "版本4" UUID。与其他 UUID 一样,4-bit 用于指示 "版本4",2-bit 或 3-bit 用于指示变体(variant)(10 或 110 分别用于 变体 1 和 2)。
         * 因此,对于变体1(即大多数 UUID),随机 "版本4" UUID 将具有 6 个预定的变体和版本位,为随机生成的部分留下122位,"版本4" 变体1 UUID 可能共计2的122次方个。
         * "版本4" 变体2 UUID (传统GUID)的可能有一半,因为可用的随机位少一个,变量消耗 3 bits。
         */

    }
}

运行结果:

82D68D2051EAF9610D36C33642E02594
  1. 产生大小为16的字节数组(转换为16进制就是32个字符)
  2. 为数组赋值(因为产生的初始数组每一个元素都为0)

其实到这里,我们的唯一id就产生了。只不过 UUID 要满足一定的规范,还有对产生的结果进行一些设置。如设置版本,设置变体。最后,我通过javax.xml.bind包下的DatatypeConverter将我们的字节数组以16进制的方式打印出来。

四、源码解析

UUID

public static UUID randomUUID() {
        SecureRandom ng = Holder.numberGenerator;//用于产生byte型的随机数组。

        byte[] randomBytes = new byte[16];//产生大小为16的byte数组
        ng.nextBytes(randomBytes);//给byte赋随机值。其实调用的是engineNextBytes。
		
		// UUID 要满足一定的规范,还有对产生的结果进行一些设置。如设置版本,设置变体
        randomBytes[6]  &= 0x0f;  /* clear version        */
        randomBytes[6]  |= 0x40;  /* set to version 4     */
        randomBytes[8]  &= 0x3f;  /* clear variant        */
        randomBytes[8]  |= 0x80;  /* set to IETF variant  */
        return new UUID(randomBytes);
    }
 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++)
        	//为什么要&0xff?是因为要不能或上一个负数byte,char,short的&都是转换为int的
        	//因为long类型是64位,在进行|运算时,需要补全0才可以运算
            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;
    }

Java中的UUID是用两个long型的变量mostSigBits 、leastSigBits 来存放UUID的。还有需要注意0xff,这是为了避免|上一个负数。为了阐述不能|一个负数,我举个例子:
在这里插入图片描述

最后就导致意外情况

五、奇技淫巧

  1. long转16进制
long x=15l;
String temp=Long.toHexString(x);
System.out.println(temp);

结果:

f
  1. byte数组转16进制
  byte[] randomBytes={15,14,12,11,-6};
  System.out.println(DatatypeConverter.printHexBinary(randomBytes));

结果:

0F0E0C0BFA

六、参考文献

维基百科
JAVA byte数组转化为16进制字符串输出

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值