单例设计模式Java语言

4

我很困惑。我在Java中找到了许多单例设计模式的实现。其中之一是以下内容:

public class MySingleton {

    private static class Loader {
        static MySingleton INSTANCE = new MySingleton();
    }

    private MySingleton () {}

    public static MySingleton getInstance() {
        return Loader.INSTANCE;
    }
}

如此解释:https://stackoverflow.com。 现在,如果这个实现应该有效,为什么下面的代码却无法正常工作?

public class MySingleton {

    private static final MySingleton INSTANCE = new MySingleton();

    private MySingleton () {}

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

我搜索了Java如何处理初始化的相关内容,但没有找到任何显示后面的代码不起作用的信息。 相反,我发现了以下内容:stackoverflow.com,它指出每个静态初始化在调用类的静态方法之前发生,因此在调用访问INSTANCE(getInstance)的唯一方法时应初始化保存单例实例的静态字段。 所以,如果这段代码有效,为什么不使用这种更简单的单例设计模式呢?

1
后者是可行的:它是一个“急切”的单例,而前者是一个“懒惰”的单例。 - Andy Turner
2个回答

5
两种实现都可以作为单例的有效方法。
第一种使用了懒加载技术,也就是说单例只有在客户端调用getInstance()方法的时候才会被创建。
第二种使用了急加载技术,也就是说当MySingleton类被类加载器加载时,单例就会被创建。
实际上,这两种方法没有太大差别。因为通常getInstance()和单例类的加载是相互关联的。非常少有客户端类不通过getInstance()方法就引用单例类的情况。
所以,在一般情况下,使用更简洁的第二种方法(急加载)应该更好。

对于通过反射创建类的额外实例的担忧怎么样? - nasukkin
@nasukkin,无论哪种方式都没有解决这个问题,所以与这个问题无关。 - Andreas
@nasukkin 许多事情都可以通过反射来改变。没有mutators的字段,带有“final”修饰符的字段等等...如果您向客户提供组件,并且需要/想要保护类定义和对象状态,则使用枚举单例是可以的,但这肯定不足够。由于公共组件显然不是问题,引用此问题将带来更多混淆而不是其他事情。 - davidxxx

1
Java内置的单例模式实现是枚举enum。当您定义一个enum时,您声明和初始化(也称为“枚举”)了在运行时可能存在的所有实例。您原始问题提供的方法存在漏洞;聪明的用户可以创建新的未管理的单例类实例。(我建议阅读Joshua Bloch的《Effective Java》一书,了解如何完成此操作的示例。)
您应该考虑这样实现您的单例类:
public enum MySingleton {
    INSTANCE();

    /* delcare instance fields here. */

    /** constructor; give it params if you need to. */
    public MySingleton() {
        // initialize whatever you need here.
    }

    /* methods you'll use go here. */
}

当你需要使用单例模式时,只需引用MySingleton.INSTANCE即可。

语法错误:枚举构造函数的修饰符非法;只有private是允许的。 - Andreas
@Andreas,你遇到语法错误是因为你定义了一个枚举写成了INSTANCE;(没有括号)。 - taran95
@taran95 不,我得到语法错误是因为构造函数是“public”,只允许使用“private”,就像错误所说的那样 - Andreas
准确来说,package-private(默认访问修饰符)和private是被允许的。 - davidxxx

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