这是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. php 位运算符的实际应用(权限判断)

    关键代码: define("READ", 1); define("WRITE", 2); define("DELETE", 4); defi ...

  2. eclipse maven tools.jar找不到y也就是在这个

    Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:compile (default-compile ...

  3. 【Excle】在重复数据中对日期排序并查询最新的一条记录

    现在存在以下数据: 需要查询出以下数据 姓名       日期 张三       2017-12-14 李四       2017-12-16 在E1中写入以下公式:=IF(D2=MAX(IF($C$ ...

  4. 【Excle数据透视表】如何将价格小于5000的显示为红色“不达标”

    例如下图:需要将价格小于5000的设置为低价格 步骤 单击"价格"列任意单元格→右键→数字格式→自定义→在类型下的文本框写入:[>=5000]G/通用格式;[红色][< ...

  5. Python 多线程和单线程本质应用区别

    先了解下CPU的简单运行原理: 它运行速度非常快,1s内可以运行成千上万次,一个核心可以把1s切分成成千上万个时间片段,这个核心确实同时只能运行一个任务:但是可以将多个任务交替执行,比如上一个时间片段 ...

  6. weex 学习

    相关资料和链接: # 官方网站https://weex.apache.org/cn/ # githubhttps://github.com/apache/incubator-weex # weex环境 ...

  7. PHP 依据IP地址获取所在城市

    有这种需求,须要依据用户的IP地址,定位用户所在的城市. 本文记录性文章,无逻辑性.有这样需求的朋友.能够直接拷贝使用.直接上代码,不需赘述. <? php header('Content-Ty ...

  8. Mybatis_遇到的问题汇总

    1.The setting logImpl is not known 我在参考某个网站学习mybatis时,出现这个错误,后来找到的原因是因为mybatis的版本(3.1.1)太低,换成3.3.1就没 ...

  9. Java Web开发笔记

    问题: 读取资源文件问题 servletContext.getRealPath() servletContext.getResouce().getPath() setvletContext.getRe ...

  10. ubuntu 下使用polipo转换HTTP代理

    ***默认是用Socks5协议的,对于Terminal的get,wget等走http协议的地方是无能为力的,所以需要转换成http代理,加强通用性,这里使用的转换方法是基于Polipo的. sudo ...