Effective Java阅读笔记——创建和销毁对象(一)
类通常提供一个公有的构造器方法,以此来让客户端可以获取自己(类)的一个实例。但是在创建对象时,应该首先考虑利用静态工厂方法代替构造器来返回一个实例。利用静态工厂方法而不是公有的构造器有几个优势:
静态工厂方法有(不同的)名字
构造器方法都有相同的名字,就是类的名字。区分不同构造器的方法是通过观察方法的签名确定的。方法的签名包括方法名,方法参数的类型,数目以及顺序,方法的返回类型不是方法签名的一部分。无法通过构造器方法名区分不同的构造器,只能通过参数的不同区分,这种情况下(特殊情况下有可能参数类型和数目相同但是顺序不同,此时也是不同的构造器方法)往往不能明显的区分不同的构造器方法的作用,不太直观造成难以理解的情况。而静态工厂方法可以使用不同的名字,直观易于理解。
例如:BigInteger(int,int,Random)构造器方法可能返回素数,可以使用一个静态工厂方法来返回一个实例:BigInteger.probablePrime,这样更加清楚直观。
静态工厂方法不必在每次调用时都创建一个新对象
很多情况下,每次调用工厂方法都需要同样的实例,因此可以使用预先构建好的实例或者将构建好的实例缓存起来,进行重复利用。这样就不用每次重新构建新的实例,节省空间和时间。类似于String,基本类型的包装类,BigInteger,BigDecimal这样的不可变类,一旦创建这些类的实例,实例就不会改变。这种不可变类就适用于这种情况。
静态工厂方法可以返回原返回类型的任何子类型的对象
利用构造器方法构建实例(使用new操作符)时,只能返回类本身的实例。而使用静态工厂方法可以返回子类型的对象,大大增加了灵活性。
举例来说,java 1.5中引入的java.util.EnumSet类没有公有构造器,只有静态工厂方法。EnumSet有两种实现类,分别是RegalarEnumSet和JumoboEnumSet两种。具体返回哪种实现类取决于静态工厂方法的参数,当底层枚举类型元素有64或者更少时使用RegalarEnumSet实现,大于等于65时使用JumoboEnumSet实现。这两种实现都不需要是公有(public)类,从而隐藏了具体的实现。可以继续增加或者减少实现类的数目,从而灵活性大大增加,类用户不必再关注于实现细节。
除了上面所举的子类外,还有一种类似的面向接口的编程思想。静态工厂方法返回类型可以限定为接口,具体的返回对象只要满足接口就可以。具体而已,返回对象可以之要求满足List<T>,实际的返回类型可以是ArrayList<T>,LinkedList<T>等实现了List接口的类型即可。用户只需要关注于接口。
静态工厂方法返回的对象所属的类在编写静态工厂方法时可以不存在,这种灵活性是服务提供者框架(service provider framework)的基础。JDBC是一个服务提供者框架。
静态工厂方法在创建参数化类型实例的时候,可以使代码更简洁(旧版本)
在创建参数化类(范型类)的实例时,在低版本的情况下,即使类型参数很明显,也必须指明类型。例如:
Map<String,List<String>> m=new HashMap<String,List<String>>();
在使用静态工厂方法的情况下,编译器可以直接进行类型推导,从而省去指明类型参数的情况。静态工厂方法为:
public static <K,V> HashMap<K,V> newInstance(){ return new HashMap<K,V>(); }
ps:高版本的java中在直接调用构造器是已经不需要指明类型参数了,这里只是做个读书笔记!!!同时明确静态工厂方法这种情况下可以进行类型推导。
使用静态工厂方法相对于直接调用构造器方法也有缺点:
缺点一:类如果不含有公有的或者受保护的构造器,就没法被继承。
缺点二:静态工厂方法只是不同的静态方法,不是特殊的机制。因此有可能造成用户的困惑,有可能使用户产生迷惑,不清楚类如何实例化。但是有几个通用的规范可以帮助用户,静态工厂方法有一些惯用名称,如valueOf,of,getInstance,newInstance,getType,newType。
总而言之,构造器和静态工厂方法都各有好处。