Java开发手册(泰山版)读书笔记

一、 编程规约
(一) 命名风格
【强制】类名使用UpperCamelCase风格,但以下情形例外:DO / BO / DTO / VO / AO / PO / UID等。
正例:ForceCode / UserDO
反例:forcecode / UserDo

  1. 【强制】抽象类命名使用Abstract或Base开头;异常类命名使用Exception 结尾;测试类 命名以它要测试的类的名称开始,以Test结尾

  2. 【强制】POJO类中的任何布尔类型的变量,都不要加is前缀,否则部分框架解析会引起序列 化错误。
    说明:在本文MySQL 规约中的建表约定第一条,表达是与否的值采用 is_xxx的命名方式,所以,需要在 设置从 is_xxx到xxx的映射关系。

9.【强制】包名统一使用小写,点分隔符之间有且仅有一个自然语义的英语单词。包名统一使用 单数形式,但是类名如果有复数含义,类名可以使用复数形式。
正例:应用工具类包名为 com.alibaba.ei.kunlun.aap.util、类名为 MessageUtils

  1. 【强制】避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名, 使可读性降低。

  2. 【推荐】在常量与变量的命名时,表示类型的名词放在词尾,以提升辨识度。 正例:startTime / workQueue / nameList / TERMINATED_THREAD_COUNT 反例:startedAt / QueueOfWork / listName / COUNT_TERMINATED_THREAD

  3. 【推荐】如果模块、接口、类、方法使用了设计模式,在命名时需体现出具体模式。 说明:将设计模式体现在名字中,有利于阅读者快速理解架构设计理念。
    正例:
    public class OrderFactory;
    public class LoginProxy;
    public class ResourceObserver;

  4. 接口和实现类的命名有两套规则:
    1)【强制】对于 Service和 DAO类,基于 SOA 的理念,暴露出来的服务一定是接口,内部的实现类用 Impl的后缀与接口区别。 正例:CacheServiceImpl 实现 CacheService 接口。 2)【推荐】如果是形容能力的接口名称,取对应的形容词为接口名(通常是–able 的形容词)。 正例:AbstractTranslator 实现 Translatable 接口。

  5. 【参考】各层命名规约:
    A) Service/DAO 层方法命名规约
    1) 获取单个对象的方法用 get 做前缀。
    2) 获取多个对象的方法用 list 做前缀,复数结尾,如:listObjects。
    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。

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

  1. 【推荐】常量的复用层次有五层:
    跨应用共享常量、应用内共享常量、子工程内共享常量、包 内共享常量、类内共享常量。 1) 跨应用共享常量:放置在二方库中,通常是 client.jar 中的constant 目录下。
    2) 应用内共享常量:放置在一方库中,通常是子模块中的constant 目录下。
    3) 子工程内部共享常量:即在当前子工程的 constant 目录下。
    4) 包内共享常量:即在当前包下单独的 constant 目录下。
    5) 类内共享常量:直接在类内部 private static final 定义。

(三) 代码格式

  1. 【强制】如果是大括号内为空,则简洁地写成{}即可

  2. 【强制】左小括号和右边相邻字符之间不出现空格;右小括号和左边相邻字符之间也不出现空 格;而左大括号前需要加空格
    反例:if (空格a == b 空格)

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

  4. 【强制】任何二目、三目运算符的左右两边都需要加一个空格。 说明:包括赋值运算符=、逻辑运算符&&、加减乘除符号等。

  5. 【强制】采用4个空格缩进,禁止使用tab字符。

  6. 【强制】单行字符数限制不超过120个,超出需要换行,换行时遵循如下原则:
    1)第二行相对第一行缩进4 个空格,从第三行开始,不再继续缩进,参考示例。
    2)运算符与下文一起换行。
    3)方法调用的点符号与下文一起换行。
    4)方法调用中的多个参数需要换行时,在逗号后进行。
    5)在括号前不要换行,

  7. 【强制】方法参数在定义和传入时,多个参数逗号后边必须加空格。

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

  9. 【推荐】单个方法的总行数不超过80行。
    正例:代码逻辑分清红花和绿叶,个性和共性,绿叶逻辑单独出来成为额外方法,使主干代码更加清晰;共 性逻辑抽取成为共性方法,便于复用和维护。

  10. 【推荐】不同逻辑、不同语义、不同业务的代码之间插入一个空行分隔开来以提升可读性。 说明:任何情形,没有必要插入多个空行进行隔开。

