为什么Java类有资格在方法中声明和初始化该类的对象?

时间:2018-12-19 04:38:52

标签: java

我有以下代码示例:

public class example {

  // class constructor
  public example(){}

  public void foo() {
    example o = new example();
    ...
  }
}

为什么可以编译并运行它而不导致StackOverflowError

2 个答案:

答案 0 :(得分:3)

您声称这将导致StackOverflowError。但是,StackOverflowError仅在方法无限期地直接或间接调用自身时才会发生。

在您的情况下,foo必须自我调用以使堆栈溢出。让我们看看foo的作用。

example o = new example();

调用foo时,它将调用example的构造函数,您在此处已声明:

public example(){}

构造函数在返回之前不执行任何操作。现在,构造函数已经返回,o被分配了新创建的实例,foo返回了,因为它无事可做。请注意,foo尚未再次调用。

您的误解可能是您错误地认为,在调用构造函数时,也会调用该类的所有方法。除非您实际上在构造函数中调用了方法,否则情况并非如此:

public example(){ foo(); } // this will cause stack overflow

答案 1 :(得分:2)

这样做的唯一原因是:foo并未作为example初始化的一部分被调用。

JLS defines a lifecycle关于类更新的内容。

在这种情况下,以下两行是相关的:

  

在每种情况下,都将特定的构造函数(第8.8节)标识为   被指定参数(可能没有参数)作为   类实例的创建过程。

     

无论何时创建新的类实例,都会分配内存空间   为此,在类中声明的所有实例变量都有空间   类型和在实例的每个超类中声明的所有实例变量   类类型,包括所有可能隐藏的实例变量   (§8.3)。

因此,这意味着除非在对象分配期间内存不足,否则不会遇到任何问题。

您显式调出StackOverflowError,这仅发生在进行的递归调用中。仅当您这样做是愚蠢的时,这种情况才会发生:

class Example {
    Example e;
    public Example() {
        e = new Example();
    }
}

每次调用new都将迫使该对象new直到另一个对象用尽,这仅仅是因为new处理对象取决于其自身是new

如果在构造函数中调用foo,也可以完成同样的事情。

class Example {
    Example e;
    public Example() {
        foo();
    }

    public void foo() {
        Example o = new Example();
    }

    public static void main(String[] args) {
        new Example();
    }
}

只要实例化对象时未调用foo,在 this 情况下,诸如您所声明的内容的声明不会导致{{1} }。