第一章 编程规约

本文参考《阿里巴巴Java开发手册》

1 命名风格

    (1)【强制】命名均不能以 "_" 或 "$" 开始和结束。

    (2)【强制】命名严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。("hangzhou"、"shanghai"等国际通用的名称可等同视为英文)

    (3)【强制】类名使用UpperCamelCase(大驼峰)风格,但以下情形例外:DO、BO、DTO、VO、AO、PO、UID等。

    (4)【强制】方法名、参数名、成员变量名、局部变量都统一使用lowerCamelCase(小驼峰)风格。

    (5)【强制】常量名全部大写,单词间使用 "_" 隔开,力求语义表达清楚,不要嫌名字长。

    (6)【强制】抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类命名以 "要测试的类名 + Test" 组成。

    (7)【强制】类型与中括号紧挨相连来表示数组声明。如:int[] arr;

    (8)【强制】POJO类中布尔类型变量名,都不要加 is 前缀,否则部分框架解析会引起序列化错误。

    (9)【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用单数形式,如果类名有复数含义,类名可以使用复数形式。

    (10)【强制】杜绝完全不规范的缩写,要做到望文生义。

    (11)【推荐】为了达到代码自解释的目标,任何自定义的编程元素在命名时,尽量使用完整的单词组合来表达其意。

    (12)【推荐】如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。(有利于快速理解架构设计理念)

    (13)【推荐】接口类中的方法和属性不要加任何修饰符,保持代码的简洁,并加上有效的 Javadoc 注释。尽量不要在接口里定义变量,肯定是与接口方法相关,并且是整个应用的基础常量,才应该加。

    (14)接口和实现类的命名有两套规则:

            a)【强制】对于 Service 和 DAO 类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl 的后缀与接口区别。

            b)【推荐】如果是形容能力的接口名称,取对应的形容词为接口名(通常是 -able 的形式)。

    (15)【参考】枚举类建议带上 Enum 后缀,枚举成员名称需要全大写,单词间使用 "_" 隔开。

    (16)【参考】各层命名规约:

            a)Service / DAO 层方法命名规约:

                1)获取单个对象的方法用 get 前缀。

                2)获取多个对象的方法用 list 前缀,复数形式结尾。 listObjects

                3)获取统计值的方法用 count 前缀。

                4)插入的方法用 save / insert 前缀。

                5)删除的方法用 remove / delete 前缀。

                6)修改的方法用 update 前缀。

            b)领域模型命名规约:

                1)数据对象:xxxDO,xxx 即为数据表名。(DAO 向 Service 传输的对象)

                2)数据传输对象:xxxDTO,xxx 为业务领域相关的名称。(Service 向 Web 传输的对象)

                3)展示对象:xxxVO,xxx 一般为网页名称。(Controller 向 页面 传输的对象)

                4)POJO 是 DO / DTO / BO / VO 的统称,禁止命名成 xxxPOJO。

2 常量定义

    (1)【强制】不允许使用任何魔法值(即未经预先定义的常量)直接出现在代码中。

    (2)【强制】给 long / Long 赋值时,数值后使用大写的 L,不能是小写 l,小写容易跟数字 1 混淆。

    (3)【推荐】不要使用一个常量类维护所有常量,要按常量功能进行归类,分开维护。

    (4)【推荐】常量的服用层次有五层:跨应用共享常量、应用内共享常量、子工程内共享常量、包内共享常量、类内共享常量。

            1)跨应用共享常量:放置在二方库中,通常是 client.jar 中的 constant 目录下。

            2)应用内共享常量:放置在一方库中,通常是子模块的 constant 目录下。

            3)子工程内部共享常量:即在当前子工程的 constant 目录下。

            4)包内共享常量:即在当前包下单独的 constant 目录下。

            5)类共享常量:直接在类内部 private static final 定义。

    (5)【推荐】如果变量值仅在一个固定范围内变化用 enum 类型来定义。

