阿里java开发手册笔记(编程规约)

825 阅读10分钟

最近在学习阿里的java开发手册,现将自己认为最重要和最容易忽视的部分进行归纳整理,有需要的掘友可以作为参考和借鉴,具体如下:

一,命名风格

  • 1,(原文第2点)所有编程相关命名严禁使用拼音与英文混合的方式,更是严禁使用中文;
    解读:这样是为了提升代码的可读性,同时避免歧义;

  • 2,(原文第5点)常量命名全部大写,单词间用下划线分割,尽量表达清楚;
    解读:还是为了提升代码的可读性;

  • 3,(原文第8点)pojo类中布尔类型的变量,不要加is前缀,避免部分框架解析时的序列化错误;

  • 4,(原文第9点)包名统一使用小写和单数形式,'.'分隔符之间仅有一个英文单词,类名有复数含义时可以使用复数形式;
    解读:统一规范,提升可读性,可参考spring的命名方式;

  • 5,(原文第11点)避免完全不规范的缩写,代码要自解释,望文知义;

  • 6,(原文第14点)模块/接口/类/方法等如果使用了设计模式,命名时要体现具体的模式;
    解读:提升代码可读性,快速理解具体的设计方式;

二,常量定义

  • 1,(原文第1点)禁止在代码中出现魔法值(未经预定义的常量);
    解读:如果是常量则一定要预先定义,避免误用出现bug;

  • 2,long或Long类型赋值时数值后要使用大写的'L',如果是小写l则容易引起误解;

三,代码格式

  • 1,(原文第5点)采用4个空格进行缩进,禁止使用tab键;
    解读:缩进格式统一,提升代码可读性,idea中代码写完直接Ctrl+Alt+L就完了

  • 2,(原文第6点)注释的双斜线与注释内容有且仅有一个空格;eg:// 这是示例注释 解读:格式统一,提升代码可读性;

  • 3,(原文第8点)单行代码字符数超过120时需要换行;

  • 4,(原文第10点)IDE的text file encoding设置为utf-8,文件换行符使用unix格式;
    解读:避免出现字符避免错误问题

  • 5,单个方法行数不要超过80行(包括签名&大括号&空行),共性逻辑进行抽取复用;
    解读:提升代码可读性;

四,OOP规约

  • 1,(原文第3点)相同的参数类型和业务含义才可以使用可变参数,不要使用Object;
    解读:可变参数编程不推荐,effective java一书中已有论述;

  • 2,(原文第5点)不要使用过时的类或方法;
    解读:过时的类和方法可能隐藏有bug或效率问题;

  • 3,(原文第6点)等值判断时要使用常量或确定有值的对象来调用equals方法;
    解读:避免出现NPE;

  • 4,(原文第7点)所有包装类型对象值得比较全部使用equals; 解读:避免值判断错误,如Integer值在(-128~127)的变量使用'=='判断时相等,在此之外的变量则不相等;

  • 5,(原文第8点)任何货币金额,均使用最小货币单位且整形类型进行存储(如人民币的分);
    解读:避免出现金额误差,个人认为也可以使用BigDecimal类型进行金额计算与存储,视具体设计方案来定;

  • 6,(原文第10点)定义DO类属性时要与数据库字段类型匹配; 解读:如果数据库字段类型为bigint,而DO对应属性为Integer则有可能出现数值溢出;

  • 7,(原文第11点)禁止使用构造方法BigDecimal(double)的方式将double值转化为BigDecimal对象;
    解读:可能出现精度损失而造成误差;

  • 8,(原文第13点)定义DO/DTO/VO等pojo类时,不要设定默认属性值;
    解读:可能导致字段误更新问题(不需要或不要更新的字段结果被更新或赋值了);

  • 9,(原文第22点)循环体重的字符串拼接使用StringBuilder的append方法;
    解读:避免创建不必要的对象,提升gc性能;

  • 10,(原文第25点)类成员与方法的访问控制能从严则从严;
    解读:避免误用,便于解耦;

五,日期时间

  • 1,日期格式化时,传入pattern表示年份则统一使用'yyyy';
    解读:jdk中语义定义不同,可能出现错误;

  • 2,不允许在程序的任何地方使用:<1,java.sql.Date;<2,java.sql.Time;<3,java.sql.Timestamp;
    解读:以上3个日期类都会导致出现bug,涉及时间/日期计算时个人强烈建议使用java8的日期时间api;

  • 3,使用枚举值代替月份;eg:jdk原生 Calendar.JANUARY;
    解读:涉及表示范围的数据时建议均使用枚举来提高程序的可维护性;

