单例设计模式–鸟瞰

几天前,当我回到家乡时,我的一位来自同事的大三学生参加了一家跨国公司的采访,在采访过程中受了重伤。 我的意思是,由于面试小组提出的难题,他无法使面试合格。 当我回到班加罗尔时,他分享了他在技术面试中遇到的尴尬处境。 根据他今天的经验,我正在撰写有关Singleton设计模式的文章。 顺便说一下,我的下级同事在Java方面拥有近四年的经验。 他面临的一个有争议的问题是“ 什么是Singleton设计模式,您将如何编写健壮的Singleton类? ”但是,让我给您提供在项目/产品开发时经常使用的Singleton设计模式的基本和关键轮廓。
如您所知,Singleton设计模式属于“ Creational Pattern ”类别。 基本原则说,在任何时间点,一个类都应该只有一个实例,而与一个类的多次调用无关。 此原理背后有许多概念和设计。 许多开发人员采用不同的方式在Java中实现Singleton。 一些开发人员根本不喜欢这种设计。 但是,我们应该专注于这种设计,而其他因素对我们而言则完全不相关。 让我们从各种角度分析此设计。

技术性

正如我已经提到的,将有一个类的实例,让我们看下面的代码。

package com.ddlab.rnd.patterns;
public class SingletonType1 
{private static SingletonType1 instance = null;private SingletonType1(){super();}public static SingletonType1 getInstance(){if( instance == null )instance = new SingletonType1();return instance;}
}

要使用和实现,我们必须编写以下代码。

SingletonType1 instance = SingletonType1.getInstance();

很好,似乎是正确的。 如果您编写10次以上的代码,您将获得相同的实例。 检查以上程序的正确性。 让我们做一个基本的临床测试。 通过调用代码“ SingletonType1.getInstance()”来创建上述类的实例,并将所有实例放入Set中。 如您所知,Set不允许重复。 因此,最后,如果获得集合1的大小,则它是正确的实现。 您也可以。 肯定会得到结果为1,即Set的大小。 现在我们想到了一个问题,我们可以打破以上设计吗? 是否可以创建上述定义的类的多个实例? 是。 我们可以。 我们可以打破以上设计,并可以创建多个实例。 那个怎么样 ????????

让我们看下面的代码。

package com.ddlab.rnd.patterns;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;public class TestSingletonType1 
{public void createMultiInstances(){System.out.println("\n** MULTIPLE INSTANCES FROM SINGLETO **\n");/** Using Reflection you can break singleton*/try {Class clazz = Class.forName("com.ddlab.rnd.patterns.SingletonType1");Constructor constructor = clazz.getDeclaredConstructors()[0];constructor.setAccessible(true);SingletonType1 instance1 = (SingletonType1)constructor.newInstance(null);SingletonType1 instance2 = (SingletonType1)constructor.newInstance(null);SingletonType1 instance3 = (SingletonType1)constructor.newInstance(null);System.out.printf( "%-15s %-15s %n", "SERIAL NO", "MULTI INSTANCES");System.out.printf( "%-15s %-15s %n", "---------", "---------------");System.out.format("%-15s %-15s %n", "INSTANCE 1 ",instance1);System.out.format("%-15s %-15s %n", "INSTANCE 2 ",instance2);System.out.format("%-15s %-15s %n", "INSTANCE 3 ",instance3);}catch (Exception e) {e.printStackTrace();}}public void createMultiInstances1(){System.out.println("\n********* MULTIPLE INSTANCES FROM SINGLETON ********\n");/** Using Reflection you can break singleton*/try {Class clazz = Class.forName("com.ddlab.rnd.patterns.SingletonType1");Method method = clazz.getDeclaredMethods()[0];Field field = clazz.getDeclaredFields()[0];field.setAccessible(true);SingletonType1 instance1 = (SingletonType1)method.invoke(clazz, null);field.set(clazz, null);SingletonType1 instance2 = (SingletonType1)method.invoke(clazz, null);field.set(clazz, null);SingletonType1 instance3 = (SingletonType1)method.invoke(clazz, null);System.out.printf( "%-15s %-15s %n", "SERIAL NO", "MULTI INSTANCES");System.out.printf( "%-15s %-15s %n", "---------", "---------------");System.out.format("%-15s %-15s %n", "INSTANCE 1 ",instance1);System.out.format("%-15s %-15s %n", "INSTANCE 2 ",instance2);System.out.format("%-15s %-15s %n", "INSTANCE 3 ",instance3);}catch (Exception e) {e.printStackTrace();}}public void createInstances(){System.out.println("\n*********** SINGLE INSTANCES FROM SINGLETON ********\n");SingletonType1 instance1 = SingletonType1.getInstance();SingletonType1 instance2 = SingletonType1.getInstance();SingletonType1 instance3 = SingletonType1.getInstance();System.out.printf( "%-15s %-15s %n", "SERIAL NO", "INSTANCES");System.out.printf( "%-15s %-15s %n", "---------", "----------");System.out.format("%-15s %-15s %n", "INSTANCE 1 ",instance1);System.out.format("%-15s %-15s %n", "INSTANCE 2 ",instance2);System.out.format("%-15s %-15s %n", "INSTANCE 3 ",instance3);}public static void main(String[] args) {new TestSingletonType1().createInstances();new TestSingletonType1().createMultiInstances();new TestSingletonType1().createMultiInstances1();}}

