Java性能优化

1、慎用异常

异常对性能不利。抛出异常首先要创建一个新的对象。Throwable接口的构造函数调用名为fillInStackTrace()的本地方法,fillInStackTrace()方法检查堆栈,收集调用跟踪信息。只要有异常被抛出,VM就必须调整调用堆栈,因为在处理过程中创建了一个新的对象。异常只能用于错误处理,不应该用来控制程序流程。

2、不要重复初始化变量

默认情况下,调用类的构造函数时,Java会把变量初始化成确定的值:所有的对象被设置成null,整数变量(byte、short、int、long)设置成0,float和double变量设置成0.0,逻辑值设置成false。当一个类从另一个类派生时,这一点尤其应该注意,因为用new关键词创建一个对象时,构造函数链中的所有构造函数都会被自动调用。

3、尽量指定类的final修饰符

带有final修饰符的类是不可派生的。在Java核心API中,有许多应用final的例子,例如java.lang.String。为String类指定final防止了人们覆盖length()方法。另外,如果指定一个类为final,则该类所有的方法都是final。Java编译器会寻找机会内联所有的final方法(这和具体的编译器实现有关)。此举能够使性能平均提高50%。

4、尽量使用局部变量

调用方法时传递的参数以及在调用中创建的临时变量都保存在栈中,速度较快。其他变量,如静态变量、实例变量等,都在堆中创建,速度较慢。另外,依赖于具体的编译器,局部变量还可能得到进一步优化。

5、使用移位操作代替乘法和除法

符号性质的乘除是一个很“昂贵”的操作,使用移位操作将会更快更有效

考虑下面的代码:

        for (val = 0; val<100000; val+=5)
        {
            alterX = val * 8;
            myResult = val * 2;
        }

用移位操作替代乘法操作可以极大地提高性能。下面是修改后的代码:

        for (val = 0; val <100000; val+= 5)
        {
            alterX = val <<3;
            myResult = val <<1;
        }

修改后的代码不再做乘以8的操作,而是改用等价的左移3位操作,每左移1位相当于乘以2。相应地,右移1位操作相当于除以2。值得一提的是,虽然移位操作速度快,但可能使代码比较难于理解,所以最好加上一些注释。
但是使用移位操作这个方法是一把双刃剑,除非是在一个非常大的循环内,性能非常重要,而且你很清楚你自己在做什么,使用这种方法比较合算。否则提高性能所带来的程序晚读性的降低将是不合算的。

6、避免在循环条件中使用复杂表达式

在不做编译优化的情况下,在循环中,循环条件会被反复计算,如果不使用复杂表达式,而使循环条件值不变的话,程序将会运行的更快。

复制代码
import java.util.Vector;
class CEL
{
    void method(Vector vector)
    {
        for (int i = 0; i < vector.size(); i++)
            ; // 执行操作
    }
}
复制代码

更正:

复制代码
class CEL_fixed
{
    void method (Vector vector) {
        int size = vector.size();
        for (int i = 0; i < size; i++)
            ; // 执行操作
     }
}
复制代码

7、使用finally释放资源

程序中使用到的资源应当被释放,以避免资源泄漏。这最好在finally块中去做。不管程序执行的结果如何,finally块总是会执行的,以确保资源的正确关闭。

8、设置访问实例内变量的getter/setter方法为final

复制代码
class MAF
{
    public void setSize(int size)
    {
        _size = size;
    }
    private int _size;
}
复制代码

更正:

复制代码
class MAF
{
    final public void setSize(int size)
    {
        _size = size;
    }
    private int _size;
}
复制代码

9、避免不需要的instanceof操作

如果左边的对象的静态类型等于右边的,instanceof表达式返回永远为true。所以也根本不需要做判断。

复制代码
public class UISO
{
    public UISO(){}
}

class Dog extends UISO
{
    void method(Dog dog, UISO u)
    {
        Dog d = dog;
        if (d instanceof UISO)      // always true.
        System.out.println("Dog is a UISO");
        UISO uiso = u;
        if (uiso instanceof Object) // always true.
        System.out.println("uiso is an Object");
    }
}
复制代码

10、少用new关键词创建类的实例

用new关键词创建类的实例时,构造函数链中的所有构造函数都会被自动调用。但如果一个对象实现了Cloneable接口,我们可以调用它的clone()方法。clone()方法不会调用任何类构造函数。
在使用设计模式(Design Pattern)的场合,如果用Factory模式创建对象,则改用clone()方法创建新的对象实例非常简单。例如,下面是Factory模式的一个典型实现:

    public static CreditgetNewCredit()
    {
        return new Credit();
    }

