java 开发手册
2022-07-09 11:19:50 59 举报
AI智能生成
登录查看完整内容
java 开发手册
作者其他创作
大纲/内容
任何货币金额,均以最小货币单位且整型类型来进行存储
指定一个误差范围,两个浮点数的差值在此范围之内,则认为是相等的
使用 BigDecimal 来定义值,再进行浮点数的运算操作
浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用 equals来判断
equals()方法会比较值和精度(1.0 与 1.00 返回结果为 false),而 compareTo()则会忽略精度
BigDecimal 的等值比较应使用 compareTo()方法,而不是 equals()方法
数据库字段的 bigint 必须与类属性的 Long 类型相对应
定义数据对象 DO 类时,属性类型要与数据库字段类型相匹配
所有的 POJO 类属性必须使用包装数据类型
RPC 方法的返回值和参数必须使用包装数据类型
所有的局部变量使用基本数据类型
基本数据类型与包装数据类型的使用标准
定义 DO/DTO/VO 等 POJO 类时,不要设定任何属性默认值
对象 clone 方法默认是浅拷贝,若想实现深拷贝,需覆写 clone 方法实现域对象的深度遍历式拷贝
慎用 Object 的 clone 方法来拷贝对象
oop规范
只要覆写 equals,就必须覆写 hashCode
因为 Set 存储的是不重复的对象,依据 hashCode 和 equals 进行判断,所以 Set 存储的对象必须覆写这两种方法
如果自定义对象作为 Map 的键,那么必须覆写 hashCode 和 equals
于 hashCode 和 equals 的处理,遵循规则
判断所有集合内部的元素是否为空,使用 isEmpty()方法,而不是 size()==0 的方式
ArrayList 的 subList 结果不可强转成 ArrayList,否则会抛出 ClassCastException 异 常:java.util.RandomAccessSubList cannot be cast to java.util.ArrayLis
使用 Map 的方法 keySet()/values()/entrySet()返回集合对象时,不可以对其进行添加元素操作,否则会抛出 UnsupportedOperationException 异常
Collections 类返回的对象,如:emptyList()/singletonList()等都是 immutable list,不可对其进行添加或者删除元素的操作
在 subList 场景中,高度注意对父集合元素的增加或删除,均会导致子列表的遍历、增加、删除产生 ConcurrentModificationException 异常
list.toArray(new String[0])
使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。
在使用 Collection 接口任何实现类的 addAll()方法时,都要对输入的集合参数进行NPE 判断
使用工具类 Arrays.asList()把数组转换成集合时,不能使用其修改集合相关的方法,它的 add/remove/clear 方法会抛出 UnsupportedOperationException 异常
频繁往外读取内容的,适合用<? extends T>。经常往里插入的,适合用<? super T>
泛型通配符<? extends T>来接收返回的数据,此写法的泛型集合不能使用 add 方法, 而<? super T>不能使用 get 方法,两者在接口调用赋值的场景中容易出错
在无泛型限制定义的集合赋值给泛型限制的集合时,在使用集合元素时,需要进行instanceof 判断,避免抛出 ClassCastException 异常
集合处理
获取单例对象需要保证线程安全,其中的方法也要保证线程安全
SimpleDateFormat 是线程不安全的类,一般不要定义为 static 变量,如果定义为 static,必须加锁,或者使用 DateUtils 工具类
必须回收自定义的 ThreadLocal 变量,尤其在线程池场景下,线程经常会被复用,如果不清理自定义的 ThreadLocal 变量,可能会影响后续业务逻辑和造成内存泄露等问题。尽量在代理中使用 try-finally 块进行回收
明:尽可能使加锁的代码块工作量尽可能的小,避免在锁代码块中调用 RPC 方法
高并发时,同步调用应该去考量锁的性能损耗。能用无锁数据结构,就不要用锁;能锁区块,就不要锁整个方法体;能用对象锁,就不要用类锁
对多个资源、数据库表、对象同时加锁时,需要保持一致的加锁顺序,否则可能会造成死锁
在使用阻塞等待获取锁的方式中,必须在 try 代码块之外,并且在加锁方法与 try 代码块之间没有任何可能抛出异常的方法调用,避免加锁成功后,在 finally 中无法解锁
在使用尝试机制来获取锁的方式中,进入业务代码块之前,必须先判断当前线程是否持有锁。锁的释放规则与锁的阻塞等待方式相同。
:如果每次访问冲突概率小于 20%,推荐使用乐观锁,否则使用悲观锁。乐观锁的重试次数不得小于3 次。
并发修改同一记录时,避免更新丢失,需要加锁。要么在应用层加锁,要么在缓存加锁,要么在数据库层使用乐观锁,使用 version 作为更新依据
只捕获InterruptedException异常,其他异常会导致线程退出,进而导致任务无法执行
多线程并行处理定时任务时,Timer 运行多个 TimeTask 时,只要其中之一没有捕获抛出的异常,其它任务便会自动终止运行,使用 ScheduledExecutorService 则没有这个问题
乐观锁在获得锁的同时已经完成了更新操作,校验逻辑容易出现漏洞,另外,乐观锁对冲突的解决策略有较复杂的要求,处理不当容易造成系统压力或数据异常,所以资金相关的金融敏感信息不建议使用乐观锁更新。
资金相关的金融敏感信息,使用悲观锁策略
使用 CountDownLatch 进行异步转同步操作,每个线程退出前必须调用 countDown 方法,线程执行代码注意 catch 异常,确保 countDown 方法被执行到,避免主线程无法执行至await 方法,直到超时才返回结果
避免 Random 实例被多线程使用,虽然共享该实例是线程安全的,但会因竞争同一 seed导致的性能下降
通过双重检查锁(double-checked locking)(在并发场景下)存在延迟初始化的优化问题隐患(可参考 The \"Double-Checked Locking is Broken\" Declaration),推荐解决方案中较为简单一种(适用于 JDK5 及以上版本),将目标属性声明为 volatile 型
volatile 解决多线程内存不可见问题。对于一写多读,是可以解决变量同步问题,但是如果多写,同样无法解决线程安全问题。
span style=\
如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件来代替。
在高并发场景中,避免使用”等于”判断作为中断或退出的条件
并发处理
当 switch 括号内的变量类型为 String 并且此变量为外部参数时,必须先进行 null判断
表达式 1 或表达式 2 的值只要有一个是原始类型
表达式 1 或表达式 2 的值的类型不一致,会强制拆箱升级成表示范围更大的那个类型
三目运算符 condition? 表达式 1 : 表达式 2 中,高度注意表达式 1 和 2 在类型对齐时,可能抛出因自动拆箱导致的 NPE 异常
循环体中的语句要考量性能,以下操作尽量移至循环体外处理,如定义对象、变量、获取数据库连接,进行不必要的 try-catch 操作(这个 try-catch 是否可以移至循环体外)
公开接口需要进行入参保护,尤其是批量操作的接口
控制语句
前后端数据列表相关的接口返回,如果为空,则返回空数组[]或空集合{}
对于需要使用超大整数的场景,服务端一律使用 String 字符串类型返回,禁止使用Long 类型
HTTP 请求通过 body 传递内容时,必须控制长度,超出最大长度后,后端解析会出错
HTTP 请求通过 URL 传递参数时,不能超过 2048 字节
前后端规约
不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”)
在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度
任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存
其他
编程规范
事务场景中,抛出异常被 catch 后,如果需要回滚,一定要注意手动回滚事务
在调用 RPC、二方包、或动态生成类的相关方法时,捕捉异常必须使用 Throwable类来进行拦截
方法的返回值可以为 null,不强制返回空集合,或者空对象等,必须添加注释充分说明什么情况下会返回 null 值
异常
logger.debug(\"Processing trade with id: {} and symbol: {}\
在日志输出时,字符串变量之间的拼接使用占位符的方式。
对于 trace/debug/info 级别的日志输出,必须进行日志级别的开关判断
日志打印时禁止直接用 JSON 工具将对象转换成 String。
日志规约
保持单元测试的独立性。为了保证单元测试稳定可靠且便于维护,单元测试用例之间决不能互相调用,也不能依赖执行的先后次序
单元测试是可以重复执行的,不能受到外界环境的影响
对于单元测试,要保证测试粒度足够小,有助于精确定位问题。单测粒度至多是类级别,一般是方法级别
和数据库相关的单元测试,可以设定自动回滚机制,不给数据库造成脏数据。或者对单元测试产生的数据有明确的前后缀标识。
单元测试
用户输入的 SQL 参数严格使用参数绑定或者 METADATA 字段值限定,防止 SQL 注入,禁止字符串拼接 SQL 访问数据库
用户请求传入的任何参数必须做有效性验证
安全规约
表达是与否概念的字段,必须使用 is_xxx 的方式命名,数据类型是 unsigned tinyint(1 表示是,0 表示否)
小数类型为 decimal,禁止使用 float 和 double。
如果存储的字符串长度几乎相等,使用 char 定长字符串类型
varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
不是频繁修改的字段
不是唯一索引的字段
不是 varchar 超长字段,更不能是 text 字段
字段允许适当冗余,以提高查询性能,但必须考虑数据一致
单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表
合适的字符存储长度,不但节约数据库表空间、节约索引存储,更重要的是提升检索速度。
建表规约
业务上具有唯一特性的字段,即使是组合字段,也必须建成唯一索引
超过三个表禁止 join。需要 join 的字段,数据类型保持绝对一致;多表关联查询时,保证被关联的字段需要有索引
在 varchar 字段上建立索引时,必须指定索引长度,没必要对全字段建立索引,根据实际文本区分度决定索引长度
页面搜索严禁左模糊或者全模糊,如果需要请走搜索引擎来解决
如果有 order by 的场景,请注意利用索引的有序性。order by 最后的字段是组合索引的一部分,并且放在索引组合顺序的最后,避免出现 file_sort 的情况,影响查询性能
利用覆盖索引来进行查询操作,避免回表
利用延迟关联或者子查询优化超多分页场景
SQL 性能优化的目标:至少要达到 range 级别,要求是 ref 级别,如果可以是 consts最好
建组合索引的时候,区分度最高的在最左边
防止因字段类型不同造成的隐式转换,导致索引失效
索引规约
当某一列的值全是 NULL 时,count(col)的返回结果为 0,但 sum(col)的返回结果为NULL,因此使用 sum()时需注意 NPE 问题
使用 ISNULL()来判断是否为 NULL 值
代码中写分页查询逻辑时,若 count 为 0 应直接返回,避免执行后面的分页语句
数据订正(特别是删除或修改记录操作)时,要先 select,避免出现误删除,确认无误才能执行更新语句
对于数据库中表记录的查询和变更,只要涉及多个表,都需要在列名前加表的别名(或表名)进行限定。
SQL 语句中表的别名前加 as,并且以 t1、t2、t3、...的顺序依次命名。
in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控制在 1000 个之内
TRUNCATE TABLE 比 DELETE 速度快,且使用的系统和事务日志资源少,但 TRUNCATE无事务且不触发 trigger,有可能造成事故,故不建议在开发代码中使用此语句
sql语句
在表查询中,一律不要使用 * 作为查询的字段列表,需要哪些字段必须明确写明
不要用 resultClass 当返回参数,即使所有类属性名与数据库字段一一对应,也需要定义<resultMap>;反过来,每一个表也必然有一个<resultMap>与之对应
不允许直接拿 HashMap 与 Hashtable 作为查询结果集的输出
@Transactional 事务不要滥用。事务会影响数据库的 QPS,另外使用事务的地方需要考虑各方面的回滚方案,包括缓存回滚、搜索引擎回滚、消息补偿、统计修正等
orm映射
mysql数据库
给 JVM 环境参数设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到 OOM场景时输出 dump 信息
服务器
在线上生产环境,JVM 的 Xms 和 Xmx 设置一样大小的内存容量,避免在 GC 后调整堆大小带来的压力。
工程结构
存储方案和底层数据结构的设计获得评审一致通过,并沉淀成为文档
需求分析与系统设计在考虑主干功能的同时,需要充分评估异常流程与业务边界
类在设计与实现时要符合单一原则
谨慎使用继承的方式来进行扩展,优先使用聚合/组合的方式来实现
系统设计阶段,根据依赖倒置原则,尽量依赖抽象类与接口,有利于扩展与维护
系统设计阶段,注意对扩展开放,对修改闭合
可扩展性的本质是找到系统的变化点,并隔离变化点
设计规约
java开发手册
0 条评论
回复 删除
下一页