阿里规约解读

  1. long或者 Long初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字1混淆,造成误解。

正例:
Long a = 2L;
反例:
Long a = 2l; 

  1. 不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。

正例:
ArrayList list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("C");

        System.out.println(list);//[A, B, C]
        
        Iterator iterator = list.iterator();
        while (iterator.hasNext()){
            String next = iterator.next();
            if(next.equals("B")){
                iterator.remove();
            }
        }

        System.out.println(list);//[A, C]
反例:
ArrayList list = new ArrayList<>();
        list.add("A");
        list.add("B");
        list.add("R");
        list.add("T");
        list.add("C");

        System.out.println(list);//[A, B, C]
        for (String str : list) {
            if(str.equals("B")){
                list.remove(str);
            }

        }

3.在iflelse/forlwhileldo语句中必须使用大括号,即使只有一行代码。

正例:
if (condition){ 
    statements;
}
反例:
 if (condition) statements;

4.在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。

正例:
private static final Pattern pattern = Pattern.compile(".*:(.*)://.*/(.*)\\?.*$");
反例:
 public void t(){
        Pattern pattern = Pattern.compile(".*:(.*)://.*/(.*)\\?.*$");
    }

5.多钱程并行处理定时任务时,Timer运行多个TimeTask时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用ScheduledExecutorService则没有这个问题。

解释:

Timer运行多个TimeTask时底层公用一个队列它会错误的认为整个 Timer 线程都会取消。
Timer 是内部是单一线程,而 ScheduledThreadPoolExecutor 内部是个线程池,所以可以支持多个任务并发执行。
正例:
private final ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(1, new ImprovedTimer.DaemonThreadFactory());
private ScheduledFuture improvedTimerFuture = null;

public void schedule(Runnable command, long initialDelay, long period){
        // initialDelay 毫秒后开始执行任务,以后每隔 period 毫秒执行一次

        // schedule方法被用来延迟指定时间来执行某个指定任务。
        // 如果你需要周期性重复执行定时任务可以使用scheduleAtFixedRate或者scheduleWithFixedDelay方法,它们不同的是前者以固定频率执行,后者以相对固定频率执行。
        // 不管任务执行耗时是否大于间隔时间,scheduleAtFixedRate和scheduleWithFixedDelay都不会导致同一个任务并发地被执行。
        // 唯一不同的是scheduleWithFixedDelay是当前一个任务结束的时刻,开始结算间隔时间,如0秒开始执行第一次任务,任务耗时5秒,任务间隔时间3秒,那么第二次任务执行的时间是在第8秒开始。

        improvedTimerFuture = executorService.scheduleAtFixedRate(command, initialDelay, period, TimeUnit.MILLISECONDS);
    }

 /**
     * 守护线程工厂类,用于生产后台运行线程
     */
    private static final class DaemonThreadFactory implements ThreadFactory {
        private AtomicInteger atoInteger = new AtomicInteger(0);

        @Override
        public Thread newThread(Runnable runnable) {
            Thread thread = new Thread(runnable);
            thread.setName("schedule-pool-Thread-" + atoInteger.getAndIncrement());
            thread.setDaemon(true);
            return thread;
        }
    }
反例:
public static void main(String[] args) throws Exception {
 
        Timer timer = new Timer();
        Thread.sleep(10000);
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("haha");
                try {
                    Thread.sleep(10000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, 1000, 1000);
 
        timer.schedule(new TimerTask() {
            @Override
            public void run() {
                System.out.println("heihei");
            }
        }, 1000,1000);
    }

6.所有的包装类对象之间值的比较,全部使用equals方法比较。

正例:
a.equals(b)
反例:
a==b

7.所有的覆写方法,必须加@Override注解。

8.线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式能更加明确线程池的运行规则,规避资源耗尽的风险。

解释:

Java通过Executors提供四种线程池,分别为∶
1)newFixedThreadPool创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
2)newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO,优先级)执行。
3)newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
4)newScheduledThreadPool创建一个定长线程池,支持定时及周期性任务执行。

Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
  主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
  主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
正例:
 @Bean
    public Executor FileCopyExecutor() {
        ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
        //核心线程数,一次处理100个
        taskExecutor.setCorePoolSize(fileCorePoolSize);
        taskExecutor.setMaxPoolSize(2 *fileCorePoolSize);
        taskExecutor.setQueueCapacity(fileQueueCapacity);
        taskExecutor.setThreadNamePrefix("file-");
        taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        taskExecutor.initialize();
        return taskExecutor;
    }
反例:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);

9.获取当前毫秒数:System.currentTimeMillis();而不是new Date().getTime();

解释:

new Date().getTime()其实最终还是调用了System.currentTimeMillis(),那还不如直接就使用System.currentTimeMillis()。
因为new Date()包含了非常多的时间信息,直接使用System.currentTimeMillis(),效率更高。

10.避免用Apache Beanutils进行属性的copy.

主要还是性能问题

1000次

10000次

100000次

1000000次

apache BeanUtils

906毫秒

807毫秒

1892毫秒

11049毫秒

apache PropertyUtils

17毫秒

96毫秒

648毫秒

5896毫秒

spring cglib BeanCopier

0毫秒

1毫秒

3毫秒

10毫秒

spring copyProperties

87毫秒

90毫秒

123毫秒

482毫秒

性能走势 --> spring cglib BeanCopier 优于 spring copyProperties 优于 apache PropertyUtils 优于 apache BeanUtils

