编码规范 - 养成良好的Java编码习惯

最近在整理公司编码规范方面的内容,2017阿里巴巴发布了编码规范插件,强烈建议大家安装使用,好的编码习惯是通往成功的阶梯。

技术群二维码在底部,欢迎进群学习!!!

简书整套文档以及源码解析

专题

专题名称

专题描述

001

Spring Boot 核心技术

讲解SpringBoot一些企业级层面的核心组件

002

Spring Cloud 核心技术

对Spring Cloud核心技术全面讲解

003

QueryDSL 核心技术

全面讲解QueryDSL核心技术以及基于SpringBoot整合SpringDataJPA

004

SpringDataJPA 核心技术

全面讲解SpringDataJPA核心技术

文档目录

  • 注释规范
    • 类注释
    • 方法注释
    • 行级注释
    • DTO/Param注释
  • 编码规范
    • 命名风格
    • 常量定义
    • 代码格式
    • OOP 规约
    • 集合处理
    • 控制语句
  • 异常日志规范
    • 异常处理
    • 日志规约

一、注释规范

类注释

类、类属性使用Javadoc规范,类上描述该类的主要作用,注释尽可能详细,推荐把使用该类地方使用@see注解进行标注,类属性详细描述该属性的保存内容。 类注释示例

/**
 * 统一资源Aop切面定义
 * 根据自定义注解配置自动设置配置的资源类型到指定的字段
 * @author:于起宇 <br/>
 * ===============================
 * Created with IDEA.
 * Date:2017/12/15
 * Time:14:05
 * ================================
 */

类属性注释示例

/**
 * 资源处理业务逻辑
 */
Autowired
ResourcePushService resourcePushService;

方法注释

接口方法、实现类方法、抽象方法等,详细描述该方法主要作用,尽可能的描述出方法的主要流程步骤,方法定义的每一个参数都需要有详细的注释描述,建议添加方法返回值描述。 方法注释示例

    /**
     * 资源设置切面方法
     * 拦截配置了@ResourceMethod注解的class method
     * cglib仅支持class 方法切面,接口切面不支持
     * @param proceedingJoinPoint 切面方法实例
     * @param resourceMethod 方法注解实例
     * @return 原方法返回对象
     */
    @Around(value = "@annotation(resourceMethod)")
    public Object resourcePutAround(ProceedingJoinPoint proceedingJoinPoint, ResourceMethod resourceMethod)
        throws Throwable
    {
        //...
    }

如果是业务逻辑方法注释建议添加步骤,如下所示:

    /**
     * 创建帖子
     * - 转换参数实体为
     * - 保存帖子基本信息
     * @param param 创建帖子请求参数实体
     * @return 帖子编号
     * @throws LogicException
     */
    public String create(CreateTopicParam param) throws LogicException {
        //...
    }

在上面代码中-分隔符代表主要执行步骤,每一个步骤以-分隔符开始,如果方法内存在逻辑分支处理,请看下面行注释

行注释

行级注释一般都是方法内使用到,分为单行注释、多行注释,单行注释采用//设置,多行注释采用/* */设置,如下所示: 单行注释

// 执行方法,获取返回值

多行注释

/*
 * 执行方法,获取返回值
 * 获取返回值进行后续逻辑处理
 */

DTO/Param注释

我们在实际开发过程中数据库对应的实体是不允许直接拿出来添加一些附加字段的,也就是禁止添加非该数据表对应实体内的字段,这种情况我们需要定义DTO/Param

DTO注释

DTO是数据返回实体定义,如果我们在查询数据库时需要关联其他表的数据并且返回给前端,那么我们可以创建XxxDTO,注意:DTO全部大写,只需要继承查询逻辑的主表实体就可以完成附加字段的添加,要为每一个附加字段添加javadoc详细注释,如下所示:

/**
 * 帖子列表数据转换实体
 * @author:于起宇 <br/>
 * ===============================
 * Created with IDEA.
 * Date:2018/1/8
 * Time:10:52
 * ================================
 */
@Data
public class TopicListDTO
        extends CommunicationTopicInfoEntity {

    /**
     * 用户昵称
     */
    private String userNickName;
    /**
     * 所属机构名称
     */
    private String orgName;
    //...
}
Param注释

