📜  Java单例设计模式实践与示例

📅  最后修改于: 2021-09-10 02:46:42             🧑  作者: Mango

在之前的文章中,我们详细讨论了单例设计模式和单例类实现。
在本文中,我们将看到如何创建单例类。阅读本文后,您将能够根据您的用途、简单性和消除的瓶颈来创建您的单例类。
在Java有很多方法可以做到这一点。所有这些方式在模式的实现上都不同,但最终,它们都实现了单个实例的相同最终结果。

  1. Eager 初始化:这是创建单例类的最简单方法。在这种情况下,类的对象是在 JVM 加载到内存时创建的。它是通过直接为引用分配实例来完成的。
    当程序会一直使用该类的实例,或者创建实例的资源和时间成本不是太大时,可以使用它。
JAVA
// Java code to create singleton class by
// Eager Initialization
public class GFG
{
  // public instance initialized when loading the class
  private static final GFG instance = new GFG();
 
  private GFG()
  {
    // private constructor
  }
  public static GFG getInstance(){
        return instance;
    }
}


JAVA
// Java code to create singleton class
// Using Static block
public class GFG
{
  // public instance
  public static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
static
  {
    // static block to initialize instance
    instance = new GFG();
  }
}


JAVA
//Java Code to create singleton class
// With Lazy initialization
public class GFG
{
  // private instance, so that it can be
  // accessed by only by getInstance() method
  private static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
 
  //method to return instance of class
  public static GFG getInstance()
  {
    if (instance == null)
    {
      // if instance is null, initialize
      instance = new GFG();
    }
    return instance;
  }
}


JAVA
// Java program to create Thread Safe
// Singleton class
public class GFG
{
  // private instance, so that it can be
  // accessed by only by getInstance() method
  private static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
 
 //synchronized method to control simultaneous access
  synchronized public static GFG getInstance()
  {
    if (instance == null)
    {
      // if instance is null, initialize
      instance = new GFG();
    }
    return instance;
  }
}


JAVA
// Java code to explain double check locking
public class GFG
{
  // private instance, so that it can be
  // accessed by only by getInstance() method
  private static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
 
  public static GFG getInstance()
  {
    if (instance == null)
    {
      //synchronized block to remove overhead
      synchronized (GFG.class)
      {
        if(instance==null)
        {
          // if instance is null, initialize
          instance = new GFG();
        }
       
      }
    }
    return instance;
  }
}


JAVA
// Java code for Bill Pugh Singleton Implementation
public class GFG
{
 
  private GFG()
  {
    // private constructor
  }
 
  // Inner class to provide instance of class
  private static class BillPughSingleton
  {
    private static final GFG INSTANCE = new GFG();
  }
 
  public static GFG getInstance()
  {
    return BillPughSingleton.INSTANCE;
  }
}


  1. 优点:
    1. 实施起来非常简单。
    2. 可能会导致资源浪费。因为类的实例总是被创建,无论是否需要。
    3. 如果不需要,CPU 时间也会浪费在创建实例上。
    4. 异常处理是不可能的。
  2. 使用静态块:这也是 Eager 初始化的一个子部分。唯一的区别是对象是在静态块中创建的,以便我们可以访问它的创建,例如异常处理。通过这种方式,对象也是在类加载时创建的。
    当使用急切初始化创建对象时有可能出现异常时,可以使用它。

Java

// Java code to create singleton class
// Using Static block
public class GFG
{
  // public instance
  public static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
static
  {
    // static block to initialize instance
    instance = new GFG();
  }
}
  1. 优点:
    1. 实施起来非常简单。
    2. 无需实现 getInstance() 方法。实例可以直接访问。
    3. 异常可以在静态块中处理。
    4. 可能会导致资源浪费。因为类的实例总是被创建,无论是否需要。
    5. 如果不需要,CPU 时间也会浪费在创建实例上。
  2. 延迟初始化:在此方法中,仅在需要时才创建对象。这可以防止资源浪费。需要返回实例的 getInstance() 方法的实现。有一个空检查,如果没有创建对象,则创建,否则返回先前创建的对象。为了确保该类不能以任何其他方式实例化,构造函数被设为 final。由于对象是在方法中创建的,因此它确保在需要之前不会创建对象。实例是私有的,所以没有人可以直接访问它。
    它可以在单线程环境中使用,因为多个线程可以破坏单例属性,因为它们可以同时访问 get 实例方法并创建多个对象。

