3

我在 IB 的 API 中声明实例变量时见过this使用,但这似乎是个坏主意。分配是否保证在this完全构建后发生?有关系吗?

我认为实例变量是在构造函数之前初始化的,因为它们可以被构造函数使用。this如果使用有一些例外吗?

如果它以某种方式起作用,这似乎不是一个好主意 -second可用于FirstClass的构造函数?如果是,是在 ?SecondClass之前构建的FirstClass?这意味着num最终是 3 并且i是 10?还是会出现运行时错误?无论哪种方式都有任何保证吗?

public class FirstClass {

    SecondClass second = new SecondClass(this);
    public int i = 3;

    FirstClass(){
        i = second.DoSomething();
    }
}

public class SecondClass{

    private int num = 10;

    SecondClass(FirstClass first){
        num = first.i;
    }
    public int DoSomething(){
        return num;
    }
    ...
}

我想 IB 拥有一支相当稳固的开发人员团队,并且知道他们在做什么。你怎么看:

  1. 可以this用来初始化实例变量吗?
  2. 应该这样做吗?

编辑

答案是肯定的,有保证的结果(现在 - 但请继续阅读......),但不应该这样做,因为它很容易在不经意间对可能改变这个“保证”结果的代码进行更改。

我现在知道在构造新对象(例如FirstClassJVM)时:

  1. 为此类及其所有超类的所有实例变量分配内存。
  2. 用默认值初始化所有变量
  3. “正确”按文本顺序初始化所有变量,从其最高超类(即对象)开始并以此类结束 - 因此second在 i 初始化为 3 之前被初始化(即构造)。
  4. 如果这些“正确”初始化中的任何一个调用另一个方法或另一个构造函数,这将在调用第一个构造函数之前完成- 因此second在构造函数运行之前构造并返回FirstClass
  5. 如果实例初始化调用了另一个构造函数(例如在第 4 项中),它会经历与第 1 项相同的过程(内存分配、初始化等)

这样做不好的原因是“保证”结果可以通过两种方式改变:

  1. 如果源代码顺序改变(例如,如果在构造i之前被初始化second),结果将会改变。很容易不注意到这一点。
  2. 如果有人将我们的类(或多个类)子类化,他们可能不会意识到微妙的平衡,并且可能会在覆盖时改变一些影响结果的东西。

所以看起来 IB 的 API 受到了这种微妙的影响,我现在必须牢记这一点。

感谢 Emil H 的回答和他对这篇文章的指导,这最终让我理解了上面的内容:http ://www.artima.com/designtechniques/initializationP.html - 强烈推荐阅读。

还要感谢 Alexander Drobyshevsky 在他的回答中提出了非常相似的观点。

4

5 回答 5

1

当然,有时您必须使用它,例如当通过构造函数给出的变量被称为与实例变量相同时:

int count;
public Test(int count){  
    this.count = count;
}
于 2013-05-17T06:18:35.877 回答
0

起初,这是一个不好的方法。您必须避免这些构造:使用未完全初始化的变量。有时使用“模板方法”设计模式可能会有所帮助。

正是对于您的示例,在这种情况下,执行 FirstClass 构造函数后 i = 0 的结果值;它取决于 FirstClass 中 i 的分配顺序。如果您通过以下方式更改分配顺序:

public int i = 3;
SecondClass second = new SecondClass(this); // just order changed

在 FirstClass 构造函数执行后,您会得到答案 i = 3。

this - 仅引用当前类实例。它在您的类实例变量开始初始化时已经存在。首先,所有变量都有其默认值(object 为 null,int 为 0,等等)。其次,初始化所有简单的赋值,如“int i = 3”,然后执行当前类的超级构造函数,最后执行当前类的构造函数。简单变量在类中按从上到下的顺序进行初始化,但您不应该依赖它。

于 2013-05-17T06:50:42.177 回答
0

是的,this(在内部是指向构造中对象的“指针”)可以在构造函数中使用。

一个愚蠢的例子可能是一个自引用类

class MySelf {
    Myself _me;
    public MySelf() { _me = this; }
}

还有更现实的例子。想象一下,您想要一个类来表示一个数学图,您将使用连接到自身的单个元素进行初始化。

于 2013-05-17T06:16:39.257 回答
0

有趣的问题。在您描述的情况下发生的情况是,当您调用new FirstClass()类加载器时,会查找该类并在尚未加载的情况下加载它。然后它似乎创建了一个FirstClass所有字段都有其默认值的实例,例如 second 为 null 而 i 为 0。

然后它加载类加载器SecondClassSecondClass创建并初始化一个实例,将num字段设置为 10。然后调用构造函数 get 传入FirstClass实例(字段具有到该点已初始化的值)。因此 in 的值numSecondClass在构造函数中设置为 0。

SecondClass构造函数完成后,对象被分配给(仍在初始化)second中的字段。FirstClass之后i被初始化为 3。现在FirstClass调用了 get 的构造函数,并为 i 分配了返回值DoSomething(),在这种情况下将为 0。

如需进一步参考,您可以查看:http ://www.artima.com/designtechniques/initializationP.html 它提供了对象初始化的描述。

TL;DR 所以回答你的问题:

  1. 是的,this可以在初始化实例变量时使用。
  2. 应该避免这种情况,因为如果有人移动字段(例如对它们进行排序)可能会改变您的类的行为,这是无法预料的。
于 2013-05-17T06:25:36.763 回答
0

这在 Java 中有两种可能的变体;首先,当您希望(从外部范围)引用全局变量时。例如:

class First {

    private int x;

    public void setX(int x) {
    this.x = x; // outer x is equal to the parameter
    }

}

另一个用途,是引用当前对象的构造函数。例如:

public Color {

    private int red, green, blue;

    public void Color(int r, int g, int b) {
    red = red;
    green = g;
    blue = b;
    }

    public void setColor(int r, g, b) {
    this(r,g,b);
    } 

}

通过使用 3 个参数调用 this(1,2,3) 来告诉编译器您希望引用带有 3 个参数的构造函数。

希望有帮助!

于 2013-05-17T06:37:12.277 回答