阿里巴巴代码规约整理(强制)

一、编程规约

(一)命名风格

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

反例:_name / $name / name_ / name$

  1. 严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。

正例:ali / taobao / aliyun / youku (国际通用的名称,可视同英文)

反例:pingJia(评价) DaZhe (打折) DianYing(电影)

  1. 代码和注释中避免使用任何语言的种族歧视性词语。

反例:RIBENGUIZI / slave

  1. 类名使用UpperCamelCase风格,但以下情形除外:DO/BO/DTO/VO/AO/PO/UID等

正例:ForceCode / UserDO /

反例:forcecode / userdo

  1. 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格。

反例:getAge / setAge / localValue

  1. 常量命名全部大写,单词间用下划线隔开,力求予以表达完整清楚,不要嫌名字长。

正例:MAX_STOCK_COUNT / CACHE_EXPRIES_TIME

  1. 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类

命名以它要测试的类的名称开始,以 Test 结尾

  1. 类型与中括号紧挨相连来表示数组。

正例:int[] arrayDemo

  1. POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列

化错误。

说明:在本文MySQl规约中的建表约定第一条,表达是与否的变量采用is_xxx的命名方 式,所以需要在<resultMap>设置从is_xxx到xxx的映射关系

反例:Boolean isDeleted这个属性,它的方法也是isDeleted(),框架在反向解析的时候,误以为对应属性名称是deleted,导致属性获取不到,进而抛出异常。

  1. 包名统一使用小写,点分隔符之间有且仅有个自然语义的英语单词。包名统一使用

单数形式,但是类名如果有复数含义,类名可以使用复数形式

正例:com.qcby.user

  1. 避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名,

使可理解性降低

说明:子类、父类之间变量名相同,即使是public类型的变量也能通过编译,另外,局部变量在同一方法内的不同代码块中同名也是合法的,这些情况我们都要避免,对于非setter/getter的参数名称也要避免与成员变量名称相同。

  1. 杜绝完全不规范的缩写,避免望文不知义。

  1. 对于service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用lmpl的后缀与接口区别。

正例:cacheServicelmpl实现CacheService接口

  1. 各层命名规约:

  1. Service/DAO层方法命名规约

  1. 获取单个对象的方法用get作为前缀。

  1. 获取多个对象的方法用list作为前缀,复数结尾,如:listObjects。

  1. 获取统计值的方法要用count作为前缀。

  1. 插入的方法用save/insert作为前缀。

  1. 删除的方法用remove/delete做前缀。

  1. 修改的方法用update前缀

(二)常量定义

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


if (age < 18) { console.log(“未成年”); }
//此时18就是魔法值,将其变为下面形式,
const MINOR_AGE = 18; 
if (age < MINOR_AGE) { console.log(“未成年”); }
//魔法值会降低代码的可读性与可维护性,因为它们含义不清晰,可能会导致错误或者混淆。
//所以要避免魔法值,使用常量代替魔法值 

  1. 在 long 或者 Long 赋值时,数值后使用大写字母 L,不能是小写字母 l,小写容易跟

数字混淆,造成误解。

正例:Long a = 2L

反例:Long a = 2l

(三)代码格式

  1. 如果是大括号内为空,则简洁地写成{}即可,大括号中间无需换行和空格;如果是非

空代码块则:

  1. 左大括号前不换行。

  1. 左大括号后换行。

  1. 右大括号前换行

  1. 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必需换行。

  1. 左小括号和右边相邻字符之间不出现空格;右小括号和左边相邻字符之间也不出现空

格;而左大括号前需要加空格。

反例:if (空格 a == b 空格)

  1. if/for/while/switch/do 等保留字与括号之间都必需加空格。

  1. 任何二目、三目运算符的左右两边都需要加一个空格。

说明:包括赋值运算符号=、逻辑运算符&&、加减乘除符号等

  1. 采用 4 个空格缩进,禁止使用 Tab 字符。

说明:如果使用 Tab 缩进,必需设置 1 个 Tab 为 4 个空格。IDEA 设置 Tab 为 4 个空格时,请勿勾选 Use tab character;而在 Eclipse 中,必需勾选 insert spaces for tabs。


public static void main(String[] args) {
 // 缩进 4 个空格
    String say = "hello";
 // 运算符的左右必需有一个空格
    int flag = 0;
 // 关键词 if 与括号之间必需有一个空格,括号内的 f 与左括号,0 与右括号不要空格
    if (flag == 0) {
        System.out.println(say);
    }
 // 左大括号前加空格且不换行;左大括号后换行
    if (flag == 1) {
        System.out.println("world");
 // 右大括号前换行,右大括号后有 else,不用换行
    } else {
          System.out.println("ok");
 // 在右大括号后直接结束,则必须换行
    }
}
  1. 注释的双斜线与注释内容之间有且仅有一个空格。

