先看再点赞,给自己一点思考的时间,微信搜索【沉默王二】关注这个靠才华苟且的程序员。
本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及我的系列文章。

一个调皮的读者在之前我写的“我去”系列文章里留言调侃说,“二哥,你是无中生小王吗?”不不不,其实真不是的,小王是真实存在的,他一直和我并肩作战,不辞辛劳,让我既爱又恨。我爱他,因为他兢兢业业,任劳任怨,和我心有灵犀;我恨他,因为他时不时会中二一下,问我一些可笑的问题,比如说这次,“二哥,你能给我说说 Java 如何生成 UUID 吗?”

UUID,全名叫做 Universally Unique Identifier,也就是通用唯一标识符的意思。有时候,也叫做全局唯一标识符,英文全名叫做 Globally Unique Identifier,简拼为 GUID。

来看一下 UUID 的格式:

123e4567-e89b-12d3-a456-556642440000
xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx

由四个中划线“-”隔开,第一部分的长度为 8,第二部分和第三部分的长度为 4,第四部分的长度为 12,总长度为 36,是固定的。每一部分都是一个十六进制的数字,注意并不是随机的任意字母+数字的字符串。

M 表示 UUID 的版本,N 为 UUID 的变体(Variants)。

M 的值有 5 个可选项:

  • 版本 1:UUID 是根据时间和 MAC 地址生成的;

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

  • 版本 3:UUID 是通过散列(MD5 作为散列算法)名字空间(namespace)标识符和名称生成的;

  • 版本 4 - UUID 使用随机性或伪随机性生成;

  • 版本 5 类似于版本 3(SHA1 作为散列算法)。

为了能兼容过去的 UUID,以及应对未来的变化,因此有了变体(Variants)这一概念。

目前已知的变体有下面 4 种:

  • 变体 0:格式为 0xxx,为了向后兼容预留。

  • 变体 1:格式为 10xx,当前正在使用的。

  • 变体 2:格式为 11xx,为早期微软的 GUID 预留。

  • 变体 3:格式为 111x,为将来的扩展预留,目前暂未使用。

在上例中,M 是 1,N 是 a(二进制为 1010,符合 10xx 的格式),这就意味着这个 UUID 是“版本 1”、“变体 1”的 UUID。

目前大多数使用的 UUID 大都是变体 1,N 的取值是 8、9、a、b 中的一个。

System.out.println(Integer.toBinaryString(Integer.valueOf("8",16))); // 1000
System.out.println(Integer.toBinaryString(Integer.valueOf("a",16))); // 1010
System.out.println(Integer.toBinaryString(Integer.valueOf("9",16))); // 1001
System.out.println(Integer.toBinaryString(Integer.valueOf("b",16))); // 1011

8 的二进制为 1000,9 的二进制为 1001,a 的二进制为 1010,b 的二进制为 1011,都符合 10xx 的格式。

由于 UUID 是全局唯一的,重复 UUID 的概率接近零,可以忽略不计。所以 Java 的 UUID 通常可用于以下地方:

  • 随机生成的文件名;

  • Java Web 应用程序的 sessionID;

  • 数据库表的主键;

  • 事务 ID(UUID 生成算法非常高效,每台计算机每秒高达 1000 万次)。

在 Java 中,就有一个叫 UUID 的类,在 java.util 包下。

package java.util;
public final class UUID implements java.io.Serializable, Comparable<UUID> {
}

该类只有一个构造方法:

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

要使用构造方法创建 UUID 对象的话,就需要传递两个参数,long 型的最高位 UUID 和最低位的 UUID。

long msb = System.currentTimeMillis();
long lsb = System.currentTimeMillis();
UUID uuidConstructor = new UUID(msb, lsb);
System.out.println("UUID : "+uuidConstructor);

输出结果如下所示:

UUID : 00000173-8efd-1b7c-0000-01738efd1b7c

UUID 类提供了一个静态方法 randomUUID():

public static UUID randomUUID() {
    SecureRandom ng = UUID.Holder.numberGenerator;

    byte[] randomBytes = new byte[16];
    ng.nextBytes(randomBytes);
    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);
}

randomUUID() 方法生成了一个版本 4 的 UUID,这也是生成 UUID 最方便的方法。如果只使用原生 JDK 的话,基本上都用的这种方式。

示例如下:

UUID uuid4 = UUID.randomUUID();
int version4 = uuid4.version();
System.out.println("UUID:"+ uuid4+" 版本 " + version4);

程序输出结果如下所示:

UUID:8c943921-d83e-424a-a627-a12d3cb474db 版本 4

除此之外,UUID 类还提供了另外两个静态方法,其一是 nameUUIDFromBytes():

