阿里开发手册:编程规约之命名风格的强制要求

2024-05-29
来源:网络整理

前言

《阿里巴巴开发手册》是近万名阿里巴巴开发者集体智慧的结晶,它以开发视角为切入点,详细罗列了如何更高效、更容错、更协同地进行开发,力求明辨是非、正反面结合,帮助 Java 开发者提升协作效率和代码质量。

高效、高质量地编写代码!1. 编程规范(一)命名风格

1.【强制】所有编程相关的名称不能以下划线或者美元符号开头或者结尾。

2.【强制】所有编程相关的名称严禁使用拼音和英文混合,不允许直接使用中文。

3.【强制】代码和评论中请避免使用任何种族歧视性或任何人类语言中的侮辱性词语。

4. 【强制】使用类名风格,但以下类型除外:DO/PO/DTO/BO/VO/UID 等。

5.【强制】方法名、参数名、成员变量、局部变量应使用统一的风格。

6. 【强制】常量名尽量使用大写字母,单词之间用下划线分隔,力求语义完整清晰,不要觉得名字过长。

7. 【强制】抽象类名以 或 Base 开头,异常类名以

8. 【强制】类型与括号相邻,定义一个数组。

9. 【强制】POJO 类中的任何布尔变量都不要添加 is 前缀,否则某些框架解析时会导致序列化错误。

10. 【强制】包名必须小写,每个点之间只能有一个自然英文单词。包名必须为单数形式,但类名若有复数含义,可以使用复数形式。

11. 【强制】避免子类与父类的成员变量之间、不同代码块的局部变量之间使用完全相同的名称,降低可理解性。

public class ConfusingName { protected int stock; protected String alibaba; // 非 setter/getter 的参数名称,不允许与本类成员变量同名 public void access(String alibaba) { if (condition) { final int money = 666; // ... } for (int i = 0; i < 10; i++) { // 在同一方法体中,不允许与其它代码块中的 money 命名相同 final int money = 15978; // ... } } } class Son extends ConfusingName { // 不允许与父类的成员变量名称相同 private int stock; }

12.【强制】避免使用完全不规范的英文缩写,以免造成对词义的误解。

13.【建议】为使代码具有可解释性,任何自定义的编程元素都应使用完整的词组合来命名。

14.【推荐】常量、变量的命名,将表示类型的名词放在词尾,以提高识别度。

15.【推荐】若模块、接口、类或方法使用了设计模式,则应在名称中体现具体模式。

public class OrderFactory; public class LoginProxy; public class ResourceObserver;

16.【推荐】接口类中的方法和属性不要添加任何修饰符(或),保持代码简洁,添加有效的注释。接口中尽量不要定义常量,若必须定义,最好确保常量与接口方法相关,且是整个应用的基础常量。

17.接口和实现类的命名有两套规则:

1)【强制】对于 DAO 类,基于 SOA 的理念,暴露的服务必须是接口,内部实现类必须使用后缀“Impl”,以和接口相区别。

2)【推荐】如果接口名需要描述能力,则使用相应的形容词作为接口名(通常以-able结尾的形容词)。

18.【参考】枚举类名以Enum为后缀,枚举成员名称全部大写,单词间以下划线分隔。

19.【参考】各层命名规范:

A)/DAO层方法命名规范:

B)领域模型命名约定:

(二)常量定义

1.【强制】不允许任何魔法值(即没有预定义的常量)直接出现在代码中。

反例:

// 开发者 A 定义了缓存的 key。 String key = "Id#taobao_" + tradeId; cache.put(key, value); // 开发者 B 使用缓存时直接复制少了下划线,即 key 是"Id#taobao" + tradeId,导致出现故障。 String key = "Id#taobao" + tradeId; cache.get(key);

2. 【强制】对 long 或 Long 类型赋值时,值后面必须使用大写 L,不能使用小写 l,小写 l 容易与数字混淆,造成误解。

3. 【强制】浮点数类型的后缀必须为大写 D 或 F。

正面例子:

public static final double HEIGHT = 175.5D; public static final float WEIGHT = 150.3F;

4.【建议】不要用一个常量类来维护所有常量,应根据常量的功能进行分类,分别维护。

5.【推荐】常量复用分为 5 个级别:跨应用程序共享常量、应用程序内共享常量、子项目内共享常量、包内共享常量、类内共享常量。

反例:简单易懂的常量也应该统一定义为应用程序内部的共享常量。两个程序员分别在两个类中定义了代表“是”的常量:

类 A 中:public static final String YES = "yes"; 类 B 中:public static final String YES = "y";