正例:// 注意这是示例注释,双斜线与注释内容之间有一个空格

  1. 在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开

正例:double first = 3.2d;

int second = (int)first + 2;

  1. 单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:

  1. 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。

  1. 运算符与下文一起换行。

  1. 方法调用的点符号与下文一起换行。

  1. 方法调用中的多个参数需要换行时,在逗号后进行。

  1. 在括号前不要换行,见反例


正例:
StringBuilder sb = new StringBuilder();
// 超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点号一起换行
sb.append("yang").append("hao")...
       .append("chen")...
       .append("chen")...
       .append("chen");

反例:
StringBuilder sb = new StringBuilder();
// 超过 120 个字符的情况下,不要在括号前换行
sb.append("you").append("are")...append
      ("lucky");
// 参数很多的方法调用可能超过 120 个字符,逗号后才是换行处
method(args1, args2, args3, ...
  , argsX);

  1. 方法参数在定义和传入时,多个参数逗号后面必需加空格。

正例:method(args1, args2, args3);

  1. IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不要

使用 Windows 格式.

(四)OOP(面向对象编程)规约

  1. 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。

  1. 所有的覆写方法,必须加@Override注解。

说明:getObject()与get0bject()的问题。一个是字母的O,一个是数字的0,加@Override 可以准确判断是否覆盖成功。另外,如果在抽象类中对方法名进行修改,其实现类会马上编译报错。(方法签名是指方法的名称和一个参数列表(参数的个数、类型和顺序)

  1. 相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object.。

说明∶可变参数必须放置在参数列表的最后。(建议开发者尽量不用可变参数编程)

正例: public List<User> listUsers(String type, Long... ids){...}

  1. 外部正在调用或者二方库(依赖于公司内部的其他项目发布的jar包)依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。

  1. 不能使用过时的类或方法。

说明: java.net.URLDecoder 中的方法decode(String encodeStr)这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。

  1. Object的equals方法容易抛空指针异常应使用常量或确定有值的对象来调用equals。

正例: "test".equals(object);

反例:Object.equals("test");

说明︰推荐使用JDK7引入的工具类java.util.Objectst#equals(Object a, Object b)

  1. 所有整型包装类对象之间值的比较,全部使用equals方法比较。

说明:对于Integer var = ?在-128至127之间的赋值,Integer对象是在 IntegerCache.cache产生,会复用已有对象,这个区间内的 Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。

  1. 任何货币金额,均以最小货币单位且整型类型来进行存储。

  1. 浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用equals来判断。

说明∶浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数,具体原理参考《码出高效》。


//反例
float a = 1.0F - 0.9F;
float b = 0.9F - 0.8F;
if (a == b) {
    //预期进入代码块,执行其它业务逻辑
    //但事实上a == b的结果为false
} 
Float x = Float.valueOf(a);
Float y =Float.valueOf(b);
if (x.equals(y)) {
    //预期进入代码块,执行其它业务逻辑
    //但事实上x.equals(y)的结果为false
}

//正例
(1)指定一个范围误差,两个浮点数的差值在此范围内,则认为相等
float a = 1.0F - 0.9F;
float b = 0.9F - 0.8F;
float diff = 1e-6F;
if(Math.abs(a-b)<diff) {
    System.out.println("turn");
}
//使用BigDecimal来定义值,再进行浮点数的运算操作
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");

BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);

if(x.compareTo(y) == 0) {
    System.out.println("trun");
}
/*BigDecimal类是java.math包中提供的一个用来对超过16位有效位的数进行精确运算的类。
BigDecimal类创建的是对象,不能使用传统的+、-、*、/等算术运算符直接对其进行数学运算,
而必须调用其对应的方法。BigDecimal类有多种构造函数,可以接受不同类型的参数,
如字符串、整数、长整数、双精度等。*/

  1. 如上所示BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法。

说明:equals()方法会比较值和精度(1.0与1.00返回结果为false ),而compareTo()则会忽略精度。

  1. 定义数据对象DO类时,属性类型要与数据库字段类型相匹配。

正例︰数据库字段的 bigint必须与类属性的Long类型相对应。

反例︰某个案例的数据库表id字段定义类型 bigint unsigned,实际类对象属性为Integer,随着id越来越大,超过Integer的表示范围而溢出成为负数。

  1. 禁止使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象。

说明∶BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。如:BigDecimal g = new BigDecimal(0.1F);实际的存储值为∶0.10000000149

正例:优先推荐入参为String 的构造方法,或使用BigDecimal的valueOf方法,此方法内部其实执行了Double的toString ,而 Double的toString 按double的实际能表达的精度对尾数进行了截断。

BigDecimal recommend1 = new BigDecimal("0.1");

BigDecimal recommend2 = BigDecimal.valueOf(0.1);

  1. 关于基本数据类型与包装数据类型的使用标准如下:

  1. 所有的POJO类属性必须使用包装数据类型。

  1. RPC方法的返回值和参数必须使用包装数据类型。

  1. 所有的局部变量使用基本数据类型。

