入职半年多了,实际工作开发过程中踩了不少坑,也学了不少招,积累了一些Java编码方面的经验和技巧,现总结于下,与大家一同分享:
- 通过一个函数生成/返回/获取一个对象,在使用这个对象的时候一定要做非空判断。即使这个函数是你自己写的,内部已经做了非空判断或者一定不会产生NPE,作为良好的习惯,也需要这么做。因为别人维护代码的时候不知道你写的这个函数内部是什么情况。关于避免使用大量非空判断解决NPE的问题,可以使用Java8的Optional对象来解决,大家可以参考笔者之前的一篇文章。
- 编码中尽量避免魔法值的出现,包括数字、字符串、字符等,应该使用常量(public static final或private final修饰的变量)或者枚举(Enum)的形式来使用,避免维护的时候难以理解。
- 编码不要出现过长的函数,尽量拆分小函数。小函数的职责要单一(一个函数最好只做一件事儿,不要好几件事儿放在一个小函数中)。小函数的有效行(去掉注释、空行、单独的括号行)尽量不要超过50行。
- 包名全部为小写,一个包名一个单词;类名开头大写,驼峰命名,最好以“名词或动名词”的形式拉力命名;方法名(除构造方法)开头小写,驼峰命名,最好以“动词+名词”的顺序来命名。
- 在一个函数内,不引起歧义的情况下,变量的命名应该尽量简洁,例如:
// 修改前
List<CustomTransferDTO> customTransferDTOList = new ArrayList();
// 修改后,若用于中间过程
List<CustomTransferDTO> dtos = new ArrayList();
// 修改后,若用于返回结果
List<CustomTransferDTO> results = new ArrayList();
- 避免if的多次、深层嵌套,可把异常过程放在if语句块后,而不是把正常过程放在if语句块后。即将if条件为真并执行修改为if条件为假返回或抛异常。例如:
//修改前
CustomTranferDTO dto = getCustomTranferDTOById(CUSTOM_TRANSFER_DTO_ID);
if(!Objects.isNull(dto)){
String dtoName = dto.getName();
if(!Objects.isNull(dtoName )){
String[] results = dtoName.splite(',');
……………………
return results;
}
}
return null;
//修改后
CustomTranferDTO dto = getCustomTranferDTOById(CUSTOM_TRANSFER_DTO_ID);
if(Objects.isNull(dto)){
return null;
}
String dtoName = dto.getName();
if(Objects.isNull(dtoName )){
return null;
}
String[] results = dtoName.splite(',');
……………………
return results;
- 方法的成员变量是没有默认初值的,需要指定;类的成员变量是有默认初值的,可以不指定。但工作中,最好对类的成员变量指定初值,即使与默认值相同,也应该人为指定,养成良好习惯,便于维护人员理解。例如:
//修改前
Public MyClass{
boolean complete;
int i;
}
//修改后
Public MyClass{
boolean complete = false;
int i = 0;
}
- Java函数的参数中,对象是引用传递。一个函数可以通过两种方式处理并得到对象,一是把该对象作为参数,二是返回该对象。如果函数外部对象一开始是新实例化出来的,或者可以为空的,则应该使用后者,否则使用前者。举例说明:
// 为了得到一个对象CustomObject,以下两种方式都是可行的:
// 方式一,将对象作为参数传入函数
private void fun(CustomObject customObject){
customObject.setName("NULL");
}
CustomObject customObject = new CustomObject();
fun(customObject);
// 方式二,将对象作为函数的返回值
private CustomObject fun(){
CustomObject customObject = new CustomObject();
customObject.setName("NULL");
return customObject;
}
CustomObject customObject = fun();
// 外部对象CustomObject一开始是新实例化出来的,或者可以为空的,此时应该使用第二种方式更便于阅读理解,有一种“生成、产生、获取”的含义;
// 若外部对象CustomObject一开始是非空的,此时应该使用第一种方式更便于阅读理解,有一种“运算、操作、变化”的含义
- 方法的参数列表中,参数顺序应该遵从:传入/处理参数靠前,传出/生成参数靠后;先使用的参数靠前。
- 涉及磁盘和网络IO的操作,例如数据库存储、Restful接口调用等,要谨慎使用for循环一条一条去处理,这样耗时会很慢,正确的做法是进行聚合后在一次批量执行,例如数据库存储时,不要在for循环中逐条save(),而是for循环把结果加入一个List,直接批量对该List进行sava()操作。
- 多线程的情况应该考虑使用线程安全的变量,例如使用ConcurrentHashMap代替HashMap。如果非要用到线程不安全的变量,例如int(该例子举得不好,int有线程安全的替代方案,即AtomicInteger),那么对于这个int变量的赋值取值,不要直接使用,而是写一个get、set方法,并对该方法加锁限制。
- 变量在使用的时候再去实例化,不要全部在方法顶部实例化出来,这样维护过程中阅读理解不方便,后面遇到这个变量,需要到方法前面去查找。
- 使用集合和循环的地方,考虑下是否可以使用Lambda表达式简化代码,大家可以参考笔者之前的文章。
总结了十余条笔者觉得比较容易忽视的编码经验和技巧,后续有新的干货,再行补充。