阿里java开发手册泰山版_总结

编程规约

常量定义

  1. 不允许任何预先未定义的常量出现在代码中。
  2. 不要使用一个常量类维护所有的常量,要按常量功能进行归类,分开维护。

OOP规约

  1. 所有的POJO类属性必须使用包装数据类型。
  2. RPC方法的返回值和参数必须使用包装数据类型。

POJO类是最简单最普通的JAVA对象,内在含义是有一些private的参数作为对象的属性,然后针对每一个参数定义get和set方法访问的接口。
【没有从任何类继承、也没有实现任何接口。更没有被其他框架侵入的java对象】
JavaBean是遵循特殊约定的POJO,其所有属性为private,且提供无参数的构造器,这个类是可序列化的,实现serializable接口。

POJO(Plain Ordinary Java Object)与JavaBean的区别:
POJO是比JavaBean更纯净的简单类或接口。其严格遵守简单对象的概念,而JavaBean中往往会封装一些简单逻辑。
POJO主要用于数据临时传递,只能装载数据,作为数据存储载体,不具有业务逻辑处理能力。
JavaBean中可以有其他业务处理的方法。

  1. 定义数据对象DO类时,属性类型要与数据库字段类型相匹配。
  2. 禁止通过使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象。推荐使用入参为String的构造方法或者BigDecimal的valueOf方法。否则会存在精度损失。
  3. 定义POJO类时,不要设定任何默认值。
  4. POJO类必须写toString方法,以便排查问题。
  5. 禁止在 POJO 类中,同时存在对应属性 xxx 的 isXxx()和 getXxx()方法。

日期时间

  1. 日期格式化时,传入pattern中表示年份统一使用小写的y。小写的y表示当天所在的年,大写的Y表示当周所在的年。
new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")
  1. 不允许在程序的任何地方使用 1)java.sql.Date 2)java.sql.Time 3)java.sql.Timestamp

集合处理

  1. 在使用java.util.stream.Collectors类的**toMap()**方法转为Map集合时,一定要使用含有参数类型为BinaryOperator,参数名为mergeFunction的方法,否则会抛出IllegalStateException异常。
  2. 在使用java.util.stream.Collectors类的**toMap()**方法转为Map集合时,当value为nulll时会抛出NPE异常。
  3. 不要在foreach循环里进行元素的remove/add操作
  4. 集合初始化时,指定集合初始值的大小。如new HashMap(int initialCapacity)
    其中 initialCapacity = (需要存储的元素个数/负载因子)+ 1 , 负载因子 = 0.75
    如果无法确定初始值,则默认16

并发处理

  1. 获取单例对象需要保证线程安全,其中的方法也要保证线程安全。
  2. 线程池不允许使用Executors创建,而是通过ThreadPoolExecutor创建,这样可以规避资源耗尽的风险。
  3. 必须回收自定义的ThreadLocal对象,否则可能会导致内存泄漏。
  4. 在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代码块之间没有任何可抛出异常的方法调用,避免加锁成功后,在finally无法解锁。
  5. 在使用尝试机制来获取锁时,进入业务代码块之前,必须先判断当前线程是否持有锁。锁的释放规则与锁的阻塞等待方式一致。
  6. 并发修改同一记录时,为避免更新丢失需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,并用version作为更新依据。

控制语句

  1. 当switch括号内的变量类型为String并且此变量为外部参数时,该变量必须先进行null判断,否则可能会导致NPE。
  2. 三目运算符 condition? 表达式 1 : 表达式 2 中,高度注意表达式 1 和 2 在类型对齐
    时,可能抛出因自动拆箱导致的 NPE 异常。

说明:以下两种场景会触发类型对齐的拆箱操作:
1) 表达式 1 或表达式 2 的值只要有一个是原始类型。
2) 表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型。
反例:
Integer a = 1;
Integer b = 2;
Integer c = null;
Boolean flag = false;
// ab 的结果是 int 类型,那么 c 会强制拆箱成 int 类型,抛出 NPE 异常
Integer result=(flag? a
b : c);

  1. 高并发场景中避免使用等于判断作为中断或退出的条件。
  2. 表达异常的分支时,少用if-else 方式,也少用if-else if - else方式。最好直接用卫语句。
    p.s. 卫语句就是将多个ifelse嵌套的语句转换成多个if语句,这样能将我们从多层嵌套的异常处理中解放出来,而关心真正的业务代码。
  3. 避免采用取反逻辑运算符,不易于理解。
  4. 接口入参保护

某业务系统,提供一个用户批量查询的接口,API 文档上有说最多查多少个,但接口实现上没做任何保护,导致调用方传了一个 1000 的用户 id 数组过来后,查询信息后,内存爆了。
7.需要进行参数校验的场景:
调用频次低的方法。
执行时间开销大。
需要极高的稳定性和可用性的方法。
对外提供的方法。
敏感权限入口。

注释规约

  1. 所有的枚举字段都必须要有注释,说明每个数据项的用途。
  2. 对于暂时被注释掉,后续可能恢复使用的代码,在注释上方,统一规定用三个斜杠说明注释掉代码的理由。

异常日志

错误码

  1. 错误码不体现版本号和错误等级信息。
  2. 全部正常,但不得不填充错误码是返回五个零: 00000
  3. 错误码为5位的字符串类型。分为两个部分:错误来源 + 四位数字编号。
  4. 错误码之外的业务独特信息由errer_message承载,不要让错误码本身涵盖过多的业务属性。

