写点什么

避免创建不必要的对象

作者:李子捌
  • 2021 年 12 月 18 日
  • 本文字数:1792 字

    阅读完需:约 6 分钟

避免创建不必要的对象

简介

在 Java 开发中,程序员要尽可能的避免创建相同的功能的对象,因为这样既消耗内存,又影响程序运行速度。在这种情况下可以考虑重复利用对象



接下来举例几种对象重复利用的场景,看看我们是不是有中招了,如果有赶紧趁着还没被发现悄悄改掉,被发现了会被 diss 啦!

1、String 和 Boolean

如下两种写法看似没有什么区别,但是如果深入 jvm 底层了解,我们可以利用 jvm 运行时常量池的特性,避免创建具有相同功能的 String 对象(尤其是在循环内部创建)可以带来比较可观的性能优化以及节约内存。​


错误写法


// 每次都会创建一个新的String对象,且不会加入常量池String name2 = new String("李子捌");
复制代码


正确写法


// 正确写法String name1 = "李子捌";
复制代码


除此之外,刚写 Java 代码的程序员们,也要正确的选择 String、StringBuilder、StringBuffer 类的使用。String 为不可变对象,通常用于定义不变字符串;StringBuilder、StringBuffer 用于可变字符串操作场景,如字符串拼接;其中 StringBuffer 是线程安全的,它通过 Synchronized 关键字来实现线程同步。


// StringBuffer中的append()方法public synchronized StringBuffer append(String str) {    toStringCache = null;    super.append(str);    return this;}
// StringBuilder中的append()方法public StringBuilder append(String str) { super.append(str); return this;}
复制代码


Boolean 是常用的类型,在开发中也应该使用 Boolean.valueof()而不是 new Boolean(),从 Boolean 的源码可以看出,Boolean 类定义了两个 final static 的属性,而 Boolean.valueof()直接返回的是定义的这两个属性,而 new Boolean()却会创建新的对象。


public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false);
复制代码

2、自动拆箱和装箱

Java 提供了基本数据类型的自动拆箱和装箱功能,那是不是意味着我们可以在代码中随意的使用这两个类型呢?其实理论上在代码层面是没得问题,不过在具体的性能方面还是有优化的空间啦!!!​


我们来测试下性能


long start = System.currentTimeMillis();Integer sum = 0;for (int i = 0; i < 100000; i++) {    sum += i;}System.out.println(System.currentTimeMillis() - start);
复制代码


使用 Integer 耗时 3 毫秒



long start = System.currentTimeMillis();// 修改Integer 为 int int sum = 0;for (int i = 0; i < 100000; i++) {    sum += i;}System.out.println(System.currentTimeMillis() - start);
复制代码


使用 int 耗时 0 毫秒



因此关于自动拆箱装箱的使用,我们其实也可以做适当的考虑,毕竟有时候代码性能就是一点点挤出来的嘛!!!


3、正则表达式

正则表达式我们经常用于字符串是否合法的校验,我们先来看一段简单的代码(大家有没有一眼看出问题呢?我想你肯定看出来了!!!):


public static void main(String[] args) {
String email = "1057301174@qq.com"; String regex = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$";
long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { email.matches(regex); }
System.out.println(System.currentTimeMillis() - start);
}
复制代码


执行这段代码的时间,一共耗时 71 毫秒,看似好像挺快的!




但是我们做个非常简单的优化,优化后的代码如下所示:


public static void main(String[] args) {
String email = "1057301174@qq.com"; String regex = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; Pattern pattern = Pattern.compile(regex);
long start = System.currentTimeMillis(); for (int i = 0; i < 10000; i++) { //email.matches(regex); pattern.matcher(email); }
System.out.println(System.currentTimeMillis() - start);
}
复制代码


再次执行代码,一共耗时 1 毫秒,快了 70 倍呀!!!



这是因为 String.matches()方法在循环中创建时,每次都需要执行 Pattern.compile(regex),而创建 Patter 实例的成本很高,因为需要将正则表达式编译成一个有限状态机( finite state machine)。这种我们经常会因为 Java api 提供了比较方便的方法调用而忽略了性能考究,往往不容易被发现。这个时候就需要优秀的你,去“咬文嚼字”啦!​

发布于: 2021 年 12 月 18 日阅读数: 5
用户头像

李子捌

关注

华为云享专家 2020.07.20 加入

公众号【李子捌】

评论

发布
暂无评论
避免创建不必要的对象