专栏名称: Java知音
专注于Java,推送技术文章,热门开源项目等。致力打造一个有实用,有情怀的Java技术公众号!
51好读  ›  专栏  ›  Java知音

请不要自己写,SpringBoot 非常实用的内置功能

Java知音  · 公众号  ·  · 2024-10-05 10:40

正文

在java的庞大体系中,其实有很多不错的小工具,也就是我们平常说的: 轮子

如果在我们的日常工作当中,能够将这些轮子用户,再配合一下idea的快捷键,可以极大得提升我们的开发效率。

今天我决定把一些压箱底的小工具,分享给大家,希望对你有所帮助。

本文会分享17个我们日常工作中一定会用得到的小工具,主要内容如下:

1. Collections

首先出场的是 java.util 包下的 Collections 类,该类主要用于操作集合或者返回集合,我个人非常喜欢用它。

1.1 排序

在工作中经常有对集合排序的需求。

看看使用 Collections 工具是如何实现升序和降序的:

  List list = new ArrayList<>();
  list.add(2);
  list.add(1);
  list.add(3);
  Collections.sort(list);//升序
  System.out.println(list);
  Collections.reverse(list);//降序
  System.out.println(list);

执行结果:

[1, 2, 3]
[3, 2, 1]

1.2 获取最大或最小值

有时候需要找出集合中的 最大值 或者 最小值 ,这时可以使用Collections的 max min 方法。例如:

List list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);
Integer max = Collections.max(list);//获取最大值
Integer min = Collections.min(list);//获取最小值
System.out.println(max);
System.out.println(min);

执行结果:

3
1

1.3 转换线程安全集合

我们都知道,java中的很多集合,比如:ArrayList、LinkedList、HashMap、HashSet等,都是线程不安全的。

换句话说,这些集合在多线程的环境中,添加数据会出现异常。

这时,可以用Collections的 synchronizedxxx 方法,将这些线程不安全的集合,直接转换成线程安全集合。例如:

  List list = new ArrayList<>();
  list.add(2);
  list.add(1);
  list.add(3 );

  List integers = Collections.synchronizedList(list);//将ArrayList转换成线程安全集合
  System.out.println(integers);

它的底层会创建 SynchronizedRandomAccessList 或者 SynchronizedList 类,这两个类的很多方法都会用 synchronized 加锁。

1.4 返回空集合

有时,我们在判空之后,需要返回空集合,就可以使用 emptyList 方法,例如:

private List fun(List list) {
    if (list == null || list.size() == 0) {
        return Collections.emptyList();
    }
    //业务处理
    return list;
}

1.5 二分查找

binarySearch 方法提供了一个非常好用的 二分查找 功能,只用传入指定集合和需要找到的key即可。例如:

List list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);

int i = Collections.binarySearch(list, 3);//二分查找
System.out.println(i );

执行结果:

2

1.6 转换成不可修改集合

为了防止后续的程序把某个集合的结果修改了,有时候我们需要把某个集合定义成不可修改的,使用Collections的 unmodifiablexxx 方法就能轻松实现:

List list = new ArrayList<>();
list.add(2);
list.add(1);
list.add(3);

List integers = Collections.unmodifiableList(list);
integers.add(4);
System.out.println(integers);

执行结果:

Exception in thread "main" java.lang.UnsupportedOperationException
 at java.util.Collections$UnmodifiableCollection.add(Collections.java:1055)
 at com.sue.jump.service.test1.UtilTest.main(UtilTest.java:19)

当然Collections工具类中还有很多常用的方法,在这里就不一一介绍了,需要你自己去探索。

2. CollectionUtils

对集合操作,除了前面说的 Collections 工具类之后, CollectionUtils 工具类也非常常用。

目前比较主流的是 spring org.springframework.util 包下的CollectionUtils工具类。

apache org.apache.commons.collections 包下的CollectionUtils工具类。

我个人更推荐使用apache的包下的CollectionUtils工具类,因为它的工具更多更全面。

举个简单的例子, spring 的CollectionUtils工具类没有判断集合不为空的方法。而 apache 的CollectionUtils工具类却有。

下面我们以 apache 的CollectionUtils工具类为例,介绍一下常用方法。

2.1 集合判空

通过CollectionUtils工具类的 isEmpty 方法可以轻松判断集合是否为空, isNotEmpty 方法判断集合不为空。

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("集合不为空");
}

2.2 对两个集合进行操作

有时候我们需要对已有的两个集合进行操作,比如取交集或者并集等。

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);

执行结果:

[1, 2, 3, 4]
[2]
[1, 3, 4]
[1, 3]

