Java开发规范(一)编程规约

整理阿里的java开发手册

一、命名风格

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

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

  3. 常量命名全部大写,单词间用下划线隔开,力求语义表达完整,不要先名字长
    正例:MAX_STOCK_COUNT
    反例:MAX_COUNT

  4. POJO类中布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列化错误。

  5. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英文单词。包名统一使用单数,但是类名如果有复数含义,类名可以使用复数形式。
    正例:应用工具类包名为com.alibaba.ai.util、类名为MessageUtils。

  6. 接口类中的方法和属性不要加任何修饰符号(public也不要加),保持代码的简洁性,并加上有效的Javadoc注释。

  7. 枚举类名建议带上Enum后缀,枚举成员名称需要全大写,单词间用下划线分隔。

  8. 各层命名规约
    A) Service/DAO层方法命名规约
     1)获取单个对象的方法用get做前缀
     2)获取多个对象的方法用list做前缀,复数形式结尾如:listObjects
     3)获取统计值得方法用count做前缀
     4)插入的方法用save/insert做前缀
     5)删除的方法用remove/delete做前缀
     6)修改的方法用update做前缀
    B)领域模型命名规约
     1)数据对象:xxxDO,xxx为数据表名
     2)数据传输对象:xxxDTO,xxx为业务领域相关的名称
     3)展示对象:xxxVO,xxx一般为网页名称
     4)POJO是DO/DTO/VO/BO的统称,禁止命名成xxxPOJO

二、OOP规约

  1. 接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。

  2. .Object的equals方法容易抛空指针异常,应使用常量或者确定有值的对象来调用equals。
    正例:“test”.equals(object);
    反例:object.equals(“test”);

  3. 所有的相同类型的包装类对象之间值的比较,全部使用equals方法比较。
    说明:对于Integer var = ? ,如果?是-128至127范围内,Integer对象是在IntegerCache.cache产生,会复用已有对象,这个区间内的Integer值可以直接使用==进行比较,但这个区间之外的所有数据,都会在堆上产生,此时用==会有问题,这是个大坑,所以对于包装类对象之间值得比较推荐使用equals方法。

  4. POJO 类必须写 toString 方法。使用 IDE 中的工具: source > generate toString
    时,如果继承了另一个 POJO 类,注意在前面加一下 super . toString 。
    说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString() 方法打印其属性值,便于排查问题。

三、 集合处理

  1. hashCode和equals的处理,遵循如下规则:
    1)只要重新equals,就必须重写hashCode。
    2)因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
    3)如果自定义对象作为Map的key,那么必须重写hashCode和equals。
    说明:String重写了equals和hashCode,所以我们可以使用String作为key使用。

  2. 集合初始化时,指定集合初始值大小。
    说明:HashMap使用HashMap(int initialCapacity)初始化。
    initalCapacity设置为(需要存储的元素个数/负载因子) + 1。负载因子默认为 0.75,如果暂时无法确定初始值大小,设置为16(默认值)

  3. 使用entrySet遍历Map类集合,而不是keySet方式遍历。
    说明:keySet其实是遍历了两次。如果是JDK8,推荐使用Map.forEach方法。

四、并发处理

  1. 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁。
    说明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用RPC方法。

  2. 对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。
    说明:线程一需要对表A、B、C依次全部加锁后才可以进行更新操作,那么线程二的加锁顺序也必须是A、B、C,否则可能出现死锁。

  3. HashMap在容量不够进行resize时由于高并发可能出现死链,导致CPU飙升,在开发过程中可以使用其他数据结构或者加锁来规避此风险。

