我可以在 Java 的构造函数中调用方法吗?
Posted
技术标签:
【中文标题】我可以在 Java 的构造函数中调用方法吗?【英文标题】:Can I call methods in constructor in Java? 【发布时间】:2011-07-10 23:24:49 【问题描述】:我有一种情况,当类被实例化时,我只想读取一次配置文件。
假设我有一个名为readConfig()
的方法,它读取配置并将其放入Map
对象中。当程序需要使用配置值时,它会使用其定义键读取对象。我想知道构造函数只调用一次它的生命周期。我可以将我的方法readConfig()
放入构造函数中,这样可以让我有一次调用的好处,还是有另一种机制可以做到这一点?
【问题讨论】:
您是否研究过单例模式? 这是一个非常古老的问题.. 但是如果有人想以 android 方式使用单例.. 看看this 尝试静态块..一旦第一次引用 rhat 类(无论对象是否创建),静态块就会运行,并且它在该类的生命周期中运行一次 【参考方案1】:你可以:这就是构造函数的用途。您还明确指出,该对象永远不会在未知状态下构造(未加载配置)。
您不应该:在构造函数中调用实例方法是危险的,因为对象尚未完全初始化(这主要适用于无法被覆盖的方法)。众所周知,构造函数中的复杂处理会对可测试性产生负面影响。
【讨论】:
【参考方案2】:更好的设计应该是
public static YourObject getMyObject(File configFile)
//process and create an object configure it and return it
Factory design pattern
【讨论】:
@sam ,如果在构造函数中调用方法,由于对象的创建生命周期没有完全完成,部分成员变量的状态已损坏。 我无法弄清楚该代码如何与您提供的工厂方法模式的链接相匹配。常见的错误。但是,+1 用于拆分文件的读取(可以进一步分解为可测试的方法)和构造使用数据的对象。 @Tom 好点 +1。 @sam 请阅读上面汤姆的评论,以便在替换//process and create an object configure it and return it
和它的静态工厂模式仅汤姆时更可测试,
这并没有回答标题中的问题。它不应该是公认的答案。【参考方案3】:
我可以将我的方法 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()
也使用它
(次要但有趣)给出一个有意义的名称来传达执行的逻辑
【讨论】:
这个答案应该在堆栈中更高,因为它不只是说“不要这样做,它可能导致未定义的行为!”,但实际上解释了何时(以及为什么)在构造函数中调用实例方法是完全安全的。 @Janus Varmarken 非常感谢您的反馈。我很高兴你觉得它有帮助。这不是估计/赞成的,因为我对问题日期的回答很晚。不过好吧....【参考方案4】:构造函数只被调用一次,所以你可以安全地做你想做的事,但是从构造函数内部而不是直接调用方法的缺点是,如果方法失败,你不会得到直接的反馈。您调用的方法越多,这就越困难。
一种解决方案是提供方法,您可以调用这些方法来查询对象的“健康状况”,一旦它被构造出来。比如isConfigOK()
方法可以用来查看配置读取操作是否OK。
另一种解决方案是在失败时在构造函数中抛出异常,但这实际上取决于这些失败的“致命”程度。
class A
Map <String,String> config = null;
public A()
readConfig();
protected boolean readConfig()
...
public boolean isConfigOK()
// Check config here
return true;
;
【讨论】:
但这会使错误比您希望的“更致命”;例如,如果调用失败,您可能不会太介意,因此您不希望通过抛出异常来使整个操作无效。【参考方案5】:你可以。但是通过将它放在构造函数中,您会使您的对象难以测试。
相反,您应该:
使用 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 一起使用。
【讨论】:
你能讨论更多关于第二点的描述形式吗? @sam - 不要在构造函数中执行工作,而是从实例化对象的位置调用init()
方法。但不要在构造函数中进行工作。查看更新
@Tom Hawtin - tackline 更新了答案以包含第一个选项的示例(设置配置)。但是如果对象是托管的,公共的 init 方法就可以了。
在第一个例子中,你的意思是在构造函数内部调用setConfiguration()方法吗?【参考方案6】:
单例模式
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.
【讨论】:
单例可能是一个解决方案。但是有比这个更好的实现。 (它不是线程安全的,你可能更喜欢用枚举来解决它)【参考方案7】:为什么不使用 Static Initialization Blocks
?此处的其他详细信息:
Static Initialization Blocks
【讨论】:
以上是关于我可以在 Java 的构造函数中调用方法吗?的主要内容,如果未能解决你的问题,请参考以下文章