小鸡爪读Effective Java记录1:用静态工厂方法代替构造器


// 小鸡爪==菜鸡


第一条:用静态工厂方法代替构造器


what?


静态工厂 方法( static factory method ),它只是一个返回类的实例的静态方法

eg:

Boolean (基本类型boolean的装箱类)的valueOf方法,这个方法将boolean基本类型值转换成一个Boolean对象的引用:

public static Boolean valueOf(boolean b) {
    return (b ? TRUE : FALSE);
}

调用方式:

boolean a = false;
Boolean aBoolean = Boolean.valueOf(a);

注意:

静态工厂方法与设计模式 [ Gamma95]中的工厂方法 (Factory Method) 模式不
同。本条目中所指的静态工厂方法并不直接对应于设计模式 (DesignPatter)中的工厂方法。

open:
对于类而言,为了让客户端获取到他自身的一个实例,除了用最传统的方法提供一个共有的构造器,还可以提供静态工厂方法。

why?


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


构造器:
一个类只能有一个带有指定签名的构造器,通过不同参数类型和参数顺序实现不同突出区别


静态工厂方法:
一个类可以定义多个静态工厂方法,可通过名称突出区别


二、不必要每次调用他们的都创建一个新对象


怕理解不好,先放原文:
建一个新对象。这使得不可变类可以使用预先构建好的实例,或者将构建好的实例缓存起来,进行重复利用,从而避免创建不必要的重复对象。Boolean.valueOf(boolean)方法说明了这项技术:它从来不创建对象。这种方法类似于享元(Flweighl)模式[Gamma95]。如果程序经常请求创建相同的对象,并且创建对象的代价很高、则这项技术可以极大地提升性能。

静态工厂方法能够为重复的调用返回相同对象、这样有助于类总能严格控制在某个时刻哪些实例应该存在。这种类被称作实例受控的美(instance-contolled)。编写实例受控的类有几个原因。实例受控使得类可以确保它是一个Singleton 或者是不可实例化的。它还使得不可变的值类可以确保不会存在两个相等的实例,即当且仅当a–b时,a.equa1s(b) 才为true。这是享元模式[ Gamma95]的基础。枚举(enum)类型 保证了这一点。

再看一下提到的Boolean.valueOf(boolean)方法例子理解比较好:


image.png
image.png
image.png

三、它们可以返回原返回类型的任何子类型的对象


这样我们在选择返回对象的类时就有了更大的灵活性。

这个看不懂,直接放原文,狗头保命,后面看懂再来修改

这种灵活性的一种应用是,API可以返回对象,同时又不会使对象的类变成公有的。以这种方式隐藏实现类会使API变得非常简洁。这项技术适用于基于接口的框架 (interface-basedframework)(详见第20条),因为在这种框架中,接口为静态工厂方法提供了自然返回型。

在Java8之前,接口不能有静态方法,因此按照惯例,接口Type的静态工厂方法被放在一个名为TyPes的不可实例化的伴生类 (详见第4条)中。例如,Java Collections Framework的集合接口有45个工具实现,分别提供了不可修改的集合、同步集合,等等。几乎所有这些实现都通过静态工厂方法在一个不可实例化的类 (java.1ections) 中导出所有返回对象的类都是非公有的。

现在的Colections FrameworkAPI比导出45个独立公有类的那种实现方式要小得多每种便利实现都对应一个类。这不仅仅是指API数量上的减少,也是概念意义上的减少;为了使用这个API,用户必须掌握的概念在数量和难度上都减少了。程序员知道,被返回的对象是由相关的接口精确指定的,所以他们不需要阅读有关的类文档。此外,使用这种静态工厂方法时,甚至要求客户端通过接口来引用被返回的对象,而不是通过它的实现类来引用被返回的对象,这是一种良好的习惯 (详见第64条)
从Java8版本开始,接口中不能包含静态方法的这一限制成为历史,因此一般没有任何理由给接口提供一个不可实例化的伴生类。已经被放在这种类中的许多公有的静态成员应该被放到接口中去。但是要注意,仍然有必要将这些静态方法背后的大部分实现代码单独放进一个包级私有的类中。这是因为在Java8中仍要求接口的所有静态成员都必须是公有的。在Java9中允许接口有私有的静态方法,但是静态域和静态成员类仍然需要是公有的。

四、所返回的对象的类可以随着每次调用而发送变化,这取决静态工厂方法的参数值


eg:

image.png

这里看到使用EnumSet.allOf()拿到所有枚举对象,我们再看进去

image.png

allOf()里面调用了noneOf,我们来看看noneOf()

image.png

我们可以看出:如果它的元素有64个或者更少,就像大多数枚举类型一样,静态工厂方法就会返回一个RegalarBumset实例用单个1ong进行支持;如果枚举类型有65个或者更多元素,工厂就返回可JumboEnumset
实例,用一个1ong数组进行支持

image.png image.png

五:方法返回的对象所属的类,在编写包含该静态工厂方法的类时可以不存在


这个也直接放原文,狗头保命

这种灵活的静态工厂方法构成了服务提供者框架 (ServiceProviderFramework) 的基础,例如JDBOJava数据库连接API。服务提供者框架是指这样一个系统:多个服务提供者实现一个服务,系统为服务提供者的客户端提供多个实现,并把它们从多个实现中解耦出来。
服务提供者框架中有三个重要的组件:服务接口 (Service Interface),这是提供者实现的;提供者注册API(Provider Registration API),这是提供者用来注册实现的;服务访问API (ServiceAccessAPI)这是客户端用来获取服务的实例。服务访问API是客户端用来指定某种选择实现的条件。如果没有这样的规定,API就会返回默认实现的一个实例,或者允许客户端遍历所有可用的实现。服务访问API是“灵活的静态工厂”,它构成了服务提供者框架的基础。
服务提供者框架的第四个组件服务提供者接口 (ServiceProviderInterface) 是可选的它表示产生服务接口之实例的工厂对象。如果没有服务提供者接口,实现就通过反射方式进行实例化(详见第65条)。对于JDBC来说,Connection就是其服务接口的一部分DriverManaqer.reqisterDriver 是提供者注册API,DriverManaqer.是服务访间API,Driver是服务提供者接口。
服务提供者框架模式有着无数种变体。例如,服务访间API可以返回比提供者需要的更丰富的服务接口。这就是桥接 (Bridge)模式[Gamma95]。依赖注人框架(详见第5条可以被看作是一个强大的服务提供者。从Java6版本开始,Java平台就提供了一个通用的服务提供者框架java.uti1iceLoader,因此你不需要(一般来说也不应该)再自己编写了(详见第59条)。JDBC不用ServiceLoader,因为前者出现得比后者早。

Other


一、静态工厂方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化


eg:
要想将 Collections Framework中的任何便利的实现类子类化,这是不可能的。但是这样也许会因祸得福,因为它鼓励程序员使用复合 (composition),而不是继承,这正是不可变类型所需要的。


二、静态工厂方法的第二个缺点在于,程序员很难发现它们


下面是静态工厂方法的一些惯用名称。这里只列出了其中的一小部分:

image.png image.png

end


书本原话:
简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处,静态工厂经常更加合适,因此切忌第一反应就是提供公有的构造,而不先考虑静态工厂。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值