有感于公司代码比较乱,完全没有规范,而我则受益于实习的时候的老大zeak的严格要求,看到这种情况表示有点难以接受,所以和老大讨论后,基于阿里的规范经过删减写了这么一个标准,今天发出来,不仅供自己时时对照,也供大家参考,最后感谢一下阿里出这份标准。

一、 编程规约

(一)命令风格

整体要求,见名知义,英文为主,类名一般是名称或者形容词,方法名为动词

具体要求:

  1. 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。除非特殊情况最好不要用美元符号(大家猜猜为啥?)
  2. 类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:BO / DTO / VO
  3. 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格,必须遵从 驼峰形式
  4. 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长
  5. 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾,枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开
  6. 中括号是数组类型的一部分,数组定义如下:String[] args
  7. POJO 类中布尔类型的变量,都不要加 is
  8. 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式
  9. 杜绝完全不规范的缩写,避免望文不知义
  10. 如果使用到了设计模式,建议在类名中体现出具体模式
  11. 接口类中的方法和属性不要加任何修饰符号(public 也不要加),并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是 与接口方法相关,并且是整个应用的基础常量
  12. 各层命名规约:
  1. 获取单个对象的方法用get或者find做前缀
  2. 获取多个对象的方法用list或者query做前缀。
  3. 获取统计值的方法用count做前缀。
  4. 插入的方法用save(推荐)或insert做前缀。
  5. 删除的方法用remove(推荐)或delete做前缀。
  6. 修改的方法用update做前缀。

(二) 常量定义

  1. 不允许任何魔法值直接出现在代码中
  2. long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l
  3. 如果变量值仅在一个范围内变化,且带有名称之外的延伸属性,定义为枚举类
  4. 常量类大写,各个单词之前下划线分开

(三) 代码格式

  1. 大括号的使用约定:
  1. 左大括号前不换行。
  2. 左大括号后换行。
  3. 右大括号前换行。
  4. 右大括号后还有else等代码则不换行;表示终止的右大括号后必须换行。
  1. 左小括号和字符之间不出现空格;同样,右小括号和字符之间也不出现空格
  2. if/for/while/switch/do 等保留字与括号之间都必须加空格
  3. 任何二目、三目运算符的左右两边都需要加一个空格
  4. 缩进采用 4 个空格,禁止使用 tab 字符(根据今年stackoverflow公布数据,空格党的平均工资略高于tab党,大家猜猜为啥?)
  5. 单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
  1. 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进。
  2. 运算符与下文一起换行。
  3. 方法调用的点符号与下文一起换行。
  4. 在多个参数超长,在逗号后换行。
  5. 在括号前不要换行。
  1. 方法参数在定义和传入时,多个参数逗号后边必须加空格
  2. 没有必要增加若干空格来使某一行的字符与上一行对应位置的字符对齐
  3. 方法体内的执行语句组、变量的定义语句组、不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行,没有必要插入多个空行进行隔开
  4. IDE的text file encoding设置为UTF-8; IDE中文件的换行符使用Unix格式, 不要使用 windows 格式。

关于代码格式的设置,eclipse用户可以参考我的GitHub:eclipse设置,按照这个设置,当你保存的时候eclipse会自动帮你formatter你改动过的代码。

(四) OOP 规约

  1. 所有的覆写方法,必须加@Override 注解。
  2. Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals
  3. 所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较
  4. 关于基本数据类型与包装数据类型的使用标准如下:
  1. 所有的POJO类属性必须使用包装数据类型。
  2. RPC方法的返回值和参数必须使用包装数据类型。
  3. 所有的局部变量使用基本数据类型
  1. 定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值
  2. 循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展
  3. 任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦
  4. 实体类都要实现序列化接口
  5. 实体类应该重写toString方法
  6. 实体类都必须实现序列化接口,并且添加序列号
  7. 类成员与方法访问控制从严:
  1. 如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
  2. 工具类不允许有public或default构造方法。
  3. 类非static成员变量并且与子类共享,必须是protected。
  4. 类非static成员变量并且仅在本类使用,必须是private。
  5. 类static成员变量如果仅在本类使用,必须是private。
  6. 若是static成员变量,必须考虑是否为final。
  7. 类成员方法只供类内部调用,必须是private。
  8. 类成员方法只对继承类公开,那么限制为protected

(五) 并发处理

  1. 获取单例对象需要保证线程安全,其中的方法也要保证线程安全
  2. 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯
  3. 线程资源必须通过线程池提供
  4. 高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能 锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁
  5. ThreadLocal 无法解决共享对象的更新问题,ThreadLocal 对象建议使用 static
    修饰

(六) 控制语句

  1. 在一个 switch 块内,每个 case 要么通过 break/return 等来终止,要么注释说明程 序将继续执行到哪一个 case 为止;在一个 switch 块内,都必须包含一个 default 语句并且 放在最后,即使它什么代码也没有
  2. 在 if/else/for/while/do 语句中必须使用大括号。即使只有一行代码
  3. 除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复 杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性
  4. 循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、
    获取数据库连接,进行不必要的 try-catch 操作

