在Java中实现单例设计模式的有效方法是什么?
当前回答
public class Singleton {
private static final Singleton INSTANCE = new Singleton();
private Singleton() {
if (INSTANCE != null)
throw new IllegalStateException(“Already instantiated...”);
}
public synchronized static Singleton getInstance() {
return INSTANCE;
}
}
由于我们在getInstance之前添加了Synchronized关键字,因此在两个线程同时调用getInstance的情况下,我们避免了竞争条件。
其他回答
Java 5+中的线程安全:
class Foo {
private static volatile Bar bar = null;
public static Bar getBar() {
if (bar == null) {
synchronized(Foo.class) {
if (bar == null)
bar = new Bar();
}
}
return bar;
}
}
注意此处的挥发性修饰语。:)这很重要,因为如果没有它,JMM(Java内存模型)就不能保证其他线程看到其值的更改。同步不会处理这一点——它只序列化对该代码块的访问。
@Bno的回答详细介绍了Bill Pugh(FindBugs)推荐的方法,并且可以更好地进行论证。去阅读并投票支持他的答案。
这是如何实现一个简单的单例:
public class Singleton {
// It must be static and final to prevent later modification
private static final Singleton INSTANCE = new Singleton();
/** The constructor must be private to prevent external instantiation */
private Singleton(){}
/** The public static method allowing to get the instance */
public static Singleton getInstance() {
return INSTANCE;
}
}
这是如何正确地延迟创建单例:
public class Singleton {
// The constructor must be private to prevent external instantiation
private Singleton(){}
/** The public static method allowing to get the instance */
public static Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
/**
* The static inner class responsible for creating your instance only on demand,
* because the static fields of a class are only initialized when the class
* is explicitly called and a class initialization is synchronized such that only
* one thread can perform it, this rule is also applicable to inner static class
* So here INSTANCE will be created only when SingletonHolder.INSTANCE
* will be called
*/
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
有四种方法可以在Java中创建单例。
Eager初始化单例公共类测试{私有静态最终测试=新测试();专用测试(){}公共静态测试getTest(){回归试验;}}惰性初始化单例(线程安全)公共类测试{私人静态挥发性测试;专用测试(){}公共静态测试getTest(){if(测试==空){同步(Test.class){if(测试==空){test=新测试();}}}回归试验;}}Bill Pugh单例与持有者模式(最好是最好的一个)公共类测试{专用测试(){}私有静态类TestHolder{私有静态最终测试=新测试();}公共静态测试getInstance(){return TestHolder.test;}}枚举单例公共枚举MySingleton{实例;私有MySingleton(){System.out.println(“此处”);}}
确保你真的需要它。在谷歌上搜索“singleton反模式”,看看反对它的一些论点。
我想这并没有什么本质上的问题,但它只是一种公开某些全局资源/数据的机制,因此请确保这是最好的方法。特别是,我发现依赖注入(DI)更有用,特别是如果您也在使用单元测试,因为DI允许您将模拟资源用于测试目的。
根据用法,有几个“正确”答案。
自Java5以来,最好的方法是使用枚举:
public enum Foo {
INSTANCE;
}
在Java 5之前,最简单的情况是:
public final class Foo {
private static final Foo INSTANCE = new Foo();
private Foo() {
if (INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return INSTANCE;
}
public Object clone() throws CloneNotSupportedException{
throw new CloneNotSupportedException("Cannot clone instance of this class");
}
}
让我们复习一下代码。首先,你希望这门课是期末考试。在本例中,我使用了final关键字来让用户知道它是final。然后,需要将构造函数设为私有,以防止用户创建自己的Foo。从构造函数中抛出异常会阻止用户使用反射来创建第二个Foo。然后创建一个私有的静态final Foo字段来保存唯一的实例,并创建一个公共的静态Foo-getInstance()方法来返回它。Java规范确保只有在首次使用类时才调用构造函数。
当您有一个非常大的对象或大量的构造代码,并且还有其他可访问的静态方法或字段,这些方法或字段可能在需要实例之前使用,那么只有在需要时才需要使用惰性初始化。
您可以使用私有静态类来加载实例。代码将如下所示:
public final class Foo {
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
}
由于行private static final Foo INSTANCE=new Foo();仅在实际使用FooLoader类时执行,这会处理延迟实例化,并保证线程安全。
当您还希望能够序列化对象时,需要确保反序列化不会创建副本。
public final class Foo implements Serializable {
private static final long serialVersionUID = 1L;
private static class FooLoader {
private static final Foo INSTANCE = new Foo();
}
private Foo() {
if (FooLoader.INSTANCE != null) {
throw new IllegalStateException("Already instantiated");
}
}
public static Foo getInstance() {
return FooLoader.INSTANCE;
}
@SuppressWarnings("unused")
private Foo readResolve() {
return FooLoader.INSTANCE;
}
}
方法readResolve()将确保返回唯一的实例,即使对象是在程序的上一次运行中序列化的。
推荐文章
- 在流中使用Java 8 foreach循环移动到下一项
- 访问限制:'Application'类型不是API(必需库rt.jar的限制)
- 用Java计算两个日期之间的天数
- 如何配置slf4j-simple
- 在Jar文件中运行类
- 带参数的可运行?
- 我如何得到一个字符串的前n个字符而不检查大小或出界?
- 我可以在Java中设置enum起始值吗?
- Java中的回调函数
- c#和Java中的泛型有什么不同?和模板在c++ ?
- 在Java中,流相对于循环的优势是什么?
- Jersey在未找到InjectionManagerFactory时停止工作
- 在Java流是peek真的只是调试?
- Recyclerview不调用onCreateViewHolder
- 将JSON字符串转换为HashMap