Kiedy są inicjalizowane zmienne statyczne?

87

Zastanawiam się, kiedy zmienne statyczne są inicjowane do wartości domyślnych. Czy to prawda, że ​​po załadowaniu klasy tworzone są (przydzielane) zmienne statyczne, a następnie wykonywane są inicjatory statyczne i inicjalizacje w deklaracjach? W którym momencie podano wartości domyślne? Prowadzi to do problemu odniesienia w przód.

Prosimy również o wyjaśnienie tego w odniesieniu do pytania zadanego w sekcji Dlaczego pola statyczne nie są inicjowane na czas? a zwłaszcza odpowiedź udzielona przez Kevina Brocka na tej samej stronie. Nie rozumiem trzeciego punktu.

Ankit
źródło
2
Możesz edytować swoje pytanie, aby zawierało cytat, do którego się odnosisz.
Oliver Charlesworth
1
Czy przeczytałeś specyfikację języka Java? Celowo jest to całkiem czytelny dokument. Jeśli to przeczytałeś, możesz zrozumieć, co się dzieje. Jeśli nie, możesz przynajmniej zadać bardziej szczegółowe pytanie ...
Maarten Bodewes
Myślę, że te pytania i odpowiedzi to podstęp z stackoverflow.com/questions/3499214 .
Stephen C

Odpowiedzi:

72

Z See Java Static Variable Methods :

  • Jest to zmienna należąca do klasy, a nie do obiektu (instancji)
  • Zmienne statyczne są inicjowane tylko raz, na początku wykonywania. Te zmienne zostaną zainicjalizowane jako pierwsze, przed inicjalizacją jakichkolwiek zmiennych instancji
  • Pojedyncza kopia do współużytkowania przez wszystkie instancje klasy
  • Dostęp do zmiennej statycznej można uzyskać bezpośrednio poprzez nazwę klasy i nie wymaga ona żadnego obiektu.

Zmienne instancji i klasy (statyczne) są automatycznie inicjowane do standardowych wartości domyślnych, jeśli nie uda się ich celowo zainicjować. Chociaż zmienne lokalne nie są inicjowane automatycznie, nie można skompilować programu, który nie może zainicjować zmiennej lokalnej ani przypisać wartości do tej zmiennej lokalnej przed jej użyciem.

Kompilator w rzeczywistości tworzy wewnętrzną procedurę inicjalizacji pojedynczej klasy, która łączy wszystkie statyczne inicjatory zmiennych i wszystkie statyczne bloki inicjatora kodu, w kolejności, w jakiej pojawiają się w deklaracji klasy. Ta pojedyncza procedura inicjalizacji jest uruchamiana automatycznie, tylko jeden raz, podczas pierwszego ładowania klasy.

W przypadku klas wewnętrznych nie mogą mieć pól statycznych

Wewnętrzna klasa to klasa zagnieżdżona, który nie jest bezpośrednio lub pośrednio zadeklarowane static.

...

Klasy wewnętrzne nie mogą deklarować statycznych inicjatorów (§8.7) ani interfejsów składowych ...

Klasy wewnętrzne nie mogą deklarować statycznych elementów członkowskich, chyba że są zmiennymi stałymi ...

Zobacz JLS 8.1.3 Klasy wewnętrzne i instancje zamykające

finalpola w Javie mogą być inicjowane niezależnie od miejsca ich deklaracji, jednak nie może to mieć zastosowania do static finalpól. Zobacz poniższy przykład.

final class Demo
{
    private final int x;
    private static final int z;  //must be initialized here.

    static 
    {
        z = 10;  //It can be initialized here.
    }

    public Demo(int x)
    {
        this.x=x;  //This is possible.
        //z=15; compiler-error - can not assign a value to a final variable z
    }
}

To dlatego, że istnieje tylko jedna kopia ze staticzmiennych związanych z typem, zamiast jednego związanego z każdej instancji typem ze zmiennych instancji, a jeśli staramy się zainicjować ztypu static finalwewnątrz konstruktora, będzie próbował ponownie zainicjować static finalpole typu zponieważ konstruktor jest uruchamiany na każdej instancji klasy, która nie może wystąpić w finalpolach statycznych .

Lew
źródło
5
In case of static inner classes, they can not have static fieldswygląda na literówkę. Klasy wewnętrzne są niestatyczne.
Daniel Lubarov
Powinieneś jednak użyć zamiast Chociaż
Suraj Jain
Kiedy uruchamiasz maszynę JVM i ładujesz klasę po raz pierwszy (jest to wykonywane przez program ładujący klasy, gdy po raz pierwszy odwołuje się do niej w jakikolwiek sposób) wszelkie statyczne bloki lub pola są „ładowane” do maszyny JVM i stają się dostępne.
nhoxbypass
1
Niestety ta odpowiedź zawiera pewne nieścisłości faktyczne dotyczące czasu inicjalizacji statyki. Zobacz stackoverflow.com/a/3499322/139985 .
Stephen C
15

Widzieć:

W szczególności ta ostatnia zapewnia szczegółowe kroki inicjalizacji, które określają, kiedy zmienne statyczne są inicjowane iw jakiej kolejności (z zastrzeżeniem, że finalzmienne klas i pola interfejsu, które są stałymi czasu kompilacji, są inicjowane jako pierwsze).

