创建和销毁对象
本文为阅读effective java的总结
文章目录
一、使用静态工厂方法代替构造器
优点:
- 有名称,更语义化
- 可以不用每次都新创建对象,缓存实例后,进行重复利用
- 可以返回原返回类型的任何子类型对象
- 可以通过参数值,让返回对象的类随着每次调用发生变化
- 方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在
缺点:
- 类中如果不含公有或者受保护的构造器,那么就不能被子类化
- 文档中标识不明确
常用静态工厂方法命名
- from–类型转换方法,它只有单个参数,返回该类型的一个相对应的实例
- of–聚合方法,带有多个参数,返回该类型的一个实例,把它们合并起来
- valueOf–比from和of更繁琐的一种替代方法
- instance和getInstance–返回的实例是通过方法的(如有)参数来描述的,但是不能说与参数具体同样的值
- create和newInstance–和上面一样,但是每次返回的都是新的实例
- getType–和getInstance一样,当工厂方法处于不同类的时候使用
- newType–和newInstance一样,当工厂方法处于不同类的时候使用
- type–getType和newType的简版
二、遇到多个构造器参数时要考虑使用构建器
静态工厂和构造器共同的局限性:它们都不能很好的扩展大量的可选参数
public class NutritionFacts {
private final int servingSize;
private final int servings;
private final int calories;
private final int fat;
private final int sodium;
private final int carbohydrate;
public NutritionFacts(int servingSize, int servings) {
this.servingSize = servingSize;
this.servings = servings;
}
public NutritionFacts(int servingSize, int servings, int calories) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
}
public NutritionFacts(int servingSize, int servings, int calories, int fat, int sodium, int carbohydrate) {
this.servingSize = servingSize;
this.servings = servings;
this.calories = calories;
this.fat = fat;
this.sodium = sodium;
this.carbohydrate = carbohydrate;
}
}
当创建实例时,就利用参数列表最短的构造器,但该列表包含了要设置的所有参数。这个构造器调用通常包含有许多你不想传递的参数,但还是不得不为它们传递值。
重叠构造器的方式并不适用于有大量参数的场景,则应该使用javabean模式。
但javabean也是有缺陷的,因为构造过程是在多次调用中的,所以javabean可能处于一个不一致的状态,javabean也把类做成不可变的可能性不复存在。
如果构造器参数过多可以使用builder模式。
三、用私有构造器或者枚举类型强化 Singleton 属性
在使用单例模式的时候,通常会私有化构造方法,常规的调用不会产生新的对象,但是通过反射机制还是可以调用到私有的构造函数,如果需要避免这类问题,修改构造器,让它在被要求创建第二个实例时抛出异常。
为了让上述方法实现的Singleton类变成可序列化的,仅仅在声明中加上 implements Serializable 是不够的 为了维护并保证Singleton,必须 明所有实例域都是瞬时( transient )的,并提供一个 readResolve 方法.否则,每次反序列化都会创建一个新的实例。
四、通过私有构造器强化不可实例化的能力
工具类记得私有化构造函数
五、优先考虑依赖注入来引用资源
静态工厂和Sigleton类不适用于需要引用底层资源的类。
最基础的依赖注入就是,创建一个新的实例时,将资源传入构造器中。
六、避免创建不必要的对象
优先使用基本类型而不是装箱基本类型,当心无意识的自动装箱
七、消除过期的对象引用
public class Stack {
private Object[] elements;
private int size = 0;
private static final int DEFAULT_INITIAL_CAPACITY = 16;
public Stack() {
ensureCapacity();
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];
}
private void ensureCapacity() {
if (elements.length == size) {
elements = Arrays.copyOf(elements, 2 * size + 1);
}
}
}
上面的程序,如果一个栈先增长,然后再收缩,那么从栈中弹出来的对象不会被当作垃圾回收,即使使用栈的程序不再引用这个对象,他们也不会被回收。那是因为栈内部维护着对这些对象的过期引用,过期引用是指永远不会被解除的引用。在本例中,凡是在elements中下标小于size的那部分元素都是过期的。解决上述问题只需要在弹栈后清空引用。
public Object pop() {
if (size == 0) {
throw new EmptyStackException();
}
Object e=elements[--size];
elements[size] =null;
return e;
}
内存泄漏的另一个可能性来自于缓存
内存泄漏的第三个来源是监听器和其它回调,确保回调立即被当作垃圾回收的方式是只保存弱引用。
八、避免使用终结方法和清除方法
不行就不行,懒得说
九、try-with-resources优先于try-finally
优先就优先,不想解释