2015-09-16-创建和销毁对象
第1条:考虑用静态工厂方法代替构造器
该规则的优势:
1.静态工厂方法有名称。构造器的参数本身没有确切描述正被返回的对象。当一个类需要多个
带有相同签名的构造器时,用静态工厂方法代替。
2.不必每次调用它们都创建一个新对象。
3.可以返回原返回类型的任何子类型的对象。
4.在创建参数化类型实例的时候,他们使代码更见简洁。
(还是不太理解,等之后再看看)
静态工厂方法的缺点:
第2条:遇到多个构造参数时考虑用构建器
由于当有多个构造参数时,且大多数构造参数是可选的,如果使用构造器的话,会使代码难以阅读,不清楚构造器中的构造参数具体是什么。此时应该使用构建器。
public class NutritionFacts{
private final int servingSize;
private final int servings;
private final int fat;
private final int calories;
private final int sodium;
private final int carbohydrate;
public static class Builder{
//Required param
private final int servings;
private final int servings;
private int calories = 0;
private int fat = 0;
private int carbohydrate = 0;
private int sodium = 0;
public Builder(int servings, int servingSize){
this.servings = servings;
this.servingSize = this.servingSize;
}
public Builder 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 Builder carbohydrate(int val){
carbohydrate = val;
return this;
}
public NutritionFacts build(){
return new NutritionFacts(this);
}
}
private NutritionFacts(Builder builder){
servings = builder.servings;
servingSize = builder.servingSize;
calories = builder.calories;
fat = builder.fat;
sodium = builder.sodium;
carbohydrate = builder.carbohydrate;
}
}
//NutritionFacts cocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).carbohydrate(27).build();
需要注意的时,由于为了创建对象必须先创建它的构建器,在十分注重性能的时候,这可能会成为一个问题。
第3条:用私有构造器或者枚举类型强化Singleton属性
Singleton是指仅仅被实例化一次的类。
方法一:
public class Elvis{
public static final Elvis INSTANCE = new Elvis();
private Elvis(){...}
...
}
方法二:
public class Elvis{
private static final Elvis INSTANCE = new Elvis();
private Elvis(){...}
public static Elvis getInstance(){return INSTANCE};
....
}
方法三(最佳):
public enum Elvis{
INSTANCE;
...
}
通过私有构造器强化不可实例化的能力
有些工具类不希望被实例化,但是如果没有显示的构造器(自己没写构造器)的话,编译器会自动提供一个公有的、无参的缺省构造器,那么这时候我们就需要通过构造一个私有的构造器来强化这种能力。
public class UtilityClass{
private UtilityClass(){
}
}
由上面我们知道,添加私有构造器的主要原因有两个:
1.希望类具有不可实例化的能力
2.没有显示构造器时,编译器会自动提供一个公有的无参的缺省构造器。
(私有的构造器也是显示的)
这种用法的副作用是,它使得一个类不能子类化,即其他类不能继承该类。原因是所有子类都显示/隐式的调用夫类的构造器。
避免创建不必要的对象
明显的,当我们创建的对象越多,对程序的性能也有一定的影响。因此应该避免创建不必要的对象。如下面例子:
publci class Person{
private final Date birthDate;
public boolean isBabyBoomer(){
Calendar gmtCal =
Calendar.getInstance(TimeZon.getTimeZon("GMT"));
gmt.set(1946,Calendar.JANUARY,1,0,0,0);
Date boomStart = gmtCal.getTime();
gmt.set(1965,Calendar.JANUARY,1,0,0,0);
Date boomEnd = gmtCal.getTime();
return birthDate.compare(boomStart)>= 0 &&
birthDate.compare(boomEnd)>=0;
}
}
改进
publci class Person{
private final Date birthDate;
private static final Date BOOM_START;
private static final Date BOOM_END;
static{
Calendar gmtCal =
Calendar.getInstance(TimeZon.getTimeZon("GMT"));
gmt.set(1946,Calendar.JANUARY,1,0,0,0);
BOOM_START = gmtCal.getTime();
gmt.set(1965,Calendar.JANUARY,1,0,0,0);
BOOM_END = gmtCal.getTime();
}
public boolean isBabyBoomer(){
return birthDate.compare(boomStart)>= 0 &&
birthDate.compare(boomEnd)>=0;
}
}
同时需要注意的是:要优先使用基本类型而不是装箱基本类型,要当心无意识的自动装箱。如long,Long
消除过期的对象引用
在Java中具有垃圾回收的功能,但是有时候也会造成内存泄露。所谓过期的对象引用是指永远也不会再被解除的引用。
public class stack{
private Object[] elements;
private 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];
/*改进
Object result = elements[--size];
elements[size] = null;
return result;
*/
}
public void ensureCapa(){
if(elements.length == size)
elements = Array.copyOf(elements, 2*siz+1);
}
}
这里造成内存泄露的原因是当调用pop函数时,虽然栈的程序不再引用这些对象,但是这些对象也不会被回收,从而造成过期引用。
清楚过期引用的另一个好处是,如果以后它们又被错误的解除引用,程序会立即抛出NullPointerException异常,而不是悄悄的错误下去。
只要类是自己管理内存的,那么程序员就应该警惕内存泄露的问题。
内存泄露的另一个常见的来源是缓存。
第三个常见的来源是监听器和其他回调。
避免使用终结方法
Java中的终结方法不同于C++的析构器,Java中的终结方法通常是不可预测的,也是很危险的,一般情况下是不必要的。
版权声明:本文为博主原创文章,未经博主允许不得转载。