Effective Item - 考虑用静态工厂方法代替构造器我们有两种常见的方法获得一个类的实例:

  • 公有的构造器
  • 提供静态工厂方法(static factory method)

相对公有的构造器,静态工厂方法有以下几大优势。

优势1.静态工厂方法的名称,因此比构造器更准确地描述返回的实例。
比如BigInteger.probablePrime方法:

  1. public static BigInteger probablePrime(int bitLength, Random rnd) {
  2. if (bitLength < 2)
  3. throw new ArithmeticException("bitLength < 2");
  4. // The cutoff of 95 was chosen empirically for best performance
  5. return (bitLength < SMALL_PRIME_THRESHOLD ?
  6. smallPrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd) :
  7. largePrime(bitLength, DEFAULT_PRIME_CERTAINTY, rnd));
  8. }

顺便也贴出其调用的largePrime方法:

  1. private static BigInteger largePrime(int bitLength, int certainty, Random rnd) {
  2. BigInteger p;
  3. p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
  4. p.mag[p.mag.length-1] &= 0xfffffffe;
  5. // Use a sieve length likely to contain the next prime number
  6. int searchLen = (bitLength / 20) * 64;
  7. BitSieve searchSieve = new BitSieve(p, searchLen);
  8. BigInteger candidate = searchSieve.retrieve(p, certainty, rnd);
  9. while ((candidate == null) || (candidate.bitLength() != bitLength)) {
  10. p = p.add(BigInteger.valueOf(2*searchLen));
  11. if (p.bitLength() != bitLength)
  12. p = new BigInteger(bitLength, rnd).setBit(bitLength-1);
  13. p.mag[p.mag.length-1] &= 0xfffffffe;
  14. searchSieve = new BitSieve(p, searchLen);
  15. candidate = searchSieve.retrieve(p, certainty, rnd);
  16. }
  17. return candidate;
  18. }

虽然smallPrime和largePrime最后都是通过公有构造器返回实例。
但是如果仅仅用构造器重载表达这个实例的特征,这很难让人记住什么时候应该调用什么构造器。
而提供一个名称去描述实例更为直观。

优势2.静态工厂方法不必每次都创建一个新的对象,我们可以对实例进行控制。
这样我们就能将创建好的实例缓存起来重复利用,尤其是在创建对象的代价较高的情况下。
比如Boolean.valueOf:

  1. public static final Boolean TRUE = new Boolean(true);
  2. public static final Boolean FALSE = new Boolean(false);
  3. public static Boolean valueOf(boolean b) {
  4. return (b ? TRUE : FALSE);
  5. }

优势3.静态工厂方法可以返回原返回类型的子类型对象。
这一点能体现静态工厂方法的灵活性,
以EnumSet为例:

  1. /**
  2. * Creates an empty enum set with the specified element type.
  3. *
  4. * @param elementType the class object of the element type for this enum
  5. * set
  6. * @throws NullPointerException if <tt>elementType</tt> is null
  7. */
  8. public static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> elementType) {
  9. Enum[] universe = getUniverse(elementType);
  10. if (universe == null)
  11. throw new ClassCastException(elementType + " not an enum");
  12. if (universe.length <= 64)
  13. return new RegularEnumSet<>(elementType, universe);
  14. else
  15. return new JumboEnumSet<>(elementType, universe);
  16. }

而RegularEnumSet和JumboEnumSet为EnumSet的子类,并且都没有提供公有构造器。

优势4.静态工厂方法创建参数化(泛型)实例的时候更加简洁。
举个例子:

  1. public static <K, V> HashMap<K, V> newInstance() {
  2. return new HashMap<K, V>();
  3. }

这样一来创建实例时就可以:

  1. Map<String,List<Integer>> n = newInstance();

而不是

  1. Map<String,List<Integer>> m = new HashMap<String,List<Integer>>();

从Java7开始这一点变得没有意义,事实上Josh Bloch也在书上提到了这点——Java以后会在构造器和方法调用中执行这种类型推导。

