如何将参数传递给静态初始化块

时间:2013-04-03 15:13:12

标签: java oop file-io singleton utility-method

我想要做的是将文件(使用Apache poi的excel文件)中的键/值对加载到将用作查找表的静态映射中。一旦加载,表格就不会改变。

public final class LookupTable
{  
   private final static Map<String, String> map;  
   static {
     map = new HashMap<String, String>();
     // should do initialization here
     // InputStream is = new FileInputStream(new File("pathToFile"));
     // not sure how to pass pathToFile without hardcoding it?
   }

   private LookupTable() {
   }

  public static void loadTable(InputStream is) {
    // read table from file
    // load it into map
    map.put("regex", "value");
  }

  public static String getValue(String key) {
    return map.get(key);
  }
}

理想情况下,我想在静态初始化块中加载地图,但是如何在没有硬编码的情况下传递流?我在使用loadTable静态方法时看到的问题是在调用其他静态方法之前可能没有调用它。

// LookupTable.loadTable(stream);  
LookupTable.getValue("regex"); // null since map was never populated.

有更好的方法吗?

7 个答案:

答案 0 :(得分:3)

您使用的任何内容都必须在启动时可访问。据我所知,您的选择是:

  1. 对路径进行硬编码。由于显而易见的原因,这很糟糕。
  2. 静态变量或静态方法。这会产生一些鸡蛋问题;最终它得到了硬编码,但至少你可以使用static方法进行搜索。
  3. 使用Java或环境变量。因此,您使用的是System.getProperty("filename", "/default/filename")。更好,因为它至少可以使用环境或JVM启动时的-D参数进行自定义。
  4. 使用ClassLoader getResource*方法。这可能是正确答案。具体来说,您可能希望在当前线程的上下文ClassLoader getResourceAsStream()上使用Thread.currentThread().getContextClassLoader()方法。 (所以,总共Thread.currentThread().getContextClassLoader().getResourceAsStream("filename")。)然后ClassLoader将为您找到您的资源(只要您将其放在CLASSPATH中的某个地方。)

答案 1 :(得分:2)

是的,有一种更好的方法,在必须使用之前使用Factory设计模式初始化对象:

http://www.oodesign.com/factory-pattern.html

答案 2 :(得分:1)

您无法将信息传递到静态初始化块 - 它们应该独立工作。由于您计划通过的流需要在程序开始执行之前知道,因此大概您的LookupTable也应该能够找到它。例如,这可能是某种为您提供流的配置实用程序。然后你可以像这样编写初始化器:

static {
    InputStream exelStream = MyConfigUtil.getExcelStreamForLookup();
    loadTable(exelStream);
}

据推测,系统中有一个类可以从已知的源中获取Excel流。源不需要硬编码:它可以从配置文件中读取位置,或从服务器上的预定义网络位置接收数据。在所有情况下,获取Excel流的过程必须在某处“触底”,因为系统中的某些东西需要能够在没有其他参数的情况下找到它。

答案 3 :(得分:0)

这可能是使用延迟加载地图的情况。

但是,您需要在第一次调用inputFileName之前设置getValue()。这将在您的应用程序的初始化代码中完成。 (或者您可以使用静态方法进行设置。)

这指出了延迟加载的优势。在第一次调用getValue()之前,您不必拥有文件名。使用静态初始化程序,您必须将文件名存储在类之外的某处,以便在类加载时(但在静态字段初始化之后)用于加载数据。

 public static String inputFileName = null;

 public static String getValue(String key) {
    if (map == null) {
        map = = new HashMap<String, String>();
        // open the file using 'inputFileName'
        loadTable(InputStream is);
    }
    return map.get(key);
  }

如果你的代码是多线程的,请告诉我,我会评论同步问题。

<强>交替

您也可以使用Spring注入地图并在其他类中构建它 - 例如MapBuilder

答案 4 :(得分:0)

尝试使用System.getProperty()并在命令行中使用-D传递参数。

static String prop;

static {
    prop = System.getProperty("java.home");
}

public static void main(String... args) {

    System.out.println(prop);
}

答案 5 :(得分:0)

这不是直接回答你的问题,但我不明白为什么map必须是静态的。您可以将map更改为非静态,并将构造函数更改为public LookupTable(File file) {...fill map...}。如果你有不同的excel文件,你甚至可以拥有该类的许多实例;它现在可能不是这样,但它将“面向未来”代码。

答案 6 :(得分:0)

如果适合您的情况,我会建议singleton enum approach

public enum LookupTable {

    INSTANCE(FileManager.getFileName());

    LookupTable(String fileName){
        props = new HashMap<String,String>();
        //Read from excel and fill the hashmap
    }

    private final Map<String, String> props;

    public String getMapValue(String key){
        return props.get(key);
    }   
}

可以通过

调用
LookupTable.INSTANCE.getMapValue("mykey");

这将按顺序调用这些方法

  • 从filemanager类获取文件名,该类根据您的需要进行参数化
  • 调用构造函数(它是私有的)并从excel文件
  • 加载属性
  • 获取密钥的getMapValue并返回

后续调用LookupTable.INSTANCE.getMapValue(“mysecondkey”)将仅在事先初始化INSTANCE时调用getMapValue。