A.YES.(B.YES)预期为真,但实际返回的是,造成线上问题。

6.【推荐】如果变量值只在固定范围内变化,请使用枚举类型定义。

注意:如果除了名称之外还有扩展属性,则需要使用枚举类型。下面例子中的数字即为扩展信息,表示季节。

正面例子:

public enum SeasonEnum { SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4); private int seq; SeasonEnum(int seq) { this.seq = seq; } public int getSeq() { return seq; } }

(三)代码格式

9. 【强制】定义并传递方法参数时,多个参数的逗号后面必须有空格。

method(args1, args2, args3);

10.【强制】IDE 的文本文件编码必须为 UTF-8;IDE 文件的换行符使用 Unix 格式,不要使用 。

11.【建议】单个方法总行数不要超过80行。

12.【推荐】变量赋值的等号与上一行对应位置的等号对齐,无需加空格。

正面例子:

int one = 1; long two = 2L; float three = 3F; StringBuilder builder = new StringBuilder();

13.【推荐】不同逻辑、语义、业务的代码之间最好插入空行,提高可读性。

(四)OOP规范

23.【推荐】扩展循环体中字符串的连接方法。

反例:

String str = "start"; for (int i = 0; i < 100; i++) { str = str + "hello"; }

24.【推荐】可以声明类、成员变量、方法、局部变量。关键字的使用场景如下:

25.【推荐】谨慎使用此方法复制对象。

26.【推荐】对类成员和方法进行严格的访问控制:

注意:严格控制任何类、方法、参数、变量的访问范围,访问范围过宽不利于模块解耦。想想看,如果是方法,你想删就删,但删掉成员方法或者成员变量,岂不是手心冒汗?变量就像自己的孩子,尽量控制在自己的视线范围内,如果变量范围过大,无节制地到处乱跑,那可就得担心了。

(五)日期和时间

1.【强制】日期格式,使用小写y表示年份。

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

2.【强制】日期格式中,明确区分大写 M 与小写 m,大写 H 与小写 h 的含义。

3.【强制】获取当前毫秒数:.();而不是new Date().()。

4.【强制】程序中任何地方都不允许使用以下函数:1)java.sql.Date 2)java.sql.Time 3)java.sql.

super((time / 1000) * 1000),在 Timestamp 属性 fastTime 和 nanos 分别存储秒和纳秒信息。

5.【强制】程序中不要将年份硬编码为 365 天,以免闰年时出现日期转换错误或程序逻辑错误。

正面例子:

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

反例:

// 第一种情况:在闰年 366 天时,出现数组越界异常 int[] dayArray = new int[365]; // 第二种情况:一年有效期的会员制,2020 年 1 月 26 日注册,硬编码 365 返回的却是 2021 年 1 月 25 日 Calendar calendar = Calendar.getInstance(); calendar.set(2020, 1, 26); calendar.add(Calendar.DATE, 365);

6.【建议】避免闰年二月的问题,闰年的二月有29天,一年后的日期不可能是2月29日。

7.【推荐】使用枚举值来指代月份。如果使用数字,请注意 Date 等日期相关类的月份值范围为 0 至 11。

(六)收款处理

15.【强制】JDK7以上版本,实现类必须满足下面三个条件,否则.sort会抛出异常。

注:三种情况如下

反例:下面的例子没有处理相等的情况,两个对象交换的结果并不互相反转,不满足第一个条件,实际使用中可能会引发异常。

new Comparator() { @Override public int compare(Student o1, Student o2) { return o1.getId() > o2.getId() ? 1 : -1; } };

16.[推荐] 使用泛型集合时,请使用语法,或者在 JDK7 及以上版本中完全省略它。

注意:菱形泛型,即直接用来引用前面指定的类型。

正面例子:

// diamond 方式,即<> HashMap userCache = new HashMap<>(16); // 全省略方式 ArrayList users = new ArrayList(10);

17.【推荐】初始化集合时,指定集合的​​初始大小。

18.【推荐】使用 Map 类集合 KV 的遍历代替直接遍历。

(七)并发处理

13.【推荐】对于资金相关的敏感财务信息,使用悲观锁策略。

14.【推荐】使用异步转同步操作,每个线程退出前都要调用该方法,执行代码时注意异常,保证方法执行成功,避免主线程直到超时才执行完该方法并返回结果。

15.【推荐】避免多线程使用实例,共享实例虽然线程安全,但是由于争夺同一个种子,性能会有所下降。

16.【推荐】通过双重检查锁实现惰性初始化(-),目标属性需声明为类型(例如,被修改的属性声明为 = null;)。

正面例子:

