1禁用魔法值
什么是魔法值?其应该是C和C++语言中的一个概念,在字节码层面被用作内存地址的偏移量。在JAVA中,这个概念被赋予了新的意义,通常是指在代码中直接出现的数值或字符串,只有在这个数值记述的那部分代码才能明确其含义的数值或字符。
禁用的原因:
- 一是可读性差,不代出代码上下文根本不知道描述的是什么;无法辨识1、2是什么含义,而如果换成常量名,如“USER_STATUS_NORMAL”“USER_STATUS_EXCEPTION”等,代码的可读性会大大提高,开发人员能够更直观地理解代码的功能和逻辑。
- 代码可维护性低,如果多个地方使用到该值,想将其变更,需要分散地查找替换不易维护;但如果用常量如“MAX_CONNECTIONS”,那么只需要在定义常量的地方修改一次值,所有引用该常量的地方都会自动更新,大大降低了维护成本。
- 不便于代码复用,如多个类或方法中需要用到该值都得重新编写代友一,无法直接复用,还容易出错。
比如说以下示例中的zhangSan,
if ("zhangSan".equals(key)) {
update.set("password1", document.getString("zhangSan"));
String ufPassWord = URLDecoder.decode(document.getString("zhangSan"), "UTF-8");
String password = MD5Util.MD5Encode(MD5Util.decrypt(zhangSan));
update.set(key, password); //密码保存
}
修改后
final String PASSWORD_CONTENT = "zhangSan";
if (PASSWORD_CONTENT .equals(key)) {
update.set("password1", document.getString(PASSWORD_CONTENT));
String ufPassWord = URLDecoder.decode(document.getString(PASSWORD_CONTENT), "UTF-8");
String password = MD5Util.MD5Encode(MD5Util.decrypt(PASSWORD_CONTENT));
update.set(key, password); //密码保存
}
2使用构造方法替换类变量的注入
原因是
- 避免潜在的错误:如果使用字段注入,可能会在对象尚未完全构建之前就尝试访问依赖项,从而导致空指针或其它运行时错误。而构造函数注入则确保了所有依赖在对象构建过程中已被正确注入,即对象创建时就被明确注入了依赖,从而避免问题产生。
- 提高可测性:在单元测试中,通过构造函数注入依赖项,可以更容易使用Mock对象来替换实际依赖项,从而提高测试的灵活性和可维护性。
@Service
public class CommonService {
@Autowired
private MongoTemplate mongoTemplate;
}
应修改为
@Service
public class CommonService {
private MongoTemplate mongoTemplate;
@Autowired
public CommonService (MongoTemplate mongoTemplate){
this.mongoTemplate=mongoTemplate;
}
}
3 去掉没有引用的import
原因是
- 无用的import会让代码显得不够整洁
- 编译速度可能受影响:import会让编译器在编译时额外处理一些信息,过多的无用的import显示会影响到编译器的编译速度。虽然可以忽略不计,但必竟没用,何必留它。
- 产生错误,在开发中,如果引入的类名与未使用import中的同名类,就可能出现编译器无法确定使用哪个类的问题.
代码中有一些为空的处理可用Jdk1.8中的Optional替换
原因是
- 代码好看,写法优雅,再通俗点是有可能被装B到
- 避免NullPointerException:optional可以显式地表示一个值是存在还是不存在,从而减少了因为忘记检查
null
而引发的NullPointerException
- 支持函数式编程、支持链式调用、还可以通过orElse、orElseGet、orElseThrow等提供默认值和替代方案
// 传统方式
String value = getValue();
if (value != null) {
// 处理非空值
} else {
// 处理空值
}
修改后
Optional<String> optionValue=Optional.ofNullable(getValue);
optionValue.ifPresent(val ->{
//处理非空值
});
optionValue.orElse(“defalut”);
Optional介绍
Optional 类是 Java 8 引入的一个新特性,旨在解决空指针异常(NullPointerException)问题,提供一种优雅的处理 null 值的方式。Optional 类是一个容器,它可以包含一个值或者不包含任何值(即为空)。使用 Optional 可以显式地表示一个值可能存在也可能不存在,从而避免直接使用 null 值。
如何使用 Optional
创建 Optional 对象:
- 使用 Optional.of(value) 创建一个包含非空值的 Optional 对象。
- 使用 Optional.ofNullable(value) 创建一个可能包含空值的 Optional 对象。
- 使用 Optional.empty() 创建一个空的 Optional 对象。
检查和获取值:
- 使用 isPresent() 方法检查 Optional 是否包含值。
- 使用 get() 方法获取 Optional 中的值,如果 Optional 为空,则抛出 NoSuchElementException。
- 使用 orElse(other) 方法获取值,如果 Optional 为空,则返回指定的默认值。
- 使用 orElseGet(Supplier) 方法获取值,如果 Optional 为空,则使用 Supplier 提供的方法生成一个默认值。
- 使用 orElseThrow(Supplier) 方法获取值,如果 Optional 为空,则使用 Supplier 提供的方法抛出异常。
链式操作: - 使用 map(Function) 方法对 Optional 中的值进行转换。
- 使用 flatMap(Function) 方法对 Optional 中的值进行转换,该转换函数本身返回一个 Optional 对象。
- 使用 filter(Predicate) 方法过滤 Optional 中的值。
解决的问题
- 减少空指针异常:通过使用 Optional,可以避免在代码中直接使用可能为 null 的值,从而减少 NullPointerException 的发生。
- 提高代码可读性:Optional 强制开发者显式处理可能为空的情况,使得代码更加清晰。
- 更好的API设计:方法可以返回 Optional 对象,而不是返回 null,这样可以明确告知调用者结果可能不存在
示例:假设我们有一个用户类 User,其中包含一个地址类 Address,地址类中有一个城市字段 city。我们想要获取用户的城市的名称,但是用户、地址或城市可能为 null。
public class User {
private Address address;
// getters and setters
}
public class Address {
private String city;
// getters and setters
}
// 使用 Optional 避免 NullPointerException
public String getCityName(User user) {
return Optional.ofNullable(user)
.map(User::getAddress)
.map(Address::getCity)
.orElse("Unknown City");
}
//在这个例子中,如果 user、address 或 city 为 null,getCityName 方法将返回 "Unknown City",而不是抛出 NullPointerException。
注意事项
- Optional 主要用于解决返回值可能为空的情况,不应用于字段或方法参数。
- 过度使用 Optional 可能会导致代码变得复杂,应该谨慎使用。
- Optional.get() 方法在 Optional 为空时会抛出异常,因此在使用前最好先使用 isPresent() 或其他方法进行检查。
- 通过合理使用 Optional,可以使代码更加健壮和易于维护。
4 避免嵌套的判断
if(!CollectionUtils.isEmpty(statusNameList)){
if(statusNameList.size() == 1){
it.put("statusName", statusNameList.get(0));
}
}
可修改为
if(!CollectionUtils.isEmpty(statusNameList) && statusNameList.size() == 1){
it.put("statusName", statusNameList.get(0));
}
//或者修改为
Optional.ofNullable(statusNameList)
.filter(list -> list.size() == 1)
.ifPresent(list -> it.put("statusName", list.get(0)));
类和方法不要太长
一个类也好,一个方法也好,真心不要太长,把所有的逻辑一股脑写在一起。如果这么做了,后面维护时,去找问题时真的很累,由期是交由本身对此代码逻辑不熟悉的人,眼累、心累,满脑会飘菜刀的,结合单一职责原则SRP,每个方法应该只做一件事,如果逻辑真的很复杂,建议拆成多个小方法,不要循环套训话,判断套判断的。方法长度建议在5-20行适中(当然这不是标准),这样会让逻辑清晰、也有勇气看下去,也便于排查问题或测试。
有的方法返回可以减少变量
public Document getAreaSecurityTest(){
Document info = new Document();
return info;
}
修改为
public Document getAreaSecurity(){
return new Document();
}
避免在判断逻辑在使用Boolean类型
原因:
Boolean类型只有在需求表示一个可能为null的布尔值时使用,它是一个对象,使用时涉及到自动装箱(boolean到Boolean)和拆箱的操作(Boolean到boolean),底层是有性能开销的。Boolean对象占用现多的内存空间,因为它是一个对象,需要额外的内存来存储对象头和引用。
Boolean b = false
if (b) { // Noncompliant, it will throw NPE when b == null
foo();
} else {
bar();
}
修改为
//这段代码只是想表述不要用包装类
boolean b=false;
if(b){
....
}else{
....
}
建议常规场景下StringBuild替换StringBuffer
StringBuilder 和 StringBuffer 都是 Java 中用于处理可变字符串的类,但它们之间有一些关键的区别:
1.线程安全性:
- StringBuffer 是线程安全的,它的所有公共方法都是同步的,可以在多线程环境中安全使用。
- StringBuilder 不是线程安全的,它的方法没有同步,因此在单线程环境中性能更好。
2.性能:
- 由于 StringBuilder 不需要同步,所以在单线程环境中,它的性能通常优于 StringBuffer。
选择使用 StringBuilder 替换 StringBuffer 的情况包括
- 单线程环境:如果您的代码运行在单线程环境中,或者您确定在多线程环境中对字符串的操作是原子的,那么使用 StringBuilder 可以获得更好的性能。
- 性能敏感的代码:在性能关键的代码段中,如果字符串操作是主要的性能瓶颈,使用 StringBuilder 可能会带来性能提升。
- 不需要线程安全:如果您确定您的代码不需要线程安全,或者您有其他机制来保证线程安全,那么使用 StringBuilder 是合适的。