【一】Java设计模式GOF23之单例模式:饿汉模式、懒汉模式、静态内部类模式、双重同步锁单例模式、枚举单例代码

单例简介:

保证一个类只有一个实例,并且提供一个访问该实例的全局访问点。

UML图

常见应用场景

1.全局计数器采用单例模式,不然不好同步。

2.应用程序的日志应用,共享日志文件一直处于打开状态,所以只能有一个实例去操作,否则内容不好追加。

3.数据库连接池的设计也用单例,否则浪费资源。

4.spring中的bean默认都是单例。

5.servlet是单例。

6.spring mvc/ structs1,控制器对象是单例。

7.项目中工具类,一般都用单例,没必要浪费资源。

常见实现方式

1.饿汉模式(线程安全、调用效率高、不能延时加载)

/**
 * 单例模式
 * 饿汉模式
 * */
public class SingletonDemo1 {
	
	//私有静态属性  类初始化的时候就new了对象
	private static /*final*/ SingletonDemo1 instance = new SingletonDemo1();
	
	//私有化构造器
	private SingletonDemo1(){}
	
	//只能从这里取对象  方法没有同步 调用效率高
	public static /*synchronized*/ SingletonDemo1 getInstance(){
		return instance;
	}
}

static在类装载的时候就初始化了(第一句 new),JVM保证只会new一次SingletonDemo,所以getInstance方法不会有并发问题,得到的都是同一个对象,可以省略synchronized。

2.懒汉模式(线程安全、调用效率不高(getInstance方法用了synchronized同步方法)、可以延时加载)

/*
 * 单例模式
 * 懒汉模式
 * 延迟加载
 * */
public class SingletonDemo2 {
	
	//这里不用new 延迟加载 等到有人要用的时候调用getinstance方法时才new
	private static SingletonDemo2 s;
	
	//私有化构造器
	private SingletonDemo2(){}
	
	//需要加synchronized,同步化该方法,不然多线程的时候可能会new很多个对象
	public static synchronized SingletonDemo2 getInstance(){
		if(s == null){
			s =  new SingletonDemo2();
		}
		return s;
	}
}

3.静态内部类模式(线程安全、调用率高、可以延时加载)

/*
 * 单例模式
 * 静态内部类模式
 * 懒加载 线程安全 效率高
 * */

public class SingletonDemo3 {

	//静态内部类  初始化SingletonDemo3的时候并不会立即初始化这个静态内部类
	private static class singletonClassInstance{
		//在静态内部类中定义单例对象
		private static final SingletonDemo3 instance = new SingletonDemo3();
	}
	
	public static SingletonDemo3 getInstance(){
		//调用的时候才初始化这个单例类  静态内部类初始化的时候是天然线程安全的,jvm只会初始化一次静态内部类
		return singletonClassInstance.instance;
	}
	
	//私有化构造器
	private SingletonDemo3(){}
	

4.懒汉模式 -》 双重同步锁单例模式

public class SingletonExample5 {

    // 私有构造函数
    private SingletonExample5() {

    }

    // 1、memory = allocate() 分配对象的内存空间
    // 2、ctorInstance() 初始化对象
    // 3、instance = memory 设置instance指向刚分配的内存

    // 单例对象 volatile + 双重检测机制 -> 禁止指令重排
    private volatile static SingletonExample5 instance = null;

    // 静态的工厂方法
    public static SingletonExample5 getInstance() {
        if (instance == null) { // 双重检测机制        // B
            synchronized (SingletonExample5.class) { // 同步锁
                if (instance == null) {
                    instance = new SingletonExample5(); // A - 3
                }
            }
        }
        return instance;
    }
}

5.枚举单例(线程安全、调用率高、不能延时加载、不会被反射反序列化生成多个实例

/*
 * 单例模式
 * 
 * 枚举模式
 * 
 * 无延迟加载
 * 避免通过反射和反序列化的漏洞创建新的对象
 */

//枚举类天然单例
public enum SingletonDemo4 {
	
	//定义一个枚举元素,它就代表了SingletonDemo4的一个实例
	INSTANCE;
	
	//单例可以有自己的操作
	public void singletonOperation(){
		//功能处理
	}
}

避免通过反射和反序列化的漏洞创建新的对象

枚举类天然单例

无延迟加载

通过反射漏洞创建多个对象

import java.lang.reflect.Constructor;

/**
 * @author liyijie
 * @date 2018年8月13日下午2:41:23
 * @email 37024760@qq.com
 * @remark 通过反射、反序列化破解单例(枚举除外)
 * @version 
 */
public class client2 {
	public static void main(String[] args) throws Exception {
		SingletonDemo3 s1 = SingletonDemo3.getInstance();
		SingletonDemo3 s2 = SingletonDemo3.getInstance();
		System.out.println(s1);
		System.out.println(s2);
		
		//得到class
		Class<SingletonDemo6> class6 = (Class<SingletonDemo6>)Class.forName("com.sid.singleton.SingletonDemo6");
		//得到构造器
		Constructor<SingletonDemo6> c =class6.getDeclaredConstructor(null);
		//跳过权限检查,不然不能访问私有方法
		c.setAccessible(true);
		SingletonDemo6 s3 = c.newInstance();
		SingletonDemo6 s4 = c.newInstance();
		
		System.out.println(s3);
		System.out.println(s4);
	}
}

防止反射漏洞创建多个对象需要修改单例类的私有化构造器,第二次创建对象时报错

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

/*
 * 防止反射、反序列化破解单例

 * implements Serializable是用来测试反序列化漏洞的,实际写单例不需要实现这个接口
 * */
public class SingletonDemo6 implements Serializable{
	//这里不用new 延迟加载 等到有人要用的时候调用getinstance方法时才new
		private static SingletonDemo6 s;
		
		//私有化构造器
		private SingletonDemo6(){
			//第二次创建这个对象报错、防止利用反射漏洞
			if(s!=null){
				throw new RuntimeException();
			}
		}
		
		//需要加synchronized,同步化该方法,不然多线程的时候可能会new很多个对象
		public static synchronized SingletonDemo6 getInstance(){
			if(s == null){
				s =  new SingletonDemo6();
			}
			return s;
		}
		
		//反序列化拿对象时返回这个s对象,防止利用反序列化漏洞创建多个实例
		private Object readResolve() throws ObjectStreamException{
			return s;
		}

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值