繁体   English   中英

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

[英]Can I call methods in constructor in Java?

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

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

你可以 :这是构造函数的用途。 此外,您清楚地表明该对象永远不会构建在未知状态(未加载配置)。

你不应该 :在构造函数中调用实例方法是危险的,因为该对象尚未完全初始化(这主要适用于可被覆盖的方法)。 此外,已知构造函数中的复杂处理对可测试性具有负面影响。

更好的设计

public static YourObject getMyObject(File configFile){
    //process and create an object configure it and return it
}

我可以将我的方法readConfig()放入构造函数中吗?

在构造函数中调用不可重写的方法是一种可接受的方法。
如果该方法仅由构造函数使用,您可能想知道是否真的需要将其提取到方法(甚至是private )中。

如果您选择将构造函数完成的某些逻辑提取到方法中,就像任何方法一样,您必须选择适合方法要求的访问修饰符,但在此特定情况下,它更重要的是保护方法不受覆盖方法的影响必须要做有可能使超类构造函数不一致

因此,如果它仅由类的构造函数(和实例方法)使用,那么它应该是private的。
否则,如果在包内或子类中重用该方法,则它应该是package-privatefinal

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

使用这种方式没有任何好处或缺点。
我不鼓励在构造函数中执行很多逻辑,但在某些情况下,在构造函数中初始化多个东西可能是有意义的。
例如,复制构造函数可以执行很多操作。
多个JDK类说明了这一点。
HashMap复制构造函数为例,该构造函数使用与指定Map参数相同的映射构造新的HashMap

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()putMapEntries()的地图的逻辑是一件好事,因为它允许:

  • 在其他环境中重用该方法。 例如, clone()putAll()使用它
  • (次要但有趣)给出一个有意义的名称,传达执行的逻辑

构造函数只调用一次,因此您可以安全地执行您想要的操作,但是在构造函数中而不是直接调用方法的缺点是,如果方法失败,则不会获得直接反馈。 您调用的方法越多越难。

一种解决方案是提供一些方法,您可以调用它们来构建对象的“运行状况”。 例如,方法isConfigOK()可用于查看配置读取操作是否正常。

另一个解决方案是在失败时在构造函数中抛出异常,但这实际上取决于这些失败的“致命”程度。

class A
{
    Map <String,String> config = null;
    public A()
    {
        readConfig();
    }

    protected boolean readConfig()
    {
        ...
    }

    public boolean isConfigOK()
    {
        // Check config here
        return true;
    }
};

您可以。 但是通过将它放在构造函数中,您将难以测试对象。

相反,你应该:

  • 为配置提供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一起使用。

单身模式

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. 
    }
}

为什么不使用Static Initialization Blocks 其他详细信息: 静态初始化块

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM