阿里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. 更新数据表记录时必须记录更新时间。

你可能感兴趣的:(Java,java,数据库,mysql)