CodeGym /Kursy /JAVA 25 SELF /Inicjalizacja obiektu: kolejność inicjalizacji

Inicjalizacja obiektu: kolejność inicjalizacji

JAVA 25 SELF
Poziom 14, Lekcja 5
Dostępny

1. Kolejność inicjalizacji obiektu w Javie: kto pierwszy, kto ostatni?

Tworzenie obiektu w Javie — to nie tylko „zaalokować pamięć i zapisać tam wartości”. To cały rytuał z jasno określoną kolejnością działań. Jeśli wyobrazisz sobie proces inicjalizacji jako start rakiety, to każdy stopień ma swoje ściśle określone miejsce.

Gdy piszesz:

Person john = new Person("Vasya", 30);

Java wykonuje następujące kroki:

  1. Alokuje pamięć dla obiektu i przypisuje wszystkim polom wartości domyślne.
  2. Wykonuje jawne inicjalizacje pól (jeśli wartości zostały podane bezpośrednio przy deklaracji).
  3. Wykonuje wszystkie niestatyczne (zwykłe) bloki inicjalizacji — w takiej kolejności, w jakiej zostały zapisane w klasie.
  4. Wykonuje ciało konstruktora, który został wywołany przez new.

Przyjrzyjmy się każdemu krokowi po kolei.

2. Inicjalizacja pól

Wartości domyślne

Gdy obiekt dopiero powstaje (zanim cokolwiek jawnie przypiszesz), wszystkie jego pola otrzymują wartości domyślne:

Typ pola Wartość domyślna
int, short, byte, long
0
float, double
0.0
boolean
false
char
'\u0000' (znak null)
Typy referencyjne (String, inne obiekty)
null

Jawna inicjalizacja

Jeśli podczas deklaracji pola od razu nadajesz mu wartość, to wartość ta zostanie ustawiona po wartościach domyślnych, ale przed wykonaniem konstruktora.

public class Person {
    private String name = "Bezimienny";
    private int age = 18;
}

Jeśli utworzysz obiekt przez pusty konstruktor, te wartości pozostaną. Jeśli przez konstruktor z parametrami — najprawdopodobniej zostaną nadpisane.

3. Bloki inicjalizacji: po co są i jak działają

W Javie można deklarować tak zwane niestatyczne (zwykłe) bloki inicjalizacji. Wykonują się za każdym razem przy tworzeniu obiektu, zaraz po inicjalizacji pól, ale przed konstruktorem.

Składnia:

public class Person {
    {
        System.out.println("Wykonywany jest niestatyczny blok inicjalizacji!");
    }
}

Jeśli w klasie jest kilka takich bloków, wykonują się w takiej kolejności, w jakiej są zapisane.

Przykład z kilkoma blokami

public class Person {
    private String name = "Bezimienny";

    {
        System.out.println("Blok 1: name = " + name);
        name = "Zagadka";
    }

    {
        System.out.println("Blok 2: name = " + name);
    }

    public Person() {
        System.out.println("Konstruktor: name = " + name);
    }
}

Jeśli utworzysz obiekt:

Person p = new Person();

Wyjście będzie następujące:

Blok 1: name = Bezimienny
Blok 2: name = Zagadka
Konstruktor: name = Zagadka

Po co są bloki inicjalizacji?

W praktyce bloki inicjalizacji są używane rzadko. Zazwyczaj wszystko, co można zrobić w bloku, da się wykonać albo przy deklaracji pola, albo w konstruktorze. Czasem jednak (np. gdy masz kilka konstruktorów, a część logiki inicjalizacji powinna być wspólna dla wszystkich) bloki inicjalizacji mogą być wygodne.

4. Konstruktor: ostatni akord inicjalizacji

Po tym, jak wszystkie pola otrzymały wartości (domyślne lub jawne) i wykonały się wszystkie bloki inicjalizacji, wywoływany jest konstruktor — ten, który wskazałeś po new.

W konstruktorze zazwyczaj ustawiasz finalne wartości pól, przyjmujesz parametry i wykonujesz inną inicjalizację zależną od danych wejściowych.

public class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        System.out.println("Konstruktor: name = " + name + ", age = " + age);
        this.name = name;
        this.age = age;
    }
}

5. Demonstracja pełnej kolejności inicjalizacji

Napiszmy klasę, która wyraźnie pokaże, w jakiej kolejności przebiega inicjalizacja.

public class Person {
    private String name = "Bezimienny";
    private int age = 18;

    {
        System.out.println("Blok inicjalizacji: name = " + name + ", age = " + age);
        age = 21;
    }

    public Person() {
        System.out.println("Konstruktor bez parametrów: name = " + name + ", age = " + age);
    }

    public Person(String name, int age) {
        System.out.println("Konstruktor z parametrami: name = " + name + ", age = " + age);
        this.name = name;
        this.age = age;
    }