对于接口、后台来说接受请求时一般都是带着一些参数,目前我们系统是前台完全分离,所以后台其实变相的也是接口,在上面DTO也有说到数据实体不允许添加附加参数,我们的参数也不可能都是数据实体内的字段,这时需要创建对应的参数实体XxxParam,参数实体内的所有字段都需要添加javadoc注释,如下所示:

/**
 * 查询帖子列表
 * - 用于查询自己、他人、关键字、首页帖子请求参数
 *
 * @author:于起宇 <br/>
 * ===============================
 * Created with IDEA.
 * Date:2018/1/8
 * Time:10:31
 * ================================
 */
@Data
public class SelectTopicParam extends PagerParam {
    /**
     * 用户编号
     */
    private String userId;
    /**
     * 查询关键字
     */
    @Length(max = 30)
    private String keyWord;
}

二、编码规范

命名风格

  1. 代码中命名不能以下划线或美元符号开始,也不能以下划线或美元符合结束。

错误示例:

_name / name_ / $name / name$ / __name / name__
  1. 命名严禁出现中文拼音与英文混合方式出现,不允许直接使用中文方式命名

错误示例

WenZhang[文章] / WenZhangInfo[文章信息] / getTopicLieBiao()[获取文章列表] / int 数量 = 0
  1. 类名使用UpperCamelCase风格,DTOVO除外

错误示例

QRCode / UserInfoDto / XMLService

正确示例

QrCode / UserInfoDTO / XmlService
  1. 方法名、参数名、成员变量、局部变量统一使用lowerCamelCase风格,必须使用驼峰命名方式。

错误示例

localvalue / GetUserInfo() / userid

正确示例

localValue / getUserInfo() / userId
  1. 常量命名全部大写,单词间下划线隔开,完整的表达含义,名字可以过长。

错误示例

MAX_COUNT / NAME

正确示例

MAX_STOCK_COUNT / DEFAULT_ORG_NAME
  1. 抽象类命名使用Abstract或者Base开头,异常类命名使用Exception结尾,测试类命名时以将要测试的类全名 + Test

正确示例

AbstractCodeMessageService / BaseCodeMessageService / LogicException / UserControllerTest
  1. 包名统一使用小写,每个分隔符必须为自然语义的英文单词,另外包名统一使用单数含义,如果需要复数含义,则可以在类名上体现。

正确示例

com.sanmi.framework.core / com.sanmi.framework.orm
  1. 杜绝不规范的缩写,避免词义表达不清楚。

错误示例

AbstractClass = > AbsClass
condition => condi
  1. 接口中的方法和属性不要添加任何修饰符(public也不要添加),为了保持代码的简洁性,加上有效的javadoc注释。

错误示例

public abstract void commit();

正确示例

void commin(); / String COMPANY_NAME = "sanmi";
  1. 对于ServiceDAO类命名时实现类后缀要以Impl结尾,Mapper接口不存在实现类,无需处理。

正确示例

UserInfoService / UserInfoServiceImpl
  1. 枚举类名统一带上Enum后缀,枚举成员名称需要全大写,单词间使用下划线隔开。

正确示例

枚举名称 => UserStatusEnum
成员名称 => ENABLE / DISABLED / DELETE
  1. 各层方法命名规约 Service/DAO/Mapper层方法命名规约如下:
    • 获取单个对象的方法用get作为前缀
    • 获取多个对象的方法用list作为前缀
    • 获取统计值的方法用count作为前缀
    • 插入方法用save / insert作为前缀
    • 删除方法用delete / remove作为前缀
    • 修改方法用update作为前缀

常量定义

  1. 不允许任何魔法值(未经过预先定义的常量)直接出现在代码中

错误示例

if("enable".equals(user.getStatus())) { 
   //.. 
}
  1. 使用封装类型代替基本数据类型

正确示例

Integer => int
Double => double
Long => long
...
  1. 如果常量仅在一个范围内变化,使用enum来代替定义

正确示例

@Getter
public enum  UserBzEnum
{
    /**
     * 普通用户
     */
    APP_USER("0"),
    /**
     * 月嫂
     */
    NANNY("1"),
    /**
     * 医生
     */
    DOCTOR("2")
    ;
    private String value;

    UserBzEnum(String value) {
        this.value = value;
    }
}

代码格式

  1. 大括号的使用约定,如果大括号内为空,直接使用{}即可,不需要换行;如果非空代码,则需要:
    • 左大括号前不换行
    • 左大括号后换行
    • 右大括号换行
    • 右大括号后还有else等代码则不换行;表示终止的右大括号后必须换行。
  2. 左小括号和字符之间不出现空格;右小括号和字符之间也不出现空格。 错误示例
