1.命名规范

   1.类名 驼峰式命名规则 MarcoPolo

    2.方法名 小写开头&单词首字母大写 testMethod

    3.常量 大写单词,单词间使用_分割,含义明显 MAX_TICKET_COUNT

    4.抽象类 以Abstract / Base 开始,异常类使用Exception 结束 , 测试类使用 Test结束

    5.boolean 类型 , 变量不以 isXXX 命名

    6.包名统一英文单词单数形式 , 不使用缩写

    7.接口中不加修饰 ,public abstract 不写

   8.形容能力的接口使用 -able结尾

2.代码格式

   1.在贴近括号两边不出现空格,if / for / while / switch / do 在括号中表达逻辑时,必须加入空格。

    2.二目、三目运算符的左右两边都需要加一个空格 a > b a > b? c : d

    3.第二行相对第一行缩进4个空格,其他不缩进

    4.传递参数的时候需要用空格隔开

    5.不同的业务逻辑之间或者不同的语义之间插入一个空行。相同业务逻辑和语义之间不需要插入空行。

3.OOP规约

    1.访问类中静态方法 、属性值,直接通过类名.方法名 、类名.属性名

    2.过时的接口,使用@Deprecated 注解

   3.不能使用过时的类和方法

   4.常量或确定有值的对象调用equals方法 需要将常量写在前面,避免空指针 “test”.equals(str)

    5.包装类对象之间值得比较,全部使用equals方法比较

    6.POJO 类属性必须使用包装数据类型,RPC方法的返回值和参数必须使用包装数据类型

    7.所有的局部变量使用基本数据类型

   8.定义 DO / DTO / VO 等 POJO类 , 不要设定任何默认值

   9.POJO 类必须写 toString 方法

    10.字符串的连接方式,使用 StringBuilder 的 append 方法进行扩展

补充 内容(8):

    1、PO即persistant Object 持久对象: 在O/R 映射(即ORM-ObjectRelationMapping)中出现的概念,通常对应数据模型(数据库),是与数据库汇总的表想影射的java对象,最简单的PO就是对应数据库中某个表中的一条记录,多个记录则用PO的集合。PO中不应该包含任何对数据库的操作。

    2、DO即Domain Object 领域对象: 是从现实世界中抽象出来的有形或无形的业务实体。

    3、TO即Transfer Object数据传输对象: 不同应用程序之间传输的对象

    4、DTO即Data Transfer Object:数据传输对象: 泛指用于展示层与服务层之间的数据传输对象

    5、VO即value Object: 通常用于业务层之间的数据传递,和PO一样仅包含数据,但是抽象出的业务对象,可以和表对应,用new 关键字创建,GC回收

    6、BO即Business Object 业务对象: 主要是将业务逻辑封装为一个对象,这个对象可以包含一个或多个其他对象,如一个简历中包含教育经历、工作经历、社会关系等,可以将一个教育经历对应一个PO、工作经历对应一个PO、设计关系对应一个PO,然后简历一个对应简历的BO兑现处理简历,每个BO包含这个PO这样处理业务逻辑是,可以针对BO去处理。封装业务逻辑的java对象,通过调用DAO方法,结合PO,VO进行业务操作。

    7、POJO即Plain Ordinary Java Object: 简单无规则的java对性,即在一些O/R 映射工具中,能做到维护数据库表记录的PO完全是一个符合Java Bean规范的纯java对象

4.集合处理

    1.只要重写 equals ,就必须重写 hashCode. 如果自定义对象作为 Map 的键,那么必须重写 hashCode 和 equals 。

    2.ArrayList 的 subList (List)结果不可强转成 ArrayList ,否则会抛出 ClassCastException 异常 。

    3.Arrays asList() 把数组转换成集合时,不能使用其修改集合相关的方法,使用 add / remove / clear 方法会抛出 java.lang.UnsupportedOperationException异常,asList 的返回对象是一个 Arrays内部类,并没有实现集合的修改方法。

示例

int[] nums = new int[]{1,2,3,4,5};
     List<int[]> ints = Arrays.asList(nums);
     ints.add(new int[]{2});

Exception in thread "main" java.lang.UnsupportedOperationException
	at java.util.AbstractList.add(AbstractList.java:148)
	at java.util.AbstractList.add(AbstractList.java:108)
	at fatcats.top.Main.main(Main.java:261)

    4.泛型通配符<? extends T> 来接受返回的数据,此写法的泛型集合不能使用 add 方法,而<? super T> 不能使用 get 方法,作为接口调用赋值时容易出错。频繁往外读取内容,使用<? extends T> ;经常往里面插入数据,使用<? super T>

    5.不要在 foreach 循环中 进行增删改操作 (remove / add)操作. remove 元素使用 Iterator 方式,如果是并发操作,需要对 Iterator 对象加锁。

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

   7.使用 entrySet 遍历 Map 类集合KV,而不是 keySet 方式进行遍历。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。

集合类

Key

Value

Super

说明

HashTable

不允许为NULL

不允许为NULL

Dictionary

线程安全

ConcurrentHashMap

不允许为NULL

不允许为NULL

AbstractMap

锁分段技术(JDK8 CAS)

TreeMap

不允许为NULL

允许为NULL

AbstractMap

线程不安全

HashMap

允许为NULL

允许为NULL

AbstractMap

线程不安全

5.并发处理

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

    2.线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,更加明确线程池的运行规则,规避资源耗尽风险。

   3.SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量, 强行使用的话,需要加锁。JDK8 以上可以使用 DateTimeFirmatter 代替 ,它的线程是安全的。

    4. 多线程并行处理定时任务时,Timer 运行多个 TimerTask 时,只要其中之一没有捕获抛出的异常,其他的任务都会自动终止运行,可以使用 ScheduledExecutorService 解决。

    5.避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一个 seed 导致的性能下降。JDK7 后 可使用 ThreadLocalRandom .

   6.在并发场景下,通过双重检查锁,实现延迟初始化的优化问题隐患,推荐解决方案中较为简单的一种,将目标属性声明 volatile 型。

    7.volatile 解决多线程内容不可见问题对于一写多读,是可以解决变量同步问题。对于多写,同意是无法解决线程安全问题。对于 count++ 操作,使用 AtomicInteger 类实现,count.addAndGet(1);对于JDK8,使用LongAdder 对象。

   8.HashMap 在容量不够进行 resize 时,在高并发环境下可能出现死链 ,导致CPU 飙升,在开发过程中可以使用其他数据结构或加锁来规避此风险。

6.控制语句

    1.表达异常分支时,少用 if-else 方法,可以改写成return 返回

if(condition){
     ...
    return obj;
}

    2.方法的返回值可以为 null, 不强制返回空集合、空对象等,必须添加注释充分说明什么情况下会返回 null 值。调用方需要进行 null 判断,防止 NPE(NullPointerException 空指针) 问题。

    3.定义时区分 unchecked / checked 异常,避免直接抛出 new RuntimeException() ,更不允许抛出 Exception 或者 Throwable , 应使用有业务含义的自定义异常。

    4.应用中不可直接使用日志系统(Log4j 、Logback)中的API ,应该使用日志框架SLF4J中的API,使用门面模式的日志框架,便于维护。

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

 	/** logger */
    private static Logger logger = LoggerFactory.getLogger(A.class);

    5.避免重复打印日志,浪费磁盘控件,务必在log4j.xml中,设置 additivity = false。

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

7.单元测试

    1.单元测试应该是全自动执行的,并且非交互式的。

    2.测试框架通常是定期执行的,执行过程必须全自动化。

    3.单元测试中不允许使用 System.out 来进行验证,必须使用 assert 验证。(断言)

