EffectiveJava——创建和销毁对象2

第2条:遇到多个构造器参数时要考虑使用构造器

构造器和静态方法有一个共同的局限性:它们都不能很好的扩展大量的可选参数。

可选方案:

1:重叠构造器:重叠构造器模式可行,但是当有许多个参数的时候,客户代码会很难编写,并且仍难以阅读,所以不推荐
2:JavaBean模式:调用无参数的构造器创建对象,然后通过各种setter方法为每个参数赋值,缺点在于:因为构造过程被分到了几个调用中,在构造过程中JavaBean可能处于不一致的状态,我的理解是各个参数导致的行为是相互影响的,JavaBean模式不能同时为各个参数赋值。
3:Builder模式:也叫构建者模式,不直接生成想要的对象,而是通过一个Builder对象设置各种可选参数,然后通过Builder的无参方法生成一个不可变对象。但是通过Builder模式创建对象需要创建额外的对象,所以在对象的构造参数教多时Builder模式才是不错的选择

第3条:用私有的构造器或者枚举类型强化Singleton属性

  • 懒汉式(注意单利引用加上volatile关键字)
  • 饿汉式(如果要防止客户端利用反射机制构建第二个对象,可在构造器中判断实例化第二个对象时抛出异常)
  • 内部类形式
  • 单元素的枚举形式

第4条:通过私有构造器强化不可实例化的能力

想Collections,Math等工具类不希望被实例化,实例对其没有任何意义,然而缺少构造器的情况下,编译器会自动为其添加一个公共的无参构造器,通过一些手段可以确保它们不会被实例化:

public class UtilityClass{
   private UtilityClass(){
      throw new AssertionError();
   }
}

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

介绍:

一般来说,最好能重用对象而不是在需要的时候都创建一个相同功能的对象,重用方式快速有方便,如果对象是不可变的,他就始终可以被重用。

一个极端反面的例子:

String s = new String("string");

该语句每次执行的时候都会创建一个新的String实例,而

String s = "string";

可以保证同一台java虚拟机运行的代码,只要他们包含相同的字符串字面常量,该对象就会被重用

1:对于同时提供静态工厂方法和构造器的不可变类,通常可以使用静态工厂方法而不是构造器,以避免创建不必要的对象,如:
     Boolean.valueOf(String);
    //源码如下:
     public static Boolean valueOf(String s) {
        return parseBoolean(s) ? TRUE : FALSE;
     }
2:除了重用不可变对象外,还可以重用那些已知的不会被改变的可变变对象
class Person {
    private final Date birthDate;

    public Person(Date date) {
        birthDate = date;
    }


    public boolean isBadyBomer() {
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
        gmtCal.set(1964, 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;
    }
}

isBadyBomer 中每次都会创建一个Calendar,一个Time.Zone和两个Date实例,如果改成下面方式:

class Person {
    private final Date birthDate;

    public Person(Date date) {
        birthDate = date;
    }

    public static final Date BOOM_START;
    public static final Date BOOM_END;

    static {
        Calendar gmtCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

        gmtCal.set(1964, 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 isBadyBomer() {
        return birthDate.compareTo(BOOM_START) >= 0 && birthDate.compareTo(BOOM_END) < 0;
    }
}

这样减少了方法每次调用时对象的创建,其次代码变得更加清晰。

这里也不建议使用延迟加载(lazily initializing),这样做会使方法实现更加复杂

3:注意Java1.5后的自动装箱(autoBoxing)

自动装箱使得基本类型和装箱基本类型(boxed primitive type)可以混用。例如下面代码:

        Long sum = 0L;
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            sum += i;
        }

变量sum被声明为Long而不是long,意味着程序大约构造了2的31方个多余的对象
所以结论是:要优先使用基本类型而不是装箱类型,当心无意识的自动装修

总结:

  1. 不要错误的认为创建对象的代价非常昂贵,我们应该尽可能的避免对象,小对象的创建与回收是非常廉价的,通过创建附加的对象,提升程序的清晰性,简洁性和功能性,这通常是好事
  2. 反之通过维护自己的对象池来避免创建对象并不是一种好的做法,除非对象是非常重量级的

你可能感兴趣的:(对象,javabean)