Nie jestem pewien, jakie jest Twoje konkretne pytanie dotyczące punktu 3 (zakładając, że masz na myśli ten zagnieżdżony?). Szczegółowa sekwencja stwierdza, że ​​będzie to rekurencyjne żądanie inicjalizacji, więc inicjalizacja będzie kontynuowana.

Dave Newton
źródło
11

Pola statyczne są inicjowane, gdy klasa jest ładowana przez program ładujący klasy. W tym momencie przypisywane są wartości domyślne. Odbywa się to w kolejności, w jakiej pojawiają się w kodzie źródłowym.

Dave
źródło
10

Kolejność inicjalizacji:

  1. Statyczne bloki inicjalizacyjne
  2. Bloki inicjalizacji instancji
  3. Konstruktorzy

Szczegóły tego procesu są wyjaśnione w dokumencie specyfikacji JVM .

Óscar López
źródło
6

zmienna statyczna

  • Jest to zmienna należąca do klasy, a nie do obiektu (instancji)
  • Zmienne statyczne są inicjowane tylko raz, na początku wykonywania (gdy Classloader ładuje klasę po raz pierwszy).
  • Te zmienne zostaną zainicjalizowane jako pierwsze, przed inicjalizacją jakichkolwiek zmiennych instancji
  • Pojedyncza kopia do współużytkowania przez wszystkie instancje klasy
  • Dostęp do zmiennej statycznej można uzyskać bezpośrednio poprzez nazwę klasy i nie wymaga ona żadnego obiektu
aleroot
źródło
4

Zaczynając od kodu z drugiego pytania:

class MyClass {
  private static MyClass myClass = new MyClass();
  private static final Object obj = new Object();
  public MyClass() {
    System.out.println(obj); // will print null once
  }
}

Odwołanie do tej klasy rozpocznie inicjalizację. Najpierw klasa zostanie oznaczona jako zainicjowana. Następnie pierwsze pole statyczne zostanie zainicjowane nową instancją MyClass (). Zauważ, że myClass natychmiast otrzymuje odwołanie do pustej instancji MyClass. Przestrzeń jest tam, ale wszystkie wartości są zerowe. Konstruktor jest teraz wykonywany i drukuje obj, co jest wartością null.

Wracając do inicjalizacji klasy: objodnosi się do nowego rzeczywistego obiektu i gotowe.

Jeśli zostało to wyłączone przez instrukcję, taką jak: MyClass mc = new MyClass();miejsce na nową instancję MyClass jest ponownie przydzielane (i umieszczane jest odniesienie mc). Konstruktor jest ponownie wykonywany i ponownie drukuje obj, co teraz nie jest null.

Prawdziwa sztuczka polega na tym, że kiedy używasz new, as in WhatEverItIs weii = new WhatEverItIs( p1, p2 ); weiinatychmiast otrzymuje odniesienie do odrobiny zerowej pamięci. JVM przejdzie następnie do zainicjowania wartości i uruchomienia konstruktora. Ale jeśli w jakiś sposób odwołujesz się weii do niego, zanim to zrobi - na przykład odwołując się do niego z innego wątku lub lub przez odwołanie z inicjalizacji klasy - patrzysz na instancję klasy wypełnioną wartościami null.

RalphChapin
źródło
1
Klasa nie jest oznaczona jako zainicjowana, dopóki inicjalizacja nie zostanie zakończona - inaczej nie miałoby sensu. Oznaczenie jako zainicjowane jest prawie ostatnim wykonanym krokiem. Zobacz JLS 12.4.2 .
Dave Newton
@DaveNewton: Gdy coś odniesie się do klasy i rozpocznie jej inicjalizację, wszystkie dalsze odwołania będą traktować klasę jako zainicjowaną. Nie będą próbowali go inicjalizować i nie będą czekać, aż zostanie zainicjowany. Stąd pola, które wydają się być niezerowe od początku programu, mogą w rzeczywistości być puste przez pewien czas. Brak zrozumienia tego powoduje całe zamieszanie. Myślę, że najprościej jest powiedzieć, że niezainicjowana klasa jest „oznaczona” jako zainicjowana przy pierwszym odwołaniu, wszystkie inne odwołania traktują ją jako zainicjowaną i dlatego tak się dzieje.
RalphChapin
Poprawka do poprzedniego komentarza: jak opisano w JLS 12.4.2 Dave'a Newtona, klasa jest blokowana podczas inicjalizacji. Inne wątki będą czekać na zainicjowanie klasy. Nie ma to jednak wpływu na ten przypadek, który wszystko dzieje się w jednym wątku.
RalphChapin
4

Zmienną statyczną można zainicjować na trzy następujące sposoby, a następnie wybierz dowolny, który Ci się podoba

  1. możesz go zinicjalizować w momencie deklaracji
  2. lub możesz to zrobić, tworząc blok statyczny, np .:

    static {
            // whatever code is needed for initialization goes here
        }
    
  3. Istnieje alternatywa dla bloków statycznych - możesz napisać prywatną metodę statyczną

    class name {
        public static varType myVar = initializeVar();
    
        private static varType initializeVar() {
            // initialization code goes here
        }
    }
    
ajay verma
źródło