反射不会返回正确的实例

时间:2011-07-19 08:54:17

标签: java

我有反思的有线经验。首先是一些示例代码:

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()而不是在超类中但在继承构造函数中来解决这个问题。

5 个答案:

答案 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。