说句实话,对两个集合的操作,在实际工作中用得挺多的,特别是很多批量的场景中。以前我们需要写一堆代码,但没想到有现成的轮子。

3. Lists

如果你引入 com.google.guava 的pom文件,会获得很多好用的小工具。这里推荐一款 com.google.common.collect 包下的集合工具: Lists

它是在太好用了,让我爱不释手。

3.1 创建空集合

有时候,我们想创建一个空集合。这时可以用Lists的 newArrayList 方法,例如:

List list = Lists.newArrayList();

3.2 快速初始化集合

有时候,我们想给一个集合中初始化一些元素。这时可以用Lists的newArrayList方法,例如:

List list = Lists.newArrayList(123);

执行结果:

[1, 2, 3]

3.3 笛卡尔积

如果你想将两个集合做 笛卡尔积 ,Lists的 cartesianProduct 方法可以帮你实现:

List list1 = Lists.newArrayList(123);
List list2 = Lists.newArrayList(4,5);
List> productList = Lists.cartesianProduct(list1,list2);
System.out.println(productList);

执行结果:

[[1, 4], [1, 5], [2, 4], [2, 5], [3, 4], [3, 5]]

3.4 分页

如果你想将一个 大集合 分成若干个 小集合 ,可以使用Lists的 partition 方法:

List list = Lists.newArrayList(12345);
List> partitionList = Lists.partition(list, 2);
System.out.println(partitionList);

执行结果:

[[1, 2], [3, 4], [5]]

这个例子中,list有5条数据,我将list集合按大小为2,分成了3页,即变成3个小集合。

这个是我最喜欢的方法之一,经常在项目中使用。

比如有个需求:现在有5000个id,需要调用批量用户查询接口,查出用户数据。但如果你直接查5000个用户,单次接口响应时间可能会非常慢。如果改成分页处理,每次只查500个用户,异步调用10次接口,就不会有单次接口响应慢的问题。

3.5 流处理

如果我们想把某个集合转换成另外一个接口,可以使用Lists的 transform 方法。例如:

List list = Lists.newArrayList("a","b","c");
List transformList = Lists.transform(list, x -> x.toUpperCase());
System.out.println(transformList);

将小写字母转换成了大写字母。

3.6 颠倒顺序

Lists的有颠倒顺序的方法 reverse 。例如:

List list = Lists.newArrayList(312);
List reverseList = Lists.reverse(list);
System.out.println(reverseList);

执行结果:

[2, 1, 3]

list的原始顺序是312,使用 reverse 方法颠倒顺序之后,变成了213。

Lists还有其他的好用的工具,我在这里只是抛砖引玉,有兴趣的朋友,可以仔细研究一下。

4. Objects

jdk7 之后,提供了 Objects 工具类,我们可以通过它操作对象。

4.1 对象判空

在java中万事万物皆对象,对象的判空可以说无处不在。Objects的 isNull 方法判断对象是否为空,而 nonNull 方法判断对象是否不为空。例如:

Integer integer = new Integer(1);

if (Objects.isNull(integer)) {
    System.out.println("对象为空");
}

if (Objects.nonNull(integer)) {
    System.out.println("对象不为空");
}

4.2 对象为空抛异常

如果我们想在对象为空时,抛出空指针异常,可以使用Objects的 requireNonNull 方法。例如:

Integer integer1 = new Integer(128);

Objects.requireNonNull(integer1);
Objects.requireNonNull(integer1, "参数不能为空");
Objects.requireNonNull(integer1, () -> "参数不能为空");

4.3 判断两个对象是否相等

我们经常需要判断两个对象是否相等,Objects给我们提供了 equals 方法,能非常方便的实现:

Integer integer1 = new Integer(1);
Integer integer2 = new Integer(1);

System.out.println(Objects.equals(integer1, integer2));

执行结果:

true

但使用这个方法有坑,比如例子改成:

Integer integer1 = new Integer(1);
Long integer2 = new Long(1);

System.out.println(Objects.equals(integer1, integer2));

执行结果:

false

具体原因不细说了,有兴趣的小伙们可以看看我的另一篇文章《 Objects.equals有坑 》,里面有非常详细的讲解。

4.4 获取对象的hashCode

如果你想获取某个对象的hashCode,可以使用Objects的 hashCode 方法。例如:

String str = new String("abc");
System.out.println(Objects.hashCode(str));

执行结果:

96354

Objects的内容先介绍到这里,有兴趣的小伙们,可以看看下面更多的方法:

5. BooleanUtils

在java中布尔值,随处可见。