public static UUID nameUUIDFromBytes(byte[] name) {
    MessageDigest md;
    try {
        md = MessageDigest.getInstance("MD5");
    } catch (NoSuchAlgorithmException nsae) {
        throw new InternalError("MD5 not supported", nsae);
    }
    byte[] md5Bytes = md.digest(name);
    md5Bytes[6]  &= 0x0f;  /* clear version        */
    md5Bytes[6]  |= 0x30;  /* set to version 3     */
    md5Bytes[8]  &= 0x3f;  /* clear variant        */
    md5Bytes[8]  |= 0x80;  /* set to IETF variant  */
    return new UUID(md5Bytes);
}

nameUUIDFromBytes() 会生成一个版本 3 的 UUID,不过需要传递一个名称的字节数组作为参数。

示例如下:

UUID uuid3 = UUID.nameUUIDFromBytes("test".getBytes());
int version3 = uuid3.version();
System.out.println("UUID:"+ uuid3+" 版本 " + version3);

程序输出结果如下所示:

UUID:098f6bcd-4621-3373-8ade-4e832627b4f6 版本 3

其二是 fromString():

public static UUID fromString(String name) {
    int len = name.length();
    if (len > 36) {
        throw new IllegalArgumentException("UUID string too large");
    }

    int dash1 = name.indexOf('-', 0);
    int dash2 = name.indexOf('-', dash1 + 1);
    int dash3 = name.indexOf('-', dash2 + 1);
    int dash4 = name.indexOf('-', dash3 + 1);
    int dash5 = name.indexOf('-', dash4 + 1);

    // For any valid input, dash1 through dash4 will be positive and dash5
    // negative, but it's enough to check dash4 and dash5:
    // - if dash1 is -1, dash4 will be -1
    // - if dash1 is positive but dash2 is -1, dash4 will be -1
    // - if dash1 and dash2 is positive, dash3 will be -1, dash4 will be
    //   positive, but so will dash5
    if (dash4 < 0 || dash5 >= 0) {
        throw new IllegalArgumentException("Invalid UUID string: " + name);
    }

    long mostSigBits = Long.parseLong(name, 0, dash1, 16) & 0xffffffffL;
    mostSigBits <<= 16;
    mostSigBits |= Long.parseLong(name, dash1 + 1, dash2, 16) & 0xffffL;
    mostSigBits <<= 16;
    mostSigBits |= Long.parseLong(name, dash2 + 1, dash3, 16) & 0xffffL;
    long leastSigBits = Long.parseLong(name, dash3 + 1, dash4, 16) & 0xffffL;
    leastSigBits <<= 48;
    leastSigBits |= Long.parseLong(name, dash4 + 1, len, 16) & 0xffffffffffffL;

    return new UUID(mostSigBits, leastSigBits);
}

fromString() 方法会生成一个基于指定 UUID 字符串的 UUID 对象,如果指定的 UUID 字符串不符合 UUID 的格式,将抛出 IllegalArgumentException 异常。

示例如下:

UUID uuid = UUID.fromString("38400000-8cf0-11bd-b23e-10b96e4ef00d");
int version = uuid.version();
System.out.println("UUID:"+ uuid+" 版本 " + version);

程序输出结果如下所示:

UUID:38400000-8cf0-11bd-b23e-10b96e4ef00d 版本 1

除了使用 JDK 原生的 API 之外,还可以使用 com.fasterxml.uuid.Generators,需要先在项目中加入该类的 Maven 依赖。

<dependency>
    <groupId>com.fasterxml.uuid</groupId>
    <artifactId>java-uuid-generator</artifactId>
    <version>3.1.4</version>
</dependency>

然后我们来看一下如何使用它:

/**
 * @author 沉默王二,一枚有趣的程序员
 */
public class UUIDVersionExample {
    public static void main(String[] args) {
        UUID uuid1 = Generators.timeBasedGenerator().generate();
        System.out.println("UUID : "+uuid1);
        System.out.println("UUID 版本 : "+uuid1.version());

        UUID uuid2 = Generators.randomBasedGenerator().generate();
        System.out.println("UUID : "+uuid2);
        System.out.println("UUID 版本 : "+uuid2.version());
    }
}

Generators.timeBasedGenerator().generate() 可用于生成版本 1 的 UUID,Generators.randomBasedGenerator().generate() 可用于生成版本 4 的 UUID。

来看一下输出结果:

UUID : ebee82f5-cfd2-11ea-82a7-8536e13d4951
UUID 版本 : 1
UUID : d2ccc752-c824-4bbc-8cc7-52c8246bbc6a
UUID 版本 : 4

好了,我想关于 UUID 的一切,我都已经说明白了。赶紧把这篇文章先发给小王预览一下,让他涨涨见识。


我是沉默王二,一枚有颜值却靠才华苟且的程序员。关注即可提升学习效率,别忘了三连啊,点赞、收藏、留言,我不挑,奥利给。

注:如果文章有任何问题,欢迎毫不留情地指正。

如果你觉得文章对你有些帮助欢迎微信搜索「沉默王二」第一时间阅读,回复「小白」更有我肝了 4 万+字的 Java 小白手册 2.0 版,本文 GitHub github.com/itwanger 已收录,欢迎 star。

