Alibaba Java Coding Guidelines 检
2020-10-18 本文已影响0人
夹胡碰
1. 概述
Alibaba Java Coding Guidelines 的检测级别有三个,严重等级从高到低依次分别是 、、,下面介绍检测级别中等的级别的检测项目。
2. 全部检测项统计
3. 检测规则展示
-
1. ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常。
说明:禁止强转,如果需要用到集合特性方法,请新建一个集合,然后置入sublist,new 集合(sublist结果)。
Negative example:
List<String> list = new ArrayList<String>();
list.add("22");
//warn
List<String> test = (ArrayList<String>) list.subList(0, 1);
Positive example:
List<String> list2 = new ArrayList<String>(list.subList(0, 1));
2. Map/Set的key为自定义对象时,必须重写hashCode和equals。
关于hashCode和equals的处理,遵循如下规则:
1) 只要重写equals,就必须重写hashCode。
2) 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
3) 如果自定义对象做为Map的键,那么必须重写hashCode和equals。
3. Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
Positive example:
public void f(String str){
String inner = "hi";
if (inner.equals(str)) {
System.out.println("hello world");
}
}
4. POJO类中的任何布尔类型的变量,都不要加is,否则部分框架解析会引起序列化错误
Positive example:
public class DemoDO{
Boolean success;
Boolean delete;
}
5. SimpleDateFormat 是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUtils工具类。
说明:如果是JDK8的应用,可以使用instant代替Date,LocalDateTime代替Calendar,DateTimeFormatter代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。
Positive example 1:
private static final String FORMAT = "yyyy-MM-dd HH:mm:ss";
public String getFormat(Date date){
SimpleDateFormat dateFormat = new SimpleDateFormat(FORMAT);
return sdf.format(date);
}
Positive example 2:
private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
public void getFormat(){
synchronized (sdf){
sdf.format(new Date());
….;
}
Positive example 3:
private static final ThreadLocal<DateFormat> DATE_FORMATTER = new ThreadLocal<DateFormat>() {
@Override
protected DateFormat initialValue() {
return new SimpleDateFormat("yyyy-MM-dd");
}
};
6. 不能使用过时的类或方法。
说明:java.net.URLDecoder 中的方法decode(String encodeStr) 这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。
7. 不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块中的return语句。
Negative example:
public static Long readFileLength(String fileName) {
try {
File file = new File(fileName);
RandomAccessFile randomAccessFile = new RandomAccessFile(file, "r");
return randomAccessFile.length();
} catch (Exception e) {
logger.error(e.getMessage(), e);
} finally {
countDownLatch.countDown();
return 0L;
}
}
8. 使用工具类Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常。
Positive example:
List<String> t = Arrays.asList("a","b","c");
//warn
t.add("22");
//warn
t.remove("22");
//warn
t.clear();
9. 使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()
Negative example:
Integer[] a = (Integer [])c.toArray();
Positive example:
Integer[] b = (Integer [])c.toArray(new Integer[c.size()]);
10. 创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。创建线程池的时候请使用带ThreadFactory的构造函数,并且提供自定义ThreadFactory实现或者使用第三方实现。
Positive example:
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
singleThreadPool.execute(()-> System.out.println(Thread.currentThread().getName()));
singleThreadPool.shutdown();
public class TimerTaskThread extends Thread {
public TimerTaskThread(){
super.setName("TimerTaskThread"); …
}
11. 在subList场景中,高度注意对原列表的修改,会导致子列表的遍历、增加、删除均产生ConcurrentModificationException异常。
Negative example:
List<String> originList = new ArrayList<String>();
originList.add("22");
List<String> subList = originList.subList(0, 1);
//warn
originList.add("22");
12. 在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有。
Positive example:
switch (x) {
case 1:
break;
case 2:
break;
default:
}
13. 对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别
Positive example:
public interface DemoService{
void f();
}
public class DemoServiceImpl implements DemoService {
@Override
public void f(){
System.out.println("hello world");
}
}
14. 常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长
Positive example:
public class ConstantNameDemo {
/**
* max stock count
*/
public static final Long MAX_STOCK_COUNT = 50000L;
15. 异常类命名使用Exception结尾
Positive example:
public class CacheDemoException extends Exception{
}
16. 必须回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用try-finally块进行回收。
Positive example:
/**
* @author caikang
* @date 2017/04/07
*/
public class UserHolder {
private static final ThreadLocal<User> userThreadLocal = new ThreadLocal<User>();
public static void set(User user){
userThreadLocal.set(user);
}
public static User get(){
return userThreadLocal.get();
}
public static void remove(){
userThreadLocal.remove();
}
}
/**
* @author caikang
* @date 2017/04/07
*/
public class UserInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
UserHolder.set(new User());
return true;
}
@Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex) throws Exception {
UserHolder.remove();
}
}
17. 所有的枚举类型字段必须要有注释,说明每个数据项的用途。
Positive example:
public enum TestEnum {
/**
* agree
*/
agree("agree"),
/**
* reject
*/
reject("reject");
private String action;
TestEnum(String action) {
this.action = action;
}
public String getAction() {
return action;
}
}
18. 所有编程相关的命名均不能以下划线或美元符号开始
19. 抽象类命名使用Abstract或Base开头
Positive example:
abstract class BaseControllerDemo{
}
abstract class AbstractActionDemo{
}
20. 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase,必须遵从驼峰形式
21. 日期格式化字符串[%s]使用错误,应注意使用小写‘y’表示当天所在的年,大写‘Y’代表week in which year。 日期格式化时,yyyy表示当天所在的年,而大写的YYYY代表是week in which year(JDK7之后引入的概念),意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的YYYY就是下一年。
Negative example:
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Positive example:
SimpleDateFormat format = new SimpleDateFormat("YYYY-mm-dd HH:mm:ss");
22. 浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用equals来判断。 浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数,具体原理参考《码出高效》,改进方式:
1)指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;
float diff = 1e-6f;
if (Math.abs(a - b) < diff) {
System.out.println("true");
}
2) 使用BigDecimal来定义值,再进行浮点数的运算操作
BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");
BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);
if (x.equals(y)) {
System.out.println("true");
}
Negative example:
float g = 0.7f-0.6f;
float h = 0.8f-0.7f;
if (g == h) {
System.out.println("true");
}
Positive example:
double dis = 1e-6;
double d1 = 0.0000001d;
double d2 = 0d;
System.out.println(Math.abs(d1 - d2) < dis);
23. 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
说明:使用线程池的好处是减少在创建和销毁线程上所花的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。
Positive example:
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder()
.setNameFormat("demo-pool-%d").build();
ExecutorService singleThreadPool = new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
singleThreadPool.execute(()-> System.out.println(Thread.currentThread().getName()));
singleThreadPool.shutdown();