定义
单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。
这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提
供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。
核心思想:构造方法私有化。
原理
单例模式的类中有且仅有一个实例被创建,其他的类要使用单例对象时都要通过这个类提供的特殊渠道来进行获取。
- 如果不想有那么多的实例,可以使构造方法私有化。
- 提供一种方式来获取该实例,且保证实例只有一个
实现步骤
单例模式的实现主要通过以下两个步骤
- 将该类的构造方法定义为私有方法,这样其他处的代码就无法通过调用该类的构造方法来实例化该类的对象,只有通过该类提供的静态方法来得到该类的唯一实例。
- 在该类内提供一个静态方法,当我们调用这个方法时,如果类持有的引用不为空就返回这个引用,如果类保持的引用为空就创建该类的实例并将实例的引用赋予该类保持的引用。
单例模式的优缺点
优点
- 单例模式可以保证内存里只有一个实例,减少了内存的开销。
- 可以避免对资源的多重占用。
- 单例模式设置全局访问点,可以优化和共享资源的访问。
缺点
- 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
- 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
- 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。
单例模式的实现方式
单例模式实现方式有8种
- 饿汉式:静态常量
- 饿汉式:静态代码块
- 懒汉式:线程不安全
- 懒汉式:线程安全(同步方法)
- 懒汉式:线程安全(同步代码块)
- 懒汉式:线程安全(双重检查)
- 静态内部类
- 枚举方式
饿汉式:静态常量
饿汉式,顾名思义,无论是否用得到,实例在初始化时就已经创建完成。
优点是无线程安全问题,缺点是较为浪费时间。在实际开发中不要使用此方式。
public class HungrySingleton {
private HungrySingleton(){
System.out.println("单例饿汉");
}
//直接实例化
private static HungrySingleton h=new HungrySingleton();
public static HungrySingleton getInstance() {
return h;
};
}
饿汉式:静态代码块
方式和上方类似,只是将实例化放在了静态代码块之中,当类加载时就会执行一次,优缺点同上。
在实际开发中不要使用此方式。
两种饿汉式的单例模式实现方式都可以使用,但是容易造成内存浪费。
public class HungrySingleton {
private HungrySingleton(){
System.out.println("单例饿汉");
}
private static HungrySingleton h=null
static{
h=new HungrySingleton();
}
public static HungrySingleton getInstance() {
return h;
};
}
懒汉式:线程不安全
懒汉式,顾名思义就是当此实例要被使用时才会去创建,要被使用的时候才会检查有没有实例,如果存在实例就直接返回,如果没有实例则新建一个。
只能在单线程下使用,多线程环境下不可使用这种方式。在实际开发中不要使用此方式。
public class LazySingleton {
//懒汉式 不安全
//安全:实例上加volatile或方法上加synchronized
private LazySingleton() {
System.out.println("单例懒汉");
}
//实例化,暂时不创建,置其为null
private static LazySingleton l = null;
//判断是否存在实例化,存在则返回,不存在则创建
public static LazySingleton getInstance() {
if(l == null)
l = new LazySingleton();
return l;
}
}
懒汉式:线程安全(同步方法)
在方法上添加synchronized关键字可以保证线程安全。
优点是解决了线程安全的问题,缺点是效率过低。在实际开发中不推荐使用此方式。
public class LazySingleton {
//安全:实例上加volatile或方法上加synchronized
private LazySingleton() {
System.out.println("单例懒汉");
}
//实例化,暂时不创建,置其为null
private static LazySingleton l = null;
//方法前添加synchronized关键字,判断是否存在实例化,存在则返回,不存在则创建
public static synchronized LazySingleton getInstance() {
if(l == null)
l = new LazySingleton();
return l;
}
}
懒汉式:线程安全(同步代码块)
此方式是对前一种的改进,增加了其效率,但是也不能多线程使用。
在实际开发中不能使用此方式。
public class LazySingleton {
//安全:实例上加volatile或方法上加synchronized
private LazySingleton() {
System.out.println("单例懒汉");
}
//实例化,暂时不创建,置其为null
private static LazySingleton l = null;
//判断是否存在实例化,同步产生实例化
public static LazySingleton getInstance() {
if(l == null){
synchronized (LazySingleton.class){
l=new LazySingleton();
}
}
return l;
}
}
懒汉式:线程安全(双重检查)
此方式使用关键字volatile来修饰实例,并且使用了两个if来实现双重检查,保证了线程的安全,也可以在多线程使用。
优点是保证线程安全,效率较高。在实际开发中推荐使用此方式。
public class LazySingleton {
//安全:实例上加volatile或方法上加synchronized
private LazySingleton() {
System.out.println("单例懒汉");
}
//使用volatile修饰实例化,暂时不创建,置其为null
private static volatile LazySingleton l = null;
//判断是否存在实例化,同步产生实例化
public static LazySingleton getInstance() {
if(l == null){
synchronized (LazySingleton.class){
if(LazySingleton==null)
l=new LazySingleton();
}
}
return l;
}
}
静态内部类
这种方式采用了类装载的机制来保证初始化实例时只有一个线程,外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。
这种方法的优点是不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化,效率较高。在实际开发中推荐使用此方式。
public class StaticClass {
private StaticClass() {
System.out.println("单例静态内部类");
}
//静态内部类StaticClassHoler
private static class StaticClassHoler{
private static StaticClass INSTANCE=new StaticClass();
}
//判断是否存在实例化,同步产生实例化
public static StaticClass getInstance() {
return StaticClassHoler.INSTANCE;
}
}
枚举方式
枚举在java中与普通类一样,都能拥有字段与方法,而且枚举实例创建是线程安全的,在任何情况下,它都是一个单例。枚举方式借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。
在实际开发中推荐使用此方式。
public enum SingleTon{
INSTANCE;
public void method(){
//TODO
}
}
参考来源:https://blog.youkuaiyun.com/codinglover5/article/details/108398814