打破单例

单例模式相信大家都已经很熟悉,主要就是为了保证在堆区中只能有一个对象,下面给出一个典型的例子.

public class Singleton {
	private static Singleton singleton;
	private Singleton(){
	}
	public static Singleton newInstance(){
		if(singleton==null)
			singleton=new Singleton();
			return singleton;
	}
}

因为构造方法是私有的,不能直接通过new关键字实例化对象,那么只能调用静态方法,以得到相同的结果!

通常我们是这样做:Singleton singleton=Singleton.newInstance();的确,这样做是能保证我们得到相同对象,但前提是在单线程的程序.

假设有如下线程类生成Singleton对象:

public class User extends Thread{
	private Singleton singleton;
	public User(){
	}
	public void run(){
		singleton=Singleton.newInstance();
		System.out.println(singleton);
	}
}

测试类:

public class SingletonTest{
	public static void main(String args[]){
		User user1=new User();
		User user2=new User();
		user1.start();
		user2.start();
		}
	}
}

结果是什么?不清楚......

if(singleton==null) singleton=new Singleton();这行决定着我们的对象是否单例!

为了方便说明问题,把Singleton类稍微改动下:

public class Singleton {
	private static Singleton singleton;
	private Singleton(){
	}
	public static Singleton newInstance(){
		if(singleton==null){
			try {
				Thread.sleep(500);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
				singleton=new Singleton();
		}
			return singleton;
	}		
}

再运行下,发现什么?线程的run方法中打印的是2个不同的对象!

为什么?一个线程调用newInstance(),发现singleton是null,就进入了if(singleton==null){}方法体,这时当前线程sleep了500毫秒,就在这500毫秒期间,另一个线程也调用了newInstance(),发现singleton也是null,也进入了if(singleton==null){}方法体,当前线程也sleep了500毫秒,这时候第一个线程得到机会继续向下运行,执行singleton=new Singleton();返回后,第二个线程也得到运行的机会,同样也执行了singleton=new Singleton();这2次实例化的对象显然不是同一个对象

单例模式不就被我们打破了吗?!但这种打破是计算机的行为,如果不显示地让线程sleep,我们无法预知能生成几个对象!不过这种问题还是可以被解决,给对象加锁,也就是在你调用的方法前加synchronized关键字,或者把你的方法写在synchronized(lock){},lock可以是任何实例化的对象,因为任何对象都有个内部锁!

说了这么多,其实还没到正题,我们要人为地打破单例!

首先:得到一个对象的方法有几种呢?

1.new 关键字,最常用的,也最方便的

2.工厂模式,内部可以用任何实例化对象的方式返回对象,解耦用的

3.反序列化,从硬盘或网络中反序列化对象

4.反射

首先看个例子:

public class SingletonTest{
	public static void main(String args[]){
		Singleton singleton1=null;
		Singleton singleton2=null;
		Class clazz=null;
		Class[] param=new Class[]{};
	try {
		clazz=Singleton.class;
		Constructor constructor=clazz.getDeclaredConstructor(param);
		constructor.setAccessible(true);
		singleton1=(Singleton)constructor.newInstance(new Object[]{});
		singleton2=(Singleton)constructor.newInstance(new Object[]{});
		System.out.println(singleton1==singleton2);
	} catch (SecurityException e) {
			e.printStackTrace();
	} catch (NoSuchMethodException e) {
			e.printStackTrace();
	} catch (IllegalArgumentException e) {
			e.printStackTrace();
	} catch (InstantiationException e) {
			e.printStackTrace();
	} catch (IllegalAccessException e) {
			e.printStackTrace();
	} catch (InvocationTargetException e) {
			e.printStackTrace();
	}
}
}

运行结果:false!充分说明singleton1,singleton1这2个引用指向的是2个不同的对象!,之所以做到这点,

就是充分利用了java的反射机制!constructor.setAccessible(true);使得构造方法的访问权限修饰符形同虚设!同样可以适用Field,Method!

至此,我们已经人为地打破了单例模式!

做这个,只是为了说明反射的重要及有趣!

 
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值