改进后:

    private static Credit BaseCredit = new Credit();

    public static CreditgetNewCredit()
    {
        return (Credit) BaseCredit.clone();
    }

11、避免不需要的强转操作

所有的类都是直接或者间接继承自Object。同样,所有的子类也都隐含的“等于”其父类。那么,由子类造型至父类的操作就是不必要的了。

12、不要在循环中调用synchronized(同步)方法

方法的同步需要消耗相当大的资料,在一个循环中调用它绝对不是一个好主意。

复制代码
public class SYN
{
    public synchronized void method(Object o)
    {
    }

    private void test()
    {
        for (int i = 0; i < vector.size(); i++)
        {
            method(vector.elementAt(i)); // violation
        }
    }

    private Vector vector = new Vector(5, 5);
}
复制代码

更正:

复制代码
public class SYN
{
    public void method(Object o)
    {
    }

    private void test () {
        //在一个同步块中执行非同步方法
        synchronized (vector)
        {
            for (int i = 0; i < vector.size(); i++) {
                method (vector.elementAt(i));  
            }
        }
     }

    private Vector vector = new Vector(5, 5);
}
复制代码

13、将try/catch块移出循环

把try/catch块放入循环体内,会极大的影响性能,性能会有明显的下降。

复制代码
import java.io.FileInputStream;

public class TRY
{
    void method(FileInputStream fis)
    {
        for (int i = 0; i < size; i++)
        {
            try
            { // violation
                _sum += fis.read();
            }
            catch (Exception e)
            {
            }
        }
    }
    private int _sum;
}
复制代码

更正:

复制代码
    void method(FileInputStream fis)
    {
        try
        { // violation
            for (int i = 0; i < size; i++)
            {
                    _sum += fis.read();
            }
        }
        catch (Exception e)
        {
        }
    }
复制代码

14、对于常量字符串,用String代替StringBuffer

常量字符串并不需要动态改变长度。把StringBuffer换成String,如果确定这个String不会再变的话,这将会减少运行开销提高性能。

复制代码
public class USC
{
    String method()
    {
        StringBuffer s = new StringBuffer("Hello");
        String t = s + "World!";
        return t;
    }
}
复制代码

15、不要在循环体中实例化变量

在循环体中实例化临时变量将会增加内存消耗

复制代码
import java.util.Vector;

public class LOOP
{
    void method(Vector v)
    {
        for (int i = 0; i < v.size(); i++)
        {
            Object o = new Object();
            o = v.elementAt(i);
        }
    }
}
复制代码

更正:

复制代码
import java.util.Vector;

public class LOOP
{
    void method(Vector v)
    {
        Object o;
        for (int i = 0; i < v.size(); i++)
        {
            o = v.elementAt(i);
        }
    }
}
复制代码

16、确定StringBuffer的容量

StringBuffer的构造器会创建一个默认大小(通常是16)的字符数组。在使用中,如果超出这个大小,就会重新分配内存,创建一个更大的数组,并将原先的数组复制过来,再丢弃旧的数组。在大多数情况下,你可以在创建StringBuffer的时候指定大小,这样就避免了在容量不够的时候自动增长,以提高性能。

复制代码
public class RSBC
{
    void method()
    {
        StringBuffer buffer = new StringBuffer(); // violation
        buffer.append("hello");
    }
}
复制代码

更正:

复制代码
public class RSBC
{
    void method()
    {
        StringBuffer buffer = new StringBuffer(MAX);
        buffer.append("hello");
    }

    private final int MAX = 100;
}
复制代码

以上对一些经常出现的对效率有所影响Java的优化建议进行了详细的描述,其实在实践过程中仍然有许多大家需要注意和优化的地方,譬如:使用System.arraycopy()代替通过来循环复制数组;为Vectors和Hashtables定义初始大小;查找单个字符的话,用charAt()代替startsWith();对于boolean值,避免不必要的等式判断等等,这些都能或多或少的提高效率。对于未涉及的Java优化的最佳实践,在实际项目组编写Java的过程中,通过跟踪程序,精确到平台程序的具体方法,从执行效率和算法逻辑两方面进行优化。

你可能感兴趣的:(java性能优化)