首页 > 学院 > 开发设计 > 正文

[Effective Java]第二章 创建和销毁对象

2019-11-14 22:44:21
字体:
来源:转载
供稿:网友
[Effective java]第二章 创建和销毁对象声明:原创作品,转载时请注明文章来自SAP师太技术博客( 博/客/园www.cnblogs.com):www.VEVb.com/jiangzhengjun,并以超链接形式标明文章原始出处,否则将追究法律责任!原文链接:http://www.VEVb.com/jiangzhengjun/p/4254987.html 第一章 前言

略...

第二章 创建和销毁对象 1、 考虑用静态工厂方法代替构造器

创建对象方法:一是最常用的公有构造器,二是静态工厂方法。下面是一个Boolean的简单示例:

public static Boolean valueOf(boolean b) {

return (b ? Boolean.TRUE : Boolean.FALSE);

}

l静态工厂方法与构造器不同的第一大优势在于,它们有名称。

作用不同的公有构造器只能通过参数来区别(因为一个类只有一个带有指定签名的构造器,所以多个构造器只能使用不同的参数列表来区分),如果使用静态的工厂方法,则方法名会很清楚地表达方法的作用。

l静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候都创建一个新对象。

不可变类完全可以使用预先构建好的实例,而不必每次使用时都创建一个对象。另外,将构建好的实例缓存起来重复使用,从而避免创建不必要的重复对象。Boolean.valueOf(boolean)方法就使用了这项技术——它从来不创建对象。

l静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类型的对象。

这样我们在选择返回对象的类时就有了更大的灵活性。这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成公有的,比如我们完全可以先定义一个产品接口类,然后采用私有的内部类去实现这个接口,静态工厂方法返回这个类的实例,这样就隐藏了具体的实现。另外,使用静态工厂方时,要求客户端通过接口来引用被返回的对象,而不是通过它的实现类来引用被返回的对象,这是一种良好的编程习惯。

公有静态工厂方法所返回的对象的类不仅可以是PRivate,而且通过静态工厂方法的参数,还可以随着每次的返回不同的类的实例,只要是已声明返回类型的子类型。这样的好处是,可以在以后的版本中删除这个类重新实现也不会影响到已使用的客户。

静态工厂方法返回的对象所属的类,在编写包含该静态工厂方法的类时可能不必存在。这种灵活的静态工厂方法构成了服务提供者框架的基础,例如JDBC API。服务提供者框架是指这样一个系统:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把他们从多个实现中解耦出来。

服务提供者框架有三个重要组件:服务接口(Service Interface),这是提供者实现的;提供者注册API(Provider Registration API),这是系统用来注册实现,让客户端访问它们的;服务访问API(Service access API),是客户端用来获取服务的实例的方法接口。服务访问API一般允许但是不要求客户端指定某种选择提供者的条件。如果没有这样的规定,API就会返默认实现的一个实例。服务访问API是“灵活的静态工厂”,它构成了服务提供者框架的基础。

服务提供者框架的第四个组件是可选的:服务提供者接口(Service Provider Interface)(即工厂方法模式中的工厂接口),这些提供者负责创建其服务实现的实例。如果没有服务提供者接口,实现就按照类名称注册,并通过反射方式进行实例化。对于JDBC来说,Connection就是它的服务接口,DriverManager.registerDriver是提供者注册API,DriverManager.getConnection是服务访问API,Driver就是服务提供者接口。

下面看看这四个组件的应用:

//服务接口,就是我们的业务接口。(相当于Connection接口,由Sun提供)

publicinterfaceService {

// ...

}

//服务提供都接口,即业务工厂接口。(相当于Driver接口,由第三方厂家实现)

publicinterfaceProvider{

Service newService();

}

//服务提供者注册与服务提供者接口(好比DriverManager)

publicclassServices{

privateServices() {}

//服务名与服务映射,即注册容器

privatestaticfinalMapproviders=newConcurrentHashMap();

publicstaticfinalStringDEFAULT_PROVIDER_NAME= "def";

//服务提供者注册API,即注册工厂实现,相当于DriverManager.registerDriver

publicstaticvoidregisterDefaultProvider(Provider p) {

registerProvider(DEFAULT_PROVIDER_NAME, p);

}

publicstaticvoidregisterProvider(String name, Provider p) {

providers.put(name, p);

}

//服务访问API,向外界提供业务实现,相当于DriverManager.getConnection

publicstaticService newInstance() {

returnnewInstance(DEFAULT_PROVIDER_NAME);

}

publicstaticService newInstance(String name) {

Provider p = (Provider)providers.get(name);

if(p ==null) {

thrownewIllegalArgumentException(

"NO provider registered with name:" + name);

}

returnp.newService();

}

}

静态工厂方法的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。比如要创建一个参数化的HashMap,我们需要如下做:

Map<String,List<String>>m=newHashMap<String, List<String>>();

这么长的类型参数实在是不太好,而且随着类型参数变得越来越长,也越来越复杂。但如果有了静态工厂方法,编译器就可以替你推导出类型,new时不需要提供参数类型。例如,假设HashMap提供了这个静态工厂:

publicstatic<k,v> HashMap<k,v>newInstance(){

returnnewHashMap<k,v>();

}

那么你就可以使用以下简洁的代码来代替上面这段繁琐的声明:

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

但可惜的是,到现在发行的版本1.6止还未加入,不过我们可以把这些方法放在自己的工具类中。

静态工厂方法的一些惯用名称:

valueOf——不太严格地讲,该方返回的实例与它的参数具有相同的值。这样的静态工厂方法实际上是类型转换方法。

of——valueOf的一种更为简洁的替换,在EnumSet中使用并流行起来。

getInstance——返回的实例是通过方法的参数来描述的,但是不能够说与参数具有同样的值。对于Singleton来说,该方法没有参数,并返回唯一值。

newInstance——像getInstance一样,但newInstance能够确保返回每个实例都与把有其他实例不同。

getType——像getInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。

newType——像newInstance一样,但是在工厂方法处于不同的类中的时候使用。Type表示工厂方法所返回的对象类型。

2、 遇到多个构造器参数时要考虑构造器

如果实例化时需要多个参数时,且这些参数中只有少数几个是必须的,而很多是可选的,这时我们一般考虑使用构造器的方式,而不是使用静态工厂方法。

对于此情况,我们可以使用重叠构造器模式——你提供一个只有必要参数的构造器,第二构造器有一个可选参数,第三个有两个可选参数,依此类推,最后一个构造器包含所有可选参数。

publicclassNutritionFacts {

privatefinalintservingSize; //必选参数

privatefinalintservings; //必选参数

privatefinalintcalories; //可选参数

privatefinalintfat; //可选参数

privatefinalintsodium; //可选参数

privatefinalintcarbohydrate; //可选参数

//第一个构造器带上所有必选参数

publicNutritionFacts(intservingSize,intservings) {

//调用另一个构造器

this(servingSize, servings, 0);//第三个参数为默认值

}

//第二个构造器在第一个构造器的基础上加上一个可先参数

publicNutritionFacts(intservingSize,intservings,

intcalories) {

//第四个参数为默认值

this(servingSize, servings, calories, 0);

}

Word-spacing: 0px; text-transform: none; margin: 0cm 0cm 0pt; letter-spacing: normal; line-height: normal; text-indent: 0px; -webkit-text-size-adjust: auto; -webkit-text-stroke-wid

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表