异常处理

  1. RuntimeException异常不应该通过catch的方式处理,如NPE、IndexOutOfBoundsException等。
  2. 异常不要用来做流程控制、条件控制。
  3. 对于非稳定代码的catch尽可能区分异常类型,再做对应的异常处理。
  4. 捕获异常是为了处理它,不要捕获了异常什么都不做而抛弃它。如果不想处理请将异常抛给调用者。最外层的业务使用者必须处理异常,并转化为用户可以理解的内容。
  5. 事务场景中,抛出异常被caache后,如需回滚,一定要手动回滚事务。
  6. finally必须对资源对象、流对象进行关闭,有异常也要做try-catch。
    如果JDK7 及以上,可以使用try-with-resources方式。
    try (Connection conn = new Connection()) {
      conn.sendData();
    }
    catch (Exception e) {
      e.printStackTrace();
    }
  1. 在调用RPC、二方包或动态生成类的相关方法时,捕获异常必须用Throwable类进行拦截。
  2. 防止NPE是程序员的基本素养。

日志规约

  1. 对于trace/debug/info级别的日志,必须进行日志级别的开关判断。
    参数可能会进行字符串拼接计算,以及无谓方法的调用开销。
    if(logger.isDebugEnabled){
        logger.dubug("Current ID is: {} and name is: {}", id, getName())
    }
  1. 日志打印时禁止直接用JSON工具将对象转换成String,如果对象中的get方法被重写,存在抛出异常的情况,则可能因为打印日志而影响正常业务流程的执行。
  2. 记录日志时请思考:这些日志真的有人看吗?这条日志能做什么?能不能给排查问题带来好处?

单元测试

  1. 单元测试中不准用SOUT人肉验证,必须使用assert验证。
  2. 单元测试是可以重复执行的,不能受到外界环境的影响。
  3. 编写单元测试代码遵守BCDE原则,以保证被测试模块的交付质量。
    ⚫ B:Border,边界值测试,包括循环边界、特殊取值、特殊时间点、数据顺序等。
    ⚫ C:Correct,正确的输入,并得到预期的结果。
    ⚫ D:Design,与设计文档相结合,来编写单元测试。
    ⚫ E:Error,强制错误信息输入(如:非法数据、异常流程、业务允许外等),并得到预期的结果。

MySQL数据库

建表规约

  1. 表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint
    (1 表示是,0 表示否)。
  2. 禁用保留字。
  3. 小数类型为decimal,禁用floaat和double,会有精度损失。
  4. varchar是可变长度字符串,不预先分配存储空间,长度不要超过5000,如果存储长度大于此值,定义字段类型为text,独立出一张表,用主键对应,避免影响其他字段的索引效率。
  5. 表必备三字段:id、gmt_creat、gmt_modeified。即id、记录创建时间、记录修改时间。
  6. 单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。

索引规约

  1. 业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。
  2. 超过三个表禁止join。
  3. 利用延迟关联或者子查询优化超多分页场景。
    MySQL并不是跳过offset行,而是取offset+N行,然后返回前放弃前offset行,返回N行。当offset特别大,效率就会非常低下。【主键索引覆盖+关联优化】

SQL语句

  1. 不要使用count(列名)或count(常量)来替代count(*),后者是SQL92定义的标准统计行数的语法。
    p.s. MySQL数据库有MyISAM和InnoDB两种执行引擎:
  • 前者不支持事务且为表级锁,它对count(*)的优化是单独记录表的总行数。
  • 后者为行级锁,对于count(*)和count(1)它会选择最小的非聚簇索引来扫表。优化的前提是不含where和聚集条件。
  1. 使用ISNULL()来判断是否为NULL值
  2. 代码中分页查询逻辑时,若count为0应直接返回,避免执行后面的分页语句。
  3. 不得使用外键和级联,一切外键概念必须在应用层解决。
  4. 禁用存储过程,难以调试和拓展,没有移植性。
  5. 数据订正时,要先select,避免出现误删除。
  6. 对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(t1,t2,…)或表明进行限定。
  7. 更新数据表记录时必须记录更新时间。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
前言 《Java 开发手册》是阿里巴巴集团技术团队的集体智慧结晶和经验总结,经历了多次大规模一线实战的检验及不断完善,公开到业界后,众多社区开发者踊跃参与,共同打磨完善,系统化地整理成册,当前的本是泰山。现代软件行业的高速发展对开发者的综合素质要求越来越高,因为不仅是编程知识点,其它维度的知识点也会影响到软件的最终交付质量。比如:数据库的表结构和索引设计缺陷可能带来软件上的架构缺陷或性能风险;工程结构混乱导致后续维护艰难;没有鉴权的漏洞代码易被黑客攻击等等。所以本手册Java 开发者为中心视角,划分为编程规约、异常日志、单元测试、安全规约、MySQL 数据库、工程结构、设计规约七个维度,再根据内容特征,细分成若干二级子目录。另外,依据约束力强弱及故障敏感性,规约依次分为强制、推荐、参考三大类。在延伸信息中,“说明”对规约做了适当扩展和解释;“正例”提倡什么样的编码和实现方式;“反例”说明需要提防的雷区,以及真实的错误案例。 手册的愿景是码出高效,码出质量。现代软件架构的复杂性需要协同开发完成,如何高效地协同呢?无规矩不成方圆,无规范难以协同,比如,制订交通法规表面上是要限制行车权,实际上是保障公众的人身安全,试想如果没有限速,没有红绿灯,谁还敢上路行驶?对软件来说,适当的规范和标准绝不是消灭代码内容的创造性、优雅性,而是限制过度个性化,以一种普遍认可的统一方式一起做事,提升协作效率,降低沟通成本。代码的字里行间流淌的是软件系统的血液,质量的提升是尽可能少踩坑,杜绝踩重复的坑,切实提升系统稳定性,码出质量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值