开发规范
- 1,使用普通for循环遍历,应实现减少对变量的重复计算
- 2,使用equals进行空字符判断时,应避免空指针异常(NullPointerException)
- 3,尽量避免不必要的创建
- 4,尽量使用移位代替 a/b 和 a*b
- 5,尽量确定StringBuffer的容量
- 6,尽量使用System.arraycopy ()代替通过来循环复制数组
- 7,如果用到双层for循环,尽量使用[Map]()集合进行优化
- 8,使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。
- 9,使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
- 10,使用条件分组或者switch来代替多个if-else语句
- 11,使用PreparedStatement而不是Statement
- 12,尽可能使用基本类型
- 13,避免使用+来拼接字符串
- 小结:
1,使用普通for循环遍历,应实现减少对变量的重复计算
// 例如
for(int i=0;i<list.size();i++)
// 改为
for(int i=0,len=list.size();i<len;i++)
2,使用equals进行空字符判断时,应避免空指针异常(NullPointerException)
String myqxin = null;
// 列如
if (myqxin.equals(""))
// 改为
if("".equals(myqxin))
3,尽量避免不必要的创建
// 列如:
A a = new A();
if(i==1){
list.add(a);
}
// 改为:
if(i==1){
A a = new A();
list.add(a);
}
4,尽量使用移位代替 a/b 和 a*b
int a = 12;
// 列如
System.out.println(a/2);
// 改为
System.out.println(a>>1);
int b = 12;
// 例如
System.out.println(b*4);
// 改为
System.out.println(b<<2);
5,尽量确定StringBuffer的容量
// 例如
StringBuffer buffer = new StringBuffer(501);
说明:StringBuffer 的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建 StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。
6,尽量使用System.arraycopy ()代替通过来循环复制数组
String[] arr = {"1","2","3"};
String[] brr = new String[arr.length];
System.arraycopy(arr,0,brr,0,arr.length);
System.out.println(Arrays.toString(brr)); // 结果 [1,2,3]
参数1:源数组,参数2:源数组起始位置,参数3:目标数组,参数4:目标数组起始位置,参数5:复制的长度
注意:参数2起始的位置,后面的长度,要小于等于参数5的复制长度,例如以下
String[] arr = {"1","2","3"};
String[] brr = new String[arr.length];
// System.arraycopy(arr,1,brr,0,arr.length); 抛出异常,ArrayIndexOutOfBoundsException
System.arraycopy(arr,1,brr,0,arr.length-1);
System.out.println(Arrays.toString(brr)); // 结果 [1,2,null]
7,如果用到双层for循环,尽量使用Map集合进行优化
ArrayList<User> ulist = new ArrayList<>();
User u1 = new User("1", "冷言1");
User u2 = new User("2", "冷言2");
User u3 = new User("3", "冷言3");
User u4 = new User("4", "冷言4");
ulist.add(u1);
ulist.add(u2);
ulist.add(u3);
ulist.add(u4);
ArrayList<Person> plist = new ArrayList<>();
Person p1 = new Person("1", "男");
Person p2 = new Person("2", "女");
Person p3 = new Person("3", "女");
Person p4 = new Person("4", "男");
plist.add(p1);
plist.add(p2);
plist.add(p3);
plist.add(p4);
// 例如
for (User u : ulist) {
for (Person p : plist) {
if(u.getUid().equals(p.getPid())){
u.setSex(p.getSex());
}
}
}
for (User user : ulist) {
System.out.println(user);
}
// 展示结果为
// User{uid='1', name='冷言1', sex='男'}
// User{uid='2', name='冷言2', sex='女'}
// User{uid='3', name='冷言3', sex='女'}
// User{uid='4', name='冷言4', sex='男'}
//改为
Map<String,Person> list2Map = plist.stream().collect(Collectors.toMap(Person::getPid, Function.identity()));
for (User user : ulist) {
Person person = list2Map.get(user.getUid());
if(person != null){
user.setSex(person.getSex());
}
}
// 展示结果为
// User{uid='1', name='冷言1', sex='男'}
// User{uid='2', name='冷言2', sex='女'}
// User{uid='3', name='冷言3', sex='女'}
// User{uid='4', name='冷言4', sex='男'}
8,使用集合转数组的方法,必须使用集合的 toArray(T[] array),传入的是类型完全一致、长度为 0 的空数组。
ArrayList<String> list = new ArrayList<>();
list.add("冷言");
list.add("魅言倾馨");
// 例如
Object[] array = list.toArray();
System.out.println(Arrays.toString(array));
// 改为
String[] array1 = list.toArray(new String[0])
System.out.println(Arrays.toString(array1));
说明:使用 toArray 带参方法,数组空间大小的 length,
1) 等于 0,动态创建与 size 相同的数组,性能最好。
2) 大于 0 但小于 size,重新创建大小等于 size 的数组,增加 GC 负担。
3) 等于 size,在高并发情况下,数组创建完成之后,size 正在变大的情况下,负面影响与 2 相同。
4) 大于 size,空间浪费,且在 size 处插入 null 值,存在 NPE 隐患
9,使用 entrySet 遍历 Map 类集合 KV,而不是 keySet 方式进行遍历。
HashMap<Integer, String> map = new HashMap<>();
map.put(1,"冷言");
map.put(2,"魅言倾馨");
// 例如
for (Integer integer : map.keySet()) {
System.out.println(integer);
}
// 改为
for (Map.Entry<Integer, String> entry : map.entrySet()) {
System.out.println(entry.getKey());
}
说明:keySet 其实是遍历了 2 次,一次是转为 Iterator 对象,另一次是从 hashMap 中取出 key 所对应的
value。而 entrySet 只是遍历了一次就把 key 和 value 都放到了 entry 中,效率更高。如果是 JDK8,使用
Map.forEach 方法。
10,使用条件分组或者switch来代替多个if-else语句
我相信大部分都看到过好多if-else吧,这样也是会影响性能的。如果在for、while等循环语句使用这一长串的if-else,情况会变得更糟。
如果我们的业务逻辑中有很多的条件,我们可以尝试着将条件分组并且返回一个布尔值,然后在将其用于if语句,下面看一个示例
// 错误示范
if(condition1) {
if(condition2){
if(condition3 || condition4) {
System.out.println(1);
}
else {
System.out.println(2);
}
}
}
// 优化改正
boolean result = (condition1 && condition2) && (condition3 || condition4)
if (result) {
System.out.println(1);
} else {
System.out.println(2);
}
11,使用PreparedStatement而不是Statement
现在应该比较少用JDBC API进行SQL查询了,但是我觉得还是有必要了解一下。
对于参数化查询,PreparedStatement比Statement更有优势,因为PreparedStatement对象被编译一次并执行多次。Statement对象在每次被调用时都会被编译和执行。
此外,PreparedStatement对象是安全的,可以避免SQL注入攻击。
12,尽可能使用基本类型
因为基本类型存储在栈内存中,而对象存储在堆内存中。如果可以的话,我们应该尽可能使用基本类型而非对象,因为栈内存的访问速度比堆内存快。
因此在某些情况下,定义一个变量或者数组,我们可以使用int而非Integer,double而非Double。
13,避免使用+来拼接字符串
从JDK5开始,Java编译器就做了优化,使用+号拼接字符串,编译器编译后实际就自动优化为使用StringBuilder。
而且String是final类,用String创建的对象无法重复使用。因此,如果我们需要连续拼接,使用+号拼接字符串将导致创建多个String对象,从而会占用更多的堆内存。
一般来说,当字符串不多的时候,+号与StringBuilder的拼接效率其实相差无几;但是如果涉及到单线程循环拼接的时候,我们最好还是使用StringBuilder以保证性能上的优化。
String str = "myqxi";
for (int i = 0; i < count; i++) {
str = str + "-" + i;
}
// 改正示例
StringBuilder stringBuilder = new StringBuilder("myqxin");
for (int i = 0; i < count; i++) {
stringBuilder.append("-");
stringBuilder.append(i);
}
小结:
以上只是我目前所接触到的