学习札记:设计模式之单例模式(如何应对在反射,反序列化时单例失效)

本文深入讲解单例模式的概念,探讨其多种实现方式,包括饿汉式、懒汉式、双重检测锁、枚举和静态内部类等。同时,剖析了序列化和反射如何导致单例模式失效,并提供了相应的解决方案。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.什么是单例模式(Singleton)

保证一个类仅有一个实例,并提供一个访问他的全局访问稳点。

二.单例模式的几种写法

1.饿汉式单例

public class SingletonInstance{
	private static SingletonInstance instance=new SingleInstance;
	
	private SingletonInstance(){
	
	}
	public static SingletonInstance instance(){
		 return instance;
	}

} 

线程安全的饿汉式单例

public class SingletonInstance{
	private static SingletonInstance instance=new SingleInstance;
	
	private SingletonInstance(){
	
	}
	public  static  synchronized SingletonInstance instance(){
		 return instance;
	}

} 

2.懒汉式单例

public class SingletonInstance{
	private static SingletonInstance instance;
	private SingletonInstance instance;
	public static SingletonInstance instance(){
	    
	    if(null==instance){
	    	instance = new SingletonInstance();
	    }
		return instance;
	}

}

线程安全的懒汉式单例

public class SingletonInstance{
	private static SingletonInstance instance;
	private SingletonInstance instance;
	public static  synchronize SingletonInstance instance(){
	    
	    if(null==instance){
	    	instance = new SingletonInstance();
	    }
		return instance;
	}

}

3.双重检测单例(DCL double check lock)

public class SingletonInstance{
	private staic SingletonInstance instance;
 	private SingletonInstance (){
	}
 	public static SingletonInstance(){
		if(null==instance){
			synchornize(SingletonInstance.class){
				if(null==instance){
					instance=new SingletonInstance();
				}
			}
		}
		return instance;
	}

}

4.枚举单列

public enum SingletonInstance{
	INSTANCE
}


5.单列管理类创建单例

public class SingletonInstanceManager {
	
	private static Map<String, Object> objMap = new HashMap<>();
	

	public static void registerService(String key,Object instance) {
		
		if(objMap.containsKey(key)) {
			objMap.put(key, instance);
		}
			
	}
	
	public static Object getService(String key) {
		
		return objMap.get(key);
	}

}

6.通过静态内部类实现单例

public class SingletonInstance{
    private SingletonInstance (){

    }
    
    private static class Builder{
      
        private static SingletonInstance singletonInstance = new SingletonInstance();
    }
 
    public static SingletonInstance instance(){
        return Builder.singletonInstance ;
    }
}

三.序列化和反射造成的单例失败及解决办法

1.反射造成的单例失败

public class client {

	
	public static void main(String[] args) {
		
		System.out.println("反射前:");
		System.out.println(SingletonInstance.instance());
		System.out.println(SingletonInstance.instance());
		
		try {
			//反射破坏了单例
			Class clz=Class.forName("com.seven.singleton.SingletonInstance");
			Constructor constru=clz.getDeclaredConstructor();	
			System.err.println(constru);
			constru.setAccessible(true);
			SingletonInstance singletonInstance=(SingletonInstance) constru.newInstance();
			System.out.println("反射后:");
			System.out.println("singleton="+singletonInstance);
			singletonInstance.print();
			SingletonInstance singletonInstance1=(SingletonInstance) constru.newInstance();
			System.out.println("singleton="+singletonInstance1);
			singletonInstance1.print();
		} catch (ClassNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (SecurityException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InstantiationException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalAccessException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

我们把控制台的打印展示出来
反射前:
com.seven.singleton.SingletonInstance@15db9742
com.seven.singleton.SingletonInstance@15db9742
反射后:
private com.seven.singleton.SingletonInstance()
singleton=com.seven.singleton.SingletonInstance@6d06d69c
this is a single
singleton=com.seven.singleton.SingletonInstance@7852e922
this is a single
我们发现通过反射后,会有两个不同的对象了,所以我们可以通过在私有构造方法里面加判断,来解除这个问题,

 private SingletonInstance (){
    	 if(null!=Builder.singletonInstance){
             throw new RuntimeException("单例模式不允许外界使用构造器!");
         }
    }
    
    private static class Builder{
      
        private static SingletonInstance singletonInstance = new SingletonInstance();
    }
 
    public static SingletonInstance instance(){
        return Builder.singletonInstance ;
    }
    
    public void print() {
    	System.out.println("this is a single");
    }

这样就解决了

2.序列化造成的单例问题

public class client {

	
	public static void main(String[] args) {
		System.out.println("反射前:");
		System.out.println(SingletonInstance.instance());
		System.out.println(SingletonInstance.instance());
        FileOutputStream fileOutputStream = null;
        ObjectOutputStream objectOutputStream = null;
        FileInputStream fileInputStream = null;
        ObjectInputStream objectInputStream = null;
 
		
		 try {
	            fileOutputStream = new FileOutputStream("SingletonInstance.txt");
	            objectOutputStream = new ObjectOutputStream(fileOutputStream);
	            objectOutputStream.writeObject(SingletonInstance.instance());
	            System.out.println("序列化对象成功!");
	        } catch (IOException e) {
	            e.printStackTrace();
	        } finally {
	            try {
	                objectOutputStream.close();
	            } catch (IOException e1) {
	                e1.printStackTrace();
	            }
	            try {
	                fileOutputStream.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        }
	        // 2.通过刚才生成的序列化对象文件反序列化生成对象
	        try {
	            fileInputStream = new FileInputStream("SingletonInstance.txt");
	            objectInputStream = new ObjectInputStream(fileInputStream);
	            SingletonInstance singletonInstance3 = (SingletonInstance) objectInputStream.readObject();
	            System.out.println(singletonInstance3);
	        } catch (FileNotFoundException e) {
	            e.printStackTrace();
	        } catch (ClassNotFoundException e) {
	            e.printStackTrace();
	        } catch (IOException e) {
	            e.printStackTrace();
	        } finally {
	            try {
	                objectInputStream.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	            try {
	                fileInputStream.close();
	            } catch (IOException e) {
	                e.printStackTrace();
	            }
	        }

	}

我们把控制台的打印展示出来
反射前:
com.seven.singleton.SingletonInstance@22f71333
com.seven.singleton.SingletonInstance@22f71333
序列化对象成功!
com.seven.singleton.SingletonInstance@685cb137
我们发现,反序列化之后,单例就失效了,那怎么解决呢

   
    /**
     * 反序列化时通过该默认方法返回对象
     * @return
     */
    private  Object readResolve(){
        return Builder.singletonInstance;
    }

加上这句即可

四.总结

本文介绍了什么是单例模式以及单例模式的写法,并介绍了单例模式在反射和反序列化失效时的原因和解决办法,如果本文对你有用,可以点个赞,如果有不正之处,欢迎留下你的评论,谢谢!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值