JAVA开发手册摘录

编程规约

命名风格

  1. 类命名用大驼峰,缩写除外;
  2. 方法名、参数名、成员变量、局部变量命名用小驼峰;
  3. 常量和枚举成员名称全部大写,用下划线_隔开;
  4. 抽象类用Abstract或Base开头;
  5. 异常类用Exception结尾;
  6. 测试类用Test结尾;
  7. POJO类中布尔变量不适用is前缀;
  8. 包名小写,点分隔符中仅有一个(单数)单词,类名可以用复数;
  9. 命名时将表示类型的名词放在词尾:startTime/nameList/workQueue
  10. 命名时在词尾体现具体设计模式(如果使用的话)
  11. 接口类方法和属性不加任何修饰符(包括public);
  12. Service和DAO类,实现类使用接口名加Impl后缀命名;

常量定义

  1. 不允许未经定义的的常量直接出线;
  2. long类型赋值时使用大写L而非小写l
  3. 按常量功能进行归类,分开维护;

代码格式

  1. 左大括号前空格但不换行,后换行;右大括号在后续有else等代码时不换行,否则前后都换行;
  2. 保留字和运算符需要空格其余内容;
  3. 注释双斜线和注释内容间有且只有一个空格;
  4. 单行字符小于120个,超出则换行,换行后须于第一行缩进4个空格(或设置1个TAB为4个空格),.,需要作为换行排头(如果存在的话);
  5. 参数列表,分隔符后需加空格;

OOP规约

  1. 只允许通过类名而非类对象来访问此类的静态成员;
  2. 覆写方法必须用@Override注解;
  3. 外部调用或二方库依赖的接口不允许修改方法签名;
  4. 接口过时必须用@Deprecated注解,并表明新接口或新服务;
  5. 禁止使用过时类或方法;
  6. Objectequal方法易抛空指针异常,应使用常量或确定有值的对象来调用该方法;
  7. 所有整型包装类对象间值的比较,全部使用equals方法;
  8. 浮点数间值的比较,基本数据类型不能用==,包装数据类型不能用equals
  9. 浮点数比较可指定一个误差范围,当两浮点数差值在误差范围内时认为是相等的;
  10. 浮点数比较可用BigDeimal获取浮点数值,运算后使用CompareTo方法忽略精度计算;
  11. 使用String的构造方法或BigDecimalvalueOf方法转化浮点对象double,避免直接使用BigDecimal的构造方法造成精度损失导致业务逻辑异常;
  12. 所有POJO类属性、RPC方法的返回值和参数必须使用包装数据类型;
  13. DO/DTO/VO等POJO类不设定初值,提示使用者必须自行显式赋值;
  14. 序列化类新增属性时,避免修改serialVersionUID字段,避免反序列失败,若完全不兼容升级,修改serialVersionUID值,避免反序列化混乱;
  15. 构造方法禁止任何业务逻辑,若有初始化逻辑需求,则放入init方法中;
  16. POJO类必须写toString方法;
  17. 禁止在POJO类中同时存在属性xxxisXxx()方法和getXxx()方法,避免两个方法的调用优先级混乱;
  18. 类内方法顺序:公有方法或保护方法>私有方法>getter/setter方法,同名方法应优先一同放置;
  19. 循环体中字符串的连接,使用StringBuilderappend方法进行扩展,避免程序自行生成一个对象造成内存资源浪费;
  20. 使用final关键字的情况:

不允许被继承的类;
不允许修改引用的域对象;
不允许被覆写的方法;
不允许运行过程中重新赋值的局部变量;
避免上下文重复使用一个变量;

  1. clone方法默认浅拷贝,深拷贝需要覆写该方法实现域对象的深度遍历拷贝;
  2. 类成员与方法控制从严:

若不允许外部直接new创建对象,那么构造方法规定为private
工具类不允许publicdefault构造方法;
类非static成员变量并且与子类共享,必须是protected
类非static成员变量并且仅在本类使用,必须是private
static成员变量如果仅在本类使用,必须是private
若是static成员变量,考虑是否为final
类成员方法只供类内部调用,必须是private
类成员方法只对继承类公开,限制为protected

