如何在Java中处理Singleton类的这种情况?

时间:2013-12-19 11:02:38

标签: java design-patterns singleton

我对如何对遵循 Singleton 模型的类进行以下操作有疑问。

我有这个原始的 Singleton 类:

public class ThreadsManager {

    // I can have only one instance:
    private final static ThreadsManager instance = new ThreadsManager();

    // Private constructor:
    private ThreadsManager() {

    }

    public static ThreadsManager getInstance(){
        return instance;
    }
}

好的,这项工作很好,但现在我必须修改它添加一个新属性(一个名为 userName 的字符串),当构建单例对象时必须初始化该属性 以后不能更改

所以我想尝试做类似的事情:

public class ThreadsManager {

    private final static ThreadsManager instance = new ThreadsManager();

    private final static String userName;

    private ThreadsManager() {

    }

    public static ThreadsManager getInstance(String user){
        userName = user;
        return instance;
    }
}

所以我正在尝试添加 String userName 变量 static (一次为该类)和 final (不能是第二次改变了)

我的问题是Eclips将行标记为错误:

1)私人最终静态字符串userName; 告诉我:

 The blank final field userName may not have been initialized

似乎该字段将被初始化(我可以将其初始化为null但是因为它是最终的...我不能在构造函数中稍后初始化)

2) userName = user; 对我说:

 The final field ThreadsManager.userName cannot be assigned

那么处理这种情况的最佳解决方案是什么?

如果我从 userName 变量定义中移除最终,在我看来工作得很好但是我可以更改此值但是我可能根本无法提供设置器这个字段的方法,所以我防止外部变化....

一些想法?

6 个答案:

答案 0 :(得分:3)

我认为你想要一个带有参数的单身人士。这应该解释一下:

Singleton with Arguments in Java

答案 1 :(得分:1)

由于这个类是Singleton,因此名称不应该真正改变太多。我建议将它作为一个常量保持在课堂内。如果在不同场合执行程序时名称可能会更改,请参阅下面的解决方案2.

解决方案1:

public class ThreadsManager 
{
    private final static ThreadsManager instance = new ThreadsManager();
    private String userName;

    private ThreadsManager()
    {
       final String name = "Name";
       userName = name;
    }

    public static synchronized ThreadsManager getInstance(String user)
    {
       return instance;
    }
}

解决方案2:

如果您确实要设置Singleton的名称,并且每次执行程序时名称可能不同。只需添加此方法:

private String userName = null;

// Can only be set after Singleton is created and when userName is null.
public void setName(String n)
{
   if(userName == null)
      userName = n;
}

不要让getInstance()方法有参数,这是一个糟糕的设计。每当有人或你试图从你的班级中获取一个实例时,他/你必须提供一个参数,这个参数将在99%的时间内无关紧要。

答案 2 :(得分:1)

如果你想要该类的实例的多个状态,那么它不会是单例,

你可以创建一个用user键入的Object缓存,这样对于同样的状态,它仍然是单例的

private final Map<String, ThreadsManager> instanceCache = Collections.synchronizedMap<String, ThreadsManager>();

如果此类有大量状态,请确保不会泄漏内存

答案 3 :(得分:0)

您可以使用嵌套类来完成。

public class ThreadsManager {

    private static String userName;

    private ThreadsManager() {

    }

    public static ThreadsManager getInstance(String user){
        if (userName == null)
             userName = user;
        // the holder's instance is only initialised at this point, 
        // after userName is set.
        return Holder.instance;
    }

    static class Holder {
        private final static ThreadsManager instance = new ThreadsManager();
    }
}

答案 4 :(得分:0)

首先: private final static String userName 只能在私有构造函数内或定义期间初始化。

其次 您可能最终得到一个null实例,因此您可能会这样做:

public class ThreadsManager {

private final static ThreadsManager instance = new ThreadsManager();

private String userName;

private ThreadsManager() {

}

private ThreadsManager(String user) {
    userName = user;
}


public static ThreadsManager getInstance(String user){
    if(instance == null) {
        instance = new ThreadsManager(user);
    } else {
        Logger.getInstance().logWarning("User has already been set. Will continue with user ["+username+"].);
    }

    return instance;
    }
}

处理如何处理第二个用户名需要一些思考。 总的来说,你应该尝试保持getInstance()方法参数免费,因为它会导致上述问题。

答案 5 :(得分:-1)

怎么样

public class ThreadsManager {
    private final static ThreadsManager instance = new ThreadsManager();
    private static String userName;

public static synchronized ThreadsManager getInstance( String user ) {
    if ( username == null ) { userName = user; }
    return instance;
}

这样可以确保userName仅在第一次设置。

然而,对于单身人员来说,在后续的getInstance()中取一个被忽略的参数 - 可能甚至是竞争条件 - 取决于你的用例,这可能是非常令人困惑的语义。

干杯,