设计模式 1-单例模式(Singleton)

Singleton单类模式是最简单的设计模式,它的主要作用是保证在程序运行生命周期中,使用了单类模式的类只能有一个实例对象存在。

单类模式实现了类似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


另外我还测试了枚举类实现的单例,果然==

完美的单例,经过序列号后还是那个实例



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值