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

Являются ли переменные Java Final значениями по умолчанию?

У меня есть такая программа:

class Test {

    final int x;

    {
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

Если я попытаюсь выполнить его, я получаю ошибку компилятора как: variable x might not have been initialized на основе значений по умолчанию java. Я должен получить правильный результат ниже.

"Here x is 0".

Будут ли конечные переменные иметь значения dafault?

если я изменю свой код следующим образом:

class Test {

    final int x;

    {
        printX();
        x = 7;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }

}

Я получаю вывод как:

Here x is 0                                                                                      
Here x is 7                                                                                     
const called

Может кто-нибудь объяснить это поведение..

4b9b3361

Ответ 1

http://docs.oracle.com/javase/tutorial/java/javaOO/initial.html, глава "Инициализация членов экземпляра":

Компилятор Java копирует блоки инициализации в каждый конструктор.

То есть:

{
    printX();
}

Test() {
    System.out.println("const called");
}

ведет себя точно так же:

Test() {
    printX();
    System.out.println("const called");
}

Как вы можете видеть, как только экземпляр был создан, последнее поле не было определенно присвоено, тогда как (из http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.2):

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

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

Значения по умолчанию: http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.12.5

Во втором фрагменте x инициализируется при создании экземпляра, поэтому компилятор не жалуется:

Test() {
    printX();
    x = 7;
    printX();
    System.out.println("const called");
}

Также обратите внимание, что следующий подход не работает. Использование значения по умолчанию для конечной переменной разрешено только с помощью метода.

Test() {
    System.out.println("Here x is " + x); // Compile time error : variable 'x' might not be initialized
    x = 7;
    System.out.println("Here x is " + x);
    System.out.println("const called");
}

Ответ 2

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

UPD. Как указано @I4mpi, JLS определяет правило, что каждое значение должно быть определенно назначенный перед любым доступом:

Each local variable (§14.4) and every blank final field (§4.12.4, §8.3.1.2) must have a definitely assigned value when any access of its value occurs.

Однако у него также есть интересное правило относительно конструкторов и полей:

If C has at least one instance initializer or instance variable initializer then V is [un]assigned after an explicit or implicit superclass constructor invocation if V is [un]assigned after the rightmost instance initializer or instance variable initializer of C.

Итак, во втором случае значение x определенно назначено в начале конструктора, потому что оно содержит задание в конце его.

Ответ 3

Если вы не инициализируете x, вы получите ошибку времени компиляции, так как x никогда не инициализируется.

Объявление x как окончательное означает, что его можно инициализировать только в конструкторе или в initializer-block (так как этот блок будет скопирован компилятором в каждый конструктор).

Причина, по которой вы получаете 0, напечатанную до инициализации переменной, связана с поведением, определенным в руководстве (см. Default Values ​​"):

Значения по умолчанию

Не всегда необходимо назначать значение при объявлении поля. Поля, объявленные, но не инициализированные, будут установлены на разумный по умолчанию компилятор. Вообще говоря, это значение по умолчанию будет равна нулю или null, в зависимости от типа данных. Опираясь на такие значения по умолчанию, однако, обычно считаются плохим программированием стиль.

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

Data Type   Default Value (for fields)
--------------------------------------
byte        0
short       0
int         0
long        0L
float       0.0f
double      0.0d
char        '\u0000'
String (or any object)      null
boolean     false

Ответ 4

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

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

Предустановленное значение любого поля является значением по умолчанию.

Ответ 5

Все нефинальные поля класса инициализируются значением по умолчанию (0 для числовых типов данных, false для булевых и null для ссылочных типов, иногда называемых сложными объектами). Эти поля инициализируются перед тем, как конструктор (или блок инициализации экземпляра) выполняется независимо от того, были ли объявлены поля до или после конструктора.

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

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

Ответ 6

Насколько мне известно, компилятор всегда будет инициализировать переменные класса по умолчанию (даже конечные переменные). Например, если вы должны инициализировать int для себя, int будет установлен на значение по умолчанию 0. См. Ниже:

class Test {
    final int x;

    {
        printX();
        x = this.x;
        printX();
    }

    Test() {
        System.out.println("const called");
    }

    void printX() {
        System.out.println("Here x is " + x);
    }

    public static void main(String[] args) {
        Test t = new Test();
    }
}

Вышеприведенный текст напечатает следующее:

Here x is 0
Here x is 0
const called

Ответ 7

Позвольте мне выразить это простейшими словами.

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

Требуется инициализировать это до того, как объект будет инициализирован.

Мы можем использовать блоки инициализации для инициализации конечных переменных. Теперь блоки инициализации имеют два типа static и non-static

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

После этого он будет вызывать все инициализаторы (в вашем случае нестатический инициализатор).

В вашем вопросе case 1: даже после завершения блока инициализации конечная переменная остается неинициализированной, что обнаруживает компилятор ошибок.

В случае 2: инициализатор инициализирует конечную переменную, поэтому компилятор знает, что до инициализации объекта окончание уже инициализировано. Следовательно, он не будет жаловаться.

Теперь вопрос в том, почему x принимает нуль. Причина здесь заключается в том, что компилятор уже знает, что ошибки нет, поэтому при вызове метода init все финалы будут инициализированы по умолчанию, а флаг установлен, который они могут изменить при фактическом присваивании, аналогичном x=7. См. Вызов инициализации ниже:

enter image description here

Ответ 8

Если я попытаюсь выполнить его, я получаю ошибку компилятора как: переменная x может не быть инициализирована на основе значений по умолчанию java. Я должен получить правильный результат ниже.

"Здесь x равно 0".

Нет. Вы не видите этот вывод, потому что в первую очередь вы получаете ошибку времени компиляции. Конечные переменные получают значение по умолчанию, но спецификация Java Language Specification (JLS) требует, чтобы вы инициализировали их до конца конструктора (LE: здесь я включаю блоки инициализации), иначе вы получите ошибку времени компиляции, которая предотвратит компиляцию и выполнение кода.

В вашем втором примере соблюдается требование, почему (1) компилируется ваш код и (2) вы получаете ожидаемое поведение.

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