正例:数据库查询结果可能是null,因为自动拆箱,用基本数据类型接受有NPE风险

反例:某业务的交易报表上显示成交总额跌涨情况,即正负x%,x为基本数据类型,调用RPC服务,调用不成功时,返回的是默认值,页面显示为0%,这显然是不合理的,应该显示成中划线-。所以包装数据类型的null值,能够表示额外的信息,如远程调用失败,异常退出。

  1. 定义DO/DTO/VO等POJO类时,不要设定任何属性默认值。

反例:POJO类中的createTime默认值是new Date(),但当这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间


DO、DTO、VO等都是POJO类。POJO(Plain Ordinary Java Object)是指只有setter/getter/toString的简单类,包括DO/DTO/BO/VO等。
DO(Data Object):此对象与数据库表结构一一对应,通过DAO层向上传输数据源对象。
DTO(Data Transfer Object):前端给后端传递的数据。
VO(View Object):后端给前端传递的数据。
  1. 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法当中。

  1. POJO类必须写toString方法。使用IDE中的工具:source>generate toString时,如果继承了另一个POJO类,注意在前面加一个super.toString。

说明:在方法抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题

  1. 禁止在POJO类中,同时存在对应属性xxx的isXxx()和getXxx()方法。

说明:框架在调用属性xxx的提取方法时,并不能确定到哪个方法一定是优先被调用到的。

(五)日期时间

  1. 日期格式化时,传入pattern中表示年份统一使用小写的y。

说明∶日期格式化时,yyyy表示当天所在的年,而大写的YYYY代表是week in which year ( JDK7之后引入的概念),意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的YYYY就是下一年。

正例:表示日期和时间的格式如下所示︰

new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")

  1. 在日期格式中分清楚大写的M和小写m,大写的H和小写的h分别指代的意义。

说明:日期格式中的这两对字母表意如下:

  1. 表示月份的是大写的M;

  1. 表示分钟是小写的m;

  1. 24小时制的大写的H;

  1. 12小时制的则是小写的h;

  1. 获取当前毫秒数:System.currentTimeMIllis();而不是new Date().getTime()。

说明:如果想获取更加精确的纳秒级的时间值,使用System.nanoTime的方式。在JDK8中,针对统计时间等场景,推荐使用Instant类。

  1. 不允许在程序任何地方中使用

  1. java.sql.Date

  1. java.sql.Time

  1. java.sql.Timestamp

说明:第1个不记录时间,getHours()抛出异常;第2个不记录日期,getYear()抛出异常;第3个在构造方法super((time/1000)*1000),在Timestamp属性fastTime和nanos分别储存和纳秒信息。

反例:java.util.Date.after(Date)进行时间比较时,当入参是java.sql.Timestamp时,会触发JDKBUG(JDK已修复),可能导致比较时的意外结果。

  1. 不要在程序当中写死一年为365天,避免在公历闰年时出现日期转换错误或程序逻辑错误。


正例
//获取今年的天数
int daysOfThisYear =LocalData.now().lengthOfYear();
//获取指定某年的天数
LocalDate.of(2011,1,1).lengthOfYear();

反例
//第一种情况:在闰年366天时,出现数组越界异常
int [] dayArray =new int [365]

//第二种情况:一年有效期的会员制,今年1月26日注册,硬编码365返回的却是1月25
Calendar calendar =Calendar.getlnstance();

calendar.set(2020,1,26);

calendar.add(Calendar.DATE,365);

(六)集合处理

  1. 关于hashCode和equals 的处理,遵循如下规则∶

  1. 只要覆写equals,就必须覆写hashCode。

  1. 因为Set存储的是不重复的对象,依据 hashCode和equals进行判断,所以Set存储的对象必须覆写这两种方法。

  1. 如果自定义对象作为Map 的键,那么必须覆写hashCode和equals。

说明:String因为覆写了hashCode和equals方法,所以可以愉快地将String对象作为key来使用。

  1. 判断所有集合内部的元素是否为空,使用isEmpty()方法,而不是 size()==0的方式。

说明︰在某些集合中,前者的时间复杂度为O(1),而且可读性更好。


正例:
Map <String, Object> map = new HashMap<>(16);
if(map.isEmpty()){
    System.out.println("no element in this map.");
}

  1. 在使用java.util.stream.Collectors类的 toMap()方法转为Map集合时,一定要使用含有参数类型为BinaryOperator ,参数名为mergeFunction的方法,否则当出现相同key值时会抛lllegalStateException 异常。

说明∶参数 mergeFunction的作用是当出现key重复时,自定义对value的处理策略。

正例:

(七)并发处理

(八)控制语句

(九)注释规约

(十)前后端规约

(十一)其他

  • 14
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

日上三杆快起床

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值