您的位置:首页 > 其它

【设计模式】——五问单例模式

2016-04-09 21:35 302 查看
0.什么是单例模式?单例模式确保一个类只有一个实例,并提供一个全局访问点。1.什么情况下使用单例模式?当你需要确保程序中的一个类最多只有一个实例时。2.如何实现单例模式?①懒汉式:适用于对性能要求不高的情况。(不推荐)缺点在于,第一次加载要初始化,反应比较慢;每一次调用这个方法都需要同步,太过累赘。
publicclassSingleton{
privatestaticSingletonuniqueInstance;
//其他有用的实例化的变量,synchronized关键字确保不会有两个线程同时进入这个方法
publicstaticsynchronizedSingletongetInstance(){
if(uniqueInstance==null){
uniqueInstance=newSingleton();
}
returnuniqueInstance;
//其他有用的方法
}
②饿汉式:适用于经常创建并使用单例,或是在创建和运行时方面的负担不太繁重时。(推荐)
publicclassSingleton{
//在静态初始化器(staticinitialize)中创建单例,这段代码保证了线程安全
privatestaticSingletonuniqueInstance=newSingleton();
//私有的构造器
privateSingleton(){}
publicstaticSingletongetInstance(){
//已经有实例,可以直接使用
returnuniqueInstance;
}
}
③双重检查加锁(不推荐)首先检查实例是否已经创建,在没有创建的情况下才进行同步:即只有第一次会同步。请注意,双重加锁检查不适用于1.4及更早版本的java,可能会出现失效。
publicclassSingleton{
//volatile关键词确保多个线程能正确处理uniqueInstance变量
privatevolatilestaticSingletonuniqueInstance;
privateSingleton(){}
publicstaticSingletongetInsatance(){
//先检查实例是否存在,不存在才进入同步区块
if(uniqueInstance==null){//避免不必要的同步
synchronized(Singleton.class){
if(uniqueInstancen==null){
uniqueInstance=newSingleton();
}
}
}
returnuniqueInstance;
}
}
④静态内部类(推荐)
publicclassSingleton{
privateSingleton(){}
publicstaticSingletongetInstance(){
returnSingletonHolder.sInstance;
}
/**
*静态内部类
*/
privatestaticclassSingletonHolder{
privatestaticfinalSingletonsInstance=newSingleton();
}
}
第一次加载Singleton类时并不会初始化sInstance,只有在第一次调用Singleton的getInstance方法才会导致sInstance被初始化,技能保证线程安全,也能保证单例对象唯一,是非常推荐的实现方式。⑤枚举单例优点:线程安全、任何情况下都是一个单例。不仅能有字段也能有自己的方法。【枚举不受反序列化的影响。】
publicenmuSingletonEnum{
INSTANCE;
publicvoiddoSomething(){
System.out.println("dosth");
}
}
其他实例中如果要防止单例对象在被反序列化时重新生成对象,需要加入以下方法:
privateObjectreadResolve()throwObjectStreamException{
returnsInstance;
}
另外反射也是可以破坏单例的:
importjava.lang.reflect.Constructor;
publicclassSingleton{
publicstaticfinalSingletonINSTANCE=newSingleton();
privateSingleton(){
}
publicSingletongetInstance(){
returnINSTANCE;
}
publicstaticvoidmain(String[]args)throwsException{
//反射机制破坏单例模式
Classclazz=Singleton.class;
Constructorc=clazz.getDeclaredConstructor();
//反射机制使得private方法可以被访问!!!
c.setAccessible(true);
//判断反射生成的对象与单例对象是否相等
System.out.println(Singleton.INSTANCE==c.newInstance());
}
}
而枚举能解决这两个问题。而枚举不好的一点就在于:枚举类内存占用上是静态变量的两倍以上,所以在Android中要尽可能的避免这种写法如果你的程序不是大量采用枚举,那么这种性能的体现是很小的,基本不会受到影响,不用特别在意。当然如果你的App出现了性能问题,理论上这个地方就是一个可以优化的性能优化点。、关于枚举是如何优化的:http://www.hollischuang.com/archives/197⑥使用容器实现单例模式
publicclassSingletonManager{
privatestaticMap<String,Object>objMap=newHashMap<String,Object>();
privateSingletonManager(){}
publicstaticvoidregisterService(Stringkey,Objectinstance){
if(!objMap.containsKey(key)){
objMap.put(key,instance);
}
}
publicstaticObjectgetService(Stringkey){
returnobjMap.get(key);
}
}
将多种单例类型注入到一个统一的管理类中,在使用时根据key获取对象对应类型的对象。这种方式使得我们可以管理多种类型的单例,并且在使用时可以通过统一的接口进行获取操作,降低了用户的使用成本,也对用户隐藏了具体实现,降低了耦合度。最后,不管采取何种方案,请时刻牢记单例的三大要点:线程安全延迟加载序列化与反序列化安全3.单例模式能否继承构造器是私有的,不能用私有构造器来扩展类,如果把构造器改成public或是protected就不能算是真正的”单例“了。4.全局变量和单例模式的比较全局变量可以提供全局访问,但是不能保证只有一个实例。这就是全局变量的局限性。参考资料:单例模式的一些注意点http://stormzhang.com/designpattern/2016/04/04/singleton-extend/让我们来破坏单例模式/article/9025803.html《Android源码设计模式解析与实战》《HeadFirst设计模式》
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: