透视变换–鸟瞰图_单例设计模式–鸟瞰

透视变换–鸟瞰图

几天前,当我回到家乡时,我的一位来自同事的准青年参加了一家跨国公司的采访,在采访过程中受了重伤。 我的意思是,由于面试小组提出了一些难题,他无法使面试合格。 当我回到班加罗尔时,他分享了他在技术面试中遇到的尴尬处境。 根据他今天的经验,我正在撰写有关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处的锁,因为实例仍然为空。 但是,由于线程1持有该锁,因此线程2在// Mark – 1处阻塞。

线程2被线程1抢占。

执行线程1,并且由于在// Mark – 2处instance仍然为null,因此创建了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。 静态类LazyHolder仅在对Something类调用静态方法getInstance时执行,并且第一次发生这种情况时,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/347977.shtml

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

相关文章

pandas整表写入excel指定位置_当Python遇到Excel,一个能打的都没有~

几乎人人都在用Excel&#xff0c;这种极易上手的数据分析工具无处不在&#xff0c;一旦你掌握了窍门&#xff0c;它就变得非常强大。而Python通常被认为更具有挑战性&#xff0c;但能做的事也更多。当Python遇到Excel会发生什么&#xff1f;本文将告诉你&#xff0c;这三项Exce…

linux用户密码转换为明文,Linux运维知识之linux下抓取内存中明文密码mimipenguin

本文主要向大家介绍了Linux运维知识之linux下抓取内存中明文密码mimipenguin&#xff0c;通过具体的内容向大家展现&#xff0c;希望对大家学习Linux运维知识有所帮助。下载地址&#xff1a;https://github.com/huntergregal/mimipenguin 基本使用语法&#xff1a;rootkali:~/e…

无需再忙了:Lambda-S3缩略图,由SLAppForge Sigma钉牢!

如果你还没有注意到了&#xff0c;我最近被唠叨试图开始使用时&#xff0c;我遭遇了陷阱AWSλ-S3正式例子 。 虽然大多数这些愚蠢的错误的指责是对我自己的懒惰&#xff0c;过度自尊和缺乏对细节的关注&#xff0c;我个人觉得&#xff0c;在开始与一家领先的无服务器供应商不应…

wap(dopra linux )命令,运营商定制的华为光猫Telnet命令恢复华为界面

首先Telnet连接光猫Welcome Visiting Huawei Home GatewayCopyright by Huawei Technologies Co., Ltd.Login:rootPassword:User name or password is wrong, please try it again!Login:rootPassword:adminWAP>suERROR::Command is not existedWAP>lsERROR::Command is …

请简述gouraud光照模型_《计算机图形学》试卷及答案

