第5条:避免创建不必要的对象

Posted MachineChen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第5条:避免创建不必要的对象相关的知识,希望对你有一定的参考价值。

最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。

如果对象是不可变(immutable),它就始终可以被重用

String s = new String("stringette");

每次执行上面的语句都会创建一个新的String实例。因为”stringette”本身就是一个String实例,这和构造器创建出来的新对象功能方面完全相同。如果频繁调用这一方法,就会创建成千上万不必要的String实例。

String s = "stringette";

这样就保证了只有一个String实例,而不是每次执行都创建一个新的实例。并且同一台JVM只要字符串字面常量相同,对象就会被重用。

同时提供静态工厂方法和构造器的不可变类,通常可以使用静态工厂方法而不是构造器以避免创建不必要对象。例如静态工厂方法Boolean.valueOf(String)回返回一个固定的Boolean实例,而构造器Boolean(String)在每次被调用都会创建一个新的对象。

已知可变对象不会被修改,则也可以重用

public class Person 
    private final Date birthDate;

    public Person(Date birthDate) 
        this.birthDate = birthDate;
    

    public boolean isBabyBoomer()
        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实例,这是不必要的。下面用静态初始化器(initializer),避免这种效率低下情况:

public class Person 
    private final Date birthDate;

    public Person(Date birthDate) 
        this.birthDate = birthDate;
    

    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;
    

这样Person类只在初始化时创建Calendar、TimeZone和Date实例一次,而不是每次调用isBabyBoomer的时候都创建这些实例。除了性能的提升,代码的含义也更加清晰——boomStart和boomEnd从局部变量改为final静态域,表明这些数据被当成常量对待,使得代码更易于理解。

自动装箱、拆箱可能会创建多余对象。

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

上面的代码sum+=i可以看成sum = sum + i,但是+这个操作符不适用于Integer对象,首先sum进行自动拆箱操作,进行数值相加操作,最后发生自动装箱操作转换成Integer对象。其内部变化如下:

int result = sum.intValue() + i;
Integer sum = new Integer(result);

sum被声明为Long而不是long,这样程序构造了2^31个多余的Long实例。所以这里结论是要优先使用基本类型而不是装箱类型,当心无意识自动装箱。

注意

不要认为“创建对象代价非常昂贵”。实际上小对象的构造器只做很少量的显式工作,所以小对象的创建和回收动作是非常廉价的,现代JVM上更是如此。通过创建附加的对象,提升程序的清晰性、简洁性和功能性。
反之自己维护对象池很麻烦,除非对象非常重量级,比如数据库连接池

以上是关于第5条:避免创建不必要的对象的主要内容,如果未能解决你的问题,请参考以下文章

第5条:避免创建不必要的对象

《Effective Java 中文版 第2版》学习笔记 第5条:避免创建不必要的对象

[Effective JavaScript 笔记]第56条:避免不必要的状态

第7条:避免使用终结方法

Java:Effective java学习笔记之 避免创建不必要的对象

Effective Java总结的78条