Statyczność w Java


Modyfikator static przed składowymi klasy (pola, metody) oznacza, że możliwy jest bezpośredni dostęp do nich, bez konieczności tworzenia obiektu. Podając ścieżkę dostępu przez operator kropki można dotrzeć do statycznego pola lub metody z innej klasy. Mówi się, że takie pola, czy metody są przypisane do klasy i żyją niezależnie od obiektów, mają też wspólną wartość dla wszystkich obiektów.

Przykład:

class Student {
    String name;
    static String schoolName = "High school Lenin.";

    public Student(String name) {
        this.name = name;
    }
    void showStudent() {
        System.out.println(name +
                ", " + schoolName);
    }
    static void changeSchoolName(String newName) {
        schoolName = newName;
    }
}

public class App {
    public static void main(String[] args) {

        Student s1 = new Student("Ned Stark");
        Student s2 = new Student("Jon Snow");
        Student s3 = new Student("Gregor Clegane");

        Student.changeSchoolName("High school National Army.");

        s1.showStudent();
        s2.showStudent();
        s3.showStudent();
    }
}

Obiektami klasy Student są uczniowie, którzy chodzą do szkoły o określonej nazwie. Nazwa ta istnieje niezależnie od obiektów klasy Student i jest wspólna dla nich wszystkich. Tak więc, gdy trendy się zmieniają i zachodzi potrzeba zmiany nazwy szkoły, to właśnie metoda statyczna oraz pole statyczne może posłużyć do tego aby to szybko osiągnąć. Używając metafory – za jednym pociągnięciem pędzla, zmienić obraz wszystkich obiektów klasy Student.

Metody statyczne mogą w swoim ciele korzystać tylko z pól oraz metod statycznych klasy. Jest tak dlatego, że pozostałe składowe klasy są dostępne tylko przez obiekty, a gdyby znajdowały się wewnątrz metody statycznej umożliwiony był by dostęp poza obiektami, była by więc to sprzeczność. Z drugiej strony dla metody statycznej te składowe niestatyczne po prostu bez obiektów nie istnieją, więc taka metoda nie może zawierać czegoś, czego nie ma. Przy okazji zauważyć też można, że metoda main uruchamiająca program, jest również statyczna, co wynika z tego, że w trakcie jej wywołania po prostu nie ma jeszcze żadnych obiektów, więc nic jej innego nie pozostaje jak bycie statyczną 😉 .

Statyczna metoda wytwórcza

W odniesieniu do metod statycznych można jeszcze wspomnieć o static factory method. Otóż w metodach statycznych można tworzyć obiekty, oraz je zwracać z poziomu innych klas, zgodnie z wstawionym przed metodą modyfikatorem dostępu. Kolokwialnie rzecz ujmując, mogą one robić za konstruktory, tworząc instancję klasy w trakcie ich wywołania. Konstruktory zwykle będą miały wtedy ustawiony modyfikator na private. Powodów takiego zastosowania można szukać w większej czytelności, gdyż te metody można adekwatnie ponazywać do tego jaką wersję obiektu zwracają. Innym powodem mogą być pewne ograniczenia jakie stawiają konstruktory, mianowicie chodzi o problem z powtarzającymi się atrybutami jakie mogą przyjąć. Takie rozwiązanie może się też przyczynić do lepszej enkapsulacji, gdyż metody te stanowią dodatkowy bufor oddzielający od składowych klasy. Wiesz, nie tworzysz obiektu danej klasy, po prostu wywołujesz metodę statyczną podając ścieżkę do niej, a ona zwraca gotowy obiekt. Ten sposób jest również zalecany w bardzo uznanej książce “Effective Java” przez jej autora Joshua Bloch. Muszę przyznać, że nie mam odpowiedniego doświadczenia, żeby na jego podstawie samodzielnie rozsądzić, czy to jest prawdziwie dobre rozwiązanie. Wiem natomiast, że nie trzeba wiele czasu aby wyszukać opinie negatywne lub nawet bardzo negatywne.

Bloki statyczne

Kolejnym aspektem wiążącym się ze statycznością są tak zwane bloki statyczne.

Przykład:

