Java开发规范精要

1.命名规则

  • 代码中的命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
  • 类名使用 UpperCamelCase 风格,必须遵从驼峰形式,但以下情形例外:(领域模型 的相关命名)DO / BO / DTO / VO等。
    正例:MarcoPolo / UserDO / XmlService / TcpUdpDeal / TaPromotion
    反例:macroPolo / UserDo / XMLService / TCPUDPDeal / TAPromotion
  • 常量命名全部大写,单词间用下划线隔开,力求语义表达完整清楚,不要嫌名字长。
    正例: MAX_STOCK_COUNT
    反例: MAX_COUNT
  • 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以它要测试的类的名称开始,以 Test 结尾
  • POJO 类中布尔类型的变量,都不要加 is,否则部分框架解析会引起序列化错误。
    反例: 定义为基本数据类型Boolean isSuccess;的属性,它的方法也是isSuccess(),RPC 框架在反向解析的时候,“以为”对应的属性名称是 success,导致属性获取不到,进而抛出异常。
  • 包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式
  • 杜绝完全不规范的缩写,避免望文不知义
    反例: AbstractClass“缩写”命名成 AbsClass;condition“缩写”命名成 condi,此类 随意缩写严重降低了代码的可阅读性。
    不要太抠,不是太长的名字直接写上就好,编译器编译优化后变量名将不存在,会编译成相对于方法堆栈bp指针地址的相对地址,长变量名不会占用更多空间。
    英文中的缩写有个惯例,去掉元音留下辅音即可,不能乱缩写。
  • 如果使用到了设计模式,建议在类名中体现出具体模式
    public class LoginProxy;
    public class ResourceObserver;
    让全世界都知道你会设计模式,这是个崇尚显摆的社会。
  • 对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。如果是形容能力的接口名称,取对应的形容词做接口名(通常是–able 的形式)
    正例: CacheServiceImpl 实现 CacheService 接口
    正例: AbstractTranslator 实现 Translatable
  • 枚举类名建议带上 Enum 后缀,枚举成员名称需要全大写,单词间用下划线隔开。
    enum内的变量同接口变量一样都是public static final类型。因此按照常量命名规范。
  • 【参考】各层命名规约:
    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。
  • 不允许出现任何魔法值(即未经定义的常量)直接出现在代码中
  • Object 的 equals 方法容易抛空指针异常,应使用常量或确定有值的对象来调用 equals。
    常量比变量,永远都不变的原则
  • 【强制】序列化类新增属性时,请不要修改 serialVersionUID 字段,避免反序列失败; 如 果完全不兼容升级,避免反序列化混乱,那么请修改 serialVersionUID 值
  • 【推荐】使用索引访问用 String 的 split 方法得到的数组时,需做最后一个分隔符后有无内容的检查,否则会有抛 IndexOutOfBoundsException 的风险
String str = "a,b,c,,";            
String[] ary = str.split(","); //预期大于 3,结果是 3 
System.out.println(ary.length);
  • 【推荐】慎用 Object 的 clone 方法来拷贝对象。
    说明: 对象的 clone 方法默认是浅拷贝,若想实现深拷贝需要重写 clone 方法实现属性对象的拷贝。
  • 【强制】关于 hashCode 和 equals 的处理,遵循如下规则:
    1) 只要重写equals,就必须重写hashCode。
    2) 因为Set存储的是不重复的对象,依据hashCode和equals进行判断,所以Set存储的对象必须重写这两个方法。
    3) 如果自定义对象做为Map的键,那么必须重写hashCode和equals。
    说明: String 重写了 hashCode 和 equals 方法,所以我们可以非常愉快地使用 String 对象作为 key 来使用。
  • 【强制】ArrayList的subList结果不可强转成ArrayList,否则会抛出ClassCastException
    异常: java.util.RandomAccessSubList cannot be cast to java.util.ArrayList ;
    说明: subList 返回的是 ArrayList 的内部类 SubList,并不是 ArrayList ,而是 ArrayList 的一个视图,对于SubList子列表的所有操作最终会反映到原列表上。
  • 【强制】 在 subList 场景中,高度注意对原集合元素个数的修改,会导致子列表的遍历、增 加、删除均产生ConcurrentModificationException 异常。
  • 【强制】使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全 一样的数组,大小就是 list.size()。
    说明: 使用 toArray 带参方法,入参分配的数组空间不够大时,toArray 方法内部将重新分配内存空间,并返回新数组地址; 如果数组元素大于实际所需,下标为[ list.size() ]的数组元素将被置为 null,其它数组元素保持原值,因此最好将方法入参数组大小定义与集合元素个数一致。
    正例:
List list = new ArrayList(2); list.add("guan");
 list.add("bao");
 String[] array = new String[list.size()]; 
 array = list.toArray(array);

反例: 直接使用 toArray 无参方法存在问题,此方法返回值只能是 Object[]类,若强转其它类型数组将出现 ClassCastException 错误。

  • 【强制】使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方 法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常。
    说明: asList 的返回对象是一个 Arrays 内部类,并没有实现集合的修改方法。Arrays.asList 体现的是适配器模式,只是转换接口,后台的数据仍是数组。
    String[] str = new String[] { “a”, “b” };
    List list = Arrays.asList(str);
    第一种情况: list.add(“c”); 运行时异常。
    第二种情况: str[0] = “gujin”; 那么list.get(0)也会随之修改。

  • 【强制】泛型通配符 <? extends T>来接收返回的数据,此写法的泛型集合不能使用add方 法,而<? super T>不能使用get方法,做为接口调用赋值时易出错。
    说明: 扩展说一下PECS(Producer Extends Consumer Super)原则: 1)频繁往外读取内容的,适合用上界 Extends。 2)经常往里插入的,适合用下界 Super。
    白话:
    <? extends T>, ? 必须是T或T的子类
    集合写(add): 因为不能确定集合实例化时用的是T或T的子类,所以没有办法写。例如:List*<? extends Number> foo = new ArrayList<*Number/Integer/Double>(),你不能add Number,因为也可能是Integer或Double的List, 同理也不能add Integer或Double,即,extends T, 不能集合add。
    集合读(get): 只能读出T类型的数据。
    <? super T>, ? 必须是T或T的父类
    集合写(add): 可以add T或T的子类。
    集合读(get): 不能确定从集合里读出的是哪个类型(可能是T也可能是T的父类,或者Object),所以没有办法使用get。例如:List*<? super Integer> foo3 = new ArrayList<*Integer/Number/Object>(); 只能保证get出来是Object。

  • 【参考】利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的 contains 方法进行遍历、对比、去重操作。

  • 【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。
    说明: Executors 返回的线程池对象的弊端如下:
    1)FixedThreadPool 和 SingleThreadPool: 允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。
    2)CachedThreadPool 和 ScheduledThreadPool: 允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

  • 【强制】高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁; 能锁区块,就不要锁整个方法体; 能用对象锁,就不要用类锁。

  • 【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造 成死锁。
  • 【强制】多线程并行处理定时任务时,Timer 运行多个 TimerTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题。
    白话:
    线程执行体、任务最上层等一定要抓住Throwable并进行相应的处理,否则会使线程终止。
  • 【推荐】使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法可以执行,避免主线程无法执行至 await 方法,直到超时才返回结果。

你可能感兴趣的:(Java,Beginner)