From 939608a3c96b3d61e74e630ac636d73dc34b457b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=B2=89=E9=BB=98=E7=8E=8B=E4=BA=8C?= Date: Thu, 30 Mar 2023 12:32:23 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B8=B8=E7=94=A8=E5=B7=A5=E5=85=B7=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +- docs/common-tool/Objects.md | 2 +- docs/common-tool/StringUtils.md | 188 +++ docs/common-tool/collections.md | 89 +- docs/common-tool/guava.md | 4 +- docs/common-tool/hutool.md | 2 +- docs/common-tool/utils.md | 284 +--- docs/home.md | 3 +- ...33\351\230\266\344\271\213\350\267\257.md" | 1406 +++++++++++++++-- 9 files changed, 1627 insertions(+), 354 deletions(-) create mode 100644 docs/common-tool/StringUtils.md diff --git a/README.md b/README.md index 575541dc9..8f51f911e 100644 --- a/README.md +++ b/README.md @@ -197,11 +197,12 @@ - [Java Scanner:扫描控制台输入的工具类](docs/common-tool/scanner.md) - [Java Arrays:专为数组而生的工具类](docs/common-tool/arrays.md) +- [Apache StringUtils:专为Java字符串而生的工具类](docs/common-tool/StringUtils.md) - [Objects:专为操作Java对象而生的工具类](docs/common-tool/Objects.md) - [Java Collections:专为集合而生的工具类](docs/common-tool/collections.md) - [Hutool:国产良心工具包,让你的Java变得更甜](docs/common-tool/hutool.md) - [Guava:Google开源的Java工具库,太强大了](docs/common-tool/guava.md) -- [其他常用Java工具类:IpUtil、CollectionUtils、StringUtils、MDC、ClassUtils、BeanUtils、ReflectionUtils](docs/common-tool/utils.md) +- [其他常用Java工具类:IpUtil、MDC、ClassUtils、BeanUtils、ReflectionUtils](docs/common-tool/utils.md) ## Java新特性 diff --git a/docs/common-tool/Objects.md b/docs/common-tool/Objects.md index 6b4474de4..0299bd8bf 100644 --- a/docs/common-tool/Objects.md +++ b/docs/common-tool/Objects.md @@ -12,7 +12,7 @@ head: content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,java,Objects,java objects --- -# 9.3 Objects +# 9.4 Objects Java 的 Objects 类是一个实用工具类,包含了一系列静态方法,用于处理对象。它位于 java.util 包中,自 Java 7 引入。Objects 类的主要目的是降低代码中的[空指针异常](https://tobebetterjavaer.com/exception/npe.html) (NullPointerException) 风险,同时提供一些非常实用的方法供我们使用。 diff --git a/docs/common-tool/StringUtils.md b/docs/common-tool/StringUtils.md new file mode 100644 index 000000000..c6c4960ec --- /dev/null +++ b/docs/common-tool/StringUtils.md @@ -0,0 +1,188 @@ +--- +title: Apache StringUtils:专为Java字符串而生的工具类 +shortTitle: Apache StringUtils +category: + - Java核心 +tag: + - 常用工具类 +description: Java程序员进阶之路,小白的零基础Java教程,从入门到进阶,Java Arrays工具类的10大常用方法 +head: + - - meta + - name: keywords + content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,java,Apache StringUtils,java StringUtils +--- + +# 9.3 Apache StringUtils + +`字符串`([String](https://tobebetterjavaer.com/string/immutable.html))在我们的日常工作中,用得非常非常非常多。 + +在我们的代码中经常需要对字符串判空,截取字符串、转换大小写、[分隔字符串](https://tobebetterjavaer.com/string/split.html)、[比较字符串](https://tobebetterjavaer.com/string/equals.html)、去掉多余空格、[拼接字符串](https://tobebetterjavaer.com/string/join.html)、使用正则表达式等等。 + +如果只用 String 类提供的那些方法,我们需要手写大量的额外代码,不然容易出现各种异常。 + +现在有个好消息是:`org.apache.commons.lang3`包下的`StringUtils`工具类,给我们提供了非常丰富的选择。 + +Maven 坐标: + +``` + + org.apache.commons + commons-lang3 + 3.12.0 + +``` + +StringUtils 提供了非常多实用的方法,大概有下图的四页到五页,我只截了两页,实在是太多了。 + +![](https://cdn.tobebetterjavaer.com/stutymore/StringUtils-20230330111122.png) + +接下来,我们来拿一些常用的方法举例说明。 + +### 字符串判空 + +其实空字符串,不只是 null 一种,还有""," ","null"等等,多种情况。 + +StringUtils 给我们提供了多个判空的静态方法,例如: + +```java +String str1 = null; +String str2 = ""; +String str3 = " "; +String str4 = "abc"; +System.out.println(StringUtils.isEmpty(str1)); +System.out.println(StringUtils.isEmpty(str2)); +System.out.println(StringUtils.isEmpty(str3)); +System.out.println(StringUtils.isEmpty(str4)); +System.out.println("====="); +System.out.println(StringUtils.isNotEmpty(str1)); +System.out.println(StringUtils.isNotEmpty(str2)); +System.out.println(StringUtils.isNotEmpty(str3)); +System.out.println(StringUtils.isNotEmpty(str4)); +System.out.println("====="); +System.out.println(StringUtils.isBlank(str1)); +System.out.println(StringUtils.isBlank(str2)); +System.out.println(StringUtils.isBlank(str3)); +System.out.println(StringUtils.isBlank(str4)); +System.out.println("====="); +System.out.println(StringUtils.isNotBlank(str1)); +System.out.println(StringUtils.isNotBlank(str2)); +System.out.println(StringUtils.isNotBlank(str3)); +System.out.println(StringUtils.isNotBlank(str4)); +``` + +执行结果: + +```java +true +true +false +false +===== +false +false +true +true +===== +true +true +true +false +===== +false +false +false +true +``` + +示例中的:`isEmpty`、`isNotEmpty`、`isBlank`和`isNotBlank`,这 4 个判空方法你们可以根据实际情况使用。 + +优先推荐使用`isBlank`和`isNotBlank`方法,因为它会把`" "`也考虑进去。 + +### 分隔字符串 + +分隔字符串是常见需求,如果直接使用 String 类的 split 方法,就可能会出现空指针异常。 + +```java +String str1 = null; +System.out.println(StringUtils.split(str1,",")); +System.out.println(str1.split(",")); +``` + +执行结果: + +```java +null +Exception in thread "main" java.lang.NullPointerException +\tat com.sue.jump.service.test1.UtilTest.main(UtilTest.java:21) +``` + +使用 StringUtils 的 split 方法会返回 null,而使用 String 的 split 方法会报指针异常。 + +### 判断是否纯数字 + +给定一个字符串,判断它是否为纯数字,可以使用`isNumeric`方法。例如: + +```java +String str1 = "123"; +String str2 = "123q"; +String str3 = "0.33"; +System.out.println(StringUtils.isNumeric(str1)); +System.out.println(StringUtils.isNumeric(str2)); +System.out.println(StringUtils.isNumeric(str3)); +``` + +执行结果: + +```java +true +false +false +``` + +### 将集合拼接成字符串 + +有时候,我们需要将某个集合的内容,拼接成一个字符串,然后输出,这时可以使用`join`方法。例如: + +```java +List list = Lists.newArrayList("a", "b", "c"); +List list2 = Lists.newArrayList(1, 2, 3); +System.out.println(StringUtils.join(list, ",")); +System.out.println(StringUtils.join(list2, " ")); +``` + +执行结果: + +```java +a,b,c +1 2 3 +``` + +### 其他方法 + +这里再列举一些,其他的方法可以自己去研究一下。 + +- `trim(String str)`:去除字符串首尾的空白字符。 +- `trimToEmpty(String str)`:去除字符串首尾的空白字符,如果字符串为 null,则返回空字符串。 +- `trimToNull(String str)`:去除字符串首尾的空白字符,如果结果为空字符串,则返回 null。 +- `equals(String str1, String str2)`:比较两个字符串是否相等。 +- `equalsIgnoreCase(String str1, String str2)`:比较两个字符串是否相等,忽略大小写。 +- `startsWith(String str, String prefix)`:检查字符串是否以指定的前缀开头。 +- `endsWith(String str, String suffix)`:检查字符串是否以指定的后缀结尾。 +- `contains(String str, CharSequence seq)`:检查字符串是否包含指定的字符序列。 +- `indexOf(String str, CharSequence seq)`:返回指定字符序列在字符串中首次出现的索引,如果没有找到,则返回 -1。 +- `lastIndexOf(String str, CharSequence seq)`:返回指定字符序列在字符串中最后一次出现的索引,如果没有找到,则返回 -1。 +- `substring(String str, int start, int end)`:截取字符串中指定范围的子串。 +- `replace(String str, String searchString, String replacement)`:替换字符串中所有出现的搜索字符串为指定的替换字符串。 +- `replaceAll(String str, String regex, String replacement)`:使用正则表达式替换字符串中所有匹配的部分。 +- `join(Iterable iterable, String separator)`:使用指定的分隔符将可迭代对象中的元素连接为一个字符串。 +- `split(String str, String separator)`:使用指定的分隔符将字符串分割为一个字符串数组。 +- `capitalize(String str)`:将字符串的第一个字符转换为大写。 +- `uncapitalize(String str)`:将字符串的第一个字符转换为小写。 + +---- + +最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) + +微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。 + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) \ No newline at end of file diff --git a/docs/common-tool/collections.md b/docs/common-tool/collections.md index 04017e80d..a31beda6b 100644 --- a/docs/common-tool/collections.md +++ b/docs/common-tool/collections.md @@ -12,7 +12,7 @@ head: content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,java,Collections,集合框架,java Collections --- -# 9.4 Collections +# 9.5 Collections Collections 是 JDK 提供的一个工具类,位于 java.util 包下,提供了一系列的静态方法,方便我们对集合进行各种骚操作,算是集合框架的一个大管家。 @@ -250,6 +250,93 @@ addAll 后:[沉默王九, 沉默王十, 沉默王二] 是否没有交集:否 ``` +### 06、CollectionUtils:Spring 和 Apache 都有提供的集合工具类 + +对集合操作,除了前面说的 JDK 原生 `Collections` 工具类,`CollectionUtils`工具类也很常用。 + +目前比较主流的是`Spring`的`org.springframework.util`包下的 CollectionUtils 工具类。 + +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330101919.png) + +和`Apache`的`org.apache.commons.collections`包下的 CollectionUtils 工具类。 + +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330103825.png) + +Maven 坐标如下: + +``` + + org.apache.commons + commons-collections4 + 4.4 + +``` + +Apache 的方法比 Spring 的更多一些,我们就以 Apache 的为例,来介绍一下常用的方法。 + +#### 集合判空 + +通过 CollectionUtils 工具类的`isEmpty`方法可以轻松判断集合是否为空,`isNotEmpty`方法判断集合不为空。 + +```java +List list = new ArrayList<>(); +list.add(2); +list.add(1); +list.add(3); + +if (CollectionUtils.isEmpty(list)) { + System.out.println("集合为空"); +} + +if (CollectionUtils.isNotEmpty(list)) { + System.out.println("集合不为空"); +} +``` + +#### 对两个集合进行操作 + +有时候我们需要对已有的两个集合进行操作,比如取交集或者并集等。 + +```java +List list = new ArrayList<>(); +list.add(2); +list.add(1); +list.add(3); + +List list2 = new ArrayList<>(); +list2.add(2); +list2.add(4); + +//获取并集 +Collection unionList = CollectionUtils.union(list, list2); +System.out.println(unionList); + +//获取交集 +Collection intersectionList = CollectionUtils.intersection(list, list2); +System.out.println(intersectionList); + +//获取交集的补集 +Collection disjunctionList = CollectionUtils.disjunction(list, list2); +System.out.println(disjunctionList); + +//获取差集 +Collection subtractList = CollectionUtils.subtract(list, list2); +System.out.println(subtractList); +``` + +执行结果: + +```java +[1, 2, 3, 4] +[2] +[1, 3, 4] +[1, 3] +``` + +说句实话,对两个集合的操作,在实际工作中用得挺多的,特别是很多批量的场景中。以前我们需要写一堆代码,但没想到有现成的轮子。 + +### 07、小结 + 整体上,Collections 工具类作为集合框架的大管家,提供了一些非常便利的方法供我们调用,也非常容易掌握,没什么难点,看看方法的注释就能大致明白干嘛的。 不过,工具就放在那里,用是一回事,为什么要这么用就是另外一回事了。能不能提高自己的编码水平,很大程度上取决于你到底有没有去钻一钻源码,看这些设计 JDK 的大师们是如何写代码的,学会一招半式,在工作当中还是能很快脱颖而出的。 diff --git a/docs/common-tool/guava.md b/docs/common-tool/guava.md index 66c4442ad..93b98155c 100644 --- a/docs/common-tool/guava.md +++ b/docs/common-tool/guava.md @@ -12,7 +12,7 @@ head: content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,java,Guava,java guava,google guava --- -# 9.6 Guava +# 9.7 Guava ### 01、前世今生 @@ -329,7 +329,7 @@ Lists还有其他的好用的工具,我在这里只是抛砖引玉,有兴趣 ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/guava-4b962b06-a626-4707-9fe9-f5729536d9c5.jpg) -### 07、尾声 +### 08、尾声 上面介绍了我认为最常用的功能,作为 Google 公司开源的 Java 开发核心库,个人觉得实用性还是很高的(不然呢?嘿嘿嘿)。引入到你的项目后不仅能快速的实现一些开发中常用的功能,而且还可以让代码更加的优雅简洁。 diff --git a/docs/common-tool/hutool.md b/docs/common-tool/hutool.md index ba5694374..176472365 100644 --- a/docs/common-tool/hutool.md +++ b/docs/common-tool/hutool.md @@ -12,7 +12,7 @@ head: content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,java,Hutool,java hutool --- -# 9.5 Hutool +# 9.6 Hutool 读者群里有个小伙伴感慨说,“Hutool 这款开源类库太厉害了,基本上该有该的工具类,它里面都有。”讲真的,我平常工作中也经常用 Hutool,它确实可以帮助我们简化每一行代码,使 Java 拥有函数式语言般的优雅,让 Java 语言变得“甜甜的”。 diff --git a/docs/common-tool/utils.md b/docs/common-tool/utils.md index af3029fbc..dcb70d6dd 100644 --- a/docs/common-tool/utils.md +++ b/docs/common-tool/utils.md @@ -1,5 +1,5 @@ --- -title: 其他常用Java工具类:IpUtil、CollectionUtils、StringUtils、MDC、ClassUtils、BeanUtils、ReflectionUtils +title: 其他常用Java工具类:IPUtil、CollectionUtils、MDC、ClassUtils、BeanUtils、ReflectionUtils shortTitle: 其他常用Java工具类 category: - Java核心 @@ -9,10 +9,10 @@ description: Java程序员进阶之路,小白的零基础Java教程,从入 head: - - meta - name: keywords - content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,java,工具类,轮子,java 工具类 + content: Java,Java SE,Java基础,Java教程,Java程序员进阶之路,Java进阶之路,Java入门,教程,java,工具类,轮子,java 工具类,java IPUtil,java CollectionUtils, --- -# 9.7 其他常用Java工具类 +# 9.8 其他常用 Java 工具类 除了我们前面提到的 Java 原生工具类,比如说 [Arrays](https://tobebetterjavaer.com/common-tool/arrays.html)、[Objects](https://tobebetterjavaer.com/common-tool/Objects.html)、[Collections](https://tobebetterjavaer.com/common-tool/collections.html)、[Scanner](https://tobebetterjavaer.com/common-tool/scanner.html) 等,还有一些第三方的工具类,比如说 [Hutool](https://tobebetterjavaer.com/common-tool/hutool.html)、[Guava](https://tobebetterjavaer.com/common-tool/guava.html) 等,以及我们今天介绍的 IpUtil、CollectionUtils、StringUtils、MDC、ClassUtils、BeanUtils、ReflectionUtils 等等,在很大程度上能够提高我们的生产效率。 @@ -42,52 +42,53 @@ public static String getLocalIP() { 本机执行后截图如下: -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/IpUtil-f35dc96f-b8ac-43d3-9393-0ff565e85fb9.jpg) +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330093633.png) 阿里云机器执行后截图如下: -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/IpUtil-f50b0de2-cf0d-4e9b-8f10-838ea4b47fd8.jpg) - -再问一句,那是否就真的没有问题了呢?在某些情况下,可能返回的是 `127.0.0.1` - -在虚拟机中执行时,就可能遇到这个问题,截图如下 - -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/IpUtil-7c14024b-57d1-4086-9f51-d7bf312b5fbf.jpg) +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330095801.png) #### 2. 进阶版 -做一点简单的改动,获取 IpV4 的地址,源码如下 +做一点简单的改动,获取 IPV4 的地址,源码如下 ```java -/** - * 直接根据第一个网卡地址作为其内网ipv4地址,避免返回 127.0.0.1 - * - * @return - */ public static String getLocalIpByNetcard() { try { + // 枚举所有的网络接口 for (Enumeration e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) { + // 获取当前网络接口 NetworkInterface item = e.nextElement(); + + // 遍历当前网络接口的所有地址 for (InterfaceAddress address : item.getInterfaceAddresses()) { + // 忽略回环地址和未启用的网络接口 if (item.isLoopback() || !item.isUp()) { continue; } + + // 如果当前地址是 IPv4 地址,则返回其字符串表示 if (address.getAddress() instanceof Inet4Address) { Inet4Address inet4Address = (Inet4Address) address.getAddress(); return inet4Address.getHostAddress(); } } } + + // 如果没有找到任何 IPv4 地址,则返回本地主机地址 return InetAddress.getLocalHost().getHostAddress(); } catch (SocketException | UnknownHostException e) { + // 抛出运行时异常 throw new RuntimeException(e); } } ``` +需要注意的是,这段代码只返回本机的 IPv4 地址,并且只返回第一个符合条件的地址。如果本机有多个网络接口或者每个接口有多个地址,则可能无法返回预期的地址。此外,如果找不到任何 IPv4 地址,则会返回本地主机地址。 + 再次测试,输出如下 -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/IpUtil-cd2f2acb-a6ea-4675-82a8-95a7e05c8498.jpg) +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330100334.png) #### 3. 完整工具类 @@ -95,34 +96,45 @@ public static String getLocalIpByNetcard() { import java.net.*; import java.util.Enumeration; -public class IpUtil { +public class IPUtil { public static final String DEFAULT_IP = "127.0.0.1"; /** * 直接根据第一个网卡地址作为其内网ipv4地址,避免返回 127.0.0.1 * - * @return + * @return 第一个符合条件的内网 IPv4 地址 */ public static String getLocalIpByNetcard() { try { + // 枚举所有的网络接口 for (Enumeration e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) { + // 获取当前网络接口 NetworkInterface item = e.nextElement(); + // 遍历当前网络接口的所有地址 for (InterfaceAddress address : item.getInterfaceAddresses()) { + // 忽略回环地址和未启用的网络接口 if (item.isLoopback() || !item.isUp()) { continue; } + // 如果当前地址是 IPv4 地址,则返回其字符串表示 if (address.getAddress() instanceof Inet4Address) { Inet4Address inet4Address = (Inet4Address) address.getAddress(); return inet4Address.getHostAddress(); } } } + // 如果没有找到符合条件的地址,则返回本地主机地址 return InetAddress.getLocalHost().getHostAddress(); } catch (SocketException | UnknownHostException e) { throw new RuntimeException(e); } } + /** + * 获取本地主机地址 + * + * @return 本地主机地址 + */ public static String getLocalIP() { try { return InetAddress.getLocalHost().getHostAddress(); @@ -133,227 +145,13 @@ public class IpUtil { } ``` -### CollectionUtils:Spring 和 Apache 都有提供的集合工具类 - -对集合操作,除了前面说的`Collections`工具类之后,`CollectionUtils`工具类也非常常用。 - -目前比较主流的是`spring`的`org.springframework.util`包下的 CollectionUtils 工具类。 - -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/CollectionUtils-3433117c-4ab2-4ac4-bf5b-4b729d87fc9a.jpg) - -和`apache`的`org.apache.commons.collections`包下的 CollectionUtils 工具类。 - -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/CollectionUtils-1bc7dfe9-f459-47bb-ae4b-2a25d4be96c1.jpg) - -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/CollectionUtils-2b8630a3-141b-4f18-9f54-5a37fc818420.jpg) - -> 我个人更推荐使用 apache 的包下的 CollectionUtils 工具类,因为它的工具更多更全面。 - -举个简单的例子,`spring`的 CollectionUtils 工具类没有判断集合不为空的方法。而`apache`的 CollectionUtils 工具类却有。 - -下面我们以`apache`的 CollectionUtils 工具类为例,介绍一下常用方法。 - -#### 集合判空 - -通过 CollectionUtils 工具类的`isEmpty`方法可以轻松判断集合是否为空,`isNotEmpty`方法判断集合不为空。 - -```java -List list = new ArrayList<>(); -list.add(2); -list.add(1); -list.add(3); - -if (CollectionUtils.isEmpty(list)) { - System.out.println("集合为空"); -} - -if (CollectionUtils.isNotEmpty(list)) { - System.out.println("集合不为空"); -} -``` - -#### 对两个集合进行操作 - -有时候我们需要对已有的两个集合进行操作,比如取交集或者并集等。 - -```java -List list = new ArrayList<>(); -list.add(2); -list.add(1); -list.add(3); - -List list2 = new ArrayList<>(); -list2.add(2); -list2.add(4); - -//获取并集 -Collection unionList = CollectionUtils.union(list, list2); -System.out.println(unionList); - -//获取交集 -Collection intersectionList = CollectionUtils.intersection(list, list2); -System.out.println(intersectionList); - -//获取交集的补集 -Collection disjunctionList = CollectionUtils.disjunction(list, list2); -System.out.println(disjunctionList); - -//获取差集 -Collection subtractList = CollectionUtils.subtract(list, list2); -System.out.println(subtractList); -``` - -执行结果: - -```java -[1, 2, 3, 4] -[2] -[1, 3, 4] -[1, 3] -``` - -说句实话,对两个集合的操作,在实际工作中用得挺多的,特别是很多批量的场景中。以前我们需要写一堆代码,但没想到有现成的轮子。 - -### StringUtils:专为 Java 字符串而生的工具类 - -`字符串`(String)在我们的日常工作中,用得非常非常非常多。 - -在我们的代码中经常需要对字符串判空,截取字符串、转换大小写、分隔字符串、比较字符串、去掉多余空格、拼接字符串、使用正则表达式等等。 - -如果只用 String 类提供的那些方法,我们需要手写大量的额外代码,不然容易出现各种异常。 - -现在有个好消息是:`org.apache.commons.lang3`包下的`StringUtils`工具类,给我们提供了非常丰富的选择。 - -#### 字符串判空 - -其实空字符串,不只是 null 一种,还有""," ","null"等等,多种情况。 - -StringUtils 给我们提供了多个判空的静态方法,例如: - -```java - String str1 = null; -String str2 = ""; -String str3 = " "; -String str4 = "abc"; -System.out.println(StringUtils.isEmpty(str1)); -System.out.println(StringUtils.isEmpty(str2)); -System.out.println(StringUtils.isEmpty(str3)); -System.out.println(StringUtils.isEmpty(str4)); -System.out.println("====="); -System.out.println(StringUtils.isNotEmpty(str1)); -System.out.println(StringUtils.isNotEmpty(str2)); -System.out.println(StringUtils.isNotEmpty(str3)); -System.out.println(StringUtils.isNotEmpty(str4)); -System.out.println("====="); -System.out.println(StringUtils.isBlank(str1)); -System.out.println(StringUtils.isBlank(str2)); -System.out.println(StringUtils.isBlank(str3)); -System.out.println(StringUtils.isBlank(str4)); -System.out.println("====="); -System.out.println(StringUtils.isNotBlank(str1)); -System.out.println(StringUtils.isNotBlank(str2)); -System.out.println(StringUtils.isNotBlank(str3)); -System.out.println(StringUtils.isNotBlank(str4)); -``` - -执行结果: - -```java -true -true -false -false -===== -false -false -true -true -===== -true -true -true -false -===== -false -false -false -true -``` - -示例中的:`isEmpty`、`isNotEmpty`、`isBlank`和`isNotBlank`,这 4 个判空方法你们可以根据实际情况使用。 - -> 优先推荐使用`isBlank`和`isNotBlank`方法,因为它会把`" "`也考虑进去。 - -#### 分隔字符串 - -分隔字符串是常见需求,如果直接使用 String 类的 split 方法,就可能会出现空指针异常。 - -```java -String str1 = null; -System.out.println(StringUtils.split(str1,",")); -System.out.println(str1.split(",")); -``` - -执行结果: - -```java -null -Exception in thread "main" java.lang.NullPointerException -\tat com.sue.jump.service.test1.UtilTest.main(UtilTest.java:21) -``` - -使用 StringUtils 的 split 方法会返回 null,而使用 String 的 split 方法会报指针异常。 - -#### 判断是否纯数字 - -给定一个字符串,判断它是否为纯数字,可以使用`isNumeric`方法。例如: - -```java -String str1 = "123"; -String str2 = "123q"; -String str3 = "0.33"; -System.out.println(StringUtils.isNumeric(str1)); -System.out.println(StringUtils.isNumeric(str2)); -System.out.println(StringUtils.isNumeric(str3)); -``` - -执行结果: - -```java -true -false -false -``` - -#### 将集合拼接成字符串 - -有时候,我们需要将某个集合的内容,拼接成一个字符串,然后输出,这时可以使用`join`方法。例如: - -```java -List list = Lists.newArrayList("a", "b", "c"); -List list2 = Lists.newArrayList(1, 2, 3); -System.out.println(StringUtils.join(list, ",")); -System.out.println(StringUtils.join(list2, " ")); -``` - -执行结果: - -```java -a,b,c -1 2 3 -``` - -当然还有很多实用的方法,我在这里就不一一介绍了。 - -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/utils-68f94af9-d2ea-46c2-81b4-7d7e08891550.jpg) - -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/utils-7314260e-4e85-4110-a50d-3bedcbbeb616.jpg) +IPUtil 类中定义了两个方法,分别是 `getLocalIpByNetcard()` 和 `getLocalIP()`。前者是获取本机的内网 IPv4 地址,避免了返回 127.0.0.1 的问题。后者是获取本地主机地址,如果本机有多个 IP 地址,则可能返回其中的任意一个。 ### MDC:一个线程安全的参数传递工具类 -`MDC`是`org.slf4j`包下的一个类,它的全称是 Mapped Diagnostic Context,我们可以认为它是一个线程安全的存放诊断日志的容器。 +`MDC` 是 [`org.slf4j`](https://tobebetterjavaer.com/gongju/slf4j.html) 包下的一个类,它的全称是 Mapped Diagnostic Context,我们可以认为它是一个线程安全的存放诊断日志的容器。 -MDC 的底层是用了`ThreadLocal`来保存数据的。 +MDC 的底层是用了 [`ThreadLocal`](https://tobebetterjavaer.com/thread/ThreadLocal.html) 来保存数据的。 我们可以用它传递参数。 @@ -478,13 +276,13 @@ System.out.println(ClassUtils.isInnerClass(User.class)); System.out.println(ClassUtils.isCglibProxy(new User())); ``` -ClassUtils 还有很多有用的方法,等待着你去发掘。感兴趣的朋友,可以看看下面内容: +ClassUtils 还有很多有用的方法,等待着你去发掘。感兴趣的小伙伴,可以看看下面的内容: ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/utils-c58920ac-cf04-4d95-ad29-90339a086569.jpg) ### BeanUtils -spring 给我们提供了一个`JavaBean`的工具类,它在`org.springframework.beans`包下面,它的名字叫做:`BeanUtils`。 +Spring 给我们提供了一个`JavaBean`的工具类,它在`org.springframework.beans`包下面,它的名字叫做:`BeanUtils`。 让我们一起看看这个工具可以带给我们哪些惊喜。 @@ -495,8 +293,8 @@ spring 给我们提供了一个`JavaBean`的工具类,它在`org.springframewo ```java User user1 = new User(); user1.setId(1L); -user1.setName("苏三说技术"); -user1.setAddress("成都"); +user1.setName("沉默王二"); +user1.setAddress("中国"); User user2 = new User(); BeanUtils.copyProperties(user1, user2); @@ -539,7 +337,7 @@ System.out.println(propertyForMethod.getName()); 有时候,我们需要在项目中使用`反射`功能,如果使用最原始的方法来开发,代码量会非常多,而且很麻烦,它需要处理一大堆异常以及访问权限等问题。 -好消息是 spring 给我们提供了一个`ReflectionUtils`工具,它在`org.springframework.util`包下面。 +好消息是 Spring 给我们提供了一个`ReflectionUtils`工具,它在`org.springframework.util`包下面。 #### 获取方法 @@ -587,6 +385,8 @@ System.out.println(ReflectionUtils.isEqualsMethod(method)); ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/utils-0a4ecb9c-b9d2-4090-a7b7-c626a0672b94.jpg) +>参考链接:[https://juejin.cn/post/7102418518599008286](https://juejin.cn/post/7102418518599008286) 作者:苏三,编辑:沉默王二 + --- 最近整理了一份牛逼的学习资料,包括但不限于 Java 基础部分(JVM、Java 集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类 Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是 2022 年全网最全的学习和找工作的 PDF 资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) diff --git a/docs/home.md b/docs/home.md index f662ff9c9..19b890291 100644 --- a/docs/home.md +++ b/docs/home.md @@ -208,11 +208,12 @@ head: - [Java Scanner:扫描控制台输入的工具类](common-tool/scanner.md) - [Java Arrays:专为数组而生的工具类](common-tool/arrays.md) +- [Apache StringUtils:专为Java字符串而生的工具类](common-tool/StringUtils.md) - [Objects:专为操作Java对象而生的工具类](common-tool/Objects.md) - [Java Collections:专为集合而生的工具类](common-tool/collections.md) - [Hutool:国产良心工具包,让你的Java变得更甜](common-tool/hutool.md) - [Guava:Google开源的Java工具库,太强大了](common-tool/guava.md) -- [其他常用Java工具类:IpUtil、CollectionUtils、StringUtils、MDC、ClassUtils、BeanUtils、ReflectionUtils](common-tool/utils.md) +- [其他常用Java工具类:IpUtil、MDC、ClassUtils、BeanUtils、ReflectionUtils](common-tool/utils.md) ### Java新特性 diff --git "a/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" "b/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" index 5004feca7..8bbee8672 100644 --- "a/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" +++ "b/\344\272\214\345\223\245\347\232\204 Java \350\277\233\351\230\266\344\271\213\350\267\257.md" @@ -24575,7 +24575,182 @@ System.out.println(Arrays.toString(arr)); ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) -## 9.3 Objects +## 9.3 Apache StringUtils + +`字符串`([String](https://tobebetterjavaer.com/string/immutable.html))在我们的日常工作中,用得非常非常非常多。 + +在我们的代码中经常需要对字符串判空,截取字符串、转换大小写、[分隔字符串](https://tobebetterjavaer.com/string/split.html)、[比较字符串](https://tobebetterjavaer.com/string/equals.html)、去掉多余空格、[拼接字符串](https://tobebetterjavaer.com/string/join.html)、使用正则表达式等等。 + +如果只用 String 类提供的那些方法,我们需要手写大量的额外代码,不然容易出现各种异常。 + +现在有个好消息是:`org.apache.commons.lang3`包下的`StringUtils`工具类,给我们提供了非常丰富的选择。 + +Maven 坐标: + +``` + + org.apache.commons + commons-lang3 + 3.12.0 + +``` + +StringUtils 提供了非常多实用的方法,大概有下图的四页到五页,我只截了两页,实在是太多了。 + +![](https://cdn.tobebetterjavaer.com/stutymore/StringUtils-20230330111122.png) + +接下来,我们来拿一些常用的方法举例说明。 + +### 字符串判空 + +其实空字符串,不只是 null 一种,还有""," ","null"等等,多种情况。 + +StringUtils 给我们提供了多个判空的静态方法,例如: + +```java +String str1 = null; +String str2 = ""; +String str3 = " "; +String str4 = "abc"; +System.out.println(StringUtils.isEmpty(str1)); +System.out.println(StringUtils.isEmpty(str2)); +System.out.println(StringUtils.isEmpty(str3)); +System.out.println(StringUtils.isEmpty(str4)); +System.out.println("====="); +System.out.println(StringUtils.isNotEmpty(str1)); +System.out.println(StringUtils.isNotEmpty(str2)); +System.out.println(StringUtils.isNotEmpty(str3)); +System.out.println(StringUtils.isNotEmpty(str4)); +System.out.println("====="); +System.out.println(StringUtils.isBlank(str1)); +System.out.println(StringUtils.isBlank(str2)); +System.out.println(StringUtils.isBlank(str3)); +System.out.println(StringUtils.isBlank(str4)); +System.out.println("====="); +System.out.println(StringUtils.isNotBlank(str1)); +System.out.println(StringUtils.isNotBlank(str2)); +System.out.println(StringUtils.isNotBlank(str3)); +System.out.println(StringUtils.isNotBlank(str4)); +``` + +执行结果: + +```java +true +true +false +false +===== +false +false +true +true +===== +true +true +true +false +===== +false +false +false +true +``` + +示例中的:`isEmpty`、`isNotEmpty`、`isBlank`和`isNotBlank`,这 4 个判空方法你们可以根据实际情况使用。 + +优先推荐使用`isBlank`和`isNotBlank`方法,因为它会把`" "`也考虑进去。 + +### 分隔字符串 + +分隔字符串是常见需求,如果直接使用 String 类的 split 方法,就可能会出现空指针异常。 + +```java +String str1 = null; +System.out.println(StringUtils.split(str1,",")); +System.out.println(str1.split(",")); +``` + +执行结果: + +```java +null +Exception in thread "main" java.lang.NullPointerException +\tat com.sue.jump.service.test1.UtilTest.main(UtilTest.java:21) +``` + +使用 StringUtils 的 split 方法会返回 null,而使用 String 的 split 方法会报指针异常。 + +### 判断是否纯数字 + +给定一个字符串,判断它是否为纯数字,可以使用`isNumeric`方法。例如: + +```java +String str1 = "123"; +String str2 = "123q"; +String str3 = "0.33"; +System.out.println(StringUtils.isNumeric(str1)); +System.out.println(StringUtils.isNumeric(str2)); +System.out.println(StringUtils.isNumeric(str3)); +``` + +执行结果: + +```java +true +false +false +``` + +### 将集合拼接成字符串 + +有时候,我们需要将某个集合的内容,拼接成一个字符串,然后输出,这时可以使用`join`方法。例如: + +```java +List list = Lists.newArrayList("a", "b", "c"); +List list2 = Lists.newArrayList(1, 2, 3); +System.out.println(StringUtils.join(list, ",")); +System.out.println(StringUtils.join(list2, " ")); +``` + +执行结果: + +```java +a,b,c +1 2 3 +``` + +### 其他方法 + +这里再列举一些,其他的方法可以自己去研究一下。 + +- `trim(String str)`:去除字符串首尾的空白字符。 +- `trimToEmpty(String str)`:去除字符串首尾的空白字符,如果字符串为 null,则返回空字符串。 +- `trimToNull(String str)`:去除字符串首尾的空白字符,如果结果为空字符串,则返回 null。 +- `equals(String str1, String str2)`:比较两个字符串是否相等。 +- `equalsIgnoreCase(String str1, String str2)`:比较两个字符串是否相等,忽略大小写。 +- `startsWith(String str, String prefix)`:检查字符串是否以指定的前缀开头。 +- `endsWith(String str, String suffix)`:检查字符串是否以指定的后缀结尾。 +- `contains(String str, CharSequence seq)`:检查字符串是否包含指定的字符序列。 +- `indexOf(String str, CharSequence seq)`:返回指定字符序列在字符串中首次出现的索引,如果没有找到,则返回 -1。 +- `lastIndexOf(String str, CharSequence seq)`:返回指定字符序列在字符串中最后一次出现的索引,如果没有找到,则返回 -1。 +- `substring(String str, int start, int end)`:截取字符串中指定范围的子串。 +- `replace(String str, String searchString, String replacement)`:替换字符串中所有出现的搜索字符串为指定的替换字符串。 +- `replaceAll(String str, String regex, String replacement)`:使用正则表达式替换字符串中所有匹配的部分。 +- `join(Iterable iterable, String separator)`:使用指定的分隔符将可迭代对象中的元素连接为一个字符串。 +- `split(String str, String separator)`:使用指定的分隔符将字符串分割为一个字符串数组。 +- `capitalize(String str)`:将字符串的第一个字符转换为大写。 +- `uncapitalize(String str)`:将字符串的第一个字符转换为小写。 + +---- + +最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) + +微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。 + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) + +## 9.4 Objects Java 的 Objects 类是一个实用工具类,包含了一系列静态方法,用于处理对象。它位于 java.util 包中,自 Java 7 引入。Objects 类的主要目的是降低代码中的[空指针异常](https://tobebetterjavaer.com/exception/npe.html) (NullPointerException) 风险,同时提供一些非常实用的方法供我们使用。 @@ -24773,7 +24948,7 @@ System.out.println(Objects.deepEquals(nestedArray1, nestedArray3)); // 输出: ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) -## 9.4 Collections +## 9.5 Collections Collections 是 JDK 提供的一个工具类,位于 java.util 包下,提供了一系列的静态方法,方便我们对集合进行各种骚操作,算是集合框架的一个大管家。 @@ -25011,6 +25186,93 @@ addAll 后:[沉默王九, 沉默王十, 沉默王二] 是否没有交集:否 ``` +### 06、CollectionUtils:Spring 和 Apache 都有提供的集合工具类 + +对集合操作,除了前面说的 JDK 原生 `Collections` 工具类,`CollectionUtils`工具类也很常用。 + +目前比较主流的是`Spring`的`org.springframework.util`包下的 CollectionUtils 工具类。 + +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330101919.png) + +和`Apache`的`org.apache.commons.collections`包下的 CollectionUtils 工具类。 + +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330103825.png) + +Maven 坐标如下: + +``` + + org.apache.commons + commons-collections4 + 4.4 + +``` + +Apache 的方法比 Spring 的更多一些,我们就以 Apache 的为例,来介绍一下常用的方法。 + +#### 集合判空 + +通过 CollectionUtils 工具类的`isEmpty`方法可以轻松判断集合是否为空,`isNotEmpty`方法判断集合不为空。 + +```java +List list = new ArrayList<>(); +list.add(2); +list.add(1); +list.add(3); + +if (CollectionUtils.isEmpty(list)) { + System.out.println("集合为空"); +} + +if (CollectionUtils.isNotEmpty(list)) { + System.out.println("集合不为空"); +} +``` + +#### 对两个集合进行操作 + +有时候我们需要对已有的两个集合进行操作,比如取交集或者并集等。 + +```java +List list = new ArrayList<>(); +list.add(2); +list.add(1); +list.add(3); + +List list2 = new ArrayList<>(); +list2.add(2); +list2.add(4); + +//获取并集 +Collection unionList = CollectionUtils.union(list, list2); +System.out.println(unionList); + +//获取交集 +Collection intersectionList = CollectionUtils.intersection(list, list2); +System.out.println(intersectionList); + +//获取交集的补集 +Collection disjunctionList = CollectionUtils.disjunction(list, list2); +System.out.println(disjunctionList); + +//获取差集 +Collection subtractList = CollectionUtils.subtract(list, list2); +System.out.println(subtractList); +``` + +执行结果: + +```java +[1, 2, 3, 4] +[2] +[1, 3, 4] +[1, 3] +``` + +说句实话,对两个集合的操作,在实际工作中用得挺多的,特别是很多批量的场景中。以前我们需要写一堆代码,但没想到有现成的轮子。 + +### 07、小结 + 整体上,Collections 工具类作为集合框架的大管家,提供了一些非常便利的方法供我们调用,也非常容易掌握,没什么难点,看看方法的注释就能大致明白干嘛的。 不过,工具就放在那里,用是一回事,为什么要这么用就是另外一回事了。能不能提高自己的编码水平,很大程度上取决于你到底有没有去钻一钻源码,看这些设计 JDK 的大师们是如何写代码的,学会一招半式,在工作当中还是能很快脱颖而出的。 @@ -25029,180 +25291,731 @@ addAll 后:[沉默王九, 沉默王十, 沉默王二] ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) -## 9.6 Guava +## 9.6 Hutool -### 01、前世今生 - -你好呀,我是 Guava。 +读者群里有个小伙伴感慨说,“Hutool 这款开源类库太厉害了,基本上该有该的工具类,它里面都有。”讲真的,我平常工作中也经常用 Hutool,它确实可以帮助我们简化每一行代码,使 Java 拥有函数式语言般的优雅,让 Java 语言变得“甜甜的”。 -我由 Google 公司开源,目前在 GitHub 上已经有 39.9k 的铁粉了,由此可以证明我的受欢迎程度。 +Hutool 的作者在[官网](https://hutool.cn/)上说,Hutool 是 Hu+tool 的自造词(好像不用说,我们也能猜得到),“Hu”用来致敬他的“前任”公司,“tool”就是工具的意思,谐音就有意思了,“糊涂”,寓意追求“万事都作糊涂观,无所谓失,无所谓得”(一个开源类库,上升到了哲学的高度,作者厉害了)。 -![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/guava-01.png) +看了一下开发团队的一个成员介绍,一个 Java 后端工具的作者竟然爱前端、爱数码,爱美女,嗯嗯嗯,确实“难得糊涂”(手动狗头)。 -我的身体里主要包含有这些常用的模块:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等。新版的 JDK 中已经直接把我引入了,可想而知我有多优秀,忍不住骄傲了。 +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/hutool-01.png) -这么说吧,学好如何使用我,能让你在编程中变得更快乐,写出更优雅的代码! +废话就说到这,来吧,实操走起! -### 02、引入 Guava +### 01、引入 Hutool -如果你要在 Maven 项目使用我的话,需要先在 pom.xml 文件中引入我的依赖。 +Maven 项目只需要在 pom.xml 文件中添加以下依赖即可。 ``` - com.google.guava - guava - 30.1-jre + cn.hutool + hutool-all + 5.4.3 ``` -一点要求,JDK 版本需要在 8 以上。 +Hutool 的设计思想是尽量减少重复的定义,让项目中的 util 包尽量少。一个好的轮子可以在很大程度上避免“复制粘贴”,从而节省我们开发人员对项目中公用类库和公用工具方法的封装时间。同时呢,成熟的开源库也可以最大限度的避免封装不完善带来的 bug。 -### 03、基本工具 +就像作者在官网上说的那样: -Doug Lea,java.util.concurrent 包的作者,曾说过一句话:“[null 真糟糕](https://tobebetterjavaer.com/exception/npe.html)”。Tony Hoare,图灵奖得主、快速排序算法的作者,当然也是 null 的创建者,也曾说过类似的话:“null 的使用,让我损失了十亿美元。”鉴于此,我用 Optional 来表示可能为 null 的对象。 +- 以前,我们打开搜索引擎 -> 搜“Java MD5 加密” -> 打开某篇博客 -> 复制粘贴 -> 改改,变得好用些 +- 有了 Hutool 以后呢,引入 Hutool -> 直接 `SecureUtil.md5()` -![](https://cdn.tobebetterjavaer.com/stutymore/guava-20230329172935.png) +Hutool 对不仅对 JDK 底层的文件、流、加密解密、转码、正则、线程、XML等做了封装,还提供了以下这些组件: +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/hutool-02.png) -代码示例如下所示。 +非常多,非常全面,鉴于此,我只挑选一些我喜欢的来介绍下(偷偷地告诉你,我就是想偷懒)。 -```java -Optional possible = Optional.of(5); -possible.isPresent(); // returns true -possible.get(); // returns 5 -``` +### 02、类型转换 -我大哥 Java 在 JDK 8 中新增了 [Optional 类](https://tobebetterjavaer.com/java8/optional.html),显然是从我这借鉴过去的,不过他的和我的有些不同。 +类型转换在 Java 开发中很常见,尤其是从 HttpRequest 中获取参数的时候,前端传递的是整型,但后端只能先获取到字符串,然后再调用 `parseXXX()` 方法进行转换,还要加上判空,很繁琐。 -- 我的 Optional 是 abstract 的,意味着我可以有子类对象;我大哥的是 final 的,意味着没有子类对象。 -- 我的 Optional 实现了 Serializable 接口,可以序列化;我大哥的没有。 -- 我的一些方法和我大哥的也不尽相同。 +Hutool 的 Convert 类可以简化这个操作,可以将任意可能的类型转换为指定类型,同时第二个参数 defaultValue 可用于在转换失败时返回一个默认值。 -使用 Optional 除了赋予 null 语义,增加了可读性,最大的优点在于它是一种傻瓜式的防护。Optional 迫使你积极思考引用缺失的情况,因为你必须显式地从 Optional 获取引用。 +```java +String param = "10"; +int paramInt = Convert.toInt(param); +int paramIntDefault = Convert.toInt(param, 0); +``` -除了 Optional 之外,我还提供了: +把字符串转换成日期: -- 参数校验 -- 常见的 Object 方法,比如说 Objects.equals、Objects.hashCode,JDK 7 引入的 Objects 类提供同样的方法,当然也是从我这借鉴的灵感。 -- 更强大的比较器 +```java +String dateStr = "2020年09月29日"; +Date date = Convert.toDate(dateStr); +``` -### 04、集合 +把字符串转成 Unicode: -首先我来说一下,为什么需要不可变集合。 +```java +String unicodeStr = "沉默王二"; +String unicode = Convert.strToUnicode(unicodeStr); +``` -- 保证线程安全。在并发程序中,使用不可变集合既保证线程的安全性,也大大地增强了并发时的效率(跟并发锁方式相比)。 -- 如果一个对象不需要支持修改操作,不可变的集合将会节省空间和时间的开销。 -- 可以当作一个常量来对待,并且集合中的对象在以后也不会被改变。 +### 03、日期时间 -与 JDK 中提供的不可变集合相比,我提供的 Immutable 才是真正的不可变,我为什么这么说呢?来看下面这个示例。 +JDK 自带的 Date 和 Calendar 不太好用,Hutool 封装的 DateUtil 用起来就舒服多了! -下面的代码利用 JDK 的 [`Collections.unmodifiableList(list)`](https://tobebetterjavaer.com/common-tool/collections.html) 得到一个不可修改的集合 unmodifiableList。 +获取当前日期: ```java -List list = new ArrayList(); -list.add("雷军"); -list.add("乔布斯"); - -List unmodifiableList = Collections.unmodifiableList(list); -unmodifiableList.add("马云"); +Date date = DateUtil.date(); ``` -运行代码将会出现以下异常: +`DateUtil.date()` 返回的其实是 DateTime,它继承自 Date 对象,重写了 `toString()` 方法,返回 `yyyy-MM-dd HH:mm:ss` 格式的字符串。 + +有些小伙伴是不是想看看我写这篇文章的时间,输出一下给大家看看: ``` -Exception in thread "main" java.lang.UnsupportedOperationException - at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1060) - at com.itwanger.guava.NullTest.main(NullTest.java:29) +System.out.println(date);// 2020-09-29 04:28:02 ``` -很好,执行 `unmodifiableList.add()` 的时候抛出了 UnsupportedOperationException 异常,说明 `Collections.unmodifiableList()` 返回了一个不可变集合。但真的是这样吗? +字符串转日期: -你可以把 `unmodifiableList.add()` 换成 `list.add()`。 +```java +String dateStr = "2020-09-29"; +Date date = DateUtil.parse(dateStr); +``` + +`DateUtil.parse()` 会自动识别一些常用的格式,比如说: + +- yyyy-MM-dd HH:mm:ss +- yyyy-MM-dd +- HH:mm:ss +- yyyy-MM-dd HH:mm +- yyyy-MM-dd HH:mm:ss.SSS + +还可以识别带中文的: + +- 年月日时分秒 + +格式化时间差: ```java -List list = new ArrayList(); -list.add("雷军"); -list.add("乔布斯"); +String dateStr1 = "2020-09-29 22:33:23"; +Date date1 = DateUtil.parse(dateStr1); -List unmodifiableList = Collections.unmodifiableList(list); -list.add("马云"); -``` +String dateStr2 = "2020-10-01 23:34:27"; +Date date2 = DateUtil.parse(dateStr2); -再次执行的话,程序并没有报错,并且你会发现 unmodifiableList 中真的多了一个元素。说明什么呢? +long betweenDay = DateUtil.between(date1, date2, DateUnit.MS); -`Collections.unmodifiableList(…)` 实现的不是真正的不可变集合,当原始集合被修改后,不可变集合里面的元素也是跟着发生变化。 +// 输出:2天1小时1分4秒 +String formatBetween = DateUtil.formatBetween(betweenDay, BetweenFormater.Level.SECOND); +``` -我就不会犯这种错,来看下面的代码。 +星座和属相: ```java -List stringArrayList = Lists.newArrayList("雷军","乔布斯"); -ImmutableList immutableList = ImmutableList.copyOf(stringArrayList); -immutableList.add("马云"); +// 射手座 +String zodiac = DateUtil.getZodiac(Month.DECEMBER.getValue(), 10); +// 蛇 +String chineseZodiac = DateUtil.getChineseZodiac(1989); ``` -尝试 `immutableList.add()` 的时候会抛出 `UnsupportedOperationException`。我在源码中已经把 `add()` 方法废弃了。 +### 04、IO 流相关 + +[IO 操作包括读和写](https://tobebetterjavaer.com/io/shangtou.html),应用的场景主要包括网络操作和文件操作,原生的 Java 类库区分[字符流](https://tobebetterjavaer.com/io/reader-writer.html)和[字节流](https://tobebetterjavaer.com/io/stream.html),字节流 InputStream 和 OutputStream 就有很多很多种,使用起来让人头皮发麻。 + +Hutool 封装了流操作工具类 IoUtil、文件读写操作工具类 FileUtil、文件类型判断工具类 FileTypeUtil 等等。 ```java -/** - * Guaranteed to throw an exception and leave the collection unmodified. - * - * @throws UnsupportedOperationException always - * @deprecated Unsupported operation. - */ -@CanIgnoreReturnValue -@Deprecated -@Override -public final boolean add(E e) { - throw new UnsupportedOperationException(); -} +BufferedInputStream in = FileUtil.getInputStream("hutool/origin.txt"); +BufferedOutputStream out = FileUtil.getOutputStream("hutool/to.txt"); +long copySize = IoUtil.copy(in, out, IoUtil.DEFAULT_BUFFER_SIZE); ``` -尝试 `stringArrayList.add()` 修改原集合的时候 immutableList 并不会因此而发生改变。 +在 IO 操作中,文件的操作相对来说是比较复杂的,但使用频率也很高,几乎所有的项目中都躺着一个叫 FileUtil 或者 FileUtils 的工具类。Hutool 的 FileUtil 类包含以下几类操作: -除了不可变集合以外,我还提供了新的集合类型,比如说: +- 文件操作:包括文件目录的新建、删除、复制、移动、改名等 +- 文件判断:判断文件或目录是否非空,是否为目录,是否为文件等等 +- 绝对路径:针对 ClassPath 中的文件转换为绝对路径文件 +- 文件名:主文件名,扩展名的获取 +- 读操作:包括 getReader、readXXX 操作 +- 写操作:包括 getWriter、writeXXX 操作 -- Multiset,可以多次添加相等的元素。当把 Multiset 看成普通的 Collection 时,它表现得就像无序的 ArrayList;当把 Multiset 看作 `Map` 时,它也提供了符合性能期望的查询操作。 -- Multimap,可以很容易地把一个键映射到多个值。 -- BiMap,一种特殊的 Map,可以用 `inverse()` 反转 - `BiMap` 的键值映射;保证值是唯一的,因此 `values()` 返回 Set 而不是普通的 Collection。 +顺带说说 classpath。 -### 05、字符串处理 +在实际编码当中,我们通常需要从某些文件里面读取一些数据,比如配置文件、文本文件、图片等等,那这些文件通常放在什么位置呢? -字符串表示字符的不可变序列,创建后就不能更改。在我们日常的工作中,字符串的使用非常频繁,熟练的对其操作可以极大的提升我们的工作效率。 +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/hutool-03.png) -我提供了连接器——Joiner,可以用分隔符把字符串序列连接起来。下面的代码将会返回“雷军; 乔布斯”,你可以使用 `useForNull(String)` 方法用某个字符串来替换 null,而不像 `skipNulls()` 方法那样直接忽略 null。 +放在项目结构图中的 resources 目录下,当项目编译后,会出现在 classes 目录下。对应磁盘上的目录如下图所示: + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/hutool-04.png) + +当我们要读取文件的时候,我是不建议使用绝对路径的,因为操作系统不一样的话,文件的路径标识符也是不一样的。最好使用相对路径。 + +假设在 `src/resources` 下放了一个文件 origin.txt,文件的路径参数如下所示: ```java -Joiner joiner = Joiner.on("; ").skipNulls(); -return joiner.join("雷军", null, "乔布斯"); +FileUtil.getInputStream("origin.txt") ``` -我还提供了拆分器—— Splitter,可以按照指定的分隔符把字符串序列进行拆分。 +假设文件放在 `src/resources/hutool` 目录下,则路径参数改为: ```java -Splitter.on(',') - .trimResults() - .omitEmptyStrings() - .split("雷军,乔布斯,, 沉默王二"); +FileUtil.getInputStream("hutool/origin.txt") ``` -### 06、缓存 +### 05、字符串工具 -缓存在很多场景下都是相当有用的。你应该知道,检索一个值的代价很高,尤其是需要不止一次获取值的时候,就应当考虑使用缓存。 +Hutool 封装的字符串工具类 StrUtil 和 Apache Commons Lang 包中的 StringUtils 类似,作者认为优势在于 Str 比 String 短,尽管我不觉得。不过,我倒是挺喜欢其中的一个方法的: -我提供的 Cache 和 ConcurrentMap 很相似,但也不完全一样。最基本的区别是 ConcurrentMap 会一直保存所有添加的元素,直到显式地移除。相对地,我提供的 Cache 为了限制内存占用,通常都设定为自动回收元素。 +```java +String template = "{},一枚沉默但有趣的程序员,喜欢他的文章的话,请微信搜索{}"; +String str = StrUtil.format(template, "沉默王二", "沉默王二"); +// 沉默王二,一枚沉默但有趣的程序员,喜欢他的文章的话,请微信搜索沉默王二 +``` -如果你愿意消耗一些内存空间来提升速度,你能预料到某些键会被查询一次以上,缓存中存放的数据总量不会超出内存容量,就可以使用 Cache。 +### 06、反射工具 -来个示例你感受下吧。 +反射机制可以让 Java 变得更加灵活,因此在某些情况下,反射可以做到事半功倍的效果。Hutool 封装的反射工具 ReflectUtil 包括: + +- 获取构造方法 +- 获取字段 +- 获取字段值 +- 获取方法 +- 执行方法(对象方法和静态方法) ```java -@Test -public void testCache() throws ExecutionException, InterruptedException { +package com.itwanger.hutool.reflect; - CacheLoader cacheLoader = new CacheLoader() { - // 如果找不到元素,会调用这里 - @Override +import cn.hutool.core.util.ReflectUtil; + +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +public class ReflectDemo { + private int id; + + public ReflectDemo() { + System.out.println("构造方法"); + } + + public void print() { + System.out.println("我是沉默王二"); + } + + public static void main(String[] args) throws IllegalAccessException { + // 构建对象 + ReflectDemo reflectDemo = ReflectUtil.newInstance(ReflectDemo.class); + + // 获取构造方法 + Constructor[] constructors = ReflectUtil.getConstructors(ReflectDemo.class); + for (Constructor constructor : constructors) { + System.out.println(constructor.getName()); + } + + // 获取字段 + Field field = ReflectUtil.getField(ReflectDemo.class, "id"); + field.setInt(reflectDemo, 10); + // 获取字段值 + System.out.println(ReflectUtil.getFieldValue(reflectDemo, field)); + + // 获取所有方法 + Method[] methods = ReflectUtil.getMethods(ReflectDemo.class); + for (Method m : methods) { + System.out.println(m.getName()); + } + + // 获取指定方法 + Method method = ReflectUtil.getMethod(ReflectDemo.class, "print"); + System.out.println(method.getName()); + + + // 执行方法 + ReflectUtil.invoke(reflectDemo, "print"); + } +} +``` + +### 07、压缩工具 + +在 Java 中,对文件、文件夹打包压缩是一件很繁琐的事情,Hutool 封装的 ZipUtil 针对 java.util.zip 包做了优化,可以使用一个方法搞定压缩和解压,并且自动处理文件和目录的问题,不再需要用户判断,大大简化的压缩解压的复杂度。 + +```java +ZipUtil.zip("hutool", "hutool.zip"); +File unzip = ZipUtil.unzip("hutool.zip", "hutoolzip"); +``` + +### 08、身份证工具 + +Hutool 封装的 IdcardUtil 可以用来对身份证进行验证,支持大陆 15 位、18 位身份证,港澳台 10 位身份证。 + +```java +String ID_18 = "321083197812162119"; +String ID_15 = "150102880730303"; + +boolean valid = IdcardUtil.isValidCard(ID_18); +boolean valid15 = IdcardUtil.isValidCard(ID_15); +``` + +### 09、扩展 HashMap + +Java 中的 HashMap 是强类型的,而 Hutool 封装的 Dict 对键的类型要求没那么严格。 + +```java +Dict dict = Dict.create() + .set("age", 18) + .set("name", "沉默王二") + .set("birthday", DateTime.now()); + +int age = dict.getInt("age"); +String name = dict.getStr("name"); +``` + +### 10、控制台打印 + +本地编码的过程中,经常需要使用 `System.out` 打印结果,但是往往一些复杂的对象不支持直接打印,比如说数组,需要调用 `Arrays.toString`。Hutool 封装的 Console 类借鉴了 JavaScript 中的 `console.log()`,使得打印变成了一个非常便捷的方式。 + +```java +public class ConsoleDemo { + public static void main(String[] args) { + // 打印字符串 + Console.log("沉默王二,一枚有趣的程序员"); + + // 打印字符串模板 + Console.log("洛阳是{}朝古都",13); + + int [] ints = {1,2,3,4}; + // 打印数组 + Console.log(ints); + } +} +``` + +### 11、字段验证器 + +做 Web 开发的时候,后端通常需要对表单提交过来的数据进行验证。Hutool 封装的 Validator 可以进行很多有效的条件验证: + +- 是不是邮箱 +- 是不是 IP V4、V6 +- 是不是电话号码 +- 等等 + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/hutool-05.png) + +```java +Validator.isEmail("沉默王二"); +Validator.isMobile("itwanger.com"); +``` + +### 12、双向查找 Map + +[Guava](https://tobebetterjavaer.com/common-tool/guava.html) 中提供了一种特殊的 Map 结构,叫做 BiMap,实现了一种双向查找的功能,可以根据 key 查找 value,也可以根据 value 查找 key,Hutool 也提供这种 Map 结构。 + +```java +BiMap biMap = new BiMap<>(new HashMap<>()); +biMap.put("wanger", "沉默王二"); +biMap.put("wangsan", "沉默王三"); + +// get value by key +biMap.get("wanger"); +biMap.get("wangsan"); + +// get key by value +biMap.getKey("沉默王二"); +biMap.getKey("沉默王三"); +``` + +在实际的开发工作中,其实我更倾向于使用 Guava 的 BiMap,而不是 Hutool 的。这里提一下,主要是我发现了 Hutool 在线文档上的一处错误,提了个 issue(从中可以看出我一颗一丝不苟的心和一双清澈明亮的大眼睛啊)。 + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/hutool-06.png) + +### 13、图片工具 + +Hutool 封装的 ImgUtil 可以对图片进行缩放、裁剪、转为黑白、加水印等操作。 + +缩放图片: + +```java +ImgUtil.scale( + FileUtil.file("hutool/wangsan.jpg"), + FileUtil.file("hutool/wangsan_small.jpg"), + 0.5f +); +``` + +裁剪图片: + +```java +ImgUtil.cut( + FileUtil.file("hutool/wangsan.jpg"), + FileUtil.file("hutool/wangsan_cut.jpg"), + new Rectangle(200, 200, 100, 100) +); +``` + +添加水印: + +```java +ImgUtil.pressText(// + FileUtil.file("hutool/wangsan.jpg"), + FileUtil.file("hutool/wangsan_logo.jpg"), + "沉默王二", Color.WHITE, + new Font("黑体", Font.BOLD, 100), + 0, + 0, + 0.8f +); +``` + +趁机让大家欣赏一下二哥帅气的真容。 + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/hutool-07.png) + +### 14、配置文件 + +>众所周知,Java 中广泛应用的配置文件 Properties 存在一个特别大的诟病:不支持中文。每次使用时,如果想存放中文字符,就必须借助 IDE 相关插件才能转为 Unicode 符号,而这种反人类的符号在命令行下根本没法看。 + +于是,Hutool 的 Setting 运用而生。Setting 除了兼容 Properties 文件格式外,还提供了一些特有功能,这些功能包括: + +- 各种编码方式支持 +- 变量支持 +- 分组支持 + +先整个配置文件 example.setting,内容如下: + +``` +name=沉默王二 +age=18 +``` + +再来读取和更新配置文件: + +```java +public class SettingDemo { + private final static String SETTING = "hutool/example.setting"; + public static void main(String[] args) { + // 初始化 Setting + Setting setting = new Setting(SETTING); + + // 读取 + setting.getStr("name", "沉默王二"); + + // 在配置文件变更时自动加载 + setting.autoLoad(true); + + // 通过代码方式增加键值对 + setting.set("birthday", "2020年09月29日"); + setting.store(SETTING); + } +} +``` + +### 15、日志工厂 + +Hutool 封装的日志工厂 LogFactory 兼容了各大日志框架,使用起来也非常简便。 + +```java +public class LogDemo { + private static final Log log = LogFactory.get(); + + public static void main(String[] args) { + log.debug("难得糊涂"); + } +} +``` + +先通过 `LogFactory.get()` 自动识别引入的日志框架,从而创建对应日志框架的门面 Log 对象,然后调用 `debug()`、`info()` 等方法输出日志。 + +如果不想创建 Log 对象的话,可以使用 StaticLog,顾名思义,一个提供了静态方法的日志类。 + +```java +StaticLog.info("爽啊 {}.", "沉默王二的文章"); +``` + +### 16、缓存工具 + +CacheUtil 是 Hutool 封装的创建缓存的快捷工具类,可以创建不同的缓存对象: + +- FIFOCache:先入先出,元素不停的加入缓存直到缓存满为止,当缓存满时,清理过期缓存对象,清理后依旧满则删除先入的缓存。 + +```java +Cache fifoCache = CacheUtil.newFIFOCache(3); +fifoCache.put("key1", "沉默王一"); +fifoCache.put("key2", "沉默王二"); +fifoCache.put("key3", "沉默王三"); +fifoCache.put("key4", "沉默王四"); + +// 大小为 3,所以 key3 放入后 key1 被清除 +String value1 = fifoCache.get("key1"); +``` + +- LFUCache,最少使用,根据使用次数来判定对象是否被持续缓存,当缓存满时清理过期对象,清理后依旧满的情况下清除最少访问的对象并将其他对象的访问数减去这个最少访问数,以便新对象进入后可以公平计数。 + +```java +Cache lfuCache = CacheUtil.newLFUCache(3); + +lfuCache.put("key1", "沉默王一"); +// 使用次数+1 +lfuCache.get("key1"); +lfuCache.put("key2", "沉默王二"); +lfuCache.put("key3", "沉默王三"); +lfuCache.put("key4", "沉默王四"); + +// 由于缓存容量只有 3,当加入第 4 个元素的时候,最少使用的将被移除(2,3被移除) +String value2 = lfuCache.get("key2"); +String value3 = lfuCache.get("key3"); +``` + +- LRUCache,最近最久未使用,根据使用时间来判定对象是否被持续缓存,当对象被访问时放入缓存,当缓存满了,最久未被使用的对象将被移除。 + +```java +Cache lruCache = CacheUtil.newLRUCache(3); + +lruCache.put("key1", "沉默王一"); +lruCache.put("key2", "沉默王二"); +lruCache.put("key3", "沉默王三"); +// 使用时间近了 +lruCache.get("key1"); +lruCache.put("key4", "沉默王四"); + +// 由于缓存容量只有 3,当加入第 4 个元素的时候,最久使用的将被移除(2) +String value2 = lruCache.get("key2"); +System.out.println(value2); +``` + +### 17、加密解密 + +加密分为三种: + +- 对称加密(symmetric),例如:AES、DES 等 +- 非对称加密(asymmetric),例如:RSA、DSA 等 +- 摘要加密(digest),例如:MD5、SHA-1、SHA-256、HMAC 等 + +Hutool 针对这三种情况都做了封装: + +- 对称加密 SymmetricCrypto +- 非对称加密 AsymmetricCrypto +- 摘要加密 Digester + +快速加密工具类 SecureUtil 有以下这些方法: + +1)对称加密 + +- SecureUtil.aes +- SecureUtil.des + +2)非对称加密 + +- SecureUtil.rsa +- SecureUtil.dsa + +3)摘要加密 + +- SecureUtil.md5 +- SecureUtil.sha1 +- SecureUtil.hmac +- SecureUtil.hmacMd5 +- SecureUtil.hmacSha1 + +只写一个简单的例子作为参考: + +```java +public class SecureUtilDemo { + static AES aes = SecureUtil.aes(); + public static void main(String[] args) { + String encry = aes.encryptHex("沉默王二"); + System.out.println(encry); + String oo = aes.decryptStr(encry); + System.out.println(oo); + } +} +``` + +### 18、其他类库 + +Hutool 中的类库还有很多,尤其是一些对第三方类库的进一步封装,比如邮件工具 MailUtil,二维码工具 QrCodeUtil,Emoji 工具 EmojiUtil,小伙伴们可以参考 Hutool 的官方文档:https://www.hutool.cn/ + +项目源码地址:[https://github.com/looly/hutool](https://github.com/looly/hutool) + +---- + +最近整理了一份牛逼的学习资料,包括但不限于Java基础部分(JVM、Java集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是2022年全网最全的学习和找工作的PDF资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) + +微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。 + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) + +## 9.7 Guava + +### 01、前世今生 + +你好呀,我是 Guava。 + +我由 Google 公司开源,目前在 GitHub 上已经有 39.9k 的铁粉了,由此可以证明我的受欢迎程度。 + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/guava-01.png) + +我的身体里主要包含有这些常用的模块:集合 [collections] 、缓存 [caching] 、原生类型支持 [primitives support] 、并发库 [concurrency libraries] 、通用注解 [common annotations] 、字符串处理 [string processing] 、I/O 等。新版的 JDK 中已经直接把我引入了,可想而知我有多优秀,忍不住骄傲了。 + +这么说吧,学好如何使用我,能让你在编程中变得更快乐,写出更优雅的代码! + +### 02、引入 Guava + +如果你要在 Maven 项目使用我的话,需要先在 pom.xml 文件中引入我的依赖。 + +``` + + com.google.guava + guava + 30.1-jre + +``` + +一点要求,JDK 版本需要在 8 以上。 + +### 03、基本工具 + +Doug Lea,java.util.concurrent 包的作者,曾说过一句话:“[null 真糟糕](https://tobebetterjavaer.com/exception/npe.html)”。Tony Hoare,图灵奖得主、快速排序算法的作者,当然也是 null 的创建者,也曾说过类似的话:“null 的使用,让我损失了十亿美元。”鉴于此,我用 Optional 来表示可能为 null 的对象。 + +![](https://cdn.tobebetterjavaer.com/stutymore/guava-20230329172935.png) + + +代码示例如下所示。 + +```java +Optional possible = Optional.of(5); +possible.isPresent(); // returns true +possible.get(); // returns 5 +``` + +我大哥 Java 在 JDK 8 中新增了 [Optional 类](https://tobebetterjavaer.com/java8/optional.html),显然是从我这借鉴过去的,不过他的和我的有些不同。 + +- 我的 Optional 是 abstract 的,意味着我可以有子类对象;我大哥的是 final 的,意味着没有子类对象。 +- 我的 Optional 实现了 Serializable 接口,可以序列化;我大哥的没有。 +- 我的一些方法和我大哥的也不尽相同。 + +使用 Optional 除了赋予 null 语义,增加了可读性,最大的优点在于它是一种傻瓜式的防护。Optional 迫使你积极思考引用缺失的情况,因为你必须显式地从 Optional 获取引用。 + +除了 Optional 之外,我还提供了: + +- 参数校验 +- 常见的 Object 方法,比如说 Objects.equals、Objects.hashCode,JDK 7 引入的 Objects 类提供同样的方法,当然也是从我这借鉴的灵感。 +- 更强大的比较器 + +### 04、集合 + +首先我来说一下,为什么需要不可变集合。 + +- 保证线程安全。在并发程序中,使用不可变集合既保证线程的安全性,也大大地增强了并发时的效率(跟并发锁方式相比)。 +- 如果一个对象不需要支持修改操作,不可变的集合将会节省空间和时间的开销。 +- 可以当作一个常量来对待,并且集合中的对象在以后也不会被改变。 + +与 JDK 中提供的不可变集合相比,我提供的 Immutable 才是真正的不可变,我为什么这么说呢?来看下面这个示例。 + +下面的代码利用 JDK 的 [`Collections.unmodifiableList(list)`](https://tobebetterjavaer.com/common-tool/collections.html) 得到一个不可修改的集合 unmodifiableList。 + +```java +List list = new ArrayList(); +list.add("雷军"); +list.add("乔布斯"); + +List unmodifiableList = Collections.unmodifiableList(list); +unmodifiableList.add("马云"); +``` + +运行代码将会出现以下异常: + +``` +Exception in thread "main" java.lang.UnsupportedOperationException + at java.base/java.util.Collections$UnmodifiableCollection.add(Collections.java:1060) + at com.itwanger.guava.NullTest.main(NullTest.java:29) +``` + +很好,执行 `unmodifiableList.add()` 的时候抛出了 UnsupportedOperationException 异常,说明 `Collections.unmodifiableList()` 返回了一个不可变集合。但真的是这样吗? + +你可以把 `unmodifiableList.add()` 换成 `list.add()`。 + +```java +List list = new ArrayList(); +list.add("雷军"); +list.add("乔布斯"); + +List unmodifiableList = Collections.unmodifiableList(list); +list.add("马云"); +``` + +再次执行的话,程序并没有报错,并且你会发现 unmodifiableList 中真的多了一个元素。说明什么呢? + +`Collections.unmodifiableList(…)` 实现的不是真正的不可变集合,当原始集合被修改后,不可变集合里面的元素也是跟着发生变化。 + +我就不会犯这种错,来看下面的代码。 + +```java +List stringArrayList = Lists.newArrayList("雷军","乔布斯"); +ImmutableList immutableList = ImmutableList.copyOf(stringArrayList); +immutableList.add("马云"); +``` + +尝试 `immutableList.add()` 的时候会抛出 `UnsupportedOperationException`。我在源码中已经把 `add()` 方法废弃了。 + +```java +/** + * Guaranteed to throw an exception and leave the collection unmodified. + * + * @throws UnsupportedOperationException always + * @deprecated Unsupported operation. + */ +@CanIgnoreReturnValue +@Deprecated +@Override +public final boolean add(E e) { + throw new UnsupportedOperationException(); +} +``` + +尝试 `stringArrayList.add()` 修改原集合的时候 immutableList 并不会因此而发生改变。 + +除了不可变集合以外,我还提供了新的集合类型,比如说: + +- Multiset,可以多次添加相等的元素。当把 Multiset 看成普通的 Collection 时,它表现得就像无序的 ArrayList;当把 Multiset 看作 `Map` 时,它也提供了符合性能期望的查询操作。 +- Multimap,可以很容易地把一个键映射到多个值。 +- BiMap,一种特殊的 Map,可以用 `inverse()` 反转 + `BiMap` 的键值映射;保证值是唯一的,因此 `values()` 返回 Set 而不是普通的 Collection。 + +### 05、字符串处理 + +字符串表示字符的不可变序列,创建后就不能更改。在我们日常的工作中,字符串的使用非常频繁,熟练的对其操作可以极大的提升我们的工作效率。 + +我提供了连接器——Joiner,可以用分隔符把字符串序列连接起来。下面的代码将会返回“雷军; 乔布斯”,你可以使用 `useForNull(String)` 方法用某个字符串来替换 null,而不像 `skipNulls()` 方法那样直接忽略 null。 + +```java +Joiner joiner = Joiner.on("; ").skipNulls(); +return joiner.join("雷军", null, "乔布斯"); +``` + +我还提供了拆分器—— Splitter,可以按照指定的分隔符把字符串序列进行拆分。 + +```java +Splitter.on(',') + .trimResults() + .omitEmptyStrings() + .split("雷军,乔布斯,, 沉默王二"); +``` + +### 06、缓存 + +缓存在很多场景下都是相当有用的。你应该知道,检索一个值的代价很高,尤其是需要不止一次获取值的时候,就应当考虑使用缓存。 + +我提供的 Cache 和 ConcurrentMap 很相似,但也不完全一样。最基本的区别是 ConcurrentMap 会一直保存所有添加的元素,直到显式地移除。相对地,我提供的 Cache 为了限制内存占用,通常都设定为自动回收元素。 + +如果你愿意消耗一些内存空间来提升速度,你能预料到某些键会被查询一次以上,缓存中存放的数据总量不会超出内存容量,就可以使用 Cache。 + +来个示例你感受下吧。 + +```java +@Test +public void testCache() throws ExecutionException, InterruptedException { + + CacheLoader cacheLoader = new CacheLoader() { + // 如果找不到元素,会调用这里 + @Override public Animal load(String s) { return null; } @@ -25346,7 +26159,7 @@ Lists还有其他的好用的工具,我在这里只是抛砖引玉,有兴趣 ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/guava-4b962b06-a626-4707-9fe9-f5729536d9c5.jpg) -### 07、尾声 +### 08、尾声 上面介绍了我认为最常用的功能,作为 Google 公司开源的 Java 开发核心库,个人觉得实用性还是很高的(不然呢?嘿嘿嘿)。引入到你的项目后不仅能快速的实现一些开发中常用的功能,而且还可以让代码更加的优雅简洁。 @@ -25360,4 +26173,387 @@ Lists还有其他的好用的工具,我在这里只是抛砖引玉,有兴趣 微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。 +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) + +## 9.8 其他常用 Java 工具类 + +除了我们前面提到的 Java 原生工具类,比如说 [Arrays](https://tobebetterjavaer.com/common-tool/arrays.html)、[Objects](https://tobebetterjavaer.com/common-tool/Objects.html)、[Collections](https://tobebetterjavaer.com/common-tool/collections.html)、[Scanner](https://tobebetterjavaer.com/common-tool/scanner.html) 等,还有一些第三方的工具类,比如说 [Hutool](https://tobebetterjavaer.com/common-tool/hutool.html)、[Guava](https://tobebetterjavaer.com/common-tool/guava.html) 等,以及我们今天介绍的 IpUtil、CollectionUtils、StringUtils、MDC、ClassUtils、BeanUtils、ReflectionUtils 等等,在很大程度上能够提高我们的生产效率。 + +当然了,如果能好好看一下它们的源码,对技术功底的提升,也是有很大帮助的。 + +### IpUtil:获取本机 Ip + +获取本机 IP 算是比较常见的一个需求场景了,比如业务报警,可能就会带上出问题的机器 IP,方便直接上去看日志定位问题,那么问题来了,如何获取机器 IP 呢? + +#### 1. 基本方法 + +如何获取机器 IP?如果了解 InetAddress 这个工具类,就很容易写出一个简单的工具类,如下 + +```java +public static String getLocalIP() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } +} +``` + +上面的实现有问题么? + +当然没问题,拿我本机和阿里服务器执行一下,并没有问题如实的输出了预期的 IP + +本机执行后截图如下: + +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330093633.png) + +阿里云机器执行后截图如下: + +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330095801.png) + +#### 2. 进阶版 + +做一点简单的改动,获取 IPV4 的地址,源码如下 + +```java +public static String getLocalIpByNetcard() { + try { + // 枚举所有的网络接口 + for (Enumeration e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) { + // 获取当前网络接口 + NetworkInterface item = e.nextElement(); + + // 遍历当前网络接口的所有地址 + for (InterfaceAddress address : item.getInterfaceAddresses()) { + // 忽略回环地址和未启用的网络接口 + if (item.isLoopback() || !item.isUp()) { + continue; + } + + // 如果当前地址是 IPv4 地址,则返回其字符串表示 + if (address.getAddress() instanceof Inet4Address) { + Inet4Address inet4Address = (Inet4Address) address.getAddress(); + return inet4Address.getHostAddress(); + } + } + } + + // 如果没有找到任何 IPv4 地址,则返回本地主机地址 + return InetAddress.getLocalHost().getHostAddress(); + } catch (SocketException | UnknownHostException e) { + // 抛出运行时异常 + throw new RuntimeException(e); + } +} +``` + +需要注意的是,这段代码只返回本机的 IPv4 地址,并且只返回第一个符合条件的地址。如果本机有多个网络接口或者每个接口有多个地址,则可能无法返回预期的地址。此外,如果找不到任何 IPv4 地址,则会返回本地主机地址。 + +再次测试,输出如下 + +![](https://cdn.tobebetterjavaer.com/stutymore/utils-20230330100334.png) + +#### 3. 完整工具类 + +```java +import java.net.*; +import java.util.Enumeration; + +public class IPUtil { + public static final String DEFAULT_IP = "127.0.0.1"; + + /** + * 直接根据第一个网卡地址作为其内网ipv4地址,避免返回 127.0.0.1 + * + * @return 第一个符合条件的内网 IPv4 地址 + */ + public static String getLocalIpByNetcard() { + try { + // 枚举所有的网络接口 + for (Enumeration e = NetworkInterface.getNetworkInterfaces(); e.hasMoreElements(); ) { + // 获取当前网络接口 + NetworkInterface item = e.nextElement(); + // 遍历当前网络接口的所有地址 + for (InterfaceAddress address : item.getInterfaceAddresses()) { + // 忽略回环地址和未启用的网络接口 + if (item.isLoopback() || !item.isUp()) { + continue; + } + // 如果当前地址是 IPv4 地址,则返回其字符串表示 + if (address.getAddress() instanceof Inet4Address) { + Inet4Address inet4Address = (Inet4Address) address.getAddress(); + return inet4Address.getHostAddress(); + } + } + } + // 如果没有找到符合条件的地址,则返回本地主机地址 + return InetAddress.getLocalHost().getHostAddress(); + } catch (SocketException | UnknownHostException e) { + throw new RuntimeException(e); + } + } + + /** + * 获取本地主机地址 + * + * @return 本地主机地址 + */ + public static String getLocalIP() { + try { + return InetAddress.getLocalHost().getHostAddress(); + } catch (UnknownHostException e) { + throw new RuntimeException(e); + } + } +} +``` + +IPUtil 类中定义了两个方法,分别是 `getLocalIpByNetcard()` 和 `getLocalIP()`。前者是获取本机的内网 IPv4 地址,避免了返回 127.0.0.1 的问题。后者是获取本地主机地址,如果本机有多个 IP 地址,则可能返回其中的任意一个。 + +### MDC:一个线程安全的参数传递工具类 + +`MDC` 是 [`org.slf4j`](https://tobebetterjavaer.com/gongju/slf4j.html) 包下的一个类,它的全称是 Mapped Diagnostic Context,我们可以认为它是一个线程安全的存放诊断日志的容器。 + +MDC 的底层是用了 [`ThreadLocal`](https://tobebetterjavaer.com/thread/ThreadLocal.html) 来保存数据的。 + +我们可以用它传递参数。 + +例如现在有这样一种场景:我们使用`RestTemplate`调用远程接口时,有时需要在`header`中传递信息,比如:traceId,source 等,便于在查询日志时能够串联一次完整的请求链路,快速定位问题。 + +这种业务场景就能通过`ClientHttpRequestInterceptor`接口实现,具体做法如下: + +第一步,定义一个 LogFilter 拦截所有接口请求,在 MDC 中设置 traceId: + +```java +public class LogFilter implements Filter { + @Override + public void init(FilterConfig filterConfig) throws ServletException { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + MdcUtil.add(UUID.randomUUID().toString()); + System.out.println("记录请求日志"); + chain.doFilter(request, response); + System.out.println("记录响应日志"); + } + + @Override + public void destroy() { + } +} +``` + +第二步,实现`ClientHttpRequestInterceptor`接口,MDC 中获取当前请求的 traceId,然后设置到 header 中: + +```java +public class RestTemplateInterceptor implements ClientHttpRequestInterceptor { + + @Override + public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException { + request.getHeaders().set("traceId", MdcUtil.get()); + return execution.execute(request, body); + } +} +``` + +第三步,定义配置类,配置上面定义的`RestTemplateInterceptor`类: + +```java +@Configuration +public class RestTemplateConfiguration { + + @Bean + public RestTemplate restTemplate() { + RestTemplate restTemplate = new RestTemplate(); + restTemplate.setInterceptors(Collections.singletonList(restTemplateInterceptor())); + return restTemplate; + } + + @Bean + public RestTemplateInterceptor restTemplateInterceptor() { + return new RestTemplateInterceptor(); + } +} +``` + +其中 MdcUtil 其实是利用 MDC 工具在 ThreadLocal 中存储和获取 traceId + +```java +public class MdcUtil { + + private static final String TRACE_ID = "TRACE_ID"; + + public static String get() { + return MDC.get(TRACE_ID); + } + + public static void add(String value) { + MDC.put(TRACE_ID, value); + } +} +``` + +当然,这个例子中没有演示 MdcUtil 类的 add 方法具体调的地方,我们可以在 filter 中执行接口方法之前,生成 traceId,调用 MdcUtil 类的 add 方法添加到 MDC 中,然后在同一个请求的其他地方就能通过 MdcUtil 类的 get 方法获取到该 traceId。 + +能使用 MDC 保存 traceId 等参数的根本原因是,用户请求到应用服务器,Tomcat 会从线程池中分配一个线程去处理该请求。 + +那么该请求的整个过程中,保存到 MDC 的 ThreadLocal 中的参数,也是该线程独享的,所以不会有线程安全问题。 + +### ClassUtils + +spring 的`org.springframework.util`包下的`ClassUtils`类,它里面有很多让我们惊喜的功能。 + +它里面包含了类和对象相关的很多非常实用的方法。 + +#### 获取对象的所有接口 + +如果你想获取某个对象的所有接口,可以使用 ClassUtils 的`getAllInterfaces`方法。例如: + +```java +Class[] allInterfaces = ClassUtils.getAllInterfaces(new User()); +``` + +#### 获取某个类的包名 + +如果你想获取某个类的包名,可以使用 ClassUtils 的`getPackageName`方法。例如: + +```java +String packageName = ClassUtils.getPackageName(User.class); +System.out.println(packageName); +``` + +#### 判断某个类是否内部类 + +如果你想判断某个类是否内部类,可以使用 ClassUtils 的`isInnerClass`方法。例如: + +```java +System.out.println(ClassUtils.isInnerClass(User.class)); +``` + +#### 判断对象是否代理对象 + +如果你想判断对象是否代理对象,可以使用 ClassUtils 的`isCglibProxy`方法。例如: + +```java +System.out.println(ClassUtils.isCglibProxy(new User())); +``` + +ClassUtils 还有很多有用的方法,等待着你去发掘。感兴趣的小伙伴,可以看看下面的内容: + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/utils-c58920ac-cf04-4d95-ad29-90339a086569.jpg) + +### BeanUtils + +Spring 给我们提供了一个`JavaBean`的工具类,它在`org.springframework.beans`包下面,它的名字叫做:`BeanUtils`。 + +让我们一起看看这个工具可以带给我们哪些惊喜。 + +#### 拷贝对象的属性 + +曾几何时,你有没有这样的需求:把某个对象中的所有属性,都拷贝到另外一个对象中。这时就能使用 BeanUtils 的`copyProperties`方法。例如: + +```java +User user1 = new User(); +user1.setId(1L); +user1.setName("沉默王二"); +user1.setAddress("中国"); + +User user2 = new User(); +BeanUtils.copyProperties(user1, user2); +System.out.println(user2); +``` + +#### 实例化某个类 + +如果你想通过反射实例化一个类的对象,可以使用 BeanUtils 的`instantiateClass`方法。例如: + +```java +User user = BeanUtils.instantiateClass(User.class); +System.out.println(user); +``` + +#### 获取指定类的指定方法 + +如果你想获取某个类的指定方法,可以使用 BeanUtils 的`findDeclaredMethod`方法。例如: + +```java +Method declaredMethod = BeanUtils.findDeclaredMethod(User.class, "getId"); +System.out.println(declaredMethod.getName()); +``` + +#### 获取指定方法的参数 + +如果你想获取某个方法的参数,可以使用 BeanUtils 的`findPropertyForMethod`方法。例如: + +```java +Method declaredMethod = BeanUtils.findDeclaredMethod(User.class, "getId"); +PropertyDescriptor propertyForMethod = BeanUtils.findPropertyForMethod(declaredMethod); +System.out.println(propertyForMethod.getName()); +``` + +如果你对 BeanUtils 比较感兴趣,可以看看下面内容: + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/utils-629ecd75-259b-46aa-b1dd-82606cfc92ee.jpg) + +### ReflectionUtils + +有时候,我们需要在项目中使用`反射`功能,如果使用最原始的方法来开发,代码量会非常多,而且很麻烦,它需要处理一大堆异常以及访问权限等问题。 + +好消息是 Spring 给我们提供了一个`ReflectionUtils`工具,它在`org.springframework.util`包下面。 + +#### 获取方法 + +如果你想获取某个类的某个方法,可以使用 ReflectionUtils 类的`findMethod`方法。例如: + +```java +Method method = ReflectionUtils.findMethod(User.class, "getId"); +``` + +#### 获取字段 + +如果你想获取某个类的某个字段,可以使用 ReflectionUtils 类的`findField`方法。例如: + +```java +Field field = ReflectionUtils.findField(User.class, "id"); +``` + +#### 执行方法 + +如果你想通过反射调用某个方法,传递参数,可以使用 ReflectionUtils 类的`invokeMethod`方法。例如: + +```java + ReflectionUtils.invokeMethod(method, springContextsUtil.getBean(beanName), param); +``` + +#### 判断字段是否常量 + +如果你想判断某个字段是否常量,可以使用 ReflectionUtils 类的`isPublicStaticFinal`方法。例如: + +```java +Field field = ReflectionUtils.findField(User.class, "id"); +System.out.println(ReflectionUtils.isPublicStaticFinal(field)); +``` + +#### 判断是否 equals 方法 + +如果你想判断某个方法是否 equals 方法,可以使用 ReflectionUtils 类的`isEqualsMethod`方法。例如: + +```java +Method method = ReflectionUtils.findMethod(User.class, "getId"); +System.out.println(ReflectionUtils.isEqualsMethod(method)); +``` + +当然这个类还有不少有趣的方法,感兴趣的朋友,可以看看下面内容: + +![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/common-tool/utils-0a4ecb9c-b9d2-4090-a7b7-c626a0672b94.jpg) + +>参考链接:[https://juejin.cn/post/7102418518599008286](https://juejin.cn/post/7102418518599008286) 作者:苏三,编辑:沉默王二 + +--- + +最近整理了一份牛逼的学习资料,包括但不限于 Java 基础部分(JVM、Java 集合框架、多线程),还囊括了 **数据库、计算机网络、算法与数据结构、设计模式、框架类 Spring、Netty、微服务(Dubbo,消息队列) 网关** 等等等等……详情戳:[可以说是 2022 年全网最全的学习和找工作的 PDF 资源了](https://tobebetterjavaer.com/pdf/programmer-111.html) + +微信搜 **沉默王二** 或扫描下方二维码关注二哥的原创公众号沉默王二,回复 **111** 即可免费领取。 + ![](https://cdn.tobebetterjavaer.com/tobebetterjavaer/images/gongzhonghao.png) \ No newline at end of file -- GitLab