Effective
创建和销毁对象
1. 考虑用静态工厂方法代替构造器
优势
- 它们有名称
- 不必在每次调用他们的时候都创建一个新对象
- 它们可以返回原返回类型的任何子类型的对象
interface Service {
}
interface Provider {
Service newService();
}
class Test implements Provider {
@Override
public Service newService() {
// TODO Auto-generated method stub
return null;
}
}
public class Services {
private Services() {}
private static final Map<String, Provider> providers = new ConcurrentHashMap<String, Provider>();
private static final String DEFAULT_PROVIDER_NAME = "<def>";
public static void registerDefaultProvider(Provider p) {
registerProvider(DEFAULT_PROVIDER_NAME, p);
}
public static void registerProvider(String name, Provider p) {
providers.put(name, p);
}
public static Service newInstance() {
return newInstance(DEFAULT_PROVIDER_NAME);
}
public static Service newInstance(String name) {
Provider p = providers.get(name);
if(p == null)
throw new IllegalArgumentException("");
return p.newService();
}
}
- 在创建参数化类型实例的时候,它们使代码变得更简洁
缺点
- 类如果不含公有的或者受保护的构造器,就不能被子类化.
- 它们与其他的静态方法实际上没有任何区别.
静态工厂方法惯用名称
valueOf ------------ 类型转换方法
of --------------valueOf 简写
getInstance ------------ 返回唯一实例
newInstance ----------- 返回新的实例
getType ------------ 跟getInstance一样, 返回对象类型
newType -----------跟newInstance一样,返回新的对象类型
2. 遇到多个构造器参数时考虑用构造器
- 重叠构造器模式可行,但是当有许多参数的时候,客户端代码很难编写,并且仍然较难以阅读
- 如果类的构造器或者静态工厂中具有多个参数,设计这种类时,Builder模式就是种不错的选择,特别是当大多数参数都是可选的时候
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int cat;
public static class Builder {
private int servingSize;
private int servings;
private int calories = 0;
private int cat = 0;
public Builder(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public Builder calories(int val) {
this.calories = val;
return this;
}
public Builder cat(int val) {
this.cat = val;
return this;
}
public NutritionFacts build() {
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder) {
servingSize = builder.servingSize;
servings = builder.servings;
calories= builder.calories;
cat = builder.cat;
}
}
3. 用私有构造器或者枚举类型强化Singleton属性
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() {}
public static Elvis getInstance() {
return INSTANCE;
}
// 防止序列化
private Object readResolve() {
return INSTANCE;
}
}
单元素的枚举类型已经成为实现Singleton的最佳方法
public enum Elvis {
INSTANCE;
}
4. 通过私有构造器强化不可实例化的能力
企图通过将类做成抽象类来强制该类不可被实例化,这是行不通的
public class UtilityClass {
private UtilityClass() {
throw new AssertionError();
}
}
5. 避免创建不必要的对象
要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱.
6. 消除过期对象的引用
- 如果一个栈先是增长,然后再收缩,那么,从栈中弹出来的对象将不会被当做垃圾回收,即使使用栈的程序不再引用这些对象,他们也不会被回收.这是因为,栈内部维护着对这些对象的过期引用(指的是永远也不会再被解除的引用)
public class Stack {
private Object[] elements;
public int size = 0;
private static final int 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 elements[--size];
}*/
//清空过期引用
public Object pop() {
if(size == 0)
throw new EmptyStackException();
Object result = elements[--size];
elements[size] = null;
return result;
}
private void ensureCapacity() {
if(elements.length == size)
elements = Arrays.copyOf(elements, 2 * size + 1);
}
public Object[] getElements() {
return this.elements;
}
public static void main(String[] args) {
Stack s = new Stack();
for(int i = 0; i < 15; i++) {
s.push(i);
}
for(int i = 0; i < s.elements.length; i++) {
System.out.print(s.elements[i] + " ");
}
System.out.println();
System.out.println(s.pop());
System.out.println(s.pop());
for(int i = 0; i < s.elements.length; i++) {
System.out.print(s.elements[i] + " ");
}
}
}
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 null
14
13
0 1 2 3 4 5 6 7 8 9 10 11 12 null null null
- 清空对象引用应该是一种例外,而不是一种规范行为
内存溢出问题
- 只要类是自己管理内存,程序员就应该警惕内存泄漏问题
- 内存泄漏的另一个常见来源是缓存
- 内存泄漏的第三个常见来源是监听器和其他回调
7. 避免使用终结方法
终结方法(finalizer)通常是不可预测的,也是很危险的,一般情况下是不必要的.
- 不应该依赖终结方法来更新重要的持久状态.
- System.gc和System.runFinalization它们确实增加了终结方法被执行的机会,但是它们并不保证终结方法一定会被执行。唯一声称保证终结方法被执行的方法是System.runFinalizersOnExit和Runtime.runFinalizersOnExit这两个方法都有致命缺陷
- 可以使用显式的终止方法通常与try-finally 结构结合起来使用, 以确保及时终止.
终结方法好处
- 当对象的所有者忘记调用显式终止方法时,终结方法可以当"安全网".
- 与对象的本地对等体(native peer)有关,本地对等体是一个本地对象(native object),普通对象通过本地方法(native method) 委托给一个本地对象.因为本地对等体不是一个普通的对象,所以垃圾回收不会知道它,当它的java对等体被回收的时候,它不会被回收.
总之,除非是作为安全网,或者是为了终止非关键的本地资源,否则不要使用终结方法.在这些很少见的情况下,既然使用了终结方法,纪要记住调用super.finalize.如果用终结方法作为安全网,要记得记录终结方法的非法用法.最后,如果需要把终结方法与公有的非final类关联起来,请考虑使用终极方法守卫者,以确保即使子类的终结方法未能调用super.finalize该终结方法也会被执行