五、控制语句

  1. 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪个case为止;在一个switch块内,必须包含一个default语句并且放在最后,即使空代码。

  2. 在高并发场景中,避免使用“等于”判断作为中断或退出条件。
    说明:如果并发控制没有处理好,容易产生等值判断被“击穿”而出现死循环的情况,使用大于或者小于的区间判断条件来代替。
    反例:判断剩余奖品数量等于0时,终止发放奖品,但因为并发处理错误导致奖品数量瞬间变成负数,这样的话,活动无法终止。

  3. if-else不得超过3层,超过3层的if-else的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现。

  4. 将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
    正例:

		final boolea existed = (file.open(fileName, "w")!=null&&.....
		if(existed) {
			.....
		}

六、其他

  1. 获取当前毫秒数用System.currentTimeMillis();而不是new Date().getTime();
    说明:如果想获取更加精确的纳秒级时间值,使用System.nanoTime()的方式。在jdk8中,针对统计时间等场景,推荐使用Instant类

  2. 注意Math.random()这个方法返回是double类型,注意取值的范围0 <= x < 1(能够取到零值,注意除零异常),如果想获取整数类型的随机数,不要将x放大10的若干倍然后取整,直接使用Random对象的nextInt或者nextLong方法。

  3. 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。

  4. 及时清理不再使用的代码段或配置信息。
    说明:对于垃圾代码或过时配置,坚决清理干净,避免程序过度臃肿,代码冗余。
    正例:对于暂时被注释掉,后续可能恢复使用的代码片段,在注释代码上方,统一规定使用三个斜杠(///)来说明注释掉代码的理由。

七、Mysql数据库

  1. 表名不使用复数名词。
    说明:表名应该仅仅表示表里面的实体内容,不应该表示实体数量,对应于DO类名也是单数形式,符合表达习惯。

  2. 禁止使用保留字,如desc、range、match、delayed等。

  3. 小数类型为decimal,禁止使用float和double。
    说明:float和double在存储的时候,存在精度损失的问题,很可能在值得比较时,得到不正确的结果。如果存储的数据范围超过decimal的范围,建议将数据拆成整数和小数分开存储。

  4. 如果存储的字符串长度几乎相等,使用char定长字符串类型。

  5. varchar是可变字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出来一张表,用主键来对应,避免影响其他字段索引效率。

  6. 表必备三个字段:id,gmt_create,gmt_modified。
    说明:其中id为主键,类型为bigint unsigned、单表时自增,步长为1。gmt_create,gmt_modified的类型均为datetime类型,前者表示创建时间,或者表示更新时间。

  7. 字段允许适当冗余,以提高查询性能,但必须考虑数据一致 。冗余字段应遵循:
    1)不是频繁修改的字段
    2)不是varchar超长字段,更不能是text字段。
    正例:商品类目名称使用频率高,字段长度短,名称基本一成不变,可在相关联的表中冗余存储类目名称,避免关联查询。

  8. 不存在负数的整型要用unsigned后缀以节省空间,同时可以节约索引存储,提升检索速度。

  9. 利用延迟关联或者子查询优化超多分页场景
    说明:MySQL并不是跳过offset行,而是取offset + N行,然后返回放弃前offset行,返回N行,那当offset特别大的时候,效率就非常的地下,要么控制返回的总页数,要么对超过特点阈值的页数进行SQL改写。
    正例:先快速定位需要获取的id段,然后再关联:

select a.* form 表1 a, (select id from 表1 where 条件 limit 100000, 20) b where a.id = b.id;
  1. 不要使用count(列名)或count(常量)来替代count(*),count(*)是SQL92定义的标准统计行数的语法,跟数据库无关,跟NULL和非NULL无关。
    说明:count(*)会统计值为null的行,而count(列名)不会统计此列为null值的行。

  2. count(distinct col)计算该列除NULL之外的不重复行数,注意count(distinct col1, cols)如果其中一列全为NULL,那么即使另一列有不同的值,也返回为0;

  3. 当某一列的值全是NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()是需注意NPE问题。
    正例:可以使用如下方式来避免sum的NPE问题:

select if(ISNULL(SUM(g)), 0, SUM(g)) from table;
  1. 在代码中写分页查询逻辑时,若count为0应直接返回,避免执行后面的语句。

  2. 不得使用外键与级联,一切外键概念必须在应用层解决。
    说明:外键与级联更新使用于单机低并发,不适合分布式、高并发集群:级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

  3. in操作能避免就避免,若实在避免不了,需要仔细评估in后边的集合元素数量,控制在1000个之内。

  4. iBatis自带的queryForList(String statementName, int start, int size)不推荐使用。
    说明:其实现方式是在数据库取到statementName对应的SQL语句的所有记录,再通过subList取start,size的子集合。

  5. 推荐只更新改动的字段(但在jpa中是全覆盖)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值