Java中的构造函数重载

4
我在这段代码片段中遇到了一个错误。
错误是:无法在超类构造函数被调用之前引用x(并指出注释1处的语句)
class Con{
    int x =10;

    Con(){
        this(++x); //1
        System.out.println("x :"+x);
    }

    Con(int i){
        x=i++;
        System.out.println("x :"+x);
    }
}

在主方法中,我有以下语句:

    Con c1=new Con();

我不理解这个错误。有人能解释一下这里实际发生了什么吗?


1
我是字面意思。在调用super()之前,您不能引用x。通常情况下,这会隐式发生,但由于您有一个this调用,因此super调用被抑制,因此出现了该消息。我想不到任何方法可以实现您想要的功能,除非稍微重构一下。 - Hot Licks
调用构造函数中的this()有什么用途?有人能解释一下吗?我可以想到一些使用带参数构造函数调用this()的情况... - boxed__l
1
@boxed__l this() 用于调用同一类中的重载构造函数。假设您有一个名为 constructor 的构造函数,它需要 4 个参数,但对于其中的 2 个字段,已经有另一个构造函数来进行初始化,在这种情况下,您需要使用 this(x,y) 来调用您的两个参数构造函数。 - Algorithmist
@Algorithmist:编译器是否会在所有构造函数(重载/空)中添加super()?如果是这样,那么不会导致多个super()调用吗? - boxed__l
@Algorithmist 哎呀,已经有人回答了 :p 抱歉 - boxed__l
1
即使 OP 接受了答案,学习也应该继续,尽可能从每个问题中获取更多信息。关于你的问题,是的,如果你没有指定任何内容,编译器总是会添加 super() 调用,这对带参构造函数也适用。如果你明确调用了 super(),那么它不会再从自己的方面添加 super()。 - Algorithmist
3个回答

5
创建类的实例时,构造函数首先调用其超类构造函数初始化超类字段。只有所有超类构造函数都运行完毕后,当前构造函数才继续初始化自己的字段。
当您在构造函数中添加"this()"调用时,它不会调用超类构造函数。这是因为构造函数的第一条语句要么是链到超类构造函数 - 使用"super()",要么是同一类的不同构造函数 - 使用"this()"。
因此,您不能在"this()"中传递字段,因为该字段尚未初始化。但是这真的没有意义,您为什么要尝试这样做呢?
请记住,编译器将字段初始化代码移动到类的每个构造函数中。因此,您的构造函数有效地相当于:
Con() {
    this(++x); //1

    // This is where initialization is done. You can't access x before it.
    x = 10;
    System.out.println("x :"+x);
}

即使使用了super()调用,这个问题仍然存在。因此,下面的代码(假设Con扩展了另一个具有参数化构造函数的类)也会产生同样的错误:
Con() {
    super(++x); //1
    System.out.println("x :"+x);
}

由于我没有扩展任何类,我们可以说超类构造函数是参数化构造函数吗? - chathura
1
@chathura2020。当您的类不继承任何类时,您需要使用super()调用Object类的0-arg构造函数。 - Rohit Jain
3
@chathura2020,你所有的类至少继承一个类。因此,你不能说你的类没有继承任何其他类。如果它没有明确地继承其他类,则Object类隐式地成为其超类。 - Rohit Jain
super(++x); 不起作用,因为 Object 类没有带参数的构造函数 :). 无论如何,这是一个很棒的答案。 - Algorithmist
@RohitJain:在上面的代码中,如果我们使用“static int x = 10;”,那么它可以编译通过。这意味着静态变量在调用超类构造函数之前被初始化。因此顺序是这样的-> 初始化静态变量(加载类Con),初始化超类,初始化子类。如果我错了,请纠正我。 - chathura
@chathura2020。静态字段在第一次加载类时初始化。因此,当您创建类的实例时,在构造函数内部时,静态字段已经初始化。所以,在某种程度上,您是正确的。 - Rohit Jain

2
Con(){
    this(++x); //1
    System.out.println("x :"+x);
}

此时,Con尚不存在。它通过调用另一个构造函数进行实例化。这意味着x还不存在(它会在另一个构造函数实例化时创建)。因此,您现在无法引用它。

如果您确实需要引用它,则必须使用静态变量。

private static int x = 10;

0
构造函数中的第一个调用只能是this()super(),如果没有其中任何一个,则编译器会自动插入对super的调用,但在您的构造函数中,您使用this()调用了其他构造函数。基本上,每当您构造一个对象时,超类首先初始化,然后子类成员得到初始化。因此,您无法引用未初始化的成员,因为它们在超类成员和超类本身之后得到初始化。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接