扎实的java功底系列(Effective java)5

避免创建不必要的对象

首先,由一个例子引出这个问题:

//首先这个是调用正常的String构造方法
String str = new String("test string");
//下面这个是一个String的自动装箱,过程就是把String类型的字符串首先构造成一个String类的对象,再赋给str
String str = "test string 2";

上面这个放在循环中,每次都会构造一个新的对象,而下面这个则会重用这个对象。比如说:

1 String string = "hei goodboy";
2 String string2 = "hei goodboy";
3 System.out.println(string == string2);
4 System.out.println(string.equals(string2));

上面这段代码输出的结果是:true true

那么可以看出string 和 string2引用的是同一个对象。

String string = new String("hei goodboy");
String string2 = new String("hei goodboy");
System.out.println(string == string2);
System.out.println(string.equals(string2));

这段代码的输出是:true false,== 比较的是两个对象的内容 而equals比较的是两个对象的引用是否相同,很明显,这里的两个string对象是不同的。

除了重用不可变的对象之外,我们也可以重用那些已知不会被修改的对象。还是用Car的例子改书上的,上代码:

package serviceprovider;

import java.sql.Date;
import java.util.Calendar;
import java.util.TimeZone;

public class Car {
    /**
     * 检验一辆车是否产于2013-2015年
     */
    private final Date birthdayDate = null;
    public boolean isBoomBetweenYear(){
        Calendar gmtcal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtcal.set(2013, Calendar.JANUARY);
        Date startDate = (Date) gmtcal.getTime();
        gmtcal.set(2015, Calendar.JANUARY);
        Date endDate = (Date) gmtcal.getTime();
        return birthdayDate.compareTo(startDate)>=0 && birthdayDate.compareTo(endDate) <0;
    }
    
    
}

上面这段代码每次调用isBoomBetweenYear的时候都要创建很多新的对象,比如gmtcal 、两个Date类型的变量,仔细观察,我们发现gmtcal对象在创建以后就可以一直使用同一个,不需要重新创建,故提出来,不放在方法中。

两个Date类型的变量也可以提出来。因此代码变成了:

package serviceprovider;

import java.sql.Date;
import java.util.Calendar;
import java.util.TimeZone;

public class Car {
    /**
     * 检验一辆车是否产于2013-2015年
     */
//    private final Date birthdayDate = null;
//    public boolean isBoomBetweenYear(){
//        Calendar gmtcal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
//        gmtcal.set(2013, Calendar.JANUARY);
//        Date startDate = (Date) gmtcal.getTime();
//        gmtcal.set(2015, Calendar.JANUARY);
//        Date endDate = (Date) gmtcal.getTime();
//        return birthdayDate.compareTo(startDate)>=0 && birthdayDate.compareTo(endDate) <0;
//    }
    
    private final Date birthdayDate = null;
    private static final Date STARTDATE;
    private static final Date ENDDATE;
    static {
        Calendar gmtcal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtcal.set(2013, Calendar.JANUARY);
        STARTDATE = (Date) gmtcal.getTime();
        gmtcal.set(2015, Calendar.JANUARY);
        ENDDATE = (Date) gmtcal.getTime();
    }
    public boolean isBoomBetweenYear(){
        return birthdayDate.compareTo(STARTDATE)>=0 && birthdayDate.compareTo(ENDDATE) <0;
    }
    
}

改进的代码只在类初始化的时候才创建一次Calendar、TimeZone、Date实例一次。

当你在自动装箱的时候,java会创建多余的对象。

public static void main(String[] args) {
        Long sum = 0L;
        for(long i = 0; i<Integer.MAX_VALUE; i++){
            sum += i;
        }
}

上面的代码能够正确运行但是在运行到 sum+=i的时候会有一个自动装箱的过程,会创建一个新的对象。下面我们验证一下:

package serviceprovider;

import java.sql.Date;
import java.util.Calendar;
import java.util.TimeZone;

public class Car {
    public static void main(String[] args) {
        Long sum = 0L;
        Long sum2 = sum;
        System.out.println(sum2 == sum);
        System.out.println(sum2.equals(sum));
        /**
         * sum 和 sum2 无论是内容和引用都是一样的
         */
        for(long i = 0; i<3; i++){
            sum += i;
            System.out.println(sum.equals(sum2));
            System.out.println(sum == sum2);
        }
    }
    
}

代码运行的结果是true true

true true

false false

false flase

也就是说在循环内的sum 由于自动装箱,引用改变了。当然在加上0的时候是不会改变引用对象的。

 

posted @ 2015-07-12 13:56  loutao  阅读(162)  评论(0)    收藏  举报