我可以在Java中用构造函数调用方法吗?

时间:2011-03-08 09:27:07

标签: java constructor

我有情况,我想在实例化类时只读取一次配置文件。

假设我有一个名为readConfig()的方法,它读取配置并将其放入Map对象中。当程序需要使用配置值时,它会使用它的define键读取对象。我想知道构造函数只调用它的生命周期。我可以将我的方法readConfig()放入构造函数中,这会给我一次调用的好处,还是有其他机制可以做到这一点?

7 个答案:

答案 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-privatefinal

  

这将给我带来一次打电话的好处,还是有另一种机制可以做到这一点?

使用这种方式没有好处或缺点。
我不鼓励在构造函数中执行很多逻辑,但是在某些情况下,在构造函数中初始化多件事可能很有意义。
例如,复制构造函数可能会执行很多操作。
多个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>

相反,你应该:

  • 为配置提供setter
  • 有一个单独的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