在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()将确保返回唯一的实例,即使对象是在程序的上一次运行中序列化的。