面向对象设计模式之单例模式

面向对象设计模式之单例模式

什么是单例

单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。

在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。

单例的特点:

  • 单例类只有一个示例对象
  • 该单例对象必须由单例类自行创建;
  • 单例类对外提供一个访问该单例的全局访问点;

如何编写

  1. 构造器私有
  2. 对外提供获取对象的方法
  3. 声明一个static的成员变量 类加载的时候创建当前单例对象。
  4. 在获取对象方法中返回成员变量的值。

单例的实现方法

饿汉式

代码示例
public class Single {
	//3.创建一个single对象
	public static Single single = new Single();
	//1.将构造器私有
	private Single(){
		
	}
	//2.创建一个返回对象的方法
	public static Single getInstance() {
		return single;
	}
	public static void eat() {}
}
优缺点

优点:

  • 天然线程安全

缺点:

  • 不能延迟加载(如果只需调用该类中的方法时,对象也被加载了)

懒汉式

代码示例
/**
 * 单例模式中的懒汉式
 */
public class Lazy {
	//创建一个对象
	private static Lazy lazy = null;
	//私有化构造器
	private Lazy() {
		
	}
	//创建一个返回该类对象的方法
	public static Lazy getInstence() {
		if(lazy == null) {
			lazy = new Lazy();
		}
		return lazy;
		
	}
	
}
优缺点

优点:

  • 可以做到延迟加载。

缺点:

  • 线程不安全。

静态内部类实现单例

public class SingletonDemo {

	private static class SingletonClassInstance {
		private static final SingletonDemo instance = new SingletonDemo();
	}

	private SingletonDemo() {
	}

	public static SingletonDemo getInstance() {
		return SingletonClassInstance.instance;
	}
}

线程安全,调用效率高,可以延时加载。

枚举类实现单例

public enum SingletonDemo4 {
      
     //枚举元素本身就是单例
     INSTANCE;
      
     //添加自己需要的操作
     public void singletonOperation(){     
     }
}

线程安全,调用效率高,不能延时加载,可以天然的防止反射和反序列化调用。

单例中防止反射

public class Lazy {
	
	private static Lazy lazy ;
	
	private Lazy() {
		if(lazy!=null) {
            //自定义声明异常,如果通过反射创建对象就将异常显示。
			throw new RuntimeException("让你丫发我野");
		}
	}
	
	public static Lazy getInstance() {
		if(lazy==null) {
			lazy=new Lazy();
		}
		return lazy;
	}
	
	public Object readResolve() {
		return lazy;
	}
	
	
}

序列化对单例的影响

​ 当程序在序列化的时候 会默认调用类中的 readResolve方法 。不重写该方法 导致每次返回的是一个当前类的对象的副本,这个副本是一个新对象,导致破坏单例模式。

​ Serializable接口是启用其序列化功能的接口。实现java.io.Serializable 接口的类是可序列化的。没有实现此接口的类将不能使它们的任意状态被序列化或逆序列化。

public class Lazy implements Serializable{
    private static Lazy lazy = null;
    private Lazy() {
    } 
    public static Lazy getInstance() {
        if(lazy==null) {
        lazy = new Lazy();
    	} 
    return lazy;
    } 
    // 当程序在序列化的时候 会默认调用类中的 readResolve方法
    // 不重写该方法 导致每次返回的是一个当前类的对象的副本,这个副本是一个新对象
    public Object readResolve() {
    return lazy;
    }
}

单例模式完整版

public class Test05 {
	public static void main(String[] args) {
		
		for(int i = 0;i<5;i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					System.out.println(Lazy.getInstance());
				}
			}).start();
		}
		
	}
	
	
}

class Lazy implements Serializable{//继承Serializable可以进行序列化
    //volatile防止lazy被重排序
	private volatile static Lazy lazy = null;
	private Lazy() {
		//防止反射,被反射时显示异常
		if(lazy!=null) { throw new RuntimeException("别反射。。。。"); }
		 
	}
	
	/*
	使用双重检验锁机制
	*/
	public static Lazy getInstance()  {
		if(lazy==null) {
			synchronized (Lazy.class) {
				if(lazy==null) {
					try {
						Thread.sleep(20);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					lazy=new Lazy();
				}
			}
		}
		return lazy;
	}
	
	//防止序列化对单例的影响
	public Object readResolve() {
		return lazy;
	}
	
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值