我想知道我的代码是否会产生真正的单例。我正在创建一个Android应用,并且所有活动都应该通过SyncApi类的一个实例访问我的API。 >
public class Api {

    private static SyncApi api = null;

    static {
        synchronized (api) {
            if (api == null) {
                api = new ApiFActory().getSyncApi();
            }
        }
    }

    public static SyncApi getInstance() {
        return api;
    }
}


评论

如果您不想懒惰,为什么不直接在声明中初始化SyncApi实例呢?不需要静态构造函数。 Java保证静态成员的初始化只会发生一次。

@Konrad,啊!那么那会有所帮助。我担心初始化(在罕见的多线程情况下)可能会发生多次。

我想指出的是,由于测试困难,单例现在被认为是反模式。另外,您还没有将构造函数设为私有,因此这实际上不是Singleton。

这里的同步可能无法正常工作,所有这些都在Joshua Bloch的Effective Java中进行了解释,或者在Wikipedia中提供了一些示例

@Athas Singletons被认为是一种反模式,因为它采用了封装和数据流控制这一不错的概念,并表示“嘿,让我们现在回到拥有全局变量的地方。”。

#1 楼

比尔·普格(Bill Pugh)在Java中有一个建立线程安全单例的惯用语:

进行显式同步和检查。这样做的好处是消除了微妙的比赛条件和多余的锁定的可能性。

评论


\ $ \ begingroup \ $
SyncApi类是如何定义的?我相信,如果Api和/或SyncApi不是最终版本,则此实现可能会被破坏。可以扩展这些类并以完全不受此代码控制的方式创建对象。 SyncApi的子类的每个实例显然也将是SyncApi的一个实例。基于枚举的解决方案不受此影响,因为所有枚举都是隐式最终的。
\ $ \ endgroup \ $
– toniedzwiedz
13年11月16日在20:51



\ $ \ begingroup \ $
@Tom实际上,我只是在某种程度上复制并粘贴了OP的代码,实际上我是说这两个类是一个类(即SyncApi = Api),所以这段代码是安全的(无法扩展Api)。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
13年11月18日在8:39

\ $ \ begingroup \ $
好吧,我想念私有构造函数是唯一的那个。最终不会为清晰起见。
\ $ \ endgroup \ $
– toniedzwiedz
13年11月18日在12:16

\ $ \ begingroup \ $
不要使用此代码,请使用aivikiss用户在答案中给出的基于枚举的安全单例成语。
\ $ \ endgroup \ $
–康拉德·鲁道夫(Konrad Rudolph)
19年7月31日在12:28

#2 楼

最好,最简单的方法是Joshua Bloch在《有效的Java第2版》中给出的。

br />

评论


\ $ \ begingroup \ $
这根本不是单例。
\ $ \ endgroup \ $
– mjcopple
2011年4月7日15:32

\ $ \ begingroup \ $
是的,很抱歉。枚举不同于单例。
\ $ \ endgroup \ $
– jjnguy
2011年4月7日15:43

\ $ \ begingroup \ $
为什么?枚举是类,不是吗?如果在上面的Api类上定义其他非静态方法,则它的行为将与单例完全相同,因为它是单例。
\ $ \ endgroup \ $
–romacafe
2011年4月7日在18:46

\ $ \ begingroup \ $
@rom,很好。我想看看这个例子。
\ $ \ endgroup \ $
– jjnguy
2011年4月9日在2:21

#3 楼

好吧,这绝对不是Singleton,因为此代码是合法的:
Api.api=new Api();

您至少应将api变量定义为final,至少应将private构造函数定义为避免实例化。

请参阅@Konrad Rudolph的解决方案。

#4 楼

如果您想要线程安全和懒惰,我完全同意Konrad Rudolph提供的答案。

这是一篇很好的文章,包含许多测试和示例:http://www.javaworld.com/javaworld/jw-04-2003/jw-0425 -designpatterns.html

但是两种方法都不能给您绝对的单例,因为不同的ClassLoader将创建不同的实例。这可能看起来几乎是不可能的情况,但是根据我的经验,我遇到了一个由这种情况引起的错误。 /communication-across-classloaders.xml

评论


\ $ \ begingroup \ $
是-他必须使用枚举来拥有绝对单例
\ $ \ endgroup \ $
– Mr_and_Mrs_D
13年7月21日在12:40

#5 楼

将api设置为null并不能帮助您发现NullPointerException

synchronized (api) // api == null, you should use Api.class


关于单例,请使用枚举。从Java 5开始,这是推荐的模式。现在,如果要延迟加载:

public class Api {

    private Api() {}

    private enum SingletonHolder {
        INSTANCE;

        private static final Api singleton = new Api();

        Api getSingleton() {
            return singleton;
        }
    }

    public Api api() {
        Api.SingletonHolder.INSTANCE.getSingleton();
    }
}


调用:

 api().logOn("jjnguy", "pa55w0rd");


您可以丢弃getSingleton()并直接在singleton内部访问Api

public class Api {

    private Api() {/*your costly init here*/}

    private enum SingletonHolder {
        INSTANCE;
        private static final Api singleton = new Api();
    }

    public Api api() {
        return Api.SingletonHolder.singleton;
    }
}


评论


\ $ \ begingroup \ $
您无需在枚举类声明中使用static和final关键字,因为enum类已经提供了static和final。
\ $ \ endgroup \ $
–Enes Unal
2014年10月1日12:46



\ $ \ begingroup \ $
@EnesUnal:正确(另请参见stackoverflow.com/a/253282/281545),已编辑
\ $ \ endgroup \ $
– Mr_and_Mrs_D
2014年10月1日下午13:31

#6 楼

我同意所有其他答案,但是正如您所说的,您的问题不是只有一个API实例,而是只有一个SyncApi实例。

您将无法阻止用户正在执行:

SyncApi api = new ApiFActory().getSyncApi();


您必须将SyncApi设置为Singleton或将其用法封装在Api类中,并且不能在您的Api之外访问它。

#7 楼

public class Singleton {
      private static Singleton instance = null;

      public static Singleton getInstance(){
         if(null == instance){
          synchronized(Singleton.class){
             //double check
               if(null == instance){
                instance = new Singleton();
              }
           }  
         }
         return instance;
       }

}


可用于确保仅创建单个实例。或只使用Enums。

评论


\ $ \ begingroup \ $
if(null == instance)Yoda风格,这听起来像。
\ $ \ endgroup \ $
– Mathieu Guindon♦
2014年1月20日,3:40