日期时间

  1. 日期传入pattern中统一使用小写的yyyy表示当天年份,YYYY表示当周年份,周跨年时返回下一年;
  2. 日期中月份----M,分钟----m,24小时制----H,12小时制----h
  3. 不允许使用java.sql.Date/Time/Timestamp方法;
  4. 闰年的存在,需要禁止使用365天/年的固定写法,避免2月29日问题;

集合处理

  1. 只要覆写equals,就必须覆写hashCode
  2. Set存储的是不重复的对象、自定义对象作为Map的键时,必须覆写这些对象的equalshashCode方法;
  3. 只能使用isEmpty()方法判断集合内部是否为空,而非size() == 0
  4. java.util.stream.Collectors类中,只能使用含有参数类型BinaryOperator,参数名mergeFunctiontoMap()方法,避免出现相同key值抛出IllegalStateException异常的情况,该方法还需要注意valuenull时抛出的NPE异常;
  5. ArrayListsubList结果不可强转为ArrayList,否则会抛出ClassCastException异常;
  6. 使用Map方法keySet()/values()/entrySet()返回集合对象时,不可对其进行添加元素操作,否则会抛出UnsupportedOperationException异常;
  7. Collections类返回的对象不能进行添加或删除元素的操作;
  8. subList场景中对父集合元素的增减,都会导致子列表的便利、增加、删除产生ConcurrentModificationException异常;
  9. 只能使用集合的toArray(T[] array)方法,传入类型完全一致、长度为0的数组,来将集合转为数组;
  10. 使用Collection接口实现任何类的addAll()方法时,都要对输入的集合参数进行NPE判断;
  11. 不能使用Arrays.asList()中修改集合相关的方法,它的add/remove/clear方法会抛出UnsupportedOperationException异常;
  12. 泛型通配符<? extends T>的泛型集合不能使用add方法,<? super T>不能使用get方法;
  13. 无泛型限制定义的集合赋值给泛型限制的集合,在使用这集合元素时,需要进行instanceof判断,避免抛出ClassCastException异常;
  14. remove/add操作不能在foreach循环中操作,使用Iterator进行元素remove,并发操作时需要对Iterator对象加锁;
  15. Comparator类的实现需要满足:

x,y 的比较结果与 y,x的比较结果相反;
x > y , y > z, 则 x > z;
x = y , 则 x 与 z 的比较结果同 y 与 z 的比较结果相同;

  1. 集合初始化时,指定集合初始化值大小,若不能确定则指定默认值16

并发处理

  1. 单例对象获取需要保证线程安全,其方法也要保证线程安全;
  2. 创建线程及线程池需要制定有意义的线程名词,易于回溯;
  3. 线程资源只能通过线程池提供,不允许自行显式创建线程;
  4. 线程池不允许使用Executors创建,通过ThreadPoolExecutor的方式避免资源耗尽的风险;
  5. SimpleDateFormat是线程不安全的类,一般不要定义为static变量,若一定要定义,必须加锁或使用DateUtils工具类;
  6. 牢记使用try-finally块回收自定义的ThreadLocal变量,避免影响后续业务逻辑和造成内存泄漏;
  7. 高并发时,尽可能使加锁的代码块工作量尽可能的小(甚至不加锁),避免在锁代码块中调用RPC方法;
  8. 对多个目标同时加锁时,需要保持一致的加锁顺序,避免死锁;
  9. 使用阻塞等待获取锁的方式只能在try代码块外,且加锁方法与try代码块间没有任何可能抛出异常的方法调用,避免加锁后在finally中无法解脱;
  10. 使用尝试机制获取锁的方式在进入业务代码块之前,必须先判断当前线程是否已经持有锁;
  11. 并发修改同一记录时,避免更新丢失,需要加锁,要么在应用层加锁,要么在缓存加锁,要么在数据库存层使用乐观锁(冲突概率小于20%时,否则使用悲观锁),使用version作为更新依据;
  12. 多线程并行处理定时任务时,使用ScheduledExcutorService代替Timer运行多个TimeTask,可以避免其中一个没有捕获抛出的异常导致其它任务自动终止运行的情况;

