记得研一刚进实验室的时候,看见师兄手里拿着一本书《Effective Java》,心理想着,这微信大佬(师兄已经被微信录取)看的书,肯定很有用,所以自己也买来一本喵喵。结果可想而知,看不懂。。。。现在自己再次拿起来看,发现有点意思了。所以把它分享出来。
书获取方式---->>>1.序
对于一个类,要获取它的一个实例,通常的做法是提供一个公用的构造函数,然而还有另一种方法,我们称之为静态工厂方法,实质上也就是一个简单的静态方法,它返回一个类的实例。其实,静态工厂方法获取对象实例,我们并不陌生,来看两个例子。
构造方法创建对象
在Java中,创建对象常用的方法是通过公有构造方法创建;
举个例子:如下,是Boolean类的一个构造方法,以及通过该构造方法创建一个Boolean对象;
public Boolean(String s) {
this(toBoolean(s));
}
Boolean bTrue = new Boolean("true");
静态工厂方法创建对象
其实,创建对象还有另外一种方法,通过公有静态工厂方法来创建对象,不过这种方法往往容易被程序员忽略;
举个例子,如下是Boolean类的valueOf方法,以及通过该静态工厂方法返回的Boolean实例,注意,这里并没有创建Boolean实例对象,而是返回事先创建好的Boolean对象;
public static Boolean valueOf(String s) {
return toBoolean(s) ? TRUE : FALSE;
}
Boolean bTrue = Boolean.valueOf("true");
2.优势
2.1.静态工厂方法与构造器不同的第一大优势在于,它们有名称 (增强了代码的可读性)
//使用构造器方法获取到一个素数
BigInteger prime = new BigInteger(int, int ,Random);
//使用静态工厂方法
BigInteger prime = BigInteger.probablePrime(int, Random);
详细示例
假设我们需要写一个产生随即数的类RandomIntGenerator,该类有两个成员属性:最小值min和最大值max,
假设我们的需求是需要创建三种类型的RandomIntGenerator对象,
1、大于min,小于max;
2、大于min 小于Integer.MAX_VALUE;
3、大于Integer.MIN_VALUE 小于max
如果我们不使用静态工厂方法,代码一般如下设计:
class RandomIntGenerator
{
/**
* 最小值
*/
private int min = Integer.MIN_VALUE;
/**
* 最大值
*/
private int max = Integer.MAX_VALUE;
/**
* 大于min 小于max
* @param min
* @param max
*/
public RandomIntGenerator(int min, int max)
{
this.min = min;
this.max = max;
}
/**
* 大于min 小于Integer.MAX_VALUE
*/
public RandomIntGenerator(int min)
{
this.min = min;
}
// 报错:Duplicate method RandomIntGenerator(int) in type RandomIntGenerator
// /**
// * 大于Integer.MIN_VALUE 小于max
// */
// public RandomIntGenerator(int max)
// {
// this.max = max;
// }
}
观察以上代码,我们发现,以上代码不仅可读性差(new RandomIntGenerator(1, 10)与new RandomIntGenerator(10),不查文档,不看注释很难知道其创建的对象的具体含义),而且在设计最后一个构造方法的时候,还报错,因为已经存在一个参数一致的工作方法了,提示重复定义;
那么假设我们使用静态工厂方法会怎样呢,如下所示:
class RandomIntGenerator
{
/**
* 最小值
*/
private int min = Integer.MIN_VALUE;
/**
* 最大值
*/
private int max = Integer.MAX_VALUE;
/**
* 大于min 小于max
* @param min
* @param max
*/
public RandomIntGenerator(int min, int max)
{
this.min = min;
this.max = max;
}
/**
* 大于min 小于max
* @param min
* @param max
*/
public static RandomIntGenerator between(int min, int max)
{
return new RandomIntGenerator(min, max);
}
/**
* 大于min 小于Integer.MAX_VALUE
*/
public static RandomIntGenerator biggerThan(int min)
{
return new RandomIntGenerator(min, Integer.MAX_VALUE);
}
/**
* 大于Integer.MIN_VALUE 小于max
*/
public static RandomIntGenerator smallerThan(int max)
{
return new RandomIntGenerator(Integer.MIN_VALUE, max);
}
}
成功满足需求:创建三种类型的RandomIntGenerator对象,而且创建对象的时候,代码可读性比使用构造方法强;
2.2.不必在每次调用它们的时候都创建一个新对象(提升性能)
通过构造方法创建对象的时候,每次都要new一个新对象。而通过静态方法,可以选择每次采用单例模式来复用对象,这对于一个性能要求高的系统,可以显著提高运行速度。
详细解析:2.3.静态工厂方法可以返回原返回类型的任何子类型对象(增大程序灵活性,减少API数量)
这样使我们在选择返回对象的类时就有了更大的灵活性。
class Father {
private Father() {
}
public static Father newInstance(String type) {
if (type.equals("ChildA")) { // 根据类型判断返回那个子类对象
return new ChildA();
} else {
return new ChildB();
}
}
public void getName() {
System.out.println("My name is father");
}
private static class ChildA extends Father {
public void getName() {
System.out.println("My name is child A");
}
}
private static class ChildB extends Father {
public void getName() {
System.out.println("My name is child B");
}
}
}
public class Test {
public static void main(String[] args) {
Father c1 = Father.newInstance("ChildA");
c1.getName();
Father c2 = Father.newInstance("ChildB");
c2.getName();
}
}
2.4.可以使代码变得更加简洁(具体类型取决于传入的参数)
2.4.1 示例1
package tmp;
class MyMap<K,V> {
/**
*
*/
public MyMap()
{
}
public static <K,V> MyMap<K,V> newInstance(){
return new MyMap<K, V>();
}
}
public class Main
{
public static void main(String[] args)
{
MyMap<String, String> map1 = new MyMap<String, String>();
//更加简洁,不需要重复指明类型参数,可以自行推导出来
MyMap<String, String> map2 = MyMap.newInstance();
}
}
但实际上,现在Java最新的版本已经构造函数已经不需要补充参数了。
HashMap<String,List<String>> map = new HashMap<>();
2.4.2 示例2
//传统写法Map<String, String> map =new HashMa<String,String>();
//用guava写法Map<String, String> map = Maps.newHashMap();
3.缺点
3.1.类如果不含有公有的类或者受保护的构造器,就不能被子类化
如下类,不能被其它类继承;
class MyMap<K,V> {
/**
*
*/
private MyMap()
{
}
public static <K,V> MyMap<K,V> newInstance(){
return new MyMap<K, V>();
}
}
3.2.它们与其他的静态方法实际上没什么区别,因此我们约定了一些静态工厂方法的常用名称
- 1.valueOf —— from 和 to 更为详细的替代 方式,例如:
BigInteger prime = BigInteger.valueOf(Integer.MAX_VALUE);
- 2.of —— 聚合方法,接受多个参数并返回该类型的实例,并把他们合并在一起,例如:
Set<Rank> faceCards = EnumSet.of(JACK, QUEEN, KING);
- 3.instance 或 getinstance —— 返回一个由其参数 (如果有的话) 描述的实例,但不能说它具有相同的值,例如:
StackWalker luke = StackWalker.getInstance(options);
- 4.create 或 newInstance —— 与 instance 或 getInstance 类似,除此之外该方法保证每次调用返回一个新的实例,例如:
Object newArray = Array.newInstance(classObject, arrayLen);
- 5.getType —— 与 getInstance 类似,但是在工厂方法处于不同的类中的时候使用。getType 中的 Type 是工厂方法返回的对象类型,例如:
FileStore fs = Files.getFileStore(path);
- 6.newType —— 与 newInstance 类似,但是在工厂方法处于不同的类中的时候使用。newType中的 Type 是工厂方法返回的对象类型,例如:
BufferedReader br = Files.newBufferedReader(path);
4.补充知识点
4.1.guava将很多集合类做了静态工厂方法封装
//用guava的工具类
List<String> list = Lists.newArrayList();
Set<String> set = Sets.newHashSet();
Map<String, String> map = Maps.newHashMap();
//而不是下面这种方式
Map<String, String> map =new HashMa<String,String>();
4.2.什么是immutable(不可变)对象
- 在多线程操作下,是线程安全的
- 所有不可变集合会比可变集合更有效的利用资源
- 中途不可改变
ImmutableList<String> immutableList = ImmutableList.of("1","2","3","4");
这声明了一个不可变的List集合,List中有数据1,2,3,4。类中的 操作集合的方法(譬如add, set, sort, replace等)都被声明过期,并且抛出异常。 而没用guava之前是需要声明并且加各种包裹集合才能实现这个功能
// add 方法 @Deprecated @Override
public final void add(int index, E element) {
throw new UnsupportedOperationException();
}
当我们需要一个map中包含key为String类型,value为List类型的时候
//以前我们是这样写的
Map<String,List<Integer>> map = new HashMap<String,List<Integer>>();
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
map.put("aa", list);
System.out.println(map.get("aa"));//[1, 2]
//现在这么写
Multimap<String,Integer> map = ArrayListMultimap.create();
map.put("aa", 1);
map.put("aa", 2);
System.out.println(map.get("aa")); //[1, 2]
4.3.注意区分静态工厂方法和工厂方法模式
注意,这里的静态工厂方法与设计模式里的工厂方法模式不是一个概念:
静态工厂方法 通常指的是某个类里的静态方法,通过调用该静态方法可以得到属于该类的一个实例;
工厂方法模式 是一种设计模式,指的是让具体的工厂对象负责生产具体的产品对象,这里涉及多种工厂(类),多种对象(类),如内存工厂生产内存对象,CPU工厂生产CPU对象;
不过,如果要说相似的话,静态工厂方法跟简单工厂模式倒有那么点像,不过区别也挺大,简单工厂模式里的静态工厂方法会创建各种不同的对象(不同类的实例),而静态工厂方法一般只创建属于该类的一个实例(包括子类);
总之,静态工厂方法和公共构造方法都有它们的用途,并且了解它们的相对优点是值得的。通常,静态工厂更可取,因此避免在没有考虑静态工厂的情况下直接选择使用公共构造方法。
5.参考文献
https://zhuanlan.zhihu.com/p/90837015
https://www.cnblogs.com/chenpi/p/5981084.html

本公众号分享自己从程序员小白到经历春招秋招斩获10几个offer的面试笔试经验,其中包括【Java】、【操作系统】、【计算机网络】、【设计模式】、【数据结构与算法】、【大厂面经】、【数据库】期待你加入!!!
1.计算机网络----三次握手四次挥手
2.梦想成真-----项目自我介绍
3.你们要的设计模式来了
4.一字一句教你面试“个人简介”
5.你们要的免费书来了