博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Java源码分析-UUID
阅读量:6948 次
发布时间:2019-06-27

本文共 6506 字,大约阅读时间需要 21 分钟。

原文链接:

本文分析的JDK版本为1.8.0_131

UUID

UUIDUniversally Unique Identifier的缩写;Java UUIDjava.util包下的一个用于获取通用唯一标识的类。有一个别名GUIDGlobally Unique Identifier)。

UUID是16字节128位长的数字,通常用36个字符的字符串表示,如下图所示:

img

字母是用16进制表示的,大小写无关!


UUID用途

  • 文件名随机
  • 网页session Id
  • 数据库表主键
  • 事务id

UUID定义

public final class UUID implements java.io.Serializable, Comparable

final修饰,不可被继承

UUID属性

//UUID的最高有效字节private final long mostSigBits;//UUID的最低有效字节private final long leastSigBits;

UUID构造器

public UUID(long mostSigBits, long leastSigBits) {    this.mostSigBits = mostSigBits;    this.leastSigBits = leastSigBits;}

public公有构造器,传入有效字节,构造UUID;

UUID还提供了一个私有构造器(字节数组构造),用于创建UUID;

private UUID(byte[] data) {    long msb = 0;    long lsb = 0;    assert data.length == 16 : "data must be 16 bytes in length";    //前8个字节,64位赋值给msb;data[i] & 0xff还是自己,有什么用?    // msb << 8 | data[i] 每次左移并取或运算 msb高位56位不变,低位8位为data[i]    for (int i=0; i<8; i++)        msb = (msb << 8) | (data[i] & 0xff);    //后8个字节,64位赋值给lsb    for (int i=8; i<16; i++)        lsb = (lsb << 8) | (data[i] & 0xff);    this.mostSigBits = msb;    this.leastSigBits = lsb;}

方法私有,只有内部使用;确保传入data字节数组的长度是16;

UUID的版本及其变体

为了兼容和应对未来的变化,UUID会有很多的版本以及变体;下图所示:

img

UUID变体

  • 0xxxReserved for NCS backward compatibility)预留,为向后兼容
  • 10xxLeach-Salz)当前正在使用的
  • 110xReserved, Microsoft Corporation backward compatibility)微软早起GUID预留
  • 111xReserved for future definition)将来扩展预留,目前还没被使用

UUID版本

  1. time-based 基于时间的UUID(版本 1)

    通过计算当前时间戳、随机数和机器MAC地址得到。由于有MAC地址,这个可以保证其在全球的唯一性。但是使用了MAC地址,就会有MAC地址暴露问题。若是局域网,可以用IP地址代替。

  2. DCE Security DCE安全的UUID(版本 2)

    DCE(Distributed Computing Environment)安全的UUID和基于时间的UUID算法相同,但会把时间戳的前4位置换为POSIX的UID或GID。这个版本的UUID在实际中较少用到。

  3. name-based 基于名字的UUID(MD5)(版本 3)

    基于名字的UUID通过计算名字和名字空间的MD5散列值得到。这个版本的UUID保证了:相同名字空间中不同名字生成的UUID的唯一性;不同名字空间中的UUID的唯一性;相同名字空间中相同名字的UUID重复生成是相同的。

  4. randomly generated UUID 随机UUID(版本 4)

    根据随机数,或者伪随机数生成UUID。这种UUID产生重复的概率是可以计算出来的,但随机的东西就像是买彩票:你指望它发财是不可能的,但狗屎运通常会在不经意中到来。

  5. name-based 基于名字的UUID(SHA1)(版本 5)

    和版本3的UUID算法类似,只是散列值计算使用SHA1(Secure Hash Algorithm 1)算法。

针对不同的应用,我们可以选择不同的UUID的版本。

JavaUUID提供了版本3、版本4的生成。

UUID方法

randomUUID

随机UUID生成

