为什么Java静态方法能调用构造函数,但不能引用this?

11

我的假设:

  1. 静态方法无法调用非静态方法。
  2. 构造函数是一种没有返回类型的方法

鉴于以下示例...

    public class Main {
        public static void main(String[] args) {
            Main p = new Main();  // constructor call
            k();  // [implicit] `this` reference
        }

        protected Main() {
            System.out.print("1234");
        }

        protected void k() {
        }
    }
  • 这一行代码打印了1234:Main p = new Main()
  • 这一行代码抛出一个异常:k()

为什么示例代码会执行这样的两个操作?它们是否与我的前提相矛盾?我的前提是正确的吗?


5
如果从静态方法中无法调用构造函数,那么你将永远无法构造任何对象。 - JB Nizet
3个回答

11

1 - 静态方法不能调用非静态方法。

当然它们可以,但是它们需要一个对象来调用该方法

在静态方法中,没有可用的this引用,所以foo()(等同于this.foo())是不合法的。

2 - 构造函数类似于没有返回类型的方法。

如果它们应该被与方法相比较,我会说构造函数更接近于非静态方法(因为构造函数内确实有一个this引用)。

有了这个观点,你应该明白为什么静态方法可以毫无问题地调用构造函数了。


因此,总结一下:

Main p = new Main();

没问题,因为new Main()不依赖于任何现有对象。

k();

这样做是不可以的,因为它等同于this.k(),而this在你的(静态)主方法中不可用。


让人们困惑的是,new main() 不是一个简单的构造函数调用,而是一种更广泛的分配和初始化过程中使用一元运算符 new 来指定要调用哪个构造函数的应用。 - Marko Topolnik
@MarkoTopolnik,JLS参考? - aioobe
你真的需要它吗,因为这很明显吗?应用new会从堆中分配内存--这不是构造函数中写的。另一个显然的事实是它还调用了所有实例初始化器--这也没有在构造函数代码中写任何东西。它还使用this定义了隐式代码而不是调用者来调用构造函数。构造函数实际上只是一个回调。 - Marko Topolnik
我之所以问这个问题,是因为我怀疑你对Java的编译和执行方式进行了特定实现的假设。当然,构造对象涉及运行实例初始化程序等内容,但分配过程、它如何最终出现在称为堆的东西上以及this如何突然出现,我不确定是否在语言规范中有定义。(虽然我可能错了。) - aioobe
好的,但是JLS也没有指定new X()只是一个函数调用--这就是核心问题,因为人们经常将其视为这样,并对Java中的其他类型的函数进行误导性比较。 - Marko Topolnik

6

构造函数在这方面不是普通的方法。构造函数的整个目的就是构建一个类的新实例。

因此,它也可以在静态范围内被调用。想一想:如果你需要一个现有的类实例来创建一个新的实例,那么你根本无法实例化它。

需要澄清几点:

静态方法不能调用非静态方法。

不完全正确。你可以从静态方法中调用非静态方法,只需要将其作用域限定为该类的特定对象即可。例如:

p.k();

在你上面的代码示例中,这将完美地起作用。

该调用

k();

在实例方法(非静态方法)中使用会很好。这等效于

this.k();

隐式的 this 指代当前类的实例。当编译器在实例方法内看到一个未经限定的调用,例如 k(),它会自动将其作用域限定为 this.。然而,由于静态方法不与类的任何实例绑定,您(以及编译器)无法在静态方法中引用 this。因此,您需要显式地命名类的一个实例来调用实例方法。

5

规则很简单:
1 - 静态方法不能调用非静态方法。

这并不完全正确。一个静态方法可以通过“目标”引用调用非静态方法。例如,在静态方法中可以这样使用:

Integer x = Integer.valueOf(10);
int y = x.intValue(); // Instance method!

“真正”的问题是“在静态方法中没有‘this’引用”。
2-构造函数有点像没有返回类型的方法。
说实话,这不是一个非常有用的模型。从调用者的角度来看,将构造函数视为具有与声明类相同的返回类型的静态方法更有意义,但即使这也并不是完美的模型。
我建议您将构造函数视为不同类型的成员。接受构造函数和方法之间的差异,而不是试图隐藏它们。

你认为将构造函数比作一个无返回值的回调函数,作为对象初始化的一部分,这个想法如何?在我看来,这种间接性会导致人们犯错误的假设。 - Marko Topolnik
@MarkoTopolnik:这取决于你是从调用者的角度考虑它(在这种情况下,“返回void”是无意义的,因为你可以使用结果),还是从实现的角度考虑它(在这种情况下,它是有意义的)。我更喜欢将其视为自己的一种类型,而不是试图将其比作方法的曲折类比。 - Jon Skeet
如果考虑到调用者仅应用new而没有调用任何函数,那么这就不是无意义的。这实际上是我的主要观点——必须尊重new,而不是忽略它。该运算符的返回值是引用,构造函数仍然可以被视为一个返回void的回调函数。我同意比喻只能走得这么远,但在学习新知识时它们仍然是有帮助的。 - Marko Topolnik
@MarkoTopolnik:从最简单的调用者角度来看,他们正在调用构造函数并返回一个引用。我认为没有必要使它比这更复杂(再次强调,从调用者的角度来看)...而且我认为将其视为回调*更加复杂。 - Jon Skeet
1
@MarkoTopolnik:类实例创建表达式 :) - Jon Skeet
显示剩余3条评论

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