ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【进阶篇】Java 实际开发中积累的几个小技巧(一) [打印本页]

作者: 美丽的神话    时间: 2024-4-23 17:52
标题: 【进阶篇】Java 实际开发中积累的几个小技巧(一)
目录

前言

笔者目前从事一线 Java 开发今年是第 3 个年头了,从 0-1的 SaaS、PaaS 的项目做过,多租户下定制化开发项目也做过,项目的 PM 也做过...
在实际的开发中积累了一些技巧和经验,包括线上 bug 处理、日常业务开发、团队开发规范等等。现在在这里分享出来,作为成长的记录和知识的更新,希望与大家共勉。
免责声明:以下所有demo、代码和测试都是出自笔者本人的构思和实践,不涉及企业隐私和商业机密,属于个人的知识分享。
一、枚举类的注解

看起来很常见的枚举,可能也隐藏着使用上的问题:你有没有在代码里不小心做过改变枚举值的操作?或者为怎么合理规范地写构造方法/成员方法而烦恼?
那么不妨来看看我的示例,注释写得比较清楚了:
  1. @Getter// 只允许对属性 get,不允许 set
  2. @RequiredArgsConstructor// 为枚举的每个属性生成有参构造
  3. public enum ProjectStatusEnum {
  4.     SURVEY("已调研"),
  5.     APPROVAL("已立项"),
  6.     PROGRESSING("进行中"),
  7.     COMPLETED("已完成");
  8.     // 对该成员变量使用 final 来修饰,表明一旦赋值就不可变
  9.     private final String name;
  10. }
复制代码
为什么要分享这个呢?团队里开发的时候还真有人在使用枚举的时候 set() 改变了枚举值,编译通过但运行在一定条件触发后,导致了 bug 排查了一下午。
二、RESTful 接口

本节的内容其实更像是一种规范,因为见过不少别的部门同事写的项目代码,接口的风格真是迥异(写什么的都有),当我接手重构的时候真是头皮发麻。
首先就是禁止使用 Swagger 和 Knife4j 接口文档生成工具,原因无它:代码侵入性太强和需要写的注解太多,而且还是公司安全漏洞扫描单上的常客。
其次可以使用开源的 smart-doc 来代替,只要遵循 Javadoc 的标准注释写法即可。
一个简单的 Controller 示例如下:
  1. /**
  2. * 测试接口
  3. */
  4. @RestController
  5. @RequestMapping("/study")
  6. public class StudyController {
  7.     @Resource
  8.     private StudyService studyService;
  9.     /**
  10.      * 新增xx
  11.      * @return 是否成功
  12.      */
  13.     @PostMapping("/add")
  14.     //还可以加上其它必要注解,如:登录/权限/日志记录等
  15.     public Response<Boolean> addStudy(@RequestBody @Valid StudyDTO studyDTO) {
  16.         return ResultUtils.success(studyService.addStudy(studyDTO));
  17.     }
  18.     /**
  19.      * xx列表(不分页)
  20.      * @return 列表数据
  21.      */
  22.     @GetMapping("/list")
  23.     public Response<List<StudyListVO>> getList(@RequestParam("id") String id) {
  24.         return ResultUtils.success(studyService.getList(id));
  25.     }
  26. }
复制代码
三、类属性转换

在实际 Java 开发中,关于 VO、Entity、DTO 等对象属性之间的赋值是我们经常遇见的,最简单使用 @Data 去逐个 .set() 或者 @Builder 链式 .build(),其实都是很靠谱的办法,而且可以控制颗粒度。但属性一多起来的话,比如二十个以上,那么代码就会显得很长。所以有没有办法一行代码就搞定类属性转换呢?
首先不推荐使用 BeanUtils.copyProperties() 作类属性的拷贝,以下是几个常见的坑:
推荐泛型 + JSON组合的方式来实现类属性的转换,具体步骤如下:
四、Stream 流

  1.     /**
  2.      * Stream 流的过滤与排序
  3.      * @param id
  4.      * @return 列表数据
  5.      */
  6.     @Override
  7.     public List<StudyVO> getList(String id) {
  8.        List<StudyVO> resultList = this.list(new LambdaQueryWrapper<Study>()
  9.                 .eq(Study::getIsDelete, NumberUtils.INTEGER_ZERO)).stream()
  10.                 .filter(e -> Constants.USER_ROLE_USER.equals(e.getUserRole()))
  11.                 .sorted(Comparator.comparing(Study::getAge).reversed())
  12.                 .map(e -> e.copyProperties(StudyVO.class)).collect(Collectors.toList());
  13.         return Optional.of(resultList).orElse(null);
  14.     }