可笑,你竟然不知道 Java 如何生成 UUID的更多相关文章

  1. 【Java】生成UUID

    import java.util.UUID; public class MainProcess { public static void main(String[] args) { UUID uuid ...

  2. 你竟然不知道Java中可以用 :: 吗?

    简介 Java8中方法引用分为三种,方法引用通过一对双冒号:: 来表示,方法引用是一种函数式接口的另一种书写方式 静态方法引用,通过类名::静态方法名, 如 Integer::parseInt 实例方 ...

  3. java 生成UUID

    UUID(Universally Unique Identifier)全局唯一标识符,是一个128位长的数字,一般用16进制表示. 算法的核心思想是结合机器的网卡.当地时间.一个随即数来生成UUID, ...

  4. java生成UUID通用唯一识别码 (Universally Unique Identifier)

    转自:http://blog.csdn.net/carefree31441/article/details/3998553 UUID含义是通用唯一识别码 (Universally Unique Ide ...

  5. (转)java生成UUID通用唯一识别码 (Universally Unique Identifier)

    (原文链接:http://blog.csdn.net/carefree31441/article/details/3998553)   UUID含义是通用唯一识别码 (Universally Uniq ...

  6. java自带uuid生成

    java自带uuid生成UUID.randomUUID().toString()

  7. 利用Java生成UUID

    UUID是什么? UUID 是 通用唯一识别码(Universally Unique Identifier)的缩写,是一种软件建构的标准,亦为开放软件基金会组织在分布式计算环境领域的一部分.其目的,是 ...

  8. java生成UUID

    UUID介绍: UUID(Universally Unique Identifier)全局唯一标识符,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的.按照开放软件基金会(OSF) ...

  9. java生成UUID通用唯一识别码 (Universally Unique Identifier) 分类: B1_JAVA 2014-08-22 16:09 331人阅读 评论(0) 收藏

    转自:http://blog.csdn.net/carefree31441/article/details/3998553 UUID含义是通用唯一识别码 (Universally Unique Ide ...

随机推荐

  1. day19 生成器+函数递归

    目录 一.yield表达式 1 yield表达式基本用法 二.三元表达式 三.生成式 1 列表生成式 2 字典生成式 3 集合生成式 4 生成器表达式 四.函数的递归 1 递归的定义 2 详解递归 前 ...

  2. PE解析器与加载器编写指南

    PE解析器与加载器编写指南 最近准备去实习,看公司要求应该开发PE相关的查杀引擎,因此再回头复习一下PE格式,重新写一个PE解析器和PE加载器,再此记录下有关坑. PE解析器部分: 1)如何确定节区表 ...

  3. 006.Nginx访问控制

    一 Nginx连接限制 1.1 HTTP协议的连接与请求 HTTP是建立在TCP, 一次HTTP请求需要先建立TCP三次握手(称为TCP连接),在连接的基础上再进行HTTP请求. HTTP请求建立在一 ...

  4. java 基础(二) 搭建Java编译环境(linux系统)

    jdk安装配置 首先下载JDK和JRE,这里你的需要看看你的Linux系统是多少位的,比如我的是64位的:下载JDK并指定到Download目录,JRE同样操作:解压并且配置环境: tar -zxvf ...

  5. 网络编程-HTTPS

    明文: 对称加密: 非对称:(公钥:pk 私钥:sk) 对称+非对称: 先用非对称方式发送num1给server,server用私钥得出key(由num1算出来),自此,约定C.S以此key(num1 ...

  6. git怎么合并远程分支

    一共就五步: 1.把代码clone到本地仓库 git clone https://github.com/573734817pc/shop.git 2.在本地创建dev分支并与远程dev分支对应 git ...

  7. [Qt2D绘图]-01Qt2D绘图基本绘制和填充

    Qt的文档位置为 Paint System   大纲:     简介     先看一个小例子     基本的绘制和填充         使用画笔(Qpen)         使用画刷(QBrush) ...

  8. 痞子衡嵌入式:SNVS Master Key仅在i.MXRT10xx Hab关闭时才能用于DCP加解密

    大家好,我是痞子衡,是正经搞技术的痞子.今天痞子衡给大家介绍的是i.MXRT系列中数据协处理器DCP使用SNVS Master Key加解密的注意事项. i.MXRT不仅仅是处理性能超强的MCU,也是 ...

  9. 产品升级前后MD5码对比

    在做产品测试的时候,经常会需要对比升级前后的MD5码,这时可以通过终端登录设备,具体步骤如下: 1.在升级前时,将MD5码写入log1.info文件: check_md5 -d / -w log1.i ...

  10. Python 实现邮件发送功能(进阶)

    上篇文章已经介绍了利用Python发送文本消息的用法,也在文末遗留了如何发送图片和附件的问题,本章主要来回答这两个问题.   本章主要包含知识点: 1. 如何将图片放到邮件主体中发送 2. 如何发送附 ...