这是仅在继承中为真还是在大多数情况下为真?
public class MyClass {
public int id;
public MyClass() {
// Some stuff
setId(5);
}
public setId(int id) {
this.id = id;
}
}
这是非常正确的。
因为setter总是public
方法。如果您的类不是final
,则存在外部方法调用的问题。这不是线程安全的,即它被称为this
引用的转义。因此,从构造函数中,如果您正在调用一个方法,它应该是final
或private
。否则对象的safe initialization
将不会发生,这在实际系统中会导致许多错误。
除此之外,我们永远不应该从constructor
调用public
方法,因为如果该类用于继承,则构造函数不能直接或间接调用可重写方法。
如果违反此规则,将导致程序失败。超类构造函数在子类构造函数之前运行,因此子类中的重写方法将在子类构造器运行之前调用。如果重写方法依赖于子类构造函数执行的任何初始化,则该方法将不会按预期运行。
来源。
您不应该从构造函数中调用可重写的方法。
如果您调用一个可以被costructor中的子类重写的方法,那么子类可能会访问尚未初始化的超类的变量。
例如,到目前为止,以下代码看起来不错
class MyClass {
public int id;
protected String someStr;
public MyClass() {
SetId(5);
someStr = "test";
}
public void SetId(int Id) {
id = Id;
}
}
如果您现在对MyClass
进行子类化并覆盖SetId
方法,您可能会访问尚未初始化的超类的someStr
变量,因此在这种情况下会导致NullPointerException
。
class MySubClass extends MyClass {
public void SetId(int Id) {
id = Id;
someStr.toString(); // will cause NullPointerException
}
}
NPE的原因可能很难看出是否有更大的继承层次。
如果它只是您想要的setter方法,那么从构造函数本身初始化您的变量。
public class MyClass {
int id;
public MyClass() {
id=5;
}
}
否则,您可以调用私有/最终方法来遵循OOP理论。
请记住,只有在执行构造函数中的最后一条语句或退出构造函数时,才会完成对象初始化。
[更新]
类似的,更有趣的讨论正在这里发生
Java-子类调用超级构造函数,该构造函数调用子类方法而不是它自己的
构造函数构造对象,您应该只调用您知道在"未完全构造状态"下工作的东西。在您的示例中,SetId除了设置值之外什么都不做,所以它很好。但是,如果SetId使用了其他尚未准备好的state/info,那么您可能会遇到问题。
这不是一条戒律,而是一条"小心你所做的"
这可能不是一个好主意。如果您不将该类设为final,也不将setName(…)方法设为private或final,则其他人可以扩展您的类并重写setName(..)方法。构造函数(在基类中)将在扩展类中调用该方法,而不是在实现中调用。没有人知道这个方法能做什么。根据经验:构造函数不应该调用可以被覆盖的方法
由于在Java中,默认情况下所有函数都是虚拟的,因此由于本线程中解释的原因,ctor中未密封的方法调用是有风险的。
这就是为什么您可能想要将SetId或MyClass设置为最终或私有。