Java编码技巧之高效代码

1 成员变量无需更改的时候,尽量定义位静态的

无论一个类实例化多少对象,它的静态变量只有一份拷贝

反列:
1
2
3
4
public class HttpConnection {
private final long timeout = 5L;
...
}
正列:
1
2
3
4
public class HttpConnection {
private static final long TIMEOUT = 5L;
...
}
代码实战
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class Static {
private static int count=0;
//private int count=0;

public static int getCount() {
return count;
}

private static void add(){
count++;
}
Static() {
// TODO Auto-generated constructor stub
add();
}
public static void main(String[] args) {
Static static1 =new Static();
System.out.println("start:"+static1.getCount());
for(int i=0;i<10;i++){
static1=new Static();
}
System.out.println("end:"+static1.getCount());
}
}
控制台输出:
start:1
end:11
如果不使用静态变量
start:1
end:1


2 如果变量的初值会被覆盖,就没有必要给变量赋初值

反例:
1
2
3
4
5
6
List<UserDO> userList = new ArrayList<>();
if (isAll) {
userList = userDAO.queryAll();
} else {
userList = userDAO.queryActive();
}
正列:
1
2
3
4
5
6
List<UserDO> userList;
if (isAll) {
userList = userDAO.queryAll();
} else {
userList = userDAO.queryActive();
}

3 尽量使用函数内的基本类型临时变量

在函数中,基本类型的的参数和变量都保存在栈中,访问速度快 引用类型的参数和临时变量引用都保存在栈中,而实际内容在堆中存储,访问较慢,在类中,任何类型的成员变量都保存在堆中,访问慢

反例:
1
2
3
4
5
6
7
8
9
public final class Accumulator {
private double result = 0.0D;
public void addAll(@NonNull double[] values) {
for(double value : values) {
result += value;
}
}
...
}
正列:
1
2
3
4
5
6
7
8
9
10
11
public final class Accumulator {
private double result = 0.0D;
public void addAll(@NonNull double[] values) {
double sum = 0.0D;
for(double value : values) {
sum += value;
}
result += sum;
}
...
}

4 尽量不使用反射赋值对象

用反射赋值对象,主要优点是节省了代码量,主要缺点却是性能有所下降 (BeanUtils.copyProperties)

反例:
1
2
3
4
5
6
7
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
UserVO userVO = new UserVO();
BeanUtils.copyProperties(userDO, userVO);
userVOList.add(userVO);
}
正列:
1
2
3
4
5
6
7
8
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
UserVO userVO = new UserVO();
userVO.setId(userDO.getId());
...
userVOList.add(userVO);
}

5 尽量使用基本数据类型作为方法参数类型,避免不必要的装箱和拆箱和空指针判断

反例:
1
2
3
4
5
public static double sum(Double value1, Double value2) {
double double1 = Objects.isNull(value1) ? 0.0D : value1;
double double2 = Objects.isNull(value2) ? 0.0D : value2;
return double1 + double2;
}
正列:
1
2
3
public static double sum(double value1, double value2) {
return value1 + value2;
}

6 尽量使用基本数据类型作为方法返回值类型,避免不必要的装箱和拆箱和空指针判断

在JDK类库的方法中,很多方法返回值都采用了基本数据类型,首先是为了避免不必要的装箱和拆箱,其次是为了避免返回值的空指针判断。比如:Collection.isEmpty()和Map.size()。

反例:
1
2
3
4
5
6
7
8
9
10
11
12
13
public static Boolean isValid(UserDO user) {
if (Objects.isNull(user)) {
return false;
}
return Boolean.TRUE.equals(user.getIsValid());
}

// 调用代码
UserDO user = ...;
Boolean isValid = isValid(user);
if (Objects.nonNull(isValid) && isValid.booleanValue()) {
...
}
正列:
1
2
3
4
5
6
7
8
9
10
11
12
public static boolean isValid(UserDO user) {
if (Objects.isNull(user)) {
return false;
}
return Boolean.TRUE.equals(user.getIsValid());
}

// 调用代码
UserDO user = ...;
if (isValid(user)) {
...
}

7 尽量减少方法的重复调用

反例:
1
2
3
4
List<UserDO> userList = ...;
for (int i = 0; i < userList.size(); i++) {
...
}
正列:
1
2
3
4
5
List<UserDO> userList = ...;
int userLength = userList.size();
for (int i = 0; i < userLength; i++) {
...
}

8 尽量不要在循环体外定义对象引用

在老版JDK里,建议“尽量不要在循环体内定义对象引用”,但是在新版JDK已经做了优化。通过对编译后的字节码分析,对象引用定义在循环体外和循环体内没有本质的区别,运行效率基本上一样。反而,根据“ 局部变量作用域最小化 ”,变量定义在循环体内更科学更便于维护,避免了大对象延长生命周期导致延缓回收问题 。

反例:
1
2
3
4
5
6
7
8
9
UserVO userVO;
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
userVO = new UserVO();
userVO.setId(userDO.getId());
...
userVOList.add(userVO);
}
正列:
1
2
3
4
5
6
7
8
List<UserDO> userDOList = ...;
List<UserVO> userVOList = new ArrayList<>(userDOList.size());
for (UserDO userDO : userDOList) {
UserVO userVO = new UserVO();
userVO.setId(userDO.getId());
...
userVOList.add(userVO);
}

9 尽量不要使用正则表达式匹配

正则表达式匹配效率较低,尽量使用字符串匹配操作。

反例:
1
2
3
String source = "a::1,b::2,c::3,d::4";
String target = source.replaceAll("::", "=");
Stringp[] targets = source.split("::");
正列:
1
2
3
String source = "a::1,b::2,c::3,d::4";
String target = source.replace("::", "=");
Stringp[] targets = StringUtils.split(source, "::");

10 尽量重复使用同一缓冲区

针对缓冲区,Java虚拟机需要花时间生成对象,还要花时间进行垃圾回收处理。所以,尽量重复利用缓冲区。

反例:
1
2
3
4
5
6
StringBuilder builder1 = new StringBuilder(128);
builder1.append("update t_user set name = '").append(userName).append("' where id = ").append(userId);
statement.executeUpdate(builder1.toString());
StringBuilder builder2 = new StringBuilder(128);
builder2.append("select id, name from t_user where id = ").append(userId);
ResultSet resultSet = statement.executeQuery(builder2.toString());
正列:
1
2
3
4
5
6
StringBuilder builder = new StringBuilder(128);
builder.append("update t_user set name = '").append(userName).append("' where id = ").append(userId);
statement.executeUpdate(builder.toString());
builder.setLength(0);
builder.append("select id, name from t_user where id = ").append(userId);
ResultSet resultSet = statement.executeQuery(builder.toString());

11 在多线程中,尽量使用线程安全类

使用线程安全类,比自己实现的同步代码更简洁更高效。

反例:
1
2
3
4
5
6
7
private volatile int counter = 0;
public void access(Long userId) {
synchronized (this) {
counter++;
}
...
}
正列:
1
2
3
4
5
private final AtomicInteger counter = new AtomicInteger(0);
public void access(Long userId) {
counter.incrementAndGet();
...
}