对于一个类而言,为了让别的类获取自身的一个实例,最常用的方法就是提供一个公有的构造器。还有一种方法,就是一个类可以提供一个公有的静态工厂方法,它只是一个返回类的实例的静态方法。这里所说的静态工厂方法与设计模式中的工厂方法模式不同。
类可以通过静态工厂方法来对外暴露实例对象,而不是通过构造器。提供静态工厂方法而不是公有的构造器,这样做具有几大优势。
静态工厂方法替换构造器的优势
-
静态工厂方法与构造器不同的第一大优势在于,它们可以有不同的名称
如果构造器的参数本身没有确切地描述正被返回的对象,那么具有适当名称的静态工厂方法会更容易使用,产出的实例代码也更易于阅读。例如,下列的Person
类的构造器Person(String name)
返回的Person
对象可能是男人
也有可能是女人
还有可能是不男不女
等可能。public class Person { private String name; private String sex; public Person(String name) { this.name = name; } // get/set ... }
如果使用名为
Person.getManPerson
的静态工厂方法来表示需要获得的实例为男人
,getWoManPerson
方法表示获得女人
对象,显然更为清楚。public class Person { private String name; private String sex; private Person(String name) { this.name = name; } public static Person getManPerson(){ Person person = new Person("张三"); person.setSex("男"); return person; } public static Person getWoManPerson(){ Person person = new Person("张三"); person.setSex("女"); return person; } // get/set .... }
测试调用过程:
public class CommonTest { public static void main(String[] args) { //获取性别为男的人 Person manPerson = Person.getManPerson(); //获取性别为女的人 Person woManPerson = Person.getWoManPerson(); System.out.println(manPerson.toString()); System.out.println(woManPerson.toString()); } }
-
静态工厂方法与构造器不同的第一大优势在于,不必每次调用它们的时候都创建一个新的对象
静态工厂方法可以使一个类避免产生多个相同对象。即将第一产生的对象缓存起来,后续在调用静态工厂方法的时候在缓存中提取,进行重复利用,从而避免创建不必要的重复对象。以下实例表示了该如何使用静态工厂方法不返回重复的对象。
静态工厂方法定义:public class Person { private String name; private String sex; private static final Map<String,Person> map = new HashMap(64); private Person(String name) { this.name = name; } public static Person getManPerson(){ if(map.containsKey("男人")){ System.out.println("我是从缓存中获取的对象"); return map.get("男人"); } Person person = new Person("张三"); person.setSex("男"); map.put("男人",person); System.out.println("我是从新创建的对象"); return person; } // get/set ... }
测试重复调用静态工厂方法最后返回的对象是否是重复的:
public class CommonTest { public static void main(String[] args) { //获取性别为男的人 Person manPerson1 = Person.getManPerson(); Person manPerson2 = Person.getManPerson(); Person manPerson3 = Person.getManPerson(); } } Result: 我是从新创建的对象 我是从缓存中获取的对象 我是从缓存中获取的对象
从结果可以看出,静态工厂可以有效的防止多次调用不会去重复的创建对象,而使用已经创建好的对象。
-
静态工厂方法与构造器不同的第一大优势在于,它们可以返回对应的子类型的对象
如下所示可以不返回
Person
类型对象,可以返回Person
对象的子类型对象。public class Person { private String name; private String sex; private static final Map<String,ManPerson> map = new HashMap(64); protected Person(String name) { this.name = name; } public static ManPerson getManPerson(){ if(map.containsKey("男人")){ System.out.println("我是从缓存中获取的对象"); return map.get("男人"); } ManPerson person = new ManPerson("张三"); person.setSex("男"); map.put("男人",person); System.out.println("我是从新创建的对象"); return person; } }
-
静态工厂方法与构造器不同的第一大优势在于,可以使用泛型类型,使代码变得更加简介
JDK 1.5
之前有时候调用构造器的时候参数个数很多,这样使用构造器新建对象的时候参数使用的就会使代码很长,越来越复杂,但是有了静态工厂方法,编译器就会替你找到类型参数。这被称作类型推导,如下所示:
没有使用静态工厂方法的HashMap
实例化的时候:Map<String,List<String>> map = new HashMap<String,List<String>>();
假如
HashMap
使用了静态工厂方法来使编译器自行推导类型(虽然后面更高版本的JDK
编译器不使用静态工厂也可以自行推导类型了。)public static<k,v> HashMap<k,v> newInstance(){ return new HashMap<k,v>(); }
就可以使用下面这句简介的代码代替上面这段繁琐的声明:
Map<String,List<String>> map = HashMap.newInstance();
静态工厂方法替换构造器的缺点
- 静态工厂方法的主要缺点在于,类如果不包含公有的或受保护的构造器,就不能被子类化
- 静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别
结论
简而言之,静态工厂方法和公有构造器都各有用处,我们需要理解它们各自的长处。静态工厂方法通常更加合适,因此切忌第一反应就是提供公有的构造器,而不先考虑静态工厂。
参考文献
Effective Java 第二版 Joshua Bloch (第二章,第一条)