先看再点赞,给本身一点思考的时间,微信搜索【 沉默王二】关注这个靠才华苟且的程序员。
本文 GitHub github.com/itwanger 已收录,里面还有一线大厂整理的面试题,以及个人系列文章。
一个调皮的读者在以前我写的“我去”系列文章里留言调侃说,“二哥,你是无中生小王吗?”不不不,其实真不是的,小王是真实存在的,他一直和我并肩做战,不辞辛劳,让我既爱又恨。我爱他,由于他兢兢业业,不辞辛苦,和我心有灵犀;我恨他,由于他时不时会中二一下,问我一些好笑的问题,好比说此次,“二哥,你能给我说说 Java 如何生成 UUID 吗?”java
UUID,全名叫作 Universally Unique Identifier,也就是通用惟一标识符的意思。有时候,也叫作全局惟一标识符,英文全名叫作 Globally Unique Identifier,简拼为 GUID。git
来看一下 UUID 的格式:程序员
123e4567-e89b-12d3-a456-556642440000 xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx
由四个中划线“-”隔开,第一部分的长度为 8,第二部分和第三部分的长度为 4,第四部分的长度为 12,总长度为 36,是固定的。每一部分都是一个十六进制的数字,注意并非随机的任意字母+数字的字符串。github
M 表示 UUID 的版本,N 为 UUID 的变体(Variants)。面试
M 的值有 5 个可选项:算法
为了能兼容过去的 UUID,以及应对将来的变化,所以有了变体(Variants)这一律念。数据库
目前已知的变体有下面 4 种:数组
在上例中,M 是 1,N 是 a(二进制为 1010,符合 10xx 的格式),这就意味着这个 UUID 是“版本 1”、“变体 1”的 UUID。微信
目前大多数使用的 UUID 大都是变体 1,N 的取值是 八、九、a、b 中的一个。session
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 中,就有一个叫 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。