一、私有构造器与Singleton
Singleton设计模式就是确保类只被实例化一次,那如何保证类只被实例化一次呢?最好的办法就是阻止客户端自己创建类实例,也就是把创建类实例的构造器给私有化。这样,客户端需要类实例时,我们就可以给它返回已经创建好的实例。这就是私有构造器与Singleton的不解之缘。
创建Singleton的方式:
1、将实例做成公有静态成员且是final型的。
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
私有构造器仅被调用一次,用来实例化公有的静态final域Elvis.INSTANCE。
2、将实例做成私有静态成员,然后提供公有的成员静态工厂方法。
public class Elvis {
private static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public static Elvis getInstance() { return INSTANCE; }
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.getInstance();
elvis.leaveTheBuilding();
}
}
3、编写一个只包含单个元素的枚举类型。
public enum Elvis {
INSTANCE;
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
三种方式比较:
1、公有域方法的主要好处在于,组成类成员的声明很清楚地表明这个类是一个Singleton:公有的静态final域保证引用的都是相同的对象。公有域方法在性能上由于现代JVM实现几乎都能把静态工厂方法的调用内联化而不会有优势。内联是JVM用编译好的二进制直接替换该段函数调用,免去编译过程,比较高效,个人理解,呵呵。。。
2、工厂方法的优势之一是灵活,在不改变约定即API的情况下,我们可以改变该实现是否保持Singleton,实现方式多样化。第二个优势与泛型有关。
3、如果涉及到singleton类序列化的话,这两种方式就没有枚举合适了。当singleton需要序列化时,不仅需要实现序列化接口,还要声明所有实例域都是瞬时的(transient),并提供一个readResolve方法。否则,每次反序列化时,都会生成一个新的实例。
加入readResolve方法保证singleton属性:
public class Elvis {
public static final Elvis INSTANCE = new Elvis();
private Elvis() { }
public void leaveTheBuilding() {
System.out.println("Whoa baby, I'm outta here!");
}
private Object readResolve() {
// Return the one true Elvis and let the garbage collector
// take care of the Elvis impersonator.
return INSTANCE;
}
// This code would normally appear outside the class!
public static void main(String[] args) {
Elvis elvis = Elvis.INSTANCE;
elvis.leaveTheBuilding();
}
}
最佳编程实践:
1、如果singleton不需要序列化的话,可以选择3种方式的任一种,根据自己的喜好,但前两种可以通过反射调用私有构造器来生成第二个实例。
2、如果需要序列化,则推荐使用单元素的枚举类型来实现Singleton。因为,它简洁,无偿的提供了序列化机制,绝对防止多次实例化,即使是在面对复杂的序列化或者反射攻击的时候,你看,多好。。。
二、私有构造器与不可实例化
一般我们编写工具类时都希望这些类不可实例化,因为实例对它没任何意义。如果我们不提供显示构造器的话,编译器会为我们创建一个默认的无参构造器,这不能阻止客户实例化工具类。企图通过将类做成抽象类来强制该类不可实例化也是行不通的,因为,客户可以实例化它的子类。这样做还容易给你一个误解,以为该类就是用来被继承的,显然违背了我们的本意。
我们可以通过将构造器私有化,来达到类不可实例的目的,虽然这样做会造成该类不能被子类化,一般工具类都不需要被子类化吧,呵呵,需要的话可以做成单粒的。