代码规范(习惯)举例

代码规范举例

一.命名风格

其中1.2.3…条为约定风格,实际代码指的是企业级代码中也存在的情况

  1. 代码中的命名不能以下划线或美元符号开始或结束。

实际代码中也出现如【getHistory_new】之类的方法名,但使用$的很少

  1. 代码中严禁使用中文拼音及中英文结合的方式命名

实际代码中如果英文很繁琐,词不达意或者晦涩难懂,全拼音也存在于代码中。

  1. 常量名/参数名下划线隔开,见名知义,方便代码回溯。

写的时候不要嫌长,尽量使用统一的前缀或后缀,既便于区分,又便于分类查找

  1. 布尔型变量不要加is前缀,虽然会看起来见名之意,但是在有些框架解析会引起序列化错误
  2. 包名统一使用单数形式,类名可以采用复数形式

实际代码中包名是复数还是有存在,如utils包等等

  1. 关于缩写,如conditon缩写成contdi,不赞成

实际代码中存在content缩写成cont的情况

  1. 变量命名时,表示类型的词放在结尾,如nameList为名称的list集合,但listName无法表达出此含义
  2. 有关 接口(service/dao)需不需要写public之类的修饰符:

实际代码中,根据个人习惯,有的喜欢全加,从代码的简洁度考虑,推荐不加 如 void updateUserInfo();

  1. spring 各层的命名规则
    • 获取单个对象用get前缀
    • 获取多个对象用list前缀,复数形式结尾如listContents
    • 获取统计值用count前缀
    • 插入/删除/修改用save(insert)/remove(delete)/update前缀

二、类属性

  1. long类型的值用大L避免小l出现歧义
  2. IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不要使用 Windows 格式
  3. 单个方法的总行数不超过 80 行

这个要求比较高,有时候业务逻辑很复杂,除了判断还有很多计算和查询,可以考虑先实现功能,然后再把能提的方法提出来,然后优化代码(如判断,循环之类)eclipse里可采用shift+alt+M进行方法提取

  1. 不要使用过时的类或方法

java.net.URLDecoder 中的方法 decode(String encodeStr) 这个方法已经过时,应该使用双参数
decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接
口;作为调用方来说,有义务去考证过时方法的新实现是什么。
实际代码中确实在使用这个方法当 参数中有特殊字符时进行编码

  1. 避免Object的equals方法抛出空指针异常,需要将确定的变量写在前面如【”success“.equals(saveFlag)】参考:jdk7引入的java.util.Objects#equals方法(#在这里表示【的】)
  2. 在映射数据库pojo(model)时,应采用基本类型的包装类来避免null值的问题(如user_id为bigint(20),则应该用Long,而不是long)
  3. 定义do,Vo,model类时,不要设定任何默认值

如 private Boolean deleteFlag= false;这种情况有危险,但在实际的代码编写中也有这种业务逻辑存在并采用上述默认值方式

  1. 定义序列化的时候serialVersionUID直接==1L即可,与=132425134233535L效果相同
  2. 对于do,vo.model中的get()/set(),hashCode.equals()/toString()方法,推荐使用ide自带生成方式,不建议手写或手动修改

IDE中shift+alt+s可以生成。举例,实际代码中,由于业务原因修改了字段的名称,由于不想删除所有get,set()方法,于是手动修改了一个属性的get/set(),但是没有注意大小写,细微的错误,还是同事发现,很低级错误,希望大家引以为戒

  1. 类内方法定义的顺序依次是:公有方法或保护方法 > 私有方法 > getter / setter方法。

如Controller中一般public的方法可能会有个私有方法,实际代码中有的时候放在类的最下面,有的时候放在当前方法的下面,这是由于代码时多人同时开发,有时候并不能做到公有方法在同一个地方,私有方法都在下面。

  1. String的拼接方式如果在循环体中,建议使用StringBuilder的append方法,避免资源浪费

Stirng str = “start”;
for(int i = 0;i<1000;i++){
str = str+“hello”;
}
扩展:不同语言中,1000这个终止循环条件如果是写成userList.size()这种获取方式,也会拖慢速度,可以考虑先在循环外进行提取。