public class LazyInitDemo { private volatile Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (helper == null) { helper = new Helper(); } } } return helper; } // other methods and fields... }

17.【参考】解决多线程系统中不可见内存问题对于一次写入多次读取的情况,可以解决变量同步问题,但是如果有多次写入,那么线程安全问题同样无法解决。

注意:如果是++操作,使用下面的类来实现:

AtomicInteger count = new AtomicInteger(); count.addAndGet(1);

如果是JDK8的话,建议使用对象,性能更佳(减少乐观锁的重试次数)。

18.【参考】容量不足时,高并发可能会出现死链接,导致CPU飙升,开发过程中请注意规避此风险。

19.【参考】修改对象用法不能解决共享对象的更新问题。

(八)控制报表

10.【推荐】考虑循环体中语句的性能,定义对象和变量、获取数据库连接、执行一些不必要的 try- 操作(这个 try- 能不能移到循环体外面)等,尽量移到循环体外面。

11.【推荐】避免使用否定逻辑运算符。

12.【推荐】公共接口需要保护输入参数,特别是批量操作的接口。

13.【参考】以下情况需要进行参数验证:

14.【参考】以下情况无需进行参数验证:

(九)注释规则

10.【参考】注释的要求:一是要准确体现设计思路和代码逻辑;二是要描述业务含义,让其他程序员能快速了解代码背后的信息。大段的代码没有注释,对读者来说就像一个谜。注释是给自己看的,这样即使过了很久,也能清楚的理解当时的思路;注释也是给你的继任者看的,这样他能快速的接手你的工作。

11.【参考】好的命名和代码结构是自解释的,注释要简洁、准确、表达得当。避免注释的另一个极端:注释太多。一旦代码的逻辑被修改,修改注释是一个相当大的负担。

反例:

// put elephant into fridge put(elephant, fridge);

方法名 put 和两个有意义的变量名 and 已经解释了它的作用。语义清晰的代码不需要额外的注释。

12.【参考】对于特殊的注释标记,请注明标记人及标记时间,注意及时处理此类标记,并经常通过标记扫描的方式清理此类标记,线上故障有时就是源于这些标记处的代码。

2. 日志异常

(一)错误代码

10.【参考】错误代码分为一级宏错误代码、二级宏错误代码、三级宏错误代码。

11.【参考】错误码后三位和HTTP状态码无关。

12.【参考】错误码有助于不同文化背景的开发者之间的沟通和代码协作。

注意:以英文单词形式出现的错误代码不利于非英语国家(如阿拉伯语、希伯来语、俄语等)开发人员之间的协作。

13.【参考】错码是人性,情感认知+口口相传,用纯数字排列错码不利于情感记忆和分类。

(二)异常处理

11.【推荐】预防NPE是程序员的基本技能,注意以下场景可能发生NPE:

正例:使用JDK8的类来避免NPE问题。

12.【建议】定义时区分 / 异常,避免直接抛出 new(),不允许抛出 or。使用具有业务含义的自定义异常。建议使用业界已经定义好的自定义异常,如:/ 等。

13、【参考】对公司外部的HTTP/API开放接口必须使用错误码,应用内部建议使用异常抛出;跨应用的RPC调用优先使用封装()方法、错误码、简要的错误信息;应用内部建议使用异常抛出。

描述:使用RPC方法返回方法的原因:

1)如果调用者没有捕获异常,就会发生运行时错误。

2)如果不添加堆栈信息,而只是新建一个自定义异常,并加入自己的理解,对调用者解决问题不会有太大的帮助。

如果加上堆栈信息,在调用频繁出错的情况下,数据序列化和传输的性能损耗也会是个问题。

3.单元测试

12.【推荐】适时对不可测试的代码进行必要的重构,使代码变为可测试的,避免为了满足测试需求而编写不规范的测试代码。

13.【建议】在设计评审阶段,开发人员需要与测试人员共同确定单元测试的范围,单元测试最好覆盖所有测试用例(UC)。

14.【建议】作为质量保证措施,单元测试应在项目提交测试前完成,不建议在项目发布后再添加单元测试用例。

15.【参考】为方便单元测试,业务代码应避免以下情况:

注意:建议使用保护语句、策略模式、状态模式等重构多层条件语句。

16.【参考】不要对单元测试有以下误解:

4. 安全规定

8.【强制】在使用平台资源时,如短信、邮件、电话、下单、支付等,必须正确实现防重放机制,如数量限制、疲劳控制、验证码验证等,避免被滥用,造成经济损失。

注意:如果注册时向手机发送验证码,且没有对此类短信的发送次数和频率进行限制,该功能可能会被用来骚扰其他用户,造成短信平台资源的浪费。

