我有情况,我想在实例化类时只读取一次配置文件。
假设我有一个名为readConfig()
的方法,它读取配置并将其放入Map
对象中。当程序需要使用配置值时,它会使用它的define键读取对象。我想知道构造函数只调用它的生命周期。我可以将我的方法readConfig()
放入构造函数中,这会给我一次调用的好处,还是有其他机制可以做到这一点?
答案 0 :(得分:58)
你可以:这就是构造函数的用途。此外,您明确指出对象永远不会构建在未知状态(未加载配置)。
你不应该:在构造函数中调用实例方法是危险的,因为该对象尚未完全初始化(这主要适用于可被覆盖的方法)。此外,已知构造函数中的复杂处理会对可测试性产生负面影响。
答案 1 :(得分:25)
更好的设计将是
public static YourObject getMyObject(File configFile){
//process and create an object configure it and return it
}
答案 2 :(得分:7)
我可以将方法readConfig()放入构造函数吗?
在构造函数中调用不可替代的方法是可以接受的方法。
虽然如果该方法仅由构造函数使用,则您可能想知道是否确实需要将其提取到方法(甚至是private
)中。
如果您选择将构造函数完成的某些逻辑提取到方法中,那么对于任何方法,您都必须选择适合该方法要求的访问修饰符,但在这种特定情况下,保护该方法更重要必须克服这种方法的覆盖问题,否则会使超类构造函数不一致。
如果仅由类的构造函数(和实例方法)使用,则应为private
。
否则,如果该方法在包内或子类中被重用,则应同时为package-private
和final
。
这将给我带来一次打电话的好处,还是有另一种机制可以做到这一点?
使用这种方式没有好处或缺点。
我不鼓励在构造函数中执行很多逻辑,但是在某些情况下,在构造函数中初始化多件事可能很有意义。
例如,复制构造函数可能会执行很多操作。
多个JDK类对此进行了说明。
以HashMap
复制构造函数为例,该构造函数构造一个新的HashMap
并具有与指定的Map
参数相同的映射:
public HashMap(Map<? extends K, ? extends V> m) {
this.loadFactor = DEFAULT_LOAD_FACTOR;
putMapEntries(m, false);
}
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { // pre-size
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else if (s > threshold)
resize();
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
putVal(hash(key), key, value, false, evict);
}
}
}
提取在putMapEntries()
中填充的地图的逻辑是一件好事,因为它允许:
clone()
和putAll()
也使用它答案 3 :(得分:2)
构造函数只被调用一次,因此您可以安全地执行您想要的操作,但是在构造函数中而不是直接调用方法的缺点是,如果方法失败,则不会获得直接反馈。您调用的方法越多,这就越困难。
一种解决方案是提供一些方法,一旦构造完就可以调用它来查询对象的“运行状况”。例如,方法isConfigOK()
可用于查看配置读取操作是否正常。
另一种解决方案是在失败时在构造函数中抛出异常,但这实际上取决于这些失败的“致命”程度。
class A
{
Map <String,String> config = null;
public A()
{
readConfig();
}
protected boolean readConfig()
{
...
}
public boolean isConfigOK()
{
// Check config here
return true;
}
};
答案 4 :(得分:1)
你可以。但是通过将它置于构造函数中,您将难以测试对象。</ p>
相反,你应该:
init()
方法依赖注入框架为您提供了这些选项。
public class ConfigurableObject {
private Map<String, String> configuration;
public ConfigurableObject() {
}
public void setConfiguration(..) {
//...simply set the configuration
}
}
第二个选项的示例(最好在对象由容器管理时使用):
public class ConfigurableObject {
private File configFile;
private Map<String, String> configuration;
public ConfigurableObject(File configFile) {
this.configFile = configFile;
}
public void init() {
this.configuration = parseConfig(); // implement
}
}
当然,这可以通过构造函数
来编写public ConfigurableObject(File configfile) {
this.configuration = parseConfig(configFile);
}
但是你将无法提供模拟配置。
我知道第二次选择听起来更冗长,容易出错(如果忘记初始化)。如果你在构造函数中执行它,它不会真的伤害到你。但是,使代码更加依赖注入 - 通常是一种很好的做法。
第一种选择是最好的 - 它可以与DI框架和手动DI一起使用。
答案 5 :(得分:1)
单身人士模式
public class MyClass() {
private static MyClass instance = null;
/**
* Get instance of my class, Singleton
**/
public static MyClass getInstance() {
if(instance == null) {
instance = new MyClass();
}
return instance;
}
/**
* Private constructor
*/
private MyClass() {
//This will only be called once, by calling getInstanse() method.
}
}
答案 6 :(得分:0)
为什么不使用Static Initialization Blocks
?其他详细信息:
Static Initialization Blocks