渭南做网站的公司电话,永州网站seo,专做宝宝辅食的网站,简单的个人网站下载它是Java中最简单的设计模式之一。 如果有人问我哪种设计模式好#xff0c;那么我会很自豪地说Singleton。 但是#xff0c;当他们深入询问单身人士的概念时#xff0c;我感到很困惑。 真的单身是那么困难吗#xff1f; 确实不是#xff0c;但是它有许多我们需要了解的… 它是Java中最简单的设计模式之一。 如果有人问我哪种设计模式好那么我会很自豪地说Singleton。 但是当他们深入询问单身人士的概念时我感到很困惑。 真的单身是那么困难吗 确实不是但是它有许多我们需要了解的场景尤其是初学者。 定义 在所有情况下该类只应允许一个实例并且我们应提供对该实例的全局访问点。 定义就像1,2,3和ABCD一样简单。 让我们看看如何实现Singleton类。 我们如何确保对象始终都是一个 提示将对象创建逻辑仅放在一个位置并且不允许用户每次尝试执行该逻辑时都只能执行一次。 对象创建逻辑-它是什么 我们如何用Java创建对象 是的使用构造函数我们不应该允许用户每次尝试访问构造函数并执行它。 但是我们应该这样做一次至少要得到一个对象。 那么如何确保构造函数只能访问和执行一次 防止在类外部访问构造函数以使任何外部人员都无法创建实例。 如何使它-如何防止类外部的方法访问 简单将make方法作为私有权限类似地将构造函数作为私有权限。 防止构造函数在类中多次执行。 如何制作-这有多种实现方式下面以示例来看。 如果满足以上两个条件则我们班级将始终有一个对象。 该类称为Singleton因为它在我们请求的所有时间都产生单个对象。 没有太多理论我们现在将开始执行它。 创建单例对象的方法有很多 方法1 急于初始化或使用前初始化 package com.kb.singleton;public class EagerSingletonClass {private static volatile EagerSingletonClass singletonInstance new EagerSingletonClass();//making constructor as private to prevent access to outsidersprivate EagerSingletonClass() {}public static EagerSingletonClass getInstance(){return singletonInstance;}} EagerSingletonClass的实例在类启动时创建。 由于它是静态的因此会在加载EagerSingletonClass的过程中加载并创建它。 Junit测试类为上述类的单例测试。 package com.kb.singleton;import static org.junit.Assert.*;import org.junit.Test;public class EagerSingletonClassTest {Testpublic void testSingleton() {EagerSingletonClass instance1 EagerSingletonClass.getInstance();EagerSingletonClass instance2 EagerSingletonClass.getInstance();System.out.println(checking singleton objects equality);assertEquals(true, instance1instance2);}} 优势 此策略在加载类期间创建对象因此从多线程方案中可以更快更安全。 我们只需要使实例具有可变性即可处理多线程方案。 坏处 这种策略会在类加载本身时创建实例因此如果我们不使用它那么这将浪费整个时间和内存来创建实例。 因此最好在需要时选择一种策略来创建实例。 什么时候使用以上策略 只要我们100确保在我们的应用程序中肯定使用了该对象。 要么 当物体不重时我们可以管理速度和内存。 方法2 延迟初始化或在需要时初始化 与其在启动时创建对象不如在需要时创建对象这是很好的。 因此让我们看看如何做到这一点 package com.kb.singleton;public class LazySingleton {private static volatile LazySingleton singletonInstance null;//making constructor as private to prevent access to outsidersprivate LazySingleton() {}public static LazySingleton getInstance(){if(singletonInstancenull){synchronized (LazySingleton.class) {singletonInstance new LazySingleton();}}return singletonInstance;}} 在以上程序中仅当通过getInstance方法发出请求时我们才创建了一个对象。 在此在首次调用getInstance的过程中对象“ singletonInstance”将为null并在条件变为true时执行if条件块并创建一个对象。 然后对getInstance方法的后续调用将返回相同的object。 但是如果我们看一下多线程方案问题就出在以下上下文中2个线程t1和t2调用getInstance方法线程t1执行ifsingletonInstance null并发现singletonInstance为null因此它进入同步块以创建一个宾语。 但是在执行对象创建逻辑之前如果线程t2执行ifsingletonInstance null那么它还将发现singletonInstance为null因此它还将尝试输入同步块但是由于已经输入的第一个线程t1它没有锁。 因此线程t2等待线程t1完成同步块的执行。 因此线程t1执行并创建对象。 现在线程t2也正在等待同步块时进入同步块并再次创建对象。 因此两个线程创建了两个对象。 因此无法实现单例。 解决上述问题的方法是“ 双重检查锁定”。 它说在我们在同步块内执行对象创建的逻辑之前请重新检查同步块内的实例变量。 这样我们可以避免多个线程多次创建对象。 怎么样 线程t1检查条件ifsingletonInstance null第一次为true因此它进入同步块然后再次检查条件ifsingletonInstance null这也为true因此创建了对象。 现在线程t2进入方法getInstance并假定它已在线程t1执行对象创建逻辑之前执行了ifsingletonInstance null条件然后t2也等待进入同步块。 在线程t1从同步块中出来之后线程t2进入了同一个块但是我们再次在其中有if条件ifsingletonInstance null但线程t1已经创建了一个对象它使条件变为false并进一步停止执行并返回相同的实例。 让我们看看如何在代码中完成它 package com.kb.singleton;public class LazySingletonDoubleLockCheck {private static volatile LazySingletonDoubleLockCheck singletonInstance null;//making constructor as private to prevent access to outsidersprivate LazySingletonDoubleLockCheck() {}public static LazySingletonDoubleLockCheck getInstance(){if(singletonInstancenull){synchronized (LazySingleton.class) {if(singletonInstance null){singletonInstance new LazySingletonDoubleLockCheck();}}}return singletonInstance;}}让我们做单元测试 package com.kb.singleton;import static org.junit.Assert.*;import org.junit.Test;public class LazySingletonDoubleLockCheckTest {Testpublic void testSingleton() {LazySingletonDoubleLockCheck instance1 LazySingletonDoubleLockCheck.getInstance();LazySingletonDoubleLockCheck instance2 LazySingletonDoubleLockCheck.getInstance();System.out.println(checking singleton objects equality);assertEquals(true, instance1instance2);//fail(Not yet implemented);}} 上面的实现是针对单例模式的最佳建议解决方案它最适合于单线程多线程等所有情况。 方法3 使用内部类的单例 让我们看一下下面的使用内部类创建对象的代码 package com.kb.singleton;public class SingletonUsingInnerClass {private SingletonUsingInnerClass() {}private static class LazySingleton{private static final SingletonUsingInnerClass SINGLETONINSTANCE new SingletonUsingInnerClass();}public static SingletonUsingInnerClass getInstance(){return LazySingleton.SINGLETONINSTANCE;}}单元测试代码 package com.kb.singleton;import static org.junit.Assert.*;import org.junit.Test;public class SingletonUsingInnerClassTest {Testpublic void testSingleton() {SingletonUsingInnerClass instance1 SingletonUsingInnerClass.getInstance();SingletonUsingInnerClass instance2 SingletonUsingInnerClass.getInstance();System.out.println(checking singleton objects equality);assertEquals(true, instance1instance2);}} 以上使用内部类创建对象的方法是创建单例对象的最佳方法之一。 在这里除非并且直到有人尝试访问LazySingleton静态内部类的静态引用变量否则将不会创建该对象。 因此这还将确保在需要时以及在需要时创建对象。 而且实现起来非常简单。 从多线程进行也是安全的。 方法4 具有序列化和反序列化的Singleton 现在假设我们的应用程序是分布式的我们序列化我们的单例对象并将其写入文件。 后来我们通过反序列化单例对象来阅读它。 取消序列化对象始终会创建一个文件内具有可用状态的新对象。 如果在写入文件后进行任何状态更改然后尝试取消序列化对象则将获得原始对象而不是新的状态对象。 因此在此过程中我们得到了2个对象。 让我们尝试通过程序来了解这个问题 首先-使Singleton类可序列化以序列化和反序列化此类的对象。 第二件事-将对象写入文件序列化 第三件事-更改对象状态 第四件事- de序列化对象 我们的单例课程如下 package com.kb.singleton;import java.io.Serializable;public class SingletonSerializeAndDesrialize implements Serializable {private int x100;private static volatile SingletonSerializeAndDesrialize singletonInstance new SingletonSerializeAndDesrialize();private SingletonSerializeAndDesrialize() {}public static SingletonSerializeAndDesrialize getInstance() {return singletonInstance;}public int getX() {return x;}public void setX(int x) {this.x x;}} 序列化我们的对象然后对状态进行一些更改然后取消序列化。 package com.kb.singleton;import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectInputStream;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;public class SerializeAndDeserializeTest {static SingletonSerializeAndDesrialize instanceOne SingletonSerializeAndDesrialize.getInstance();public static void main(String[] args) {try {// Serialize to a fileObjectOutput out new ObjectOutputStream(new FileOutputStream(filename.ser));out.writeObject(instanceOne);out.close();instanceOne.setX(200);// Serialize to a fileObjectInput in new ObjectInputStream(new FileInputStream(filename.ser));SingletonSerializeAndDesrialize instanceTwo (SingletonSerializeAndDesrialize) in.readObject();in.close();System.out.println(instanceOne.getX());System.out.println(instanceTwo.getX());} catch (IOException e) {e.printStackTrace();} catch (ClassNotFoundException e) {e.printStackTrace();}}} 输出 200 100 它清楚地表明即使单身我们也有2个不同的对象。 之所以发生这种情况是因为反序列化会创建具有文件中可用状态的新实例。 如何克服这个问题 意味着如何防止在反序列化期间创建新实例 解决方案非常简单–在您的singleton类中实现以下方法 Access_modifier Object readResolve() throws ObjectStreamException{
} 例 Public Object readResolve() throws ObjectStreamException{
return modifiedInstance;
} 将其应用于上面的单例课程则完整的单例课程如下 package com.kb.singleton;import java.io.ObjectStreamException;
import java.io.Serializable;public class SingletonSerializeAndDesrialize implements Serializable {private int x100;private static volatile SingletonSerializeAndDesrialize singletonInstance new SingletonSerializeAndDesrialize();private SingletonSerializeAndDesrialize() {System.out.println(inside constructor);}public static SingletonSerializeAndDesrialize getInstance() {return singletonInstance;}public int getX() {return x;}public void setX(int x) {this.x x;}public Object readResolve() throws ObjectStreamException{return singletonInstance;}} 现在运行上面的序列化和反序列化类以检查两个实例的输出。 输出 200 200 这是因为在反序列化期间它将调用readResolve方法并且在此返回现有实例这将阻止创建新实例并确保单例对象。 小心序列号 在序列化之后和取消序列化之前只要类结构发生更改。 然后在反序列化过程中它将找到一个不兼容的类并因此引发异常java.io.InvalidClassExceptionSingletonClass; 本地类不兼容流classdesc serialVersionUID 5026910492258526905本地类serialVersionUID 3597984220566440782 因此为避免发生此异常我们必须始终对可序列化的类使用序列号ID。 其语法如下 private static final long serialVersionUID 1L; 所以最后通过涵盖以上所有情况单例类的最佳解决方案如下我建议始终使用此解决方案 package com.kb.singleton;import java.io.Serializable;public class FinalSingleton implements Serializable{private static final long serialVersionUID 1L;private FinalSingleton() {}private static class LazyLoadFinalSingleton{private static final FinalSingleton SINGLETONINSTANCE new FinalSingleton();}public static FinalSingleton getInstance(){return LazyLoadFinalSingleton.SINGLETONINSTANCE;}private Object readResolve() {return getInstance();}}翻译自: https://www.javacodegeeks.com/2014/05/java-singleton-design-pattern.html