if ( a == b )
  1. if / for / while / switch / do 等保留字与括号之间都必须加空格
  2. 任何二目、三目运算符的左右两边都需要加一个空格。

正确示例

 if (ValidateTools.isEmpty(uploadBackEntity) || ValidateTools.isEmpty(uploadBackEntity.getUploadUrl())) {
 //...
        }
  1. 采用4个空格缩进,禁止使用Tab控制符。
  2. 行级注释的//与注释内容之间有且仅有一个空格。

正确示例

// 定义用户名
String userName = user.getName();
  1. 单行字符不超过120个,超过需要换行,换行原则如下:
    • 第二行相对于第一行缩进4哥空格,从第三行开始不再进行缩进
    • 运算符一起换行(如:+)
    • .符号一起换行
    • 方法调用中的多个参数需要换行时,在逗号后进行。

正确示例

StringBuffer buffer = new StringBuffer();
// 超过120个字符的情况下,换行缩进4个空格,``.``和方法名一块换行
buffer.append("san").append("mi") ...
        .append("ke")
        .append("ji");
// 参数过多超120个字符时,在 , 后换行
method(args1, args2, args3, ...
args98, args99
)        
  1. 方法参数在定义和传入时,多个参数后面必须加空格。 正确示例
method(args1, args2);
OOP 规约
  1. 避免通过一个类的对象引用访问此类的静态变量或者静态方法,会造成编译器解析成本,直接用类名访问即可。【JVM堆、栈、静态代码块解析成本是不一样的】
  2. 所有覆盖方法,必须添加@Overrider注解
  3. 对外部正在调用或者二方库依赖的接口,不允许修改方法签名,以避免对接口调用方产生影响;如果接口已经过时,必须添加@Deprecated注解,并清晰的说明替代接口或者替代服务。
  4. 禁止使用过时类或方法。
  5. Objectequals方法容易抛出空指针异常,应使用常量或确定值的对象来调用equals方法。

错误示例

object.equals("test");

正确示例

"test".equals(object)
  1. 所有相同类型的封装类之间比较,必须使用equals方法。

说明int封装类Integer-128 ~ 127范围内的赋值会在IntegerCache.cache中产生,该区间的值可以直接使用==进行比对,但是该区间外的值都会以引用类型在内创建,对象之间是无法使用==来进行比对的!

  1. 构造函数内禁止编写任何业务逻辑,如果有业务逻辑请创建init方法使用。
集合处理
  1. 使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小则是list.size()

正确示例

List<String< list = new ArrayList();
list.add("jinan");
list.add("sanmi");
// 生成list长度的数组实例
String[] array = new String[list.size()];
// 执行集合转换数据
array = list.toArray(array);
  1. 在使用工具类Arrays.asList()方法时,不能使用修改集合相关的方法,add / remove / clean方法会抛出异常。
  2. 禁止在foreach循环内进行元素的remove / add操作,remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

正确示例

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String item = iterator.next();
    if (condition) {
        iterator.remove();
    }
}
控制语句
  1. 在一个switch内,每个case要么使用break / return来终止,要么注释说明程序将继续执行到具体的哪个case为止;在一个switch内必须包含default代码块在所有case之后,就算业务逻辑为空也要存在!
  2. if / else / for / while / do语句中必须使用大括号,即便是只有一行代码,也需要添加大括号。

错误示例

if (condition) statements;

正确示例

if (condition) {
    statements;
}
  1. 在表达异常分支时,尽可能的少用if / else if嵌套方式,可以修改成:
if (condition) {
    //...
    return object;
}
// 继续处理 else / else if 业务逻辑代码

如果不得不使用超过三层的if / else if逻辑判断,可以将代码改成卫语句策略设计模式状态设计模式,下面是卫语句示例:

public void today() {
    if (isBuy()) {
        System.out.println("do something.");
        return;
    }
    if (isFree()) {
        System.out.println("do something.");
        return;
    }
    System.out.println("coding..");
}

在我们系统设计时,一般都会使用throw new XxxException();return;代替,所有逻辑异常判断都采用自定义业务逻辑异常进行处理。

三、异常日志规范

异常规约
  1. Java 类库中定义的可以预判断来规避RuntimeException,不应该采用try {} catch(Exception e){}来处理。

错误示例

