NPE 被抛出将 key/value 对加载到 java.util.Properties 对象中

NPE was thrown loading key/value pairs into java.util.Properties object

我 运行 遇到了一个问题,关于从流中将 key/value 对加载到 java.util.Properties 对象中,我自己很难回答。在我正在处理的 Foo servlet class 中有一个调用 loadProperties() 方法的方法。在情况 1) 中检索选定键的值有效,但在情况 2) loadProperties().getProperty("bar") 中抛出 NullPointerException。我不确定为什么会抛出 NPE。我忘了添加,但是在同一个 Foo 实例中多次调用了 loadProperties()。

案例 1)

public class Foo extends HttpServlet {
    private InputStream is = null;

    private Properties loadProperties() {
        Properties p = new Properties();
        is = Foo.class.getClassLoader().getResourceStream("/com/test/bar.properties");
        p.load(is);
        return p;
    }
}

案例2)

public class Foo extends HttpServlet {
    private final InputStream is = Foo.class.getClassLoader().getResourceStream("/com/test/bar.properties);

    private Properties loadProperties() {
        Properties p = new Properties();
        p.load(is);
        return p;
    }
}

正在调用 loadProperties()

public class Foo extends HttpServlet {
    private Properties loadProperties() { .... }

    private void doSomething() {
        PrintStream ps = new PrintStream(new FileOutputStream(loadProperties().getProperty("bar"))); // NPE was thrown in the case 2)
        is.close();
    }

    private void doSomething2() {
        PrintStream ps = new PrintStream(new FileOutputStream(loadProperties().getProperty("xyz")));
        is.close();
    }
}

[更新]

安迪回答了我的问题。当他问我 loadProperties() 是否调用了不止一次时,我检查了 Foo class 的长行,唉!我发现它在 doPost 方法中无意中被调用了一次。

public class Foo extends HttpServlet {
    protected void doPost(...) {
        loadProperties();

        callDoSomething();
        callDoSomething2();
    }

    private Properties loadPropeties() {
        ....
    }

    private void doSomething() {
        ....
    }

    private void doSomething2() {
        ....
    }
}

在情况 2 中,您试图在每次调用 loadProperties 方法时重新使用相同的流。

可能 在第一次调用时正常工作:Properties.load 将消耗流中的所有数据,直到到达末尾,然后返回所有它加载的Propertys。

(“可能”是因为下面提到的线程安全问题)

但是,在随后调用 loadProperties()(情况 2)时,没有更多内容可读取 - 流中的所有数据都已被消耗。除非你明确地倒回流(你甚至可能做不到,这取决于返回的 InputStream 的特定子class),你将没有更多的数据可读。

然而,在情况 2 中还有另一个问题,这意味着您不应尝试倒带流:它不是线程安全的。如果两个线程试图同时调用 loadProperties(),我不想猜测会发生什么。你可能只会胡说八道。

Properties.load(InputStream) 的 Javadoc 没有说明在传递的 InputStream 上同步的方法。因此,您应该避免陷入线程不安全代码的境地——在情况 1 中您就是这样做的,方法是为每次调用创建一个新的 InputStream

我假设您试图避免多次重新阅读属性。我建议在 class 之外加载 Properties 并将它们作为构造函数参数注入:

class Foo extends HttpServlet {
  private final Properties properties;

  Foo(Properties properties) {
    this.properties = checkNotNull(properties);
  }

  private void doSomething() {
    PrintStream ps = new PrintStream(new FileOutputStream(properties.getProperty("bar")));
    // ...
}

这样,如果您有一个 Foo 的实例,它就具有有效的 Properties;您不是在等待执行特定的代码路径,这将触发属性的加载,并且加载失败。

它还使代码更易于测试 - 您不再依赖于从文件加载的属性 - 它可以来自任何地方。