Путаница при компиляции Java: почему этот код компилируется? - PullRequest
2 голосов
/ 26 марта 2012

Я не уверен, почему этот код (дает 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!");
   }
}

Причина, по которой я запутался, заключается в том, что в определении класса я пытаюсь создать объект изкласс, который я пытаюсь определить.Разве это не должно быть ошибкой времени компиляции?

Ответы [ 8 ]

5 голосов
/ 26 марта 2012

Это не ошибка во время компиляции, потому что компилятор не может определить, будет ли он генерироваться бесконечно во время компиляции.

Вы и я можем посмотреть на это и увидеть это наверняка, но компилятор заботится только о том, чтобы объявления были правильными. В этом объявлении нет ничего синтаксического недопустимого, поэтому компилятор просто его пропустит.

Это связано с проблемой остановки , в которой программа не может сообщить, если она успешно остановится.

1 голос
/ 26 марта 2012

Можно предположить, я полагаю, что компилятор должен перехватить эту рекурсивную версию бесконечного цикла, но в определении языка нет ничего, что делало бы невозможным для объекта создание другого экземпляра самого себя при создании экземпляра. *

Это, безусловно, то, что я ожидаю от инструментов анализа кода, таких как FindBugs , на которые можно пожаловаться.

0 голосов
/ 26 марта 2012

Нет, это не должно давать ошибку, написав следующую строку StackOverflow overflow = new StackOverflow();, вы фактически создаете переменную экземпляра в классе.И эта строка StackOverflow newStackOverflow = new StackOverflow(); собирается создать вашу локальную переменную.

если вы обновите свой код следующим образом, то он также будет работать.

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!");
   }
}
0 голосов
/ 26 марта 2012

Причина, по которой я запутался, заключается в том, что в определении класса я пытаюсь создать объект класса, который пытаюсь определить. Разве это не должно быть ошибкой времени компиляции?

Когда компилятор видит этот кусок кода, вы уже полностью определили свой класс, и компилятор может создать объект этого класса без неоднозначности. Следовательно это не показывает ошибок времени компиляции

0 голосов
/ 26 марта 2012

Поскольку компилятору действительно невозможно отловить все возможные способы, которыми вы можете это сделать, нет требования, чтобы компилятор его отлавливал. Как правило, неограниченная рекурсия обнаруживается только во время выполнения. (И даже тогда все, что вы можете обнаружить, это превышение разрешенных пределов.)

0 голосов
/ 26 марта 2012

Нет. Java ничего не делает для обнаружения бесконечных циклов или бесконечных рекурсий во время компиляции. Вы можете скомпилировать код, подобный вашему, или метод, приведенный ниже, просто отлично, он взорвется во время выполнения.

 private int recurseForever(){
     return recurseForever();
 }

В вашем случае конструктор создает новый экземпляр класса и, таким образом, вызывает другой конструктор и т. Д.

Что касается того, почему этого не происходит: потому что это невозможно сделать для всех случаев (остановка проблемы) и использование ресурсов при частичном решении (это работает только иногда, но делает компиляцию медленной всегда и для всех, возможно, также вносит ошибки, потому что компилятор теперь более сложный), по-видимому, не считалось хорошей идеей.

0 голосов
/ 26 марта 2012

Это дает вам исключение переполнения стека, потому что каждый раз, когда вы создаете StackOverflow, строка

StackOverflow overflow = new StackOverflow();

Создает другой StackOverflow, который создает еще один, и так далее, и так далее, пока стек не будет исчерпан ивыдает исключение.

0 голосов
/ 26 марта 2012

Нет, это не проблема с Java.Это довольно быстро и свободно о динамических декларациях и не нужно объявлять вещи перед их использованием.Он вычисляет все это позже и делает язык немного проще в использовании.

...