java常用优化技巧

1.LK最近在总结其他博友的优化方法后,结合自身实践,总结了一篇比较实用的java代码优化策略,引起了LK对性能的关注,你是不是跟我有同样的经历,都是在爬坑的路上,希望可以和大家一起进步。

2.循环内不要不断创建对象引用
例如:

for (int i = 1; i <= count; i++)
{
    Object obj = new Object();    
}

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

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

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


3.字符串变量和字符串常量equals的时候将字符串常量写在前面
这是一个比较常见的小技巧了,如果有以下代码:

String str = "123";
if (str.equals("123"))
{
    ...
}

建议修改为:

String str = "123";
if ("123".equals(str))
{
    ...
}

4.不要对数组使用toString()方法
看一下对数组使用toString()打印出来的是什么:

public static void main(String[] args)
{
    int[] is = new int[]{1, 2, 3};
    System.out.println(is.toString());
}

结果是:
[I@18a992f
本意是想打印出数组内容,却有可能因为数组引用is为空而导致空指针异常。不过虽然对数组toString()没有意义,但是对集合toString()是可以打印出集合里面的内容的,因为集合的父类AbstractCollections重写了Object的toString()方法。


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

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

建议替换为:

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

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

import java.util.vector;
class cel {
    void method(vector vector) {
        for (int i = 0; i < vector.size(); i++) // violation
        // ...
    }
}

修改为:

class cel {
    void method(vector vector) {
        int size = vector.size();
        for (int i = 0; i < size; i++)
        // ...
    }
}

7.为’vectors’和’hashtables’定义初始大小
JVM为Vector扩充大小的时候需要重新创建一个更大的数组,将原原先数组中的内容复制过来,最后,原先的数组再被回收。可见Vector容量的扩大是一个颇费时间的事.
例子:

import java.util.vector;
public class dic {
    public void addobjects(object[] o) {
        // if length > 10, vector needs to expand
        for (int i = 0; i< o.length;i++) {    
            v.add(o); // capacity before it can add more elements.
        }
    }
    public vector v = new vector(); // no initialcapacity.
}

修改为:
// 自己设定初始大小

public vector v = new vector(20);  
public hashtable hash = new hashtable(10);

8.在finally块中关闭stream


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

import java.io.*;
public class cs {
    public static void main(string args[]) {
        cs cs = new cs();
        cs.method();
    }
    public void method() {
        try {
            fileinputstream fis = new fileinputstream("cs.java");
            int count = 0;
            while (fis.read() != -1)
                count++;
            system.out.println(count);
            fis.close();
        } catch (filenotfoundexception e1) {
        } catch (ioexception e2) {
        }
    }
}

修改为: 在最后一个catch后添加一个finally块


9.使用’system.arraycopy()'来复制数组
‘system.arraycopy()’ 要比通过循环来复制数组快的多。
例子:

public class irb
{
    void method() {
        int[] array1 = new int[100];
        for (int i = 0; i < array1.length; i++) {
            array1[i] = i;
        }
        int[] array2 = new int[100];
        for (int i = 0; i < array2.length; i++) {
            array2[i] = array1[i]; // violation
        }
    }
}

修改为:

public class irb
{
    void method() {
        int[] array1 = new int[100];
        for (int i = 0; i < array1.length; i++) {
            array1[i] = i;
        }
        int[] array2 = new int[100];
        system.arraycopy(array1, 0, array2, 0, 100);
    }
}

10.避免不必要的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");
    }
}

修改为:
删掉不需要的instanceof操作。

class dog extends uiso {
    void method() {
        dog d;
        system.out.println("dog is an uiso");
        system.out.println("uiso is an uiso");
    }
}

11.查找单个字符,用charat()代替startswith()
用一个字符作为参数调用startswith()也会工作的很好,但从性能角度上来看,调用用string api无疑是错误的!
例子:

public class pcts {
    private void method(string s) {
        if (s.startswith("a")) { // violation
            // ...
        }
    }
}

修改为:
将’startswith()‘替换成’charat()’.

public class pcts {
    private void method(string s) {
        if ('a' == s.charat(0)) {
            // ...
        }
    }
}

12.在字符相加的时候,使用’'代替""
例子:

public class str {
    public void method(string s) {
        string string = s + "d"; // violation.
        string = "abc" + "d"; // violation.
    }
}

修改为:
将一个字符的替换成’ ’

public class str {
    public void method(string s) {
        string string = s + 'd'
        string = "abc" + 'd'   
    }
}

13、将try/catch块移出循环
把try/catch块放入循环体内,会极大的影响性能,如果编译jit被关闭或者你所使用的是一个不带jit的JVM,性能会将下降21%之多!
例子:

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;
}

修改为:
将try/catch块移出循环

void method(fileinputstream fis) {
    try {
        for (int i = 0; i < size; i++) {
            _sum += fis.read();
        }
    } catch (exception e) {}
}