循环/集合

  1. 不要在 foreach 循环里进行元素的 remove/add 操作。remove 元素请使用
    Iterator 方式,如果并发操作,需要对 Iterator 对象加锁。
  • 错误
 for (String item :list){
     
	if("1".equals(item){
     
		list.remove(item)}
	)
}
  • 正确
List<String> list = new ArrayList<>();
list.add("1");
list.add("2");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
     
	String item = iterator.next();
	if(判断条件){
     
		iterator.remove();
	}	
}
  1. 省略写法:菱形泛型<>

HashMap userCache = new HashMap<>();
但注意JDK7以上才支持

14.集合初始化的时候指定大小

一般Map newMap = new HashMap<>(16);
其中16为默认值,当不确定其大小的时候可以设置
举例当需要放置1024个元素时,由于没有设置容量的初始大小,随着元素的不断增加,连续扩容7次,resize时需要重建hash表,影响性能

  1. 遍历map,keySet和entrySet
  • keySet遍历了2次,一次转换为Iterator对象,另一次是从hashMap中去除key对应value,得到的是K值集合
  • entrySet只遍历一次,效率更高,得到的是K-V组合
  • JDK8,推荐使用Map.forEach方法
  1. 利用 Set 元素唯一的特性,可以快速对一个集合进行去重操作,避免使用 List 的
    contains 方法进行遍历、对比、去重操作

控制语句

  1. switch

每个case要么通过continue/break/return来终止,要么注释说明程序将执行到哪个cases为止,
每个switch必须包含一个default语句并放在最后,即使它什么代码都没有

  1. 当 switch 括号内的变量类型为 String 并且此变量为外部参数时,必须先进行 null判断
  2. 在 if / else / for / while / do 语句中必须使用大括号

实际代码中,最好使用大括号,有的人为了代码简洁,省略大括号,但可读性差,不利于维护

 if(a>b)b=a;
 相对于
 if(a>b){
     
     b=a;
 }
  1. 在高并发场景中,避免使用”等于”判断作为中断或退出的条件。

说明:如果并发控制没有处理好,容易产生等值判断被“击穿”的情况,使用大于或小于的区间判断条件
来代替。

  1. 少用if-else,多用if(xxx){return false;}
if(a>b){
     
     return false;
 }
 //todo,业务逻辑

这种情况企业代码中很常见,if的循环嵌套会导致代码冗长,还得找每个else 对应的if(虽然在eclipse里,在if的左大括号双击就能高亮期间的代码),很费劲。

  • 举例:在Controller层接受参数的时候,判断参数如果为空或非数字,直接return返回到页面提示参数有误,直到所有条件被筛选完成,才进行真正的业务逻辑的编写。
  1. 不要在条件判断中执行其它复杂的语句,将复
    杂逻辑判断的结果赋值给一个有意义的布尔变量名,以提高可读性。

final boolean existed = (file.open(fileName, “w”) != null) && (…) || (…);
if (existed) {

}

  1. 循环体里避免定义对象、变量、数据库连接、try-catch操作(try-cache是否 可以移至循环体外)
  2. 避免采用取反逻辑运算符(不利于快速理解)

commons.lang3中
a: !StringUtils.isBlank(a)
b: StringUtils.isNotBlank(a)
推荐使用b而不是a

注释规范

  1. 类,类属性,类方法、抽象方法的注释使用javaDoc规范:使用【/**内容 */】格式,不得使用// xxx方式,eclipse里shift+alt+快捷键,还可以配置生成注释的时间、作者、参数说明等
  2. 方法内部 单行注释在被注释语句上方另起一行,使用// 注释。方法内部多行注释使用/* */。

异常处理

  1. 可以用判断的方式避免使用try…catch,如空指针和数组下标越界

if(obj!=null){…}

  1. 捕获异常是为了处理它,不要捕获了却什么都不处理而抛弃之,如果不想处理它,请将该异常抛给它的调用者。最外层的业务使用者,必须处理异常,将其转化为用户可以理
    解的内容。
  2. 有 try 块放到了事务代码中,catch 异常后,如果需要回滚事务,一定要注意手动回
    滚事务。

