1.考虑用静态工厂方法代替构造器
静态工厂方法与构造器不同的第一大优势在于:1.它们有名称
2.不必在每次调用它们的时候都创建一个新对象
3.它们可以返回原返回类型的任何子类型的对象
4.在创建参数化类型实例的时候,它们使代码变得更加简洁 列如类型推导:
public static <K, V> HashMap<K, V> newInstance() { return new HashMap<K, V>();
主要缺点在于:类如果不含公有的或者受保护的构造器,就不能被子类化;它们与其他的静态方法实际上没有任何区别。
静态工厂通常更加合适,因此切忌第一反应就是提供平公有的构造器,而不先考虑静态方法。
2.遇到多个构造器参数时要考虑用构造器
采用重叠构造器:在参数较多时,客户端代码会很难写,并且难以阅读。
采用javabeans模式:调用一个无参构造器来创建对象,然后调用setter方法来设置参数。 有线程安全问题。
Builder模式:既保证像重叠构造模式那样的安全性,也能保证像javabean模式那么好的可读性。
示例:
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
public static class Builder {
//required parameters
private final int servingSize;
private final int serving;
//optional parameters - initialized to default values
private int calories = 0;
private int fat = 0;
private int sodium = 0;
pulbic Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
pulbic Builer calories(int val)
{ calories = val; return this;}
public Builder fat(int val)
{ fat=val; return this;}
public Builder sodium(int val)
{ sodium = val; return this;}
public NutritionFacts build() {
return new NutritionFacts(this);
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
serving = builder.serving;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
}
}
下面是客户端代码
NutritionFacts cocaCola=new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();
Builder模式的不足:为了创建对象,必须先创建它的构造器。虽然创建构造器的开销在实践中可能不那么明显,但是在某些十分注重性能的情况下,可能就成问题了。Builder模式还比重叠构造器模式更加冗长,因此它只在有很多参数的时候才使用,比如四个或者更多。
但是将来你可能需要添加参数,如果一开始使用构造器或者静态工厂,等到需要多个参数时才添加构建器,就会无法控制,那些过时的构造器或者静态工厂显得十分不协调,因此,通常最好一开始就使用构建器。
3.用私有构造器或者枚举类型强化Singleton属性
没怎么懂
4.通过私有构造器强化不可实例化的能力
对于一些只包含静态方法和静态域的类,例如java.lang.Math或者java.util.Arrays这种。这样的工具类不希望被实例化,实例对它没有意义。
由于只有当类不包含显式的构造器时,编译器才会生成缺省的构造器,因此可以让这个类包含私有的构造器,它就不能被实例化了:
//Noninstaniable utility class
public class UtilityClass {
//Suppress default constructor for noninstantiability
private UtilityClass() {
throw new AssertionError();
}
……
}
由于显示的构造器是私有的,所以不可以在该类的外部访问它。AssertionError不是必需的,但是它可以避免不小心在类的内部调用构造器。
副作用是它使得一个类不能被子类化。所有的构造器都必须显式或隐式地调用超类(superclass)构造器,在这种情形下,子类就没有可访问的超类构造器可调用了。
5.避免创建不必要的对象
一般来说,最好能重用对象而不是在每次需要的时候就创建一个相同功能的新对象。
但是不要错误地认为本条目所介绍的内容暗示着“创建对象的代价非常昂贵,我们应该要尽可能地避免创建对象”。相反,小对象的创建和回收动作是非常廉价的,通过创建附加的对象,提升程序的清晰性、简洁性和功能性,这通常是件好事。
通过维护自己的对象池来避免创建对象并不是一种好的做法,除非池中的对象是非常重量级的。
与本条目对应的是38条中有关“保护性拷贝”的内容。注意,在提倡使用保护性拷贝的时候,因重用对象而付出的代价要远远大于因创建重复对象而付出的代价。必要时如果没能实施保护性拷贝,将会导致潜在的错误和安全漏洞,而不必要的创建对象则只会影响程序的风格和性能。
6.消除过期的对象引用
示例:
public class Stack {
private Object[] elements;
private int size = 0;
private static final jint DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
elements = new Object[DEFAULT_INITIAL_CAPACITY];
}
public void push(Object e) {
ensureCapacity();
elements[size++] = e;
]
public Object pop() {
if (size==0)
throw new EmptyStackException();
return element[--size];
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2*size+1);
}
}
栈内部维护着这些对象的过期引用。所谓过期引用是指永远也不会再被解除的引用。在本例中,凡是在elements数组的“活动部分”之外的任何引用都是过期的。活动部分是指elements中下标小于size的那些元素。
pop方法的修订版本如下所示:
public Object pop() {
if(size ==0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null; //eliminate obsolete reference
return result;
}
内存泄露的另一个常见来源是缓存
第三个常见来源是监听器和其他回调。
7.避免使用终结方法
终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的。
此条目需要再理解。