个人感觉就是参考的《阿里编程规范手册》...
一、命名
-
JDK统一使用JDK8,因需要支持WAS,编译输出级别需为1.6(代码风格请使用JDK6风格,JDK7以上风格会导致编译不通过)。编译统一使用maven,maven中会有编译需要的一切配置,禁止使用本地配置编译代码。
-
禁止对jdk自带的sun包的调用
-
提交前要进行比较,确认修改,不允许不比较就提交代码。
-
不允许对全代码进行Format,以免无法对比。
-
禁止使用TortoiseSVN(俗称小乌龟)提交代码。
-
【强制】代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
-
【强制】常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
-
【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。
-
【强制】类型与中括号紧挨相连来表示数组。
-
【强制】POJO 类中布尔类型变量都不要加 is 前缀,否则部分框架解析会引起序列化错误。
-
【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使 用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
-
【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。
-
【强制】项目包结构约束,禁止直接在 com.seeyon.下进来代码编写。
-
18.【参考】各层命名规约:
A) Service/DAO 层方法命名规约 :
1) 获取单个对象的方法用 get / select 做前缀, 如:get / getOne / selectOne。
2) 获取多个对象的方法用 list/select 做前缀,复数形式结尾,如:listObjects / selectList。
3) 获取统计值的方法用 count 做前缀。
4) 插入的方法用 save/insert 做前缀。
5) 删除的方法用 remove/delete 做前缀。
6) 修改的方法用 update 做前缀。
7) 检查带参查询, 如:getBeanByName / getNameById
B) 领域模型命名规约 :
1) 数据对象:xxxDO,xxx 即为数据表名。
2) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。
3) 展示对象:xxxVO,xxx 一般为网页名称。
4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。
二、常量定义
-
【强制】在 long 或者 Long 赋值时,数值后使用大写的 L,不能是小写的 l,小写容易跟数字1混淆,造成误解。
-
【推荐】常量的复用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、 包内共享常量、类内共享常量。
1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。
2) 应用内共享常量:放置在一方库中,通常是子模块中的 constant 目录下。
正确示范:
错误示范:易懂变量也要统一定义成应用内共享常量,两位工程师在两个类中分别定义了“YES”的变量:
类 A 中:
public static final String YES = "yes";
类 B 中:
public static final String YES = "y"; // 预期是 true,但实际返回为 false,导致线上问题。 A.YES.equals(B.YES),
3) 子工程内部共享常量:即在当前子工程的 constant 目录下。
4) 包内共享常量:即在当前包下单独的 constant 目录下。
5) 类内共享常量:直接在类内部 private static final 定义。
-
【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。
说明:如果存在名称之外的延伸属性应使用 enum 类型,下面正例中的数字就是延伸信息,表示一年中的第几个季节。 并且枚举中仅提供get方法,不提供set方法
正确示范:
public enum SeasonEnum { SPRING(1), SUMMER(2), AUTUMN(3), WINTER(4); private int seq; SeasonEnum(int seq) { this.seq = seq; } public int getSeq() { return seq; } }
三、代码格式
-
【强制】if/for/while/switch/do 等保留字与括号之间都必须加空格。
-
【强制】任何二目、三目运算符的左右两边都需要加一个空格。
-
【强制】采用 4 个空格缩进,禁止使用 tab 字符。
-
【强制】注释的双斜线与注释内容之间有且仅有一个空格。
-
【强制】单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
1)第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
2)运算符与下文一起换行。
3)方法调用的点符号与下文一起换行。
4)方法调用中的多个参数需要换行时,在逗号后进行。
5)在括号前不要换行,见反例。
StringBuilder sb = new StringBuilder(); // 超过 120 个字符的情况下,换行缩进 4 个空格,点号和方法名称一起换行 sb.append("Jack").append("Ma")... . .append("seeyon")... . .append("seeyon"")... . .append("seeyon"");
四、OOP规约
-
【强制】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析 成本,直接用类名来访问即可。
正确示范:
public class SeeyonStatic { public static final String TEST_STRING = "test_string"; } SeeyonStatic.TEST_STRING
-
【强制】为了防止精度损失,禁止使用构造方法 BigDecimal(double)的方式把 double 值转 化为 BigDecimal 对象。
说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。
如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0.10000000149
正确示范:优先推荐入参为 String 的构造方法,或使用 BigDecimal 的 valueOf 方法,此方法内部其实执行了 Double 的 toString,而 Double 的 toString 按 double 的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal("0.1"); BigDecimal recommend2 = BigDecimal.valueOf(0.1);
-
【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败;如果 完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。 说明:注意 serialVersionUID 不一致会抛出序列化运行时异常。
-
【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
-
【强制】POJO 类必须写 toString 方法。使用 IDE 中的工具:source> generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。
说明:在方法执行抛出异常时,可以直接调用 POJO 的 toString()方法打印其属性值,便于排查问题。
-
【推荐】使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内 容的检查,否则会有抛 IndexOutOfBoundsException 的风险。
说明:
String str = "a,b,c,,"; String[] ary = str.split(","); // 预期大于 3,结果是 3 System.out.println(ary.length);
-
【推荐】 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter 方法。
-
【推荐】final 可以声明类、成员变量、方法、以及本地变量,下列情况使用 final 关键字:
1) 不允许被继承的类,如:String 类。
2) 不允许修改引用的域对象。
3) 不允许被覆写的方法,如:POJO 类的 setter 方法。
4) 不允许运行过程中重新赋值的局部变量。
5) 避免上下文重复使用一个变量,使用 final 可以强制重新定义一个变量,方便更好地进行重构。
-
【推荐】类成员与方法访问控制从严:
1) 如果不允许外部直接通过 new 来创建对象,那么构造方法必须是 private。
2) 工具类不允许有 public 或 default 构造方法。
3) 类非 static 成员变量并且与子类共享,必须是 protected。
4) 类非 static 成员变量并且仅在本类使用,必须是 private。
5) 类 static 成员变量如果仅在本类使用,必须是 private。
6) 若是 static 成员变量,考虑是否为 final。
7) 类成员方法只供类内部调用,必须是 private。
8) 类成员方法只对继承类公开,那么限制为 protected。
说明:任何类、方法、参数、变量,严控访问范围。过于宽泛的访问范围,不利于模块解耦。思考:是一个private 的方法,想删除就删除,可是一个 public 的 service 成员方法或成员变量,删除一下,不 得手心冒点汗吗?变量像自己的小孩,尽量在自己的视线内,变量作用域太大,无限制的到处跑,那么你会担心的。
-
1
-
1
五、集合处理
-
【强制】ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异常,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。 说明:subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 而是 ArrayList 的一个视图,对 于 SubList 子列表的所有操作最终会反映到原列表上。
-
【强制】泛型通配符<?extendsT>来接收返回的数据,此写法的泛型集合不能使用add方 法,而<? super T>不能使用 get 方法,作为接口调用赋值时易出错。
说明:扩展说一下 PECS(Producer Extends Consumer Super)原则:
第一、频繁往外读取内容的,适合 用<? extends T>。
第二、经常往里插入的,适合用<? super T>
六、并发处理
-
特殊说明:并发相关问题,研发人员都需要和直线经理沟通,直线经理不能解决可以和业务线的架构师沟通解决。并发相关的重要原则:能不使用锁解决就不使用锁,能使用乐观锁就不使用悲观锁,用锁就需要业务线的架构review代码并提供对锁使用的评估和锁使用的方案
-
【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用 DateUtils 工具类。
说明:如果是 JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
正确示范:注意线程安全,使用 DateUtils。亦推荐如下处理:
private static final ThreadLocal<DateFormat> df = new ThreadLocal<DateFormat>() { @Override protected DateFormat initialValue() { return new SimpleDateFormat("yyyy-MM-dd"); } };
七、控制语句
-
【强制】表达异常的分支时,少用if-else方式,这种方式可以改写成:
if (condition) { … return obj; } // 接着写 else 的业务逻辑代码;
说明:如果非使用 if()…else if()…else…方式表达逻辑,避免后续代码维护困难,【强制】请勿超过 3 层。
正确示范:超过 3 层的 if-else 的逻辑判断代码可以使用卫语句、策略模式、状态模式等来实现,其中卫语句 即代码逻辑先考虑失败、异常、中断、退出等直接返回的情况,以方法多个出口的方式,解决代码中判断 分支嵌套的问题,这是逆向思维的体现。
示例如下:
public void findBoyfriend(Man man) { if (man.isUgly()) { System.out.println("本姑娘是外貌协会的资深会员"); return; } if (man.isPoor()) { System.out.println("贫贱夫妻百事哀"); return; } if (man.isBadTemper()) { System.out.println("银河有多远,你就给我滚多远"); return; } System.out.println("可以先交往一段时间看看"); }
八、注释规约
-
【参考】谨慎注释掉代码。在上方详细说明,而不是简单地注释掉。如果无用,则删除。
-
【参考】对于注释的要求:第一、能够准确反映设计思想和代码逻辑;第二、能够描述业务 含义,使别的程序员能够迅速了解到代码背后的信息。完全没有注释的大段代码对于阅读者 形同天书,注释是给自己看的,即使隔很长时间,也能清晰理解当时的思路;注释也是给继 任者看的,使其能够快速接替自己的工作。
-
【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描,经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
\1) 待办事宜(TODO):(标记人,标记时间,[预计处理时间]) 表示需要实现,但目前还未实现的功能。这实际上是一个Javadoc 的标签,目前的 Javadoc 还没有实现,但已经被广泛使用。只能应用于类,接口和方法(因为它是一个 Javadoc标签)。
\2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])在注释中用 FIXME 标记某代码是错误的,而且不能工作,需要及时纠正的情况。
九、异常
-
【强制】Java 类库中定义的可以通过预检查方式规避的 RuntimeException 异常不应该通 过 catch 的方式来处理,比如:NullPointerException,IndexOutOfBoundsException 等 等。
说明:无法通过预检查的异常除外,比如,在解析字符串形式的数字时,可能存在数字格式错误,不得不 通过 catch NumberFormatException 来实现。
正确示范:if (obj != null) {...}
错误示范:try { obj.method(); } catch (NullPointerException e)
-
【强制】捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它, 请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理 解的内容。
-
【强制】finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。
说明:如果 JDK7 及以上,可以使用 try-with-resources 方式。
-
【强制】不要在 finally 块中使用 return。
说明:try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存 在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。
正确示范:
错误示范:
private int x = 0; public int checkReturn() { try { // x 等于 1,此处不返回 return ++x; } finally { // 返回的结果是 2 return ++x; } }
-
【强制】在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable 类来进行拦截。
-
【推荐】定义时区分 unchecked / checked 异常,避免直接抛出 new RuntimeException(), 更不允许抛出 Exception 或者 Throwable,应使用有业务含义的自定义异常。推荐业界已定 义过的自定义异常,如:DAOException / ServiceException 等。
-
1
-
1
-
1
十、其它
-
【强制】后台输送给页面的变量必须加$!{var}——中间的感叹号。
-
【强制】注意 Math.random() 这个方法返回是 double 类型,注意取值的范围 0≤x<1(能够 取到零值,注意除零异常),如果想获取整数类型的随机数,不要将 x 放大 10 的若干倍然后 取整,直接使用 Random 对象的 nextInt 或者 nextLong 方法。
-
【强制】获取当前毫秒数 System.currentTimeMillis(); 而不是 new Date().getTime();
说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中,针对统计时 间等场景,推荐使用 Instant 类。
-
表示日期和时间的格式如下所示: new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");