zoukankan      html  css  js  c++  java
  • Java代码规范

    一、前言

    本文参考《阿里巴巴Java开发手册》,这本书主要定义了一些代码的规范以及一些注意事项。我只根据我自己的不足,摘录了一些内容,方便以后查阅。 

    二、读书笔记

    • 命名

    1、代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。

    2、常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
     
    3、抽象类命名使用 Abstract 或 Base 开头; 异常类命名使用 Exception 结尾; 测试类命名以它要测试的类的名称开始,以 Test 结尾。
     
    4、中括号是数组类型的一部分,数组定义如下: String[] args;
     
    5、POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
    反例: 定义为基本数据类型 Boolean isDeleted; 的属性,它的方法也是 isDeleted()。 RPC框架在反向解析的时候, “以为”对应的属性名称是 deleted,导致属性获取不到,进而抛出异常。
     
    6、包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,但是类名如果有复数含义,类名可以使用复数形式。
     
    7、如果使用到了设计模式,建议在类名中体现出具体模式。有利于阅读者快速理解架构设计思想。
     
    8、接口类中的方法和属性不要加任何修饰符号(public 也不要加) ,保持代码的简洁性,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,如果一定要定义变量,肯定是与接口方法相关,并且是整个应用的基础常量。
     
    9、枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
    说明: 枚举其实就是特殊的常量类,且构造方法默认强制为私有。
    正例: 枚举名字: DealStatusEnum, 成员名称: SUCCESS / UNKOWN_REASON。
     
    10、各层命名规约:
    A) Service/DAO 层方法命名规约
    1) 获取单个对象的方法用 get 做前缀。
    2) 获取多个对象的方法用 list 做前缀。
    3) 获取统计值的方法用 count 做前缀。
    4) 插入的方法用 save(推荐) 或 insert 做前缀。
    5) 删除的方法用 remove(推荐) 或 delete 做前缀。
    6) 修改的方法用 update 做前缀。
    B) 领域模型命名规约
    1) 数据对象: xxxDO, xxx 即为数据表名。
    2) 数据传输对象: xxxDTO, xxx 为业务领域相关的名称。
    3) 展示对象: xxxVO, xxx 一般为网页名称。
    4) POJO 是 DO/DTO/BO/VO 的统称,禁止命名成 xxxPOJO。 
     
    • 常量定义
    1、不允许任何未经定义的常量直接出现在代码中。
    反例: String key = "Id#taobao_" + tradeId;
    cache.put(key, value);
     
    2、long 或者 Long 初始赋值时,必须使用大写的 L,不能是小写的 l,小写容易跟数字1 混淆,造成误解。
     
    3、不要使用一个常量类维护所有常量,应该按常量功能进行归类,分开维护。如:缓存相关的常量放在类: CacheConsts 下; 系统配置相关的常量放在类: ConfigConsts 下。
     
    • OOP规约
    1、避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
     
    2、外部正在调用或者二方库依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated 注解,并清晰地说明采用的新接口或者新服务是什么。
     
    3、不能使用过时的类或方法。
     
    4、Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用
    equals
    正例: "test".equals(object);
    反例: object.equals("test");
     
    5、所有的相同类型的包装类对象之间值的比较,全部使用 equals 方法比较。对于 Integer var = ? 在-128 至 127 范围内的赋值, Integer 对象是在IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用 equals 方法进行判断。
     
    6、关于基本数据类型与包装数据类型的使用标准如下:
    1) 【强制】 所有的 POJO 类属性必须使用包装数据类
    2) 【强制】 RPC 方法的返回值和参数必须使用包装类
    3) 【推荐】 所有的局部变量使用基本数据类型。
    说明:用基本数据类型数据默认值是0,而包装数据类型默认值是null,数据库的查询结果可能为null,因为自动拆箱,用基本数据类型接收有NPE风险。
     
    7、定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值(在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。)
     
    8、序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败; 如果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值。注意 serialVersionUID 不一致会抛出序列化运行时异常。
     
    9、构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在 init 方法中。
     
    10、类内方法定义顺序依次是:公有方法或保护方法 > 私有方法 > getter/setter方法。
     
    11、循环体内,字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展。
     
    12、慎用 Object 的 clone 方法来拷贝对象。对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象的拷贝。
     
    13、工具类不允许有 public 或 default 构造方法。
     
    • 集合处理

    1、ArrayList的subList结果不可强转成ArrayList,否则会抛出 ClassCastException异常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是ArrayList 的一个视图,对于 SubList 子列表的所有操作最终会反映到原列表上。

    2、在 subList 场景中, 高度注意对原集合元素个数的修改,会导致子列表的遍历、增加、删除均产生 ConcurrentModificationException 异常。

    3、使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一样的数组,大小就是 list.size()。

    说明: 使用 toArray 带参方法,入参分配的数组空间不够大时, toArray 方法内部将重新分配内存空间,并返回新数组地址; 如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。
    正例:
    List<String> list = new ArrayList<String>(2);
    list.add("guan");
    list.add("bao");
    String[] array = new String[list.size()];
    array = list.toArray(array);
    反例: 直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误

    4、使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。

    说明: asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList体现的是适配器模式,只是转换接口,后台的数据仍是数组。

    5、泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法, 而<? super T>不能使用 get 方法,做为接口调用赋值时易出错。

    说明: 扩展说一下 PECS(Producer Extends Consumer Super)原则: 1) 频繁往外读取内容的,适合用上界 Extends。 2) 经常往里插入的,适合用下界 Super。

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

    正例:
    Iterator<String> it = a.iterator();
    while (it.hasNext()) {
    String temp = it.next();
    if (删除元素的条件) {
    it.remove();
    }
    }
    反例:
    List<String> a = new ArrayList<String>();
    a.add("1");
    a.add("2");
    for (String temp : a) {
    if ("1".equals(temp)) {
    a.remove(temp);
    }
    }

    7、集合初始化时, 指定集合初始值大小。

    说明: HashMap 使用 HashMap(int initialCapacity) 初始化,
    正例:initialCapacity = (需要存储的元素个数 / 负载因子) + 1。注意负载因子(即 loaderfactor) 默认为 0.75, 如果暂时无法确定初始值大小, 请设置为 16。

    9、使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。

    说明: keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出key 所对应的 value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用 Map.foreach 方法。
    正例: values()返回的是 V 值集合,是一个 list 集合对象; keySet()返回的是 K 值集合,是一个 Set 集合对象; entrySet()返回的是 K-V 值组合集合。

    10、在 java 集合类库中,List 的 contains 方法普遍时间复杂度是 O(n) ,如果在代码中需要频繁调用 contains 方法查找数据,可以先将 list 转换成 HashSet 实现,将 O(n) 的时间复杂度降为 O(1) 。

    11、高度注意 Map 类集合 K/V 能不能存储 null 值的情况,如下表格:

    Hashtable 不允许为 null 不允许为 null Dictionary 线程安全
    ConcurrentHashMap 不允许为 null 不允许为 null AbstractMap 分段锁技术
    TreeMap 不允许为 null 允许为 null AbstractMap 线程不安全
    HashMap 允许为 null 允许为 null AbstractMap 线程不安全
    反例: 由于 HashMap 的干扰,很多人认为 ConcurrentHashMap 是可以置入 null 值,而事实上,存储 null 值时会抛出 NPE 异常。 
     
     
     
    • 控制语句

    1、在 if/else/for/while/do 语句中必须使用大括号。 即使只有一行代码,避免使用单行的形式: if (condition) statements;

    2、表达异常的分支时, 少用 if-else 方式。如果非得使用 if()...else if()...else...方式表达逻辑,避免后续代码维护困难, 请勿超过 3 层。

    3、除常用方法(如 getXxx/isXxx)等外,不要在条件判断中执行其它复杂的语句,将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

    正例:
    final boolean existed = (file.open(fileName, "w") != null) && (...) || (...);
    if (existed) {
    ...
    }
    反例:
    if ((file.open(fileName, "w") != null) && (...) || (...)) {
    ...
    }

     

    • 异常处理

    1、Java 类库中定义的一类 RuntimeException 可以通过预先检查进行规避,而不应该通过 catch 来处理,比如: IndexOutOfBoundsException, NullPointerException 等等。

    正例: if (obj != null) {...}
    反例: try { obj.method() } catch (NullPointerException e) {...}
     
    2、异常不要用来做流程控制,条件控制,因为异常的处理效率比条件分支低。
     
    3、有 try 块放到了事务代码中, catch 异常后,如果需要回滚事务,一定要注意手动回滚事务。
     
    4、不能在 finally 块中使用 return, finally 块中的 return 返回后方法结束执行,不会再执行 try 块中的 return 语句。
     
    5、防止空指针异常,是程序员的基本修养。
     
    6、避免出现重复的代码(Don’t Repeat Yourself) ,即 DRY 原则。
    说明: 随意复制和粘贴代码,必然会导致代码的重复,在以后需要修改时,需要修改所有的副本,容易遗漏。必要时抽取共性方法,或者抽象公共类,甚至是共用模块。
     
    • 日志规约

    1、应用中不可直接使用日志系统Log4jLogback中的 API,而应依赖使用日志框架SLF4中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。 

    private static final Logger logger = LoggerFactory.getLogger(Abc.class);

    2、日志文件推荐至少保存 15 天,因为有些异常具备以为频次发生的特点。 

    3、trace/debug/info 级别的日志输出,必须使用条件输出形式或者使用占位符的方式。

    说明: logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
    如果日志级别是 warn,上述日志不会打印,但是会执行字符串拼接操作,如果 symbol 是对象,会执行 toString()方法,浪费了系统资源,执行了上述操作,最终日志却没有打印。
    正例: (条件)
    if (logger.isDebugEnabled()) {
    logger.debug("Processing trade with id: " + id + " symbol: " + symbol);
    }
    正例: (占位符)
    logger.debug("Processing trade with id: {} symbol : {} ", id, symbol); 

    4、避免重复打印日志,浪费磁盘空间,务必在 log4j.xml 中设置 additivity=false。 

    <logger name="com.taobao.dubbo.config" additivity="false">

    5、谨慎地记录日志。生产环境禁止输出 debug 日志有选择地输出 info 日志如果使用 warn 来记录刚上线时的业务行为信息,一定要注意日志输出量的问题,避免把服务器磁盘撑爆,并记得及时删除这些观察日志 。

     

    • 其他

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

    说明: 不要在方法体内定义: Pattern pattern = Pattern.compile(规则);

    2、后台输送给页面的变量必须加$!{var}——中间的感叹号。

    说明: 如果 var=null 或者不存在,那么${var}会直接显示在页面上。

    3、不要在视图模板中加入任何复杂的逻辑。

    说明: 根据 MVC 理论,视图的职责是展示,不要抢模型和控制器的活。

    4、任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。

    5、对于“明确停止使用的代码和配置”,如方法、变量、类、配置文件、动态配置属性等要坚决从程序中清理出去,避免造成过多垃圾。

    6、new BigDecimal(double) 存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。请使用 BigDecimal.valueOf(double) 。

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

    正例: 注意线程安全,使用DateTools 。亦推荐如下处理: 
    public class DateTools
    {
    private static ThreadLocal<SimpleDateFormat> t1=new ThreadLocal<>();
    public static SimpleDateFormat getSdf(String pattern){
    SimpleDateFormat simpleDateFormat = t1.get();
    if (simpleDateFormat==null){
    simpleDateFormat=new SimpleDateFormat(pattern);
    }
    t1.set(simpleDateFormat);
    return simpleDateFormat;
    }

    }
    说明: 如果是 JDK8 的应用,可以使用 Instant 代替 Date, LocalDateTime 代替 Calendar,DateTimeFormatter代替Simpledateformatter,官方给出的解释:simple beautiful strong immutable thread-safe。
            // Instant 相当于 java.util.Date 类
            Instant instant = Instant.now();
            instant.getLong(INSTANT_SECONDS); //时间戳
            
            
            // LocalDate 不包含时间的日期
            LocalDate localDate = LocalDate.now();
            // 自定义日期时间
            LocalDate of = LocalDate.of(2019, 10, 1);
            LocalDate parse = LocalDate.parse("2019-09-02");
            // 日期加减
            LocalDate days = parse.plusMonths(1L).minusDays(1L);
            // 日期比较
            System.out.println(of.isBefore(days));
            System.out.println(of.isAfter(days));
            System.out.println(of.equals(days));
    
            // LocalTime 不包含日期的时间
            LocalTime localTime = LocalTime.now();
            System.out.println(LocalTime.MAX);
            System.out.println(LocalTime.MIN);
    
            // LocalDateTime 包含了日期及时间,没有偏移信息(时区),相当于 java.util.Calendar 类
            LocalDateTime localDateTime = LocalDateTime.now();
            LocalDateTime atTime = localDate.atTime(LocalTime.MAX);
            LocalDateTime atDate = localTime.atDate(LocalDate.of(2019, 10, 14));
    
    
            // 时间日期格式化,相当于 java.text.SimpleDateFormat
            LocalDateTime dateTime = LocalDateTime.now();
            dateTime.format(DateTimeFormatter.ISO_LOCAL_DATE_TIME);
            dateTime.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));
    View Code

    书籍链接: 阿里巴巴Java开发手册

  • 相关阅读:
    复习提纲
    查看版本和存储的地方
    0到255的颜色
    stixel-world和psmnet结合出现的问题
    python plt 保存jpg出错
    三和韓長庚 著 正易 對讀 161-200
    startActivity、 startActivityForResult 、广播的使用
    01背包+卡精度 Hdu 2955
    c++ string 之 find_first_not_of 源码
    java:[1,0] illegal character: 65279 问题
  • 原文地址:https://www.cnblogs.com/jmcui/p/7444646.html
Copyright © 2011-2022 走看看