CodeGym /Kursy /JAVA 25 SELF /Bloki inicjalizacji

Bloki inicjalizacji

JAVA 25 SELF
Poziom 15 , Lekcja 4
Dostępny

1. Czym są bloki inicjalizacji

W Javie istnieją dwa typy bloków inicjalizacji:

  • Niestatyczne (zwykłe) bloki inicjalizacji – wykonywane przy każdym tworzeniu nowego obiektu, zaraz po inicjalizacji pól i przed wywołaniem konstruktora.
  • Statyczne bloki inicjalizacji – wykonywane raz podczas ładowania klasy do pamięci (przed utworzeniem pierwszego obiektu lub odwołaniem do statycznych pól/metod).

Blok inicjalizacji niestatyczny

Deklaruje się go po prostu w ciele klasy, bez żadnych słów typu static:

public class User {
    private String name;

    // Niestatyczny blok inicjalizacji
    {
        System.out.println("Wykonywany jest blok niestatyczny!");
        name = "Nazwa domyślna";
    }

    public User() {
        System.out.println("Wykonywany jest konstruktor!");
    }
}

Blok inicjalizacji statyczny

Deklaruje się go ze słowem kluczowym static:

public class Config {
    public static String appName;

    static {
        System.out.println("Wykonywany jest blok statyczny!");
        appName = "Moja superaplikacja";
    }
}

2. Kolejność inicjalizacji: kto tu rządzi?

W Javie kolejność inicjalizacji elementów klasy to nie „od czapy”, lecz ściśle określona sekwencja. Jeśli kiedykolwiek próbowaliście złożyć meble z IKEA bez instrukcji, zrozumiecie, dlaczego taka kolejność jest ważna: jeśli pomieszać kroki, zamiast szafy wyjdzie obiekt artystyczny.

Kolejność inicjalizacji:

  1. Pola statyczne i statyczne bloki – w kolejności ich deklaracji w klasie. Wykonywane są raz przy ładowaniu klasy.
  2. Pola niestatyczne i bloki niestatyczne – w kolejności ich deklaracji, przy każdym tworzeniu nowego obiektu.
  3. Konstruktor – wykonywany po wszystkich niestatycznych inicjalizacjach.

Schematycznie

+-------------------------------+
|   Ładowanie klasy w JVM       |
+-------------------------------+
| 1. Pola statyczne             |
| 2. Bloki statyczne            |
|         ↓                     |
|   Tworzenie obiektu           |
|         ↓                     |
| 3. Pola niestatyczne          |
| 4. Bloki niestatyczne         |
| 5. Konstruktor                |
+-------------------------------+

Przykład z wypisem

Napiszmy klasę, która pokaże, w jakiej kolejności wszystko się dzieje:

public class Demo {
    static String staticField = print("1. static pole");

    static {
        print("2. static blok");
    }

    String field = print("3. niestatyczne pole");

    {
        print("4. niestatyczny blok");
    }

    public Demo() {
        print("5. konstruktor");
    }

    static String print(String msg) {
        System.out.println(msg);
        return msg;
    }

    public static void main(String[] args) {
        System.out.println("Tworzymy pierwszy obiekt Demo:");
        Demo d1 = new Demo();

        System.out.println("\nTworzymy drugi obiekt Demo:");
        Demo d2 = new Demo();
    }
}

Co zobaczymy na ekranie?

1. static pole
2. static blok
Tworzymy pierwszy obiekt Demo:
3. niestatyczne pole
4. niestatyczny blok
5. konstruktor

Tworzymy drugi obiekt Demo:
3. niestatyczne pole
4. niestatyczny blok
5. konstruktor

Zwróć uwagę: części statyczne (static) wykonują się tylko raz – przy pierwszym odwołaniu do klasy. Wszystko, co nie jest static, – za każdym razem przy tworzeniu obiektu.

3. Przykłady kodu: po co są bloki inicjalizacji

Gdy konstruktor nie wystarcza

Zdarza się, że część inicjalizacji powinna być wspólna dla wszystkich konstruktorów. Na przykład klasa ma kilka konstruktorów i nie chcesz kopiować tej samej inicjalizacji do każdego z nich. W takim przypadku wygodnie jest wynieść ją do bloku niestatycznego:

public class Person {
    private String id;
    private String name;

    {
        // Ten kod zostanie wykonany przed każdym konstruktorem
        id = java.util.UUID.randomUUID().toString();
        System.out.println("Generujemy unikalne id: " + id);
    }