9.【强制】文件上传功能,必须严格检查和控制文件的大小和类型。

描述:攻击者可以利用上传漏洞将恶意文件上传到服务器并远程执行,从而控制网站服务器。

10.【强制】配置文件中的密码需要加密。

11.【推荐】发帖、评论、发送等需要用户输入的即时消息,必须实施反垃圾信息、内容禁忌词过滤等风险控制策略。

5.数据库

(一)建表规则

15.【推荐】当单表行数超过500万或者单表容量超过2GB时,建议进行分库分表。

注意:如果你预计三年后的数据量不会达到这个程度,请不要在创建表时拆分数据库或表。

16.【参考】适当的字符存储长度,不但可以节省数据库表空间和索引存储,更重要的是提高检索速度。

正例:无符号数值可以避免误存负数,扩大表示范围:

(二)索引惯例

9.【推荐】创建组合索引时,将区分度最高的放在最左边。

10.【推荐】避免字段类型不同导致的隐式转换,造成索引失效。

11.【参考】创建索引时应避免以下极端误区:

(三)SQL语句

11.【建议】尽量避免使用 in 操作。若无法避免,请谨慎评估 in 之后集合中的元素个数,控制在 1000 个以内。

12.【参考】由于国际化要求,所有字符存储和表示均采用utf8字符集,因此需要注意字符计数方法。

阐明:

SELECT LENGTH("轻松工作");--返回为 12 SELECT CHARACTER_LENGTH("轻松工作");--返回为 4

如果需要存储表达式,则选择'/u01/utf8'来存储,同时注意它和utf8编码的区别。

13.【参考】该语句比 速度更快,占用更少的系统和事务日志资源,但是不会触发事务,可能会造成意外,所以不建议在开发代码中使用该语句。

描述:功能上与没有子句的语句相同。

(四)ORM映射

7.【强制】更新数据表记录时,必须将该记录对应字段值更新为当前时间。

8.【建议】不要写一个大而全的数据更新接口,传入POJO类时,不管是否是目标更新字段,都设置c1=、c2=、c3=,这样是不对的。执行SQL时,不要更新不变的字段,一是容易出错,二是效率低,三是增加存储。

9.【参考】@不要滥用事务,事务会影响数据库的QPS,另外使用事务的地方需要考虑各种回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。

10.[] 是与属性值进行比较的常量,通常为数字,表示当它们相等时,执行此条件;表示当不为空且不为null时,执行此条件;表示当不为null值时,执行此条件。

6. 工程结构

(一)应用分层

1.【建议】基于业务架构实践,结合行业分层规范、热门技术框架分析,建议分层结构如图所示,默认上层依赖下层,箭头关系表示直接依赖。例如开放API层可以依赖Web层(层),也可以直接依赖层,等等:

(二)第二方库依赖

11.【建议】第二方库不应该有配置项,至少不要再增加任何配置项。

12.【推荐】不要使用不稳定的工具包或类。

13.【参考】为避免使用二方库时出现依赖冲突,二方库发布者应遵循以下原则:

(三)服务器

6.【建议】线上生产环境,将 JVM 的 Xms 与 Xmx 的内存容量设置为相同大小,避免 GC 完成后调整堆大小的压力。

7.【推荐】了解各个服务的平均耗时,可以单独配置线程池,将较慢的服务与主线程池隔离,避免不同服务的线程一起被销毁。

8.【参考】必须使用服务器内部重定向;外部重定向地址必须使用URL生成,否则浏览器会提示“不安全”,因为是线上协议。另外,还会造成URL维护不一致的问题。

7.设计规格ps:新增11种规格!

例如浮点数的后缀必须全部大写;枚举的属性字段必须是私有的、不可变的;配置文件中的密码需要加密等等。

在描述中添加了 2 个正面和负面的例子

例如多个构造函数的顺序处理;

添加了 5 条扩展说明

例如父集合元素的异常增加或者删除。

修改描述 22 处

例如,魔法值、问题等的示例代码。

修正了嵩山版本中的一些代码格式错误和描述错误。

阿里巴巴Java开发手册(黄山版)2022年最新版已整理出来,开发者们赶紧行动起来,遵守代码规范吧,哈喽,我哈喽,大家哈喽!

没有规则就没有秩序,没有标准就没有合作

众所周知,交通法规表面上是为了限制驾驶权利而制定的,但实际上是为了保障公众的人身安全。试想一下,如果没有速度限制、没有红绿灯、也没有右侧行驶的规定,谁还敢在路上开车呢?

分享