(四) OOP规约
3. 【强制】相同参数类型,相同业务含义,才可以使用 Java的可变参数,避免使用 Object。 说明:可变参数必须放置在参数列表的最后。
正例:public List listUsers(String type, Long… ids) {…}

  1. 【强制】所有整型包装类对象之间值的比较,全部使用equals方法比较
    在-128至127 之间的问题

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

  3. 【强制】定义数据对象DO类时,属性类型要与数据库字段类型相匹配。 正例:数据库字段的 bigint 必须与类属性的 Long 类型相对应。 反例:某个案例的数据库表id 字段定义类型bigint unsigned,实际类对象属性为 Integer,随着 id 越来 越大,超过 Integer 的表示范围而溢出成为负数。

  4. 【强制】禁止使用构造方法 BigDecimal(double)的方式把double值转化为 BigDecimal对象。

  5. 关于基本数据类型与包装数据类型的使用标准如下:
    1) 【强制】所有的 POJO类属性必须使用包装数据类型。
    2) 【强制】RPC 方法的返回值和参数必须使用包装数据类型。
    3) 【推荐】所有的局部变量使用基本数据类型。
    说明:POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何 NPE问题,或 者入库检查,都由使用者来保证。
    正例:数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 NPE 风险。 反例:某业务的交易报表上显示成交总额涨跌情况,即正负 x%,x为基本数据类型,调用的 RPC 服务,调 用不成功时,返回的是默认值,页面显示为 0%,这是不合理的,应该显示成中划线-。所以包装数据类型 的 null值,能够表示额外的信息,如:远程调用失败,异常退出。

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

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

  8. 【推荐】使用索引访问用String的split方法得到的数组时,需做最后一个分隔符后有无内容 的检查,否则会有抛IndexOutOfBoundsException 的风险。

  9. 【推荐】当一个类有多个构造方法,或者多个同名方法,这些方法应该按顺序放置在一起,便 于阅读,此条规则优先于下一条。

  10. 【推荐】 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter 方法。 说明:公有方法是类的调用者和维护者最关心的方法,首屏展示最好;保护方法虽然只是子类关心,也可 能是“模板设计模式”下的核心方法;

  11. 【推荐】setter方法中,参数名称与类成员变量名称一致,this.成员名 = 参数名。在 getter/setter方法中,不要增加业务逻辑,增加排查问题的难度。

  12. 【推荐】类成员与方法访问控制从严:
    1) 如果不允许外部直接通过 new来创建对象,那么构造方法必须是 private。
    2) 工具类不允许有 public或default 构造方法。
    3) 类非static 成员变量并且与子类共享,必须是 protected。
    4) 类非static 成员变量并且仅在本类使用,必须是private。
    5) 类static 成员变量如果仅在本类使用,必须是 private。
    6) 若是static 成员变量,考虑是否为final。
    7) 类成员方法只供类内部调用,必须是 private。
    8) 类成员方法只对继承类公开,那么限制为 protected

(五) 日期时间

  1. 【强制】日期格式化时,传入pattern 中表示年份统一使用小写的y
    说明:日期格式化时,yyyy 表示当天所在的年,而大写的 YYYY代表是 week in which year(JDK7 之后 引入的概念),意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的 YYYY 就是下一年。
    正例:表示日期和时间的格式如下所示:
    new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”)

  2. 【强制】在日期格式中分清楚大写的M和小写的m,大写的H和小写的h分别指代的意义。
    说明:日期格式中的这两对字母表意如下:
    1) 表示月份是大写的 M;
    2) 表示分钟则是小写的 m;
    3) 24 小时制的是大写的H;
    4) 12 小时制的则是小写的 h。

  3. 【强制】获取当前毫秒数:System.currentTimeMillis(); 而不是new Date().getTime()。 说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime 的方式。在 JDK8 中,针对统计时间 等场景,推荐使用 Instant 类。

  4. 【强制】不允许在程序任何地方中使用:1)java.sql.Date 2)java.sql.Time 3) java.sql.Timestamp。

  5. 【推荐】使用枚举值来指代月份。如果使用数字,注意Date,Calendar等日期相关类的月份 month 取值在0-11之间。
    说明:参考JDK 原生注释,Month value is 0-based. e.g., 0 for January.
    正例: Calendar.JANUARY,Calendar.FEBRUARY,Calendar.MARCH 等来指代相应月份来进行传参或 比较。