public static UUID randomUUID() {    //伪随机数生成器    SecureRandom ng = Holder.numberGenerator;        byte[] randomBytes = new byte[16];    //生成16字节的伪随机数    ng.nextBytes(randomBytes);    //第7个字节的高4位全部变0,低4位不变    randomBytes[6]  &= 0x0f;    //第7个字节的高4位变0100,低4位不变,也就是设置版本号为4    randomBytes[6]  |= 0x40;    //下面两步跟上面差不多,就是把变体所在字节变10xx,也就是变体2    randomBytes[8]  &= 0x3f;      randomBytes[8]  |= 0x80;     //然后调用私有构造器生成    return new UUID(randomBytes);}

nameUUIDFromBytes

基于名字的UUID生成

public static UUID nameUUIDFromBytes(byte[] name) {    //信息摘要类,获取MD5,SHA1等算法    MessageDigest md;    try {        md = MessageDigest.getInstance("MD5");    } catch (NoSuchAlgorithmException nsae) {        throw new InternalError("MD5 not supported", nsae);    }    //结合名字name,生成md5值,md5也是16字节    byte[] md5Bytes = md.digest(name);    //设置版本为3    md5Bytes[6]  &= 0x0f;     md5Bytes[6]  |= 0x30;     //设置变体为2 10xx    md5Bytes[8]  &= 0x3f;    md5Bytes[8]  |= 0x80;    //然后调用私有构造器生成    return new UUID(md5Bytes);}

fromString

这个方法不是生成了,是用UUID的字符串(36个字符,包含-),生成UUID对象

public static UUID fromString(String name) {    String[] components = name.split("-");    if (components.length != 5)        throw new IllegalArgumentException("Invalid UUID string: "+name);    for (int i=0; i<5; i++)        components[i] = "0x"+components[i];    long mostSigBits = Long.decode(components[0]).longValue();    mostSigBits <<= 16;    mostSigBits |= Long.decode(components[1]).longValue();    mostSigBits <<= 16;    mostSigBits |= Long.decode(components[2]).longValue();    long leastSigBits = Long.decode(components[3]).longValue();    leastSigBits <<= 48;    leastSigBits |= Long.decode(components[4]).longValue();    return new UUID(mostSigBits, leastSigBits);}

逻辑简单,通过字符“-”,将字符串spilt为字符串数组;然后数组中每个字符串都转成16进制,然后位运算构造UUID。

toString

将UUID输出为36个字符的字符串,可以结合前面的UUID示意图,理解起来就很简单了。

public String toString() {    return (digits(mostSigBits >> 32, 8) + "-" +            digits(mostSigBits >> 16, 4) + "-" +            digits(mostSigBits, 4) + "-" +            digits(leastSigBits >> 48, 4) + "-" +            digits(leastSigBits, 12));}

0x yy 16进制中每两个字母表示1个字节;在我理解其实就是先把 64位转成16进制32个字符字符串,然后用“-”分割。

private static String digits(long val, int digits) {    long hi = 1L << (digits * 4);    return Long.toHexString(hi | (val & (hi - 1))).substring(1);}

hashcode&equals

UUID重写了这两个方法

public int hashCode() {    //最高有效和最低有效的32位 做异或运算    long hilo = mostSigBits ^ leastSigBits;    //然后把异或运算的结果的 高位32和自己 再做异或运算(这样做就是hilo 高低位都参与了hash,减小了hash碰撞的概率)    return ((int)(hilo >> 32)) ^ (int) hilo;}
public boolean equals(Object obj) {    if ((null == obj) || (obj.getClass() != UUID.class))        return false;    UUID id = (UUID)obj;    //最高有效和最低有效 值相等,则equals    return (mostSigBits == id.mostSigBits &&            leastSigBits == id.leastSigBits);}

compareTo

两个UUID的比较,实现了Comparable接口

public int compareTo(UUID val) {    return (this.mostSigBits < val.mostSigBits ? -1 :            (this.mostSigBits > val.mostSigBits ? 1 :             (this.leastSigBits < val.leastSigBits ? -1 :              (this.leastSigBits > val.leastSigBits ? 1 :               0))));}

基本逻辑就是先比较最高有效,在比较最低有效。

-1 表示 当前uuid 小于 val

0 表示 当前uuid 等于 val

1 表示 当前uuid 大于 val

UUID属性方法

获取最高有效字节

public long getLeastSignificantBits() {    return leastSigBits;}

获取最低有效字节

public long getMostSignificantBits() {    return mostSigBits;}

获取UUID版本

//返回结果,UUID版本,1,2,3,4;4个版本public int version() {    //0x000000000000F000与运算    return (int)((mostSigBits >> 12) & 0x0f);}
UUID : 5289df73-7df5-3326-bcdd-22597afb1facUUID Version : 3

获取变体

public int variant() {    return (int) ((leastSigBits >>> (64 - (leastSigBits >>> 62)))                  & (leastSigBits >> 63));}

参考变体定义类型,返回值有 {0,2,6,7}

0 = 0xxx

2 = 10xx

6 = 110x

7 = 111x

获取时间戳及时钟序列

只有版本1才有时间戳和时钟序列

public long timestamp() {    if (version() != 1) {        throw new UnsupportedOperationException("Not a time-based UUID");    }    return (mostSigBits & 0x0FFFL) << 48         | ((mostSigBits >> 16) & 0x0FFFFL) << 32         | mostSigBits >>> 32;}
public int clockSequence() {    if (version() != 1) {        throw new UnsupportedOperationException("Not a time-based UUID");    }    return (int)((leastSigBits & 0x3FFF000000000000L) >>> 48);}

获取MAC地址信息

只有版本1才有

public long node() {    if (version() != 1) {        throw new UnsupportedOperationException("Not a time-based UUID");    }    return leastSigBits & 0x0000FFFFFFFFFFFFL;}

UUID就介绍到这里了,有什么问题,欢迎指正!

转载于:https://www.cnblogs.com/yy1024/p/8034198.html

你可能感兴趣的文章
数学基础-概率论05(统计推断-分布拟合检验)
查看>>
手机评测
查看>>
java ssm 后台框架平台 项目源码 websocket 即时通讯 IM quartz springmvc
查看>>
我的小爬虫—cocoa 中的正则表达式
查看>>
HTML5 中 div 和section以及 article 的不同之处
查看>>
阿超的烦恼 javaScript篇
查看>>
Yii2学习笔记之场景
查看>>
CS Website
查看>>
docker - 容器里安装ssh
查看>>
Ant design 组件开发
查看>>
那些让 Web 开发者们深感意外的事情
查看>>
完整性约束
查看>>
docker 17.09.0-ce 启动更换网络地址
查看>>
关于《大道至简》第六章的收获
查看>>
JavaWeb部分面试题
查看>>
mac osx 系统开发php 的一些工具
查看>>
Tcp的三次握手,以及原理详解
查看>>
sprintboot 中占位符及多环境配置
查看>>
Oracle资源
查看>>
你需要一点点CIL
查看>>