阿里巴巴Java开发手册学习记录

阿里巴巴Java开发手册学习记录

一、编程规约

1.命名风格

  • 严禁使用英文 + 拼音混合使用

  • 类名应所有单词的首字母大写,除了(UserDO,XxxDTO, XxxPo等)

  • 常量的命名应该是大写 + 单词间用下划线连接

  • 抽象类的应以Abstract/Base开头

  • POJO类中的布尔变量,都不要以is前缀,否则某些框架解析会引起序列化错误, 。 (之前看一个文章就是因为XxxDTO方法的命名为isXxxXxx(),导致方法被错误的序列化,造成了线上事故,文章:https://mp.weixin.qq.com/s/994BAkKPEeBz_gs_6LN2DQ)

  • 如果模块、接口、类、方法使用了设计模式,应体现在命名中

  • 领域模型命名规约

    • 数据对象:xxxDO, xxx为数据表名
  • 数据传输对象:xxxDTO, xxx为业务领域相关

  • 展示对象,xxxVO,xxx为网页名称

  • POJO是DO/DTO/BO/VO的统称,禁止使用xxxPOJO

  • 如果变量值仅在一个固定范围内变化用enum声明

2.代码格式

  • 单行字符限制不超过120各,超出要换行:
    • 第二行相对于第一行缩进4个空格
    • 运算符与下文一起换行(例如:“.”)
    • 方法调用中多个参数,在逗号后换行
  • 可以插入一个空行将不同的逻辑、语义、谈业务分隔开,提高可读性。

3.OOP规约

  • 所有重写父类的方法都需要加@Override,可以判断是否重写成功
  • 接口过时必须加@Deprecated,并说明新接口的位置。
  • 不使用过时的类和方法
  • 在使用equals时,应使用常量或者确定有值的对象的equals的方法
  • 所有的POJO类属性必须使用包装类型,RPC方法的返回值和参数必须使用包装类型
  • 如果完全不兼容升级,避免反序列化混乱,要修改serialVersionUID
  • 构造方法中不加任何业务逻辑
  • 禁止在POJO类中同时存在一个属性的isXxx()和getXxx()方法
    • 因为在序列化时,不确定优先调用哪个方法,对于is开头的枚举型变量,会在序列化调用isXxx的时候会把属性的is吃掉,导致序列化出错。(案例:https://zhuanlan.zhihu.com/p/265869701)
    • 解决方案:1.遵循开发规范,不要用is开头的变量。2.使用JSONField(name = “anotherName”)来定制属性名。3.可以手动修改getter和setter。
  • 类内方法定义顺序:公有方法>私有>getter/setter方法
  • 对象的clone方法默认时浅拷贝,若想要实现深拷贝,需要重写clone方法。
  • 工具类不允许有public和default构造方法

4.集合处理

  • 只要重写equals,就必须重写hashcode
  • 使用set存储自定义对象时,自定义对象需要重写equals、hashcode 方法
  • 在使用sublist方法时,对原集合的增加或删除,均会导致子列表遍历、增加、删除产生并发修改异常。
  • 在进行list的toArray(size)方法转数组时,应该传入list.size()作为参数这样返回的就是当前类型的数组,如果直接使用无参toArray方法时,返回值是Object类型的数组。
  • Arrays.asList()把数组转换成集合时,不可以使用add等修改方法,否则抛异常。
  • PECS(Producer Extends Consumer Super)原则:频繁往外读取内容的,适合用<? extends T>。经常往里插入的,适合用<? super T>。
  • 不用在foreach里进行元素的remove/add操作,remove可以使用iterator方式,如果并发操作需要对Iterator对象加锁。(这里虽说foreach本质也是迭代器实现,但是反编译以后会发现最后删除时是通过list直接remove,而迭代器删除的是通过迭代器的remove方法删除的

5.并发处理

  • 获取单例对象时,需要保证线程安全
  • 应该使用ThreadPoolExecutor创建线程池
  • SimpleDateFormat线程不安全,如果定义为static,必须加锁,也可以使用DateUtils
  • 如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 SimpleDateFormat
  • 对多个资源(表、对象)同时加锁时,应该保持加锁顺序的一致性,避免死锁。
  • 并发修改同一记录时,避免更新丢失,可以在应用层、缓存加锁,或者数据库层使用乐观锁。(如果每次访问冲突概率小于20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3 次。)
  • 避免多个线程使用一个Random(Random或Math.random),会导致性能下降,可以使用ThreadLocalRandom
  • 在多线程场景下计数,volatile无法应对多写的场景,一般我们会选择AtomicInteger,但是在竞争比较激烈的场景下,可以使用LongAdder性能更好(空间换时间,内部将数组分段计数最后求和,但是只使用计数场景下)。
  • ThreadLocal 建议使用static修饰,只需要创建一次,一个线程内的所有对象都已操作这个变量。

6.控制语句

  • 高并发场景中,避免使用等于作为中断或者退出的条件,如果并发条件没有处理好,会出现等值“击穿”的情况,所以应该使用大于或者小于
  • 避免在if条件判断中使用复杂的方法,(getXxx/isXxx除外)
  • 循环体内要考虑性能,所以一些不必要的操作应该放到循环体外进行

7.注释规约

  • 注释的主要作用:能精确反应设计思想和代码逻辑、描述业务含义。
  • 代码逻辑修改的同时,注释也要进行相应的修改,尤其是参数、返回值、异常、核心逻辑
  • 所有的枚举类型的字段必须要有注释

8.异常处理

  • catch异常时,对于非稳定代码来说catch尽量区分异常类型,再做对应的异常处理,不要对一大段代码做try-catch处理
  • 捕获了异常就应该处理它
  • 应该严格避免NPE

9.日志规约

  • 应用中不可直接使用日志系统(Log4j、Logback)中的Api,而应该依赖日志框架SLF5J中的Api。
  • 应用中的扩展日志命名方式:appName_logType_logName.log
  • 对trace/debug/info级别的日志输出,应该使用条件输出或者使用占位符(slf4j支持占位符)。
  • 生产环境禁止输出debug级别日志,有选择地输出info日志,写日志输出语句时思考:有人看吗?看到这条日志能做什么?能不能给排查问题带来好处?
  • 可以使用warn日志级别来记录用户输出参数错误。

10.单元测试

  • 单元测试应该遵循AIR原则(自动化,独立性、可重复性)
  • 单元测试的粒度应该足够小,有助于定位问题,一般是方法级
  • 核心业务、逻辑的增量代码应被单元测试覆盖到
  • 对于不可测的代码建议做重构来使代码变得可测。

11.安全规约

  • 和用户相关的功能必须做权限校验
  • 对于用户的敏感信息需要进行脱敏
  • 用户请求传入的任何参数必须做有效性验证
    • page size 过大导致内存溢出
    • 恶意 order by 导致数据库慢查询
    • 任意重定向
    • SQL 注入
    • 反序列化注入
    • 正则输入源串拒绝服务 ReDoS(当我们用正则验证客户端输入时,用可能被特殊字符串攻击)
  • 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重发机制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资源浪费。

11.索引规约

  • 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引
  • 在varchar字段上建立索引时,必须指定索引长度,没必要对全字段建立索引(可以使用 count(distinct left(列名, 索引长度))/count(*)的区分度 来确定)
  • 页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决
  • MySQL做分页查询MySQL 并不是跳过 offset 行,而是取 offset+N行,然后返回放弃前offset行,返回N行
  • 不要使用 count(列名)或 count(常量)来替代count(*),count(*)是SQL92定义的标准统计行数的语法,跟数据库无关,count(*)会统计值为NULL的行,而count(列名)不会统计此列为NULL值的行
  • count(distinct col) 计算该列除NULL之外的不重复行数,不会计算为NULL行
  • 当某一列的值全是NULL时,count(col)为0,但sum(col)为NULL,因此使用sum()时需注意NPE问题
  • 不得使用外键与级联,与外键相关的逻辑必须在应用层解决
  • 禁止使用存储过程

持续更新…

猜你喜欢

转载自blog.csdn.net/qq_50876039/article/details/132267076