面向对象设计模式之单例模式
什么是单例
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。例如,Windows 中只能打开一个任务管理器,这样可以避免因打开多个任务管理器窗口而造成内存资源的浪费,或出现各个窗口显示内容的不一致等错误。
在计算机系统中,还有 Windows 的回收站、操作系统中的文件系统、多线程中的线程池、显卡的驱动程序对象、打印机的后台处理服务、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象、应用程序中的对话框、系统中的缓存等常常被设计成单例。
单例的特点:
- 单例类只有一个示例对象
- 该单例对象必须由单例类自行创建;
- 单例类对外提供一个访问该单例的全局访问点;
如何编写
- 构造器私有
- 对外提供获取对象的方法
- 声明一个static的成员变量 类加载的时候创建当前单例对象。
- 在获取对象方法中返回成员变量的值。
单例的实现方法
饿汉式
代码示例
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;
}
}