读阿里Java开发手册有感
highlight: ascetic theme: condensed-night-purple
前言
都说代码不是写给自己看的,而是写给其他人看的,大家都能看懂的代码才是好代码!可是怎样的代码大家才能看懂呢?阿里作为国内Java当之无愧的大佬,最近两天抽空断断续续读完了阿里的《Java开发手册》,书中制定了很多规范也提出了很多建议。我想我们每个Javaer在正式开始写代码前都去看看。
其中有一些规范是之前就已经了解过的,也有一些是直接依靠现在的ide就可以直接避免的(代码规范问题),因此我在这里就分享一些我之前没有接触过的,比较陌生的一些知识。
一.编码规约
1.1 命名风格
1.代码中的命名严禁使用拼音和英文结合的方式,全拼音的方式也要避免采用。
2.常量命名全部大写,单词之间用下划线来隔开,力求语义表达清楚,不要嫌名字长。
正例:MAX_STOCK_COUNT
反例:MAX_COUNT
3.抽象类命名使用Abstract或者Base开头,异常类命名使用Exception结尾,测试类要使用测试的类名开头,Test结尾。
4.POJO类中boolean类型的变量,都不要加is,否则部分框架解析会引起序列化的错误。
反例:定义基本数据类型Boolean isDeleteed;的属性,它的方法也是isDeleted(),RPC框架在反向解析的时候,以为对应的属性名称是deletedd,导致属性获取不到,进而抛出异常。
5.包名统一使用小写,单数形式,类名可以使用复数形式。
6.如果使用到了设计模式,建议在类名中体现出设计模式。有利于阅读者快速理解架构设计思想。
例:public class OrderFactory
- 获取单个对象方法用get做前缀;获取多个对象的方法用list做前缀;获取统计值的方法用count做前缀;插入的方法用save做前缀;删除的方法用remove做前缀,修改的方法用update做前缀
1.2 常量定义
1.不允许任何魔法值(未经定义的常量)直接出现在代码中。
2.long或者Long赋初值时,必须使用大写的L,不能使用小写的l,不容易区分。
3.如果一个常量值仅在一个范围内变化,且带有名称之外的延展属性,定义为枚举类。
4.所有相同类型的包装类之间对象值的比较,全部使用equals方法比较。
对于Integer var = ? 在-128-127之间范围内的赋值,Integer对象是在IntegerCache.cache产生,会复用已有的对象,这个区间内的 Integer 值可以直接使用==进行 判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑, 推荐使用 equals 方法进行判断。
5.所有pojo的属性必须使用包装类型,局部变量使用基本数据类型。
1.3集合处理
1.使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。
正例:
Listlist = new ArrayList (2);
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);
2.不要在foreach循环中进行remove/add操作。remove元素请使用Iterator方式,并发操作需要对Iterator进行加锁。
3.每个集合初始化时必须要指定集合初始值的大小,否则频繁扩容非常影响性能。
4.使用entrySet来遍历map,而不是keySet来遍历。entryset一次遍历出所有的key和value。
1.4并发处理
1.所有的线程必须通过线程池来进行创建,不要使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样更能充分明白线程池的运行规则,规避风险。
2.高并发时同步调用应该去考虑锁的性能损耗,能用无锁数据结构,就不要用锁,能锁区块就不要锁方法题,能用对象锁就不要用类锁。
3.并发修改统一记录时,避免更新丢失,需要加锁,要么在应用层枷锁,要么在缓存加锁,要么在数据库层使用乐观锁,用version作为版本更新依据。
- 对于一写多读的变量,可以使用volatile来解决变量同步的问题。
5.ThreadLocal对象无法解决共享对象的更新问题。 建议使用static修饰,这样只分配一块存储空间,所有此类的对象都可以操作这个变量。
1.5 控制语句
1.表达异常分支时,少用if-else方式表达逻辑,可以改用多个if判断。
2.try-catch的范围要尽可能的小。
1.6 注释规约
1.所有抽象类和接口中的方法必须有完整的注释。
二.数据库
1.表达是与否概念的字段,必须使用is_deleted的方式命名,数据类型时unsigned tinyint。
2.主键索引名为pk_字段名;唯一索引名为uk_字段名;普通索引名为idx_字段名(id是index简称)。
3.小数类型为decimal类型,禁止使用float和double。
4.表必备的三个字段:id,gmt_creat,gmt_modified。id的类型为bigint。
5.为了提高查询效率,字段能够适当冗余,但是必须要考虑数据一直,冗余字段应遵循不是频繁修改的字段,不是varchar超长字段,更不能是text字段。
三.工程结构
1. 应用分层
开放接口层:可直接封装 Service 方法暴露成 RPC 接口; 通过 Web 封装成 http 接口; 进行
网关安全控制、 流量控制等。
但是我个人觉得更好的是应该将接口层和实现层分离开,只提供接口出去。具体的业务逻辑在接口实现类中完成。也就是将开放接口层分为两层:开放接口层,开放接口实现类层。
Web 层:主要是对访问控制进行转发,各类基本参数校验,或者不复用的业务简单处理等。
Service 层:相对具体的业务逻辑服务层。
Manager 层:通用业务处理层,它有如下特征:
1) 对第三方平台封装的层,预处理返回结果及转化异常信息;
2) 对 Service 层通用能力的下沉,如缓存方案、 中 间件通用处理;
3) 与 DAO 层交互,对多个 DAO 的组合复用。
DAO 层:数据访问层,与底层 MySQL、 Oracle、 Hbase 进行数据交互。
外部接口或第三方平台:包括其它部门 RPC 开放接口,基础平台,其它公司的 HTTP 接口。
四. 安全规约
1.用户传入的所有参数必须做有效性验证。
2.表单,ajax提交必须执行csrf安全过滤。
3.隶属于用户个人页面或者功能必须进行权限控制校验。
五.总结
在开发手册中不仅定义了代码编写上的规范,其实也给我们避免了很多开发上的小坑!这对于我们的开发无疑是大大有帮助的。
借用书中的一句话:
很多编程方式客观上没有对错之分,一致性很重要,可读性很重要,团队沟通效率很重要。程序员天生需要团队协作,而协作的正能量要放在问题的有效沟通上。个性化应尽量表现在系统架构和算法效率的提升上,而不是在合作规范上进行纠缠不休的讨论、争论,最后没有结论。