六,集合处理

  • 1,(原文第1点)对象只要重写了hashcode,就必须重写equals;
    解读:避免Set/Map进行数据比较或存储出现错误;

  • 2,(原文第3点)在使用java.util.stream.Collectors类的toMap方法转为Map集合时务必使用含有参数类型为BinaryOperator,参数名为mergeFunction的方法,否则当key值相同时会抛出IllegalStateException;
    解读:mergeFunction可以在当key相同时,自定义对value的处理策略;

  • 3,(原文第4点)在使用java.util.stream.Collectors类的toMap方法转为Map集合时,务必注意当value为null时将抛出NPE;

  • 4,(原文第5点)ArrayList的subList结果不能强转为ArrayList,否则将抛出ClassCastException;
    解读:subList返回结果是ArrayList的内部类,只是ArrayList的一个视图,此时对父集合的修改操作将会导致子列表的增删查出现异常;

  • 5,(原文第9点)使用集合转数组的方法时,必须使用toArray(T[] array)方法,array为类型完全一致的空数组;
    解读:直接使用toArray()方法时返回结果是Object[]类,若强转则抛ClassCastException;

  • 6,(原文第10点)在使用 Collection 接口任何实现类的 addAll()方法时,务必对输入的集合参数进行非null校验,否则将抛NPE;

  • 7,(原文第13点)使用工具类Arrays.asList把数组转换成集合时,不能使用其修改集合相关的方法,如add/remove等操作将会抛出UnsupportedOperationException 异常;
    解读:asList返回结果只是Arrays内部类,并未实现集合的修改方法;

  • 8,(原文第14点)切勿在 foreach 循环里进行元素的 remove/add 操作。 remove元素请使用 Iterator 方式,如果并发操作,要对 Iterator 对象加锁。
    解读:Iterator是对集合进行迭代修改的标准操作方式;

  • 9,(原文第17点)集合初始化时,指定初始容量大小;
    解读:避免不断扩容造成的资源和性能损耗;

  • 10,使用 entrySet 遍历 Map 集合,而不是 keySet 方式进行遍历,如果为java8则使用Map.forEach 方法。
    解读:entrySet方式效率更高(只遍历一次);

  • 11,高度注意ConcurrentHashMap的key&value均不能为null/TreeMap key不能为null问题,否则将抛NPE;

七,并发处理

  • 1,(原文第1点)获取单例对象要保证线程安全,其中方法也要保证线程安全;
    解读:可以通过饿汉/DCL/同步懒汉的方式创建和获取单例对象,线程安全相对性能的部分损失更重要;

  • 2,(原文第2&3&4点)线程资源必须通过线程池,而且是ThreadPoolExecutor显式创建的方式进行创建,避免使用Executors;
    解读:降低线程的频繁创建和销毁带来的资源消耗问题,同时显式创建极大提升了可读性,规避资源耗尽的风险;

  • 3,(原文第5点)SimpleDateFormat线程非安全,不要定义为static,否则可能出现并发相关问题;
    解读:java8环境下最好使用新的的日期/时间api,eg:LocalDate/LocalDateTime等(线程安全且更api更友好);

  • 4,(原文第6点)并发环境下,必须回收自定义的ThreadLocal变量,否则可能会影响后续业务逻辑和造成内存泄漏问题;

  • 5,(原文第7点)对多个资源进行加锁要保证一致的加锁顺序,否则可能导致死锁问题;

  • 6,(原文第9点)在使用lock进行加锁时要严格注意加锁的位置和方法,防止出现加锁不成功时进行解锁异常或加锁成功未能成功释放锁导致死锁;
    解读:正确的加锁姿势:

  • 7,(原文第10点)在进行尝试获取锁时,处理下一步逻辑之前,必须判断是否加锁成功; 解读: 如果不进行判断则尝试获取锁失败后,执行下面逻辑导致释放锁时候抛出异常;

  • 8,(原文第11点)并发更新某一记录要进行同步加锁处理,在应用层/缓存/数据库/等某一层面进行加锁,同时考虑性能问题,注意乐观锁(访问冲突概率小于20%)或悲观锁的使用选择;

  • 9,并发处理定时任务时,不要使用Timer(会出现任一任务异常任务全终止问题),使用ScheduledExecutorService;

  • 10,(原文第14点)使用CountDownLatch进行同步控制时,每个线程退出前必须保证countDown方法被执行;
    解读:避免main线程因无法执行await而不能继续执行;

八,控制语句

  • 1,(原文第1点)switch中每个case要么通过 continue/break/return等来终止,要么注释说明程序将继续执行到哪一个 case 为止,同时必须在最后包含一个 default语句;
    解读:提升代码的可读性,避免出现bug;

  • 2,(原文第1点)当 switch 括号内的变量类型为 String 并且此变量为外部参数时,必须先进行 null 判断,否则将出现NPE;

  • 3,(原文第4点)三目运算符(条件? 表达式 1 : 表达式 2) 中,高度注意表达式 1 和 2 在类型对齐 时,可能抛出因自动拆箱导致的 NPE 异常。

  • 4,(原文第5点)高并发场景中,避免使用” 等于” 判断作为中断或退出的条件。
    解读: 可能出现等值判断击穿问题;

  • 5,(原文第10点)循环中尽量避免进行不必要对象创建/获取数据库连接/try-catch等操作;
    解读:提升程序的性能;

  • 6,进行批量操作的接口,要进行接口入参保护;

九,注释规约

  • 1,(原文第1点)类、类属性、类方法的注释必须使用 Javadoc 规范,使用/**内容*/格式;
    解读:提升代码的可读性,通过javadoc还可以生成代码注释;

  • 2,(原文第2点)所有的抽象方法(包括接口中的方法) 必须要用 Javadoc 注释、除了返回值、参数、 异常说明外,还必须指出该方法做什么事情,实现什么功能; 解读:抽象方法实际上就是对外暴露的接口api,api就一定要要考虑易用性;

  • 3,(原文第4点)方法内部单行注释,在被注释语句上方另起一行,使用//注释;

  • 4,(原文第5点)所有的枚举类型字段必须要有注释,说明每个数据项的用途; 解读:提升代码的可读性;

十,其他

  • 1,使用正则表达式时,利用好其预编译功能,可有效加快正则匹配速度,Pattern.compile(“可共用的规则” )结果声明为static final;

  • 2,避免用 Apache Beanutils 进行属性的 copy,性能较差;

  • 3,后台输送给页面的变量必须加$!{var}中间的感叹号,否则var不存在时${var}会直接显示在页面;

    最后附上下载链接: Java开发手册泰山版,以供参考;