避免创建不必要的对象

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

简介

在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提供了比较方便的方法调用而忽略了性能考究,往往不容易被发现。这个时候就需要优秀的你,去“咬文嚼字”啦!

目录
相关文章
|
7月前
对象的优化
对象的优化
|
2月前
|
缓存 监控 算法
如何确保延迟加载的资源能够在需要时及时加载?
【10月更文挑战第14天】保障延迟加载资源的及时加载是一个复杂而持续的过程,需要我们不断地探索和实践。通过合理的设计、优化和管理,我们能够为用户提供更流畅、高效的体验,同时也能更好地发挥资源的价值。
42 2
|
2月前
|
缓存 数据挖掘 UED
如何判断资源是否需要延迟加载?
【10月更文挑战第14天】总之,准确判断资源是否需要延迟加载是资源管理中的重要环节。通过综合考虑资源的类型、用户行为、性能指标等因素,并结合适当的延迟加载实现方式,可以在保证应用功能和性能的前提下,实现资源的最优利用,为用户提供更好的体验。
37 3
|
2月前
|
数据挖掘 UED
判断资源是否需要延迟加载的方法
【10月更文挑战第14天】总之,准确判断资源是否需要延迟加载是一项复杂而重要的任务。通过深入分析和综合考量各种因素,我们能够做出更明智的决策,实现资源的优化配置,提升应用的整体性能和用户体验。
34 1
|
2月前
|
存储 Java
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
这篇文章详细地介绍了Java对象的创建过程、内存布局、对象头的MarkWord、对象的定位方式以及对象的分配策略,并深入探讨了happens-before原则以确保多线程环境下的正确同步。
59 0
JVM知识体系学习四:排序规范(happens-before原则)、对象创建过程、对象的内存中存储布局、对象的大小、对象头内容、对象如何定位、对象如何分配
|
2月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(二)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
2月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(三)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
2月前
|
存储 编译器 C++
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作(一)
【C++】掌握C++类的六个默认成员函数:实现高效内存管理与对象操作
|
6月前
new 一个对象的过程中发生了什么
new 一个对象的过程中发生了什么
|
7月前
|
C++
C++程序对象动态建立和释放
C++程序对象动态建立和释放
55 1