8.Mysql

    1.表达是否概念的字段,必须使用 is_xxx 方式命名,数据类型 unsigned tinyint (1 表示是,0表示否)

    2.主键索引名为 pk_字段名;唯一索引名为 uk_字段名;普通索引名则为 idx_字段名。

    3.小数类型为 decimal ,禁止使用 float 和 double。

    4.表必备三字段:id,gmt_create , gmt_modified

    5.表的命名最好是“业务名称_表的作用”

    6.超过三个表禁止 join。需要 join 的字段,数据类型必须绝对一致; 多表关联查询时,保证被关联的字段需要有索引。

    7.页面搜索严禁左模糊或者全模糊,使用搜索引擎来解决此问题。索引文件具有 B-tree 的最左前缀匹配特性,如果左边的值未确定,那么无法使用此索引。

    8.不要使用 count( 列名 ) 或 count( 常量 ) 来替代 count(*) , count() 是 SQL 92定义的标准统计行数的语法,跟数据库、NULL 、非NULL 无关。count( *) 会统计值为NULL的行,而 count(列名)不会统计此列为NULL值得行。

    9.count(distinct col)计算该列除NULL之外的不重复的行数,注意 count(distinct col1 ,col2) 如果其中一列全为NULL , 那么即使另一列有不同的值,也返回为0.

    10.当某一列的值全是NULL时,count(col) 的返回结果为0 ,但是 sum(col) 的返回结果为NULL ,因此使用 sum() 时需要注意 NPE 问题。可以使用 SELECT IF(ISNULL(SUM(g)),0,SUM(g)) FROM table;

    11.使用ISNULL() 判断是否为 NULL 值。NULL 与任何值的比较都为NULL。

   12.分页查询逻辑,若 count 为 0 应该直接返回,避免执行后面的分页语句。

   13.不得使用外键与级联,一切外键概念必须在应用层解决。

9.服务器

    1.高并发的服务器建议调小TCP协议的time_wait超时时间。操作系统默认是240秒才会关闭处于time_wait的链接,高并发下服务端会因为处于time_wait链接数太多,无法建立新连接,需要调小等待值。

在linux服务器上通过变更/etc/sysctl.conf 修改缺省值

net.ipv4.tcp_fin_timeout = 30

    2.调大服务器所支持最大文件的句柄数。主流操作系统将TCP/UDP链接采用与文件一样的连接方式管理,一个连接对应一个fd.

linux 默认 fd数为1024.并发数过大会导致 “open too many files"错误

   3.给JVM设置-XX:+HeapDumpOnOutOfMemoryError参数,让JVM碰到OOM输出Dump

    4.线上JVM的Xms初始堆大小和Xmx最大堆大小一样储存容量,避免GC调整给堆带来压力。

    5.服务器内重定向使用forward,外部重定向使用URL拼装工具生成,否则带来URL维护不一致问题。

10.二方库依赖

    1.线上应用不要依赖snapsho版本,不依赖是保证发布的幂等性。

    2.二方库的新增或者升级,保持除功能点之外的其他jar包仲裁结果不变。如果有改变,必须明确评估和验证,建议进行dependency:resolve前后信息对比,如果仲裁结果完全不一致,通过dependency:tree找出差异点,进行excludes排除jar包。

    3.二方库里可以定义枚举类型,参数可以使用枚举类型,但是接口返回值不允许使用枚举类型,或包含枚举类型的pojo。

    4.依赖于一个二方库时,必须定义一个统一的版本,避免版本号不一致。

11.应用分层

    1.在Dao层,无法用细粒度异常进行catch,所以使用catch(Exception e)方式,兵throw new DAOException(e) 不进行打印。

    在manager/service层进行捕获,并打印到日志中,service层讲日志输出到磁盘,web层跳转到友好界面。

12.ORM映射

    1.查询表的时候,一律不使用 * 作为查询字段列表,必须写明需要查询的字段。

    2.pojo属性不能加is ,数据库必须要加is_xxx,需要在mybatis生成器中将代码进行修改。

   3.sql.xml配置参数使用 #{} , 不要使用 ${} ,会发生SQL注入问题。

   4.不允许直接拿HashMap和HashTable作为查询结果集的输出。

    5.事务不要滥用,事务影响数据库的QPS,使用事务的地方需要考虑各方面的回滚。

13.SQL语句

    1.禁止使用存储过程,存储过程难以调试和扩展,更没有移植性。

    2.数据订正时,删除和修改记录,要先使用select 查询,避免出现误删除,确认无误避免出现错误删除。

    3.in操作能避免就避免,不能避免要控制in后边集合数量,控制在1000个之内。

    4.如果有全球化的需求,所有字符使用utf-8进行储存。

