单例模式 vs 公共静态 final 变量

4

我知道单例模式是这样实现的:

public class ClassName {
    private static ClassName instance;

    public static ClassName getInstance() {
        if (instance == null) {
            instance = new ClassName();
        }
        return instance;
    }

    private ClassName() {}
}

我想问的是为什么你不能像这样做:
public class ClassName {
    public static final ClassName instance = new ClassName();
    private ClassName() {}
}

这段代码行数更少,看起来实现的功能也和原来一样。当然,这里没有懒加载,但我并不认为懒加载会带来重大的好处。由于我经验有限,如果您能分享您的知识,我将不胜感激。


你的第二个解决方案很好,不懒惰。 - ZhongYu
4个回答

5

将单例实例初始化内联并让类加载器担心同步可能不是非常常见的做法,但绝对不是无人问津。

然而,通常的惯用语是将实例变量设置为私有,并通过getter返回它,以便其他代码不直接依赖于它。这样,如果将来您决定需要更高级的东西(比如您提到的延迟初始化),您可以轻松地重构您的代码而不会破坏API:

public class ClassName {
    private static final ClassName instance = new ClassName();

    public static ClassName getInstance() {
        return instance;
    }

    private ClassName() {}
}

我仍然不明白使用单例的优势。你总是可以在静态构造函数中初始化实例。 - Rakshith Ravi
2
如果这不是一个公共API,我们就不需要担心它;我们随时可以重构它。 - ZhongYu

2
你的第一个代码使用了带有 synchronized 关键字的延迟创建。这种代码的问题是,instance 变量可能在单例构造之前变为非空,并且可以在伪代码中解释,JRE 为创建实例而执行的操作:
mem = allocate() ;
instance = mem ;
ctorSingleton(instance)

如果多个线程同时访问getInstance方法,那么有可能会得到一个新实例,因为在Java中你有两条指令和JRE解释的伪代码需要三个。我认为你的第二个实现是好的,因为你确信它能正常工作,并且线程或同步问题很难修复。这来自于一篇法语文章:http://christophej.developpez.com/tutoriel/java/singleton/multithread/

1

这是我总是问 Singleton 爱好者的同样问题,迄今为止,我还没有听到令人满意的答案。人们说“全局变量是不好的”(我来自 C++ 世界,所以措辞是 C++ 特定的,但思想是相同的)。当我问他们那些对 Singleton 难舍难分的人,它究竟如何不同于全局变量时,我只听到含糊不清的回答。我个人认为,Singleton 只有一个好处——延迟初始化,这使得可以控制实例化的顺序。如果不需要,也就不需要 Singleton。


在Java中,加载通常是惰性的 - 一个类只有在第一次使用时才被加载(更准确地说,是初始化)。 - ZhongYu
getIntance() 是更多的抽象层;正如他们所说,每个问题的解决方案都是更多的抽象化 :) - ZhongYu

0
优点在于单例模式的编写者可以更新getter(getInstance)以具有更复杂的规则,而不会导致单例模式的用户必须重新编译其代码。这就是Mureinik所说的:“这样,如果将来您决定要使用更高级的功能(例如,懒惰初始化),则可以轻松地重构代码而不会破坏API”。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接