头图

好的代码的品味

1. 代码检测工具

  1. 代码格式检测工具-CheckStyle
  2. 代码规范检测工具-Sonarlint
  3. 代码规范检测工具-Alibaba-Java-Coding-Guidelines
  4. AlibabaJava开发规范PDF版
  5. 代码规范检测工具-SonarQube

2. 代码规范书籍

  1. Effective Java
  2. AlibabaJava开发规范

3. 规范规则-使用新版本特性

3.1 lambda语法-Java8

3.1.1 提取元素

正例:

List<String> emails = users.stream()
.map(User::getEmail)
.collect(Collectors.toUnmodifiableList());
int total = users.stream()
  .filter(Objects::nonNull)
  .mapToInt(NumberUtils::toInt)
                    .sum();

反例:

List<String> emails = Lists.newArrayListWithCapacity(users.size());
for (User u : users) {
  emails.add(u.getEmail());
}
3.1.2 过滤元素

正例:

List<String> emails = users.stream()
                .map(User::getEmail)
                .filter(Objects::nonNull)
                .collect(Collectors.toUnmodifiableList());
3.1.3 分组

正例:

Map<String, List<Person>> peopleByCity
         = personStream.collect(Collectors.groupingBy(Person::getCity));
3.1.4 连接指定字符串
String email = users.stream()
  .map(User::getEmail)
  .collect(Collectors.joining(","));
3.1.5 排序
users.stream()
                .sorted(Comparator.comparing(User::getRegistrationTime, Comparator.nullsLast(Comparator.reverseOrder())))
                .collect(Collectors.toList());

3.2 try-with-resources语法-Java8

正例:

static String readFirstLineFromFile(String path) throws IOException {
  try (FileReader fr = new FileReader(path);
       BufferedReader br = new BufferedReader(fr)) {
    return br.readLine();
  }
}

反例:

static String readFirstLineFromFileWithFinallyBlock(String path) throws IOException {
    FileReader fr = new FileReader(path);
    BufferedReader br = new BufferedReader(fr);
    try {
        return br.readLine();
    } finally {
        br.close();
        fr.close();
    }
}

3.4 使用不可变集合

3.4.1 Java9
static <E> List<E> of() {
  return ImmutableCollections.emptyList();
}

static <E> Set<E> of() {
  return ImmutableCollections.emptySet();
}

static <K, V> Map<K, V> of() {
  return ImmutableCollections.emptyMap();
}
3.4.2 Guava

如果使用的是JDK9一下的版本可以考虑使用Guava,JDK9不可变集合的设计在一定程度上参考了Guava。

ImmutableList

public static <E> ImmutableList<E> of() {
  return (ImmutableList<E>) EMPTY;
}

ImmutableMap

public static <K, V> ImmutableMap<K, V> of(K k1, V v1) {
  return ImmutableBiMap.of(k1, v1);
}

3.5 Optional

return Optional.of(user).map(User::getNickname).orElse(user.getUsername());
return Optional.ofNullable(user).ifPresent(this.userService::delete);
this.userService.getOne(id).orElse(defaultUser);

3.6 日期格式转换

TimeUnit.of(ChronoUnit.MILLIS).convert(diffInMillis, TimeUnit.MINUTES);

4. 使用工具类

  1. Guava
  2. Apache-Commons
  3. hutool

4.1 org.springframework.util.CollectionUtils

public static boolean isEmpty(@Nullable Collection<?> collection) {
  return (collection == null || collection.isEmpty());
}
public static boolean isEmpty(@Nullable Map<?, ?> map) {
  return (map == null || map.isEmpty());
}
@Nullable
public static <T> T firstElement(@Nullable List<T> list) {
  if (isEmpty(list)) {
    return null;
  }
  return list.get(0);
}
@Nullable
public static <T> T lastElement(@Nullable List<T> list) {
  if (isEmpty(list)) {
    return null;
  }
  return list.get(list.size() - 1);
}

