透视变换–鸟瞰图

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

透视变换–鸟瞰图

透视变换–鸟瞰图_单例设计模式–鸟瞰相关推荐

  1. 单例模式示例_单例设计模式示例

    单例模式示例 本文是我们名为" Java设计模式 "的学院课程的一部分. 在本课程中,您将深入研究大量的设计模式,并了解如何在Java中实现和利用它们. 您将了解模式如此重要的原因 ...

  2. 反射和内省_单例设计模式–内省和最佳实践

    反射和内省 定义: Singleton是" 四人帮"设计模式的一部分,它属于创新设计模式. 在本文中,我们将更深入地研究Singleton模式的用法. 就建模而言,它是最简单的设计 ...

  3. 多个if用什么设计模式_抽丝剥茧——单例设计模式

    单例设计模式 兄弟们好,今天是最后一个设计模式了,也是我们最熟悉的单例设计模式,可以说这个设计模式是我们最先接触到的设计模式了.想当年学习JavaSE的时候,老师聊到一个「饿汉式和懒汉式」,我还纠结了 ...

  4. java 单例设计_Java 之单例设计模式

    设计模式: 对问题行之有效的解决方式, 其实它是一种思想. 单例设计模式 解决的问题:就是可以保证一个类在内存中的对象唯一性. 即单个实例. 比如对于A 和 B 两个程序使用同一个配置信息对象时, A ...

  5. Java查漏补缺(08)关键字:static、单例设计模式、理解main方法、类的成员之四:代码块、final关键字、抽象类、接口、内部类、枚举类、注解、包装类

    Java查漏补缺(08)关键字:static.单例设计模式.理解main方法.类的成员之四:代码块.final关键字.抽象类.接口.内部类.枚举类.注解.包装类 本章专题与脉络 1. 关键字:stat ...

  6. python基础知识整理 第七节:单例设计模式、异常、模块、包、制作模块、文件

    1.单例设计模式 单例设计模式就是为对象在内存中分配空间的时候,永远只会返回一个唯一的固定的内存空间.这样就能保证在内存中这个类的对象只有唯一的一份,这个就叫做单例.(为对象分配空间使用的是内置方法_ ...

  7. 单例设计模式详解。。。。。。。。。。。

    public class Demo01 { public static void main(String[] args) { // TODO Auto-generated method stub /* ...

  8. 对于java程序语言的单例设计模式讲解

    1.设计模式:解决某一类问题最行之有效的方法.(java中有23种通用设计模式) 单例设计模式:解决一个类在内存中只存在一个对象. 2.单例设计模式有两种方式: 1)饿汉式 先初始化对象.当类一进内存 ...

  9. java软件设计模式只单例设计模式

    概述 设计模式(Design pattern)是一套被反复使用.多数人知晓的.经过分类编目的.代码设计经验的总结.使用设计模式是为了可重用代码.让代码更容易被他人理解.保证代码可靠性. 毫无疑问,设计 ...

最新文章

  1. ssm框架app管理平台_后端程序员跨平台应用的前端框架uni-app初探
  2. 实现自己的轻量级http调用工具
  3. 键盘录入学生信息,保存到集合中
  4. 动态生成表格、隐藏表格、选中删除任意行、jquery、输入验证
  5. java中finalizer终结方法学习心得
  6. java每过一段时间执行一次代码(方法)
  7. gitbook使用实录
  8. 网络人的未来分享讲义_酒品看人品,未来酱分享饮酒识人技巧!谁是你值得深交的人?...
  9. 【angularjs】pc端使用angular搭建项目,实现导出excel功能
  10. 软考高项-项目知识管理体系
  11. EasyRoad3D使用手册
  12. 使用replaceAll()方法替换字符串中的反斜杠:左斜杠(\)和右斜杠(/)
  13. 古典问题:有一对兔子,从出生后第3个月起每个月都生一对兔子,小兔子长到第三个月后每个月又生一对兔子,假如兔子都不死,问每个月的兔子总数为多少?
  14. python中跳过本次循环的语句是_Python跳过for循环
  15. 有特点的3D网络拓扑图
  16. java中级工程师所需的技能_java工程师简历专业技能填写样本
  17. 蚂蚁金服 CTO 新加坡演讲:小蚂蚁是如何“爬”上区块链的?
  18. 电脑连接热点无internet访问权限_电脑连接无线网,显示无网络访问权限
  19. 20211030.剪绳子
  20. 增强网络口碑营销效果的5个方法

热门文章

  1. Java JVM总结
  2. JS获取自定义属性data-*值与dataset
  3. Java中的垃圾回收与对象生命周期
  4. 乐观锁的一种实现方式——CAS
  5. 一个诡异的可见性问题
  6. Class类中的getEnclosing*、getDeclared*与getDeclaringClass的区别
  7. 【LSB】图片隐写文档大纲
  8. java中判断 101-200 之间有多少个素数,并输出所有的素数
  9. Ajax基本案例详解之$.getjson的实现
  10. 28. 实现 strStr()---LeetCode---JAVA(今天又是一行超人)