Java编译混乱:为什么要编译这段代码?

Java编译混乱:为什么要编译这段代码?,java,javac,Java,Javac,我不确定为什么这段代码(在运行时产生stackoverflow)会编译: import java.io.*; import java.util.*; public class StackOverflow { StackOverflow overflow = new StackOverflow(); public void myCall() { overflow.myPrint(); } public static void main(String[]

我不确定为什么这段代码(在运行时产生stackoverflow)会编译:

import java.io.*;
import java.util.*;

public class StackOverflow {
   StackOverflow overflow = new StackOverflow();

   public void myCall() {
       overflow.myPrint();
   }

   public static void main(String[] args) {
       StackOverflow newStackOverflow = new StackOverflow();
       newStackOverflow.myCall();
   }

   public void myPrint() {
       System.out.println("I am confused!");
   }
}

我感到困惑的原因是,在类定义中,我试图创建我试图定义的类的对象。这不应该是编译时错误吗?

不,这不是Java的问题。动态声明非常快速和宽松,在使用之前不需要声明东西。它会在以后处理所有这些问题,并使语言更易于使用。

它会给您一个堆栈溢出异常,因为每次创建堆栈溢出时,行

StackOverflow overflow = new StackOverflow();

创建另一个StackOverflow,这将创建另一个StackOverflow,依此类推,直到堆栈最大化并抛出异常。

我认为,编译器应该捕获无限循环的递归版本,但是,在语言定义中没有任何东西可以使对象在实例化时构造自身的另一个实例是非法的


这当然是我希望代码分析工具会抱怨的问题。

这不是编译时错误,因为编译器无法判断它是否将在编译时无限生成

您和我都可以看到这一点,并且可以肯定地看到它,但是编译器只关心声明是否正确。这个声明在语法上没有什么不合法的地方,这就是为什么编译器会放弃它


它与相关,因为程序无法报告是否将成功停止。

否。Java在编译时不检测无限循环或无限递归。 您可以编译像您的代码或下面的方法一样的代码,但很好,它会在运行时崩溃

 private int recurseForever(){
     return recurseForever();
 }
在您的例子中,构造函数创建类的一个新实例,并因此调用另一个构造函数,依此类推


至于为什么不这样做:因为不可能对所有情况都这样做(停止问题),并将资源投入到部分解决方案中(这只在某些情况下有效,但总是会使编译变慢,而且对每个人都是如此,可能还会引入错误,因为编译器现在更复杂了)显然,这不是一个好主意。

因为编译器不可能捕获所有可能的方法,所以不要求编译器捕获它。通常,无限递归只能在运行时检测到。(即便如此,你也只能检测到它超出了允许的限制。)

我感到困惑的原因是,在类定义中,我试图创建我试图定义的类的对象。这不应该是编译时错误吗


当编译器看到这段代码时,您已经完全定义了类,并且编译器可以创建此类的对象而不会产生歧义。因此,这不会显示编译时错误。

不,它不应该给出错误,方法是写入以下行
StackOverflow overflow=new StackOverflow()实际上是在类中创建实例变量。这一行
StackOverflow newStackOverflow=newStackOverflow()
将创建局部变量

如果您按照如下方式更新代码,那么它也将正常工作

import java.io.*;
import java.util.*;

public class StackOverflow {
   private static StackOverflow overflow = new StackOverflow();

   public void myCall() {
       overflow.myPrint();
   }

   public static void main(String[] args) {
       overflow.myCall();
   }

   public void myPrint() {
       System.out.println("I am confused!");
   }
}

我认为OP知道堆栈溢出的原因,并且询问为什么允许这样的构造。这甚至不会编译。您不能直接从静态方法
main
引用实例变量
overflow
。我不知道编译器的情况,但肯定有一些检查程序(如Findbugs)能够警告此问题?通常,您希望发现开发人员仅通过读取/运行代码不太可能发现的细微错误。在这种情况下,只要您尝试运行它,错误就会很明显。