4.2 org.springframework.beans.BeanUtils

  1. org.springframework.beans.BeanUtils#copyProperties(java.lang.Object, java.lang.Object)

4.3 org.apache.commons.lang3.StringUtils

  1. org.apache.commons.lang3.StringUtils#isEmpty
  2. org.apache.commons.lang3.StringUtils#left
  3. org.apache.commons.lang3.StringUtils#split(java.lang.String)
  4. org.apache.commons.lang3.StringUtils#equals
  5. org.apache.commons.lang3.StringUtils#startsWith(java.lang.CharSequence, java.lang.CharSequence)
  6. ...

4.4 org.apache.commons.lang3.math.NumberUtils

  1. org.apache.commons.lang3.math.NumberUtils#toInt(java.lang.String)

4.5 org.apache.commons.lang3.ArrayUtils

  1. org.apache.commons.lang3.ArrayUtils#isEmpty(boolean[])

4.6 org.apache.commons.lang3.time.DateUtils

  1. org.apache.commons.lang3.time.DateUtils#addDays
  2. org.apache.commons.lang3.time.DateUtils#isSameDay(java.util.Calendar, java.util.Calendar)

4.7 cn.hutool.core.date.DateUtil

  1. cn.hutool.core.date.DateUtil#beginOfDay
  2. cn.hutool.core.date.DateUtil#betweenDay

4.8 org.springframework.core.NamedThreadLocal

4.9 org.springframework.util.StopWatch

4.10 org.springframework.util.ObjectUtils

  1. org.springframework.util.ObjectUtils#nullSafeEquals
  2. org.springframework.util.ObjectUtils#isEmpty(java.lang.Object)

4.11 org.apache.commons.lang3.EnumUtils

  1. org.apache.commons.lang3.EnumUtils#getEnum(java.lang.Class<E>, java.lang.String)

4.12 org.springframework.util.ResourceUtils

  1. org.springframework.util.ResourceUtils#getURL
  2. org.springframework.util.ResourceUtils#getFile(java.lang.String)

5. 避免使用魔法值

5.1. org.springframework.http.MediaType

@PostMapping(path = "/create", consumes = {MediaType.APPLICATION_JSON_VALUE})
public Message<Entity> create() {}

5.2. org.springframework.http.HttpHeaders

5.3. org.springframework.http.HttpStatus

5.4. java.nio.charset.StandardCharsets

5.5. org.apache.commons.collections4.ArrayUtils

org.apache.commons.collections4.ArrayUtils#INDEX_NOT_FOUND

5.6. org.apache.commons.lang3.StringUtils

public static final String SPACE = " ";

public static final String EMPTY = "";

public static final String LF = "\n";

public static final String CR = "\r";

public static final int INDEX_NOT_FOUND = -1;

5.7. org.springframework.util.ObjectUtils

private static final String EMPTY_STRING = "";
private static final String NULL_STRING = "null";
private static final String ARRAY_START = "{";
private static final String ARRAY_END = "}";
private static final String EMPTY_ARRAY = ARRAY_START + ARRAY_END;
private static final String ARRAY_ELEMENT_SEPARATOR = ", ";
private static final Object[] EMPTY_OBJECT_ARRAY = new Object[0];

5.8 反例

HttpHeaders headers = new HttpHeaders();
headers.add("Cache-Control", "no-cache, no-store, must-revalidate");
headers.add("Pragma", "no-cache");
headers.add("Expires", "0");
int index = userIds.indexOf(userId);
if (index != -1) {
  // ...do something
}
URLEncoder.encode(url, "utf-8")

5.9 正例

HttpHeaders headers = new HttpHeaders();
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename=file.txt");
headers.add(HttpHeaders.CACHE_CONTROL, "no-cache, no-store, must-revalidate");
headers.add(HttpHeaders.PRAGMA, "no-cache");
headers.add(HttpHeaders.EXPIRES, "0");
int index = userIds.indexOf(userId);
if (index != ArrayUtils.INDEX_NOT_FOUND) {
  // ...do something
}
URLEncoder.encode(url, StandardCharsets.UTF_8)

