代码这样写,它不香吗
前言
好的代码习惯可以让开发更有效率,阅读更有流畅,后续维护更方便。
本文记录的是我在JAVA开发中使用的一些小技巧,也有一些是和同事交流分享的,更有一部分是在浏览博客中有所得,下面进入正题。
避免过多过于频繁地创建java对象
反例1
public class Example{
public void clear(){
// 清空数据
}
}
for ( ) {
Example ex = new Example();
ex.create().andIdEqual();
// 查询数据
mapper.select(ex);
}
反例1中在每次的for循环中都会进行 new Example() ,这样会导致系统要在heap中多次创建对象,占用内存,并且还要花费时间进行GC。
建议尽量重用已创建的对象,可提升程序执行效率。
正例1
Example ex = new Example();
for ( ) {
ex.create().andIdEqual();
// 查询数据
mapper.select(ex);
ex.clear();
}
反例2
Example ex = new Example();
if (i == 1) {
ex.create().andIdEqual();
// 查询数据
mapper.select(ex);
}
反例2中,无论 i==1 是否成立,程序都要创建一个Example对象,效果不如正例2。
正例2
if (i == 1) {
Example ex = new Example();
ex.create().andIdEqual();
// 查询数据
mapper.select(ex);
}
减少重复计算
反例
for (int i=0; i< list.size(); i++) {
// do something
}
反例中每次循环都要进行计算 list.size() ,这没有必要,不如在初始化阶段进行,如正例所示
正例
for (int i=0, listSize = list.size(); i< listSize; i++) {
// do something
}
尽量使用已缓存的对象
反例
BigDecimal a = new BigDecimal("0");
BigDecimal b = new BigDecimal("10");
BigDecimal会在加载时将一些常用的数值缓存到常量池中,能使用缓存值尽量使用缓存值,可以节省加载时间及内存空间。
同样,遇到别的类型在创建时先检查一下是否有已缓存的数据。
正例
BigDecimal a = BigDecimal.ZERO;
BigDecimal b = BigDecimal.TEN;
StringBuffer,StringBuilder
反例
public void test2() {
StringBuffer sbu = new StringBuffer();
sbu.append("123").append(456);
System.out.println(sbu.toString());
}
都知道StringBuffer线程安全但执行效率低,StringBuilder线程不安全但执行效率高。
在方法体中,建议使用StringBuilder提升执行效率,原因是执行方法时都是在stack内操作的,不存在线程不安全的问题。
正例
public void test2() {
StringBuilder sbu = new StringBuilder();
sbu.append("123").append(456);
System.out.println(sbu.toString());
}
获取中间数,注意防止溢出
反例
public void test2() {
int start = 2147483500;
int end = 2147483647;
int mid = (start + end) / 2;
System.out.println(mid);// 结果是-74
}
反例中,start + end 很明显会溢出,得到的mid结果肯定是负值,和实际结果不符。
正例
public void test2() {
int start = 2147483500;
int end = 2147483647;
int mid = start + (end - start) / 2;
System.out.println(mid);// 结果是2147483573
}
类型之间的转换
反例
public void test2() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("a", "abc");
map.put("b", 123);
// 获取b的值
Integer b = (Integer) map.get("b");
System.out.println(b);
}
曾经的痛 当时有人将map中b的值改为 字符串123 ,导致在获取b的值进行类型转换时出现类型转换异常,后来改为正例方式,避免了类型转换异常。
正例
public void test2() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("a", "abc");
map.put("b", "123");
// 获取b的值
Integer b = Integer.valueOf(String.valueOf(map.get("b")));
System.out.println(b);
}
String 的 split 方法,传入的分隔字符串是正则表达式
反例
public void test2() {
"a.ab.abc".split("."); // 结果为[]
"a|ab|abc".split("|"); // 结果为["a", "|", "a", "b", "|", "a", "b", "c"]
}
String 的 split 方法,要求传入的分隔字符串是正则表达式,针对特殊字符(如.\[]()|等)需要做转义处理。
正例
public void test2() {
"a.ab.abc".split("\\."); // 结果为["a", "ab", "abc"]
"a|ab|abc".split("\\|"); // 结果为["a", "ab", "abc"]
}
Mysql语句中禁止使用双引号
反例
UPDATE sys_user SET user_name = "测试账号" WHERE user_code = "test" and user_no = "no";
mysql语句中禁止使用双引号,双引号会有sql截断风险,建议只使用单引号。
反例中WHERE语句可能会被解析为user_code = “test and user_no = no”,至于为什么会小概率解析成这样,尚未查明原因,为了避免出现问题,我都要求公司的同事sql中只使用单引号。
正例
UPDATE sys_user SET user_name = '测试账号' WHERE user_code = 'test' and user_no = 'no';
数据库字段尽量设置缺省值
反例
CREATE TABLE IF NOT EXISTS sys_user (
user_id INT(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
user_name VARCHAR(45) DEFAULT '' COMMENT '名称',
user_status INT(2) DEFAULT NULL COMMENT '状态,1正常',
PRIMARY KEY (user_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统用户表';
有一次,我从数据库读user数据进行处理,出现空指针异常,原因是user的status为null,在拆箱时会进行user.getUserStatus().intValue()操作转为基本类型。
if (user.getUserStatus() == 1) { // 报空指针异常
// do something
}
正例
CREATE TABLE IF NOT EXISTS sys_user (
user_id INT(10) NOT NULL AUTO_INCREMENT COMMENT '主键id',
user_name VARCHAR(45) DEFAULT '' COMMENT '名称',
user_status INT(2) DEFAULT 1 COMMENT '状态,1正常',
PRIMARY KEY (user_id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='系统用户表';
BigDecimal的数值取反
反例
BigDecimal fuyi = new BigDecimal("-1");
BigDecimal aa = BigDecimal.TEN.multiply(fuyi);// 结果为-10
正例
BigDecimal aa = BigDecimal.ZERO.subtract(BigDecimal.TEN);// 结果为-10
正例比起反例,少创建一个对象,并且CPU操作减法比操作乘法更快。
三元运算符
示例
User是对应数据库user表的实体类,user_stat是其中的一个字段。
User user = service.getOneUser();
hashMap.put("userStat", user == null ? 9 : user.getUserStat());
经过JVM编译后,JVM会显式调用intValue()方法然后再通过valueOf()将userStat的值转为Integer对象。
User user = this.service.getOneUser();
hashMap.put("userStat", Integer.valueOf(user == null ? 9 : user.getUserStat().intValue()));
当数据库中user_stat的值为null时,在调用intValue()会报空指针异常。
使用三元运算符需要注意。
Map
Map<String, String> map = service.getMap();
if (map.get("123") == null) {
map.put("123", "123123");
}
System.out.println(map);
可优化为
Map<String, String> map = service.getMap();
map.putIfAbsent("123", "123123");
System.out.println(map);