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

一、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、要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值