控制语句

  1. switch括号内的变量类型为String并且此变量为外部参数时,必须先进行null判断;
  2. 注意三目运算符表达式1和表达式2类型对齐时,可能抛出的因自动拆箱导致的NPE异常;
  3. 高并发场景中,避免使用==作为中断或推出的条件;
  4. 条件判断中避免使用复杂语句,将复杂语句的判断结果赋值给一个有意义的布尔变量,提高可读性;
  5. 避免使用取反逻辑!,采用对应的正向逻辑写法;
  6. 公开接口需要采取入参保护,尤其是批量操作的接口;

注释规约

  1. 类、类属性、类方法的注释必须使用Javadoc规范;
  2. 所有抽象方法(包括接口中的方法)必须使用Javadoc注释返回值、参数、异常说明和指出该方法做什么事情,实现什么功能;
  3. 所有类都必须添加创建者和创建日期;
  4. 方法内部单行注释在被注释语句上方另起一行,使用//注释,多行注释使用/**/注释,且需与代码对齐;
  5. 所有枚举类型字段必须要有注释,说明每个数据项的用途;

前后端规约

  1. 前后端交互的API需要明确协议、域名、路径、请求方法、状态码、响应体;
  2. 前后端数据列表相关的接口返回,若为空则返回空数组或空集合,减少前端琐碎的null判断;
  3. 服务端发生错误时,返回给前端的响应信息包含HTTP状态码、errorCodeerrorMessage、用户提示信息四个部分,分别针对浏览器、前端开发、错误排查人员、用户;
  4. JSON格式数据中,所有key必须为小驼峰风格;
  5. 需要使用超大整数的场景,服务端一律使用String字符串类型返回,而非Long类型;
  6. HTTP请求通过URL传递参数时,不能超过2048字节。通过body传递内容时,不能超过设定长度,否则后端解析出错;
  7. 服务器内部重定向必须使用forward;外部重定向必须使用URL统一代理模块生成,否则会导致浏览器“不安全”提示,及URL维护不一致;
  8. 前后端时间格式统一为yyyy-MM-dd HH:mm:ss,统一为GMT

其它

  1. 避免使用Apache Beanutils进行属性copy
  2. 后台输送给页面的变量必须加感叹号成$!{var}形式,否则var等于null或不存在时,${var}会直接显示在页面上;
  3. Math.random()返回值为double类型,取值范围在0到1之间(左闭右开),取整数类型直接使用Random对象的nextIntnextLong方法;
  4. 任何数据结构的构造或初始化,都应该指定大小,避免数据结构无限增长吃光内存;
  5. 及时清理不再使用的代码段及配置信息;

异常日志

错误码

  1. 不体现版本号和错误等级;
  2. 正常情况返回00000
  3. 错误码为5位字符串类型,错误产生来源+四位数字编号;
  4. 不能直接输出给用户作为提示信息;
  5. 错误码之外的业务独特信息由error_message承载;

异常处理

  1. 类库中定义的可以通过预检查方式规避的RuntimeException异常不应该通过catch方式处理;
  2. 异常捕获后不能作为流程控制、条件控制;
  3. 分清稳定代码(无论如何不会出错的代码)和非稳定代码,对后者的catch尽可能区分异常类型后,再做处理;
  4. 事务场景异常被catch后,若需要回滚则自行手动回滚;
  5. finally块必须对资源对象、流对象关闭,异常也需要进行try-catch,禁止使用return
  6. 捕获与抛出异常必须完全匹配,或者捕获异常是抛出异常的父类;
  7. 调用RPC、二方包、动态生成类的相关方法时,捕获异常必须使用Throwable类进行拦截;
  8. 时刻注意NPE的防止;

日志规约

  1. 应用中不可直接使用日志系统的API,应使用日志框架中的API
  2. 按公司要求进行扩展日志命名;
  3. 日志输出时,使用占位符进行字符串变量之间的拼接;
  4. trace/debug/info级别的日志输出,必须进行日志级别的开关判断;
  5. 在日志配置文件中设置additivity=false,避免重复打印日志,浪费空间;
  6. 生产环境禁止直接使用System.outSystem.err输出日志或e.printStackTrace()打印异常堆栈;
  7. 异常信息应该包括两类信息:案发现场信息和异常堆栈信息。如果不处理,那么通过关键字throws往上抛出
