记-阿里开发手册规范(JAVA)

从接触java以来,我们一直学习着各种技术和架构。但我们常忽略代码可读性和一些特殊的规范和约定。有很多人尤其是新人写的代码,运行可能没什么问题。但却不忍直视,甚至内涵风险。

业内代码规范可能各有各的理解,这里介绍一下国内的大佬,也是大部分初入职场的同学都会看到阿里Java开发手册。这里只列一些个人觉得较重要的,还是建议搭建看一下完整版。

编程规范

命名规范

  1. 禁止使用拼音和英文混合
    这里还是建议大家使用英文命名,纯拼音也不要用,有助于理解和规范,不会可以百度翻译嘛
  2. 类名使用驼峰,特殊模型相关可以忽略
    正例:UserService / UserDAO / RemotingAdapter
    反例:Userservice / UserDao / TCPUDPDeal
    这里需要注意像TCP/UDP/XML等这种英文缩写建议也使用驼峰而非全大写
  3. 方法名、参数名、变量都统一采用驼峰写法,首字母小写
  4. 常量命名全大写,单次间使用下划线
    如:MAX_CONNECTION_COUNT
  5. 抽象类命名,以Abstract或Base开头;异常类以Exception结尾;测试类以Test结尾。
  6. boolean类型变量,不要以is开头,避免部分序列化框架出错。
    正例:boolean exists
    反例:boolean isExists
  7. 包名统一小写,使用点分割,尽可能点之间都是一个自然语义的英文单次。报名统一使用单数形式。
    如:com.alibaba.open.util
  8. 如果使用设计模式,在类名中体现,有利于阅读者理解
    如:OrderFactory / LoginProxy / ResourceObserver
  9. 类接口中属性和方法不要加任何修饰符
    注:JDK8中接口允许默认实现,有default
  10. 对于Service和DAO类,一定是接口,实现需要以Impl的后缀结尾。区别于接口
    如:CacheService CacheServiceImpl
  11. 枚举类名加上Enum后缀,成员名称全部大写,下划线分割
    枚举就是特殊常量类,所以命名按常量方式;类命名加Enum后缀,如果使用枚举是为了实现单例模式,可以不加Enum后缀

常量定义

  1. 不允许出现魔法值,即:未经定义的常量
    如:String key = “id=” + id;
  2. long和Long初始赋值时,必须使用大写,为了避免和数字1搞混。
    正例:Long a = 2L
    反例:Long a = 2l
  3. 不要使用一个常量类维护所有的常量,尽可能根据功能进行拆分归类,分开维护。便于理解和维护。

格式规范

  1. 大括号使用规定
    1. 大括号内若为空,则简洁写成{},不需换行
    2. 左大括号前不换行,后换行
    3. 右大括号前换行,后如果还有else等代码不换行;如果表示终止必须换行
  2. 左括号与后一个字符之间不出现空格,右括号与前一个字符之间不出现空格
    如:if (flag == 0)
  3. if/for/while/switch/do等保留字与左右括号之间必须加空格
  4. 任何运算符左右必须加空格
  5. 方法参数在定义和传入时,多个参数逗号后必须加空格
    如:method(“a”, “b”, “c”);

oop规范

  1. 不要使用对象访问类的静态变量和静态方法,直接使用类名访问即可,避免增加编译器解析成本
  2. 所有覆写方法,必须加@Override
  3. 对外的接口,原则上避免修改,可以增加新接口,过时接口使用@Deprecated注解
  4. 尽可能避免使用过时方法和类
  5. 对象使用equals时,将常量或确定有值的对象放到前面
    如:“test”.equals(object)
    注:JDK1.7可以使用Objects.equals()方法
  6. 同类型的类比较,使用值比较,即 :equals方法,不要使用 ==
    注:Integer 赋值-128至127之间使用的是常量,即:此时使用==判断也是对的,但避免掉坑,使用equals判断。
  7. 序列化类新增属性时,不要修改serialVersionUID字段,避免兼容反序列化失败。如果确定不兼容,可以修改serialVersionUID字段。
  8. 类中方法顺序规范
    1. 一个类有多个构造方法时或同名方法,将这些方法顺序放在一起
    2. 类内方法定义顺序:公有方法 > 保护方法 > 私有方法 > getter/setter方法
  9. 循环体内字符串拼接,使用StringBuilder的append方法
  10. final可提高程序响应效率,能声明final尽可能声明final

