开发规范是所有程序员开发过程中必须掌握的技能,早期的软件开发过程可能不重视开发规范导致后期维护成本极高,现在国内的大厂都会制定自己的开发规范,完善的开发规范不仅可以提高团队效率,还可以避免很多意外的bug问题。下面我找了几篇关于代码规范重要性的文章,大家可以参考下。
本系列文章将整合 阿里巴巴《Java开发手册》 和 谷歌《Java编程规范》 ,总结Java开发过程的编码规范,并通过具体编码案例给出编码规范的原因,如果总结内容存在问题还望指出。
Java开发规范之OOP规约篇共上中下三篇,具体内容参考 阿里巴巴《Java开发手册》的目录,同时补充 谷歌《Java编程规范》的内容,阿里巴巴规约内容比较丰富,谷歌规约很多实际内容都没有。
Java开发规范之OOP规约篇(中)
9.DO类属性必须匹配数据库字段类型
10.double值转化为BigDecimal对象禁止直接使用构造方法
11.基本数据类型与包装数据类型的使用标准
12.POJO类禁止设定任何属性默认值
13.序列化类尽量避免修改serialVersionUID字段
14.构造方法禁止加入任何业务逻辑
15.POJO类必须写toString方法
16.POJO类中禁止存在对应属性xxx的isXxx()和getXxx()方法
本篇文章内容将承接上一篇 《Java开发规范之OOP规约篇(上)》 继续介绍面向对象程序设计中规范,如果OOP (Object Oriented Programming)含义不够了解,请参考上一篇介绍内容。
Alibaba规约(强制)
定义数据对象DO类时,属性类型要与数据库字段类型相匹配。
Google规约
未明确定义类似规范
说明:DO(Domain Object)领域对象一般是指从现实世界中抽象出来的有形或无形的业务实,常和数据中的表结构对应,如数据库中有一个 Student 学生表,对应程序中需要有一个学生类存储对应数据,可记作 StudentDO。Java实体类的属性类型与数据库表字段类型对应表可以参考这篇文章,点击 链接 跳转查看。
正例:数据库字段的bigint必须与类属性的Long类型相对应。
反例::某个案例的数据库表id字段定义类型bigint unsigned,实际类对象属性为Integer,随着id越来越大,超过Integer的表示范围而溢出成为负数。
Alibaba规约(强制)
为了防止精度损失,禁止使用构造方法BigDecimal(double)的方式把double值转化为BigDecimal对象。
Google规约
未明确定义类似规范
说明:BigDecimal(double)存在精度损失风险,在精确计算或值比较的场景中可能会导致业务逻辑异常。如:BigDecimal g = new BigDecimal(0.1f); 实际的存储值为:0.10000000149
正例:优先推荐入参为String的构造方法,或使用BigDecimal的valueOf方法,此方法内部其实执行了Double的toString,而Double的toString按double的实际能表达的精度对尾数进行了截断。
BigDecimal recommend1 = new BigDecimal("0.1");
BigDecimal recommend2 = BigDecimal.valueOf(0.1);
反例:
BigDecimal number = new BigDecimal(0.1);
System.out.println("number=" + number);
// 输出结果 number=0.1000000000000000055511151231257827021181583404541015625
Alibaba规约
关于基本数据类型与包装数据类型的使用标准如下:
- 【强制】所有的POJO类属性必须使用包装数据类型。
- 【强制】RPC方法的返回值和参数必须使用包装数据类型。
- 【推荐】所有的局部变量使用基本数据类型。
POJO类属性没有初值是提醒使用者在需要使用时,必须自己显式地进行赋值,任何空指针异常问题,或者入库检查,都由使用者来保证。
Google规约
未明确定义类似规范
说明:POJO是Plain OrdinaryJava Object的缩写,可以简单理解为不包含业务逻辑的单纯用来存储数据的 Java类(实际就是普通JavaBean,是为了避免和EJB混淆所创造的简称)。使用POJO名称是为了避免和EJB混淆起来, 而且简称比较直接. 其中有一些属性及其getter setter方法的类,没有业务逻辑,有时可以作为VO(value -object)或DTO(Data Transform Object)来使用。
正例:数据库的查询结果可能是null,因为自动拆箱,用基本数据类型接收有空指针异常风险。
反例:基本数据类型有默认值导致显示异常,比如显示成交总额涨跌情况,即正负x%,x为基本数据类型,调用的RPC服务,调用不成功时,返回的是默认值,页面显示为0%,这是不合理的,应该显示成中划线。所以包装数据类型的null值,能够表示额外的信息,如:远程调用失败,异常退出。
Alibaba规约(强制)
定义DO/DTO/VO等POJO类时,不要设定任何属性默认值
Google规约
未明确定义类似规范
说明:VO(View Object,视图对象)一般作用于前台页面与表示层之间,将所有的数据封装到一起,比如表单数据,这些参数并不一定完全与数据库中表的所有字段均匹配。DTO(Data Transfer Object,数据传输对象)作用于表示层与业务层之间,Action/Controller将接收到的VO对象进行业务逻辑处理,转化或者构造成DTO对象将其传递给service层。DO(Domain Object,领域对象)作用于业务层与dao层之间,service层使用接收到的DTO数据传输对象构造或者重构DO对象,传递到DAO层。DAO(data access object,数据访问对象),负责数据库的操作并为service层提供接口。
反例:POJO类的createTime默认值为new Date(),但是这个属性在数据提取时并没有置入具体值,在更新其它字段时又附带更新了此字段,导致创建时间被修改成当前时间。
Alibaba规约(强制)
序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值。
Google规约
未明确定义类似规范
说明:serialVersionUID 用来表明类的不同版本间的兼容性,serialVersionUID不一致会抛出序列化运行时异常。
补充: Java的序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,JVM会把传来的字节流中的serialVersionUID与本地相应实体(类)的serialVersionUID进行比较,如果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常。当实现java.io.Serializable接口的实体(类)没有显式地定义一个名为serialVersionUID,类型为long的变量时,Java序列化机制会根据编译的class自动生成一个serialVersionUID作序列化版本比较用,这种情况下,只有同一次编译生成的class才会生成相同的serialVersionUID 。
Alibaba规约(强制)
构造方法里面禁止加入任何业务逻辑,如果有初始化逻辑,请放在init方法中。
Google规约
未明确定义类似规范
说明:构造方法一般是类的初始化方法,如果加入业务逻辑代码将非常影响可读性,如果有必须的初始化操作方法可以创建init方法进行操作。
Alibaba规约(强制)
POJO类必须写toString方法。使用IDE中的工具:source> generate toString时,如果继承了另一个POJO类,注意在前面加一下super.toString。
Google规约
未明确定义类似规范
说明:在方法执行抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题。使用lombok框架可以自动生成该方法,但有部分人认为该框架存在隐患。
Alibaba规约(强制)
禁止在POJO类中,同时存在对应属性xxx的isXxx()和getXxx()方法。
Google规约
未明确定义类似规范
说明:框架在调用属性xxx的提取方法时,并不能确定哪个方法一定是被优先调用到。Mybatis 和 Hibernate 框架是根据获取方法找到对应属性,因此上述定义可能存在问题。