这是Effective Java第2章提出的第一条建议:

考虑用静态工厂方法代替构造器

此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文《java中final与static的使用场景总结》

什么是静态工厂方法?

可以参考书中的例子(摘自JDK1.7 java.lang.Boolean)

public final class Boolean implements java.io.Serializable,
Comparable<Boolean> { public static final Boolean TRUE = new Boolean(true);
public static final Boolean FALSE = new Boolean(false); public static Boolean valueOf(boolean b) {
return (b ? TRUE : FALSE);
}
}

如果需要获取一个Boolean对象,常规的方法是new Boolean(true),但是也可以如上图所示Boolean.valueOf(true),这便是静态工厂方法。

静态工厂方法的优势

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

使用构造函数构造对象时,我们需要通过文档仔细比对传递什么样的参数能够构造什么样的对象。但是静态工厂方法可以使用不同的方法名字使得其构造的对象更加明晰。我们完全可以通过方法名明白构造了什么样的对象。

例如下面的例子(摘自JDK1.7 java.math.BigInteger)

public class BigInteger extends Number implements Comparable<BigInteger> {
/**
* Returns a positive BigInteger that is probably prime, with the
* specified bitLength. The probability that a BigInteger returned
* by this method is composite does not exceed 2<sup>-100</sup>.
*/
public static BigInteger probablePrime(int bitLength, Random rnd) {
// XXX
}
}

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

我们调用静态工厂方法返回的可能是缓存的一个对象,而不是新对象。可以进行重复利用,从而避免创建不必要的重复对象。 
如果程序经常请求创建相同的对象,并且创建的代价很高,则静态工厂方法可以极大地提升性能。 
前面提到的Boolean.valueOf(boolean)便说明了这项技术。

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

我们在选择返回对象的类时有了更大的灵活性。参见java.util.EnumSet,其本身被abstract修饰,无法直接调用其构造函数。但可以调用其静态方法noneOf来创建对象,并且根据参数返回合适的对象。

public abstract class EnumSet<E extends Enum<E>> extends AbstractSet<E>
implements Cloneable, java.io.Serializable { EnumSet(Class<E>elementType, Enum[] universe) {
}
//RegularEnumSet与JumboEnumSet均为EnumSet的子类
public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
if (universe.length <= 64)
return new RegularEnumSet<>(elementType, universe);
else
return new JumboEnumSet<>(elementType, universe);
}
}

第四大优势在于,创建参数化类型实例的时候,可以使代码变得更加简洁。

例如对于HashMap的实例化:

//常规实例化方式
Map<String, List<String>> m =
new HashMap<String, List<String>>(); public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
//使用静态工厂方法实例化,简化繁琐的声明
Map<String, List<String>> m = HashMap.newInstance();

静态工厂方法的劣势

类如果不含公有的或受保护的构造器,就不能被实例化。

如果我们在类中将构造函数设为private,只提供静态工厂方法来构建对象,那么我们将不能通过继承扩展该类。 
但是这也会鼓励我们使用复合而不是继承来扩展类。

它们与其他的静态方法实际上没有任何区别。

在API文档中,构建对象的静态工厂方法并没有像构造器那样明确标识出来,不能和其他静态方法很方便地区分开来。 
如果类中只提供静态工厂方法而不是构造器,要想查明如何实例化一个类将会变得困难。 
我们可以通过遵循静态工厂方法的命名规范来弥补这一劣势:

  • valueOf - 返回的实例与它的参数具有相同的值,一般作为类型转换使用,例如Boolean.valueOf(boolean)
  • of - valueOf的更为简洁的替代。
  • getInstance - 返回的实例通过方法的参数来描述,但不能说与参数具有同样的值。对于Singleton来说,使用无参getInstance,返回唯一的实例。
  • newInstance - 像getInstance一样,但其能够确保每次都返回新的对象。
  • getType - 像getInstance一样,但此方法返回的对象是另一个不同的类。
  • newType - 像getType一样,但每次返回一个新对象。

总而言之,静态工厂方法和公有构造器具有各自的用处,但静态工厂方法通常更加合适,所以我们应该优先考虑静态工厂方法。