select length("轻松工作") // 返回12
select character_length("轻松工作") // 返回4

存储表情用utfmb4进行储存,注意它和utf-8的区别。

    5.不建议使用truncate。

14.索引规约

    1.业务上具有唯一特性的字段,即使多个字段的组合,也必须构建唯一索引。

    2.在varchar上创建索引,必须指明索引长度,没有必要对全字段建立索引,根据实际文本区分度决定索引长度即可。

count(distinct left(列名,索引长度)) /count(*)

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

索引 a_b_c where a = ? and b = ? order by c

    4.利用覆盖索引来进行查询,避免回表,能够建立索引的种类:主键索引、唯一索引、普通索引,而覆盖索引是一种查询的一种效果,用explain的结果,extra列会出现。

    5.利用延迟关联或者子查询优化差多分页场景。

    6.SQL的性能目标,至少要达到range级别,要求是ref级别,如果可以是consts最好consts单表中最多只能有一个匹配行,在优化阶段即可读取到数据。ref指的是使用普通索引。

    7.建立组合索引的时候,区分度最高的在最左面,如果 where a = ? and b = ? a列几乎接近于唯一值,那么只需要单建idx_a索引即可。

    存在非等号和等号混合判断条件时,在创建索引时,把等号条件的列前置。where a > ? and b = ? 即使a的区分度很高也需要b放在索引的最前面。

    8.防止字段类型不同锁造成的隐式转化,导致索引失效。

    9.创建索引要避免宁滥勿缺,认为查询需要创建一个索引,宁缺毋滥也不要,认为索引会消耗控件,拖慢更新和新增速度。地址唯一索引,认为唯一索引需要在应用层通过先插后插方式解决。

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

    11.单行表数据超过500万或者单行表容量超过2GB,才推荐分库分表

   12.合适的字符存储长度,不但节约数据库的表空间,节约索引的储存,更重要的是提升检索速度。

15.安全规则

    1.用户个人的页面必须进行权限校验。

   2.用户铭感数据禁止直接展示,必须脱敏,手机号隐藏中间4位。

   3.用户输入的sql参数严格禁止使用参数绑定或者metadata字段值限定,防止SQL注入,禁止字符串拼接SQL访问数据库。

    4.用户请求传入的参数必须进行有效的验证:否则导致

   1).page size过大内存溢出

    2).恶意order by 导致数据库查询慢

    3).任意重定向

    4).SQL注入

    5).反序列化注入

    6).正则输入源串拒绝服务ReDos

   5.禁止向HTML页面输出未经安全过滤或者未正确转义的用户数据。

   6.表单、AJAX提交必须执行CSRF安全过滤。

    CSRF跨站请求伪造是一类常见的编程漏洞,对于存在CSRF漏洞的应用网站,攻击者可以事先构造好URL,只要受害用户已访问,后台便在用户不知情的情况下对数据库进行修改。

    7.单元测试可以重复执行,不能受外面环境的影响,在设计时就要把SUT改为注入,在测试时使用spring这样的DI框架注入一个本地实现。

16.异常处理

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

    2.有try块放到事务代码中,catch后,需要事务回滚,一定注意手动回滚。

    3.不能在finally中使用return,finally块中的return返回方法后结束执行,不会再执行try中return语句。

    4.方法的返回值可以为null,不强制返回空集合和空对象,必须添加注释说明什么情况下返回为空。

17.其他

    1.在使用正则表达式时,要利用预编译,加快正则匹配速度,定义正则的时候不要在方法体内进行。

    2.volocity调用POJO类属性的时候,使用属性名取名即可,模板引擎会自动按照规约调用Pojo的getXxx(),如果是boolean基本数据类型 ,调用isXxx(),如果是Boolean包装对象,调用getXxx()方法。

    3.后台输出给页面的变量必须加!var中间感叹号,如果var = null 不存在,则{var}不会显示到页面上

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

    5.对于暂时被注释掉,后续可能恢复使用的代码片段,统一使用 ///来说明注释掉代码的理由。