6. Git Commit 规范

image.png

6.1 概述

使用 git commit -m "" 可以提交对应的更改信息。

git commit -m "";

6.2. Commit 规范

6.2.1 commit 格式

提交中应该包含 标题类型主题正文页脚,其中标题、类型、主题包含在了header/标题,这与我们HTML类似,一个完整的网页应该包含 headerbodybody。例如:

git commit -m "${type}(${scope}): ${body}" -m "${footer}"
6. 2.2 标题

标题是必选的,它以下部分组成:

  • 类型:提交的类型,有功能、修复、格式整理等。
  • 范围:更改的范围,例如数据访问层、控制层、对外api、Spring配置等。
  • 主题:对本次修改的概述。

绝大多数场景中,我们提交信息都只包含了标题部分,例如:

git commit -m "feat(dao): 增加自定义TypeHandler";

feat 表示提交的类型,后面使用()包含修改的范围,使用:和一个空格分割消息的正文。

6.3 正文

可选

6.4 页脚

可选

6.5 查看提交记录

git log;
git reflog;
git log --oneline;
git log --oneline -n3;
git log --oneline | grep "feat"

6.3. 类型

除了commit的规范之外,还规定了常见的提交类型。

6.3.1 build/chore

build/chore,用于构建系统(包括脚本、配置或工具)和依赖的变化等,例如:

git commit -m "build(deps/pom.xml): bump maven-deploy-plugin from 2.8.2 to 3.0.0"
git commit -m "fix(deps): upgrade com.google.code.gson:gson from 2.8.9 to 2.9.0"
6.3.2 ci

ci 用于系统持续集成、持续部署相关的配置文件、脚本文件、配置或者工具,例如:

git commit -m "ci: add docker file"
6.3.3 docs

docs 用于标识项目相关文档的更改,例如:

git commit -m "doc(README.md): Update README.md"
6.3.4 feat

feat 用于标识新功能,例如:

git commit -m "feat(oa): 增加审批流程支持";
6.3.5 fix

fix 用于标识bug修复,例如:

git commit -m "fix(router): IE9下路由没有响应";
6.3.6 perf

perf 用于标识性能提升,例如:

git commit -m "perf(excel/export): 使用线程池批处理文件";
6.3.7 refactor

refactor 用于标识代码重构,既不添加新功能也不修复错误--例如删除冗余代码、简化代码、重命名变量等。

git commit -m "refactor(cms): 重构CMS客户模块";
6.3.8 style

style 用于标记代码格式化,代码风格调制,修复checkstyle等问题,例如:

git commit -m "style: Reformat Code.";
6.3.9 test

test 用于标记测试相关的更改,修改现有测试或添加新的测试,例如:

git commit -m "test(dao): add user unit tests.";
6.3.10 revert

revert 用于版本回滚,例如:

git commit -m "revert: rever to xxx";

7. 包命名规范

7.1 support

7.2 controller

7.3 service

7.4 service/impl

7.5 utils

7.6 annotation

7.7 constants

7.8 enums

7.9 aspect

7.10 exception

7.11 pojo

  1. DTO:数据传输对象(Data Transfer Object),一般用于接受前端传输过来的值,类以 DTO 结尾。
  2. VO:值对象,一般用于返回给前端的数据,类以 VO 结尾。
  3. BO:业务对象,一般用于业务模块内部使用,类以 BO结尾。

8. pom.xml规范

8.1 依赖版本管理

对于依赖的版本需要放在 pom.xmlproperties元素中,标签名以依赖的 artifactId加上 .version作为标签名,例如:

<properties>
  <jackson-xml-databind.version>0.6.2</jackson-xml-databind.version>
</properties>

8. 集合规范

8.1 指定集合初始化容量

正例:

List<String> emails = Lists.newArrayListWithCapacity(users.size());
for (User u : users) {
  emails.add(u.getEmail());
}
Map<String, Object> result = Maps.newHashMapWithExpectedSize(3);
result.put("code",100);
result.put("msg","success");
result.put("result",Collections.emptyList());

