单例模式的七种写法及其优缺点分析

模式设计

设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

单例模式

单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一。这种类型的设计模式属于创建型模式,它提供了一种创建对象的最佳方式。

这种模式涉及到一个单一的类,该类负责创建自己的对象,同时确保只有单个对象被创建。这个类提供了一种访问其唯一的对象的方式,可以直接访问,不需要实例化该类的对象。

 

单例模式的实现方式

1.饿汉式(静态常量)

饿汉模式:像一个饿汉一样,不管需不需要用到实例都要先去创建实例,即在类产生的时候就创建好实例,这是一种空间换时间的做法。

代码

public class Singleton {
    //私有化构造方法,让外部无法直接访问
    private Singleton(){

    }
    //创建静态的实例对象
    private static Singleton singleton=new Singleton();
    //创建静态的方法返回类中以及创建好的实例对象,让外部通过该方法访问
      public static Singleton getInstance() {
          return singleton;
      }
}
 

优点:书写比较简单,类加载是就实例化。避免了线程同步问题。

缺点:类装载时就完成了实例化,如果从始至终都没使用果该类,则会造成资源的浪费。

结论: 这种方法可用,是线程安全的,但是可能造成内存浪费

2.饿汉式(静态代码块)

代码

public class Singleton {
    //私有化构造方法,让外部无法直接访问
    private Singleton(){

    }
    //声明类的实例变量
    private static Singleton singleton;
    //将对象的实例化方法在静态代码块中
    static{
        singleton = new Singleton();
    }
    //创建静态的方法返回类中以及创建好的实例对象,让外部通过该方法访问
      public static Singleton getInstance() {
          return singleton;
      }
}

优缺点:这种方法与上面的饿汉式(静态常量)有些类似,只是将实例化对象放到了静态代码块中。优缺点和上面是一样。
结论: 这种方法可用,是线程安全的,但是可能造成内存浪费

3.懒汉式(线程不安全)

懒汉式,就是需要用到该实例时才去创建,如果有则返回,没有则新建。有线程安全和线程不安全两种写法。

代码

public class Singleton {
    //私有化构造方法,让外部无法直接访问
    private Singleton(){

    }
    //声明类的实例变量
    private static Singleton singleton=null;
    //将对象的实例化方法在静态代码块中

    //创建静态方法返回实例对象
    //先判断实例变量是否为null,如果是则实例化对象
      public static Singleton getInstance() {
          if(singleton==null){
              singleton=new Singleton();
          }
          return singleton;
      }
}


