《Effective Java》读书笔记04--避免创建不必要的对象

来源:互联网 发布:网络发展趋势 编辑:程序博客网 时间:2024/09/21 09:08

一、Java的String对象

java中String对象很特殊,因为它是个不可变对象。每次对String进行截取,拼接都会返回一个新的String对象。正因为这个原因,初学者经常陷入对字符串操作的误区,包括我,呵呵。

两种生成字符串的方式:

String s = new String("strubgette"); //每次执行都会创建一个新的String实例,即使它们在功能方面是等同的。何必呢,呵呵,又不是内存用不完。。。

String s = "strubgtte"; //如果JVM常量池中有该字符串字面常量,则直接将引用赋给s,否则创建一个新的,基本可以保证只用到一个String实例。

二、重用对象之道

1、不可变对象(immutable object)始终应该被重用。

2、不会被修改的可变对象也应该被重用。

反面示例:

public class Person {    private final Date birthDate;    public Person(Date birthDate) {        // 保护性拷贝,Date是可变对象,否则,哈哈,有你受的        this.birthDate = new Date(birthDate.getTime());    }    // DON'T DO THIS!//判断一个是是否在生育高峰期出生(1946~1964)    public boolean isBabyBoomer() {        // Unnecessary allocation of expensive object        Calendar gmtCal =            Calendar.getInstance(TimeZone.getTimeZone("GMT"));        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);        Date boomStart = gmtCal.getTime();        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);        Date boomEnd = gmtCal.getTime();        return birthDate.compareTo(boomStart) >= 0 &&               birthDate.compareTo(boomEnd)   <  0;    }}
这里每调用isBabyBoomer()方法都会创建一个Calendar,一个TimeZone和两个Date,这玩意可不是闹着玩的,一个Calendar实例的创建可以买头猪。。。

修正版:

class Person {    private final Date birthDate;    public Person(Date birthDate) {        //保护性拷贝        this.birthDate = new Date(birthDate.getTime());    }    /**     * The starting and ending dates of the baby boom.     */    private static final Date BOOM_START;     private static final Date BOOM_END;    static {        Calendar gmtCal =            Calendar.getInstance(TimeZone.getTimeZone("GMT"));        gmtCal.set(1946, Calendar.JANUARY, 1, 0, 0, 0);        BOOM_START = gmtCal.getTime();        gmtCal.set(1965, Calendar.JANUARY, 1, 0, 0, 0);        BOOM_END = gmtCal.getTime();    }    public boolean isBabyBoomer() {        return birthDate.compareTo(BOOM_START) >= 0 &&               birthDate.compareTo(BOOM_END)   <  0;    }}
两个Date做成了final静态域,Calendar和TimeZone也只会在Person类初始化时创建一次,不仅代码逻辑上清晰了,而且性能貌似也有了提高。当然,还可以延迟初始化BOOM_START和BOOM_END,但没必要这样做,因为延迟初始化是有风险的。

三、有个名词叫自动装箱(autoboxing)

Java1.5发行版中,一个牛叉的概念出现了,自动装箱。它允许程序员将基本类型和装箱基本类型(Boxed Primitive Type)混用,按需要自动装箱和拆箱。这貌似成了我们Java菜鸟的福音,动不动就来个Integer,它会装箱哈,多牛叉。殊不知,性能刷刷的滑,这回臭大了。。。

来看一个不小心就会犯的错误:

public class Sum {    // Hideously slow program! Can you spot the object creation?    public static void main(String[] args) {        Long sum = 0L;        for (long i = 0; i < Integer.MAX_VALUE; i++) {            sum += i;        }        System.out.println(sum);    }}
变量sum被声明成Long而不是long,打错一个字符,意味着程序需要额外构造大约2^31个多余的Long实例,很蛋疼吧?!

四、最佳编程实践

1、对于同时提供静态工厂方法和构造器的不可变类,通常可以使用静态工厂方法而不是构造器,以避免创建不必要的对象。静态的是类级的,跟对象没啥关系,创不创建都能调用里面的方法。

2、不可变对象(immutable object)始终应该被重用。

3、不会被修改的可变对象也应该被重用。

4、要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。

原创粉丝点击