单类模式实现了类似C语言中全局变量的功能,单类模式常用于注册/查找的服务。
笔者知道的实现单例模式有三种:
第一种 饱汉/饿汉
饱汉就是类创建时就创建实例
class Singletion1{
private static Singletion1 singletion1 = new Singletion1();
private Singletion1(){
}
public static Singletion1 getInstance(){
return singletion1;
}
}
饿汉就是延迟加载实例
public static synchronized T getInstance(){
if(instance==null){ //此方法线程不安全 可以在方法上面加synchronized关键字 但是性能不好 所以引出下面的解决方案
instance = new Singleton();
}
return instance;
}
并不是每次获取单例时都会有线程不安全问题,所以没有必要为整个方法加锁
class Singletion2{
private static Singletion2 singletion2 = new Singletion2();
private Singletion2(){
}
public static Singletion2 getInstance(){
if(singletion2==null){
synchronized(Singletion2.class) {
singletion2 = new Singletion2();
}
}
return singletion2;
}
}
一般都认为饱汉比饿汉更安全。
第二种方法:Initialization on demand holder
类里面由于没有静态属性,所以会延时加载
加载一个类时,其内部类不会同时被加载。一个类被加载,当且仅当其某个静态成员(静态域、构造器、静态方法等)被调用时发生。
这样实现不会牺牲性能
public class Init{
private Init(){
}
private static class lazyHolder{
private static Init init = new Init();
}
public static Init getInstance(){
return lazyHolder.init;
}
}
第三种方法:枚举类实现
enum Singletion4{
instance{
public void doing(){
System.out.println("123456");
}
};
public abstract void doing();
}
Singleton单类模式中只有一个INSTANCE枚举元素,枚举可以保证真个程序生命周期中只有一个实例对象存在,同时还避免了常规Singleton单类模式private构造方法被反射调用和序列化问题(枚举提供了序列化保证机制,确保多次序列化和反序列化不会创建多个实例对象)。
注意:java中除了构造方法可以创建对象实例以外,还可以通过克隆方法(clone()是Object中的protected方法)来创建对象,若单类对象直接继承自Object对象,则如果没有提供具体clone方法实现,则当调用克隆方法创建对象时,会抛出运行时的异常CloneNotSupportedException。
若单类类继承了实现克隆方法的类,则在单类类中必须覆盖父类的克隆方法,显式抛出异常CloneNotSupportedException。
另外,实现了单类模式的类不能再有派生子类,因为构造方式是私有的,子类无法调用父类构造方法,因此达到了Final的效果。
JDK的中单态模式的应用:
java.lang.Runtime
public class Runtime {
private static Runtime currentRuntime = new Runtime();
public static Runtime getRuntime() {
return currentRuntime;
}
}
真正是单例???
虽然看上去代码已经很完美了,但是还有两个地方可能会让其产生不同的实例
1.构造函数虽然是私有的但是依然可以通过反射来访问访问(虽然不知道除了我还有谁会这样干)
/**
* 由此可见~~单例模式也不是绝对的,除了第三个用枚举类实现的,老夫真的没辙了
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
Constructor constructor = (Constructor<SingletionA>) SingletionA.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
System.out.println("No1 Instance:"+SingletionA.getInstance());
System.out.println("No2 Instance:"+constructor.newInstance());
System.out.println("No3 Instance:"+constructor.newInstance());
System.out.println("==========================================");
constructor = (Constructor<SingletionB>) SingletionB.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
System.out.println("No1 Instance:"+SingletionA.getInstance());
System.out.println("No2 Instance:"+constructor.newInstance());
System.out.println("No3 Instance:"+constructor.newInstance());
System.out.println("==========================================");
constructor = (Constructor<SingletionC>) SingletionC.class.getDeclaredConstructors()[0];
constructor.setAccessible(true);
System.out.println("No1 Instance:"+SingletionA.getInstance());
System.out.println("No2 Instance:"+constructor.newInstance());
System.out.println("No3 Instance:"+constructor.newInstance());
}
}
class SingletionA{
private SingletionA(){
}
private static class lazyHolder{
private static SingletionA init = new SingletionA();
}
public static SingletionA getInstance(){
return lazyHolder.init;
}
}
class SingletionB{
private static SingletionB singletion = new SingletionB();
private SingletionB(){
}
public static SingletionB getInstance(){
if(singletion==null){
synchronized(SingletionB.class) {
singletion = new SingletionB();
}
}
return singletion;
}
}
enum SingletionC{
instance{
public void doing(){
System.out.println("123456");
}
};
public abstract void doing();
}
测试结果:
No1 Instance:day20150730.SingletionA@65690726
No2 Instance:day20150730.SingletionA@2a9931f5
No3 Instance:day20150730.SingletionA@2f9ee1ac
==========================================
No1 Instance:day20150730.SingletionA@65690726
No2 Instance:day20150730.SingletionB@3fbefab0
No3 Instance:day20150730.SingletionB@133c5982
==========================================
No1 Instance:day20150730.SingletionA@65690726
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:511)
at day20150730.NotSingletion.main(NotSingletion.java:30)
由于反射机制的存在,无论你怎么隐藏构造函数,他都可能被调用。。。
不过枚举类实现的实例方法除外!
2.如果类实现了sericlizable接口,反序列化的时候会得到不一样的实例
测试:
public class SerializableTest {
@Test
public void testWriteAndRead() throws Exception, IOException{
String src ="./";
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream(new File(src+"writeToFile.dat")));
ObjectInputStream inputStream = new ObjectInputStream(new FileInputStream(new File(src+"writeToFile.dat")));
SingletionSer s = SingletionSer.getInstance();
System.out.println(s);
objectOutputStream.writeObject(s);
System.out.println(inputStream.readObject());
objectOutputStream.close();
inputStream.close();
}
}
class SingletionSer implements Serializable{
private static SingletionSer singletion2 = new SingletionSer();
private SingletionSer(){
}
public static SingletionSer getInstance(){
if(singletion2==null){
synchronized(SingletionSer.class) {
singletion2 = new SingletionSer();
}
}
return singletion2;
}
}
运行结果:
day20150730.SingletionSer@9506dc4
day20150730.SingletionSer@bbe0f0a
解决方法是:新建一个readResolve方法。
private Object readResolve(){
return singleton;
}
测试结果:
day20150730.SingletionSer@9506dc4
day20150730.SingletionSer@9506dc4
另外我还测试了枚举类实现的单例,果然==
完美的单例,经过序列号后还是那个实例