• Java:代码高效优化


    本文转自阿里技术站,感谢阿里前辈提供的技术知识,微信关注 "阿里技术" 公众号即可实时学习。

    1.常量&变量

    1.1.直接赋值常量值,禁止声明新对象

    直接赋值常量值,只是创建了一个对象引用,而这个对象引用指向常量值。

    反例:

    #错误方式:包装类声明对象并赋值
    Long i = new Long(1L);
    String s = new String("abc");

    正例:

    #包装类直接赋值
    Long i = 1L;
    String s = "abc";

    1.2.当成员变量值无需改变时,尽量定义为静态常量

    类的每个对象实例中,每个成员变量都有一份副本,而成员静态常量只有一份实例

    反例:

    #timeout为成员变量(常量),但是有一份副本
    public class HttpConnection {
        private final long timeout = 5L;
        ...
    }

    正例:

    #如果是一个常量,我们不需要副本,即设置静态成员变量(常量)加载一次就好
    public class HttpConnection {
        private static final long TIMEOUT = 5L;
        ...
    }

    1.3.尽量使用基本数据类型,避免自动装箱和拆箱

    Java 中的基本数据类型double、float、long、int、short、char、boolean,分别对应包装类Double、Float、Long、Integer、Short、Character、Boolean。

    JVM支持基本类型与对应包装类的自动转换,被称为自动装箱和拆箱装箱和拆箱都是需要CPU和内存资源的,所以应尽量避免使用自动装箱和拆箱

    反例:

    #基本类型与包装类的自动转换是消耗CPU资源的,程序运行时会造成一定的cpu压力
    Integer sum = 0;
    int[] values = ...;
    for (int value : values) {
        // 相当于result = Integer.valueOf(result.intValue() + value);
        sum += value; 
    }

    正例:

    #确定好使用的类型,以免给cpu施压
    int sum = 0;
    int[] values = ...;
    for (int value : values) {
        sum += value;
    }

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

    如果代码内会对变量的初值进行覆盖,那变量就不必赋予初值

    反例:

    #代码运行时会覆盖userList,故无需赋予初值
    List<UserDO> userList = new ArrayList<>();
    if (isAll) {
        userList = userDAO.queryAll();
    } else {
        userList = userDAO.queryActive();
    }

    正例:

    #不需要赋予初值
    List<UserDO> userList;
    if (isAll) {
        userList = userDAO.queryAll();
    } else {
        userList = userDAO.queryActive();
    }

    1.5.尽量使用函数内的基本类型临时变量(#重点)

    函数内基本类型参数和临时变量都保存在栈(Stack)中访问速度较快对象类型参数和临时变量的引用都保存在栈(Stack)中内容都保存在堆(Heap)中访问速度较慢

    类中任何类型成员变量保存在堆(Heap)中访问速度较慢

    反例:

    #result为类的成员变量,保存在堆中,访问较慢
    public final class Accumulator {
        private double result = 0.0D;
        public void addAll(@NonNull double[] values) {
            for(double value : values) {
                result += value;
            }
        }
        ...
    }

    正例:

    #定义局部变量sum,只操作一次成员变量
    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;
        }
        ...
    }

    1.6.尽量不要在循环体外定义变量(#重点

    新版的JDK中已经做了优化,通过对编译后的字节码分析,变量定义在循环体外和循环体内没有本质的区别,运行效率基本上是一样的。

    反而,根据“ 局部变量作用域最小化 ”原则,变量定义在循环体内更科学更便于维护避免延长大对象生命周期导致延缓回收问题

    反例:

    #userVO定义在循环体外,延长了对象的生命周期以致回收延缓
    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);
    }

    正例:

    #UserVo定义在循环体内部
    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);
    }

    1.7.不可变的静态常量,尽量使用非线程安全类

    不可变的静态常量虽然需要支持多线程访问可以使用非线程安全类

    反例:

    public static final Map<String, Class> CLASS_MAP;
    static {
    #线程安全类 ConsurrentHashMap Map
    <String, Class> classMap = new ConcurrentHashMap<>(16); classMap.put("VARCHAR", java.lang.String.class); ... CLASS_MAP = Collections.unmodifiableMap(classMap); }

    正例:

    public static final Map<String, Class> CLASS_MAP;
    static {
        #使用非线程安全类 HashMap
        Map<String, Class> classMap = new HashMap<>(16);
        classMap.put("VARCHAR", java.lang.String.class);
        ...
        CLASS_MAP = Collections.unmodifiableMap(classMap);
    }

    1.8.不可变的成员变量,尽量使用非线程安全类

    不可变的成员变量虽然需要支持多线程访问,也可以使用非线程安全类

    反例:

    @Service
    public class StrategyFactory implements InitializingBean {
        @Autowired
        #成员变量List
        private List<Strategy> strategyList;
        private Map<String, Strategy> strategyMap;
        @Override
        public void afterPropertiesSet() {
            if (CollectionUtils.isNotEmpty(strategyList)) {
                #List的size没有变化
                int size = (int) Math.ceil(strategyList.size() * 4.0 / 3);
                #可以不用线程安全类            
                Map<String, Strategy> map = new ConcurrentHashMap<>(size);
                for (Strategy strategy : strategyList) {
                    map.put(strategy.getType(), strategy);
                }
                strategyMap = Collections.unmodifiableMap(map);
            }
        }
        ...
    }

    正例:

    @Service
    public class StrategyFactory implements InitializingBean {
        @Autowired
        private List<Strategy> strategyList;
        private Map<String, Strategy> strategyMap;
        @Override
        public void afterPropertiesSet() {
            if (CollectionUtils.isNotEmpty(strategyList)) {
                int size = (int) Math.ceil(strategyList.size() * 4.0 / 3);
                #可以用非线程安全类
                Map<String, Strategy> map = new HashMap<>(size);
                for (Strategy strategy : strategyList) {
                    map.put(strategy.getType(), strategy);
                }
                strategyMap = Collections.unmodifiableMap(map);
            }
        }
        ...

    2.对象&类

    2.1.禁止使用JSON转化对象

    JSON提供把对象转化为JSON字符串、把JSON字符串转为对象的功能,于是被某些人用来转化对象。这种对象转化方式,虽然在功能上没有问题但是在性能上却存在问题

    反例:

    List<UserDO> userDOList = ...;
    #对象转化字符,转List
    List<UserVO> userVOList = JSON.parseArray(JSON.toJSONString(userDOList), UserVO.class);

    正例:

    List<UserDO> userDOList = ...;
    List<UserVO> userVOList = new ArrayList<>(userDOList.size());
    #最基本的for循环转换
    for (UserDO userDO : userDOList) {
        UserVO userVO = new UserVO();
        userVO.setId(userDO.getId());
        ...
        userVOList.add(userVO);
    }

    2.2.尽量不使用反射赋值对象

    反射赋值对象主要优点是节省了代码量主要缺点却是性能有所下降

    反例:

    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);
    }

    正例:

    List<UserDO> userDOList = ...;
    List<UserVO> userVOList = new ArrayList<>(userDOList.size());
    #最基本的for循环
    for (UserDO userDO : userDOList) {
        UserVO userVO = new UserVO();
        userVO.setId(userDO.getId());
        ...
        userVOList.add(userVO);
    }

    2.3.采用Lambda表达式替换内部匿名类

    大多数刚接触JDK8的同学来说,都会认为Lambda表达式就是匿名内部类的语法糖。实际上, Lambda表达式在大多数虚拟机中采用invokeDynamic指令实现,相对于匿名内部类在效率上会更高一些

    反例:

    List<User> userList = ...;
    #内部匿名类
    Collections.sort(userList, new Comparator<User>() {
        @Override
        public int compare(User user1, User user2) {
            Long userId1 = user1.getId();
            Long userId2 = user2.getId();
            ...
            return userId1.compareTo(userId2);
        }
    });

    正例:

    List<User> userList = ...;
    Collections.sort(userList, (user1, user2) -> {
        Long userId1 = user1.getId();
        Long userId2 = user2.getId();
        ...
        return userId1.compareTo(userId2);
    });

     

  • 相关阅读:
    USASO Greedy Gift Givers
    Mat 类型用法
    OpenCV错误:Unhandled exception at 0x0133bc63 ....0xC0000005: Access violation reading location 0x00000004.
    C++ seekp 函数文件流跳转功能产生数据覆盖问题解决
    C++中文件名称必须是C风格的char*格式
    char*, char[] ,CString, string的转换
    Visual Stdio 2008 最大内存分配块大小问题: 使用new 分配连续723M内存 出错 std::bad_alloc at memory location 0x0013e0b8
    string类型转化为char*错误: error C2440: '=' : cannot convert from 'const char *' to 'char *'
    Mat 和 IplImage、CvMat格式的互相转换
    指针数组和数组指针
  • 原文地址:https://www.cnblogs.com/nhdlb/p/13298569.html
Copyright © 2020-2023  润新知