3 代码格式

    (1)【强制】大括号的使用约定,如果大括号内为空,则简洁地写成 {} 即可,不需要换行;如果是非空代码块则:

            1)左大括号 "{" 前不换行;后换行。

            2)右大括号 "}" 前换行,后还有 else 等代码则不换行,表示终止的右大括号换行。

    (2)【强制】左小括号 "(" 之后和右小括号 ")" 之前不出现空格;左大括号 "{" 前需要空格。

    (3)【强制】if / for / while / switch / do 等保留字与括号之间都必须加空格。

    (4)【强制】任何二、三目运算符的左右两边都要加空格。

    (5)【强制】采用4个空格缩进,禁止使用 tab 字符。(IED 里都有 tab 键映射 4 个空格的功能)。

    (6)【强制】注释的双斜线与注释内容间有且仅有一个空格。

    (7)【推荐】单行字符数限制不超过 120 个超出需要换行,换行时遵循如下原则:

            1)第二行相对第一行缩进 4 个空格,从第三行开始,不在缩进。

            2)运算符与下文一起换行。

            3)方法调用的 "." 与下文一起换行。

            4)方法调用中的多个参数需要换行时,在 "," 之后进行。

            5)在括号前不换行。

    (7)【自定义】单行字符数限制为 1000,即基本不换行。

            1)声明方法时,形参有注解,则每个参数占一行,首个参数不换行。

            2)方法的 chain 调用时,每个 .xxx() 占一行,首个不换行。

    (8)【强制】方法参数在定义和传入时,多个 "," 之后有空格。

    (9)【强制】IDE 的 text file encoding 设置为 UTF-8;IDE中的文件的换行符号使用 Unix 格式,不要使用 Windows 格式。

    (10)【推荐】单个方法的总行数不超过80行。

    (11)【推荐】变量名不要在同一列上

    (12)【推荐】不同逻辑、不同语义、不同业务的代码之间插入一个空行来提升可读性。

4 OOP 规约

    (1)【强制】直接使用 类名 来访问 静态变量(类变量)和静态方法(类方法)。

    (2)【强制】所有覆写方法,必须加 @Override 注解。

    (3)【强制】相同业务含义,才使用 Java 的可变参数,避免使用 Object。

    (4)【强制】外部正在调用或者二方库依赖的接口,不允许修改方法签名;接口过时必须加 @Deprecated 注解,并清楚地说明采用的新接口或者新服务是什么。

    (5)【强制】不能使用过时的类或方法。

    (6)【强制】Object 的 equals 方法容易抛空指针异常,应使用常量或者确定有值的对象来调用 equals。

    (7)【强制】所有相同类型的包装类对象之间的值比较,全部使用 equals 方法。

    (8)关于基本数据类型与包装数据类型的使用标准如下:

            1)【强制】所有的 POJO 类属性使用包装数据类型。

            2)【强制】RPC 方法的返回值和参数使用包装类型。

            3)【推荐】所有的局部变量使用基本数据类型。

    (9)【强制】定义 DO / DTO / VO 等 POJO 类时,不要设定任何属性默认值。

    (10)【强制】序列化类新增属性时,不要修改 serialVersionUID 字段;如果完全不兼容升级,要修改 serialVersionUID 字段。

    (11)【强制】构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,放在 init 方法中。

    (12)【强制】POJO 类必须写 toString 方法,有继承类时,需要添加 super.toString()。

    (13)【强制】POJO 类中的 isXxx() 和 getXxx(),只能存在一个。

    (14)【推荐】使用索引访问 String#split 方法得到的数组时,需要做最后一个分隔符后有无内容的检查。

    (15)【推荐】当一个类有多个同名方法时,这些方法应该按顺序放置在一起,此规则优先于第(16)条规则。

    (16)【推荐】类内的方法定义的顺序:构造方法 > protected > public > private > getter / setter / toString。

    (17)【推荐】setter 方法中,参数名称与类成员变量名称一致。

    (18)【推荐】循环体内,字符串的连接方式,使用 StringBuilder#append 进行扩展。

    (19)【推荐】以下情况使用 final 声明类、成员变量、方法、本地变量:

            1)不允许被继承的类。

            2)不允许修改引用的域对象。

            3)不允许被重写的方法。

            4)不允许运行过程中重新赋值的局部变量。

            5)避免上下文重复使用同一个变量,使用 final 可以强制重新定义一个变量,方便更好地进行重构。

    (20)【推荐】慎用 Object#clone 来拷贝对象。(这是浅拷贝)

    (21)【推荐】类成员与方法访问控制从严:

            1)如果不允许外部直接通过 new 来创建对象,那么构造方法是 private。

            2)工具类不允许有 public 或 default 构造方法。

            3)类非 static 成员变量并且与子类共享,protected。

            4)类非 static 成员变量并且仅在本类使用,private。

            5)类 static 成员变量如果仅在本类使用,private。

            6)static 成员变量,考虑是否为 final。

            7)类成员方法只提供内部调用,private。

            8)类成员方法只对继承类公开,protected。

