2011-01-14 6 views
2
public class Foo { 
    private int var; 

    public Foo() { 
     var = 10; 
    } 
} 

В этом фрагменте кода будет var первым будет присвоено значение по умолчанию, а затем переведен на 10, или же он будет назначен на 10 непосредственно без присваивается значение по умолчанию?Инициализация переменных поля

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

ответ

3

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

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

+0

Ну, это не совсем так. Поскольку поле является частным, и не определены никакие аксессоры, кто говорит, что это невозможно оптимизировать? –

+0

@Axel: потребовалось бы больше работы, чтобы выяснить, какие места памяти в распределении не нужно обнулять, чем просто нулевое целое, поэтому оптимизация будет иметь неприятные последствия. Также рассмотрим случай, когда конструктор выбирает частное поле с использованием отражения. Это случай сомнительной реальной ценности, но он демонстрирует одну из причин того, почему поля ведут себя так, как они делают во время построения объекта - даже необходимо определить случаи сомнительной ценности реального мира. Если бы это было поле ссылочного типа, оставив мусор там, так как оптимизация была бы серьезной угрозой безопасности ... – cdhowie

+0

Хорошо, давайте не будем путать вещи здесь. Компилятор выполняет поиск в _compile time_, чтобы сохранить инициализацию в _run time_ –

3

Согласно spec: (раздел 4.2.15)

Первый 0.

Тогда 10.

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

+0

-1, но только если вы обращаетесь к переменной перед ее использованием в конструкторе! – Daniel

+0

Но мы говорим о доступе внутри конструктора. –

+1

Вы можете вызвать метод, который смотрит на «var» перед его назначением, даже для конечных переменных. –

1

Сначала ему будет присвоено значение по умолчанию. В частности, если 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

Не обижайтесь, но кто заботится о том, что C# сделал бы? –

+0

@Erick: Множество людей, учитывая, что они похожие языки, и многие используют оба. Учитывая, что это может вытащить людей, я думаю, что это стоит отдельного предложения в конце подробного сообщения. –

0

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

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

5

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

  • Класс конструктор только присваивает значение 10 (bipush и putfield). Конструктор класса сначала не назначает 0, а затем 10.
  • VM будет иметь значение по умолчанию 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 
+0

+1 блестящий !!! –

0

Спецификация языка говорит:

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

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

Затем вызывается конструктор.

Смежные вопросы