Подтвердить что ты не робот

Почему две программы имеют пересылающие ошибки, а третья - нет?

Следующее не компилируется, передавая сообщение "незаконное прямое обращение":

class StaticInitialisation {

    static
    {
        System.out.println("Test string is: " + testString);
    }

    private static String testString;

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

Однако, компилируется следующее:

class InstanceInitialisation1 {

    {
        System.out.println("Test string is: " + this.testString);
    }

    private String testString;

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

Но следующее не компилируется, передавая сообщение "незаконное прямое обращение":

class InstanceInitialisation2 {

        private String testString1;

    {
        testString1 = testString2;
    }

    private String testString2;

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

Почему StaticInitialisation и InstanceInitialisation2 не компилируются, а InstanceInitialisation1 делает?

4b9b3361

Ответ 1

Это описано в разделе 8.3.3 JLS:

Использование переменных класса, декларации которых появляются по тексту после использования, иногда ограничены, хотя эти переменные класса находятся в области видимости (§6.3). В частности, это ошибка времени компиляции, если выполняются все следующие условия:

  • Объявление переменной класса в классе или интерфейсе C появляется после использования переменной класса text

  • Использование - простое имя либо в инициализаторе переменной класса C, либо в статическом инициализаторе C;

  • Использование не в левой части задания;

  • C - самый внутренний класс или интерфейс, охватывающий использование.

Использование переменных экземпляра, объявления которых появляются по тексту после использования, иногда ограничены, хотя эти переменные экземпляра находятся в области видимости. В частности, это ошибка времени компиляции, если выполняются все следующие условия:

  • Объявление переменной экземпляра в классе или интерфейсе C появляется после использования переменной экземпляра text

  • Использование - простое имя либо в инициализаторе переменной экземпляра C, либо в инициализаторе экземпляра C;

  • Использование не в левой части задания;

  • C - самый внутренний класс или интерфейс, охватывающий использование.

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

Если вы измените его на:

System.out.println("Test string is: " + testString);

... тогда он не будет компилироваться.

Или в противоположном направлении вы можете изменить код в статическом блоке инициализатора на:

System.out.println("Test string is: " + StaticInitialisation.testString);

Нечетный, но так, как это происходит.

Ответ 2

Давайте рассмотрим эти два примера, я думаю, это позволит вам понять.

public class InstanceAndSataticInit {

    {
        System.out.println("Test string is (instance init): " + this.testString);
    }

    static{
        System.out.println("Test string is (static init ): " + InstanceAndSataticInit.testStringStatic);
    }

    public  static String testStringStatic="test";
    public  String testString="test";

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

}

Вывод:

Test string is (static init ): null
Test string is (instance init): null

И

public class InstanceAndSataticInitVariableFirst {

    public  static String testStringStatic="test";
    public  String testString="test";

    {
        System.out.println("Test string is (instance init): " + this.testString);
    }

    static{
        System.out.println("Test string is (static init ): " + InstanceAndSataticInitVariableFirst.testStringStatic);
    }



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


}

вывод:

Test string is (static init ): test
Test string is (instance init): test

Итак, вы можете сказать, что последовательность такая.

  • Статическая переменная будет создана, но не будет инициализирована.

  • Статическая инициализация будет выполняться в соответствии с заданной последовательностью.

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

По порядку я подразумеваю появление в коде.

Я думаю, что эти шаги отвечают на ваш два не работающих пример StaticInitialisation и InstanceInitialisation2

Но в случае вашего второго рабочего примера InstanceInitialisation1 с помощью ключевого слова this вы действительно помогаете компилятору упускать из вида текстовую иерархию. То же самое происходит в случае static, когда я вызываю InstanceAndSataticInit.testStringStatic в моем первом примере InstanceAndSataticInit

Ответ 3

Простая причина - слишком дорого или невозможно проанализировать и запретить все прямые ссылки. например.

{
    print( getX();  );    // this.x
    print( that().x );    // this.x
}

int x;
int getX(){ return x; }

This that(){ return this; }

Спецификация оседает на запрете некоторых простых случаев, свидетельствующих об ошибках обычного программиста.

См. также Рекурсивный инициализатор работает, когда я добавляю "his" ?

Ответ 4

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

  • Блок выполняется, если объект создан.
  • Это означает, что объект создается в области кучи.
  • Внешне используйте это ключевое слово, чтобы получить значение переменной экземпляра.
  • Здесь создается объект со значениями по умолчанию, которые будут возвращаться как значение.
  • Без использования этого ключевого слова вы также не можете скомпилировать второй фрагмент.