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

发布于 2024-10-20 16:58:53 字数 231 浏览 3 评论 0原文

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

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

I have situation, where I want to read configuration file only one time, when class is instantiated.

Suppose I have a method named readConfig(), that reads configuration and puts it into a Map object. When the program is required to use configuration value it reads object with it's define key. I want to know that constructor calls only once it's life cycle. Can I put my method readConfig() into constructor, which would give me benefit of one time calling or is there another mechanism to do that?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

高跟鞋的旋律 2024-10-27 16:58:53

你可以:这就是构造函数的用途。此外,您还要明确该对象永远不会在未知状态下构造(未加载配置)。

你不应该:在构造函数中调用实例方法是危险的,因为对象尚未完全初始化(这主要适用于可以重写的方法)。此外,构造函数中的复杂处理也会对可测试性产生负面影响。

You can: this is what constructors are for. Also you make it clear that the object is never constructed in an unknown state (without configuration loaded).

You shouldn't: calling instance method in constructor is dangerous because the object is not yet fully initialized (this applies mainly to methods than can be overridden). Also complex processing in constructor is known to have a negative impact on testability.

断念 2024-10-27 16:58:53

更好的设计是

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

Better design would be

public static YourObject getMyObject(File configFile){
    //process and create an object configure it and return it
}
权谋诡计 2024-10-27 16:58:53

我可以将我的方法 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() 也使用它
  • (次要但有趣),给出一个有意义的名称来传达执行的逻辑

Can I put my method readConfig() into constructor?

Invoking a not overridable method in a constructor is an acceptable approach.
While if the method is only used by the constructor you may wonder if extracting it into a method (even private) is really required.

If you choose to extract some logic done by the constructor into a method, as for any method you have to choose a access modifier that fits to the method requirement but in this specific case it matters further as protecting the method against the overriding of the method has to be done at risk of making the super class constructor inconsistent.

So it should be private if it is used only by the constructor(s) (and instance methods) of the class.
Otherwise it should be both package-private and final if the method is reused inside the package or in the subclasses.

which would give me benefit of one time calling or is there another mechanism to do that ?

You don't have any benefit or drawback to use this way.
I don't encourage to perform much logic in constructors but in some cases it may make sense to init multiple things in a constructor.
For example the copy constructor may perform a lot of things.
Multiple JDK classes illustrate that.
Take for example the HashMap copy constructor that constructs a new HashMap with the same mappings as the specified Map parameter :

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);
        }
    }
}

Extracting the logic of the map populating in putMapEntries() is a good thing because it allows :

  • reusing the method in other contexts. For example clone() and putAll() use it too
  • (minor but interesting) giving a meaningful name that conveys the performed logic
缪败 2024-10-27 16:58:53

构造函数仅被调用一次,因此您可以安全地执行您想要的操作,但是从构造函数内部而不是直接调用方法的缺点是,如果方法失败,您将无法获得直接反馈。调用的方法越多,这就会变得越困难。

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

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

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

    protected boolean readConfig()
    {
        ...
    }

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

The constructor is called only once, so you can safely do what you want, however the disadvantage of calling methods from within the constructor, rather than directly, is that you don't get direct feedback if the method fails. This gets more difficult the more methods you call.

One solution is to provide methods that you can call to query the 'health' of the object once it's been constructed. For example the method isConfigOK() can be used to see if the config read operation was OK.

Another solution is to throw exceptions in the constructor upon failure, but it really depends on how 'fatal' these failures are.

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

    protected boolean readConfig()
    {
        ...
    }

    public boolean isConfigOK()
    {
        // Check config here
        return true;
    }
};
愁以何悠 2024-10-27 16:58:53

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

相反,您应该:

  • 提供带有 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 一起使用。

You can. But by placing this in the constructor you are making your object hard to test.

Instead you should:

  • provide the configuration with a setter
  • have a separate init() method

Dependency injection frameworks give you these options.

public class ConfigurableObject {
   private Map<String, String> configuration;
   public ConfigurableObject() {

   }

   public void setConfiguration(..) {
       //...simply set the configuration
   }
}

An example of the 2nd option (best used when the object is managed by a container):

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

This, of course, can be written by just having the constructor

public ConfigurableObject(File configfile) {
    this.configuration = parseConfig(configFile);
}

But then you won't be able to provide mock configurations.

I know the 2nd opttion sounds more verbose and prone to error (if you forget to initialize). And it won't really hurt you that much if you do it in a constructor. But making your code more dependency-injection oriented is generally a good practice.

The 1st option is best - it can be used with both DI framework and with manual DI.

淡笑忘祈一世凡恋 2024-10-27 16:58:53

单例模式

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

Singleton pattern

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. 
    }
}
酒中人 2024-10-27 16:58:53

为什么不使用静态初始化块?其他详细信息请参见此处:
静态初始化块

Why not to use Static Initialization Blocks ? Additional details here:
Static Initialization Blocks

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文