一、简介

代码优化,目的有两个,第一个就是为了让我们的代码体积尽可能变小,看起来比较清晰,第二个就是为了提高代码运行效率。个人觉得,在平时编写代码的同时,尽量要求自己,养成良好的编码习惯,一个小的优化点,积攒起来肯定会有好处,也能对我们自己以后的代码风格有好处。本文参考了阿里巴巴开发者手册以及网上一些博客,个人进行编码实践之后,重新总结了一些平时项目中常用的一些代码优化技巧,供大家参考学习。

二、优化方法详解

【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代码优化的方法,仅供大家参考学习,欢迎大家补充一起学习,实际项目中,注意养成良好的编码习惯,让代码看起来更清晰易懂。