14.对于boolean值,避免不必要的等式判断
将一个boolean值与一个true比较是一个恒等操作(直接返回该boolean变量的值). 移走对于boolean的不必要操作至少会带来2个好处:

  1. 代码执行的更快(生成的字节码少了5个字节);

  2. 代码也会更加干净 。
    例子:

    public class ueq
     {
         boolean method(string string) {
             return string.endswith("a") == true; // violation
         }
     }
    

修改为:

public class ueq
{
    boolean method(string string) {
        return string.endswith("a");
    }
}

15.常量字符串用’String’代替’Stringbuffer’
常量字符串并不需要动态改变长度。
例子:

public class usc {
    String method() {
        Stringbuffer s = new Stringbuffer("hello");
        String t = s + "world!";
        return t;
    }
}

16.用’stringtokenizer’代替’indexof()‘和’substring()’
字符串的分析在很多应用中都是常见的。使用indexof()和substring()来分析字符串容易导致stringindexoutofboundsexception。而使用stringtokenizer类来分析字符串则会容易一些,效率也会高一些。
例子:

public class ust {
    void parsestring(String string) {
        int index = 0;
        while ((index = string.indexof(".", index)) != -1) {
            System.out.println(string.substring(index, string.length()));
        }
    }
}

17.使用条件操作符替代"if(cond) return; else return;"
条件操作符更加的简捷
例子:

public class if {
    public int method(boolean isdone) {
        if (isdone) {
            return 0;
        } else {
            return 10;
        }
    }
}

修改为:

public class if {
    public int method(boolean isdone) {
        return (isdone ? 0 : 10);
    }
}

18、使用条件操作符代替"if(cond) a=b; else a=c;"
例子:

public class ifas {
    void method(boolean istrue) {
        if (istrue) {
            _value = 0;
        } else {
            _value = 1;
        }
    }
    private int _value = 0;
}

修改为:

public class ifas {
    void method(boolean istrue) {
        _value = (istrue ? 0 : 1); // compact expression.
    }
    private int _value = 0;
}

19.不要总是使用取反操作符(!)
取反操作符(!)降低程序的可读性,所以不要总是使用。
例子:

public class dun {
    boolean method(boolean a, boolean b) {
        if (!a)
            return !a;
        else
            return !b;
    }
}

修改为:
如果可能不要使用取反操作符(!)


20.重复代码的提炼

       重复代码是重构收效最大的手法之一,进行这项重构的原因不需要多说。它有很多很明显的好处,比如总代码量大大减少,维护方便,代码条理更加清晰易读。
       它的重点就在于寻找代码当中完成某项子功能的重复代码,找到以后请毫不犹豫将它移动到合适的方法当中,并存放在合适的类当中。

小实例

class BadExample {

public void someMethod1(){
    //code
    System.out.println("重复代码");/* 重复代码块 */
    //code
}

public void someMethod2(){
    //code
    System.out.println("重复代码");/* 重复代码块 */
    //code
}

}

/* ---------------------分割线---------------------- */

class GoodExample {

    public voi

d someMethod1(){
        //code
        someMethod3();
        //code
    }

public void someMethod2(){
    //code
    someMethod3();
    //code
}

public void someMethod3(){
    System.out.println("重复代码");/* 重复代码块 */
}

}

21.冗长方法的分割

     有关冗长方法的分割,其实有时候与重复代码的提炼是有着不可分割的关系的,往往在我们提炼重复代码的过程中,就不知不觉的完成了对某一个超长方法的分割。倘若在你提炼了大部分的重复代码之后,某一些冗长方法依然留存,此时就要静下心来专门处理这些冗长方法了。
     这其中有一点是值得注意的,由于我们在分割一个大方法时,大部分都是针对其中的一些子功能分割,因此我们需要给每一个子功能起一个恰到好处的方法名,这很重要。可以说,能否给方法起一个好名字,有时候能体现出一个程序猿的大致水准。

小实例

class BadExample {

public void someMethod(){
    //function[1]
    //function[2]
    //function[3]
}

}

/* ---------------------分割线---------------------- */

class GoodExample {

public void someMethod(){
    function1();
    function2();
    function3();
}

private void function1(){
    //function[1]
}

private void function2(){
    //function[2]
}

private void function3(){
    //function[3]
}

}

22.嵌套条件分支的优化(1)

       大量的嵌套条件分支是很容易让人望而却步的代码,我们应该极力避免这种代码的出现。尽管结构化原则一直在说一个函数只能有一个出口,但是在这么大量的嵌套条件分支下,让我们忘了这所谓的规则吧。
       有一个专业名词叫卫语句,可以治疗这种恐怖的嵌套条件语句。它的核心思想是,将不满足某些条件的情况放在方法前面,并及时跳出方法,以免对后面的判断造成影响。经过这项手术的代码看起来会非常的清晰,下面就给各位举一个经典的例子,各位可以自行评判一下这两种方式,哪个让你看起来更清晰一点。

小实例

class BadExample {

public void someMethod(Object A,Object B){
    if (A != null) {
        if (B != null) {
            //code[1]
        }else {
            //code[3]
        }
    }else {
        //code[2]
    }
}

}

