- 参考相关文档及总结
- 命名风格代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。 反例:name / __name / $name / name / name$ / name__
- 类名使用 UpperCamelCase 风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID 等。 正例:ForceCode / UserDO / HtmlDTO / XmlService / TcpUdpDeal / TaPromotion 反例:forcecode / UserDo / HTMLDto / XMLService / TCPUDPDeal / TAPromotion
- 方法名、参数名、成员变量、局部变量都统一使用 lowerCamelCase 风格。 正例: localValue / getHttpMessage() / inputUserId
- 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。 正例:MAX_STOCK_COUNT / CACHE_EXPIRED_TIME 反例:MAX_COUNT / EXPIRED_TIME
- 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类 命名以它要测试的类的名称开始,以 Test 结尾。
- 类型与中括号紧挨相连来表示数组。 正例:定义整形数组 int[] arrayDemo。 反例:在 main 参数中,使用 String args[]来定义。
- 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。 正例:应用工具类包名为 com.xjgx.util、类名为 MessageUtils
- 规范缩写,望文知义。 反例:AbstractClass“缩写”成 AbsClass;condition“缩写”成 condi;Function 缩写”成 Fu,此类 随意缩写严重降低了代码的可阅读性。
- 包及各层命名规约
包命名:[基本包].[项目名].[模块名].[子模块名]...
如:com.xjgx.his.inpatient.dao,com.xjgx.his.inpatient.service,com.xjgx.his.inpatient.controller,com.xjgx.his.utils。 · Service/DAO 类名规约1)DAO层:jpa以Repository为后缀XxxRepository, XxxMapper,如:UsersRepository; mybatis以Mapper为后缀XxxMapper,如:UsersMapper; 其中Xxx为实体名称。2)Service层:以Service为后缀XxxxService,如:UsersService。其中Xxx为实体名称。对应的实现类在此基础上加:Impl为后缀,如:UsersServiceImpl。 · Service/DAO 层方法命名规约 1) 获取单个对象的方法用 get 做前缀。 2) 获取多个对象的方法用 list 做前缀,复数结尾,如:listUsers。 3) 获取统计值的方法用 count 做前缀。 4) 插入的方法用 save/insert 做前缀。 5) 删除的方法用 remove/delete 做前缀。 6) 修改的方法用 update 做前缀。· 领域模型命名规约 1) 数据传输对象:xxxDTO,xxx 为业务领域相关的名称。 2) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。 - 在 long 或者 Long 赋值时,数值后使用大写字母 L,不能是小写字母 l,小写容易跟 数字混淆,造成误解。 说明:Long a = 2l; 写的是数字的 21,还是 Long 型的 2?
- 如果是大括号内为空,则简洁地写成{}即可,大括号中间无需换行和空格;如果是非 空代码块则: 1) 左大括号前不换行。 2) 左大括号后换行。 3) 右大括号前换行。 4) 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必须换行。
- if/for/while/switch/do 等保留字与括号之间都必须加空格。
- 注释的双斜线与注释内容之间有且仅有一个空格。
- 在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开。 正例: double first = 3.2d; int second = (int)first + 2;
- 方法参数在定义和传入时,多个参数逗号后面必须加空格。 正例:下例中实参的 args1,后边必须要有一个空格。 method(args1, args2, args3);
- 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成 本,直接用类名来访问即可。
- 所有的覆写方法,必须加@ Override 注解。 说明:getObject()与 get0bject()的问题。一个是字母的 O,一个是数字的 0,加@Override 可以准确判 断是否覆盖成功。另外,如果在抽象类中对方法签名进行修改,其实现类会马上编译报错。
- 不能使用过时的类或方法。 说明:java.net.URLDecoder 中的方法 decode(String encodeStr) 这个方法已经过时,应该使用双参数 decode(String source, String encode)。
- Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals 。 正例:"test".equals(object); 反例:object.equals("test"); 说明:推荐使用 JDK7 引入的工具类 java.util.Objects#equals(Object a, Object b)
- BigDecimal 的等值比较应使用 compareTo()方法,而不是 equals()方法。 说明:equals()方法会比较值和精度 (1.0 与 1.00 返回结果为 false) ,而 compareTo()则会忽略精度。
- 禁止使用构造方法 BigDecimal(double) 的方式把 double 值转化为 BigDecimal 对象。new BigDecimal(0.1F) // 输出:0.100000001490116119384765625new BigDecimal("0.1") // 输出:0.1
- 实体类中的基本数据类型都必须使用其包装类避免出现NPE
- POJO 类必须写 toString 方法。使用 IDE 中的工具:source> generate toString 时,如果继承了另一个 POJO 类,注意在前面加一下 super.toString。
- 循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
反例:String str = "s" ;for (int i = 0; i < 10; i++) {
str += i ;
}
Code:
5: goto 30
8: new #18 // class java/lang/StringBuilder
11: dup 12: aload_1
13: invokestatic #20 // Method java/lang/String.valueOf:(Ljava/lang/Object;)Ljava/lang/String; 16: invokespecial #26 // Method java/lang/StringBuilder."":(Ljava/lang/String;)V
19: iload_2
20: invokevirtual #29 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder; 23: invokevirtual #33 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
26: astore_1
27: iinc 2, 1
30: iload_2 - 获取当前毫秒数:System.currentTimeMillis(); 而不是 new Date().getTime()。 说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime 的方式。在 JDK8 中,针对统计时间 等场景,推荐使用 Instant 类。
- 判断所有集合内部的元素是否为空,使用 isEmpty()方法,而不是 size()==0 的方式。 说明:在某些集合中,前者的时间复杂度为 O(1),而且可读性更好。
- ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异 常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList 。 说明:subList()返回的是 ArrayList 的内部类 SubList,并不是 ArrayList 本身。
- 使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一 致、长度为 0 的空数组。
- 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。线程池的创建不允许使用Executors,必须通过ThreadPoolExecutor,并且要给线程池创建一个有意义的名称。示例:ThreadPoolExecutor tableThreadPool = new ThreadPoolExecutor(5, 5, 60, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100), new DqcThreadFactory("dqctable"), new ThreadPoolExecutor.AbortPolicy()) ;private class DqcThreadFactory implements ThreadFactory {private ThreadGroup group = new ThreadGroup("DQCGroup") ;private final AtomicInteger threadNumber = new AtomicInteger(1);private String cate = "default" ;private String threadPrefix = "-thread-pool-current-"; public DqcThreadFactory(String cate) {this.cate = cate ;
}@Overridepublic Thread newThread(Runnable r) {
Thread t = new Thread(group, r, cate + threadPrefix + threadNumber.getAndIncrement()) ;
t.setUncaughtExceptionHandler((currentThread, e) -> {
logger.error("发生错误, 当前线程:{}, 异常信息:{}", currentThread.getName(), e.getCause()) ;
});return t ;
}
} - SimpleDateFormat 是线程不安全的类,禁止定义为成员变量或类变量。在JDK8中可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替 SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。示例:
- 必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用, 如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。 尽量在代理中使用 try-finally 块进行回收。示例:threadLocal.set(xxxx) ;try {// todo...
} finally {
threadLocal.remove() ;
} - 在 if / else / for / while / do 语句中必须使用大括号。反例:if (condition) statement;
- 类、类属性、类方法的注释必须使用 Javadoc 规范,使用/*内容/格式,不得使用// xxx 方式
- 所有的抽象方法 ( 包括接口中的方法 ) 必须要用 Javadoc 注释、除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。
- 方法内部单行注释,在被注释语句上方另起一行,使用//注释。方法内部多行注释使用/* */注释,注意与代码对齐。
- 避免用 Apache Beanutils 进行属性的 copy使用getter,setter。
示例:Users user = new Users() ;
user.setId(1L) ;
user.setCode("S001") ;
user.setEmail("3@qq.com") ;
user.setName("N") ;
user.setQq("11111") ;
user.setTable("CTO") ;
user.setAddress("xj") ;
long start = System.currentTimeMillis() ;
Users target = new Users() ;
BeanUtils.copyProperties(target, user) ;
System.out.println("Apache BeanUtils: " + (System.currentTimeMillis() - start) + "ms") ;
start = System.currentTimeMillis() ;
org.springframework.beans.BeanUtils.copyProperties(target, user) ;
System.out.println("Spring BeanUtils: " + (System.currentTimeMillis() - start) + "ms") ;
start = System.currentTimeMillis() ;
BeanCopier.create(Users.class, Users.class, false).copy(user, target, null) ;
System.out.println("Cglib BeanCopier: " + (System.currentTimeMillis() - start) + "ms") ;
start = System.currentTimeMillis() ;
target.setId(user.getId()) ;
target.setAddress(user.getAddress()) ;
target.setCode(user.getCode()) ;
target.setEmail(user.getEmail()) ;
target.setName(user.getName()) ;
target.setQq(user.getQq()) ;
target.setTable(user.getTable()) ;
System.out.println("Getter Setter: " + (System.currentTimeMillis() - start) + "ms") ;
输出结果:
Apache BeanUtils: 75ms
Spring BeanUtils: 98ms
Cglib BeanCopier: 61ms
Getter Setter: 0ms - 在单例类中禁止定义成员变量如:在Controller,Service中如需要使用同步控制
- 异常捕获后不要用来做流程控制,条件控制。 异常设计的初衷是解决程序运行中的各种意外情况,且异常的处理效率比条件判断方式要低很多。
- finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。java7后,也可使用 try-with-resources
- 应用中不可直接使用日志系统 (Log 4 j 、 Logback) 中的 API ,而应依赖使用日志框架SLF4J 中的 API ,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
- 在日志输出时,字符串变量之间的拼接使用占位符的方式。
示例:logger.info("接受到name:{}, sex:{}", name, sex) ; - 生产环境禁止直接使用 System.out 或 System.err 输出日志或使用e.printStackTrace()打印异常堆栈。
- 项目中禁止使用lombok
- 减少对变量的重复计算
反例:for(int i=0; i < list.size(); i++)
正例:for(int i=0, leng = list.size(); i < leng; i++) - 能使用位移计算的一律使用位移示例:int middle = n / 4
int middle = n >> 2 // 推荐 - 访问量比较大的业务接口合理使用异步方式如:Controller 返回DeferredResult和Callbale。快速将应用服务器线程(tomcat)释放,能够处理更多的请求。
- 尽量使用基本数据类型,避免自动装箱和拆箱
示例:long start = System.currentTimeMillis() ;
Integer sum = 0 ;for (int i = 0; i < 100000; i++) {
sum += i ;
}
System.out.println((System.currentTimeMillis() - start) + "ms") ;
平均输出:5ms
将Integer sum = 0 ;改为: int sum = 0;
平均输出:1ms
当使用包装类型时,相当于执行了如下操作:
sum += i; ====> sum = Integer.valueOf(Integer.intValue(sum) + i) ;
反编译:
12: goto 29
15: aload_3
16: invokevirtual #28 // Method java/lang/Integer.intValue:()I
19: iload 4
21: iadd
22: invokestatic #22 // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
25: astore_3
26: iinc 4, 1 - 尽量不在循环体外定义变量,致使作用域放大。老版本的jdk中,建议“尽量不要在循环体内定义变量”,但在新版本的jdk中已经做了优化。反例:
Users user ;
for (int i = 0; i < 100; i++) {
user = new Users() ;
}
正例:
for (int i = 0; i < 100; i++) {
Users user = new Users() ;
} - 禁止以子类的形式初始化集合类数据创建子类就多了一份类加载。
反例:Map<String, String> datas = new HashMap<String, String>() {private static final long serialVersionUID = 1L;
{
put("1", "1") ;
}
} ;
正例:private static Map<String, String> datas = new HashMap<>() ;static {
datas.put("1", "1") ;
} - 方法参数及返回值尽量使用基本数据类型方法参数如果是包装类那么在使用前你的判断是否为null。返回值如果是包装类型可能出现NPE。
反例:public static Double calc() {return null ;
}
double result = calc() ; - 禁止使用""+转换字符串
反例:int i = 10 ;
String str = i + "" ;正例:int i = 10 ;
String str = String.valueOf(i) ;
示例:for (int i = 0;i < 1_000_000; i++) {// String res = String.valueOf(i) ; // 平均执行55ms// String res = i + "" ; // 平均执行95ms
} - 集合对象中禁止先判断是否存在再进行获取先判断是否存在,在进行元素获取。这其中就有两次元素的查找。
反例:
Map<String, String> cache = new HashMap<>() ;if (cache.contains("a")) {
String value = cache.get("a") ;
}
正例:
Map<String, String> cache = new HashMap<>() ;
String value = cache.get("a") ;
![be343eb9a7d17c661d2542b0cd551ef1.png](https://i-blog.csdnimg.cn/blog_migrate/fa4b6891422012b2bf0c8cee02ac7c98.jpeg)