关于将键/值对从流中加载到java.util.Properties对象中,我遇到了一个我自己无法回答的问题。我正在研究的Foo servlet类中有一个调用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类的长行和a!我发现它在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
将消耗流中的所有数据,直到到达末尾,然后将返回的所有Property
都退还给您。
(“可能”是由于下面提到的线程安全性问题)。
但是,在随后的loadProperties()
调用(情况2)上,没有更多要读取的内容-流中的所有数据都已消耗。除非您明确地倒带流(根据返回的InputStream
的特定子类,您甚至可能无法倒带),否则将没有更多的数据可读取。
但是,在情况2中还有另一个问题,这意味着您不应尝试倒回该流:它不是线程安全的。如果两个线程尝试同时调用loadProperties()
,我不想猜测会发生什么。您可能会胡说八道。Properties.load(InputStream)
的Javadoc没有说明在传递的InputStream
上进行同步的方法。因此,应避免遇到线程不安全代码的情况-在第一种情况下,您需要为每个调用创建一个新的InputStream
。
我假设您正在尝试避免多次重新读取属性。我建议在类外加载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
;您并不是在等待执行特定的代码路径,这将触发属性的加载,并导致加载失败。这也使代码更易于测试-您不再依赖于从文件加载的属性-它可以来自任何地方。