Java 带参数的单例类

Java 带参数的单例类,java,oop,singleton,design-patterns,Java,Oop,Singleton,Design Patterns,我正在开发计算机视觉应用程序,我将需要分类器类。该类在每次运行应用程序时都是不可变的,它在初始化时从磁盘加载经过训练的数据。我想确保整个程序都可以访问相同的训练数据,并且我想在加载数据后阻止从磁盘重新加载数据 我考虑的是使用静态类或单例。我不确定如何将数据加载到静态类中,因为在编译时不知道数据文件的路径——这将是程序参数。所以我想到了单例模式,但我不知道如何动态初始化它 我的想法是使用以下方法: class Singleton { private static Singleton ins

我正在开发计算机视觉应用程序,我将需要分类器类。该类在每次运行应用程序时都是不可变的,它在初始化时从磁盘加载经过训练的数据。我想确保整个程序都可以访问相同的训练数据,并且我想在加载数据后阻止从磁盘重新加载数据

我考虑的是使用静态类或单例。我不确定如何将数据加载到静态类中,因为在编译时不知道数据文件的路径——这将是程序参数。所以我想到了单例模式,但我不知道如何动态初始化它

我的想法是使用以下方法:

class Singleton {
    private static Singleton instance;
    private Singleton() { ... }
    private static SomeDataObject data;

    public static Singleton getInstance() {
        if(instance == null)
            instance = new Singleton();

            return instance;
    }

    public static init(string dataPath){
        if(data == null)
             loadDataFromFile(dataPath)
    }
}
这将不起作用,因为我无法控制首先调用哪个方法

我知道正确的方法是在开始时用数据创建实例,并将其传递给所有需要它的类和方法,但这并不是真正的通用解决方案。我可以在自己的代码中跟踪对分类器的所有调用,但如果我将代码作为API,这将是一个问题

简言之,如何在运行时初始化singleton?

我认为(确切地说)您想要做的事情不起作用

以下建议将起作用:

public static void main(String[] args)
{
  Singleton.init("somepath");
  ...
  Singleton.getInstance().doingStuff();
  ...
}
更好的实现可能是:(如果您尝试在不首先调用
init
的情况下使用它,则会导致
NullPointerException
)(但不再是真正的单例)

还有:(可能的糟糕编码实践除外)

我想这并不能解决很多问题。

我使用的是比当前获胜的解决方案“更”线程安全的解决方案,几乎没有使用同步

import java.util.function.Supplier;

public class InitOnce {

/**
 * Marked as final to prevent JIT reordering
 */
private final Supplier<String> theArgs;

private InitOnce(Supplier<String> supplier) {
    super();
    this.theArgs = supplier;
}

/**
 * Uses the arguments to do something
 * 
 * @return
 */
public String doSomething() {
    return "Something : " + theArgs.get();
}

/**
 * Initializes all the things
 * 
 * @param someArgs
 */
public static synchronized void init(final Supplier<String> someArgs) {
    class InitOnceFactory implements Supplier<InitOnce> {
        private final InitOnce initOnceInstance = new InitOnce(someArgs);

        @Override
        public InitOnce get() {
            return initOnceInstance;
        }
    }

    if (!InitOnceFactory.class.isInstance(instance)) {
        instance = new InitOnceFactory();
    } else {
        throw new IllegalStateException("Already Initialized");
    }
}

private static Supplier<InitOnce> instance = new InitOnceHolder();

/**
 * Temp Placeholder supplier
 * 
 */
private static final class InitOnceHolder implements Supplier<InitOnce> {
    @Override
    public synchronized InitOnce get() {

        if (InitOnceHolder.class.isInstance(instance))
            throw new IllegalStateException("Not Initialized");

        return instance.get();
    }

}

/**
 * Returns the instance
 * 
 * @return
 */
public static final InitOnce getInstance() {
    return instance.get();
}
}
导入java.util.function.Supplier;
公共类一次初始化{
/**
*标记为“最终”以防止JIT重新排序
*/
私人最终供应商THARGS;
private InitOnce(供应商){
超级();
this.theArgs=供应商;
}
/**
*使用参数执行某些操作
* 
*@返回
*/
公共字符串doSomething(){
return“Something:+theArgs.get();
}
/**
*初始化所有的东西
* 
*@param someArgs
*/
公共静态同步void init(最终供应商参数){
类InitOnceFactory实现供应商{
private final InitOnce initOnceInstance=new InitOnce(someArgs);
@凌驾
公共InitOnce get(){
返回初始化实例;
}
}
如果(!InitOnceFactory.class.isInstance(实例)){
instance=new InitOnceFactory();
}否则{
抛出新的IllegalStateException(“已初始化”);
}
}
私有静态供应商实例=new InitOnceHolder();
/**
*临时占位符供应商
* 
*/
私有静态最终类InitOnceHolder实现供应商{
@凌驾
公共同步InitOnce get(){
if(InitOnceHolder.class.isInstance(实例))
抛出新的非法状态异常(“未初始化”);
返回instance.get();
}
}
/**
*返回实例
* 
*@返回
*/
公共静态final InitOnce getInstance(){
返回instance.get();
}
}

如何以编程方式访问程序参数
dataPath
您不需要静态单例实例和数据对象。另外,您当前的单例实现不是线程安全的。@MattBall它可以来自GUI,也可以来自命令行参数。首先,您需要确保您需要使用单例模式。当您需要>确保只创建一个类实例时,应使用Singleton和>提供对象的全局访问点资料来源:看来你不需要单身。全局变量不好,也是单例。@bancer我需要可访问的SomeDataObject的单个实例,该实例应可由多个其他类型的分类器访问,不必共享某些父类。但我也可以执行Singleton.getInstance().doStuff(),这将是错误的,因为没有进行初始化。正如我在文章中所说,在我的代码中,我可以跟踪这一点,但通常这解决不了任何问题。@jnovacho您可以让构造函数读取存储在其他地方的静态变量,但我认为这基本上是相同的问题。我不认为你能做得比我在答案中提供的更好(除非你想在每次调用时将路径传递给
getInstance
)。对我来说,第一个是使用公共静态void init()
class Main
{
  public static void main(String[] args)
  {
    Singleton.currentPath = "somepath";
    ...
  }
}

class Singleton
{
  public static String currentPath = null;
  private static Singleton instance;
  private SomeDataObject data;

  private Singleton(String path) { loadDataFromFile(path); ... }

  public static Singleton getInstance() {
     if(instance == null && currentPath != null)
        instance = new Singleton(currentPath);
     return instance;
  }
}
import java.util.function.Supplier;

public class InitOnce {

/**
 * Marked as final to prevent JIT reordering
 */
private final Supplier<String> theArgs;

private InitOnce(Supplier<String> supplier) {
    super();
    this.theArgs = supplier;
}

/**
 * Uses the arguments to do something
 * 
 * @return
 */
public String doSomething() {
    return "Something : " + theArgs.get();
}

/**
 * Initializes all the things
 * 
 * @param someArgs
 */
public static synchronized void init(final Supplier<String> someArgs) {
    class InitOnceFactory implements Supplier<InitOnce> {
        private final InitOnce initOnceInstance = new InitOnce(someArgs);

        @Override
        public InitOnce get() {
            return initOnceInstance;
        }
    }

    if (!InitOnceFactory.class.isInstance(instance)) {
        instance = new InitOnceFactory();
    } else {
        throw new IllegalStateException("Already Initialized");
    }
}

private static Supplier<InitOnce> instance = new InitOnceHolder();

/**
 * Temp Placeholder supplier
 * 
 */
private static final class InitOnceHolder implements Supplier<InitOnce> {
    @Override
    public synchronized InitOnce get() {

        if (InitOnceHolder.class.isInstance(instance))
            throw new IllegalStateException("Not Initialized");

        return instance.get();
    }

}

/**
 * Returns the instance
 * 
 * @return
 */
public static final InitOnce getInstance() {
    return instance.get();
}
}