如果运行上面的程序,您将能够看到已定义的singleton类的许多实例。

但是我们知道,可以使用反射来破坏Singleton的私有构造方法。 在上述情况下,我们可以创建具有私有构造函数的类的实例,也可以访问私有字段。 哦,是的。。。您真的创建了多个实例吗?是的,BOSS,我做了,您觉得呢? 您以任何方式构建设计,但我可能会破坏。 确实,这伤害了像我这样真正的情感开发者的情绪。 OKKkkk。 现在,我将编写一个非常有效的代码,这样您就不会崩溃。 真的是…….. ???????? 学习Java Relection机制对于探索JAVA的美丽至关重要。

现在让我们看看如何编写更好的代码,以便其他开发人员将无法使用反射来破坏代码。

package com.ddlab.rnd.patterns;
import java.lang.reflect.ReflectPermission;
import java.security.Permission;public class SingletonType2 
{static{getInstance();}private static SingletonType2 instance = null;private SingletonType2(){super();//Add the following piece of code so that it can not be invoked using relectionSystem.setSecurityManager(new SecurityManager() {@Overridepublic void checkPermission(Permission perm) {if (perm instanceof ReflectPermission ){System.out.println("\nYes I will not allow you to create the instance using Reflection...\n");throw new SecurityException();}else{//Do nothing}}});}public static SingletonType2 getInstance(){if( instance == null )instance = new SingletonType2();return instance;}
}

现在确实如此,您的反射攻击将不会影响上述代码。 如果使用反射创建另一个实例,则将在此处获得Exception。 您可以考虑一下,使用Java自省实用程序可能会破坏它。 您可能还认为,我们将不会访问构造函数,而是将访问该字段,然后将字段值设置为null,然后再次调用该字段。 这是一个很好的策略,但是您会失败,因为自省实用程序是另一种反映。 由于我们不允许反射,因此您将无法创建多个实例。 但是,您仍然可以使用自省来调用方法“ getInstance()”,但是您将获得相同的实例。 因此,我们的反思和内省思想可以在这种情况下被抛弃。 让我们以不同的方式思考,指向类的序列化。 那么如果我们要序列化会发生什么呢? 在上面的类中,您不能进行继承,因为构造函数是私有的;对于防弹机制,您可以将类定为最终类。 我们无法序列化SingletonType2类,因为它没有实现Serializable接口,并且我们也不允许反射。 但是,我们无法序列化未实现Serilizable接口的类。 但是,有时需要将Singleton对象保留一天。 在这种情况下,我们必须在单例类中实现Serializable接口。 现在我们的项目或产品需要序列化,并且我们将不使用SecurityManager概念。 让我们修改上面的类。

让我们看看带有Seri​​alizable接口的Singleton类。

package com.ddlab.rnd.patterns;
import java.io.Serializable;public class SingletonType11 implements Serializable
{private static final long serialVersionUID = -4137189065490862968L;private static SingletonType11 instance = null;private SingletonType11(){super();}public static SingletonType11 getInstance(){if( instance == null )instance = new SingletonType11();return instance;}
}

好的,我们将能够序列化上面的类,但是我们再次为黑客提供了创建多个实例的机会,因此我们的概念再次在这里中断。 让我们看看如何通过对象序列化再次打破Singleton的概念。 让我们用这样编写一个小类。

package com.ddlab.rnd.patterns;
import java.io.Serializable;public class BreakSingleton implements Serializable 
{private static final long serialVersionUID = 5904306999023481976L;private SingletonType11 instance2 = SingletonType11.getInstance();public SingletonType11 getInstance2() {return instance2;}public void setInstance1(SingletonType11 instance2) {this.instance2 = instance2;}	
}

让我们看看上面的测试工具类。

package com.ddlab.rnd.patterns;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;public class TestBreakSingleton 
{public static void main(String[] args) throws Exception{BreakSingleton bs = new BreakSingleton();OutputStream out = new FileOutputStream("data/a.ser");ObjectOutputStream oout = new ObjectOutputStream(out);oout.writeObject(bs);oout.flush();oout.close();out.flush();out.close();InputStream in = new FileInputStream("data/a.ser");ObjectInputStream oin = new ObjectInputStream(in);BreakSingleton bs1 = (BreakSingleton)oin.readObject();oin.close();in.close();System.out.println("Instance from Serialization :::"+bs1.getInstance2());System.out.println("Normal Instance :::"+SingletonType11.getInstance());InputStream in1 = new FileInputStream("data/a.ser");ObjectInputStream oin1 = new ObjectInputStream(in1);BreakSingleton bs2 = (BreakSingleton)oin1.readObject();oin1.close();in1.close();System.out.println("Another Instance from Serialization :::"+bs2.getInstance2());}}

如果运行上述程序,则将获得以下类型的输出。

Instance from Serialization :::com.ddlab.rnd.patterns.SingletonType11@2586db54Normal Instance :::com.ddlab.rnd.patterns.SingletonType11@12276af2Another Instance from Serialization :::com.ddlab.rnd.patterns.SingletonType11@38a97b0b

因此,现在您获得了Singleton类的三个不同实例。 同样,我们遇到了多个实例的问题。 有什么办法可以使我们不会给黑客机会创建多个实例,而是可以序列化该对象? 哦,是的,有。 现在,让我们看一下修改后的单例Java类,以便我们能够序列化该对象,并且在任何时间点我们都将获得一致的单例类。

package com.ddlab.rnd.patterns;
import java.io.ObjectStreamException;
import java.io.Serializable;public class SingletonType11 implements Serializable
{private static final long serialVersionUID = -4137189065490862968L;private static SingletonType11 instance = null;private SingletonType11(){super();}public static SingletonType11 getInstance(){if( instance == null )instance = new SingletonType11();return instance;}private Object readResolve() throws ObjectStreamException{return instance;}private Object writeReplace() throws ObjectStreamException{return instance;}
}

在上述方法中,我们将从序列化对象和“ getInstance()”方法的常规调用中获得一致的单例对象。 但是,我们仍然可以使用Reflection创建多个实例,并且由于要序列化对象而无法防止反射。 在这种情况下,我们可以向开发人员提出请求并达成协议,不要仅使用反射来避免反射策略。 开发人员达成了一项协议,不要使用反思来打破。

那么多线程或在多线程应用程序中使用单例呢? 让我们看看这里发生了什么。 让我们看看在Singleton类的情况下线程的使用。

让我们考虑一下我们前面讨论的第一个Singleton类。

package com.ddlab.rnd.patterns;
public class SingletonType1 
{private static SingletonType1 instance = null;private SingletonType1(){super();}public static SingletonType1 getInstance(){if( instance == null )instance = new SingletonType1();return instance;}
}

基本上,这种方法称为延迟初始化。 在多线程的情况下,如果处理不当,我们可以获得多个实例。 让我们看下面的代码。

package com.ddlab.rnd.patterns;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;class Thread1 extends Thread
{@Overridepublic void run() {SingletonType1 instance = SingletonType1.getInstance();//		System.out.println("In Thread 1 - Singleton Instance ---->"+instance);TestSingletonType1_Thread.singletonSet.add(instance);}
}class Thread2 extends Thread
{@Overridepublic void run() {SingletonType1 instance = SingletonType1.getInstance();//		System.out.println("In Thread 2 - Singleton Instance ---->"+instance);TestSingletonType1_Thread.singletonSet.add(instance);}
}

让我们看看测试类如何使用它。

public class TestSingletonType1_Thread 
{private static Set singletonSet1 = new HashSet();public static Set singletonSet = Collections.synchronizedSet(singletonSet1);public static void main(String[] args) {//Singleton concept is broken herefor( int i = 0 ; i < 100 ; i++ ){new Thread1().start();new Thread2().start();if( singletonSet.size() > 1 )break;elsecontinue;}System.out.println(singletonSet);}
}

如果您多次运行上述程序,则将获得Singleton类的不同实例。

运行该程序后,您可能会得到类似的结果。 输出如下。

[com.ddlab.rnd.patterns.SingletonType1@60723d7c, com.ddlab.rnd.patterns.SingletonType1@6d9efb05, com.ddlab.rnd.patterns.SingletonType1@8dd20f6]

那么该怎么办 ? 我们可以声明volatile变量,现在让我们看看。 让我们拥有修改后的程序。

package com.ddlab.rnd.patterns;
public class SingletonType1 
{private static volatile SingletonType1 instance = null;private SingletonType1(){super();}public static SingletonType1 getInstance(){if( instance == null )instance = new SingletonType1();return instance;}
}

在多次运行该程序后,您可能会得到这样的信息。

[com.ddlab.rnd.patterns.SingletonType1@3f0ef90c, com.ddlab.rnd.patterns.SingletonType1@2e471e30]

但是,使用volatile不能满足我们的目的。 还是同样的问题,我们可以使用同步方法吗,是的,我们可以做到。 在许多情况下,大多数开发人员会提出与volatile关键字的用法及其在Singleton中的用法有关的问题。 如果经验不足的开发人员在其计算机上的第一次运行中获得上述类的单个实例,则可能会感到高兴。 我有很多开发人员在他们的机器上运行该程序是有道理的,他们也向我展示了。 这是正确的,因为他们很幸运。 但是我在他们的机器上多次运行了该程序,并告诉他们不要忘记事实。 现在,他们中的许多人开始使用java的great关键字和“同步的”生命保护程序来修改程序。 让我们看看这个关键字会发生什么。

让我们在下面看到。

package com.ddlab.rnd.patterns;
public class SingletonType1 
{private static volatile SingletonType1 instance = null;private SingletonType1(){super();}public static synchronized SingletonType1 getInstance(){if( instance == null )instance = new SingletonType1();return instance;}
}

但是会出现性能问题。 但是,当您对其进行分析时,您将意识到仅在第一次调用该方法时才需要同步。 后续调用不需要同步。 因此,不建议在每次调用时都使用关键字“ synchronized”。 从长远来看,它可能会对您的产品/项目开发产生不利影响。 为了提高上述程序的效率,让我们以不同的方式修改上述程序。 我们不会同步整个方法,而是会做敏感区域。

package com.ddlab.rnd.patterns;
public class SingletonType1 
{private static volatile SingletonType1 instance = null;private SingletonType1(){super();}public static SingletonType1 getInstance(){if (instance == null){synchronized(SingletonType1.class) {instance = new SingletonType1();}}return instance;}
}

上面的程序看起来还不错,我们很高兴,现在让我们庆祝。 但是,由于存在一个非常大的问题,我们仍然远离艰苦的现实。 当instance为null时,两个线程可以同时进入if语句内部。 然后,一个线程进入同步块以初始化实例,而另一个则被阻塞。 当第一个线程退出同步块时,等待线程进入并创建另一个Singleton对象。 请注意,当第二个线程进入同步块时,它不会检查实例是否为非空。 让我们做一个小的临床测试来面对现实。

package com.ddlab.rnd.patterns;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;class Thread11 extends Thread
{@Overridepublic void run() {SingletonType111 instance = SingletonType111.getInstance();//		System.out.println("In Thread 1 - Singleton Instance ---->"+instance);TestSingletonType111_Thread.singletonSet.add(instance);}
}class Thread22 extends Thread
{@Overridepublic void run() {SingletonType111 instance = SingletonType111.getInstance();//		System.out.println("In Thread 2 - Singleton Instance ---->"+instance);TestSingletonType111_Thread.singletonSet.add(instance);}
}public class TestSingletonType111_Thread 
{private static Set singletonSet1 = new HashSet();public static Set singletonSet = Collections.synchronizedSet(singletonSet1);public static void main(String[] args) {//Singleton concept is broken herefor( int i = 0 ; i < 100 ; i++ ){new Thread11().start();new Thread22().start();if( singletonSet.size() > 1 )break;elsecontinue;}System.out.println(singletonSet);}}

现在,您将多次了解上述程序。 接下来要做什么。

现在让我们考虑另一个被称为“双重检查锁定”的概念,该概念似乎对于一组开发人员来说是著名的。 许多开发人员在许多情况下都适用,并认为这是最强大的单例形式。

在软件工程中,双重检查锁定(也称为“双重检查锁定优化”)是一种软件设计模式,用于通过首先测试锁定标准(“锁定提示”)来减少获取锁定的开销,而无需实际获取锁。 只有在锁定

标准检查表明是否需要锁定,实际的锁定逻辑是否继续进行。 在大多数技术面试中,技术小组都希望候选人能给出这个答案。 如果候选人能够根据自己的喜好回答此问题,技术小组将很高兴并选择候选人。 如今,这个概念已变得非常重要,但是我要说的是技术小组对此概念没有足够的经验。 让我们对其进行深入分析。 “双重检查锁定”的基本结构如下。

public static SingletonType1 getInstance()
{if (instance == null){synchronized(SingletonType1.class)  // Mark - 1{  if (instance == null)          // Mark - 2instance = new SingletonType1();  // Mark - 3}}return instance;
}

双重检查锁定背后的理论是// // Mark – 2处的第二次检查使得不可能创建两个不同的Singleton对象。 好的... 对于单线程应用程序可能是正确的。 细粒度的多线程应用程序呢? 让我们看下面的顺序。

线程1进入getInstance()方法。

线程1在// Mark – 1处进入同步块,因为实例为空。

线程1被线程2抢占。

线程2进入getInstance()方法。

线程2尝试获取// Mark – 1处的锁,因为实例仍然为null。 但是,由于线程1持有该锁,因此线程2在// Mark – 1处阻塞。

线程2被线程1抢占。

执行线程1,并且由于在// Mark – 2处instance仍然为空,因此创建了Singleton对象并将其引用分配给实例。

线程1退出同步块,并从getInstance()方法返回实例。

线程1被线程2抢占。

线程2获取// // Mark – 1处的锁,并检查instance是否为null。

由于instance非null,因此不会创建第二个Singleton对象,并且将返回线程1创建的对象。 双重检查锁定背后的理论是完美的。 不幸的是,现实是完全不同的。 双重检查锁定的问题在于不能保证它可以在单处理器或多处理器计算机上运行。 双重检查锁定失败的问题不是由于JVM中的实现错误,而是由于当前的Java平台内存模型。 内存模型允许所谓的“乱序写入”,这是该成语失败的主要原因。 但是,“乱序写入”的概念超出了我们的讨论范围。 最重要的是,不应使用任何形式的双重检查锁定,因为您不能保证它可以在任何JVM实现中使用。 如我们所见,虽然“双重检查锁定”可能有效,但可能会意外失败。 解决办法是什么 ?

Bill Pugh的解决方案

马里兰大学计算机科学研究员Bill Pugh(摘自Wikipedia)撰写了有关用Java实现Singleton模式的代码问题。 Pugh对“双重检查锁定”这一习惯用法的努力导致了Java 5中Java内存模型的变化,并导致了通常被视为在Java中实现Singletons的标准方法。 这种技术称为按需初始化持有人惯用语,它尽可能懒惰,并且可以在所有已知的Java版本中使用。 它利用了有关类初始化的语言保证,因此可以在所有Java兼容的编译器和虚拟机中正常工作。 嵌套类的引用不早于调用getInstance()的时间(因此,类加载器不会更早地对其进行加载)。 因此,该解决方案是线程安全的,不需要特殊的语言构造(即易失性或同步的)。

public class Singleton 
{// Private constructor prevents instantiation from other classesprivate Singleton() { }/*** SingletonHolder is loaded on the first execution of Singleton.getInstance() * or the first access to SingletonHolder.INSTANCE, not before.*/private static class SingletonHolder { public static final Singleton INSTANCE = new Singleton();}public static Singleton getInstance() {return SingletonHolder.INSTANCE;}
}

上面称为“ 按需初始化持有人习惯用法 ”。 Singleton设计的上述结构在高度多线程的应用程序中非常强大。 让我们详细了解这个概念。 让我们考虑一个下面的小例子。

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

怎么运行的

该实现依赖于Java虚拟机(JVM)内指定的执行初始化阶段。 有关详细信息,请参见Java语言规范(JLS)的12.4节。 当JVM加载Something类时,该类将进行初始化。 由于该类没有任何要初始化的静态变量,因此初始化很容易完成。 在JVM确定必须执行LazyHolder之前,不会初始化其中的静态类定义LazyHolder。 仅当在Something类上调用静态方法getInstance时,才执行静态类LazyHolder,并且第一次发生这种情况时,JVM将加载并初始化LazyHolder类。 LazyHolder类的初始化导致静态变量INSTANCE的执行是通过对外部类Something执行(私有)构造函数来进行的。 由于JLS保证类的初始化阶段是串行的,即非并发的,因此在加载和初始化期间,静态getInstance方法中不需要进一步的同步。 并且由于初始化阶段在串行操作中写入了静态变量INSTANCE,因此对getInstance的所有后续并发调用将返回相同的正确初始化的INSTANCE,而不会引起任何其他同步开销。

但是,使用“按需初始化持有人惯用语”模式的概念,我们可以实现线程安全的单例构造。 再次出现问题,我们可以反思地打破吗。 是的,我们可以使用我已经提到的java反射机制打破上述概念。 现在问题来了,是否还有其他方法可以构建适当的单例设计方法。 是的, Joshua Bloch(Google技术实验室首席技术架构师和著名的Book Effective Java的作者)建议使用另一种方法。

package com.ddlab.rnd.patterns;
public enum SingletonType3 
{INSTANCE;public void doSomething(String arg) {//... perform operation here ...}
}

这是创建单例类的唯一可靠方法,该类是可序列化的,并且在默认情况下是完全线程安全的,而枚举是完全线程安全的。 关于反射,使用上面的反射方法,您不能破坏单例对象,因为它没有构造函数。 关于序列化,您将能够对其进行序列化,但是每次都会获得相同的实例。 因此,最后我们必须吸收这种创建Singleton设计类的现代方法。 但是,许多开发人员对此一无所知。

但是,大多数访调员不会接受以上两种方法,因为对他们而言,这可能是一个新概念。 您可以基于JLS和书籍参考进行辩论。 我的大三学生,同事和朋友每天都抱怨,在面试时这是他们通常在面试时面临的最困难的问题。 无论他们以何种方式回答问题,面试官都不会感到满意,这是因为在单例设计课程中,大多数人都不了解枚举的方法。 如果您也遇到相同的问题,请举约书亚·布洛赫(Joshua Bloch)为例。 您可能会遇到一些开发人员或访问员的问题,即“ Singleton类必须具有私有构造函数,并且应该有一个名为getInstance()的方法”。 您必须论证说下划线语句是错误的,并且它不是协议或任何经验法则。 这只是我们一段时间以来采用的一种方法。 单例背后的主要概念是在任何时间点,都应该只有一个实例,与如何编写代码无关。 如果面试不断与您争论,您会问他将枚举定义为单例方法的问题所在。 胆小的面试官可能会提出一些无稽之谈。 最后,您告诉他,在JDK 5中,枚举由Josh Bloch和Neal Gafter编写。 如果开发人员或面试官有胆量,他可以将邮件发送给这些优秀的建筑师。 如果傲慢的面试官仍在作出错误的论点,请教给他一堂课,“先生,您告诉我单身人士的做法,这是无法打破的。 至少我会以各种方式破坏您的Singleton设计。”

仍然不能使用枚举破坏上述单例方法,但是我们可以通过编写代码来创建多个实例来破解上述方法。 下面给出的代码请勿将以下代码用于您的商业产品。 这是打破单例的讨厌方法。 让我们看下面的代码。

package com.ddlab.rnd.patterns;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.Set;
import sun.reflect.ConstructorAccessor;public class CrackEnumSingleton 
{public static void main(String[] args){Set set = new HashSet();try {SingletonType3 firstInstance = SingletonType3.INSTANCE;System.out.println(firstInstance.getClass() + " " + firstInstance + " = " + System.identityHashCode(firstInstance));set.add(firstInstance);Constructor constructor = SingletonType3.class.getDeclaredConstructors()[0];Method acquire = constructor.getClass().getDeclaredMethod("acquireConstructorAccessor");//"acquireConstructorAccessor" fields for crackingacquire.setAccessible(true);acquire.invoke(constructor);Method get = constructor.getClass().getDeclaredMethod("getConstructorAccessor");//"getConstructorAccessor" fields for crackingget.setAccessible(true);ConstructorAccessor invoke = (ConstructorAccessor) get.invoke(constructor);Object secondInstance = invoke.newInstance(new Object[] {null,1});System.out.println(secondInstance.getClass() + " " + secondInstance + " = " + System.identityHashCode(secondInstance));set.add(secondInstance);System.out.println("Total No of Singletons :::"+set.size());}catch (Exception e) {e.printStackTrace();}}}

但是,上述方法只是一种学习技术,而并非在任何地方实现。 在这个世界上,每个对象都具有积极和消极的态度,但是我们必须遵循积极的态度才能顺利开发我们的产品或项目。

结论

本文没有任何商业意义。 在本文中,我提供了编写更好的Singleton设计类的更好方法。 可能有最佳方法,如果您知道其他最佳方法或最佳做法,请与我分享。 还提供一些注释,以便我们可以为更好的编码标准做出更好的贡献。 希望您喜欢我的文章。 如有任何错误,请通过debadatta.mishra@gmail.com向我报告。 谢谢。

参考: Singleton设计模式– Debadatta Mishra博客上来自我们JCG合作伙伴 Debadatta Mishra的鸟瞰图 。

翻译自: https://www.javacodegeeks.com/2013/06/singleton-design-pattern-a-lions-eye-view.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367689.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件实践第一步——自我介绍

031602631&#xff1b;我是超爱美食的苏韫月&#xff1b;我的爱好是品尝所能接触到的所有甜点&#xff08;迟早&#xff01;&#xff09;&#xff1b;我最近新宠&#xff1a;拌面加扁肉&#xff08;京园淳百味&#xff09;&#xff1b;时隔一年我又回归心惊胆颤写博客文的日子。…

CSS3中使用translate() 方法实现元素位置的移动

translate() 方法通过 translate() 方法&#xff0c;元素从其当前位置移动&#xff0c;根据给定的 left&#xff08;x 坐标&#xff09; 和 top&#xff08;y 坐标&#xff09; 位置参数&#xff1a;实例div{transform: translate(50px,100px);-ms-transform: translate(50px,1…

java机器学习库_Amazon Deep Java Library——Java 机器学习工具箱

近年来&#xff0c;人们对机器学习的兴趣稳步增长。具体来说&#xff0c;现在&#xff0c;企业在各种各样的场景中使用机器学习进行图像识别。它在汽车工业、医疗保健、安全、零售、仓库、农场和农业的自动化产品跟踪、食品识别&#xff0c;甚至通过手机摄像头进行实时翻译等方…

阻抗计算

si9000也可以算 一般有点差异&#xff0c;板厂可以微调 加上滤油&#xff0c;实际阻抗会降低2-3欧 影响最大的是层厚 先选择最薄的芯板&#xff0c;注意差分线的阻抗匹配&#xff0c;间距越小&#xff0c;阻抗小&#xff0c; 转载于:https://www.cnblogs.com/IotI/p/8587277.h…

Spring Data Solr教程:动态查询

Solr通常被称为搜索服务器&#xff0c;我们可以在实现全文搜索功能时使用它。 但是&#xff0c;当我们实现从搜索表单获取其输入的搜索功能时&#xff0c;利用Solr的性能通常是明智的。 在这种情况下&#xff0c;执行的搜索查询取决于收到的输入。 这意味着查询参数的数量取决…

[cdq分治][树状数组] Jzoj P4419 hole

Description GFS打算去郊外建所别墅&#xff0c;享受生活&#xff0c;于是他耗费巨资买下了一块风水宝地&#xff0c;但令他震惊的是&#xff0c;一群DSJ对GFS的富贵生活深恶痛绝&#xff0c;决定打洞以搞破坏。现在我们简化一下这个问题&#xff0c;在这片土地上会按顺序发生一…

关于CSS3实现响应式布局的一些概念和术语

响应式布局也被应用到网站前端开发中&#xff0c;在国内这一词想必是非常火吧&#xff0c;那网站为什么要使用响应式布局呢&#xff1f;原因和其他创意性的生活用品基本上是一样的&#xff0c;处于对人力物力财力的节省和对生活富有诗意的一种追求。在人力物力和财力有限的情况…

AjaxSubmit+Servlet表单文件上传和下载

一、背景 前段时间公司要求我做一个上传和下载固件的页面&#xff0c;以备硬件产品在线升级&#xff0c;现在我把这部分功能抽取出来作为一个Demo Project给大家分享。 话不多说&#xff0c;先看项目演示 --> 演示 源码 二、源码 前端 js库&#xff1a;jquery-3.2.1.min.js…

番石榴的EventBus –简单的发布者/订阅者

在查看Google的Guava Libraries 版本10的最新版本时&#xff0c;我注意到EventBus的添加。 这是发布-订阅样式消息传递系统的轻量级实现。 这类似于JMS提供的发布-订阅模型&#xff0c;但是消息保留在应用程序内&#xff0c;而不是在外部广播。 EventBus允许您在程序中创建对象…

apscheduler -定时任务

https://apscheduler.readthedocs.io/en/latest/userguide.html 简单的使用方式为&#xff1a; from apscheduler.schedulers.blocking import BlockingScheduler sched BlockingScheduler()sched.add_job(ff_task,cron,hour0-1,8-23,minute28)sched.add_job(avor_task, cron,…

java迭代器 异常_java迭代器失效 | 学步园

今天在测试代码的时候出现一个异常ConcurrentModificationException&#xff0c;该异常网上很多解决方案以及解释&#xff0c;但我还是再记录一遍吧。代码抽象出来是这样的&#xff1a;import java.util.ArrayList;import java.util.List;public class Test {public static voi…

CSS3中的圆角边框属性详解(border-radius属性)

实例向 div 元素添加圆角边框&#xff1a;div{border:2px solid;border-radius:25px;}页面底部有更多实例。浏览器支持IEFirefoxChromeSafariOperaIE9 、Firefox 4 、Chrome、Safari 5 以及 Opera 支持 border-radius 属性。定义和用法border-radius 属性是一个简写属性&#x…

83.const与类

const常量对象,无法改变数据,只能引用尾部带const方法类的成员如果是const,可以默认初始化,也可以构造的初始化,不可在构造函数内部初始化类中的const成员,无法直接修改,可以间接修改类的成员函数const三种情形:1.返回值const,2.返回常量,3.参数const,可读不可写,尾部const,常量…

使用Apache CXF开发RESTful服务

介绍 如您所知&#xff0c;有两种开发Web服务的方法 简单对象访问协议&#xff08;SOAP&#xff09; 代表性状态转移&#xff08;REST&#xff09; 在继续学习如何使用Apache CXF创建基于REST的Web服务之前&#xff0c;我们将了解什么是REST。 REST不是一项技术&#xff0c;…

CTU 2017 J - Punching Power (二分图匹配)

题意&#xff1a; 公园管理层最终决定在公园的各个战略位置安装一些流行的拳击机。实际上&#xff0c;为了弥补以前缺少的机器&#xff0c;他们决定安装尽可能多的机器。令人惊讶的是&#xff0c;公园不会被新机器堵塞&#xff0c;因为对于机器的位置存在一些非常严重的法律限制…

UI设计师必知:link和@import引用css文件方法的区别

<link>元素所参考的样式用户可以自由的选择加以改变&#xff0c;而导入的样式表单就自动的与剩下的样式表融合在一起了CSS与HTML文档结合的4中方法&#xff1a;1 使用<link>元素链接到外部的样式文件2 在<head>元素中使用"style"元素来指定3 使用C…

javascript mqtt 发布订阅消息

js client使用paho-mqtt&#xff0c;官网地址&#xff1a;http://www.eclipse.org/paho/&#xff0c;参考http://www.eclipse.org/paho/clients/js/官网给出例子Getting Started&#xff0c;写出下面简单的测试代码。 <!DOCTYPE html> <html> <head> <met…

linux java jar war_简单介绍Java 的JAR包、EAR包、WAR包区别

WAR包WAR(Web Archive file)网络应用程序文件&#xff0c;是与平台无关的文件格式&#xff0c;它允许将许多文件组合成一个压缩文件。War专用于Web方面。大部分的JAVA WEB工程&#xff0c;都是打成WAR包进行发布的。War是一个web模块&#xff0c;其中需要包括WEB-INF&#xff0…

Spring Web应用程序的最大缺陷

在其应用程序中使用Spring Framework的开发人员很好地谈论了依赖注入的好处。 不幸的是&#xff0c;他们并不是很好地利用它的好处&#xff0c;例如单一负责的原则和在应用程序中关注点的分离 。 如果我们看一下任何基于Spring的Web应用程序&#xff0c;很可能会通过使用以下常…

用C++调用tensorflow在python下训练好的模型(centos7)

本文主要参考博客https://blog.csdn.net/luoyexuge/article/details/80399265 [1] bazel安装参考:https://blog.csdn.net/luoyi131420/article/details/78585989 [2] 首先介绍下自己的环境是centos7&#xff0c;tensorflow版本是1.7&#xff0c;python是3.6&#xff08;anacond…