Почему две программы имеют пересылающие ошибки, а третья - нет?
Следующее не компилируется, передавая сообщение "незаконное прямое обращение":
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 делает?
Ответы
Ответ 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
Здесь нам нужно понять, что во втором фрагменте кода вы используете блок и это ключевое слово.
- Блок выполняется, если объект создан.
- Это означает, что объект создается в области кучи.
- Внешне используйте это ключевое слово, чтобы получить значение переменной экземпляра.
- Здесь создается объект со значениями по умолчанию, которые будут возвращаться как значение.
- Без использования этого ключевого слова вы также не можете скомпилировать второй фрагмент.