大家好,欢迎来到IT知识分享网。
一、编程规约
(一)命名风格
- 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。
反例:_name / $name / name_ / name$
- 严禁使用拼音与英文混合的方式,更不允许直接使用中文的方式。
正例:ali / taobao / aliyun / youku (国际通用的名称,可视同英文)
反例:pingJia(评价) DaZhe (打折) DianYing(电影)
- 代码和注释中避免使用任何语言的种族歧视性词语。
反例:RIBENGUIZI / slave
- 类名使用UpperCamelCase风格,但以下情形除外:DO/BO/DTO/VO/AO/PO/UID等
正例:ForceCode / UserDO /
反例:forcecode / userdo
- 方法名、参数名、成员变量、局部变量都统一使用lowerCamelCase风格。
反例:getAge / setAge / localValue
- 常量命名全部大写,单词间用下划线隔开,力求予以表达完整清楚,不要嫌名字长。
正例:MAX_STOCK_COUNT / CACHE_EXPRIES_TIME
- 抽象类命名使用 Abstract 或 Base 开头;异常类命名使用 Exception 结尾;测试类
命名以它要测试的类的名称开始,以 Test 结尾
- 类型与中括号紧挨相连来表示数组。
正例:int[] arrayDemo
- POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列
化错误。
说明:在本文MySQl规约中的建表约定第一条,表达是与否的变量采用is_xxx的命名方 式,所以需要在<resultMap>设置从is_xxx到xxx的映射关系
反例:Boolean isDeleted这个属性,它的方法也是isDeleted(),框架在反向解析的时候,误以为对应属性名称是deleted,导致属性获取不到,进而抛出异常。
- 包名统一使用小写,点分隔符之间有且仅有个自然语义的英语单词。包名统一使用
单数形式,但是类名如果有复数含义,类名可以使用复数形式
正例:com.qcby.user
- 避免在子父类的成员变量之间、或者不同代码块的局部变量之间采用完全相同的命名,
使可理解性降低
说明:子类、父类之间变量名相同,即使是public类型的变量也能通过编译,另外,局部变量在同一方法内的不同代码块中同名也是合法的,这些情况我们都要避免,对于非setter/getter的参数名称也要避免与成员变量名称相同。
- 杜绝完全不规范的缩写,避免望文不知义。
- 对于service和DAO类,基于SOA的理念,暴露出来的服务一定是接口,内部的实现类用lmpl的后缀与接口区别。
正例:cacheServicelmpl实现CacheService接口
- 各层命名规约:
- Service/DAO层方法命名规约
- 获取单个对象的方法用get作为前缀。
- 获取多个对象的方法用list作为前缀,复数结尾,如:listObjects。
- 获取统计值的方法要用count作为前缀。
- 插入的方法用save/insert作为前缀。
- 删除的方法用remove/delete做前缀。
- 修改的方法用update前缀
(二)常量定义
- 不允许使用魔法值(未经预先定义的常量)直接出现在代码当中
if (age < 18) { console.log(“未成年”); } //此时18就是魔法值,将其变为下面形式, const MINOR_AGE = 18; if (age < MINOR_AGE) { console.log(“未成年”); } //魔法值会降低代码的可读性与可维护性,因为它们含义不清晰,可能会导致错误或者混淆。 //所以要避免魔法值,使用常量代替魔法值
- 在 long 或者 Long 赋值时,数值后使用大写字母 L,不能是小写字母 l,小写容易跟
数字混淆,造成误解。
正例:Long a = 2L
反例:Long a = 2l
(三)代码格式
- 如果是大括号内为空,则简洁地写成{}即可,大括号中间无需换行和空格;如果是非
空代码块则:
- 左大括号前不换行。
- 左大括号后换行。
- 右大括号前换行
- 右大括号后还有 else 等代码则不换行;表示终止的右大括号后必需换行。
- 左小括号和右边相邻字符之间不出现空格;右小括号和左边相邻字符之间也不出现空
格;而左大括号前需要加空格。
反例:if (空格 a == b 空格)
- if/for/while/switch/do 等保留字与括号之间都必需加空格。
- 任何二目、三目运算符的左右两边都需要加一个空格。
说明:包括赋值运算符号=、逻辑运算符&&、加减乘除符号等
- 采用 4 个空格缩进,禁止使用 Tab 字符。
说明:如果使用 Tab 缩进,必需设置 1 个 Tab 为 4 个空格。IDEA 设置 Tab 为 4 个空格时,请勿勾选 Use tab character;而在 Eclipse 中,必需勾选 insert spaces for tabs。
public static void main(String[] args) { // 缩进 4 个空格 String say = "hello"; // 运算符的左右必需有一个空格 int flag = 0; // 关键词 if 与括号之间必需有一个空格,括号内的 f 与左括号,0 与右括号不要空格 if (flag == 0) { System.out.println(say); } // 左大括号前加空格且不换行;左大括号后换行 if (flag == 1) { System.out.println("world"); // 右大括号前换行,右大括号后有 else,不用换行 } else { System.out.println("ok"); // 在右大括号后直接结束,则必须换行 } }
- 注释的双斜线与注释内容之间有且仅有一个空格。
正例:// 注意这是示例注释,双斜线与注释内容之间有一个空格
- 在进行类型强制转换时,右括号与强制转换值之间不需要任何空格隔开
正例:double first = 3.2d;
int second = (int)first + 2;
- 单行字符数限制不超过 120 个,超出需要换行,换行时遵循如下原则:
- 第二行相对第一行缩进 4 个空格,从第三行开始,不再继续缩进,参考示例。
- 运算符与下文一起换行。
- 方法调用的点符号与下文一起换行。
- 方法调用中的多个参数需要换行时,在逗号后进行。
- 在括号前不要换行,见反例
正例: StringBuilder sb = new StringBuilder(); // 超过 120 个字符的情况下,换行缩进 4 个空格,并且方法前的点号一起换行 sb.append("yang").append("hao")... .append("chen")... .append("chen")... .append("chen"); 反例: StringBuilder sb = new StringBuilder(); // 超过 120 个字符的情况下,不要在括号前换行 sb.append("you").append("are")...append ("lucky"); // 参数很多的方法调用可能超过 120 个字符,逗号后才是换行处 method(args1, args2, args3, ... , argsX);
- 方法参数在定义和传入时,多个参数逗号后面必需加空格。
正例:method(args1, args2, args3);
- IDE 的 text file encoding 设置为 UTF-8; IDE 中文件的换行符使用 Unix 格式,不要
使用 Windows 格式.
(四)OOP(面向对象编程)规约
- 避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可。
- 所有的覆写方法,必须加@Override注解。
说明:getObject()与get0bject()的问题。一个是字母的O,一个是数字的0,加@Override 可以准确判断是否覆盖成功。另外,如果在抽象类中对方法名进行修改,其实现类会马上编译报错。(方法签名是指方法的名称和一个参数列表(参数的个数、类型和顺序))
- 相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object.。
说明∶可变参数必须放置在参数列表的最后。(建议开发者尽量不用可变参数编程)
正例: public List<User> listUsers(String type, Long… ids){…}
- 外部正在调用或者二方库(依赖于公司内部的其他项目发布的jar包)依赖的接口,不允许修改方法签名,避免对接口调用方产生影响。接口过时必须加@Deprecated注解,并清晰地说明采用的新接口或者新服务是什么。
- 不能使用过时的类或方法。
说明: java.net.URLDecoder 中的方法decode(String encodeStr)这个方法已经过时,应该使用双参数decode(String source, String encode)。接口提供方既然明确是过时接口,那么有义务同时提供新的接口;作为调用方来说,有义务去考证过时方法的新实现是什么。
- Object的equals方法容易抛空指针异常应使用常量或确定有值的对象来调用equals。
正例: “test”.equals(object);
反例:Object.equals(“test”);
说明︰推荐使用JDK7引入的工具类java.util.Objectst#equals(Object a, Object b)
- 所有整型包装类对象之间值的比较,全部使用equals方法比较。
说明:对于Integer var = ?在-128至127之间的赋值,Integer对象是在 IntegerCache.cache产生,会复用已有对象,这个区间内的 Integer值可以直接使用==进行判断,但是这个区间之外的所有数据,都会在堆上产生,并不会复用已有对象,这是一个大坑,推荐使用equals方法进行判断。
- 任何货币金额,均以最小货币单位且整型类型来进行存储。
- 浮点数之间的等值判断,基本数据类型不能用==来比较,包装数据类型不能用equals来判断。
说明∶浮点数采用“尾数+阶码”的编码方式,类似于科学计数法的“有效数字+指数”的表示方式。二进制无法精确表示大部分的十进制小数,具体原理参考《码出高效》。
//反例 float a = 1.0F - 0.9F; float b = 0.9F - 0.8F; if (a == b) { //预期进入代码块,执行其它业务逻辑 //但事实上a == b的结果为false } Float x = Float.valueOf(a); Float y =Float.valueOf(b); if (x.equals(y)) { //预期进入代码块,执行其它业务逻辑 //但事实上x.equals(y)的结果为false } //正例 (1)指定一个范围误差,两个浮点数的差值在此范围内,则认为相等 float a = 1.0F - 0.9F; float b = 0.9F - 0.8F; float diff = 1e-6F; if(Math.abs(a-b)<diff) { System.out.println("turn"); } //使用BigDecimal来定义值,再进行浮点数的运算操作 BigDecimal a = new BigDecimal("1.0"); BigDecimal b = new BigDecimal("0.9"); BigDecimal c = new BigDecimal("0.8"); BigDecimal x = a.subtract(b); BigDecimal y = b.subtract(c); if(x.compareTo(y) == 0) { System.out.println("trun"); } /*BigDecimal类是java.math包中提供的一个用来对超过16位有效位的数进行精确运算的类。 BigDecimal类创建的是对象,不能使用传统的+、-、*、/等算术运算符直接对其进行数学运算, 而必须调用其对应的方法。BigDecimal类有多种构造函数,可以接受不同类型的参数, 如字符串、整数、长整数、双精度等。*/
- 如上所示BigDecimal的等值比较应使用compareTo()方法,而不是equals()方法。
说明:equals()方法会比较值和精度(1.0与1.00返回结果为false ),而compareTo()则会忽略精度。
- 定义数据对象DO类时,属性类型要与数据库字段类型相匹配。
正例︰数据库字段的 bigint必须与类属性的Long类型相对应。
反例︰某个案例的数据库表id字段定义类型 bigint unsigned,实际类对象属性为Integer,随着id越来越大,超过Integer的表示范围而溢出成为负数。
- 禁止使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象。
说明∶BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。如:BigDecimal g = new BigDecimal(0.1F);实际的存储值为∶0.
正例:优先推荐入参为String 的构造方法,或使用BigDecimal的valueOf方法,此方法内部其实执行了Double的toString ,而 Double的toString 按double的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal(“0.1”);
BigDecimal recommend2 = BigDecimal.valueOf(0.1);
- 关于基本数据类型与包装数据类型的使用标准如下:
- 所有的POJO类属性必须使用包装数据类型。
- RPC方法的返回值和参数必须使用包装数据类型。
- 所有的局部变量使用基本数据类型。
正例:数据库查询结果可能是null,因为自动拆箱,用基本数据类型接受有NPE风险
反例:某业务的交易报表上显示成交总额跌涨情况,即正负x%,x为基本数据类型,调用RPC服务,调用不成功时,返回的是默认值,页面显示为0%,这显然是不合理的,应该显示成中划线-。所以包装数据类型的null值,能够表示额外的信息,如远程调用失败,异常退出。
- 定义DO/DTO/VO等POJO类时,不要设定任何属性默认值。
反例:POJO类中的createTime默认值是new Date(),但当这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间
DO、DTO、VO等都是POJO类。POJO(Plain Ordinary Java Object)是指只有setter/getter/toString的简单类,包括DO/DTO/BO/VO等。 DO(Data Object):此对象与数据库表结构一一对应,通过DAO层向上传输数据源对象。 DTO(Data Transfer Object):前端给后端传递的数据。 VO(View Object):后端给前端传递的数据。
- 构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法当中。
- POJO类必须写toString方法。使用IDE中的工具:source>generate toString时,如果继承了另一个POJO类,注意在前面加一个super.toString。
说明:在方法抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题
- 禁止在POJO类中,同时存在对应属性xxx的isXxx()和getXxx()方法。
说明:框架在调用属性xxx的提取方法时,并不能确定到哪个方法一定是优先被调用到的。
(五)日期时间
- 日期格式化时,传入pattern中表示年份统一使用小写的y。
说明∶日期格式化时,yyyy表示当天所在的年,而大写的YYYY代表是week in which year ( JDK7之后引入的概念),意思是当天所在的周属于的年份,一周从周日开始,周六结束,只要本周跨年,返回的YYYY就是下一年。
正例:表示日期和时间的格式如下所示︰
new SimpleDateFormat(“yyyy-MM-dd HH:mm:ss”)
- 在日期格式中分清楚大写的M和小写m,大写的H和小写的h分别指代的意义。
说明:日期格式中的这两对字母表意如下:
- 表示月份的是大写的M;
- 表示分钟是小写的m;
- 24小时制的大写的H;
- 12小时制的则是小写的h;
- 获取当前毫秒数:System.currentTimeMIllis();而不是new Date().getTime()。
说明:如果想获取更加精确的纳秒级的时间值,使用System.nanoTime的方式。在JDK8中,针对统计时间等场景,推荐使用Instant类。
- 不允许在程序任何地方中使用
- java.sql.Date
- java.sql.Time
- java.sql.Timestamp
说明:第1个不记录时间,getHours()抛出异常;第2个不记录日期,getYear()抛出异常;第3个在构造方法super((time/1000)*1000),在Timestamp属性fastTime和nanos分别储存和纳秒信息。
反例:java.util.Date.after(Date)进行时间比较时,当入参是java.sql.Timestamp时,会触发JDKBUG(JDK已修复),可能导致比较时的意外结果。
- 不要在程序当中写死一年为365天,避免在公历闰年时出现日期转换错误或程序逻辑错误。
正例 //获取今年的天数 int daysOfThisYear =LocalData.now().lengthOfYear(); //获取指定某年的天数 LocalDate.of(2011,1,1).lengthOfYear(); 反例 //第一种情况:在闰年366天时,出现数组越界异常 int [] dayArray =new int [365] //第二种情况:一年有效期的会员制,今年1月26日注册,硬编码365返回的却是1月25 Calendar calendar =Calendar.getlnstance(); calendar.set(2020,1,26); calendar.add(Calendar.DATE,365);
(六)集合处理
- 关于hashCode和equals 的处理,遵循如下规则∶
- 只要覆写equals,就必须覆写hashCode。
- 因为Set存储的是不重复的对象,依据 hashCode和equals进行判断,所以Set存储的对象必须覆写这两种方法。
- 如果自定义对象作为Map 的键,那么必须覆写hashCode和equals。
说明:String因为覆写了hashCode和equals方法,所以可以愉快地将String对象作为key来使用。
- 判断所有集合内部的元素是否为空,使用isEmpty()方法,而不是 size()==0的方式。
说明︰在某些集合中,前者的时间复杂度为O(1),而且可读性更好。
正例: Map <String, Object> map = new HashMap<>(16); if(map.isEmpty()){ System.out.println("no element in this map."); }
- 在使用java.util.stream.Collectors类的 toMap()方法转为Map集合时,一定要使用含有参数类型为BinaryOperator ,参数名为mergeFunction的方法,否则当出现相同key值时会抛lllegalStateException 异常。
说明∶参数 mergeFunction的作用是当出现key重复时,自定义对value的处理策略。
正例:
(七)并发处理
(八)控制语句
(九)注释规约
(十)前后端规约
(十一)其他
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/110645.html