为了账号安全,请及时绑定邮箱和手机立即绑定

Java中带有参数的Singleton

/ 猿问

Java中带有参数的Singleton

qq_花开花谢_0 2019-10-15 14:00:23

我正在阅读Wikipedia上的Singleton文章,并且遇到了以下示例:


public class Singleton {

    // Private constructor prevents instantiation from other classes

    private Singleton() {}


    /**

     * SingletonHolder is loaded on the first execution of Singleton.getInstance() 

     * or the first access to SingletonHolder.INSTANCE, not before.

     */

    private static class SingletonHolder { 

        private static final Singleton INSTANCE = new Singleton();

    }


    public static Singleton getInstance() {

        return SingletonHolder.INSTANCE;

    }

}

虽然我真的很喜欢Singleton的行为方式,但是我看不到如何修改它以将参数合并到构造函数中。用Java执行此操作的首选方法是什么?我需要做这样的事情吗?


public class Singleton

{

    private static Singleton singleton = null;  

    private final int x;


    private Singleton(int x) {

        this.x = x;

    }


    public synchronized static Singleton getInstance(int x) {

        if(singleton == null) singleton = new Singleton(x);

        return singleton;

    }

}

谢谢!


编辑:我想我对使用Singleton的渴望已经引发了一场争论的风暴。让我解释一下我的动机,并希望有人可以提出一个更好的主意。我正在使用网格计算框架来并行执行任务。总的来说,我有这样的事情:


// AbstractTask implements Serializable

public class Task extends AbstractTask

{

    private final ReferenceToReallyBigObject object;


    public Task(ReferenceToReallyBigObject object)

    {

        this.object = object;

    }


    public void run()

    {

        // Do some stuff with the object (which is immutable).

    }

}

发生的事情是,即使我只是将对我的数据的引用传递给所有任务,但是当序列化任务时,数据会一遍又一遍地复制。我要做的是在所有任务之间共享对象。自然,我可以像这样修改类:


// AbstractTask implements Serializable

public class Task extends AbstractTask

{

    private static ReferenceToReallyBigObject object = null;


    private final String filePath;


    public Task(String filePath)

    {

        this.filePath = filePath;

    }


如您所见,即使在这里,我仍然遇到这样的问题:在传递第一个文件路径之后,传递不同的文件路径没有任何意义。这就是为什么我喜欢答案中张贴的商店的想法。无论如何,我没有在run方法中包含用于加载文件的逻辑,而是希望将此逻辑抽象为Singleton类。我不会再提供另一个示例,但是希望您能理解。请让我听听您的想法,以更优雅的方式完成我要尝试的工作。再次感谢你!


查看完整描述

3 回答

?
繁花不似锦

我会很清楚地指出:具有参数的单例不是单例。


根据定义,单例是您希望被实例化的对象不超过一次。如果您试图将参数提供给构造函数,那么单例的意义是什么?


您有两个选择。如果您希望用一些数据初始化单例,则可以在实例化之后用数据加载它,如下所示:


SingletonObj singleton = SingletonObj.getInstance();

singleton.init(paramA, paramB); // init the object with data

如果您的单例正在执行的操作是重复发生的,并且每次都使用不同的参数,则最好将这些参数传递给正在执行的main方法:


SingletonObj singleton = SingletonObj.getInstance();

singleton.doSomething(paramA, paramB); // pass parameters on execution

无论如何,实例化总是没有参数的。否则,您的单身人士将不是单身人士。


查看完整回答
反对 回复 2019-10-15
?
慕粉4167745

我认为您需要像工厂这样的东西来实例化和重用各种参数的对象。可以通过使用同步对象HashMap或ConcurrentHashMap将参数(Integer例如)映射到“单个”可参数化类来实现。


尽管您可能会改用常规的非单例类(例如,需要10.000个不同参数化的单例)。


这是此类商店的示例:


public final class UsefulObjFactory {


    private static Map<Integer, UsefulObj> store =

        new HashMap<Integer, UsefulObj>();


    public static final class UsefulObj {

        private UsefulObj(int parameter) {

            // init

        }

        public void someUsefulMethod() {

            // some useful operation

        }

    }


    public static UsefulObj get(int parameter) {

        synchronized (store) {

            UsefulObj result = store.get(parameter);

            if (result == null) {

                result = new UsefulObj(parameter);

                store.put(parameter, result);

            }

            return result;

        }

    }

}

为了进一步推动它enum,尽管只允许使用固定数量的静态变体,但是Java 也可以视为(或用作)参数化的单例。


但是,如果您需要分布式1解决方案,请考虑一些横向缓存解决方案。例如:EHCache,Terracotta等。


1可能跨越多台计算机上的多个VM。


查看完整回答
反对 回复 2019-10-15
?
慕雪6173905

您可以添加可配置的初始化方法,以将实例化与获取分开。


public class Singleton {

    private static Singleton singleton = null;

    private final int x;


    private Singleton(int x) {

        this.x = x;

    }


    public static Singleton getInstance() {

        if(singleton == null) {

            throw new AssertionError("You have to call init first");

        }


        return singleton;

    }


    public synchronized static Singleton init(int x) {

        if (singleton != null)

        {

            // in my opinion this is optional, but for the purists it ensures

            // that you only ever get the same instance when you call getInstance

            throw new AssertionError("You already initialized me");

        }


        singleton = new Singleton(x);

        return singleton;

    }


}

然后,您可以调用Singleton.init(123)一次进行配置,例如在应用启动时。


查看完整回答
反对 回复 2019-10-15

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信