单例设计模式

一:什么是单例设计模式

单例例模式便是创建型设计模式的一种,它确保某一个类在系统中只有一个实例,并自行实例化,同时向外部提供获取这个唯一实例的接口。 单例设计模式主要有以下三个特点:

1、只能有一个实例。  
2、必须自己创建自己的唯一实例。  
3、必须给所有其他对象提供这一实例。

二:单例设计模式种类

饿汉设计模式:类加载就会导致改单例实例对象被创建

饿汉设计模式分为两种:
1.静态成员变量的方式
2.静态代码块方式

懒汉设计模式:类加载不会创建对象,首次使用才会创建对象

懒汉设计模式分为六种:
1.懒汉设计模式 (线程不安全)
2.懒汉设计模式 (线程安全,效率低)
3.懒汉设计模式 (双重检查锁模式)volatile 关键字
4.懒汉设计模式(静态内部类模式)
5.懒汉设计模式(序列化破坏单例)readResolve()方法
6.懒汉设计模式(反射破坏单例)
7.懒汉设计模式(枚举)

三:饿汉设计模式

1.静态成员变量的方式:

/**
 * 饿汉设计模式 (静态成员变量的方式)
 */
public class ThehungryDemo {


    //私有构造方法,外界不能创建对象
    private ThehungryDemo() {

    }

    // 静态变量进行创建声明
    private static  ThehungryDemo thehungryDemo=new ThehungryDemo();

    //通过公共方法进行创建返回,
    public static  ThehungryDemo  getInstance(){

        return thehungryDemo;
    }

}

2.静态代码块方式:

/**
 * 饿汉设计模式 (静态代码块方式)
 */
public class ThehungryDemo2 {

    //私有构造方法,外界不能创建对象
    private ThehungryDemo2(){
    }

    // 静态变量进行创建声明
    private static  ThehungryDemo2 thehungryDemo;

    static {
        thehungryDemo=new ThehungryDemo2();
    }

    //通过公共方法进行创建返回,
    public static  ThehungryDemo2  getInstance(){

        return thehungryDemo;
    }
}

饿汉设计模式总结:

缺点: 以空间换时间,在实例化时就创建,灵活性不高,空间使用率不高。
优点:在类加载过程中便会创建。由此带来的好处是Java的类加载机制本身为我们保证了实例化过程的线程安全性。

四:懒汉设计模式

1.懒汉设计模式 (线程不安全)

多线程情况下会出现线程不安全

/**
 *  懒汉设计模式 (线程不安全)
 */
public class IdlerDemo {

    //私有无参构造方法
    private IdlerDemo() {

    }

    // 声明静态的对象
    private static  IdlerDemo idlerDemo;

    // 静态的无参获取对象
    public static IdlerDemo getInstance(){
	// 此处会出现,多线程争抢时间片的情况,导致线程不安全
        if (idlerDemo == null) {
            idlerDemo = new IdlerDemo();
        }
        return idlerDemo;
    }

}

2.懒汉设计模式 (线程安全,效率低)

读写是互斥,但是读和读不互斥

/**
 *  懒汉设计模式 (线程安全,效率低)
 *  synchronized 关键字
 */
public class IdlerDemo {

    //私有无参构造方法
    private IdlerDemo() {

    }

    // 声明静态的对象
    private static  IdlerDemo idlerDemo;

    // 静态的无参获取对象
    // 加入 synchronized  关键字后,保证了线程安全。读写是互斥,但是读和读不互斥
    public static synchronized IdlerDemo getInstance(){
        if (idlerDemo == null) {
            idlerDemo = new IdlerDemo();
        }
        return idlerDemo;
    }

}

3.懒汉设计模式 (双重检查锁模式) volatile 关键字重点

volatile 关键字三大特性
1.原子性:一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
2.可见性:指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。
3.有序性:即程序执行的顺序按照代码的先后顺序执行。

/**
 *  懒汉设计模式 (双重检查锁模式)
 */
public class IdlerDemo2 {

    //声明私有构造方法
    private IdlerDemo2() {

    }

//    //声明静态的对象
//   不使用volatile关键字 ,在多线程时会报空指针错误 。因为JVM在运行时无法保证有序
//    private static  IdlerDemo2  idlerDemo2;

    //声明静态的对象
    //  volatile 关键字可以保证我们JVM在运行的时候有序进行,保证线程安全
    private static volatile IdlerDemo2  idlerDemo2;

