影响Java代码性能的一些编码习惯

在Java程序中,性能问题的大部分原因并不在于Java语言,而是程序本身。养成良好的编码习惯是非常重要的,一个优秀的程序员,不仅要会编写程序,更要会编写高质量的程序。

尽量在合适的场合使用单例

使用单例可以减轻加载的负担,缩短加载的时间,提高加载的效率。它的目的是确保一个类只有一个实例,并提供一个全局访问点。在某些情况下,单例可以非常有用,例如:

  • 当一个类的实例化过程非常耗费资源,而且只需要一个实例时,可以使用单例来避免重复创建实例的开销。
  • 当需要确保全局只有一个对象时,例如某个系统的配置信息,可以使用单例来确保全局只有一个配置对象。

然而,并不是所有情况下都适合使用单例。以下是一些使用单例的不适当场合:

  • 当一个类具有多个实例,并且每个实例都有自己的状态行为时,使用单例会导致状态共享和行为不一致。
  • 当需要在多个线程中使用一个类时,单例需要额外的同步机制来确保线程安全,这可能会导致性能问题。

尽量避免随意使用静态变量

当某个对象被定义为static变量所引用,那么GC通常是不会回收这个对象所占有的内存。

public class A {

    private static B b = new B();

}

此时静态变量b的生命周期与A类同步,如果A类不会卸载,那么b对象会常驻内存,直到程序终止,并且可能造成内存泄漏,所以一般前期建议少用或者不用。

尽量避免循环内执行数据库操作

消耗性能!消耗性能!消耗性能!(重要事情说三遍)

因为数据库操作通常比较耗时,而循环内的操作会被执行多次,这会导致性能问题。而往往在平时写业务需求时,特别是遇见业务逻辑特别复杂的需求,可能隐藏着多层嵌套数据库操作,而这有可能就是造成性能相差几个数量级的原因。

一般我们会采用批量操作,或者如果是查询的话,可以将数据缓存在内存中,可以避免在循环内频繁地访问数据库。但是,需要注意内存缓存的大小和有效期限,以避免内存泄漏和数据不一致的问题。

"外小内大"循环原则

for (int i = 0; i < 10; i++) {
   for (int j = 0; j < 10000; j++) {
   }
}

"外小内大"循环是指外层循环的次数比内层循环的次数小的一种循环结构。由于分支优化规则(记住概念即可),这种循环结构可以更好地利用计算机的缓存机制,提高程序的执行效率。但是"外小内大"循环并不是适用于所有的循环场景,具体要根据实际情况进行选择和优化。

尽量避免循环内创建对象

for (int i = 0; i < n; i++) {
    Object obj = new Object();
}

这种做法会导致内存中有n份Object对象引用存在,当n很大的话,就耗费内存了,建议写成:

Object obj = null;
for (int i = 0; i < n; i++) {
     obj = new Object(); 
}

这样的话,内存中只有一份Object对象引用,每次new Object()的时候,Object对象引用指向不同的Object罢了,但是内存中只有一份,这样就大大节省了内存空间了。

尽量避免循环内使用final类的对象进行操作

String result = "";
Long sum = 0L;
for (int i = 0; i < n; i++) {
    result += i;
    sum += i;
}

尽量避免循环内使用final类的对象进行操作,例如String拼接、用Long对象计算总和等,是因为这些操作会导致频繁的对象创建和销毁,从而降低程序的性能。对于String、Long等,它们是不可变的,每次操作都会创建新的对象,因此在循环内频繁进行操作会导致大量的对象创建,从而影响程序的性能。建议写成:

StringBuilder sb = new StringBuilder(); //如果需要线程安全则用StringBuffer
long sum = 0L; //Long 改为基本类型
for (int i = 0; i < n; i++) {
    sb.append(i);
    sum += i;
}
String result = sb.toString();

尽量减少对变量的重复计算

for (int i = 0; i < list.size(); i++)

上面这段代码运行起来没有什么问题,但是在循环中,循环条件会被反复计算,也就是list.size()会反复计算多次,浪费了性能,建议写成:

for (int i = 0, len = list.size(); i < len; i++)

位移代替乘除法

int num = a / 4;
int num = a * 8;

位运算(包括位移运算、按位与、按位或等)通常比乘除法等算术运算更快。因此,在一些需要高效计算的场景中,可以使用位移运算代替乘除法等算术运算,提高程序的性能。

int num = a >> 2; //除以4
int num = a << 3; //乘以8

尽量使用局部变量

局部变量的作用域仅限于方法内部或语句块内部(存于栈中),速度较快;而如果使用成员变量或静态变量,它们的作用域可能会超出方法或语句块的范围(在堆中创建),速度较慢。

pubilc class A {

    private final Lock lock = new ReentrantLock();

    public void someMethod() {
        // 将lock锁对象定义为局部变量
        Lock lock = this.lock;
        lock.lock();
        try {
            // 执行业务逻辑
        } finally {
            lock.unlock();
        }
    }
}

尽量确定容器容量大小

尽量确定容器容量大小是为了提高程序的性能和效率。Java中的容器(如ArrayList、HashMap、StringBuffer等)在创建时,都需要指定其初始容量大小。如果未指定初始容量,容器会按照默认规则进行扩容。每次扩容都需要重新分配内存空间,并将原有元素复制到新的内存空间中,这会导致程序的性能下降。如果容器容量设置过小会导致频繁扩容,容器容量大小过大会浪费内存空间。因此,在确定容器容量大小时,需要根据实际情况进行合理估算。

尽量使用final修饰符

带有final修饰符的类是不可派生的。在Java核心的API中,有许多应用final的例子(例如String)。为String类指定final,防止使用者覆盖length()方法。另外,如果一个类是final的,则该类的所有方法都是final的。java编译器会寻找机会内联(inline)所有的final方法(这个具体的编译器实现有关),此举能够使性能平均提高50%。例如说让访问实例内变量的getter/setter方法变成final,这样会告诉编译器,这个方法不会被重载,所以会变成inlined。

尽量采用懒加载的策略,即在需要的时候才创建

懒加载的策略是指在需要的时候才创建对象或执行操作。这种策略可以避免不必要的资源浪费,并且可以延迟对象的创建时间,提高程序的响应速度。

String str = "aaa";
if (i == 1){
    list.add(str);
}

// 建议替换为:

if (i == 1){
    String str = "aaa";
    list.add(str);
}

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值