如何避免创建不必要的对象?

  Java   7分钟   609浏览   0评论

在Java开发中,我们要尽可能的避免创建相同的功能的对象,因为这样既消耗内存,又影响程序运行速度。

在这种情况下可以考虑重复利用对象

String和Boolean

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

错误写法

// 每次都会创建一个新的String对象,且不会加入常量池
String name2 = new String("小邹");

正确写法

// 正确写法
String name1 = "小邹";

除此之外,我们也要正确的选择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);

自动拆箱和装箱

Java提供了基本数据类型的自动拆箱和装箱功能,那是不是意味着我们可以在代码中随意的使用这两个类型呢?

其实理论上在代码层面是没得问题,不过在具体的性能方面还是有优化的空间的。

我们来测试下性能

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

使用Integer耗时10毫秒

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毫秒

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

正则表达式

正则表达式我们经常用于字符串是否合法的校验,我们先来看一段简单的代码

public static void main(String[] args) {
        String email = "1565453341@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);
    }

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

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

public static void main(String[] args) {
        String email = "1565453341@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++) {
            pattern.matcher(email);
        }
        System.out.println(System.currentTimeMillis() - start);
}

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

这是因为String.matches()方法在循环中创建时,每次都需要执行Pattern.compile(regex),而创建Patter实例的成本很高,因为需要将正则表达式编译成一个有限状态机( finite state machine)。

这种我们经常会因为Java api提供了比较方便的方法调用而忽略了性能考究,往往不容易被发现。

如果你觉得文章对你有帮助,那就请作者喝杯咖啡吧☕
微信
支付宝
  0 条评论