如何确保JVM中只有一个类实例?

时间:2015-01-05 15:36:23

标签: java jvm

我正在开发一个设计模式,我想确保这里只是Java虚拟机中一个类的一个实例,通过一个点汇集一些资源的所有请求,但我不知道它是否是可能的。

我只能想到一种计算类实例的方法,并在创建第一个实例后销毁所有实例。

这是一种正确的方法吗?如果没有,还有其他方法吗?

9 个答案:

答案 0 :(得分:28)

使用单件模式。最简单的实现包括private constructorfield来保存其结果,以及static访问器方法,其名称为getInstance()

私有字段可以在静态初始化程序块中分配,或者更简单地使用初始化程序分配。 getInstance()方法(必须是公共的)然后只返回此实例

public class Singleton {
    private static Singleton instance;

    /**
     * A private Constructor prevents any other class from
     * instantiating.
     */
    private Singleton() {
        // nothing to do this time
    }

    /**
     * The Static initializer constructs the instance at class
     * loading time; this is to simulate a more involved
     * construction process (it it were really simple, you'd just
     * use an initializer)
     */
    static {
        instance = new Singleton();
    }

    /** Static 'instance' method */
    public static Singleton getInstance() {
        return instance;
    }

    // other methods protected by singleton-ness would be here...
    /** A simple demo method */
    public String demoMethod() {
        return "demo";
    }
}

请注意在getInstance()方法中使用“延迟评估”的方法 在Design Patterns中提倡,在Java中不是必需的,因为Java已经使用了“懒惰” 加载。“你的单例类可能不会被加载,除非它getInstance() 被称为,所以没有必要试图推迟单身人士的建设,直到需要它为止 通过getInstance()测试null的单例变量并创建单例 那里。

使用此类同样简单:只需获取并保留引用,并在其上调用方法:

public class SingletonDemo {
    public static void main(String[] args) {
        Singleton tmp = Singleton.getInstance();
        tmp.demoMethod();
    }
}

一些评论员认为单身人士也应提供公开决赛 只抛出异常的clone()方法,以避免“作弊”的子类 clone()单身人士。但是,显然只有一个私有构造函数的类 不能被分类,所以这种偏执似乎没有必要。

答案 1 :(得分:5)

您需要Singleton模式。有excellent discussion如何正确实现这一点。如果你这样做,那么只有一个类的实例。

基本上你要做的是创建一个类,在静态级别保存该类的单个实例化对象,并提供一个静态访问器来获取它(getInstance()或类似的)。使构造函数最终,这样人们就无法创建自己的实例。上面的链接对如何做到这一点有很多很好的建议。

答案 2 :(得分:5)

这是众所周知的Singleton模式:您可以按如下方式实现:

public class SingletonClass {

    //this field contains the single instance every initialized.
    private static final instance = new SingletonClass();

    //constructor *must* be private, otherwise other classes can make an instance as well
    private SingletonClass () {
        //initialize
    }

    //this is the method to obtain the single instance
    public static SingletonClass getInstance () {
        return instance;
    }

}

然后使用:

调用实例(就像构建非单例一样)
SingletonClass.getInstance();

但在文献中, Singleton 通常被认为是bad design idea。当然,这在某种程度上取决于具体情况,但大多数程序员都反对。只说出来,不要向信使开枪......

答案 3 :(得分:5)

有一种思想流派认为单身人士模式实际上是一种反模式。

考虑到你只希望拥有A类的A类,那么另一种选择是拥有一个构建器或工厂类,它本身限制了A类对象数量的创建,并且可以通过一个简单的计数器。 优点是A级不再需要担心,它专注于其真正的目的。每个使用它的类不再需要担心它是单例(不再需要getInstance()调用)。

答案 4 :(得分:4)

使用枚举。在Java中,enum是创建单例的唯一真正方法。私有构造函数仍然可以通过反射来调用。

有关详细信息,请参阅此StackOverflow问题: Implementing Singleton with an Enum (in Java)

讨论: http://javarevisited.blogspot.com/2012/07/why-enum-singleton-are-better-in-java.html

答案 5 :(得分:3)

  

我只能想到一种计算类实例的方法,并在创建第一个实例后销毁所有实例。这是一种正确的方法吗?如果没有,还有其他方法吗?

正确的技术方法是将类的所有构造函数声明为private,以便类的实例只能由类本身创建。然后你只编写类来创建一个实例。

其他答案显示了实现这一目标的一些方法,根据" Singleton"设计模式。但是,实现像这样的单例有一些缺点,包括使编写单元测试变得更加困难。

答案 6 :(得分:2)

我更喜欢懒惰的单例类,它会覆盖readResolve方法。

  

对于Serializable和Externalizable类,readResolve方法允许类在返回到调用者之前替换/解析从流中读取的对象。通过实现readResolve方法,类可以直接控制自己反序列化的实例的类型和实例。

使用/Initialization-on-demand_holder_idiom的懒惰单身人士:

public final class  LazySingleton {
    private LazySingleton() {}
    public static LazySingleton getInstance() {
        return LazyHolder.INSTANCE;
    }
    private static class LazyHolder {
        private static final LazySingleton INSTANCE = new LazySingleton();
    }
    private Object readResolve()  {
        return LazyHolder.INSTANCE;
    }
}

主要提示:

  1. final关键字禁止通过子类化扩展此类
  2. private构造函数禁止在调用者类中使用new运算符创建直接对象
  3. readResolve禁止在对象反序列化期间创建多个类实例

答案 7 :(得分:1)

为此您需要使用单例模式,我只是发布了一个可能对您有用的演示代码。

例如:如果我只想要一个这个Connect类的对象:

public final class Connect {

    private Connect() {}

    private volatile static Connect connect = null;

    public static Connect getinstance() {
        if(connect == null) {
            synchronized (Connect.class) {
                connect = new Connect();
            }
        }
        return connect;
    }
}

此处构造函数是私有的,因此没有人可以使用new关键字来创建新实例。

答案 8 :(得分:0)

class A{
    private A(){

    }
    public static A creator(A obj){
        A ob=new A();
        return ob;
    }
    void test(){
        System.out.println("The method is called");
    }
}

class Demo{
    public static void main(String[] args){
        A ob=null;
        ob=A.creator(ob);
        ob.test();
    }
}