在springmvc中,如果配置了同一的事物管理spring-transaction.xml文件,一般在controller里将调用manager层的方法try catch,在manager里不用catch,也不用throw。

  1. finally 块必须对资源对象、流对象进行关闭,有异常也要做 try-catch。说明:如果 JDK7 及以上,可以使用 try-with-resources 方式。
finally(){
     
	if(bis!=null){
     
		try{
     
	  	    bis.close();
		}catch(e){
     
			log.err(xxxxxxxxxxxxxx)
		}
	}
}
  1. 不要在 finally 块中使用 return。

try 块中的 return 语句执行成功后,并不马上返回,而是继续执行 finally 块中的语句,如果此处存
在 return 语句,则在此直接返回,无情丢弃掉 try 块中的返回点。
反例:

private int x = 0;
public int checkReturn() {
     
	try {
     
		// x 等于 1,此处不返回
		return ++x;
	} finally {
     
		// 返回的结果是 2
		return ++x;
	}
}

日志规范

  1. 应用中不可直接使用日志系统 (Log 4 j 、 Logback) 中的 API ,而应依赖使用日志框架SLF 4 J 中的 API ,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
  1. 在日志输出时,字符串变量之间的拼接使用占位符的方式。

说明:因为 String 字符串的拼接会使用 StringBuilder 的 append()方式,有一定的性能损耗。使用占位符
仅是替换动作,可以有效提升性能。
正例: logger.debug(“Processing trade with id: {} and symbol: {}”, id, symbol);

  1. 避免重复打印日志,浪费磁盘空间,务必在 log 4 j . xml 中设置 additivity = false

< name=“com.taobao.dubbo.config” additivity=“false”>

  1. 异常信息应该包括两类信息:案发现场信息(一些字段的值)和异常堆栈信息。如果不处理,那么通过关键字 throws 往上抛出。

logger.error(各类参数或者对象 toString() + “_” + e.getMessage(), e);

  1. 避免打印大量日志使服务器磁盘占满

大量地输出无效日志,不利于系统性能提升,也不利于快速定位错误点。记录日志时请思考:这些
日志真的有人看吗?看到这条日志你能做什么?能不能给问题排查带来好处?

安全规约

  1. 用户敏感信息禁止直接展示,必须对展示数据进行脱敏

手机号码139****2525,隐藏中间四位

  1. 用户请求的任何参数必须做有效性验证
  • page size过大导致内存溢出
  • 恶意order by导致数据库慢查询
  • 任意重定向
  • SQL注入
  • 反序列化注入
  1. 禁止向 HTML 页面输出未经安全过滤或未正确转义的用户数据。
  2. 在使用平台资源,譬如短信、邮件、电话、下单、支付,必须实现正确的防重放的机制,如数量限制、疲劳度控制、验证码校验,避免被滥刷而导致资损。
    说明:如注册时发送验证码到手机,如果没有限制次数和频率,那么可以利用此功能骚扰到其它用户,并造成短信平台资源浪费。
  3. 发贴、评论、发送即时消息等用户生成内容的场景必须实现防刷、文本内容违禁过滤等风控策略。

举例:评论有敏感词,分一级(直接驳回)二级(人工审核)

MySQL数据库

  1. 表名不使用复数形式---->对应的pojo、model类也是如此
  2. 小数类型为decimal,禁止使用float和double
  • 存在精度损失的问题
  • 如果存储范围超过decimal范围,建议将数据拆成整数和小数并分开存储
  1. varchar 是可变长字符串,不预先分配存储空间,长度不要超过 5000,如果存储长度大于此值,定义字段类型为 text ,独立出来一张表,用主键来对应,避免影响其它字段索引效率。
  2. 表必备三字段:id, create_time, update_time。其中 id 必为主键,类型为 bigint unsigned(无符号值可以避免误存负数,且扩大了表示范围)、单表时自增、步长为 1。create_time, update_time的类型均为 datetime 类型。
  3. 表的命名最好是遵循“业务名称_表的作用”。
    正例:alipay_task / force_project / trade_config
  4. 单表行数超过 500 万行或者单表容量超过 2GB,才推荐进行分库分表。为提高查询,处理效率,也有100万行、200万行开始分。
  5. 业务上具有唯一特性的字段,即使是多个字段的组合,也必须建成唯一索引。错误:认为业务的惟一性一律需要在应用层通过“先查后插”方式解决。