一、填空题(每空0.5分&#xff0c;共 1 0 分)1、 计算机图形学中的图形是指由点、线、面、体等 和明暗、灰度(亮度)、色彩等 构成的&#xff0c;从现实世界中抽象出来的带有灰度、色彩及形状的图或形。 2、 一个计算机图形系统至少应具有 、 、输入、输出、 等基本功能。3、 常…

linux下运行yolo,Ubuntu下CPU/GPU模式YOLOv3代码运行

YOLO是近几年物体检测主要算法之一&#xff0c;2018年已发展到YOLOv3&#xff0c;是目前速度最快的物体检测算法&#xff0c;详细内容可查看YOLO主页。YOLO的主要优势在于基于纯C语言编写的DarkNet&#xff0c;可查看DarkNet主页&#xff0c;不需要其他依赖库&#xff0c;跨平台…

sqlyog设置自动补全_Visual Studio Code 最好的功能、插件和设置

Visual Studio Code 是由 Microsoft(微软) 发布的一个免费的&#xff0c;开源的跨平台文本编辑器。他们基于在线编辑 Visual Studio Online (代号为 “Monaco”)&#xff0c;并结合 GitHub 的 Electron 实现的一个跨平台编辑器。他们在为程序员创建一个快速且高效工作环境方面取…

ejb 示例 2018_Java EE EJB拦截器教程和示例

ejb 示例 2018在此示例中&#xff0c;我们将看到如何在EJB中使用拦截器并使用简单的Web应用程序对其进行测试。 1.简介 顾名思义&#xff0c;当您想拦截对EJB方法的调用时&#xff0c;将使用拦截器。 如果为Bean声明一个拦截器&#xff0c;则每次调用该Bean的方法时&#xff0…

linux怎么使用串口线,如何在Linux下使用USB转串口线,鼎力告诉您

亲爱的朋友&#xff0c;如果您使用的Linux的系统&#xff0c;应该怎么来使用我们的鼎力的USB转串口线呢&#xff1f;下面就来告诉您吧&#xff01;用Linux&#xff0c;不需要另外安装USB转串口驱动!前提是你要先安装minicom输入命令&#xff1a;dmesg| grep usb&#xff0c;看到…

stlink 升级固件以后失败_STM32固件升级的一点经验

上面理论实践当初花了3天时间弄完的&#xff0c;但是&#xff0c;当你真正做项目的时候&#xff0c;你会发现&#xff0c;只有上面的这些知识还不够&#xff0c;还有更多的细节要去处理&#xff1a;APP程序怎么跳转到BootLoader程序&#xff1f;APP程序和BootLoader之间是否会互…

linux搭建Django环境,Linux (ubuntu 12.04)下搭建Python Django环境

1. 检查python是否安装&#xff1a;直接在shell里输入python&#xff0c;如果已经安装了python&#xff0c;即可进入python bash&#xff0c;并看到版本号(如Python 2.7.3)——在ubuntu中python应该是已经默认安装好了2. 安装Django:sudopythonsetup.pyinstall检查Django是否安…

comsol分析时总位移代表什么_超弹性材料模型的压缩分析

为了表征超弹性材料&#xff0c;需要进行各种测试获取实验数据&#xff0c;包括承受单轴拉伸和压缩、双轴拉伸和压缩以及扭转测试。今天&#xff0c;我们向大家介绍如何使用通过单轴和双轴测试获得的拉伸和压缩测试数据&#xff0c;模拟由弹性泡沫材料制成的球体的压缩。通过案…

linux函数计时,Linux 中的计时——gettimeofday函数

&#xff11;.使用C语言进行计时在用户空间中可以使用C语言函数gettimeofday 得到时间&#xff0c;它的调用格式是&#xff1a;#include int gettimeofday(struct timeval *tv, struct timezone *tz);int settimeofday(const struct timeval *tv , const struct timezone *tz);…

由对称性知定点一定在x轴上_线上优秀教学案例(九)|计算机科学与工程学院刘钊:“延期不延教”之“1+X课堂”...

【编者按】受新冠肺炎疫情影响&#xff0c;按照教育部和河北省教育厅统一部署&#xff0c;学校延迟春季开学时间。为最大程度减少疫情和延期开学对我校教育教学工作的影响&#xff0c;本学期&#xff0c;我校以线上教学的形式拉开序幕。面对新的教学模式&#xff0c;各学院、广…

cxf restful_使用Apache CXF开发RESTful服务

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

linux adc测试程序,基于Qt4.7的ADC测试程序

所使用的开发板&#xff1a;友善之臂tiny6140widget.h#ifndef WIDGET_H#define WIDGET_H#include #include namespace Ui {class Widget;}class TMainForm : public QWidget{Q_OBJECTpublic:TMainForm(QWidget * parent 0, const char * name 0, Qt::WFlags f 0);virtual ~T…

和显卡驱动要配套吗_天天学渲染,你的显卡驱动用对了吗?

大家好&#xff0c;最近一直有很多人和我反馈说&#xff0c;自己在使用Octane或者Redshift渲染器渲染的时候经常容易崩溃&#xff0c;甚至是闪退了&#xff0c;其实这个问题有很大一部分原因是由于你的显卡驱动没用对导致的。接下来就和大家说下如何解决。首先我们大部分人买了…

网络研讨会:Java 9的第一印象–构建可伸缩企业应用程序的新方法

在此网络研讨会上听我们对新Java版本的一些初步想法 Java 9的新版本引起了很多炒作。在Mark Reinhold领导的专门团队的带领下&#xff0c;将Java平台迁移到模块花费了近十年的辛苦工作。 除了备受期待的Project Jigsaw&#xff0c;此版本还包含许多其他令人兴奋的功能。 现在是…

linux cat 文本颜色,linux文本文件查看、展示命令 :cat head tail grep more less nl

linux文本文件查看、显示命令 :cat head tail grep more less nllinux文本文件查看、显示命令 :cat head tail grep more less nl1、cat 显示文件内容命令。命令格式&#xff1a;#cat [[选项]] 常用选项:-b,计算所有非空输出行&#xff0c;开始行编号为1。-n&#xff0c;计算…

noj数据结构稀疏矩阵的加法十字链表_一个算法毁了一款好游戏?算法和数据结构到底有多重要?...

来源 | 异步前段时间大火的国产游戏——《太吾绘卷》&#xff0c;由于创新的玩法和精良的制作一度广受好评&#xff0c;然而随着玩家游戏的深入和时长的积累&#xff0c;发现该游戏在玩的过程中游戏外的问题很多很多。首先是存档速度慢&#xff0c;然后是密集的计算导致功耗大量…