/* ---------------------分割线---------------------- */

class GoodExample {

public void someMethod(Object A,Object B){
    if (A == null) {
        //code[2]
        return;
    }
    if (B == null) {
        //code[3]
        return;
    }
    //code[1]
}

}

23.嵌套条件分支的优化(2)

      此处所说的嵌套条件分支与上面的有些许不同,它无法使用卫语句进行优化,而应该是将条件分支合并,以此来达到代码清晰的目的。由这两条也可以看出,嵌套条件分支在编码当中应当尽量避免,它会大大降低代码的可读性。
      下面请尚且不明觉厉的猿友看下面这个典型的小例子。

小实例

class BadExample {

public void someMethod(Object A,Object B){
    if (A != null) {
        if (B != null) {
            //code
        }
    }
}

}

/* ---------------------分割线---------------------- */

class GoodExample {

public void someMethod(Object A,Object B){
    if (A != null && B != null) {
        //code
    }
}

}

24.去掉一次性的临时变量

     生活当中我们都经常用一次性筷子,这无疑是对树木的摧残。然而在程序当中,一次性的临时变量不仅是对性能上小小的摧残,更是对代码可读性的亵渎。因此我们有必要对一些一次性的临时变量进行手术。

小实例

class BadExample {

private int i;

public int someMethod(){
    int temp = getVariable();
    return temp * 100;
}

public int getVariable(){
    return i;
}

}

/* ---------------------分割线---------------------- */

class GoodExample {

private int i;

public int someMethod(){
    return getVariable() * 100;
}

public int getVariable(){
    return i;
}

}

25.消除过长参数列表

      对于一些传递了大批参数的方法,对于追求代码整洁的程序猿来说,是无法接受的。我们可以尝试将这些参数封装成一个对象传递给方法,从而去除过长的参数列表。大部分情况下,当你尝试寻找这样一个对象的时候,它往往已经存在了,因此绝大多数情况下,我们并不需要做多余的工作。

小实例

class BadExample {

public void someMethod(int i,int j,int k,int l,int m,int n){
    //code
}

}

/* ---------------------分割线---------------------- */

class GoodExample {

public void someMethod(Data data){
    //code
}

}

class Data{

private int i;
private int j;
private int k;
private int l;
private int m;
private int n;

  //getter&&setter
    
}

28.提取类或继承体系中的常量

     这项重构的目的是为了消除一些魔数或者是字符串常量等等,魔数所带来的弊端自不用说,它会让人对程序的意图产生迷惑。而对于字符串等类型的常量的消除,更多的好处在于维护时的方便。因为我们只需要修改一个常量,就可以完成对程序中所有使用该常量的代码的修改。
     顺便提一句,与此类情况类似并且最常见的,就是Action基类中,对于INPUT、LIST、SUCCESS等这些常量的提取。

小实例

class BadExample {

public void someMethod1(){
    send("您的操作已成功!");
}

public void someMethod2(){
    send("您的操作已成功!");
}

public void someMethod3(){
    send("您的操作已成功!");
}

private void send(String message){
    //code
}
}

/* ---------------------分割线---------------------- */

class GoodExample {

protected static final String SUCCESS_MESSAGE = "您的操作已成功!";

public void someMethod1(){
    send(SUCCESS_MESSAGE);
}

public void someMethod2(){
    send(SUCCESS_MESSAGE);
}

public void someMethod3(){
    send(SUCCESS_MESSAGE);
}

private void send(String message){
    //code
}

}

29.让类提供应该提供的方法

     很多时候,我们经常会操作一个类的大部分属性,从而得到一个最终我们想要的结果。这种时候,我们应该让这个类做它该做的事情,而不应该让我们替它做。而且大部分时候,这个过程最终会成为重复代码的根源。

小实例

class BadExample {

public int someMethod(Data data){
    int i = data.getI();
    int j = data.getJ();
    int k = data.getK();
    return i * j * k;
}

public static class Data{
    
    private int i;
    private int j;
    private int k;
    
    public Data(int i, int j, int k) {
        super();
        this.i = i;
        this.j = j;
        this.k = k;
    }

    public int getI() {
        return i;
    }
    
    public int getJ() {
        return j;
    }
    
    public int getK() {
        return k;
    }
    
}

}

/* ---------------------分割线---------------------- */

class GoodExample {

public int someMethod(Data data){
    return data.getResult();
}

public static class Data{
    
    private int i;
    private int j;
    private int k;
    
    public Data(int i, int j, int k) {
        super();
        this.i = i;
        this.j = j;
        this.k = k;
    }

    public int getI() {
        return i;
    }
    
    public int getJ() {
        return j;
    }
    
    public int getK() {
        return k;
    }
    
    public int getResult(){
        return i * j * k;
    }
    
}

}
发布了47 篇原创文章 · 获赞 18 · 访问量 5731

猜你喜欢

转载自blog.csdn.net/yuruizai110/article/details/85788329