单例模式详解(解决反射反序列化问题)

来源:www.bjsxt.com

1.饿汉式:

package singleton;

public class Demo01 {

	private static Demo01 d=new Demo01();
	
	private Demo01(){
	}
	public static Demo01 getInstance(){
		return d;
	}
}

2.懒汉式:

package singleton;

import java.io.ObjectStreamException;
import java.io.Serializable;

public class Demo02 implements Serializable {

	private static Demo02 d;
	
	private Demo02(){
		if(d!=null){
			throw new RuntimeException();
		}
	}
	
	public static synchronized Demo02 getInstance(){
		if(null==d){
			d=new Demo02();
		}
		return d;
	}
	
	private Object readResolve() throws ObjectStreamException{
		return d;
	}
}

3.双重检查锁:

package singleton;

public class Demo03 { 
	  
	  private static Demo03 instance = null; 
	  
	  public static Demo03 getInstance() { 
	    if (instance == null) { 
	    	Demo03 sc; 
	      synchronized (Demo03.class) { 
	        sc = instance; 
	        if (sc == null) { 
	          synchronized (Demo03.class) { 
	            if(sc == null) { 
	              sc = new Demo03(); 
	            } 
	          } 
	          instance = sc; 
	        } 
	      } 
	    } 
	    return instance; 
	  } 
	  
	  private Demo03() { 
	  
	  } 
}


4.静态内部类:

package singleton;

public class Demo04 {

	private static class SingletonDemo04{
		private static Demo04 d=new Demo04();
	}
	private Demo04(){
		
	}
	
	public static Demo04 getInstance(){
		return SingletonDemo04.d;
	}
}


5.枚举:

package singleton;

public enum Demo05 {

	INSTANCE;
}


单例模式并不是真正意义上的单例,某些情况下就不是单例了,比如:反射和反序列化

反射演示:

package singleton;

import java.lang.reflect.Constructor;

public class Test {
	public static void main(String[] args) throws Exception {
		Demo02 s1 = Demo02.getInstance();
		Demo02 s2 = Demo02.getInstance();
		 
		System.out.println(s1);
		System.out.println(s2);
		 Class.forName("singleton.Demo02");
		/**
		 * 单例模式反射漏洞測試
		 */
		Class<?> clazz = Class.forName("singleton.Demo02");
	  Constructor<Demo02> c = (Constructor<Demo02>) clazz.getDeclaredConstructor(null);
	  c.setAccessible(true);
	  Demo02  s3 = c.newInstance();
	  Demo02  s4 = c.newInstance();
	  System.out.println(s3);
	  System.out.println(s4);
		
		
		
		
	
	}
}

运行结果:(发现并不是同一个对象)

解决办法:(在构造方法加上判断是不是为空,不等于空就抛出异常就避免了问题)



反序列化演示:

package singleton;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;

public class Test {
	public static void main(String[] args) throws Exception {
		Demo02 s1 = Demo02.getInstance();
		Demo02 s2 = Demo02.getInstance();
		 
		System.out.println(s1);
		System.out.println(s2);
		 Class.forName("singleton.Demo02");
		/**
		 * 单例模式反射漏洞測試
		 */
	/*	Class<?> clazz = Class.forName("singleton.Demo02");
	  Constructor<Demo02> c = (Constructor<Demo02>) clazz.getDeclaredConstructor(null);
	  c.setAccessible(true);
	  Demo02  s3 = c.newInstance();
	  Demo02  s4 = c.newInstance();
	  System.out.println(s3);
	  System.out.println(s4);*/
		
		/**
		 * 单例模式反序列化漏洞测试
		 */
		
		
		
		FileOutputStream fos = new FileOutputStream("d:/a.txt");
		ObjectOutputStream oos = new ObjectOutputStream(fos);
		oos.writeObject(s1);
		oos.close();
		fos.close();
		 
		ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
		Demo02 s3 =  (Demo02) ois.readObject();
		System.out.println(s3);
	}
}

运行结果:


解决办法:(反序列化时,如果定义了readResolve()则直接返回此方法指定的对象。而不需要单独再创建新对象



同时可以测试这五种效率:(一个一个测试看输出耗时)

package singleton;

import java.util.concurrent.CountDownLatch;

public class Test2 {
	public static void main(String[] args) throws Exception {
		 
		long start = System.currentTimeMillis();
		int threadNum = 10;
		final CountDownLatch  countDownLatch = new CountDownLatch(threadNum);
		 
		for(int i=0;i<threadNum;i++){
		new Thread(new Runnable() {
		@Override
		public void run() {
		 
		for(int i=0;i<1000000;i++){
			Object o1 = Demo01.getInstance();
			/*Object o2 = Demo02.getInstance();
			Object o3 = Demo03.getInstance();
		    Object o4 = Demo04.getInstance();
			Object o5 = Demo05.INSTANCE;*/
		}
		 
		countDownLatch.countDown();
		}
		}).start();
		}
		 
		countDownLatch.await(); //main线程阻塞,直到计数器变为0,才会继续往下执行!
		 
		long end = System.currentTimeMillis();
		System.out.println("总耗时:"+(end-start));
		}
}

优缺点选用问题:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值