说明:不要以为唯一索引影响了 insert 速度,这个速度损耗可以忽略,但提高查找速度是明显的;另外,即使在应用层做了非常完善的校验控制,只要没有唯一索引,根据墨菲定律,必然有脏数据产生

  1. 利用延迟关联或者子查询优化超多分页场景。
    说明:MySQL 并不是跳过 offset 行,而是取 offset+N 行,然后返回放弃前 offset 行,返回 N 行,那当offset 特别大的时候,效率就非常的低下,要么控制返回的总页数,要么对超过特定阈值的页数进行 SQL改写。

正例:先快速定位需要获取的 id 段,然后再关联:
SELECT a.* FROM 表 1 a, (select id from 表 1 where 条件 LIMIT 100000,20 ) b where a.id=b.id

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

说明:
1) consts 单表中最多只有一个匹配行(主键或者唯一索引),在优化阶段即可读取到数据。
2) ref 指的是使用普通的索引(normal index)。
3) range 对索引进行范围检索。
反例:explain 表的结果,type=index,索引物理文件全扫描,速度非常慢,这个 index 级别比较 range
还低,与全表扫描是小巫见大巫。

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

说明:存在非等号和等号混合时,在建索引时,请把等号条件的列前置。如:where c>? and d=? 那么
即使 c 的区分度更高,也必须把 d 放在索引的最前列,即索引 idx_d_c

  1. 不要使用 count(列名)或 count(常量)来替代 count(),count()是 SQL92 定义的标准统计行数的语法,跟数据库无关,跟 NULL 和非 NULL 无关。

说明:count(*)会统计值为 NULL 的行,而 count(列名)不会统计此列为 NULL 值的行。

  1. 使用如下方式来避免 sum 的 NPE 问题:SELECT IFNULL(SUM(column), 0) FROM table;
  2. 使用 ISNULL() 来判断是否为 NULL 值。

说明:NULL 与任何值的直接比较都为 NULL。
1) NULL<>NULL 的返回结果是 NULL,而不是 false。
2) NULL=NULL 的返回结果是 NULL,而不是 true。
3) NULL<>1 的返回结果是 NULL,而不是 true。

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

说明:以学生和成绩的关系为例,学生表中的 student_id 是主键,那么成绩表中的 student_id 则为外键。如果更新学生表中的 student_id,同时触发成绩表中的 student_id 更新,即为级联更新。外键与级联更新适用于单机低并发,不适合分布式、高并发集群;级联更新是强阻塞,存在数据库更新风暴的风险;外键影响数据库的插入速度。

  1. 数据订正(特别是删除、修改记录操作)时,要先 select ,避免出现误删除,确认无误才能执行更新语句。
  2. in 操作能避免则避免,若实在避免不了,需要仔细评估 in 后边的集合元素数量,控
    制在 1000 个之内。

服务器

  1. 给 JVM 环境参数设置-XX:+HeapDumpOnOutOfMemoryError 参数,让 JVM 碰到OOM 场景时输出 dump 信息。

说明:OOM 的发生是有概率的,甚至相隔数月才出现一例,出错时的堆内信息对解决问题非常有帮助。

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

其他

  1. 在使用正则表达式时,利用好其预编译功能,可以有效加快正则匹配速度 不要在方法体内定义:Pattern pattern = Pattern.compile(“规则”);
  2. 获取当前毫秒数 System . currentTimeMillis(); 而不是 new Date() . getTime();
    说明:如果想获取更加精确的纳秒级时间值,使用 System.nanoTime()的方式。在 JDK8 中,针对统计时间等场景,推荐使用 Instant 类
  3. 任何数据结构的构造或初始化,都应指定大小,避免数据结构无限增长吃光内存

摘自 Java开发手册华山版,仅作为学习交流,如有侵权,请告知删除。

你可能感兴趣的:(代码规范,代码规范,java)