(七) 注释规约

  1. 类、类属性、类方法的注释必须使用 Javadoc 规范
  2. 所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释、除了返回值、参数、
    异常说明外,还必须指出该方法做什么事情,实现什么功能
  3. 对于注释的要求:第一、能够准确反应设计思想和代码逻辑;第二、能够描述业务含 义,使别的程序员能够迅速了解到代码背后的信息
  4. 好的命名、代码结构是自解释的,注释力求精简准确、表达到位。避免出现注释的
    一个极端:过多过滥的注释,代码的逻辑一旦修改,修改注释是相当大的负担
  5. 特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记

(八) 其它

  1. 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度
  2. 获取当前毫秒数 System.currentTimeMillis()
  3. 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存

二、异常日志

  1. Java 类库中定义的一类 RuntimeException 可以通过预先检查进行规避,而不应该 通过catch 来处理
  2. 对大段代码进行 try-catch,这是不负责任的表现。catch 时请分清稳定代码和非稳 定代码,稳定代码指的是无论如何不会出错的代码。对于非稳定代码的 catch 尽可能进行区分 异常类型,再做对应的异常处理
  3. 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回 滚事务
  4. 不能在 finally 块中使用 return
  5. 定义时区分unchecked/checked 异常,避免直接抛出newRuntimeException(), 更不允许抛出 Exception 或者 Throwable
  6. 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理解的内容
  7. 方法的返回值可以为 null,不强制返回空集合,或者空对象等,防止 NPE,是程序员的基本修养,注意 NPE 产生的场景
  8. 打印错误日志
  1. 完整打印堆栈信息,即e对象,而不是e.getMessage()
  2. 使用{}占位的时候,{}的个数应该少一个,否则最后一个只能打印e的名字,而没有堆栈信息
  3. 每一条日志都应该取一个唯一且有意义的名字,有利于快速定位错误

三、安全规约

  1. 用户请求传入的任何参数必须做有效性验证,又是pageNo和pageSize,防止数值过大,出现oom
  2. 隶属于用户个人的页面或者功能必须进行权限控制校验
  3. 用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入, 禁止字符串拼接 SQL 访问数据库
  4. sql.xml 配置参数使用:#{},#param# 不要使用${}

标准发出后,有部分同事提出了一些问题,借用我是实习的时候老大的一句话就是,我是以德服人的,所以这是我的回答(如果你还有疑问,欢迎留言交流):

  1. 为什么不用实体类属性不用is?
    答:如果前端模板引擎用velocity等,is会解析不出来
  2. sql.xml 配置参数使用:#{},#param# 不要使用${}这个我貌似碰到过一次limit后面#解析不对的问题
    答:应该不会,${}会有SQL注入问题
  3. 这个跟阿里的那个规范有改动吗
    答:几乎没有,我删去一些知识性的东西,更注重代码的规范,更通用性一些,不仅适用于Java,像.net、C#等也差不多可以直接用
  4. 不是一般不建议用get开头的方法,反射的时候有时候会有问题
    答:一般情况下,并没有什么问题,最起码我目前还没有遇到过,也可以用find打头。
  5. 更习惯用GetXxxxList()和GetXxxCount()
    答:get、query、count打头已经表意了,没必要再加List等
  6. 为什么这么规定方法打头?
    答:规定方法的打头,是因为我之前做的项目,数据库读写分离,是根据方法打头的单词做的,如果方法打头的单词比较确定,将来业务量大了,需要数据库读写分离可以直接切过去
  7. xxxBy这样的不好用,如果参数特别多。。发现后面不知道怎么写,一个参数的还好
    答:多个参数,我以前习惯写byCriteria或者byExample
  8. DTO、VO有些需要有默认值,比如某些布尔类型默认false 。 page对象默认page和pageSize
    答:这个也是我个人的习惯,实体类和和实体类相关的东西,比如vo、dto之类的,都只有get、set、toString方法,如果有默认值,别人直接使用不一定知道,会和预期结果不一致,增加排查问题的难度。还有vo、dto之类的我都会继承相应的实体类,这样的话属性一定是一样的,我们有些系统里面不是这么做的,导致属性值不一样,转换的时候copy不过去,催收系统已经遇到过这种问题
  9. 要不要出一个公用的util,各个系统 有好多个stringutils和dateutils 而且都不是很全
    答:可以,但其实stringutils其实很多时候apache的已经够用了,apache的commons包要善于应用,当然也可以定义公司自己的commons包,但最好是扩展
  10. 什么是魔法值?
    答:魔法数字就是代码里面,出现1,2,3,4等等这类数,像变魔法一样,不知道具体表示什么
  11. 不用魔法值,意思是统一用枚举类对吧?
    答:不是枚举,枚举适合于固定的值,例如一周就7天,一年就12个月。我说的这种是常量,定义常量类就好了
  12. 为什么不让用tab?
    答:没有为什么,这标准是我置顶的,而我不喜欢tab。(其实据说不同的系统下tab的长度会不一样,不一定都是4个空格,代码会乱掉)