Effective Java 读书笔记(一):使用静态工厂方法代替构造器的更多相关文章

  1. 【读书笔记 - Effective Java】01. 考虑用静态工厂方法代替构造器

    获取类的实例有两种方法: 1. 提供一个公有的构造器(最常用). 2. 提供一个公有的静态工厂方法(static factory method). // 静态工厂方法示例 public static ...

  2. effective java 3th item1:考虑静态工厂方法代替构造器

    传统的方式获取一个类的实例,是通过提供一个 public 构造器.这里有技巧,每一个程序员应该记住.一个类可以对外提供一个 public 的 静态工厂方法 ,该方法只是一个朴素的静态方法,不需要有太多 ...

  3. 改善JAVA代码01:考虑静态工厂方法代替构造器

    前言 系列文章:[传送门]   每次开始新的一本书,我都会很开心.新书新心情. 正文 静态工厂方法代替构造器 说起这个,好多可以念叨的.做了一年多的项目,慢慢也有感触. 说起构造器 大家很明白,构造器 ...

  4. 【Effective Java读书笔记】创建和销毁对象(一):考虑使用静态工厂方法代替构造器

    类可以提供一个静态方法,返回类的一个静态实例,如Boolean包装类的一个获取实例的静态方法 public static Boolean valueOf(boolean b) { return (b ...

  5. Effective java读书札记第一条之 考虑用静态工厂方法取代构造器

    对于类而言,为了让client获取它自身的一个实例,最经常使用的方法就是提供一个共同拥有的构造器. 另一种放你发,也应该子每一个程序猿的工具箱中占有一席之地.类能够提供一个共同拥有的静态 工厂方法.它 ...

  6. Effective java读书笔记

    2015年进步很小,看的书也不是很多,感觉自己都要废了,2016是沉淀的一年,在这一年中要不断学习.看书,努力提升自己 计在16年要看12本书,主要涉及java基础.Spring研究.java并发.J ...

  7. Effective Java 读书笔记之一 创建和销毁对象

    一.考虑用静态工厂方法代替构造器 这里的静态工厂方法是指类中使用public static 修饰的方法,和设计模式的工厂方法模式没有任何关系.相对于使用共有的构造器来创建对象,静态工厂方法有几大优势: ...

  8. Effective java 读书笔记(2)

    第四条:通过私有构造器强化不可实例化的能力 有时可能需要编写只包含静态方法和静态域的类,这样的工具类不希望被实例化,因为实例化对它来说没有意义. 然而,在缺少显式构造器的情况下,系统会自动提供一个缺省 ...

  9. Effective Java 读书笔记

    创建和销毁对象 >考虑用静态工厂方法替代构造器. 优点: ●优势在于有名称. ●不必再每次调用他们的时候都创建一个新的对象. ●可以返回原返回类型的任何子类型的对象. ●在创建参数化类型实例的时 ...

随机推荐

  1. UVA 111 (复习dp, 14.07.09)

     History Grading  Background Many problems in Computer Science involve maximizing some measure accor ...

  2. kali渗透综合靶机(一)--Lazysysadmin靶机

    kali渗透综合靶机(一)--Lazysysadmin靶机 Lazysysadmin靶机百度云下载链接:https://pan.baidu.com/s/1pTg38wf3oWQlKNUaT-s7qQ提 ...

  3. nginx静态文件缓存

    open_file_cache max=65535 inactive=30s; open_file_cache 打开缓存的同时也指定了缓存最大数目,以及缓存的时间 open_file_cache_va ...

  4. php装饰器

    <?php /* * 用一个类来装饰另一个类,动态的给一个对象增加一些额外功能,这些功能一般是在这个对象调用方法前或方法后 * 比如我们要给User类增加一个登陆日志的功能 */ // 抽象构件 ...

  5. vue-router $route

    1.$route 除了 $route.params 外,$route 对象还提供了其它有用的信息,例如,$route.query (如果 URL 中有查询参数).$route.hash 等等

  6. 基于Django进行简单的微信开发

    代码地址如下:http://www.demodashi.com/demo/11756.html 一.微信公众号的准备: 1. 注册 访问地址:https://mp.weixin.qq.com/ 按照提 ...

  7. Eclipse.ini参数设置

    最近Eclipse不知道是由于项目过多还是其他原因导致Eclipse进程容易卡死,一卡死Workspace保存出错,项目就全都不见了,又得重新导入...鉴于此原因,自己也上网查询了相关资料,现整理如下 ...

  8. XCode5无法设置Deployment Target的解决办法

    今天使用XCode5创建新项目的时候发现无法修改Deployment Target,只能选择iOS7,谷歌了一下找到了答案,在这里分享给大家:) 这是由于XCode5默认会选择在64位的环境下运行,在 ...

  9. C++井字棋游戏,DOS界面版

    据说有一个能保证不败的算法.明天看看先再写个PVC版的. 正题.今天无聊写了个井字棋游戏,顺便逐渐让自己习惯良好的代码风格,放上来给新手学习学习. jzq2.cpp /* N字棋游戏PVP版,DOS版 ...

  10. android开发系列之aidl

    aidl在android开发中的主要作用就是跨进程通讯来着,说到进程相比很多人都是非常熟悉了,但是为什么会有跨进程通讯这个概念呢?原来在android系统中,有这么一套安全机制,为了各个Apk数据的独 ...