logger.error("inputParams:{} and errorMessage:{}", 各类参数或对象toString(), e.getMessage(), e);
  1. 日志打印时禁止直接用JSON工具将对象转换成String

单元测试

  1. 单元测试需要遵循AIR原则:

automatic
independent
repeatable

  1. 单元测试应为全自动、非交互式执行;
  2. 保证单元测试的独立性,测试用例间禁止相互调用、依赖;
  3. 必须可以重复执行;
  4. 测试中的测试粒度需足够小,至多为类级别,常为方法级别,便于精确定位问题;
  5. 源码编译时会跳过如下目录src/test/java,单元测试框架默认扫描,因此单元测试代码需写在该目录中;
  6. 单元测试的语句覆盖率至少70%,核心模块的语句覆盖率和分支覆盖率要求100%;
  7. 单元测试代码遵守BCDE原则

Border:边界值测试
Correct:正确输入,得到预期结果
Design:结合设计文档
Error:强制错误信息输入,得到预期结果

  1. 业务代码应避免:

在构造方法中做过多的内容;
存在过多的全局变量和静态方法;
存在过多的外部依赖;
存在过多的条件语句;

安全规约

  1. 率属于用户个人的页面或功能必须进行权限控制校验;
  2. 用户敏感数据禁止直接展示;
  3. 对用户输入的SQL参数严格使用参数绑定或METADATA字段值限定,放置SQL注入,禁止字符串拼接SQL访问数据库;
  4. 用户输入的任何参数必须做有效性严重;
  5. 禁止向HTML页面输出未经安全过滤或未正确转义的用户数据;
  6. 表单、AJAX提交必须执行CSRF安全验证;
  7. URL外部重定向传入的目标地址必须执行白名单过滤;
  8. 使用平台资源必须实现正确的防重放机制,避免滥刷导致资损;

MySQL数据库

建表规约

  1. 是否概念的字段必须用is_xxx的方式命名,且使用unsigned tinyint数据类型;
  2. 表明字段名必须使用小写字母或数字,禁止数字开头、下划线间只出现数字;
  3. 表名固定为单数名词,尽量遵循“业务名称_表作用”,库名和应用名尽量保持一致;
  4. 主键索引名为pk_字段名,唯一索引名为uk_字段名,普通索引名为idx_字段名
  5. 小数类型仅使用decimal,禁止使用floatdouble
  6. 若存储几乎相同长度字符串时,优先使用char定长字符串类型;
  7. varchar是可变长字符串,避免其长度超过5000,超过则定义类型为text,独立一张表并用主键对应;
  8. 表必备字段:

id
create_time
update_time

  1. 不是频繁修改的字段、不是唯一索引的字段、不是varchartext字段,则允许为了提高查询性能适当冗余,但数据必须一致;
  2. 单表超过500万行或单表容量超过2GB,才考虑进行分库分表;

索引规约

  1. 业务上具有唯一特性的字段,即便是组合字段也必须建成唯一索引;
  2. 超过三张表则禁止join
  3. varchar字段建立索引时,必须指定索引长度,根据实际文本区分度决定索引长度,不必对全字段建立索引;
  4. 页面搜索禁止左模糊或全模糊;
  5. SQL优化的目标要求至少ref,最少达到range,最好做到consts
  6. 组合索引中区分度最高的内容在最左边;

SQL语句

  1. 禁止使用count(列名)count(常量)来替代count(*)
  2. 某列全为NULL时,count(col)的返回结果为0,但sum(col)的返回结果为NULL,因此使用sum()是注意NPE问题;
  3. 只使用ISNULL()来判断是否为NULL值;
  4. 分页查询逻辑编写时,count0直接返回,避免后续执行;
  5. 禁止使用外键与级联,一切外键概念必须在应用层解决;
  6. 禁止使用存储过程;
  7. 数据订正(特别是删除或修改)时,要先select,避免误删除;
  8. 设计多张表的查询和变更,都需要在列名前加表名或表的别名进行限定;
  9. 尽量避免in操作,实在要用则需将数量控制在1000个以内;
  10. 不建议在开发代码中使用TRUNCATE TABLE;