11.避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。

解释:

增加jvm成本,增加无谓的内存消耗
通过对象支持调用静态变量和静态方法,最终都会在编译后转换为类直接调用静态变量或静态方法。
正例:
String str = TestConst.a;
反例:
TestConst tc = new TestConst();
String str = tc.a;

12.ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException异常。

解释:

1)SubList 继承 AbstractList ,所以具有List接口的所有方法。
2)SubList 是ArrayList 的一个内部类。SubList并没有重新创建一个List,而是直接引用原有的List,只不过对原来List做截取而已。
3)ArrayList 也是继承AbstractList,但是 SubList 和 ArrayList 没有继承关系,所以不能强转换。
正例:
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);

List subList1  = list.subList(1, 3);
反例:
List list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
    
ArrayList subList1 = (ArrayList)list.subList(1, 3);

13.Map/Set的key为自定义对象时,必须重写hashCode和equals.

解释:

Map/Set首先将自定义对象的hashcode值与集合中已经存在的数据的hashcode值比较,不相等则直接添加,如果相等再进一步判断他们的equals是否相等,不相等则添加。
如果不重写自定义对象类的hashcode和equals方法,则他们的equals方法不相同;同样他们的hashcode值也大概率不相等

14.Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。

15.POJO类中的任何布尔类型的变量,都不要加is,否则部分框架解析会引起序列化错误

解释:

类中的属性用is开头会导致对象序列化后产生意想不到的结果。所以在平时编码的过程中,我们不能在类变量中用is开头如: “isSuccess”,这样容易造成程序错误

16.SimpleDateFormat是线程不安全的类,一般不要定义为static变量,如果定义为static,必须加锁,或者使用DateUltis工具类.

17.不能使用过时的类或方法。

18.不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块中的return语句。

解释:

try块中的return值会先保存起来,然后执行完finally中的代码后,才会把try块中的return值返回,所以finally中的代码逻辑是不会影响try块中的return值的。
但如果在finally中使用return了就会导致try块中的代码得不到执行而无法返回正确的结果

19.使用工具类Arays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的add/removelclear方法会抛出Unsupported()

解释:

asList的返回对象是一个Arrays内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。
反例:
String[] str = new String[]{"1","2"};
List strings = Arrays.asList(str);
strings.add("3");//java.lang.UnsupportedOperationException
--------------------------------------------------------------------------------
String[] str = new String[]{"1","2"};
List strings = Arrays.asList(str);
str[0]="3";//strings 内容会同步被改掉

20.使用集合转数组的方法,必须使用集合的toArray(Tarray),传入的是类型完全一样的数组,大小就是list size()

正例:
List list = new ArrayList();
list.add("guan");
list.add("bao");
String[] array = new String[list.size()];
array = list.toArray(array);

21.创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。创建线程池的时候请使用带ThreadFactory的构造函数,并且提供自定义ThreadFactory实现或者使用第三方实现。

22.在subList场景中,高度注意对原列表的修改,会导致子列表的遍历、增加、删除均产生ConcurentModificationException异常。

反例:
 List list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(3);

        List subList1  = list.subList(1, 3);
        list.remove(2);
        for (Integer integer : subList1) {

        }

23.在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内都必须包含一个default语句并且放在最后,即使它什么代码也没有。

24.对于Service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用Impl的后缀与接口区别

25.常量命名应该全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长

26.异常类命名使用Exception结尾

27.必须回收自定义的ThreadLocal变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的ThreadLocal变量,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用try-finally块进行回收。

正例:
ThreadLocal objectThreadLocal = new ThreadLocal();
 objectThreadLocal.set(1);
try {
    // 业务逻辑
} finally {
    objectThreadLocal.remove();
}

28.所有的枚举类型字段必须要有注释,说明每个数据项的用途。

正例:
public enum AppExceptionEnum implements ExceptionInterface {

    APP_CODE_OR_SECRET_ERROR("应用编码有误或密钥错误"),
    APP_CODE_NOT_EXIST("应用编码不存在"),
    ;
    private String message;

    AppExceptionEnum(String message) {
        this.message = message;
    }

}

29.所有编程相关的命名均不能以下划线或美元符号开始

反例:
private void $test(){
    System.out.println("命名不能以美元符号开始")
}

private void _test(){
    System.out.println("命名不能以下划线开始")
}

30.抽象类命名使用Abstract或Base开头

31.方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase,必须遵从驼峰形式

32.日期格式化字符串[%s]使用错误,应注意使用小写y'表示当天所在的年,大写Y代表week in which year。

解释:

日期格式化时,yyyy表示当天所在的年,而大写的YYYY代表是week in which year(JDK7之后引入的概念),
意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的YYYY就是下一年。

33.浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用equals来判断。

正例:
BigDecimal aa = new BigDecimal("1.0");
BigDecimal bb = new BigDecimal("0.9");
BigDecimal cc = new BigDecimal("0.8");

BigDecimal x = aa.subtract(bb);
BigDecimal y = bb.subtract(cc);

x.equals(y);
反例:
float a = 1.0f - 0.9f;
float b = 0.9f - 0.8f;

a==b

34.线程资源必须通过线程池提供,不允许在应用中自行显式创建线程

解释:

如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

如有解释不当处,欢迎各位大佬指正

你可能感兴趣的:(代码规范,java)