java单例模式详解完美实现(包括反射破坏的防止和线程安全)

一.描述 
Singleton(单例)是设计模式的一种,为了保证一个类仅有一个实例,并提供一个访问它的全局访问点。
主要用途是保证某个很占系统资源的类,在同一时间只能拥有一个的情况。
例如:一个系统中可以存在多个打印任务,但是只能有一个正在工作的任务;
一个系统只能有一个窗口管理器或文件系统;一个系统只能有一个计时工具或ID(序号)生成器。
二.单例模式的主要特点:
1.某个类只能有一个实例;
2.必须自行创建这个实例;
3.必须自行向整个系统提供这个实例。
三.简单的具体实现:
第一种:懒汉式 (线程不安全)

 

public class SingletonDemo {
	private static SingletonDemo single = null;
	//私有的构造方法,无法主动实例化这个类
	private SingletonDemo(){}

	//静态工厂方法 通过getInstance得到具体的单例对象
	public static SingletonDemo getInstance(){
		if(single == null){
			single = new SingletonDemo(); 
		}
		return single;
	}
}

 

 

 

第二种 懒汉 线程安全

package com.hjh.Singleton;

/**
 * 
 * @author Administrator
 * 	懒汉式单例 线程安全
 *	把构造方法private 无法主动实例化Singletondemo类
 *	通过getInstance 实例化对象
 */
public class SingletonDemo {
	private static SingletonDemo single = null;
	
	private SingletonDemo(){}

	//静态工厂方法
	public static synchronized SingletonDemo getInstance(){
		if(single == null){
			single = new SingletonDemo(); 
		}
		return single;
	}
	
}

 

 

四. 反射破坏单例

看似单例好像私有了构造方法。无法访问,只能通过getInstance()得到唯一的实例对象。实际上java的反射技术可以破坏单例

public class ReflectSingleton {
	private static ReflectSingleton singleton = null;
	
	private ReflectSingleton(){};
	
	private String name = "singleton";
	
	/**
	 * 懒汉式线程安全 单例
	 */
	public static synchronized ReflectSingleton getInstance(){
		//常量写在前面可以避免一些小错误 比如 == 写成 = 
		if(null == singleton){
			singleton = new ReflectSingleton();
		}
		return singleton;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 利用反射破坏单例
	 */
	public static void main(String[] args) {
		try {
			//得到构造方法
			Constructor cons = ReflectSingleton.class.getDeclaredConstructor();
			//把构造方法设为可访问 private失效
			cons.setAccessible(true);
			//利用反射实例化的单例对象
			ReflectSingleton s1 = (ReflectSingleton) cons.newInstance();
			s1.setName("s1");
			
			ReflectSingleton s2 = (ReflectSingleton) cons.newInstance();
			s2.setName("s2");
			
			//常规方法实例化的对象
			ReflectSingleton s3 = ReflectSingleton.getInstance();
			s3.setName("s3");
			
			ReflectSingleton s4 = ReflectSingleton.getInstance();
			s4.setName("s4");
			
			System.out.println("s1的name = "+s1.getName() );
			System.out.println("s2的name = "+s2.getName() );
			System.out.println("s3的name = "+s3.getName() );
			System.out.println("s4的name = "+s4.getName() );
			
		} catch (NoSuchMethodException | 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();
		}
	}
}

 

最后运行结果s3的name = s4 说明s3和s4是同一个单例,

但是s1,s2却成功的创建了新的实例化对象。

这就破坏了单例模式

 

所以我们必须做出防范。

 

五.防止反射破坏的单例模式。

 

package com.hjh.Reflect;

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

/**
 * 防止反射破坏的安全单例
 */
public class SafeSingleton {

	private String name = "singleton";
	private static boolean flag = false;
	
	private SafeSingleton(){
		synchronized (this){
			//如果没有创建,就创建实例
			if(false == flag){
				flag = true;
			}else{
				throw new RuntimeException("单例对象已经创建,不能反复创建");
			}
		}
	};
	//static 在 jvm 创建时就会调用该实例化方法创建单例
	private  static class SingletonHolder{
        private static final SafeSingleton INSTANCE = new SafeSingleton();
    }
	

	public static synchronized SafeSingleton getInstance(){
		//返回这个实例化对象
		return SingletonHolder.INSTANCE;
	}
	
	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	/**
	 * 利用反射破坏单例
	 */
	public static void main(String[] args) {
		try {
			//得到构造方法
			Constructor cons = SafeSingleton.class.getDeclaredConstructor();
			//把构造方法设为可访问 private失效
			cons.setAccessible(true);
			//利用反射实例化的单例对象
			SafeSingleton s1 = (SafeSingleton) cons.newInstance();
			s1.setName("s1");
			
			//常规方法实例化的对象
			SafeSingleton s2 = SafeSingleton.getInstance();
			s2.setName("s2");
			
		} catch (NoSuchMethodException | 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();
		}
	}
}

 

结果是抛出异常 此时既然s1已经存在,就无法getInstance 或newInstance 得到s2

 

六. 进阶 --- enum枚举完美实现单例模式

 

jdk1.5以后的单例模式 里面enum实现

创建一个只有一个实例的enum 不仅可以防止反射破坏,还可以防止序列化破坏单例

写法也非常简单

public enum SingletonClass
{
    INSTANCE;
 
    public void test()
    {
        System.out.println("The Test!");
    }
}

单元素的枚举类型已经成为实现Singleton模式的最佳方法。

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值