一、简介
代码优化,目的有两个,第一个就是为了让我们的代码体积尽可能变小,看起来比较清晰,第二个就是为了提高代码运行效率。个人觉得,在平时编写代码的同时,尽量要求自己,养成良好的编码习惯,一个小的优化点,积攒起来肯定会有好处,也能对我们自己以后的代码风格有好处。本文参考了阿里巴巴开发者手册以及网上一些博客,个人进行编码实践之后,重新总结了一些平时项目中常用的一些代码优化技巧,供大家参考学习。
二、优化方法详解
【1】变量的声明尽量不要以下划线或美元符号开始,也不要以下划线或美元符号结束
//不推荐用法
String _name = "weixiaohuai";
String $name = "weixiaohuai";
String name_ = "weixiaohuai";
String name$ = "weixiaohuai";
//推荐用法(见名知意)
String name = "weixiaohuai";
【2】变量的声明尽量不要使用拼音,也不要使用中文,应该使用英文
//不推荐用法
String xingming = "weixiaohuai";
String 姓名 = "weixiaohuai";
//推荐用法
String stuName = "weixiaohuai";
【3】 类名、方法名、参数名称都必须采用驼峰命名规则(见名知意)
//不推荐用法
String studentname = "weixiaohuai";
// void printstudentname(String studentname) {}
//推荐用法
String studentName = "weixiaohuai";
// void printStudentName(String studentName) {}
【4】常量必须全部使用大写字母,并且多个英文之间用下划线_隔开
//不推荐用法
final int max_num = 1024;
final String accessKey = "test";
//推荐用法
final int MAX_NUM = 1024;
final String ACCESS_KEY = "test";
【5】声明数组的时候尽量采用String[]方式
//不推荐用法
String arr[] = {"a", "b", "c"};
//推荐用法
String[] newArr = {"a", "b", "c"};
【6】声明变量、属性的时候尽量使用通俗易懂的英文,必须随意取名字
//不推荐用法
int a = 10;
//推荐用法
int num = 10;
【7】接口中的方法、属性尽量不要加任何访问修饰符
public interface UserRepository {
//不推荐用法
// public abstract void getUserDetail(Long userId);
// public static final String COMPAN_EMAIL = "12345678@qq.com";
//推荐用法
void getUserDetail(Long userId);
String COMPAN_EMAIL = "12345678@qq.com";
}
【8】枚举类命名尽量加上Enum后缀,Enum成员名称必须使用大写字母,字母间使用下划线_分隔开
public enum OrderStatusEnum {
ORDER_NOT_START("ORDER_NOT_START"),
ORDER_END("ORDER_END"),
ORDER_DOING("ORDER_DOING");
private String value;
OrderStatusEnum(String value) {
this.value = value;
}
public String getValue() {
return value;
}
public static OrderStatusEnum getName(String value) {
switch (value) {
case "ORDER_NOT_START":
return ORDER_NOT_START;
case "ORDER_DOING":
return ORDER_DOING;
case "ORDER_END":
return ORDER_END;
default:
return null;
}
}
}
【9】各层命名规约:
* A) Service/DAO层方法命名规约
* 1) 获取单个对象的方法用get做前缀。 如: getUser()
* 2) 获取多个对象的方法用list做前缀。 如: getUserList()
* 3) 获取统计值的方法用count做前缀。 如:countUser()
* 4) 插入的方法用save/insert做前缀。 如:saveUser()
* 5) 删除的方法用remove/delete做前缀。 如:deleteUser()
* 6) 修改的方法用update做前缀。 如:updateUser()
* B) 领域模型命名规约
* 1) 数据对象:xxxDO,xxx即为数据表名。 如:UserDO
* 2) 数据传输对象:xxxDTO,xxx为业务领域相关的名称。 如:UserDTO
* 3) 展示对象:xxxVO,xxx一般为网页名称。 如: userVO
* 4) POJO是DO/DTO/BO/VO的统称,禁止命名成xxxPOJO.
【10】尽量不要允许任何魔法值(即未经定义的常量)直接出现在代码中
//不推荐用法
String userId = "123456";
String key = "KEY" + userId;
【11】long或者Long初始赋值时,使用大写的L,不能是小写的l,小写容易跟数字1混淆,造成误解。
//不推荐用法
Long chineseScore = 100l;
long mathScore = 90l;
//推荐用法
Long englishScore = 100L;
long networkScore = 90L;
【12】避免通过一个类的对象引用访问此类的静态变量或静态方法,无谓增加编译器解析成本,直接用类名来访问即可
public class Person {
private String personName;
private int age;
public static final String name = "weixiaohuai";
public static void printName() {
}
public static void printNames(int id, String... names) {
for (String name : names) {
System.out.println(name);
}
}
@Override
public String toString() {
return "Person{" +
"personName='" + personName + '\'' +
", age=" + age +
'}';
}
public void sayHello() {
}
}
//不推荐用法
Person person = new Person();
person.printName();
System.out.println(person.name);
//推荐用法
Person.printName();
System.out.println(Person.name);
【13】相同参数类型,相同业务含义,才可以使用Java的可变参数,避免使用Object。 说明:可变参数必须放置在参数列表的最后。
public static void printNames(int id, String... names) {
for (String name : names) {
System.out.println(name);
}
}
//推荐用法
Person.printNames(1, "zhangsan", "lisi", "wangwu");
【14】Object的equals方法容易抛空指针异常,应使用常量或确定有值的对象来调用equals。
//不推荐用法
String personName = null;
System.out.println(personName.equals("zhangsan"));
//推荐用法
System.out.println("zhangsan".equals(personName));
System.out.println(Objects.equals("zhangsan", personName));
【15】序列化类新增属性时,请不要修改serialVersionUID字段,避免反序列失败;如果完全不兼容升级,避免反序列化混乱,那么请修改serialVersionUID值。 说明:注意serialVersionUID不一致会抛出序列化运行时异常。
【16】POJO类必须写toString方法。如果继承了另一个POJO类,注意在前面加一下super.toString。
* 说明:在方法执行抛出异常时,可以直接调用POJO的toString()方法打印其属性值,便于排查问题。
@Override
public String toString() {
return "Person{" +
"personName='" + personName + '\'' +
", age=" + age +
'}';
}
【17】循环体内,字符串的连接方式,使用StringBuilder的append方法进行扩展。
* 说明:反编译出的字节码文件显示每次循环都会new出一个StringBuilder对象,然后进行append操作,最后通过toString方法返回String对象,造成内存资源浪费。
// 不推荐用法
String str = "start";
for (int i = 0; i < 100; i++) {
str = str + "hello";
}
//推荐用法
StringBuilder stringBuilder = new StringBuilder("start");
for (int i = 0; i < 100; i++) {
stringBuilder.append("hello");
}
【18】类成员以及方法尽量严格控制访问权限
* 1) 如果不允许外部直接通过new来创建对象,那么构造方法必须是private。
* 2) 工具类不允许有public或default构造方法。
* 3) 类非static成员变量并且与子类共享,必须是protected。
* 4) 类非static成员变量并且仅在本类使用,必须是private。
* 5) 类static成员变量如果仅在本类使用,必须是private。
* 6) 若是static成员变量,必须考虑是否为final。
* 7) 类成员方法只供类内部调用,必须是private。
* 8) 类成员方法只对继承类公开,那么限制为protected。
【19】使用集合转数组的方法,使用集合的toArray(T[] array),传入的是类型完全一样的数组,大小就是list.size()。
List<String> namesList = new ArrayList<>(10);
namesList.add("zhangsan");
namesList.add("lisi");
String[] namesArr = new String[namesList.size()];
//不推荐用法
// Object[] array = namesList.toArray();
//推荐用法
namesArr = namesList.toArray(namesArr);
System.out.println(Arrays.toString(namesArr));
【20】不要在foreach循环里进行元素的remove/add操作。remove元素请使用Iterator方式,如果并发操作,需要对Iterator对象加锁。
//不推荐用法
List<String> list = new ArrayList<>(10);
list.add("1");
list.add("2");
for (String item : list) {
if ("1".equals(item)) {
list.remove(item);
}
}
//推荐用法
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("1".equals(item)) {
iterator.remove();
}
}
【21】尽量使用entrySet遍历Map类集合KV,而不是keySet方式进行遍历
* 说明: :keySet其实是遍历了2次,一次是转为Iterator对象,另一次是从hashMap中取出key所对应的value。而entrySet只是遍历了一次就把key和value都放到了entry中,效率更高
//不推荐用法
Set<String> keySet = map.keySet();
for (String k : keySet) {
Object value = map.get(k);
System.out.println(k + ", " + value);
}
//推荐用法
Map<String, Object> map = new HashMap<>(10);
map.put("name", "weixiaohuai");
map.put("sex", "male");
map.put("age", "20");
Set<Map.Entry<String, Object>> entrySet = map.entrySet();
for (Map.Entry<String, Object> entry : entrySet) {
String entryKey = entry.getKey();
Object entryValue = entry.getValue();
System.out.println(entryKey + ", " + entryValue);
}
【22】在一个switch块内,每个case要么通过break/return等来终止,要么注释说明程序将继续执行到哪一个case为止;在一个switch块内,都必须包含一个default语句并且放在最后,即使它什么代码也没有
//推荐用法
String switchKey = "a";
switch (switchKey) {
case "a":
break;
case "b":
case "c":
break;
default:
break;
}
【23】获取当前毫秒数尽量使用System.currentTimeMillis(); 而不是new Date().getTime()
//不推荐用法
long currentTimeMillis = System.currentTimeMillis();
//推荐用法
long time = new Date().getTime();
【24】在使用可能抛出运行时异常的代码时,尽量使用预先检查机制来规避运行时异常,不应该使用try-catch来处理这些运行时异常
//不推荐用法
Person p = null;
try {
p.sayHello();
} catch (NullPointerException e) {
e.printStackTrace();
}
//推荐用法
if (null != p) {
p.sayHello();
}
【25】不能在finally块中使用return,finally块中的return返回后方法结束执行,不会再执行try块中的return语句
//不推荐用法
public String say() {
try {
//...
return "a";
} catch (Exception e) {
e.printStackTrace();
} finally {
return "b";
}
}
【26】尽量重用对象,避免不必要的new创建对象
/**
* 优化方法1: 尽量重用对象,避免不必要的new创建对象
*/
String namePrefix = "wei";
String nameSuffix = "xiaohuai";
//不推荐用法
//Java虚拟机会重新创建一个新的对象返回,影响效率
String name = namePrefix + nameSuffix;
System.out.println(name);
//推荐用法
//字符串拼接使用StringBuilder或者StringBuffer
StringBuilder stringBuilder = new StringBuilder("wei");
stringBuilder.append("xiaohuai");
System.out.println(stringBuilder);
【27】在进行IO操作的时候,要及时关闭流对象
//推荐用法
InputStream inputStream = null;
try {
inputStream = new FileInputStream(new File("D:/a.txt"));
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
if (null != inputStream) {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
【28】for循环遍历的时候尽量减少对变量(集合长度)的重复计算
//不推荐用法
List<String> list = new ArrayList<>(10);
list.add("zhangsan");
list.add("lisi");
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//推荐用法
//先计算集合的总长度,避免重复计算,在集合非常大的时候效率会提高很多
for (int i = 0, size = list.size(); i < size; i++) {
System.out.println(list.get(i));
}
【29】在需要用到的时候才创建变量(懒加载)
//不推荐使用
String str = "wangwu";
int idx = 1;
if (idx == 1) {
System.out.println(str);
}
//推荐使用(在需要使用的时候才创建)
if (idx == 1) {
String s = "wangwu";
System.out.println(s);
}
【30】复制大量数据时,使用System.arraycopy(Object src,int srcPos,Object dest,int destPos,int length)
//推荐用法
String[] names = new String[]{"a", "b", "c", "d"};
String[] newNames = new String[names.length];
System.arraycopy(names, 0, newNames, 0, names.length);
//[a, b, c, d]
System.out.println(Arrays.toString(newNames));
【31】在循环中不要不断创建对象引用
//不推荐用法
//这样在内存中会存在10000份person对象的引用,耗内存资源
for (int i = 0; i < 10000; i++) {
Person person = new Person();
//...一系列操作
}
//推荐用法
//内存中只有一份Person对象引用,每次new Person()创建对象,person对象引用指向不同的Person,节省内存资源
Person person = null;
for (int i = 0; i < 10000; i++) {
person = new Person();
//...一系列操作
}
【32】避免随意使用静态变量
//不推荐用法
//Java的垃圾回收器不会回收static对象,如果A类不被卸载,那么引用B指向的B对象会常驻内存,直到程序终止
public class A {
private static B b = new B();
}
【33】常量尽量定义为static final,这样在编译的时候就可以把值放入常量池中,避免计算常量的值
static final String ACCESS_TOKEN = "abcd";
【34】随机访问比较多、顺序插入的场景使用ArrayList,中间插入、删除、更新多、随机访问少的场景使用LinkedList
* 说明: ArrayList底层实现是数组,随机访问直接通过下标获取,速度快,但是删除、中间插入需要移动元素,速度慢 * LinkedList底层实现是链表,插入删除只需要改变指针,速度快,但是随机访问需要循环遍历,速度慢
【35】字符串变量和字符串常量equals的时候将字符串常量写在前面
//不推荐用法
//可能会发生NotPointerException空指针异常
String word = "abc";
if (word.equals("abc")) {
//...
}
//推荐用法
if ("abc".equals(word)) {
//...
}
//或
boolean isEqual = Objects.equals("abc", word);
【36】基本数据类型转为字符串,基本数据类型.toString()是最快的方式、String.valueOf(数据)次之、数据+""最慢
//推荐使用
int i = 10;
String iStr = Integer.toString(i);
String s = String.valueOf(10);
//不推荐
String s1 = i + "";
【37】使用效率高的方法遍历Map,尽量使用entrySet,直接将key/value都查询出来。
//推荐使用
Map<String, Object> map = new HashMap<>(10);
map.put("name", "weixiaohuai");
map.put("age", "20");
Set<Map.Entry<String, Object>> entries = map.entrySet();
for (Map.Entry<String, Object> entry : entries) {
String key = entry.getKey();
Object value = entry.getValue();
}
【38】底层使用可变数组的数据结构尽量指定长度
//推荐用法
List<String> newList = new ArrayList<>(10);
Map<String, Object> newMap = new HashMap<>(10);
【39】String类尽量使用StringBuffer、StringBuilder
//不推荐用法
//java 虚拟机会在堆中创建三个变量,"wei" 、"xiaohuai"、 "weixiaohuai,最终newName指向"weixiaohuai","wei"、"xiaohuai"就没有对象引用它们,需要GC回收,耗性能
String newNamePrefix = "wei";
String newName = newNamePrefix + "xiaohuai";
//推荐用法
//java虚拟机只会在堆中开辟一个空间"wei",执行append时只会在 "wei" 的空间上 + "xiaohuai" , 避免了GC的回收,也避免了内存的浪费
StringBuilder sb = new StringBuilder("wei");
sb.append("xiaohuai");
【40】尽可能多使用三目运算符,代码看起来会比较清晰
* 说明: 对于if-else结构的,视情况优化为三目运算符
//不推荐用法
int a = 10;
if (a > 10) {
System.out.println("a大于10");
} else {
System.out.println("a小于10");
}
//推荐用法
System.out.println(a > 10 ? "a大于10" : "a小于10");
三、总结
以上就是一些Java代码优化的方法,仅供大家参考学习,欢迎大家补充一起学习,实际项目中,注意养成良好的编码习惯,让代码看起来更清晰易懂。