说说静态工厂方法的缺点。

  • 类如果不含公有或者受保护的构造器就不能被子类化。
    所以上面说的静态工厂方法可以返回原返回类型的子类型对象。并不完全正确。
    虽然我们可以通过复合方式(composition)解决这一问题,但这样两个类就不是is-a关系了。
  • 静态工厂方法的本质还是静态方法,他没有一个特别的标准。
    我们无法在API文档中把一个静态工厂方法特别标识出来(也许可以加个标准annotation?)。
    当我要从API中找一个方法去实例化一个类时,相对构造器而言还是不够直观。
    虽然没有特个特别的标准,但我们也可以用标准的命名来弥补一点点。
    比如valueOf,getInstance,newInstance,newType等...

Java - 用静态工厂方法代替构造器的更多相关文章

  1. Effective Java —— 用静态工厂方法代替构造器

    本文参考 本篇文章参考自<Effective Java>第三版第一条"Consider static factory methods instead of constructor ...

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

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

  3. Effective Java 读书笔记(一):使用静态工厂方法代替构造器

    这是Effective Java第2章提出的第一条建议: 考虑用静态工厂方法代替构造器 此处的静态工厂方法并不是设计模式,主要指static修饰的静态方法,关于static的说明可以参考之前的博文&l ...

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

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

  5. 高效JAVA之用静态工厂方法代替构造器

    程序员这行干的久了,总会染上一些恶习,我就染上一个让人深恶痛绝,自己却津津乐道的习惯,还不想改的那种,它可以叫做强迫症,也可以叫做洁癖.那就是我不允许我的IDEA出现一点点警告,什么黄色背景,绿色波浪 ...

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

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

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

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

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

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

  9. 静态工厂方法VS构造器

    我之前已经介绍过关于构建者模式(Builder Pattern)的一些内容,它是一种很有用的模式用于实例化包含几个属性(可选的)的类,带来的好处是更容易读.写及维护客户端代码.今天,我将继续介绍对象创 ...

随机推荐

  1. ClamAV学习【2】——clamscan入口函数浏览

    就简单给代码加上些注释,方便理解.第一次浏览,应该会有不正确的理解.后面会继续学习修改. 文件:clamscan\clamscan.c 代码如下: nt main(int argc, char **a ...

  2. 弹性盒子模型display:flex

    1.div上下左右居中 <!DOCTYPE html> <html lang="en"> <head> <meta charset=&qu ...

  3. 关于JNDI技术链接操作数据库-2019.1.10

    Java 命名与目录接口(Java Naming and Directory Interface) ==需要使用的包为java.sql&javax.naming包==Mysql 步骤: 1.配 ...

  4. 如何在WS系统的DOS命令台打印JAVA_HOME变量

    echo %JAVA_HOME% 查看环境变量 path 新增临时环境变量 path D:\test;%path% 注意是反斜杆 cls 清空 F7 显示历史CMD指令

  5. Prufer codes与Generalized Cayley's Formula学习笔记

    \(Prufer\)序列 在一棵\(n\)个点带标号无根树里,我们定义这棵树的\(Prufer\)序列为执行以下操作后得到的序列 1.若当前树中只剩下两个节点,退出,否则执行\(2\) 2.令\(u\ ...

  6. Qt5学习笔记(消息过滤器)

    T06EventFilter.pro HEADERS += \ MyWidget.h SOURCES += \ MyWidget.cpp QT += widgets gui MyWidget.h #i ...

  7. SpringBoot整合MyBatis及Thymeleaf

    http://www.cnblogs.com/ludashi/archive/2017/05/08/6669133.html 上篇博客我们聊了<JavaEE开发之SpringBoot工程的创建. ...

  8. React 初识

    React We built React to solve one problem: building large applications with data that changes over t ...

  9. 基于.Net平台C#的微信网页版API

    git上有很多类似的项目,但大多都是python和js的,为了便于.Net windows平台的使用,我重构了一个.Net版本的,已整理开源 https://github.com/leestar54/ ...

  10. Mac下配置git环境和客户端SourceTree+Git常用命令大全(Mac 10.12)

    前言: 如果不想折腾,直接下载GitHub桌面端,高度集成git,不需要学习git的任何命令. https://desktop.github.com/ 一.配置git环境 1.上官网https://g ...