代码之家  ›  专栏  ›  技术社区  ›  Rachel

Java单例设计模式:问题

  •  17
  • Rachel  · 技术社区  · 15 年前

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

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

    1. 我们可以用多少种不同的方法 实现单例设计模式 在爪哇?
    2. Singleton对象的作用域是什么?它实际上是如何在JVM中工作的?我知道我们总是有一个Singleton对象的实例,但是这个对象的实际作用域是什么,是在JVM中,还是在JVM中有多个应用程序在运行,而不是在JVM中每个上下文的基础上,我真的很困惑,无法给出满意的解释?

    任何关于单例的输入都会被高度赞赏,在处理单例时,需要记住的主要事情是什么?

    谢谢。

    11 回复  |  直到 9 年前
        1
  •  15
  •   Tim Reddy    15 年前

    有几种方法可以在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”属性即可。

        2
  •  2
  •   Will Hartung    15 年前

    我不同意“无可挽回”。

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

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

    类加载器是Java和JVM中最重要的概念之一,而singleton就是其中之一,因此我认为Java程序员“关心”这一点很重要。

        3
  •  2
  •   emory    15 年前

    public enum Singleton { ONE_AND_ONLY_ONE ; ... members and other junk ... }
    

    至于更高级别的单例——也许我很傻——但我倾向于分发JVM本身(并限制类装入器)。那么枚举就足够了。

        4
  •  1
  •   Michael Aaron Safyan    15 年前

    Singleton通常通过一个静态实例对象来实现( private SingletonType SingletonType.instance )通过静态 SingletonType SingletonType.getInstance() Google Guice 页面特别有助于理解单例陷阱以及DI如何解决这个问题。

        5
  •  1
  •   Stephen C    15 年前

    3:最后他问我们是否可以使用带有集群的Singleton对象,有没有办法让Spring在调用Bean工厂获取对象时不实现Singleton设计模式?

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

    问题的第二部分是springbean工厂可以配置不同的作用域。默认情况下是单例(作用域在webapp级别),但也可以是会话或请求作用域,或者应用程序可以定义自己的作用域机制。

        6
  •  0
  •   irreputable    15 年前

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

    我们每个集群能有一个单子吗?那是个概念游戏。我不希望面试官这样说。

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

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

    3. --

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

        8
  •  0
  •   raja kolluru    15 年前

    Singleton是一种创造性的模式,因此控制对象实例化。创建单例将强制要求您自愿或非自愿地放弃对创建对象的控制,而是依赖某种方式获得对它的访问权。

    这可以通过使用静态方法、依赖注入或使用工厂模式来实现。手段是无关紧要的。对于普通的protected constructor()方法,consumer perforce需要使用静态方法来访问singleton。在DI的情况下,使用者自愿放弃对类实例化的控制,而是依赖DI框架将实例注入自身。

        9
  •  0
  •   st0le    15 年前

    以下代码来自 here

    Override 这个 clone 方法…维基百科 example

    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;
    }
    
        10
  •  0
  •   Community CDub    8 年前

    查询1:

    1. Normal Singleton :静态初始化
    2. 枚举
    3. Lazy Singleton :双重锁定单例(&:初始化-按需\u持有者\u惯用法单例

    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();
        }
    }
    

    相关SE问题:

    What is an efficient way to implement a singleton pattern in Java?

    问题2:

    ClassLoader . 如果要避免创建 Singleton 期间的对象 Serializaiton ,重写下面的方法并返回相同的实例。

    private Object readResolve()  { 
        return instance; 
    }
    

    实现集群级 单子 Terracotta Coherence 等。

        11
  •  0
  •   Partharaj Deb    8 年前

    单体设计模式的意图:

    • 访问它。
    • 首次使用”。

    我在这里展示了三种类型的实现。

    1. 实时初始化 (在第一次运行期间分配内存,即使您不使用它)

      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;
          }
      
      }
      
    2. 首次使用时初始化(或延迟初始化)

      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;
          }
      }
      
    3. 这是另一种类型的延迟初始化,但其优点是,此解决方案是线程安全的,不需要特殊的语言构造(即volatile或synchronized)。阅读更多信息 SourceMaking.com

      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;
          }
      
      }
      
    推荐文章