(六) 集合处理
2. 【强制】判断所有集合内部的元素是否为空,使用isEmpty()方法,而不是size()==0的方式

  1. 【强制】在使用java.util.stream.Collectors类的 toMap()方法转为Map集合时,一定要使 用含有参数类型为BinaryOperator,参数名为mergeFunction 的方法,否则当出现相同key 值时会抛出IllegalStateException 异常

  2. 【强制】在使用java.util.stream.Collectors类的 toMap()方法转为Map集合时,一定要注 意当value为null时会抛NPE异常。

  3. 【强制】使用集合转数组的方法,必须使用集合的toArray(T[] array),传入的是类型完全一 致、长度为0的空数组。
    String[] array = list.toArray(new String[0]);

  4. 【强制】泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add方法, 而<? super T>不能使用get方法,两者在接口调用赋值的场景中容易出错。 说明:扩展说一下 PECS(Producer Extends Consumer Super)原则:第一、频繁往外读取内容的,适合用 <? extends T>。第二、经常往里插入的,适合用<? super T>

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

  6. 【推荐】使用 entrySet遍历 Map类集合 KV,而不是 keySet方式进行遍历。 说明:keySet 其实是遍历了2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出key所对应的 value。而 entrySet 只是遍历了一次就把 key和value都放到了entry中,效率更高。如果是 JDK8,使用 Map.forEach 方法。

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

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

  2. 【强制】线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,

  3. 【强制】SimpleDateFormat 是线程不安全的类,一般不要定义为 static变量,如果定义为 static, 必须加锁,或者使用 DateUtils工具类。
    说明:如果是JDK8 的应用,可以使用 Instant 代替 Date,LocalDateTime 代替 Calendar, DateTimeFormatter 代替SimpleDateFormat,官方给出的解释:simple beautiful strong immutable thread-safe。

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

  1. 【强制】在使用阻塞等待获取锁的方式中,必须在try代码块之外,并且在加锁方法与try代 码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在 finally中无法解锁。 说明一:如果在 lock 方法与 try代码块之间的方法调用抛出异常,那么无法解锁,造成其它线程无法成功 获取锁。
    说明二:如果lock 方法在try代码块之内,可能由于其它方法抛出异常,导致在 finally代码块中,unlock 对未加锁的对象解锁,它会调用 AQS的tryRelease 方法(取决于具体实现类),抛出 IllegalMonitorStateException 异常。
    说明三:在Lock 对象的lock 方法实现中可能抛出 unchecked 异常,产生的后果与说明二相同。 正例:
    Lock lock = new XxxLock();
    // …
    lock.lock();
    try {
    doSomething();
    doOthers(); }
    finally {
    lock.unlock(); }

  2. 【推荐】资金相关的金融敏感信息,使用悲观锁策略。

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

  4. 【推荐】通过双重检查锁推荐解决方案中将目标属性声明为 volatile型

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

(八) 控制语句

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

  2. 【强制】在 if/else/for/while/do语句中必须使用大括号。 说明:即使只有一行代码

  3. 【强制】在高并发场景中,避免使用”等于”判断作为中断或退出的条件。 说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件 来代替。

  4. 【推荐】表达异常的分支时,少用 if-else 方式,这种方式可以改写成:
    可以使用卫语句, 多个if 替代if-else

8.将复杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。
final boolean existed = (file.open(fileName, “w”) != null) && (…) || (…);
if (existed) {

  1. 【推荐】避免采用取反逻辑运算符。 说明:取反逻辑不利于快速理解,并且取反逻辑写法必然存在对应的正向逻辑写法。 正例:使用if (x < 628) 来表达 x 小于628。 反例:使用if (!(x >= 628)) 来表达 x 小于 628

(九) 注释规约
3. 【强制】所有的类都必须添加创建者和创建日期。
11. 【参考】好的命名、代码结构是自解释的

  1. 【参考】特殊注释标记,请注明标记人与标记时间。注意及时处理这些标记,通过标记扫描, 经常清理此类标记。线上故障有时候就是来源于这些标记处的代码。
    1) 待办事宜(TODO):(标记人,标记时间,[预计处理时间])

2) 错误,不能工作(FIXME):(标记人,标记时间,[预计处理时间])
在注释中用 FIXME标记某代码是错误的,而且不能工作,需要及时纠正的情况。

(十) 其它

  1. 【强制】在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度。 说明:不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”);