反例:

List<String> emails = new ArrayList<>();
for (User u : users) {
  emails.add(u.getEmail());
}
Map<String, Object> result = new HashMap<>();
result.put("code",100);
result.put("msg","success");
result.put("result",Collections.emptyList());

9. 避免空指针

9.1 返回空对象

return Collections.emptyList();
return StringUtils.EMPTY;
return Optinal.empty();
return ArrayUtils.EMPTY_CHAR_ARRAY;

10. 使用业务命名而非技术命名

反例:

List bookList = service.getBooks();

正例:

List books = service.getBooks();

11. 使用卫语句

反例:

function getPayAmount() {
  let result;
  if (isDead)
    result = deadAmount();
  else {
    if (isSeparated)
      result = separatedAmount();
    else {
      if (isRetired)
        result = retiredAmount();
      else
        result = normalPayAmount();
    }
  }
  return result;
}

正例:

function getPayAmount() {
  if (isDead) return deadAmount();
  if (isSeparated) return separatedAmount();
  if (isRetired) return retiredAmount();
  return normalPayAmount();
}

12 使用语意化API

12.1 日期的表示

反例:

long millis = 1000;

正例:

long millis = Duration.ofSeconds(1).toMillis();

12.2 日期的比较

反例:

if (startTime.getTime() > now) {
  // do something
}
if(startTime.compareTo(now) > 0) {
  // do something
}

正例:

if(startTime.after(now)) {
  // do something
}
7 声望
1 粉丝
0 条评论
推荐阅读
Spring Security – RequestRejectedException
Spring Security 提供了 RequestRejectedHandler 来处理当请求被拒绝时候如何处理,在没有进行配置的情况下,默认是使用 DefaultRequestRejectedHandler 直接将异常进行抛出:

async_wait阅读 733

刨根问底 Redis, 面试过程真好使
充满寒气的互联网如何在面试中脱颖而出,平时积累很重要,八股文更不能少!下面带来的这篇 Redis 问答希望能够在你的 offer 上增添一把🔥。

菜农曰17阅读 957

封面图
PHP转Go实践:xjson解析神器「开源工具集」
我和劲仔都是PHP转Go,身边越来越多做PHP的朋友也逐渐在用Go进行重构,重构过程中,会发现php的json解析操作(系列化与反序列化)是真的香,弱类型语言的各种隐式类型转换,很大程度的减低了程序的复杂度。

王中阳Go10阅读 2k评论 3

封面图
万字详解,吃透 MongoDB!
MongoDB 是一个基于 分布式文件存储 的开源 NoSQL 数据库系统,由 C++ 编写的。MongoDB 提供了 面向文档 的存储方式,操作起来比较简单和容易,支持“无模式”的数据建模,可以存储比较复杂的数据类型,是一款非常...

JavaGuide5阅读 827

封面图
计算机网络连环炮40问
本文已经收录到Github仓库,该仓库包含计算机基础、Java基础、多线程、JVM、数据库、Redis、Spring、Mybatis、SpringMVC、SpringBoot、分布式、微服务、设计模式、架构、校招社招分享等核心知识点,欢迎star~

程序员大彬8阅读 1.1k

与RabbitMQ有关的一些知识
工作中用过一段时间的Kafka,不过主要还是RabbitMQ用的多一些。今天主要来讲讲与RabbitMQ相关的一些知识。一些基本概念,以及实际使用场景及一些注意事项。

lpe2348阅读 1.9k

封面图
Git操作不规范,战友提刀来相见!
年终奖都没了,还要扣我绩效,门都没有,哈哈。这波骚Git操作我也是第一次用,担心闪了腰,所以不仅做了备份,也做了笔记,分享给大家。问题描述小A和我在同时开发一个功能模块,他在优化之前的代码逻辑,我在开...

王中阳Go5阅读 2.3k评论 2

封面图
7 声望
1 粉丝
宣传栏