    public Person() {
        System.out.println("Person() bez parametrów");
    }

    public Person(String name) {
        this.name = name;
        System.out.println("Person(String name)");
    }
}

Wynik: podczas tworzenia dowolnego obiektu Person najpierw zostanie wygenerowany id, a potem wykona się odpowiedni konstruktor.

Inicjalizacja złożonych danych statycznych

Bloku statycznego często używa się do inicjalizacji „ciężkich” lub złożonych pól statycznych, np. wczytywania konfiguracji z pliku, tworzenia kolekcji, połączenia z bazą itd.

public class Settings {
    public static final java.util.Map<String, String> DEFAULTS;

    static {
        DEFAULTS = new java.util.HashMap<>();
        DEFAULTS.put("theme", "light");
        DEFAULTS.put("language", "ru");
        System.out.println("Statyczny blok Settings: ustawienia domyślne");
    }
}

4. Przydatne niuanse

Kiedy używać bloków inicjalizacji

Kiedy warto używać

  • Do wspólnej inicjalizacji, potrzebnej we wszystkich konstruktorach.
  • Do złożonych danych statycznych, których nie da się wyrazić prostym przypisaniem.
  • Do inicjalizacji zasobów statycznych (np. wczytanie pliku konfiguracyjnego przy starcie aplikacji).

Kiedy NIE warto używać

  • Jeśli wystarczy zwykłe przypisanie albo konstruktor – użyj ich.
  • Nie „ukrywaj” logiki biznesowej w blokach inicjalizacji – utrudnia to czytanie i utrzymanie kodu.
  • Jeśli inicjalizacja zależy od parametrów konstruktora, użyj samego konstruktora.

Nie nadużywaj bloków inicjalizacji

Bloki inicjalizacji to potężne, ale raczej rzadko używane narzędzie. W większości przypadków wystarczy proste przypisanie albo konstruktor. Jeśli w klasie jest zbyt wiele bloków inicjalizacji, kod staje się nieczytelny i trudny w utrzymaniu.

Nie używaj bloku niestatycznego do logiki zależnej od parametrów konstruktora

W bloku niestatycznym nie można używać parametrów konstruktora, ponieważ wykonuje się on PRZED konstruktorem. Jeśli musisz coś zainicjalizować na podstawie parametrów, zrób to w samym konstruktorze.

Bloki statyczne a dziedziczenie

Statyczne bloki inicjalizacji nie są dziedziczone. Każda klasa ma własny blok statyczny. Przy ładowaniu klasy pochodnej najpierw wykona się blok statyczny klasy bazowej, a potem blok statyczny klasy pochodnej.

5. Typowe błędy przy pracy z blokami inicjalizacji

Błąd nr 1: Oczekiwanie, że blok niestatyczny „widzi” parametry konstruktora.
Wielu początkujących próbuje używać parametrów konstruktora w bloku niestatycznym, ale dostaje błąd kompilacji albo nieoczekiwany rezultat. Pamiętaj: blok niestatyczny wykonuje się PRZED konstruktorem, więc parametrów jeszcze nie ma.

Błąd nr 2: Za dużo logiki w blokach inicjalizacji.
Jeśli w blokach inicjalizacji pojawia się złożona logika, kod staje się zagmatwany. Lepiej wykonać główną pracę w konstruktorze lub osobnych metodach.

Błąd nr 3: Kilka bloków statycznych i kolejność ich deklaracji.
Jeśli w klasie jest kilka bloków statycznych, wykonują się one w takiej kolejności, w jakiej zostały zadeklarowane w kodzie, razem z polami statycznymi. Czasem prowadzi to do nieoczekiwanych rezultatów, jeśli np. jeden blok zależy od wyniku innego.

Błąd nr 4: Oczekiwanie, że blok statyczny wykona się przy każdym tworzeniu obiektu.
static-blok wykonuje się tylko raz – przy ładowaniu klasy. Jeśli liczysz na ponowną inicjalizację, to się nie wydarzy.

Błąd nr 5: Próba odwoływania się do pól niestatycznych z bloku statycznego.
W bloku statycznym dostępne są tylko zmienne i metody statyczne. Próba odwołania się do pól niestatycznych (zwykłych) spowoduje błąd kompilacji.

1
Ankieta/quiz
Enkapsulacja, poziom 15, lekcja 4
Niedostępny
Enkapsulacja
Zasady enkapsulacji
Komentarze
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION