Как статическая переменная доступна до объявления? - PullRequest
5 голосов
/ 13 сентября 2010
public class Main {

    static int x = Main.y;
//  static int x = y; //Not allowed; y is not defined
    static int y = x;
    public static void main(String[] args) {
        System.out.println(x);//prints 0
    }
}

Почему мне разрешено использовать y в классе, но не напрямую?

Когда определяется y?

Ответы [ 5 ]

10 голосов
/ 13 сентября 2010

Точные правила, управляющие прямой ссылкой на переменные класса, описаны в разделе §8.3.2.3 JLS:

8.3.2.3 Ограничения на использование полей при инициализации

Декларация члена должна появляются в тексте перед использованием только если член является экземпляром (соответственно static) поле класса или интерфейс C и все выполняются следующие условия:

  • Использование происходит в экземпляре (соответственно static) переменной инициализатор C или в экземпляре (соответственно static) инициализатор C.
  • Использование не в левой части назначения.
  • Использование через простое имя.
  • C является самым внутренним классом или интерфейсом, включающим использование.

Ошибка времени компиляции происходит, если любой из четыре вышеуказанных требования не являются мет.

Это означает, что ошибка во время компиляции Результаты тестовой программы:

  class Test {
      int i = j;  // compile-time error: incorrect forward reference
      int j = 1;
  }

тогда как следующий пример компилируется без ошибок:

  class Test {
      Test() { k = 2; }
      int j = 1;
      int i = j;
      int k;
  }

хотя конструктор (§8.8) для теста относится к поле к, которое объявлено три строки позже.

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

class Z {
  static int i = j + 2; 
  static int j = 4;
}

и

class Z {
  static { i = j + 2; }
  static int i, j;
  static { j = 4; }
}

приводит к ошибкам во время компиляции. Доступ по методам не проверяется в вот так вот:

class Z {
  static int peek() { return j; }
  static int i = peek();
  static int j = 1;
}
class Test {
  public static void main(String[] args) {
      System.out.println(Z.i);
  }
}

производит вывод:

0

потому что инициализатор переменной для i использует метод класса Peek для доступа значение переменной j перед j был инициализирован его переменной инициализатор, в этот момент он еще имеет значение по умолчанию (§4.12.5) .

2 голосов
/ 13 сентября 2010

Я бы предположил, что при использовании класса компилятор будет откладывать поиск переменной до тех пор, пока класс не будет завершен, поэтому он находит y, но если вы просто определите его как комментарий, он еще не определен, поэтому он потерпит неудачу *

1 голос
/ 13 сентября 2010

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

При использовании Main.y, я думаю, что происходит следующее:

  • Вы загружаете Main, x инициализация называется
  • Когда вы определяете x дляравный Main.y, компилятор видит ссылку на класс, поэтому он заканчивает определение x текущим значением члена y класса Main.Он обрабатывает этот случай, как если бы Main был другим классом.

Обратите внимание, что в этом случае при инициализации x, y не определено в данный момент.Так что x будет иметь значение 0.

0 голосов
/ 13 сентября 2010

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

0 голосов
/ 13 сентября 2010

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

...