复制代码
像上述从MySQL 里查表数据的例子,其实能在数据库做的操作就没必要在 Stream 流里操作。像 .select()、.eq()、.gt()、.orderByDesc() 等都可以完成,非数据库语句查询的情况下,使用 Stream 操作集合还是有必要的。
  1. /**
  2.      * 测试 Stream 的 AnyMatch 方法
  3.      * @return
  4.      */
  5.     public List<ArticleVO> testStreamAnyMatch(){
  6.         List<Article> articleList = this.list(new LambdaQueryWrapper<Article>().eq(Article::getIsDelete, NumberUtils.INTEGER_ZERO));
  7.         if (CollectionUtils.isNotEmpty(articleList)){
  8.             //AnyMatch() 方法返回的是一个布尔,用来判断流中是否有满足条件的元素
  9.             final boolean flag = articleList.parallelStream().anyMatch(e -> Objects.nonNull(e)
  10.                     //文章要有内容
  11.                     && Objects.nonNull(e.getContent())
  12.                     //文章要有标题
  13.                     && StringUtils.isNotBlank(e.getTitle()));
  14.             if (flag){
  15.                 return articleList.parallelStream().map(e -> e.copyProperties(ArticleVO.class)).collect(Collectors.toList());
  16.             }
  17.         }
  18.         return new ArrayList<>();
  19.     }
复制代码
五、判空和断言

5.1判空部分

首先什么情况下需要判空?基本是以下这 3 种情况:
那么,常用判空的工具有哪些呢?从我个人的开发经验来说主要有以下几种:
<ul>对象的判空
推荐统一使用 java.util 包的 Objects.nonNull() 等方法。
集合的判空
推荐统一使用 org.apache.commons.collections.CollectionUtils 包的 .isNotEmpty() 等方法。
Map 对象判空
推荐统一使用 Map 自带的 .isEmpty() 、 .containsKey()、.equals() 这三者配合使用。
字符串的判空
推荐统一使用 org.apache.commons.lang3.StringUtils 包的 .isNotBlank() 等方法。
Optional类
  1. Optional
  2.      //of(T value)方法用于创建一个包含指定值的 Optional 对象,该方法接收一个非 null 值作为参数
  3.    .of()
  4.      //ofNullable(T value)方法用于创建一个包含指定值的 Optional 对象,该方法接收一个可能为 null 的值作为参数
  5.      .ofNullable()
  6.      //isPresent()方法用于判断 Optional 对象中是否存在非 null 值,有值就返回 true ,否则返回 false
  7.      .isPreset()
  8.      //orElse(T other)方法顾名思义,泛型 T 表示其它的类型
  9.      .orElse()
  10.      //ifPresent(Consumer<? super T> consumer) 判断该对象是否值,有则调用传入的 Consumer 类型函数处理该值。否则,什么也不做
  11.      .ifPresent()  
  12.      //map(Function<? super T, ? extends U> mapper) 用于对 Optional 对象中的值进行映射,并返回一个新的 Optional 对象
  13.      .map()
  14.      //filter(Predicate<? super T> predicate) 用于过滤 Optional 对象中的值,只有当值满足特定条件时才保留
  15.      .filter()
复制代码
文章小结

作为一个系列文章的开头,本文的内容偏基础。在之后的文章中我会分享一些关于真实项目中关于线上 bug 处理、缓存的使用、异步/解耦等内容,敬请期待。
那么 Java 实际开发中值得注意的几个小技巧的分享到这里就暂时结束了,如有不足和错误,还请大家指正。或者你有其它想说的,也欢迎大家在评论区交流!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4