Инициализация полевых переменных - PullRequest
2 голосов
/ 14 января 2011
public class Foo {
    private int var;

    public Foo() {
        var = 10;
    }
}

В этом фрагменте кода будет ли var сначала назначаться значение по умолчанию, а затем переназначаться на 10, или ему будет назначено 10 напрямую, без назначения значения по умолчанию?

Довольно банальный вопрос, но мне любопытно.

Ответы [ 6 ]

5 голосов
/ 14 января 2011

Если вы посмотрите на декомпилированный байт-код Foo.class, вы заметите следующее:

  • Сам конструктор класса присваивает только значение 10 (bipush и putfield).Конструктор класса сначала не присваивает 0, а затем 10.
  • Виртуальная машина будет иметь значение по умолчанию 0 для поля при каждом обращении к нему, независимо от того, из какого кода.Таким образом, это значение по умолчанию нигде не будет отображаться - по крайней мере, в байт-коде класса или других классов, которые обращаются к полю, например, с помощью отражения.Примитивные значения по умолчанию запекаются в ВМ.
  • Явная установка значения по умолчанию приведет к созданию другого байт-кода, см. Второй пример.

.

public class Foo {

  private int var;

  public Foo();
     0  aload_0 [this]
     1  invokespecial java.lang.Object() [10]
     4  aload_0 [this]
     5  bipush 10
     7  putfield Foo.var : int [12]
    10  return

Если вы пишетеследующее:

public class Foo {
    private int var = 0;

    public Foo() {
        var = 20;
    }
}

байт-код будет:

 0  aload_0 [this]
 1  invokespecial java.lang.Object() [10]
 4  aload_0 [this]
 5  iconst_0
 6  putfield Foo.var : int [12]
 9  aload_0 [this]
10  bipush 20
12  putfield Foo.var : int [12]
15  return

Следующий пример показывает, что доступ к переменной по-прежнему не приведет к присвоению какого-либо значения:

public class Foo {
    private int var;

    public Foo() {
        System.out.println(var);
        var=10;
    }
}

Этот код будет печатать 0, потому что getField Foo.var с кодом операции 8 поместит «0» в стек операндов:

public Foo();
   0  aload_0 [this]
   1  invokespecial java.lang.Object() [10]
   4  getstatic java.lang.System.out : java.io.PrintStream [12]
   7  aload_0 [this]
   8  getfield Foo.var : int [18]
  11  invokevirtual java.io.PrintStream.println(int) : void [20]
  14  aload_0 [this]
  15  bipush 10
  17  putfield Foo.var : int [18]
  20  return
3 голосов
/ 14 января 2011

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

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

3 голосов
/ 14 января 2011

Согласно спецификациям : (раздел 4.2.15)

Первые 0.

Тогда 10.

Если вы сначала прочитаете это в конструкторе, вы получите 0.

public class Foo {
    private int var;

    public Foo() {
        System.out.println(var); //0
        var = 10;
    }
}
1 голос
/ 14 января 2011

Сначала будет дано значение по умолчанию. В частности, если Foo был получен из Bar, и конструктор Bar мог каким-то образом получить значение var (например, через виртуальный метод, объявленный в Bar и переопределенный в Foo), это значение по умолчанию было бы видимым, даже если переменная окончательный. Например:

class Parent {

    public Parent() {
        showVariables();
    }

    public void showVariables() {
    }
}

class Child extends Parent {
    private final int x;

    public Child() {
        x = 10;
    }

    @Override
    public void showVariables() {
        System.out.println("x = " + x); // Prints x = 0
    }
}

public class Test {

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

Обратите внимание, что это все еще происходит, даже когда поле инициализируется в точке объявления:

public class Foo {
    private int var = 10;

    public Foo() {
        // Implicit call to super constructor - this occurs *before*
        // var is assigned the value 10
    }
}

В этом отношении Java отличается от C #. В C # var будет присвоено значение 10 перед вызовом базового конструктора.

0 голосов
/ 14 января 2011

В спецификации языка сказано:

В противном случае все переменные экземпляра в новом объекте, включая объявленные в суперклассах, инициализируются в их значения по умолчанию (§4.12.5).

http://java.sun.com/docs/books/jls/third_edition/html/execution.html#44410

Тогда конструктор вызывается.

0 голосов
/ 14 января 2011

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

Если вы посмотрите на байт-код, единственный код - это присвоение нового значения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...