有助于改善性能的技巧

1. 慎用异常

  在Java软件开发中,经常会使用try-catch进行错误捕获,但是,try-catch语句对系统性能而言是非常糟糕的。因此,应尽量避免将其应用在循环当中。

2. 使用局部变量

  调用方法时传递的参数以及在调用中创建的临时变量都保存在栈(Stack)中,速度较快。其它变量,如静态变量、实例变量等,都在堆(Heap)中创建,速度较慢。

3. 位运算代替乘除法

  在所有的运算中,位运算是最为高效的。因此,可以尝试使用位运算代替部分算术运算,来提高系统的运算速度。最典型的就是对于整数的乘除法运算优化。

4. 替换switch

  关键字switch语句用于多条件判断,switch语句的功能类似于if-else语句,两者的性能也差不多。因此,不能说switch语句会降低系统的性能。但是,在绝大部分情况下,switch语句还是有性能提升空间的。

  来看下面的例子:

        int re = 0;
        for(int i=0;i<10000000;i++){
            re = switchInt(i);
        }
        protected int switchInt(int z){
            int i = z%10 + 1;
            switch(i){
                case 1:return 3;
                case 2:return 6;
                case 3:return 7;
                case 4:return 8;
                case 5:return 10;
                case 6:return 16;
                case 7:return 18;
                case 8:return 44;
                default:return -1;
            }
        }

  上例中,就分支逻辑而言,这种switch模式的性能并不差。但是如果换一种全新的思路替代switch,实现相同的程序功能,那么性能就能有很大的提升空间。以下代码实现了与上例switch语句相同的功能:

        int re = 0;
        int[] sw = new int[]{0, 3, 6, 7, 8, 10, 16, 18, 44}; //替代switch逻辑
        for(int i=0;i<10000000;i++){
            re = arrayInt(sw, i);
        }
        protected int arrayInt(int[] sw, int z){
            int i = z%10 + 1;
            if(i<1||i>8)    //模拟switch的default
                return -1;
            else
                return sw[i];
        }

  以上代码采用全新的思路,使用一个连续的数组代替了switch语句。因为对数组的随机访问时非常快的,至少好于switch的分值判断,因此它的速度一定会快于原来的实现。通过实验,使用switch的语句相对耗时80ms,而使用数组的实现只相对耗时62ms。

5. 一维数组代替二维数组

  由于数组的随机访问性能非常好,许多JDK类库,如Arraylist、Vector等都使用了数组作为其底层实现。但是,作为软件开发人员也必须知道,一维数组和二维数组的访问速度是不一样的。一维数组的访问速度要优于二维数组。因此,在性能敏感的系统中要使用二维数组的,可以尝试通过可靠的算法,将二维数组转为一维数组再进行处理,以提高系统的响应速度。

6. 提取表达式

   在软件开发过程中,程序员很容易有意无意地让代码做一些“重复劳动”,在大部分情况下,由于计算机的高速运行,这些“重复劳动”并不会对性能构成太大的威胁,但若希望将系统性能发挥到极致,提取这些“重复劳动”相当有意义。尽可能让程序少做重复计算,尤其要关注在循环体内的代码,从循环体内提取重复的代码可以有效地提升系统性能。

7. 展开循环

   与前面所介绍的优化技巧略有不同,展开循环是一种在极端情况下使用的优化手段,因为展开循环很可能会影响代码的可读性和可维护性,而这两者对软件系统来说也是极为重要的。但是,当性能问题成为系统的主要矛盾时,展开循环绝对是一种值得尝试的技术。

一个普通的循环代码如下所示:

     int[] array = new int[9999999];
        for(int i=0;i<9999999;i++){
            array[i] = i;
        }

展开循环后,类似以下格式:

        int[] array = new int[9999999];
        for(int i=0;i<9999999;i += 3){
            array[i] = i;
            array[i+1] = i+1;
            array[i+2] = i+2;
        }

  以上两段代码功能完全相同,但第二段代码进行了循环展开的优化,在一个循环体内处理了原代码段中的3个循环逻辑。运行以上两段代码,第一段代码相对耗时94ms,第二段代码相对耗时31ms,可见展开循环后,减少循环次数,对提升系统性能很有帮助。

8. 布尔运算代替位运算

   虽然位运算的速度远远高于算术运算,但是在条件判断时,使用位运算替代布尔运算却是非常错误的选择。因为在条件判断时,Java会对布尔运算做相当充分的优化。在布尔表达式的计算中,只要表达式的值可以确定,就会立即返回,而跳过剩余子表达式的计算。若使用位运算(按位与、按位或)代替逻辑与和逻辑或,虽然位运算本身性能没有问题,但是位运算总是要将所有的子表达式全部计算完成后,再给出最终结果。因此,从这个角度说,使用位运算替代布尔运算会使系统进行很多无用计算。

9. 使用arraycopy()

  数组复制是一项使用频率很高的功能,JDK中提供了一个高效的API来实现它:

public static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length);

  System.arraycopy()函数是native函数,通常native函数的性能要优于普通函数,仅出于性能考虑,在软件开发时,应尽可能调用native函数。

10. 使用Buffer进行I/O操作

  除NIO外,使用java进行I/O操作有两种基本方式:

  1. 使用基于InputStream和OutputStream的方式
  2. 使用Writer和Reader

  无论使用哪种方式进行文件的I/O,如果能合理地使用缓冲,就能有效地提高I/O的性能。

 11. 使用clone()代替new

  在Java中新建对象实例最常用的方法是使用new关键字。JDK对new关键字的支持非常好,使用new关键字创建轻量级对象时,速度非常快。但是,对于重量级对象,由于对象在构造函数中可能会进行一些复杂而且耗时的操作,因此,构造函数的执行时间可能会比较长。这就导致了创建对象的耗时很长,同时也使得系统无法在短期内获得大量的实例。为了解决这个问题,可以使用Object.clone()方法。

  Object.clone()方法可以绕过对象构造函数,快速复制一个对象实例。由于不需要调用对象构造函数,因此,clone()方法不会受到构造函数性能的影响,能够快速生成一个实例。但是,在默认的情况下,clone()方法生成的实例只是原对象的浅拷贝。如果需要深拷贝,则需要重新实现clone()方法。

 12. 静态方法替代实例方法

  使用关键字static声明的方法为静态方法。在Java中,由于实例方法需要维护一张类似虚函数表的结构,以实现对多态的支持。与静态方法相比,实例方法的调用需要更多的资源。因此,对于一些常用的工具类方法,没有对其进行重载的必要,那么就可以将它们声明为static,便可以加速方法的调用。对于一些工具类,应该使用static方法实现,这样不仅可以加速函数调用的速度,同时,调用static方法也不需要生成类的实例,比调用实例方法更为方便、易用。

 

posted @ 2015-10-23 14:49  ~风轻云淡~  阅读(715)  评论(0编辑  收藏  举报