如果你使用了布尔的包装类: Boolean ,总感觉有点麻烦,因为它有三种值: null true false 。我们在处理Boolean对象时,需要经常判空。

头疼!!!

但如果使用 BooleanUtils 类处理布尔值,心情一下子就愉悦起来了。

5.1 判断true或false

如果你想判断某个参数的值是true或false,可以直接使用 isTrue isFalse 方法。例如:

Boolean aBoolean = new Boolean(true);
System.out.println(BooleanUtils.isTrue(aBoolean));
System.out.println(BooleanUtils.isFalse(aBoolean));

5.2 判断不为true或不为false

有时候,需要判断某个参数不为true,即是null或者false。或者判断不为false,即是null或者true。

可以使用 isNotTrue isNotFalse 方法。例如:

Boolean aBoolean = new Boolean(true);
Boolean aBoolean1 = null;
System.out.println(BooleanUtils.isNotTrue(aBoolean));
System.out.println(BooleanUtils.isNotTrue(aBoolean1));
System.out.println(BooleanUtils.isNotFalse(aBoolean));
System.out.println(BooleanUtils.isNotFalse(aBoolean1));

执行结果:

false
true
true
true

5.3 转换成数字

如果你想将true转换成数字1,false转换成数字0,可以使用 toInteger 方法:

Boolean aBoolean = new Boolean(true);
Boolean aBoolean1 = new Boolean(false);
System.out.println(BooleanUtils.toInteger(aBoolean));
System.out.println(BooleanUtils.toInteger(aBoolean1));

执行结果:

1
0

5.4 Boolean转换成布尔值

我们有时候需要将包装类 Boolean 对象,转换成原始的 boolean 对象,可以使用 toBoolean 方法。例如:

Boolean aBoolean = new Boolean(true);
Boolean aBoolean1 = null;
System.out.println(BooleanUtils.toBoolean(aBoolean));
System.out.println(BooleanUtils.toBoolean(aBoolean1));
System.out.println(BooleanUtils.toBooleanDefaultIfNull(aBoolean1, false));

我们无需额外的判空了,而且还可以设置Boolean对象为空时返回的默认值。

BooleanUtils类的方法还有很多,有兴趣的小伙伴可以看看下面的内容:

6. StringUtils

字符串 (String)在我们的日常工作中,用得非常非常非常多。

在我们的代码中经常需要对字符串判空,截取字符串、转换大小写、分隔字符串、比较字符串、去掉多余空格、拼接字符串、使用正则表达式等等。

如果只用String类提供的那些方法,我们需要手写大量的额外代码,不然容易出现各种异常。

现在有个好消息是: org.apache.commons.lang3 包下的 StringUtils 工具类,给我们提供了非常丰富的选择。

6.1 字符串判空

其实空字符串,不只是null一种,还有""," ","null"等等,多种情况。

StringUtils给我们提供了多个判空的静态方法,例如:

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));

执行结果:

true
true
false
false
=====
false
false
true
true
=====
true
true
true
false
=====
false
false
false
true

示例中的: isEmpty isNotEmpty isBlank isNotBlank ,这4个判空方法你们可以根据实际情况使用。

优先推荐使用 isBlank isNotBlank 方法,因为它会把 " " 也考虑进去。

6.2 分隔字符串

分隔字符串是常见需求,如果直接使用String类的split方法,就可能会出现空指针异常。

String str1 = null;
System.out.println(StringUtils.split(str1,","));
System.out.println(str1.split(","));

执行结果:

null
Exception in thread "main" java.lang.NullPointerException
 at com.sue.jump.service.test1.UtilTest.main(UtilTest.java:21)

使用StringUtils的split方法会返回null,而使用String的split方法会报指针异常。

6.3 判断是否纯数字

给定一个字符串,判断它是否为纯数字,可以使用 isNumeric 方法。例如:

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));

执行结果:

true
false
false

6.4 将集合拼接成字符串

有时候,我们需要将某个集合的内容,拼接成一个字符串,然后输出,这时可以使用 join 方法。例如:

List list = Lists.newArrayList("a""b""c");
List list2 = Lists.newArrayList(123);
System.out.println(StringUtils.join(list, ","));
System.out.println(StringUtils.join(list2, " "));

执行结果:

a,b,c
1 2 3

当然还有很多实用的方法,我在这里就不一一介绍了。

7. Assert

很多时候,我们需要在代码中做判断:如果不满足条件,则抛异常。

有没有统一的封装呢?

其实 spring 给我们提供了 Assert 类,它表示 断言

7.1 断言参数是否为空

