Java单例设计模式:问题

Java单例设计模式:问题,java,spring,design-patterns,singleton,Java,Spring,Design Patterns,Singleton,我最近接受了一次采访,他问我关于单例设计模式的问题,关于它们是如何实现的,我告诉他,使用静态变量和静态方法,我们可以实现单例设计模式 他似乎对答案半满意,但我想知道 我们可以用多少种不同的方法 实现单例设计模式 在爪哇 Singleton对象的范围是什么?它在JVM中实际是如何工作的?我知道我们总是会有一个Singleton对象的实例,但该对象的实际作用域是什么,是在JVM中还是如果JVM中有多个应用程序在运行,而不是JVM中每个上下文的作用域,我真的很困惑,无法给出令人满意的解释 最后,他问是

我最近接受了一次采访,他问我关于单例设计模式的问题,关于它们是如何实现的,我告诉他,使用静态变量和静态方法,我们可以实现单例设计模式

他似乎对答案半满意,但我想知道

  • 我们可以用多少种不同的方法 实现单例设计模式 在爪哇
  • Singleton对象的范围是什么?它在JVM中实际是如何工作的?我知道我们总是会有一个Singleton对象的实例,但该对象的实际作用域是什么,是在JVM中还是如果JVM中有多个应用程序在运行,而不是JVM中每个上下文的作用域,我真的很困惑,无法给出令人满意的解释
  • 最后,他问是否有可能使用带有集群的单例对象,并解释一下,当我们调用Bean Factory获取对象时,有没有办法让Spring不实现单例设计模式
  • 任何关于单例的输入都将受到高度赞赏,在处理单例时,需要记住的主要事项是什么


    谢谢

    Singleton通常是通过一个静态实例对象(
    private SingletonType SingletonType.instance
    )来实现的,该对象通过一个静态的
    SingletonType SingletonType.getInstance()
    方法延迟实例化。使用单体有很多陷阱,事实上,许多人认为单体是设计反模式。考虑到有关Spring的问题,面试官可能不仅想了解单身人士,还想了解他们的陷阱,以及解决这些陷阱的方法,即依赖注入。您可能会发现页面上的视频特别有助于理解单例的缺陷以及DI如何解决这一问题。

    一个静态字段可以在一个JVM中多次出现-通过使用不同的类加载器,可以多次加载和初始化同一个类,但是每个类都是独立存在的,JVM将结果加载的类视为完全不同的类

    我认为Java程序员不应该在意,除非他正在编写一些框架。“每个虚拟机一个”是一个足够好的答案。人们经常这样说,严格来说,他们说的是“每个类加载器一个”

    每个集群可以有一个单例吗?这是一场概念游戏。我不希望面试官这样说

  • 这是标准方法,你已经介绍过了。而且,大多数依赖注入方案都有某种方式将类标记为单例;这样,该类看起来就像其他类一样,但是框架确保当您注入该类的实例时,它始终是同一个实例

  • private Object readResolve()  { 
        return instance; 
    }
    
  • 这就是它变得毛茸茸的地方。例如,如果该类在Tomcat应用程序上下文中初始化,那么单例实例的生存期将绑定到该上下文。但是很难预测类将在哪里初始化;所以最好不要做任何假设。如果您想绝对确保每个上下文只有一个实例,您应该将其绑定为ServletContext的一个属性。(或者让依赖项注入框架来处理它。)

  • --

  • 我不确定我是否理解这个问题——但是如果您正在谈论在多个集群节点之间共享一个单例实例,那么我认为EJB使这成为可能(通过远程bean),尽管我从未尝试过。不知道春天是怎么过的


  • 有几种方法可以在Java中实现单例模式:

    // private constructor, public static instance
    // usage: Blah.INSTANCE.someMethod();
    public class Blah {
        public static final Blah INSTANCE = new Blah();
        private Blah() {
        }
        // public methods
    }
    
    // private constructor, public instance method
    // usage: Woo.getInstance().someMethod();
    public class Woo {
        private static final Woo INSTANCE = new Woo();
        private Woo() {
        }
        public static Woo getInstance() {
            return INSTANCE;
        }
        // public methods
    }
    
    // Java5+ single element enumeration (preferred approach)
    // usage: Zing.INSTANCE.someMethod();
    public enum Zing {
        INSTANCE;
        // public methods
    }
    
    给出上述示例,每个类加载器将有一个实例

    关于在集群中使用单个实例……我不确定“使用”的定义是什么……采访者是否暗示在集群中创建了单个实例?我不确定这是否有意义


    最后,在spring中定义一个非单例对象只需通过singleton=“false”属性即可

    单例的作用域是它在类加载器树中的节点。它包含类加载器,任何子类加载器都可以看到单例

    理解这个作用域的概念很重要,尤其是在具有复杂类加载器层次结构的应用程序服务器中

    例如,如果在应用程序服务器的系统类路径上的jar文件中有一个库,并且该库使用了一个单例,那么对于部署到应用程序服务器中的每个“应用程序”,该单例(很可能)都是相同的。这可能是好事,也可能不是好事(取决于图书馆)

    类加载器,IMHO,是Java和JVM中最重要的概念之一,单例就是其中之一,所以我认为Java程序员“关心”是很重要的

    3:最后,他问是否可以使用带有集群的Singleton对象,并解释一下,当我们调用Bean Factory获取对象时,有没有办法让Spring不实现Singleton设计模式

    如果没有技术背景,这个问题的第一部分很难回答。如果集群平台能够像调用本地对象一样对远程对象进行调用(例如,在引擎盖下使用RMI或IIOP的EJB可能),则可以这样做。例如,驻留JVM的单例对象可以是集群范围的单例对象的代理,该单例对象最初通过JNDI或其他方式定位/连接。但是集群范围的单例是一个潜在的瓶颈,因为对其中一个单例代理的每次调用都会导致对单个远程对象的(昂贵的)RPC

    问题的第二部分是Springbean工厂可以配置不同的作用域。默认情况下是单例(作用域在webapp级别),但它们也可以是会话或请求作用域,或者应用程序可以定义自己的
    public class SingletonObject
    {
      private SingletonObject()
      {
        // no code req'd
      }
    
      public static SingletonObject getSingletonObject()
      {
        if (ref == null)
            // it's ok, we can call this constructor
            ref = new SingletonObject();        
        return ref;
      }
    
      public Object clone()
        throws CloneNotSupportedException
      {
        throw new CloneNotSupportedException(); 
        // that'll teach 'em
      }
    
      private static SingletonObject ref;
    }
    
    public final class Singleton{
        private static final Singleton instance = new Singleton();
    
        public static Singleton getInstance(){
            return instance; 
        }
        public enum EnumSingleton {
            INSTANCE;   
        }   
        public static void main(String args[]){
            System.out.println("Singleton:"+Singleton.getInstance());
            System.out.println("Enum.."+EnumSingleton.INSTANCE);
            System.out.println("Lazy.."+LazySingleton.getInstance());
        }
    }
    final class LazySingleton {
        private LazySingleton() {}
        public static LazySingleton getInstance() {
            return LazyHolder.INSTANCE;
        }
        private static class LazyHolder {
            private static final LazySingleton INSTANCE = new LazySingleton();
        }
    }
    
    private Object readResolve()  { 
        return instance; 
    }
    
    class Foo{
    
        // Initialized in first run
        private static Foo INSTANCE = new Foo();
    
        /**
        * Private constructor prevents instantiation from outside
        */
        private Foo() {}
    
        public static Foo getInstance(){
            return INSTANCE;
        }
    
    }
    
    class Bar{
    
        private static Bar instance;
    
        /**
        * Private constructor prevents instantiation from outside
        */
        private Bar() {}
    
        public static Bar getInstance(){
    
            if (instance == null){
                // initialized in first call of getInstance()
                instance = new Bar();
            }
    
            return instance;
        }
    }
    
    class Blaa{
    
        /**
         * Private constructor prevents instantiation from outside
         */
        private Blaa() {}
    
        /**
         * BlaaHolder is loaded on the first execution of Blaa.getInstance()
         * or the first access to SingletonHolder.INSTANCE, not before.
         */
        private static class BlaaHolder{
            public static Blaa INSTANCE = new Blaa();
        }
    
        public static Blaa getInstance(){
            return BlaaHolder.INSTANCE;
        }
    
    }