Неинициализированные члены класса в Java не вызывают ошибок компилятора. однако, локальные переменные. Зачем?
Рассмотрим следующий фрагмент кода в Java. Он не будет компилироваться.
package temppkg;
final public class Main
{
private String x;
private int y;
private void show()
{
String z;
int a;
System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.
System.out.println(y); // Works fine displaying its default value which is zero.
System.out.println(z.toString()); // Causes a compile-time error - variable z might not have been initialized.
System.out.println(a); // Causes a compile-time error - variable a might not have been initialized.
}
public static void main(String []args)
{
new Main().show();
}
}
Почему члены класса (x и y), указанные в приведенном выше фрагменте кода, не выдают ошибку времени компиляции, даже если они явно не инициализированы, и для инициализации требуются только локальные переменные?
Ответы
Ответ 1
В случае сомнений проверьте Спецификацию Java Language (JLS).
В введение вы найдете:
В главе 16 описывается точный способ обеспечения языка что локальные переменные определенно установлены перед использованием. Хотя все остальные переменные автоматически инициализируются значением по умолчанию, Java язык программирования не автоматически инициализирует локальные переменные чтобы избежать маскировки ошибок программирования.
В первом абзаце глава 16 указано,
Каждая локальная переменная и каждое пустое конечное поле должны иметь определенную присвоенное значение, когда происходит любой доступ к его значению.... Компилятор Java должен провести конкретный консервативный анализ потока, чтобы убедиться что для каждого доступа локальной переменной или пустого конечного поля f, f определенно назначается перед доступом; в противном случае время компиляции ошибка должна произойти.
Значения по умолчанию сами находятся в разделе раздел 4.12.5. Раздел открывается с помощью
Каждая переменная класса, переменная экземпляра или компонент массива инициализируется значением по умолчанию при его создании.
... и затем перечислит все значения по умолчанию.
JLS действительно не так уж сложно понять, и я все больше и больше использовал его, чтобы понять, почему Java делает то, что он делает... в конце концов, это библия Java!
Ответ 2
Почему они выдают предупреждение компиляции?, поскольку переменные экземпляра String получат значение по умолчанию null, а int получит значение по умолчанию 0.
Компилятор не знает, что x.toString() вызовет исключение во время выполнения, потому что значение null на самом деле не установлено до истечения времени выполнения.
Ответ 3
Члены класса могли быть инициализированы в другом месте вашего кода, поэтому компилятор не может проверить, были ли они инициализированы во время компиляции.
Ответ 4
В общем случае компилятор не мог точно знать, был ли элемент класса ранее или не был инициализирован. Например, у вас может быть метод setter, который устанавливает значение для члена класса и другой метод, который обращается к этому члену. Компилятор не может выдать предупреждение при обращении к этой переменной, поскольку он не может знать, был ли вызван сеттер раньше или нет.
Я согласен, что в этом случае (член является приватным, и нет единого метода, который записывает переменную), похоже, что он может вызвать предупреждение от компилятора. Ну, на самом деле вы все еще не уверены, что переменная не была инициализирована, поскольку к ней можно было получить доступ через рефлексию.
Тонкости намного проще с локальными переменными, так как они не могут быть доступны извне метода или даже с помощью рефлексии (afaik, пожалуйста, исправьте меня, если не так), поэтому компилятор может быть немного полезнее и предупредить нас о неинициализированные переменные.
Надеюсь, этот ответ поможет вам:)
Ответ 5
Элементы-члены автоматически инициализируются значениями по умолчанию при создании (создании экземпляра) объекта. Это справедливо, даже если вы их вручную инициализировали, сначала они будут инициализированы значениями по умолчанию, а затем значениями, которые вы указали.
Это небольшая небольшая статья, но она объясняет это: Инициализация объектов в Java
В то время как локальные переменные (те, которые объявлены внутри метода) не инициализируются автоматически, это означает, что вы должны делать это вручную, даже если вы хотите, чтобы они имели свои значения по умолчанию.
Вы можете увидеть, какие значения по умолчанию для переменных с разными типами данных здесь.
Значение по умолчанию для переменных ссылочного типа null
. Вот почему он бросает NullPointerException
на следующее:
System.out.println(x.toString()); // Causes a NullPointerException but doesn't issue a compiler error.
В следующем случае компилятор достаточно умен, чтобы знать, что переменная еще не инициализирована (потому что она локальная, и вы ее не инициализировали), поэтому проблема компиляции:
System.out.println(z.toString()); // "Cuases a compile-time error - variable z might not have been initialized.