我有反思的有线经验。首先是一些示例代码:
public abstract class A {
public A () {
init();
}
public abstract void init ();
}
public class B extends A {
private int i = 0;
public B () {
super();
System.out.println(i);
}
public void init () {
i = 1;
}
}
在我的代码中的某处,我使用反射api来实例化一个对象B.
Class<AbstractSection> bc = (Class<AbstractSection>) Class.forName(B);
Constructor<?> bcon = bc.getConstructor();
B b = (B) bcon.newInstance();
我期望的是B的实例,变量i设置为值'1'。我得到的是一个B的实例,我仍然设置为'0'。 通过调试器仔细观察,我发现这不完全正确:我仍然没有设置为'0'。它被改为'1' init()方法,并在super()调用返回的那一刻设置回'0'。
任何人都有线索?提前谢谢,
曼努埃尔
PS:我知道我可以通过调用init()而不是在超类中但在继承构造函数中来解决这个问题。
答案 0 :(得分:8)
对于初学者来说,这与反射无关 - 如果你自己实例化这个类,你会得到相同的结果。
您的混淆可能源于i
字段的定义方式 - 看起来像一旦“存在”就设置为0
。实际上,赋值为零是构造函数的第一行(但是,关键的是,在调用super()
之后,正如构造函数所要求的那样)。
换句话说,您的课程完全等同于以下内容:
public class B extends A {
private int i;
public B () {
super();
i = 0;
System.out.println(i);
}
public void init () {
i = 1;
}
}
我认为你现在可以看到为什么输出是0
而不是1
- 因为对init()
的调用发生之前字段初始化为{ {1}}。
由于这个原因以及其他原因,总的来说你应该避免从超类构造函数中调用子类方法 - 因为子类在这一点上甚至都没有被初始化,所以很容易违反不变量。 (在未构造的对象上调用方法总是一个非常糟糕的主意!)这是您的问题的根本原因,以及您应该通过修复解决的方向。
由于这个原因,构造函数应该将自己限制为仅调用private或final的方法。有关详细信息,请参阅(以及其他):
答案 1 :(得分:1)
任何非静态类属性都将在类的构造函数中设置为其默认值:
boolean false
char '\u0000'
byte,short,int,long 0
float, double +0.0f or +0.0d
object null
答案 2 :(得分:0)
如果您调用构造函数而不是使用反射,则没有区别。
宣言如
private int i = 0;
在超级调用之后立即成为每个声明的构造函数的一部分。
因此,在分配给init
之前调用0
。
答案 3 :(得分:0)
因为执行顺序如下:子类的构造函数调用超级构造函数,超级构造函数设置i = 0,子类中的字段初始化然后设置i = 1,最后执行其余的构造函数子类。
这遵循一般规则:对象首先由超类初始化,然后由子类初始化。这就是为什么,如果你试图在super()的参数中调用非静态方法,你会得到一个错误“在调用超类型构造函数之前不能引用它”。如果您尝试修改super()中的字段,则会收到另一条错误消息。这可以防止在超级构造函数初始化之前初始化子类。
答案 4 :(得分:0)
正如其他答案所述,该方法(由编译器生成并初始化您的字段)在super()之后调用,所以它是这样的:
super()
init() // i = 1
this()
<init> // i = 0
我想补充一点,你不应该在构造函数中调用一个可能被客户覆盖的方法。这可能会导致不可预测的“fuckup”;)
有效java中的更多细节,现在找不到项目rigth。