5 集合处理

    (1)【强制】关于 hashCode 和 equals 的处理:

            1)只要重写 equals, 就必须重写 hashCode。

            2)因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须重写这两个方法。

            3)如果自定义对象作为 Map 的键,重写 hashCode 和 equals。

    (2)【强制】ArrayList 的 subList 结果不可强转为 ArrayList,否则会抛出 ClassCastException,即 java.util.RandomAccessSubList cannot be cast to java.util.ArrayList。

    (3)【强制】在 subList 场景中,高度注意对原集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException。

    (4)【强制】使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一样,大小为 list.size() 的数组。

    (5)【强制】使用工具类 ArrayList.asList() 把数组转换成集合时,不能使用其修改集合相关的方法,它的 add / remove / clear 方法会抛出 UnsupportedOperationException 。

    (6)【强制】泛型通配符 < ? extends T > 来接收返回的数据,此写法的泛型集合不能使用 add 方法,<? super T> 不能使用 get 方法,作为接口调用赋值时易出错。

    (7)【强制】不要在foreach 循环里面进行元素的 remove / add 操作。remove 元素请使用 Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。

    (8)【强制】在 jdk7 版本,Comparator 实现类要满足以下三个条件,不然 Arrays.sort,Collections.sort 会抛 IllegalArgumentException 。

            1)x,y的比较结果和y,x的比较结果相反。

            2)x > y,y > z,则 x > z。

            3)x = y,则 x,z 比较的结果和 y,z 的比较结果相同。

    (9)【推荐】集合泛型定义时,在 jdk7 版本,使用 diamond 语法。

    (10)【推荐】集合初始化时,指定集合大小的初始值。

    (11)【推荐】使用 entrySet 遍历 Map 集合,而不是 keySet 方式。

    (12)【推荐】高度注意 Map 集合 K / V 能不能为 null 的情况。

    (13)【参考】合理利用好集合的有序性(sort)和稳定性(order),避免集合的无序性(unsort)和不稳定性(unorder)带来的负面影响。

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

6 并发处理

    (1)【强制】获取单例对象需要保证线程安全,其中的方法也要保证线程安全。

    (2)【强制】创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。

    (3)【强制】线程资源必须通过线程池提供,不允许在应用中自行显示创建线程。

    (4)【强制】线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

    (5)【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static,必须加锁,或者使用 DateUtils 工具类。

    (6)【强制】高并发时,同步调用应该去考量锁的性能损耗。无锁 > 块锁 > 方法锁;对象锁 > 类锁。

    (7)【强制】对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁。

    (8)【强制】并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库底层使用乐观锁,使用 version 作为更新依据。

    (9)【强制】多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其他任务便会终止运行,使用 ScheduleExecutorService 则没有这个问题。

    (10)【推荐】使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行至 await 方法,直到超时才返回结果。

    (11)【推荐】避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed 导致性能下降。

    (12)【推荐】在并发场景下,通过双重检查锁(double-checked locking)实现延迟初始化的优化隐患问题,推荐解决方案中较为简单一种,将目标声明为 volatile 类型。

    (13)【参考】volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。如果是 count++ 操作,使用如下实现:

            AtomicInteger count  = new AtomicInteger();

            count.addAndGet(1);

            JDK 8 推荐使用 LongAdder 对象,性能比 AtomicLong 好(减少乐观锁的重试次数)。

    (14)【参考】HashMap 在容量不够进行 resize 时由于高并发可能出现死链,导致 CPU 飙升,在开发过程中可以使用其他数据结构或加锁来规避此风险。

    (15)【参考】ThreadLocal 无法解决共享对象的更新问题,ThreadLocal 对象建议使用static 修饰。这个变量是针对一个线程内所有操作共享的,所以设置为静态变量,所有此类实例共享此静态变量,也就是说在类第一次使用时装载,只分配一块存储空间,所有此类的对象(只要是这个线程内定义的)都可以操控这个变量。

7 控制语句

    (1)【强制】在一个 switch 块内,每个 case 要么通过 break / return 终止,要么注释说明程序将继续执行到哪一个 case 为止;在一个 switch 块内,必须包含一个 default 语句并且放在最后,即使空代码。

    (2)【强制】在 if / else / for / while / do 语句中必须使用大括号。即使只有一行代码,避免采用单行模式。

    (3)【强制】在高并发场景中,避免使用 "等于" 判断作为中断或退出的条件。

    (4)【推荐】表达异常的分支时,少用 if - else 方式。

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

    (6)【推荐】循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)。

    (7)【推荐】避免采用取反逻辑运算符。

    (8)【推荐】接口入参保护,这张场景常见的是用作批量操作的接口。

    (9)【参考】下列情形,需要进行参数校验:

            1)调用频次低的方法。

            2)执行时间开销很大的方法。

            3)需要极高稳定性和可用性的方法。

            4)对外提供的开放接口,如 RPC / API / HTTP 接口。

            5)敏感权限入口。

    (10)【参考】下列情形,不需要进行参数校验:

            1)极有可能被循环调用的方法。但在方法说明里必须注明外部参数检查要求。

            2)底层调用频度比较高的方法。

            3)被声明成 private 的方法。

8 注释规约

    (1)【强制】类、类属性、类方法的注释必须使用 Javadoc 规范。

    (2)【强制】所有的抽象方法(包括接口中的方法)必须要用 Javadoc 注释,除了返回值、参数、异常说明外,还必须指出该方法做什么事情,实现什么功能。对子类的实现要求或注意事项。

    (3)【强制】所有的类都必须添加或者创建者和创建日期。

    (4)【强制】方法内部单行注释,使用 "//" 、在上方、独占一行。多行注释使用"/* */",注意与代码对齐。

    (5)【强制】所有的枚举类型字段必须要有注释说明每个数据项的用途。

    (6)【推荐】注释使用中文,专有名词、关键字保留英文即可。

    (7)【推荐】修改代码的同时,注释也要进行修改,尤其是参数、返回值、异常、核心逻辑。

    (8)【参考】谨慎注释掉代码。在上方详细说明(统一使用 /// 开头来说明),而不是简单的注释掉。如果无用,则删除。

    (9)【参考】对于注释的要求:

            1)准确反应设计思想和逻辑代码。

            2)描述业务含义,见其知道代码背后的信息。

    (10)【参考】好的命名、代码结构是自解释的,注释力求言简意赅,避免泛滥。

    (11)【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过扫描标记,经常清理此类标记。

            1)代办事项(TODO):(标记人,标记时间,[预计处理时间])。

            2)错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])。

9 其它

    (1)【强制】在使用正则表达式时,利用好器预编译功能(不要在方法体内才去定义 Pattern),可以有效加快正则匹配速度。

    (2)【强制】velocity 调用 POJO 类属性时,建议直接使用属性名取值即可,模板引擎会自动按规范调用 POJO 的 getXxx(),如果是 boolean 基本数据类型属性,会调用 isXxx()。如果是Boolean 包装类,会优先调用 getXxx()。

    (3)【强制】后台输送给页面的变量必须加 $!{var} —— 中间的感叹号。

    (4)【强制】注意 Math.random() 返回的是 double(0 ≤ x < 1)。

    (5)【强制】获取当前毫秒数 System.currentTimeMillis();而不是 new Date().getTime()。获取纳秒级时间值,使用System.nanoTime()。JDK8 使用 Instant 类。

    (6)【推荐】不要在视图模板中加入任何复杂的逻辑。

    (7)【推荐】任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存。

    (8)【推荐】及时清理不在使用的代码段或配置信息。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,718评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,683评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,207评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,755评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,862评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,050评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,136评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,882评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,330评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,651评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,789评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,477评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,135评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,864评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,099评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,598评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,697评论 2 351

推荐阅读更多精彩内容