    // 声明公共get()方法
    public static   IdlerDemo2 getInstance(){
		// 在if判断当中加入了synchronized 锁,保证了读读不互斥,提高了效率
        if(idlerDemo2 == null){
            synchronized (IdlerDemo2.class){
             idlerDemo2=new IdlerDemo2();
            }
        }
        return idlerDemo2;
    }

}

4.懒汉设计模式(静态内部类模式)

JVM在加载外部类的时候,不会加载静态内部类

/**
 *  懒汉设计模式(静态内部类模式)
 *  JVM在加载外部类的时候,不会加载静态内部类
 */
public class IdlerDemo3 {
    
  //声明私有构造方法
    private IdlerDemo3(){

    }

    //声明静态内部类 ,并给与一个静态常量创建对象。  
    private static class IdlerDemoHolder {
        private static  final IdlerDemo3 IDLERDEMO= new IdlerDemo3();
    }
    
    // 获取静态内部类常量创建对象
    public  static  IdlerDemo3 getInstance(){
        return IdlerDemoHolder.IDLERDEMO;
    }
}

5.懒汉设计模式(序列化破坏单例)readResolve()方法

通过输入流,输出流破坏单例

/**
 *  输入流,输出流破坏单例模式
 */
public class Test {
    public static void main(String[] args)throws Exception {
        writeObjectOutputStream();
        IdlerDemo3 idlerDemo3 = writeObjectInputStream();
        IdlerDemo3 idlerDemo31 = writeObjectInputStream();
        System.out.println(idlerDemo3==idlerDemo31);
    }

    /**
     * 输入流,输入对象
     * @throws IOException
     */
    public  static void writeObjectOutputStream() throws IOException {
        IdlerDemo3 instance = IdlerDemo3.getInstance();
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("F:\\a.txt"));
        oos.writeObject(instance);
        oos.close();
    }

    /**
     *  输出流获取对象
     * @return
     * @throws Exception
     */
    public  static IdlerDemo3 writeObjectInputStream()throws Exception{
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("F:\\a.txt"));
        IdlerDemo3  idlerDemo3=(IdlerDemo3)  ois.readObject();
        System.out.println(idlerDemo3);
        return idlerDemo3;
    }
}

解决办法:实现 readResolve()方法

public class IdlerDemo3 implements Serializable {

    private IdlerDemo3(){

    }

    private static class IdlerDemoHolder {
        private static  final IdlerDemo3 IDLERDEMO= new IdlerDemo3();
    }

    public  static  IdlerDemo3 getInstance(){
        return IdlerDemoHolder.IDLERDEMO;
    }

    /**
     *  查看源码发现,实现序列化接口Serializable之后
     *   需要实现readResolve()方法, 序列化底层方法会判断序列化对象有没实现有这个方法,
     *   如果没有就return新的对象
     * @return
     */
    public  Object readResolve() {
         return IdlerDemoHolder.IDLERDEMO;
    }
}

6.懒汉设计模式(反射破坏单例)

问题:反射可以通过突破你的封装,获取你的无参构造方法创建对象。
解决办法:在无参构造方法中加入锁synchronized 保证线程安全,并且加入判断只在第一次突破封装时创建。

public class IdlerDemo3 implements Serializable {

 
    //声明一个全局静态变量,初始为false 
    private static  Boolean finale=false;

    private IdlerDemo3(){
        // 加上锁, 第一次让他进行创建,第二次就抛异常。防止反射进行获取对象
        synchronized (IdlerDemo3.class){
            if (finale){
                throw  new RuntimeException("不能创建多个对象");
            }
        }
        finale =true;
    }

    private static class IdlerDemoHolder {
        private static  final IdlerDemo3 IDLERDEMO= new IdlerDemo3();
    }

    public  static  IdlerDemo3 getInstance(){
        return IdlerDemoHolder.IDLERDEMO;
    }
    }

7.懒汉设计模式(枚举)

因为枚举类型是线程安全的,并且只会装载一次。所以枚举设计模式是线程安全的,并且是唯一个不会被破坏的单例实现模式。

/**
 *  懒汉模式(枚举),但是还是饿汉式模式(消耗内存)
 */
public enum IdlerDemo4 {

    IDLER_DEMO_4;

}

懒汉设计模式总结

缺点:不适用于变化的对象,如果同一类型的对象总是要在不同的用例场景发生变化,单例设计模式就不能保存彼此的状态,导致数据错误。
优点:由于在系统内存中只存在一个对象,因此可以 节约系统资源,当 需要频繁创建和销毁的对象时单例模式无疑可以提高系统的性能。并且可以避免共享资源的多重占用。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值