单例模式 设计模式系列1

本文详细介绍了Java中的单例模式,包括其意图、何时使用和如何解决。单例模式确保一个类只有一个实例,提供全局访问点。文章列举了几种实现方式,如饿汉式、懒汉式(线程安全与不安全)、双重校验锁(DCL)、登记式(静态内部类)和枚举方式,并讨论了各自的优缺点。对于可能存在的反射攻击,提出了防御策略。此外,还探讨了单例模式在延迟加载、线程安全和资源管理等方面的应用。
摘要由CSDN通过智能技术生成

概述

这种模式涉及到一个单一的类,只能有一个实例,由自己创建。这个类向外提供访问其唯一的对象的方法,可以直接访问,不能也不需要实例化该类的对象。

意图 保证一个类仅有一个实例,并提供一个访问它的全局访问点。

**主要解决:**一个全局使用的类频繁地创建与销毁。

**何时使用:**当您想控制实例数目,节省系统资源的时候。

**如何解决:**判断系统是否已经有这个单例,如果有则返回,如果没有则创建。

**关键代码:**构造函数是私有的。

使用场

  • 1、要求生产唯一序列号。
  • 2、WEB 中的计数器,不用每次刷新都在数据库里加一次,用单例先缓存起来。
  • 3、创建的一个对象需要消耗的资源过多,比如 I/O 与数据库的连接等。
  • 1、一个班级只有一个班主任。

简单实例(饿汉式)

public class SingleObject {
 
   //创建 SingleObject 的一个对象
   private static SingleObject instance = new SingleObject();
 
   //让构造函数为 private,这样该类就不会被实例化
   private SingleObject(){}
 
   //获取唯一可用的对象
   public static SingleObject getInstance(){
     /* 
       if (instance == null) {  
        instance = new Singleton();  
    } 
    如果在这里初始化,那么只有第一次被调用才初始化,起到lazyload效果, 即 非线程安全懒汉式
    */
      return instance;
   }
 
   public void showMessage(){
      System.out.println("Hello World!");
   }
}
public class SingletonPatternDemo {
   public static void main(String[] args) {
 
      //不合法的构造函数
      //编译时错误:构造函数 SingleObject() 是不可见的
      //SingleObject object = new SingleObject();
 
      //获取唯一可用的对象
      SingleObject object = SingleObject.getInstance();
 
      //显示消息
      object.showMessae();
   }
}

几种实现方式

1懒汉式,线程不安全(简单实例 即是)

2懒汉式,线程安全

lazy loading,能够在多线程中很好的工作

须加锁 synchronized 才能保证单例,效率极低,很大程度不能同步

适用于 调用getinstance不频繁

public class Singleton {  
    private static Singleton instance;  
    private Singleton (){}  
    public static synchronized Singleton getInstance() {  
    if (instance == null) {  
        instance = new Singleton();  
    }  
    return instance;  
    }  
}

3饿汉式(上文简单实例)

不lazyload,类加载就初始化,浪费空间 (实例话instance 耗费大时必须延迟加载)

通过classloader机制保证线程安全,不加锁效率高,

public class Singleton {  
    private static Singleton instance = new Singleton();  
    private Singleton (){}  
    public static Singleton getInstance() {  
    return instance;  
    }  
}

4双重校验锁 DCL double-checked locking

结合懒汉,饿汉优点 lazyloading 线程安全高效 实例域

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton (){}  
    public static Singleton getSingleton() {  
    if (singleton == null) {  
        synchronized (Singleton.class) {  
        if (singleton == null) {  
            singleton = new Singleton();  
            /*
            	并不是一个院原子操作,
            	1,分配内存空间
            	2,执行构造
            	3,对象引用指向这个空间
            	指令可能重排,如果线程a  132  执行3时进来另一个线程,执行到了第一个判断singleton == null
            	的语句(在sycronize外面,为true),,但是还没构造
            */
        }  
        }  
    }  
    return singleton;  
    }  
}

会有什么问题,反射可以打破private的构造,生成新的实例;

Constructor<Singleton> singletonConstructor = Singleton.class.getDeclaredConstructor();
singletonConstructor.setAccessible(true);
Singleton sing1=singletonConstructor.newInstance();
System.out.println(sing1);

怎么办,在构造函数内加入 syncronize( if(实例!=null) throw exption “禁止反射”);(当然,这种进使用你已经正经产生了一个实例,如果一开始就去反射,还是没办法禁止)

if(实例!=null)无法解决,那么用一个标志呢,比如一个 private boolen ,初始为false,构造时为false执行构造,之后改为ture,避免二次构造,但是如果通过反编译得到的你这个变量,任然能通过反射,setfiled更改,套娃…

反编译命令 javap -p xx.calss 还有个叫jad的反编译软件

5登记式/静态内部类

classloader 机制来保证初始化 instance 时只有一个线程

与DCL区别,dcl实例域,这个是静态域

与饿汉式区别,饿汉不延迟加载,这种延迟加载,Singleton 类被装载了,instance 不一定被初始化,只有显式调用 getInstance 方法时,才会显式装载 SingletonHolder 类,从而实例化 instance。

public class Singleton {  
    private static class SingletonHolder {  
    private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){}  
    public static final Singleton getInstance() {  
    return SingletonHolder.INSTANCE;  
    }  
}

6枚举

不lazy, 线程安全,自动支持序列化机制,绝对防止多次实例化。 (不支持反射)

public enum Singleton {  
    INSTANCE;  
    public void whateverMethod() {  
    }  
}

枚举也是饿汉模式

总结

一般用 饿汉

要延迟加载 用DCL、登记

要反序列化创建对象,要安全。用 枚举

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值