1. Pola statyczne: jak je inicjalizować?
Pole statyczne (static) nie należy do obiektu, lecz do całej klasy. Jego wartość istnieje w jednym, wspólnym egzemplarzu dla wszystkich obiektów tej klasy. Analogicznie — to jak ogłoszenie „wspólnej kasy” dla wszystkich pracowników firmy: nieważne, kto akurat przyszedł, kasa jest jedna dla wszystkich.
Inicjalizacja przy deklaracji
Najprostszy i najczęściej spotykany sposób inicjalizacji pola statycznego — przypisać mu wartość bezpośrednio przy deklaracji:
public class User {
private static int userCount = 0; // inicjalizacja bezpośrednio tutaj
// ... pozostały kod
}
Takie pole zostanie zainicjalizowane przy pierwszym załadowaniu klasy User do pamięci.
Inicjalizacja w bloku statycznym
Czasem potrzebna jest bardziej złożona logika inicjalizacji — na przykład wczytanie danych z pliku albo wykonanie obliczeń. W takim przypadku używa się statycznego bloku inicjalizacji:
public class Config {
public static String configPath;
static {
// Ten blok wykona się DOKŁADNIE raz podczas ładowania klasy
configPath = System.getenv("APP_CONFIG_PATH");
if (configPath == null) {
configPath = "/etc/app/default.conf";
}
System.out.println("Config path zainicjalizowana: " + configPath);
}
}
Kiedy wykonywany jest blok statyczny?
- Przy pierwszym odwołaniu do klasy (np. przy utworzeniu pierwszego obiektu lub wywołaniu dowolnej metody/pola statycznego).
- Wykonuje się tylko raz dla każdej klasy.
Dostęp do pól statycznych — najważniejsze cechy
- Do pól statycznych odwołujemy się przez nazwę klasy: User.userCount.
- Można odwołać się także przez obiekt, ale to zły styl (wprowadza czytającego kod w błąd).
Przykład:
User u1 = new User();
User u2 = new User();
System.out.println(User.userCount); // poprawnie
System.out.println(u1.userCount); // działa, ale nie jest zalecane!
2. Pola final: kiedy i jak je inicjalizować?
final — to modyfikator, który mówi: „To pole można przypisać tylko raz, a później nie można go zmienić”. Po inicjalizacji wartość pola staje się niezmienna: dla obiektu — to jego stała właściwość, a dla klasy (jeśli pole jest statyczne) — wspólna stała.
Przykłady użycia:
- Stałe (np. PI).
- Unikalny identyfikator obiektu, którego nie można zmienić po utworzeniu.
Wymagania dotyczące inicjalizacji pól final
W języku Java obowiązuje ścisła zasada: każde final-pole musi być zainicjalizowane albo przy deklaracji, albo w każdym konstruktorze klasy.
Inicjalizacja przy deklaracji
public class Circle {
public static final double PI = 3.1415926535; // stała klasy
private final String id = "CIRCLE"; // stała obiektu
}
Inicjalizacja w konstruktorze
Czasem wartość final-pola jest znana dopiero w momencie tworzenia obiektu:
public class User {
private final int id;
public User(int id) {
this.id = id; // przypisujemy pole final w konstruktorze
}
}
Ważne: jeśli klasa ma kilka konstruktorów, final-pole musi być zainicjalizowane w każdym z nich!
Przykład łączony
public class Token {
private final String value;
private final long timestamp;
public Token(String value) {
this.value = value;
this.timestamp = System.currentTimeMillis();
}
}
Błędy kompilacji przy nieprawidłowej inicjalizacji
Jeśli zapomnisz zainicjalizować final-pole, kompilator nie pozwoli zbudować programu:
public class Broken {
private final int x; // nie został zainicjalizowany
public Broken() {
// x nie jest przypisywane!
}
}
// Błąd: variable x might not have been initialized
3. Połączenie static i final: deklarowanie stałych klasy
Bardzo często spotyka się połączenie public static final. W języku Java tak deklaruje się stałe klasy — wartości, które ustala się raz i nie zmieniają się w czasie działania programu. Te stałe należą do całej klasy i są takie same dla wszystkich jej obiektów.
Składnia i przykład
public class MathUtils {
public static final double PI = 3.1415926535;
public static final String APP_NAME = "MyApp";
}
Wyjaśnienie:
- public — dostępne wszędzie.
- static — należy do całej klasy, a nie do pojedynczego obiektu.
- final — nie można zmienić po inicjalizacji.
Użycie
double area = MathUtils.PI * r * r;
System.out.println(MathUtils.APP_NAME);
Konwencja: nazwy stałych zazwyczaj zapisuje się WIELKIMI_LITERAMI_Z_PODKREŚLENIEM.
4. Przykłady kodu: sposoby inicjalizacji pól statycznych i final
Przykład 1: Prosta klasa ze stałą
public class Constants {
public static final int DAYS_IN_WEEK = 7;
public static final String COMPANY = "Daisy LLC";
}
Przykład 2: Pole statyczne zainicjalizowane w bloku statycznym
public class AppConfig {
public static final String DEFAULT_PATH;
static {
// Można wykonać złożoną logikę
String env = System.getenv("APP_PATH");
if (env != null) {
DEFAULT_PATH = env;
} else {
DEFAULT_PATH = "/usr/local/app";
}
}
}
Przykład 3: pole final obiektu — inicjalizacja w konstruktorze
public class User {
private static int nextId = 1;
private final int id;
private String name;
public User(String name) {
this.id = nextId++;
this.name = name;
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Użycie:
User u1 = new User("Ivan");
User u2 = new User("Maria");
System.out.println(u1.getId()); // 1
System.out.println(u2.getId()); // 2
Przykład 4: błąd przy nieprawidłowej inicjalizacji pola final
public class BadExample {
private final int number;
public BadExample() {
// number nie został zainicjalizowany!
}
}
// Błąd kompilacji: variable number might not have been initialized
5. Najlepsze praktyki pracy z polami static i final
Używaj public static final tylko dla prawdziwych stałych
Jeśli wartość może się zmienić w przyszłości (np. lista pracowników), nie oznaczaj jej jako final! Stałe muszą być naprawdę niezmienne.
public static final int MAX_USERS = 1000; // dobrze
public static final String[] USERS = new String[100]; // źle!
Chociaż sama referencja USERS się nie zmieni, zawartość tablicy można modyfikować. To może prowadzić do nieoczekiwanych błędów.
Do złożonej inicjalizacji używaj bloków statycznych
Jeśli stałej nie da się wyrazić prostym przypisaniem (np. wymaga obliczeń lub odczytu z pliku), użyj bloku statycznego.
Nie nadużywaj pól statycznych
Pola statyczne to globalne zmienne. Zbyt duża ich liczba może prowadzić do trudnych do wykrycia błędów i utrudniać utrzymanie kodu.
Nie rób obiektów zmiennych jako public static final
Jeśli obiekt jest zmienny, nie rób go publicznym i final. To otworzy wszystkim dostęp do jego wnętrza — ktoś na pewno z tego skorzysta.
6. Typowe błędy przy inicjalizacji pól statycznych i final
Błąd nr 1: niezainicjalizowane pole final. Jeśli zapomnisz zainicjalizować final-pole przy deklaracji albo we wszystkich konstruktorach, kompilator zgłosi błąd. Na przykład, jeśli klasa ma dwa konstruktory, a w jednym z nich zapomnisz przypisać wartość final-polu — pojawi się błąd.
Błąd nr 2: zmiana wartości pola final. Próba zmiany wartości final-pola po inicjalizacji skończy się błędem kompilacji.
public class Demo {
private final int x = 5;
public void change() {
x = 10; // Błąd: cannot assign a value to final variable x
}
}
Błąd nr 3: publiczne mutowalne pola static final. Jeśli zrobić zmienny obiekt (String[], int[] i in.) public static final, każdy kod będzie mógł zmieniać jego zawartość. To narusza enkapsulację i może prowadzić do trudnych do wykrycia błędów.
Błąd nr 4: używanie niestatycznych pól w bloku statycznym. W bloku statycznym nie można odwoływać się do pól niestatycznych, ponieważ nie są jeszcze zainicjalizowane (w tym momencie w ogóle nie istnieją).
Błąd nr 5: nieoczekiwana kolejność inicjalizacji. Jeśli w bloku statycznym odwołujesz się do pól statycznych zadeklarowanych dalej w kodzie, mogą one nie być jeszcze zainicjalizowane. Zawsze deklaruj pola statyczne przed blokami statycznymi, jeśli planujesz ich używać.
GO TO FULL VERSION