断言 参数 是否空,如果不满足条件,则直接抛异常。

String str = null;
Assert.isNull(str, "str必须为空");
Assert.isNull(str, () -> "str必须为空");
Assert.notNull(str, "str不能为空");

如果不满足条件就会抛出 IllegalArgumentException 异常。

7.2 断言集合是否为空

断言 集合 是否空,如果不满足条件,则直接抛异常。

List list = null;
Map map = null;
Assert.notEmpty(list, "list不能为空");
Assert.notEmpty(list, () -> "list不能为空");
Assert.notEmpty(map, "map不能为空" );

如果不满足条件就会抛出 IllegalArgumentException 异常。

7.3 断言条件是否为空

断言是否满足某个 条件 ,如果不满足条件,则直接抛异常。

List list = null;
Assert.isTrue(CollectionUtils.isNotEmpty(list), "list不能为空");
Assert.isTrue(CollectionUtils.isNotEmpty(list), () -> "list不能为空");

当然Assert类还有一些其他的功能,这里就不多介绍了。

8. IOUtils

IO 流在我们日常工作中也用得比较多,尽管java已经给我们提供了丰富的API。

但我们不得不每次读取文件,或者写入文件之后,写一些重复的的代码。手动在 finally 代码块中关闭流,不然可能会造成 内存溢出

有个好消息是:如果你使用 org.apache.commons.io 包下的 IOUtils 类,会节省大量的时间。

8.1 读取文件

如果你想将某个txt文件中的数据,读取到字符串当中,可以使用IOUtils类的 toString 方法。例如:

String str = IOUtils.toString(new FileInputStream("/temp/a.txt"), StandardCharsets.UTF_8);
System.out.println(str);

8.2 写入文件

如果你想将某个字符串的内容,写入到指定文件当中,可以使用IOUtils类的 write 方法。例如:

String str = "abcde";
IOUtils.write(str, new FileOutputStream("/temp/b.tx"), StandardCharsets.UTF_8);

8.3 文件拷贝

如果你想将某个文件中的所有内容,都拷贝到另一个文件当中,可以使用IOUtils类的 copy 方法。例如:

IOUtils.copy(new FileInputStream("/temp/a.txt"), new FileOutputStream("/temp/b.txt"));

8.4 读取文件内容到字节数组

如果你想将某个文件中的内容,读取字节数组中,可以使用IOUtils类的 toByteArray 方法。例如:

byte




    
[] bytes = IOUtils.toByteArray(new FileInputStream("/temp/a.txt"));

IOUtils类非常实用,感兴趣的小伙们,可以看看下面内容。

9. MDC

MDC org.slf4j 包下的一个类,它的全称是Mapped Diagnostic Context,我们可以认为它是一个线程安全的存放诊断日志的容器。

MDC的底层是用了 ThreadLocal 来保存数据的。

我们可以用它传递参数。

例如现在有这样一种场景:我们使用 RestTemplate 调用远程接口时,有时需要在 header 中传递信息,比如:traceId,source等,便于在查询日志时能够串联一次完整的请求链路,快速定位问题。

这种业务场景就能通过 ClientHttpRequestInterceptor 接口实现,具体做法如下:

第一步,定义一个LogFilter拦截所有接口请求,在MDC中设置traceId:

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中:

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 类:

@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

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中的参数,也是该线程独享的,所以不会有线程安全问题。

10. ClassUtils

spring的 org.springframework.util 包下的 ClassUtils 类,它里面有很多让我们惊喜的功能。

它里面包含了类和对象相关的很多非常实用的方法。

10.1 获取对象的所有接口

如果你想获取某个对象的所有接口,可以使用ClassUtils的 getAllInterfaces 方法。例如:

Class>[] allInterfaces = ClassUtils.getAllInterfaces(new User());

10.2 获取某个类的包名

如果你想获取某个类的包名,可以使用ClassUtils的 getPackageName 方法。例如:

String packageName = ClassUtils.getPackageName(User.class);
System.out.println(packageName);

10.3 判断某个类是否内部类

如果你想判断某个类是否内部类,可以使用ClassUtils的 isInnerClass 方法。例如:

System.out.println(ClassUtils.isInnerClass(User.class));

10.4 判断对象是否代理对象

如果你想判断对象是否代理对象,可以使用ClassUtils的 isCglibProxy 方法。例如:

System.out.println(ClassUtils.isCglibProxy(new User()));

ClassUtils还有很多有用的方法,等待着你去发掘。感兴趣的朋友,可以看看下面内容:

11. BeanUtils







请到「今天看啥」查看全文