Java

//Java Code to create singleton class
// With Lazy initialization
public class GFG
{
  // private instance, so that it can be
  // accessed by only by getInstance() method
  private static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
 
  //method to return instance of class
  public static GFG getInstance()
  {
    if (instance == null)
    {
      // if instance is null, initialize
      instance = new GFG();
    }
    return instance;
  }
}
  1. 优点:
    1. 仅在需要时才创建对象。它可以克服资源克服和 CPU 时间的浪费。
    2. 在方法中也可以进行异常处理。
    3. 每次都必须检查 null 条件。
    4. 实例不能直接访问。
    5. 在多线程环境中,它可能会破坏单例属性。
  2. 线程安全单例:创建了一个线程安全单例,以便即使在多线程环境中也能保持单例属性。为了使单例类线程安全, getInstance() 方法被同步,以便多个线程不能同时访问它。

Java

// Java program to create Thread Safe
// Singleton class
public class GFG
{
  // private instance, so that it can be
  // accessed by only by getInstance() method
  private static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
 
 //synchronized method to control simultaneous access
  synchronized public static GFG getInstance()
  {
    if (instance == null)
    {
      // if instance is null, initialize
      instance = new GFG();
    }
    return instance;
  }
}
  1. 优点:
    1. 延迟初始化是可能的。
    2. 它也是线程安全的。
    3. getInstance() 方法是同步的,因此它会导致性能下降,因为多个线程无法同时访问它。
  2. 带有双重检查锁定的延迟初始化:在这种机制中,我们克服了同步代码的开销问题。在这个方法中,getInstance 不是同步的,但是创建实例的块是同步的,因此最少数量的线程必须等待,而且这只是第一次。

Java

// Java code to explain double check locking
public class GFG
{
  // private instance, so that it can be
  // accessed by only by getInstance() method
  private static GFG instance;
 
  private GFG()
  {
    // private constructor
  }
 
  public static GFG getInstance()
  {
    if (instance == null)
    {
      //synchronized block to remove overhead
      synchronized (GFG.class)
      {
        if(instance==null)
        {
          // if instance is null, initialize
          instance = new GFG();
        }
       
      }
    }
    return instance;
  }
}
  1. 优点:
    1. 延迟初始化是可能的。
    2. 它也是线程安全的。
    3. 由于 synchronized 关键字,性能开销得到了降低。
    4. 第一次,它会影响性能。
  2. Bill Pugh Singleton 实现:在Java5之前,内存模型有很多问题,以上方法在多线程环境中的某些场景下会导致失败。因此,Bill Pugh 提出了一个用于单例的内部静态类的概念。

Java

// Java code for Bill Pugh Singleton Implementation
public class GFG
{
 
  private GFG()
  {
    // private constructor
  }
 
  // Inner class to provide instance of class
  private static class BillPughSingleton
  {
    private static final GFG INSTANCE = new GFG();
  }
 
  public static GFG getInstance()
  {
    return BillPughSingleton.INSTANCE;
  }
}
  1. 加载单例类时,不会加载内部类,因此在加载类时不会创建对象。只有在调用 getInstance() 方法时才会创建内部类。所以它可能看起来像急切初始化,但它是延迟初始化。
    这是使用最广泛的方法,因为它不使用同步。

何时使用什么

  1. Eager 初始化很容易实现,但可能会导致资源和 CPU 时间浪费。仅当初始化类的成本在资源方面较少时才使用它,或者您的程序将始终需要类的实例。
  2. 通过在 Eager 初始化中使用静态块,我们可以提供异常处理,也可以控制实例。
  3. 使用同步我们也可以在多线程环境中创建单例类,但它会导致性能下降,因此我们可以使用双重检查锁定机制。
  4. Bill Pugh 实现是单例类使用最广泛的方法。大多数开发人员更喜欢它,因为它的简单性和优点。