// 直接使用不确定对象
object.setXxx(value);

正确示例

// 判断非空后使用不确定对象
if (object != null) {
    //...
}
  1. 捕获异常目的是为了处理它,不要捕获了却什么都不处理而抛弃,如果不想处理它,请将该异常抛给它的调用者,最外层的业务使用者必须处理异常,将异常信息转换成用户可以理解的提示信息。
  2. try代码块放到了事务代码中,catch异常后,如果需要回滚事务,一定要注意rollback事务。
  3. finally代码块必须对资源对象、刘对象进行关闭操作,即使有异常也要做try-catch操作。
  4. 不能在finally代码块中使用return
  5. 业务逻辑异常请交付给框架处理,我们将业务逻辑验证使用业务逻辑异常处理的机制进行抛给框架处理。

正确示例

if (condition) {
    throw new LogicException(ErrorCodeEnum.USER_NOT_FOUND);
}
日志规约
  1. 代码中不可直接使用日志系统(Log4j、Logback)中的API,而依赖使用日志框架SLF4j中的API。使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。 正确示例
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
// 获取日志对象
private static final Logger logger = LoggerFactory.getLogger(Xxx.class);

日志对象定义一般都会在项目框架搭建初期定义,直接使用即可,如果需要自定义,则按照上面的方式进行声明日志对象。

  1. trace / debug / info级别的日志输出,必须使用占位符的方式,如果不使用占位符而是直接拼接,可能会导致变量为null导致系统异常,还一点日志等级不匹配时虽然不会打印,但是会执行字符串的拼接,浪费服务器系统资源。

正确示例

logger.debug("执行查询用户:{},基本信息。",user.getId());
  1. 针对方法的主要参数需要打印对应的值,方便后期日志调试项目。

正确示例

public void today(String userId) {
    logger.debug("查询用户:{},今日任务完成进度",userId);
}
  1. 方法内分支逻辑需要打印对应的日志信息,方便后期日志调试项目。

正确示例

if (condition1) {
    logger.debug("do something.");
} else if (condition2) {
    logger.debug("do other thing.");
}
  1. 系统异常包括两大类:案发现场异常堆栈信息.

正确示例

logger.error(参数或对象.toString() + "_" + e.getMessage(), e);

写在最后

强烈建议IDEA开发工具安装使用阿里巴巴国际编码规约插件,为良好的编码习惯打下基础。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏JackieZheng

FreeMarker模板开发指南知识点梳理

freemarker是什么? 有什么用? 怎么用? (问得好,这些都是我想知道的问题) freemarker是什么?   FreeMarker 是一款 模板引擎...

2349
来自专栏企鹅号快讯

8行代码实现ui文件到py文件转换

在用PyQt进行GUI编程时,一般先通过Qt Designer产生后缀为.ui的UI文件(类似于XML文件),接着将.ui文件转换成.py文件,再通过一个pyt...

2848
来自专栏向治洪

JavaScript严格模式

"use strict" 指令 "use strict" 指令在 JavaScript 1.8.5 (ECMAScript5) 中新增。它不是一条语句,但是是一...

1795
来自专栏名山丶深处

Hello——Java10新特性,请了解一下

1405
来自专栏小尘哥的专栏

thymeleaf全局常量定义

目测应该是重写这货就可以了,talk is cheap,show me the code

951
来自专栏芋道源码1024

Redis 从入门到起飞(上)

1. Redis 介绍1.1 NoSQL 基本概念1.2 NoSQL 分类1.3 Redis 基本概念1.4 发展历史1.5 应用场景2. Redis 安装2....

1454
来自专栏JavaQ

Java研发方向如何准备BAT技术面试答案(上)

最近因为忙于工作,没时间整理,本篇是下班后晚上抽空整理的,文中部分答案本来是想自己好好整理一份的,但是时间真的很紧,所以就整理了一下网络上的文章链接,挑了写的不...

3565
来自专栏名山丶深处

Hello——Java10新特性,请了解一下

2505
来自专栏后端技术探索

反射机制、依赖注入、控制反转

反向: dll->类[方法,属性]. 从已经有的dll文件反编译得到其中的一些可用的方法.

872
来自专栏小灰灰

Java 回调函数的使用

回调函数 回调函数是什么鬼, 回调函数干嘛用,回调函数可以怎么用 如果有过android开发经验,经常可以看到一些类似下面的代码 Button Btn1 = ...

3068

扫码关注云+社区