public class Foo {
    static {
        System.out.println("Hej, tu Foo, klasa której obiekt zdaje się właśnie utworzyłeś," +
                " proszę bądź dla niego miły. \n");
    }
    public void showSomething() {
        System.out.println("Hej, jestem nowym obiektem klasy Foo i właśnie wylądowałem na" +
                " zarezerwowanej dla mnie stercie pamięci. ");
    }
}

public class App {
    public static void main(String[] args) {

    Foo foo = new Foo();
    foo.showSomething();

    }
}

Można sobie sprawdzić, że gdy uruchomiony zostanie kod powyżej, to zawartość bloku statycznego zostanie wykonana wraz z wywołaniem klasy, więc przed utworzeniem obiektu w klasie App. Oznacza to, że wpierw wyświetli się komunikat z bloku static, a następnie info powiązane z nowo utworzonym obiektem. Troszkę więcej można na ten temat znaleźć tutaj.

Wewnętrzne klasy statyczne

Wewnętrzne klasy statyczne są kolejnym tematem zahaczającym o statyczność w Java. Od razu dodam, że klasa wewnętrzna może też być niestatyczna. Zachodzi wówczas różnica między nimi, co do sposobu tworzenia ich instancji. Mianowicie obiekt klasy wewnętrznej niestatycznej tworzy się poprzez wykorzystanie obiektu klasy, w której klasa wewnętrzna została zagnieżdżona. Natomiast w przypadku klasy wewnętrznej – statycznej, do utworzenia jej instancji te obiekty klasy zewnętrznej nie są potrzebne.

Przykład:

public class Foo {
    public static class InnerFoo {
    }
}

public class App {
    public static void main(String[] args) {
        Foo.InnerFoo innerFoo = new Foo.InnerFoo();
    }
}

Statyczna klasa wewnętrzna ma również podobnie jak statyczna metoda, bezpośredni dostęp tylko do statycznych składowych klasy opakowującej/zewnętrznej. W świetle tego, że obiekty statycznej klasy wewnętrznej można tworzyć w oderwaniu od klasy zewnętrznej, to w sumie wychodziło by mi, że to klasa wewnętrzna, jednak poprzez swoją statyczność jest jak zwykła klasa.

Uzasadnienie korzystania z klas wewnętrznych można upatrywać w ścisłym powiązaniu obu klas, tak że klasa wewnętrzna nie miała by sensu bez zewnętrznej. W przypadku niestatycznej klasy wewnętrznej ważne też jest, że ma ona dostęp do wszystkich składowych klasy zewnętrznej, bez względu na modyfikator dostępu. Tak więc, służyć może również jako dodatkowy bufor oddzielający bezpośredni dostęp do składowych klasy zewnętrznej, czyli lepszej hermetyzacji.

Przy okazji zwrócę uwagę na pewne hasełko, którym dev-wannabe mógł by się poczuć zaskoczony inner class vs subclass? Te pierwsze, to właśnie klasy zagnieżdżone, a te drugie to klasy dziedziczące.

Konstruktory

Konstruktor to specjalna metoda, która jest wywoływana zawsze kiedy tworzony jest obiekt. Jak go napiszę, to się uruchomi mój, a jak nie, to wywoła go kompilator z klasy nadrzędnej. W przypadku braku jawnego dziedziczenia, z klasy Object. Czemu wspominam o nim w odniesieniu do statyczności w Java? No dlatego, że jeżeli to od niego zależą wartości  jakie przyjmie obiekt, którego jeszcze nie ma, to musi działać poza obiektami, tak jak metody statyczne. Wyciągnąłem więc sobie wniosek, że konstruktor jest metodą niejawnie statyczną – zły wniosek, konstruktory nie są statyczne w Java! Otóż, konstruktor wywoływany jest wraz z słowem new tak jak obiekt, posiłkując się stackoverflow wydaje mi się całkiem logiczne, że uruchamia się on na tym obiekcie zaraz po jego utworzeniu, a następnie inicjuje w jego wnętrzu wartości pól. Taka kolejność wyklucza wywołanie innej metody na obiekcie przed konstruktorem.

Więcej o konstruktorach po polsku i po angielsku.

Może mogłem darować sobie ten akapit, bo w sumie do tej statyczności aż tak nie pasuje, pomyślałem jednak, że podzielę się doświadczeniami 😉 .

Warning! Tego nie pisał programista, ale dev-wannabe. Jak coś poknociłem, chętnie się o tym dowiem.

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *