在Java中实现单子模式的有效方法是什么?[英] What is an efficient way to implement a singleton pattern in Java?

本文是小编为大家收集整理的关于在Java中实现单子模式的有效方法是什么?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

在Java中实现单身设计模式的有效方法是什么?

推荐答案

使用枚举:

public enum Foo {
    INSTANCE;
}

joshua bloch在他的有效的Java Reloaded中解释了这种方法 Google I/o 2008:链接到视频. Also see slides 30-32 of his presentation (effective_java_reloaded.pdf):

实现可序列化单例

的正确方法
public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

编辑: ."

其他推荐答案

取决于用法,有几个"正确"答案.

自Java 5以来,最好的方法是使用枚举:

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关键字来让用户知道它是最终的.然后,您需要将构造函数私有化,以防止用户创建自己的foo.从构造函数上抛出异常可阻止用户使用反射来创建第二个FOO.然后,您创建一个private static final Foo字段来保存唯一的实例,以及public static Foo getInstance()返回它的方法. Java规范确保仅在首次使用类时才调用构造函数.

当您有一个非常大的对象或重型施工代码和还具有其他可访问的静态方法或可能在需要实例之前使用的静态方法或字段,然后您才需要使用懒惰的初始化.

您可以使用private static class加载实例.然后,代码看起来像:

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();仅在实际使用了"沉睡器"时执行,因此可以照顾懒惰的实例,并且保证它是线程安全的.

当您还希望能够序列化对象时,您需要确保避免化不会创建副本.

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

其他推荐答案

免责声明:我刚刚总结了所有很棒的答案,并用我自己的话写了.


在实施单例时,我们有两个选择:

  1. 懒惰加载
  2. 早期加载

懒惰的加载增加了高架(老实说),因此,只有当您有一个非常大的对象或重型施工代码和时,才使用它也可能具有其他可访问的静态方法或可能是在需要实例之前使用,然后您才需要使用懒惰的初始化.否则,选择早期加载是一个不错的选择.

实现单例的最简单方法是:

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

一切都很好,除了它是一个早期的单身人士.让我们尝试懒惰的Singleton

class Foo {

    // Our now_null_but_going_to_be sole hero
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

到目前为止,一切都很好,但是我们的英雄在与多个邪恶的线程中独自战斗的时候无法生存,这些邪恶的线程想要许多我们的英雄实例. 因此,让我们保护它免受邪恶的多螺纹:

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

但实际上还不足以保护英雄!!!这是我们可以/应该做的最好的帮助我们的英雄:

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

这称为"双重检查的锁定成语".很容易忘记动荡的陈述,很难理解为什么需要. 有关详细信息: double-Checkecked Locking the Double-Checked Local offaration oferaration"

现在我们对邪恶的线程确定,但是残酷的序列化呢?我们必须确保即使没有创建新对象,我们也必须确保:

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // The rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

方法readResolve()将确保唯一的实例将返回,即使对象在我们程序的先前运行中序列化.

最后,我们为线程和序列化增加了足够的保护,但是我们的代码看起来笨重和丑陋.让我们改头换面:

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

是的,这是我们的英雄:)

由于该行private static final Foo INSTANCE = new Foo();仅在实际使用类FooLoader时执行,因此可以照顾懒惰的实例,并且保证它是线程安全的.

我们已经走了那么远.这是实现我们所做的一切的最佳方法是最好的方法:

public enum Foo {
    INSTANCE;
}

内部将被视为

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

就是这样!不再担心序列化,线程和丑陋的代码.另外,

这种方法在功能上等同于公共场地方法, 除了更简洁,提供序列化机械 免费,并为多重多重保证提供铁克的保证 即使面对复杂的序列化或 反射攻击.尽管这种方法尚未广泛采用,但 单元素枚举类型是实现单身人士的最佳方法.

-joshua bloch在"有效Java"中

现在,您可能已经意识到为什么枚举被视为实施单身人士的最佳方法,并感谢您的耐心等待:)

在我的 .

本文地址:https://www.itbaoku.cn/post/627430.html

问题描述

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

推荐答案

Use an enum:

public enum Foo {
    INSTANCE;
}

Joshua Bloch explained this approach in his Effective Java Reloaded talk at Google I/O 2008: link to video. Also see slides 30-32 of his presentation (effective_java_reloaded.pdf):

The Right Way to Implement a Serializable Singleton

public enum Elvis {
    INSTANCE;
    private final String[] favoriteSongs =
        { "Hound Dog", "Heartbreak Hotel" };
    public void printFavorites() {
        System.out.println(Arrays.toString(favoriteSongs));
    }
}

Edit: An online portion of "Effective Java" says:

"This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton."

其他推荐答案

Depending on the usage, there are several "correct" answers.

Since Java 5, the best way to do it is to use an enum:

public enum Foo {
   INSTANCE;
}

Pre Java 5, the most simple case is:

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

Let's go over the code. First, you want the class to be final. In this case, I've used the final keyword to let the users know it is final. Then you need to make the constructor private to prevent users to create their own Foo. Throwing an exception from the constructor prevents users to use reflection to create a second Foo. Then you create a private static final Foo field to hold the only instance, and a public static Foo getInstance() method to return it. The Java specification makes sure that the constructor is only called when the class is first used.

When you have a very large object or heavy construction code and also have other accessible static methods or fields that might be used before an instance is needed, then and only then you need to use lazy initialization.

You can use a private static class to load the instance. The code would then look like:

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

Since the line private static final Foo INSTANCE = new Foo(); is only executed when the class FooLoader is actually used, this takes care of the lazy instantiation, and is it guaranteed to be thread safe.

When you also want to be able to serialize your object you need to make sure that deserialization won't create a copy.

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

The method readResolve() will make sure the only instance will be returned, even when the object was serialized in a previous run of your program.

其他推荐答案

Disclaimer: I have just summarized all of the awesome answers and wrote it in my own words.


While implementing Singleton we have two options:

  1. Lazy loading
  2. Early loading

Lazy loading adds bit overhead (lots of to be honest), so use it only when you have a very large object or heavy construction code and also have other accessible static methods or fields that might be used before an instance is needed, then and only then you need to use lazy initialization. Otherwise, choosing early loading is a good choice.

The most simple way of implementing a singleton is:

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        return INSTANCE;
    }
}

Everything is good except it's an early loaded singleton. Lets try lazy loaded singleton

class Foo {

    // Our now_null_but_going_to_be sole hero
    private static Foo INSTANCE = null;

    private Foo() {
        if (INSTANCE != null) {
            // SHOUT
            throw new IllegalStateException("Already instantiated");
        }
    }

    public static Foo getInstance() {
        // Creating only  when required.
        if (INSTANCE == null) {
            INSTANCE = new Foo();
        }
        return INSTANCE;
    }
}

So far so good, but our hero will not survive while fighting alone with multiple evil threads who want many many instance of our hero. So let’s protect it from evil multi threading:

class Foo {

    private static Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        // No more tension of threads
        synchronized (Foo.class) {
            if (INSTANCE == null) {
                INSTANCE = new Foo();
            }
        }
        return INSTANCE;
    }
}

But it is not enough to protect out hero, really!!! This is the best we can/should do to help our hero:

class Foo {

    // Pay attention to volatile
    private static volatile Foo INSTANCE = null;

    // TODO Add private shouting constructor

    public static Foo getInstance() {
        if (INSTANCE == null) { // Check 1
            synchronized (Foo.class) {
                if (INSTANCE == null) { // Check 2
                    INSTANCE = new Foo();
                }
            }
        }
        return INSTANCE;
    }
}

This is called the "double-checked locking idiom". It's easy to forget the volatile statement and difficult to understand why it is necessary. For details: The "Double-Checked Locking is Broken" Declaration

Now we are sure about evil threads, but what about the cruel serialization? We have to make sure even while de-serialiaztion no new object is created:

class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    private static volatile Foo INSTANCE = null;

    // The rest of the things are same as above

    // No more fear of serialization
    @SuppressWarnings("unused")
    private Object readResolve() {
        return INSTANCE;
    }
}

The method readResolve() will make sure the only instance will be returned, even when the object was serialized in a previous run of our program.

Finally, we have added enough protection against threads and serialization, but our code is looking bulky and ugly. Let’s give our hero a makeover:

public final class Foo implements Serializable {

    private static final long serialVersionUID = 1L;

    // Wrapped in a inner static class so that loaded only when required
    private static class FooLoader {

        // And no more fear of threads
        private static final Foo INSTANCE = new Foo();
    }

    // TODO add private shouting construcor

    public static Foo getInstance() {
        return FooLoader.INSTANCE;
    }

    // Damn you serialization
    @SuppressWarnings("unused")
    private Foo readResolve() {
        return FooLoader.INSTANCE;
    }
}

Yes, this is our very same hero :)

Since the line private static final Foo INSTANCE = new Foo(); is only executed when the class FooLoader is actually used, this takes care of the lazy instantiation, and is it guaranteed to be thread-safe.

And we have come so far. Here is the best way to achieve everything we did is best possible way:

public enum Foo {
    INSTANCE;
}

Which internally will be treated like

public class Foo {

    // It will be our sole hero
    private static final Foo INSTANCE = new Foo();
}

That's it! No more fear of serialization, threads and ugly code. Also ENUMS singleton are lazily initialized.

This approach is functionally equivalent to the public field approach, except that it is more concise, provides the serialization machinery for free, and provides an ironclad guarantee against multiple instantiation, even in the face of sophisticated serialization or reflection attacks. While this approach has yet to be widely adopted, a single-element enum type is the best way to implement a singleton.

-Joshua Bloch in "Effective Java"

Now you might have realized why ENUMS are considered as best way to implement a singleton and thanks for your patience :)

Updated it on my blog.