ORM映射

  1. 表查询中禁止使用*作为查询的字段列表,必须明确查询字段;
  2. POJO类布尔属性不能加is,数据库字段必须加is,要求二者在resultMap中进行属性和字段之间的映射;
  3. 禁止使用resultClass作为返回参数,必须定义<resultMap>,因此每张表也必然有一个<resultMap>对应;
  4. sql.xml配置参数使用:#{}#param#,禁止使用${},后者易于出现SQL注入;
  5. 不推荐使用iBATIS自带的queryForList(String statementName, int start, int size)
  6. 禁止直接使用HashMapHashtable作为查询结果集的输出;
  7. 执行SQL时不要更新无改动的字段,易出错、效率低、增加binlog存储;
  8. 禁止滥用@Transactional事务,避免影响数据的QPS

工程结构

应用分层

  1. 默认上层依赖于下层,箭头关系表示直接向下依赖;

开放API层
终端显示层:模板渲染及显示;
Web层:转发、校验及不复用的业务简单处理层;
Service层:具体业务逻辑层;
Manager层:通用业务层;
DAO层:数据访问层;
第三方及外u接口:其他部门PRC服务接口、基础平台、其他公司接口;

在这里插入图片描述

二方库依赖

  1. 定义GAV遵循规则:

groupID:com.{公司/BU}.业务线 [.子业务线]
artifactID:com.taobao.jstorm
version

  1. 指定版本号命名方式为:

主版本号:产品方向改变,或大规模API不兼容,或架构不兼容升级
次版本号:保持相对兼容性,增加主要功能特性,影响范围极小的API不兼容修改
修订号:保持完全兼容性,修复BUG,新增次要功能特性

  1. 线上应用禁止依赖于SNAPSHOT版本(安全包除外),正式发布的类库必须先向中央仓库进行查证,使RELEASE版本号有延续性,且版本号不允许覆盖升级;
  2. 二方库的新增或升级,保持除功能点之外的其它jar包仲裁结不变。如果有改变,必须明确评估和验证;
  3. 允许二方库定义枚举,参数使用枚举类型,但接口返回值不允许使用枚举或包含枚举的POJO对象;
  4. 禁止在子项目的pom依赖中出现相同的GroupIdArtifactId,但不是相同的Version
  5. 所有pom文件中的依赖声明放在<dependencies>语句块中,所有版本仲裁放在<dependencyManagement>语句块中;
  6. 二方库发布者应遵循精简可控原则和稳定可追溯原则;

服务器

  1. 高并发服务器建议调小TCP协议的time_wait超时时间,调大服务器所支持的最大文件句柄数;
  2. 线上生产环境JVMXmsXmx设置一样大小的内容容量,避免在GC后调整堆大小带来的压力;

设计规约

  1. 需求分析阶段:

系统交互的User超过一类,且相关User Case超过五个,使用用例图来表达更清晰的结构化需求;
业务对象的状态超过三种,使用状态图表达并明确状态变化的各个触发条件;
系统中某功能的调用链路上对象超过三个,使用时序图表达并明确各调用环节的输入与输出;
系统中模型类超过五个并存在复杂的依赖关系,使用类图表达并明确类之间的关系;
系统中超过两个对象间存在协作关系,并需要表示复杂的处理流程,使用活动图来表示;

  1. 系统架构设计阶段:

确定系统边界;
确定系统内模块间的关系;
确定知道后续设计与演化的原则;
确定非功能性需求;

  1. 需求分析与系统设计时,需充分考虑异常流程与业务边界;
  2. 类的设计与实现过程中要符合单一原则;
  3. 谨慎使用继承,优先使用聚合/组合;
  4. 系统设计时尽量依赖抽象类与接口,注意对扩展开放,对修改闭合,同时抽取出公共模块、公共配置、公共类、公共方法等;
  5. 可扩展的本质是找到系统的变化点,并隔离它;
  6. 设计的本质是识别和表达系统难点。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值