    public void printInfo() {
        System.out.println("Person: name = " + name + ", age = " + age);
    }
}

A teraz w naszej klasie głównej (np. Main):

public class Main {
    public static void main(String[] args) {
        System.out.println("Tworzymy obiekt p1:");
        Person p1 = new Person();
        p1.printInfo();

        System.out.println("\nTworzymy obiekt p2:");
        Person p2 = new Person("peter", 30);
        p2.printInfo();
    }
}

Wynik będzie mniej więcej taki:

Tworzymy obiekt p1:
Blok inicjalizacji: name = Bezimienny, age = 18
Konstruktor bez parametrów: name = Bezimienny, age = 21
Person: name = Bezimienny, age = 21

Tworzymy obiekt p2:
Blok inicjalizacji: name = Bezimienny, age = 18
Konstruktor z parametrami: name = Bezimienny, age = 21
Person: name = peter, age = 30

Zwróć uwagę: w konstruktorze z parametrami wartości pól są jeszcze stare (te po bloku inicjalizacji), a dopiero po konstruktorze pola otrzymują wartości finalne.

6. Diagram kolejności inicjalizacji

Oto mały schemat (schemat blokowy) do wizualizacji:

flowchart TD
    A[Alokacja pamięci, wartości domyślne] --> B[Jawna inicjalizacja pól]
    B --> C[Wykonanie niestatycznych bloków inicjalizacji]
    C --> D[Wykonanie konstruktora]
    D --> E[Obiekt gotowy do użycia]

7. Uwagi i niuanse

Pola statyczne i bloki statyczne

W tym wykładzie mówimy tylko o niestatycznych (zwykłych) polach i blokach. Pola statyczne i bloki inicjalizują się tylko raz przy ładowaniu klasy, a nie przy każdym tworzeniu obiektu. Więcej o nich powiemy w tematach o enkapsulacji.

Kiedy co się inicjalizuje?

  • Pola — przy każdym utworzeniu obiektu.
  • Bloki inicjalizacji — przy każdym utworzeniu obiektu.
  • Pola i bloki statyczne — tylko raz przy ładowaniu klasy.

Czy można odwoływać się do pól w blokach inicjalizacji?

Tak, można! Ważne jednak, że jeśli pole jest zadeklarowane niżej niż blok, to już istnieje, ale jeśli odwołasz się do niego przed jego jawną inicjalizacją, otrzymasz wartość domyślną.

8. Typowe błędy przy inicjalizacji obiektów

Błąd nr 1: Oczekiwanie, że pola zostaną zainicjalizowane przed ich deklaracją.
W Javie kolejność deklaracji pól i bloków inicjalizacji w klasie ma znaczenie. Jeśli w bloku inicjalizacji odwołujesz się do pola, które jest zadeklarowane niżej, to już istnieje, ale nie zostało jeszcze jawnie zainicjalizowane — będzie miało wartość domyślną.

Błąd nr 2: Duplikowanie inicjalizacji w blokach i konstruktorach.
Początkujący często powtarzają tę samą logikę zarówno w bloku, jak i w konstruktorze. Lepiej użyć albo bloku, albo konstruktora, albo wywołać jeden konstruktor z drugiego przez this(...).

Błąd nr 3: Oczekiwanie, że pola statyczne będą inicjalizowane przy każdym tworzeniu obiektu.
Pola i bloki statyczne działają inaczej — inicjalizują się tylko raz przy ładowaniu klasy.

Błąd nr 4: Używanie niezainicjalizowanych pól referencyjnych.
Jeśli zapomnisz jawnie zainicjalizować pole referencyjne (na przykład, String name;), to będzie równe null dopóki nie przypiszesz mu wartości jawnie w bloku inicjalizacji lub w konstruktorze.

1
Zadanie
JAVA 25 SELF, poziom 14, lekcja 5
Niedostępne
Obserwacja pojawiania się wirtualnych istot
Obserwacja pojawiania się wirtualnych istot
1
Zadanie
JAVA 25 SELF, poziom 14, lekcja 5
Niedostępne
Standaryzacja nowych książek w wydawnictwie
Standaryzacja nowych książek w wydawnictwie
1
Zadanie
JAVA 25 SELF, poziom 14, lekcja 5
Niedostępne
Śledzenie rozpoczęcia montażu każdego samochodu na linii produkcyjnej
Śledzenie rozpoczęcia montażu każdego samochodu na linii produkcyjnej
1
Zadanie
JAVA 25 SELF, poziom 14, lekcja 5
Niedostępne
Droga studenta do oceny końcowej
Droga studenta do oceny końcowej
1
Ankieta/quiz
Klasy i konstruktory, poziom 14, lekcja 5
Niedostępny
Klasy i konstruktory
Klasy i konstruktory
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION