effective java 学习笔记 之 创建和销毁对象

effective java 学习笔记 之 考虑用静态工作方法代替构造函数

转职:http://hi.baidu.com/macula7/blog/category/effective%20java%D1%A7%CF%B0%B1%CA%BC%C7/index/1

并对该博文的内容做了适当的补充整理。

第二章创建和销毁对象

第一条 考虑用静态工作方法代替构造函数。

publicstatic Boolean valueOf(boolean b) {

return (b ? TRUE : FALSE);

}

 

该方法根据给定的参数返回一个Boolean对象,但是需要注意的一点是。如果提供的参数相同的话获得的是同一个对象。

 

例如:

 

Booleanb1=Boolean.valueOf(true);

Booleanb2=Boolean.valueOf(true);

System.out.println(b1==b2);

Booleanb3=new Boolean(true);

Booleanb4=new Boolean(true);

System.out.println(b3==b4);

好处:

1、静态工厂方法的一个好处是,与构造函数不同,静态工厂方法具有名字。

2、静态工厂方法的第二个好处是,与构造函数不同,它们每次被调用的时候,不要求非得创建一个新的对象。

3、静态工厂方法的第三个好处是.与构造函数不同,它们可以返回一个原返回类型的子类型的对象。

publicinterface Service{

}

publicinterface Provider{

ServicenewService();

}

publicclass Services{

privateServices(){}

privatestatic final Map<String,Provider> providers=new ConcurrentHashMap<String,Provider>();

publicstatic final String DEFAULT_PROVIDER_NAME=”DEFAULT”;

publicstatic void registerDefaultProvider(Provider p){

registerProvider(DEFAULT_PROVIDER_NAME,p);

}

publicstatic void registerProvider(String name,Provider p){

registerProvider(name,p);

}

publicstatic Service newInstance(){

returnnewInstance(DEFAULT_PROVIDER_NAME);

}

 

publicstatic Service newInstance(String name){

Providerp=providers.get(name);

if(p==null)

thrownew IllegalArgumentException(“No provider registerd withname:”+name);

returnp.newService();

}

}

坏处:

1、静态工厂方法的主要缺点是,类如果不舍公有的或者受保护的构造函数,就不能被子类化。

2、静态工厂方法的第二个缺点是,它们与其他的静态方法没有任何区别。

 

例如 Map<String,List<String>> m=new Map<String ,List<String>>();

使用静态工厂方法就可以这样:

publicstatic <K,V>HashMap<K,V>newInstance(){

returnnew HashMap<K,V>();

}

Map<String,List<String>> m=HashMap.newInstance();

 

第二条 处理多个构造函数参数(重点是Builder模式)

静态工厂方法和构造函数有相同的局限性:当构造的对象有多个函数时候,编写都不方便。

最常用的方法是使用多个构造,不同的构造函数使用不同的参数。这种方式是可行的,但是客户端代码会难以编写,并且难以阅读。

另一种方法是JavaBean模式,即对每个属性都提供set和get方法,这样来对属性进行控制。但是该模式的局限性在于该模式不能保证一致性。原因是对象的构造过程被分到了几个不同的调用中,这就有可能会产生不一致状态的对象导致程序的失败。需要付出额外的努力来保持线程安全。

有一种更好的解决方案是Builer模式。看下面的例子:

package test;
/*
* 该类用来演示Builder模式
*/
public class Date1 {
private final int year;
private final int month;
private final int day;

/*
* 静态内部类用于构建外部对象,该类称为构造器
*/
public static class Builder{
private int year;
private int day;
private int month;

public Builder(int year){
this.year=year;

}
/*
* 方法用来设置属性值,返回值类型为该类的对象
*/
public Builder month(int month){
this.month=month;
return this;
}
public Builder day(int day){
this.day=day;
return this;
}
/*
* 通过内部类对象构建外部类对象
*/
public Date1 build(){
return new Date1(this);
}

}
/*
* 外部类构造函数,参数为静态内部类
*/
private Date1(Builder builder){
year=builder.year;
month=builder.month;
day=builder.day;
}
@Override
public String toString(){
return “year:”+year+” month:”+month+” day:”+day;
}
public static void main(String[] args) {
Date1 date=new Date1.Builder(2008).month(8).day(8).build();//这样就能使用一个语句来创建对象,并且能够明确的定义各个属性值
System.out.println(date);
}

}

这种方法唯一的不足就是创建对象的时候需要首先创建构造器,这会影响程序的性能。

第三条:用私有构造器或者枚举类型来实现Singleton属性

有些类不需要被实例化,如一些工具类Math Arrays等。这时候可以将构造器私有化,这样外部就不能构造该类的实例了。注意不要使用抽象类来避免将类实例化,因为子类同样可以实例化。

这样的副作用,它使得类不能被子类化,因为子类构造器必须显示或者隐式的调用父类构造器。

public class UtilityClass{

private UtilityClass(){

throw new IllegalAccessException();

}

}

第四条:用私有构造器或者枚举类型来实现Singleton属性

方法1

public class Elvis{

public static final Elvis instance=new Elvis();

private Elvis(){}

}

有可能会受到反射机制的攻击,解决方式是在构造器构造第二个实例的时候抛出异常

方法2静态工厂

public class Elvis{

private static final Elvis instance=new Elvis();

private Elvis(){}

public static Elvis getInstance(){ return instance;}

}

序列化的时候需要将属性加上transient

方法3:枚举类型

public enum Elvis{

INSTANCE;

。。。。

}

无偿的提供了序列化机制,可以防止多次实例化。最佳方法

第五条:避免创建不需要的对象,消除过期对象的引用

创建String对象的时候用

String s=”abcdefg”; !=String s=new String(“abcdegf”);

不要创建一些不必要的对象,尽量使用已知不会被修改的可变对象。尤其是在一些方法中,尽量重用可重复使用的对象。

需要注意的一点是能使用基本类型就不要使用装箱类型。

要及时的清空过期的对象引用

例子:

Long sum=0l;//应该是long sum=0l;

for(long i=0;i<Integer.MAX_VALUE;i++){

sum+=i;

}

System.out.println(sum);

第六条:避免使用终结方法 尽量避免使用finalizer

在Java中终结方法的缺点在于不能保证被及时的执行。Java语言规范不仅不保证终结方法会及时执行,而且根本就不保证他们会被执行。所以不应该依赖终结方法来更新重要的持久状态。

另外,使用终结方法会有非常严重的性能损失。

执行finalize()方法的线程的优先级低于正常线程的优先级,所以很可能出现finalize方法没被执行,gc又在等待finalize方法。dead lock!

System.gc()和 System.runFinalization()方法只是建议gc执行,但是没有必须要执行的保证!

唯一能确保必须执行的方法是System.runFinalizersOnExit 和Runtime.runFinalizersOnExit 方法。但是已经停用!

用处:

1、如果某个类应该执行清理工作,但是没有做清理工作的时候使用finalize方法清理。

2、jni,因为jni调用的不是java object,所以做有必要的清理工作。

发表评论

电子邮件地址不会被公开。 必填项已用 * 标注

您可以使用这些 HTML 标签和属性: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>