2 【强制】避免用Apache Beanutils进行属性的copy。 说明:Apache BeanUtils性能较差,可以使用其他方案比如 Spring BeanUtils, Cglib BeanCopier,注意 均是浅拷贝

  1. 【强制】注意 Math.random() 这个方法返回是 double类型
    直接使用 Random对象的 nextInt或者 nextLong方法

  2. 【推荐】及时清理不再使用的代码段或配置信息。
    正例:对于暂时被注释掉,后续可能恢复使用的代码片断,在注释代码上方,统一规定使用三个斜杠(///) 来说明注释掉代码的理由。如:
    /// 业务方通知活动暂停
    // Business business = new Business();
    // business.active(); System.out.println(“it’s finished”); }

二、异常日志
(一) 错误码
(二) 异常处理
7. 【强制】不要在finally块中使用return。 说明:try块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally块中的语句,如果此处存 在 return 语句,则在此直接返回,无情丢弃掉try块中的返回点。

(三) 日志规约

  1. 【强制】应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架 (SLF4J、JCL–Jakarta Commons Logging)中的 API,使用门面模式的日志框架,有利于维护和 各个类的日志处理方式统一。

  2. 【强制】在日志输出时,字符串变量之间的拼接使用占位符的方式。
    正例:logger.debug(“Processing trade with id: {} and symbol: {}”, id, symbol);

  3. 【强制】生产环境禁止直接使用System.out 或System.err 输出日志或使用 e.printStackTrace()打印异常堆栈。

  4. 【强制】异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过 关键字 throws往上抛出。

三、单元测试
2. 【强制】单元测试应该是全自动执行的,并且非交互式的。测试中不准使用 System.out来进行人肉验证,必须使用 assert来验证。

四、安全规约

五、MySQL数据库
(一) 建表规约

  1. 【强制】表达是与否概念的字段,必须使用is_xxx的方式命名,数据类型是unsigned tinyint (1表示是,0表示否)。 说明:任何字段如果为非负数,必须是 unsigned。

  2. 【强制】表名、字段名必须使用小写字母或数字,禁止出现数字开头,禁止两个下划线中间只 出现数字。
    说明:MySQL 在Windows下不区分大小写,但在 Linux下默认是区分大小写。因此,数据库名、表名、 字段名,都不允许出现任何大写字母,避免节外生枝。

  3. 【强制】表名不使用复数名词。对应于 DO类名也是单数形式,符合 表达习惯。

  4. 【强制】禁用保留字,如desc、range、match、delayed等

  5. 【强制】主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。 说明:pk_ 即primary key;uk_ 即 unique key;idx_ 即index的简称

  6. 【强制】小数类型为decimal,禁止使用float和double。

  7. 【强制】varchar是可变长字符串,不预先分配存储空间,长度不要超过5000,如果存储长度 大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效 率。

  8. 【强制】表必备三字段:id, gmt_create, gmt_modified

  9. 【推荐】表的命名最好是遵循“业务名称_表的作用”。

  10. 【推荐】单表行数超过500万行或者单表容量超过2GB,才推荐进行分库分表。 说明:如果预计三年后的数据量根本达不到这个级别,请不要在创建表时就分库分表。

  11. 【参考】合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索 速度。 正例:无符号值可以避免误存负数,且扩大了表示范围。

(二) 索引规约

  1. 【强制】业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引。

  2. 【强制】在 varchar字段上建立索引时,必须指定索引长度,

  3. 【强制】页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决

  4. 【推荐】如果有order by的场景,请注意利用索引的有序性。order by 最后的字段是组合索 引的一部分,并且放在索引组合顺序的最后,避免出现file_sort的情况,影响查询性能

  5. 【推荐】SQL 性能优化的目标:至少要达到 range 级别,要求是 ref级别,如果可以是 consts 最好。

(三) SQL语句

  1. 【强制】不要使用count(列名)或count(常量)来替代count(*),

  2. 【强制】不得使用外键与级联,一切外键概念必须在应用层解决

  3. 【强制】对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或 表名)进行限定

  4. 【推荐】SQL语句中表的别名前加as,并且以t1、t2、t3、…的顺序依次命名。 说明:1)别名可以是表的简称,或者是根据表出现的顺序,以 t1、t2、t3 的方式命名。2)别名前加 as 使别名更容易识别

(四) ORM映射
2. 【强制】POJO类的布尔属性不能加is,而数据库字段必须加is_,要求在resultMap中进行 字段与属性之间的映射。

  1. 【强制】不要用resultClass当返回参数,即使所有类属性名与数据库字段一一对应,也需要 定义;反过来,每一个表也必然有一个与之对应。

  2. 【强制】不允许直接拿 HashMap与 Hashtable作为查询结果集的输出。

  3. 【强制】更新数据表记录时,必须同时更新记录对应的gmt_modified 字段值为当前时间。

  4. 【参考】@Transactional 事务不要滥用。事务会影响数据库的QPS,另外使用事务的地方需 要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等。

六、工程结构
(一) 应用分层
(二) 二方库依赖
(三) 服务器
3. 【推荐】给JVM环境参数设置-XX:+HeapDumpOnOutOfMemoryError参数,让JVM碰到OOM 场景时输出dump信息。

  1. 【推荐】在线上生产环境,JVM的 Xms和 Xmx设置一样大小的内存容量,避免在 GC 后调整 堆大小带来的压力。

七、设计规约
3. 【强制】如果某个业务对象的状态超过3个,使用状态图来表达并且明确状态变化的各个触发 条件。

附2:专有名词解释
11. 一方库: 本工程内部子项目模块依赖的库(jar 包)。
12. 二方库: 公司内部发布到中央仓库,可供公司内部其它应用依赖的库(jar 包)。
13. 三方库: 公司之外的开源库(jar 包)。

附3:错误码列表
见书籍

  • 20
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值