集合处理

  1. 关于hashCode和equals
    1. 只要重写equals,就必须重写hashCode
    2. 因为Set存储不重复对象,所以需要重写hashCode和equals两个方法
    3. 如果对象作为Map的键,就必须重写hashCode和equals
  2. ArrayList的subList方法结果不可转成ArrayList
    注:subList返回的是ArrayList的内部类,并不是ArrayList,而且是ArrayList的一个视图,对SubList所有的操作都会反映到ArrayList原列表上。
  3. 使用集合转数组方法,必须使用集合的toArray(T[] array)
    注:如果传入数组array空间不够大,toArray方法将重新分配内存空间,并返回新的数组地址;如果传入数组array大于实际所需空间,则下标list.size()的数组元素将被至为null,建议入参数组大小与元素个数一致
  4. 使用工具类Arrays.asList()将数组转换成集合时,不能使用修改集合相关方法,如:add/remove/clear都将抛出UnsupportedOperationException异常
    注:asList返回对象是一个Arrays内部类,并没有实现集合的修改方法。该模式为适配器模式,只是转换接口,真实数据还是落在数组上。即:修改数组,list也会被修改
  5. 不在foreach循环使用remove/add操作。remove元素请使用Iterator方式,并发情况需要加锁。
  6. 在JDK7以上,Comparator需要满足自反性,传递性,对称性,不然sort会报错
    注:如果没有考虑相等情况,使用中将可能出现异常。
  7. Map遍历尽可能使用entrySet遍历,而非keySet遍历。因为keySet遍历,会造成两次遍历。

并发处理

  1. 单例模式需要确保线程安全
  2. 线程资源必须通过线程池提供,不允许在应用中自行显示创建线程,避免过多创建线程、减少创建和销毁线程开销
  3. SimpleDateFormat是非线程安全的,一般不要定义为static,建议可以放到ThreadLocal中,与线程绑定
  4. 并发情况,多考虑锁的性能损耗。能不用锁就不用锁;能锁区块,就不要锁整个方法;能锁对象就别锁类
  5. 对多资源加锁,需要保证加锁顺序一致性,否则会造成死锁
  6. 如果访问冲突概率不高,推荐使用乐观锁
  7. 执行定时任务,尽可能使用ScheduledExecutorService,而非Timer
  8. 使用CountDownLatch进行同步转异步,线程推出前,必须调用coutDown,注意catch异常,确保countDown可以执行
  9. 避免Random被多线程使用,虽然是线程安全的,但多线程竞争会导致性能下降
    注:JDK7以后可以使用ThreadLocalRandom,在JDK7之前,可以使用ThreadLocal自行绑定
  10. 使用docker-checked模式做延迟初始化操作时,为了避免指令重排序隐患。可以将目标属性声明成volatile型。如:docker-checked单例模式
  11. HashMap在并发情况下,resize时,会造成死链,导致CPU飙升
  12. ThreadLocal对象建议使用static修饰

控制语句

  1. switch块内,每个case要么通过break/return终止,要么注释说明将执行到哪一个case为止;在一个switch必须包含default,就算是空的
  2. 避免在判断条件中执行复杂的语句,可以将判断结果赋给一个boolean变量,从变量名可以阅读出含义
    正例:if((file.open(fileName, “w”) != null) && (…) || (…))
    反例:boolean existed = (file.open(fileName, “w”) != null) && (…) || (…)
    if(existed)
  3. 循环体内语句尽可能考虑性能,如:对象、变量、获取数据库连接、try-catch操作等,尽可能移至循环体外操作

注释规范

  1. 类、类属性、类方法使用Javadoc规范,/* 内容 /格式,而非 //内容 方式
  2. 所有的抽象方法(包括接口中的方法),必须要用Javadoc注释
  3. 所有类必须添加创建者信息,不要因为是坑就不敢留名
  4. 特殊标记,TODO(待办事项) FIXME(错误标记)

异常日志

异常处理

  1. 不要捕获继承自RuntimeException的运行时异常,这类异常需要自行检查规避,如:IndexOutOfBoundsException / NullPointerException
  2. 异常不要用来做流程控制,条件控制,因为异常处理效率比条件分支低
  3. 尽量不要对大段代码try-catch,尽可能区分异常类型
  4. 不要捕获异常后,什么都不做又抛出;如果不处理,请抛给调用者
  5. 不要在finally执行return,否则不再执行try块中的return语句

日志约定

  1. 不要执行使用日志实现类,而应该使用SLF4J的API接口,使用门面模式,容易做到所有类的日志处理方式统一
  2. 日志文件保存至少15天,因为有些异常具备“周”发生频率
  3. 日志文件命名尽可能划分规范,从日志上就分类,方便维护
  4. 对于trace/debug/info日志,必须加条件输出判断或使用占位符,这样可以避免字符串拼接等资源浪费
  5. 避免日志重复打印,在log4j.xml中需要设置additivity=false
  6. 异常信息必须包括,错误信息和堆栈

MySQL规范

  1. 表达是否的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint(1 是,0 否)
  2. 表名 字段名必须使用小写字符或数字;禁止出现数字开头,禁止两个下划线之间只有数字
  3. 表名不要使用复数名词
  4. 唯一索引使用uk_字段名;普通索引使用inx_字段名
  5. 小数使用decimal,禁止使用float和double
    注:floathe double在存储时,存在进度损失问题
  6. 如果存储字符串长度几乎相等,使用char定长字符串类型
  7. varchar为变长字符串,长度不要超过5000,如果存储长度超过该值,使用text,并独立表出来

索引规范

  1. 业务上具有唯一特性的字段。即使是组合字段,也必须构建唯一索引
  2. 超过三个表禁止join,需要join的字段数据类型必须一致,多表关联查询时,保证被关联的字段需要有索引
    注:即使是双表join也需要注意表索引,SQL性能
  3. 在varchar字段上建索引,必须指定索引长度,没必要对全字段建立索引
    注:一般对字符串类型数据,长度为20的索引,区分度就高达90%以上
  4. 禁止左模糊匹配或全模糊匹配
  5. 利用延迟关联或子查询优化超多分页场景
    注:MySQL并不是跳过offset行,而是取offset+N行,然后放弃offset行

SQL规范

  1. 不要使用count(列名/常量)替代count(*)
    注:count(*)会统计NULL的行,而count(列名)不会
  2. 当一行全为NULL时。count(col)为0,而sum(col)为NULL
  3. 使用ISNULL()来判断是否是NULL值,因为NULL与任何值直接比较都是NULL
  4. 不要在SQL层使用外键和级联,外键在应用层定义就好

ORM规范

  1. 在表查询中,不要使用*作为查询的字段列表,需要哪些字段明确写出来
    注:使用*,会增加查询分析成本,且增减字段容易造成与resultMap不一致
  2. XML配置,使用#{},#param#,不要使用${},防止SQL注入
  3. 不推荐使用ibatis自带的queryForList(statementName,start,size),因为内部实现是取出所有记录,再通过subList取子集
  4. 不允许直接拿HashMap与HashTable作为查询结果集的输出
  5. 不要写一个大而全的更新接口,传入POJO类,不管是否需要更新的字段都进行了update,这样造成很多无需改动的字段进行了修改。容易出错、效率低、增加了binlog存储。

完整文档:阿里巴巴Java开发手册 密码:9qhb
版权声明:本文为博主原创文章,未经博主允许不得转载。转载请标明出处:
https://blog.csdn.net/caohao1210
https://blog.csdn.net/caohao1210/article/details/81041201

猜你喜欢

转载自blog.csdn.net/caohao1210/article/details/81041201