第一条:考虑用静态工厂方法代替构造器
静态工厂方法:只是一个返回类实例的静态方法。
例:
public static Boolean valueOf(boolean b){
return b ? Boolean.TRUE : Boolean.FALSE;
}
提供静态工厂方法而不是公有的构造器的优势:
1.静态工厂方法与构造器不同的第一大优势在于,它们有名称。(避免了多个构造器带来的参数过多,不方便记忆和使用)
2.静态工厂方法与构造器不同的第二大优势在于,不必在每次调用它们的时候都创建一个新对象。(例如:单例)
例:Boolean.valueOf(boolean);
实例受控的类:原因:确保唯一或不可实例化。
3.静态工厂方法与构造器不同的第三大优势在于,它们可以返回原返回类型的任何子类类型的对象。
4.静态工厂方法与构造器不同的第四大优势在于,在创建参数化类型实例的时候,它们使代码变得更加简洁。
复杂的:Map<String, List<String>> m = new HashMap<String, List<String>>();
假设HashMap提供了如下的静态工厂:
public static <K, V> HashMap<K, V> newInstance(){
return new HashMap<K, V>();
}
Map<String, List<String>> m = HashMap.newInstance();
5.静态工厂方法的主要缺点在于,类如果不含公有的或者受保护的构造器,就不能被子类化
6.静态工厂方法的第二个缺点在于,它们与其他的静态方法实际上没有任何区别。
valueOf、of、getInstance、newInstance、getType、newType
第二条:遇到多个构造器参数时要考虑用构造器
静态工厂和构造器有个共同的局限性:它们都不能很好地扩展到大量的可选参数。
面对必须的参数和可选的参数的构造器:
1.习惯采用重叠构造器
写多个构造器,第一个是必须参数的构造器,第二个是必须参数加上一个可选参数组成的构造器,第三个是必须参数加上两个个可选参数组成的构造器....
重叠构造器模式可行,但是当有许多参数的时候,客户端代码会比较难写,并且难以阅读。
2.采用javaBeans模式
创建一个无参的构造器,然后利用setter方法来设置必须的参数以及每个相关的可选参数。
javaBeans模式自身的缺点:
1.构造过程被分到几个调用中,在构造过程中JavaBean可能处于不一致的状态。类无法仅仅通过检验构造器参数的有效性来保证一致性。
2.JavaBeans模式阻止了类做成不可变的可能。这可能需要程序员付出额外的努力来确保它的线程安全。
3.采用Builder模式
既能保证像重叠构造器模式那样的安全性,也能保证像JavaBeans模式那么好的可读性。 不直接生成想要的对象,而是让客户端利用所有必要的参数调用构造器(或静态工厂),得到一个builder对象。然后客户端在builser对象上调用类似于setter的方法,来设置每个相关的可选参数。最后,客户端调用午餐的builder方法来生成不可变的对象。
示例:
package unit.test.no2; public class NutritionFacts { private final int servingSize; private final int servings; private final int calories; private final int fat; private final int sodium; private final int carbohydrate; public static class Builder{ //Required parameters private final int servingSize; private final int servings; //Optional parameters - initialized to default values private int calories = 0; private int fat = 0; private int sodium = 0; private int carbohydrate = 0; public Builder(int servingSize, int servings){ this.servingSize = servingSize; this.servings = servings; } public Builder calories(int val) { calories =val; return this; } public Builder fat(int val) { fat =val; return this; } public Builder sodium(int val) { sodium =val; return this; } public Builder carbohydrate(int val) { carbohydrate =val; return this; } public NutritionFacts build() { return new NutritionFacts(this); } } public NutritionFacts(Builder builder) { servingSize = builder.servingSize; servings = builder.servings; calories = builder.calories; fat = builder.fat; sodium = builder.sodium; carbohydrate = builder.carbohydrate; } }
客户端代码:
NutritionFactscocaCola = new NutritionFacts.Builder(240,8).calories(100).sodium(35).build();
builder像个构造器一样,可以对其参数强加约束条件。builder方法可以检验这些约束条件。将参数从builder拷贝到对象之后,并在对象域而不是builder域中对他们进行检验,这一点很重要,如果违反任何约束条件,build方法就应该抛出IllegalStateException。
第三条:用私有构造器或者枚举类型强化Singleton属性
Singleton指仅仅被实例化一次的类。
1.公有静态成员是个final域
public class Elvis{
public static final Elvis INSTANCE = new Elvis();
private Elvis(){...}
public void leaveTheBuilding(){...}
} .
私有构造器仅被调用一次,用来实例化公有的静态final域Elvis.INSTANCE.
提醒:享有特权的客户端可借助AccessibleObject.setAccesssible方法,通过反射机制调用私有的构造器。如果想抵御这种攻击,可以修改构造器,让它在被要求创建第二个示例的时候抛出异常。
2.公有成员是个静态工厂方法
public class Elvis{
private static final Elvis INSTANCE = new Elvis();
public Elvis(){...}
public static Elvis getInstance(){ return INSTANCE;}
public void leaveTheBuilding(){...}
}
唯一的优势是提供了灵活性:在不改变API的前提下,我们可以改变该类是否应该为Singleton的想法。 也要考虑第一种的被攻击的情况,抛除其他不相干的优势,第一种public域的方法比较简单。
当Singleton类变成可序列化的(Serializable)时候?
直接implements Serializable时,反序列化时都会创建一个新的实例,与Singleton的单例想法相违背。为了维护并保证Singleton,可以参考以下方法:
1.implements Serializable,并且声明所有的实例域都是瞬时transient的,并提供一个readResolve方法。
private Object readResolve(){
return INSTANCE;
}
a. transient:仅存在调用者的内存中,而不会写到磁盘里持久化。 (常用在不想序列化的敏感字段(密码,账号等))
b. readRosolve()是序列化操作提供的一个钩子(hook)---类中具有一个私有的被实例化的方法readResolve()。这个方法确保类开发人员在反序列化会返回怎样的object上具有发言权,虽不是静态的,但反序列化创建实例时会被调用。
2.从java1.5发行版起,实现Singleton还有第三种方法,只需要编写一个包含单个元素的枚举类型。
public enum Elvis{
INSTANCE;
public void leaveTheBuilding(){...}
}
这种方法在功能上与公有域方法相近,但是更加简洁,而且无偿提供了序列化机制,绝对防止多次实例化。可以不在考虑面对复杂的序列化或者反射攻击。 单元素的枚举类型已经成为实现Singleton的最佳方法。
参考博客:
Java transient关键字使用小记 http://www.cnblogs.com/lanxuezaipiao/p/3369962.html
序列化-理解readResolve() https://blog.csdn.net/fg2006/article/details/6409423
单例模式的八种写法比较 https://www.cnblogs.com/zhaoyan001/p/6365064.html