缺点:如果在多线程下,一个线程进入了 if (singleton == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以在多线程环境下不可使用这种方式
结论:在实际开发中,不要使用这种方式

4.懒汉式(线程安全、同步方法)

代码

public class Singleton {
    //私有化构造方法,让外部无法直接访问
    private Singleton(){

    }
    //声明类的实例变量
    private static Singleton singleton=null;
    //将对象的实例化方法在静态代码块中

    //创建静态方法返回实例对象
    //先判断实例变量是否为null,如果是则实例化对象
    // 提供一个静态的公有方法,加入同步处理的代码,解决线程安全问题
      public static synchronized Singleton getInstance() {
          if(singleton==null){
              singleton=new Singleton();
          }
          return singleton;
      }
}

优点:解决了线程安全问题

缺点:效率太低啦,每个线程想获得类的实例时,执行getInstance()方法都要进行同步。大大降低了同步效率 

结论:在实际开发中,不推荐使用这种方式

 

5.懒汉式(双重效验锁)

代码

public class Singleton {
    //私有化构造方法,让外部无法直接访问
    private Singleton(){

    }
    //使用volatile修饰singleton保证可见性,提高效率
    private static volatile Singleton singleton = null;

    public static Singleton getSingleton() {
        if (singleton==null) {
            synchronized (Singleton.class) {
                //为了保证单例,返回的是同一个对象
                if (singleton==null) {
                    singleton=new Singleton();
                }
            }
        }
        return singleton;
    }

}

 

Double-Check 概念是多线程开发中常使用到的,如代码中所示,我们进行了两次 if (singleton == null)检查,这样就可以保证线程安全了。
这样,实例化代码只用执行一次,后面再次访问时,判断 if (singleton == null),直接 return 实例化对象,也避免的反复进行方法同步.
线程安全;延迟加载;效率较高
结论:在实际开发中,推荐使用这种单例设计模式
 

6.静态内部类

代码

public class Singleton {
    //私有化构造方法,让外部无法直接访问
    private Singleton(){

    }
    //通过内部类维护单例JVM在类加载的时候,是互斥的,所以可以保证线程安全问题
    private static class SingleTon{
        private static  Singleton singleton = new Singleton();
    }
    //提供一个静态的公有方法,直接返回 SingletonInstance.singleton
      public static synchronized Singleton getInstance() {

          return SingleTon.singleton;
      }
}

 

这种方式采用了类装载的机制来保证初始化实例时只有一个线程。

类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM 帮助我们保证了线程的安全性,在类进行初始化时,别的线程是无法进入的。

优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高

结论:推荐使用.
 

7.枚举

 代码

public enum Singleton {
    singleton;
    public void geteverMethod() {
    }
}

这借助 JDK1.5 中添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象。

这种方式是 Effective Java 作者 Josh Bloch 提倡的方式

结论:推荐使用

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

<think>好的,用户想了解如何用代码实现单例设计模式。我之前学过几种实现方式,但得仔细想想,确保正确性。 首先,单例模式的核心是确保一个类只有一个实例,并提供全局访问点。常见的实现方法有饿汉式、懒汉式、静态内部类和枚举。需要逐一回忆这些方法的代码结构和特点。 饿汉式应该是在类加载时就创建实例,线程安全。但可能会造成资源浪费。代码结构记得是用静态变量直接初始化实例,私有构造方法,然后提供获取实例的方法。 然后是懒汉式,延迟加载,但需要考虑线程安全。早期的懒汉式不加同步的话会有多线程问题,所以可能会用synchronized修饰方法,但这样效率不高。之后可能用双重检查锁来优化,需要配合volatile关键字防止指令重排序。 静态内部类的方式利用了类加载机制,静态内部类在第一次使用时才会加载,从而延迟实例化,同时由JVM保证线程安全。记得外部类持有内部类的静态实例,通过一个公有方法返回这个实例。 枚举方式实现单例是Effective Java推荐的,因为枚举实例的创建默认是线程安全的,且能防止反射和反序列化破坏单例。代码结构是定义一个枚举类型,里面包含一个实例,然后添加所需的方法。 现在需要检查每种方法的代码是否正确。比如饿汉式是否正确使用static和private,构造方法是否私有。懒汉式的双重检查锁是否正确使用volatile和synchronized块。静态内部类是否在内部类中持有实例,外部类如何获取。枚举是否只定义一个实例,并在其中实现方法。 还要注意引用的问题,用户提供的引用中有提到静态内部类和枚举的实现,所以需要正确关联引用。比如静态内部类对应引用1,枚举对应引用2和3。 接下来需要组织回答的结构,先概述单例模式,再分点介绍每种方法,给出代码示例,并说明优缺点。最后相关问题部分需要生成相关的问题,比如线程安全、优缺点比较、应用场景等。 需要确保代码语法正确,比如Java的类结构,构造方法的私有化,获取实例的方法是否为静态。例如,饿汉式的代码是否正确,静态内部类是否正确嵌套,枚举是否使用枚举类型。 可能遇到的错误点:双重检查锁是否遗漏volatile导致指令重排问题;静态内部类是否没有正确声明为private static;枚举的单例是否正确地通过INSTANCE访问。 现在需要验证每种方法的正确性: 1. 饿汉式:实例在类加载时创建,构造私有,静态方法返回实例。正确。 2. 懒汉式双重检查锁:实例用volatile修饰,同步块内双重检查,正确。 3. 静态内部类:内部类持有实例,外部类通过getInstance调用,正确。 4. 枚举:枚举类型只有一个实例,构造方法私有,通过INSTANCE访问,正确。 确保引用标注正确,比如静态内部类对应引用1,枚举对应引用2和3。在解释每个方法时,在相关部分添加引用标记。 最后,生成相关问题时要考虑用户可能想深入的点,比如各种方法的线程安全、如何防止反射攻击、性能比较等。</think>### 单例设计模式的代码实现方法 单例设计模式的核心目标是确保一个类只有一个实例,并提供全局访问点。以下是四种常见的实现方式及其代码示例: --- #### 1. 饿汉式(线程安全) 在类加载时直接创建实例,线程安全但可能造成资源浪费。 ```java public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} // 私有构造方法 public static Singleton getInstance() { return instance; } } ``` --- #### 2. 懒汉式(双重检查锁,线程安全) 延迟实例化,通过双重检查锁和`volatile`关键字保证线程安全。 ```java public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` --- #### 3. 静态内部类(线程安全) 利用静态内部类的类加载机制实现延迟加载,由JVM保证线程安全[^1]。 ```java public class Singleton { private Singleton() {} private static class Holder { static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return Holder.instance; } } ``` --- #### 4. 枚举实现(线程安全,防反射/反序列化) 枚举天然支持单例特性,能防止反射攻击和反序列化破坏单例[^2][^3]。 ```java public enum Singleton { INSTANCE; public void doSomething() { // 具体方法实现 } } // 调用方式:Singleton.INSTANCE.doSomething(); ``` --- ### 不同实现方法的对比 | 方法 | 线程安全 | 延迟加载 | 防反射攻击 | 代码复杂度 | |---------------|----------|----------|------------|------------| | 饿汉式 | 是 | 否 | 否 | 低 | | 懒汉式 | 是 | 是 | 否 | 中 | | 静态内部类 | 是 | 是 | 否 | 低 | | 枚举 | 是 | 否 | 是 | 极低 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值