Modyfikatory

Jako programiści musimy być świadomi tego, że nic nie wejdzie nam do głowy samo. Razem z każdą linijką kodu nabieramy doświadczenia, ale co nam po doświadczeniu, jeżeli jest ono związane ze złymi nawykami.

Tak jest m.in. w przypadku modyfikatorów. Na upartego można rozwiązywać to metodą prób i błędów, ale czy nie lepiej poświęcić chwilkę i zrozumieć podstawy?

Nawet jeżeli miałbyś wyłapać tylko jedną rzecz której nie wiedziałeś, warto spróbować!


Koniec gadania, bierzemy się do roboty.

Zastosowanie modyfikatorów dostępu

Dostęp do klas, konstruktorów, pól czy metod jest w javie regulowany przez różne modyfikatory. Java pozwala nam kontrolować to które elementy naszej klasy będą osiągalne przez inne. W miarę możliwości powinniśmy ten dostęp jak najbardziej ograniczać zgodnie z założeniami enkapsulacji (wikipedia.org)

Modyfikatory dostępu w javie

'default' – jest to domyślny modyfikator dostępu (w przypadku pozostawienia pustego miejsca). Często mylnie traktowany jako równy modyfikatorowi publicznemu. Pozwala on na dostęp tylko klasom w tym samym pakiecie.

publicnajmniej restrykcyjny modyfikator dostępu. Pozwala na dostęp wszystkim klasom w tym samym programie. Są widoczne z każdego miejsca, bez względu na to, czy są w tym samym pakiecie, czy nie.

privatenajbardziej restrykcyjny modyfikator dostępu. Wszystkie elementy zadeklarowane jako private są widoczne tylko w obrębie danej klasy. Klasa znajdująca się na najwyższym poziomie nie może zostać zadeklarowana jako prywatna. Standardowe projektowanie w javie zakłada, że wszystkie pola deklarujemy jako prywatne, udostępniając dla nich metody zwracające oraz zmieniające ich wartości (gettery oraz settery).

protected – jest to modyfikator chroniony. Pozwala on na dostęp wszystkim klasom w tym samym pakiecie oraz klasom po nim dziedziczącym.

Inne modyfikatory

static
Zmienna statyczna
Po dopisaniu modyfikatora static otrzymujemy nic innego jak zmienną klasową (ang. class variable). Oznacza to, że wszystkie obiekty utworzone na podstawie klasy w której taka zmienna się znajduje, przechowują referencję do tego samego pola w pamięci.
W dużym uproszczeniu wszystkie obiekty będą korzystały z tej samej zmiennej o wspólnej wartości, zamiast tworzenia ich kopii w przypadku każdego obiektu.
Wydaje się skomplikowane? Wcale tak nie jest, proszę zobaczyć.

W powyższym przypadku otrzymamy wynik:

Jak widać stworzyliśmy dwa niezależne obiekty klasy ‚Car’. Odwołując się do jednego z nich, zmieniliśmy wartości pola ‚speed’. W rezultacie jednak zmieniona została wartość wspólna, czyli także ta należąca do obiektu drugiego.

Dodatkowo mamy dostęp do tej zmiennej bez konieczności tworzenia instancji obiektu.

Metoda statyczna
Podobnie jak w przypadku zmiennej statycznej, dostęp do takiej metody możemy uzyskać bez konieczności tworzenia obiektu danej klasy. Dochodzi tutaj jednak kilka dodatkowych ograniczeń:
– taka metoda może odwoływać się tylko do danych oznaczonych jako statyczne. Oznacza to, że z metody statycznej nie możemy odwoływać się do „normalnych” zmiennych oraz metod.
– w żadnym przypadku nie może odwołać się do słów kluczowych this lub super.

Warto wspomnieć także, że oprócz dostępu do zmiennych statycznych, takie metody mają prawo do ich modyfikacji.

Do tego odpowiemy sobie na pytanie, które może nurtować kilka osób. Dlaczego funkcja main w javie jest typu static? Odpowiedź jest bardzo prosta. W przypadku metod statycznych obiekt nie jest wymagany do jej wywołania. Gdyby funkcja main nie była statyczna, przy każdym wywołaniu naszego programu, tworzyłby on niepotrzebny obiekt, tym samym niepotrzebnie zapychając pamięć.

Blok statyczny
Tutaj mała niespodzianka. Nie każdy młody programista jest świadomy tego, że mamy do dyspozycji coś takiego jak bloki statyczne.
Mój cały następny wpis będzie poświęcony blokom, zachęcam do cierpliwości i przeczytania następnego wpisu ^^.

Klasa statyczna
Klasa może zostać zadeklarowana jako statyczna tylko jeśli jest klasą zagnieżdżoną (ang. nested class). Opisałem wam to jako komentarze w kodzie.

Takie klasy mają dostęp tylko do danych statycznych. Próba odwołania się do zmiennej nie-statycznej z poziomu takiej klasy będzie skutkowałą błędem. Poniższy kod zwróci błąd.

final
Zmienna finalna
Jeżeli zadeklarujemy zmienną obiektową jako finalną możemy zmienić (a właściwie przypisać) jej referencję tylko raz. Jest to coś w rodzaju stałej. Z reguły robi się to w konstruktorze, warto jednak zaznaczyć, że mówimy tutaj o przypisaniu referencji do zmiennej. Tak długo jak nie zmienimy referencji, możemy swobodnie na niej pracować. Troszeczkę inaczej wygląda to w przypadku zmiennych prymitywnych, ale najpierw przykład obiektowych.

Tak jak wcześniej zaznaczyłem w przypadku zmiennych prymitywnych wygląda to troszkę inaczej. Operujemy tutaj bezpośrednio na wartości a nie referencji, dlatego wartość może być nadana tylko raz. W tym przypadku modyfikacja jest niedozwolona.

Klasy oraz metody finalne
Metody finalne nie mogą zostać nadpisane (ang. Override) podczas tworzenia klasy dziedziczącej. Jest to coś w rodzaju stałej metody, gdzie możemy ciągle dziedziczyć, nawet wielokrotnie, z tym, że mamy pewność, że ta konkretna metoda nie ulegnie zmianie.

Jak widać w powyższym przykładzie metoda increaseBy ma zwiększyć licznik o daną wartość. Takie jest jej zadanie i zakładamy, że nigdy nie zostanie ono zmienione, nawet w przypadku wielokrotnego dziedziczenia.

W przypadku klas finalnych działa to troszkę inaczej. Żadna tworzona przez nas klasa nie będzie mogła po niej dziedziczyć. Jest ona klasą ostateczną.

Puste statyczne finalne zmienne (z ang. static blank final variable)
Jest to typ zmiennej, który może zostać zainicjowany jedynie w bloku statycznym. Tak jak obiecałem opowiem wam o tym więcej w następnym wpisie!

abstract
Klasy abstrakcyjne
Nie można stworzyć ich obiektu, można natomiast po nich dziedziczyć. Są bardzo podobne do interfejsów, z tą różnicą, że mogą zawierać w sobie implementacje niektórych metod, które mogą, ale nie muszą zostać nadpisane.

Metody abstrakcyjne
Muszą zostać zaimplementowane w klasie dziedziczącej. Podobnie jak interfejsy informują podklasę o konieczności implementacji danej metody. Same nie zawierają ciała.

Mam nadzieję, że kod mówi sam za siebie, tutaj nie ma co teoretyzować, najlepiej wziąć i napisać prostą implementację samemu!

synchronized
Wychodząc na przeciw wszystkim pytaniom, co można synchronizować w javie, odpowiadam, że chodzi o wielowątkowość. Są to narzędzia które dają nam bardzo dużo możliwości, ale niosą też za sobą o wiele większe ryzyko błędów. Jednym z nich jest właśnie problem z synchronizacją.

Wyobraźmy sobie sytuację gdzie nasza aplikacja jest serwerem, z którym łączy się wiele użytkowników. Dla każdego z nich tworzymy osobny wątek, pracujący na liście obiektów. W trakcie, gdy jeden jest w trakcie odczytywania elementu o ostatnim indeksie inny wątek go usuwa. Skutkuje to tym, że nasz program oczywiście zwraca błąd.

Więcej na ten temat napiszę we wpisie o wielowątkowości i Socketach.

volatile
Tutaj podobnie jak z synchronized – dotyczy to wielowątkowości, zostawmy to sobie na jeden z kolejnych wpisów. W dużym skrócie zapewnia ona widoczność zmian pomiędzy wątkami.

native
Ten modyfikator odnosi się on do Java native interface (JNI). Zaznacza on, że dana metoda została napisana w innym języku programowania.

Wykorzystujemy go, gdy chcemy:
– uruchomić fragment kodu, który został napisany w innym języku (przeważnie C lub C++).
– chcemy odwołać się do zasobów systemu, lub urządzeń, do których mają dostęp tylko inne języki.

Ten temat także zostawimy sobie na osobny wpis.

Takim oto sposobem dotarliśmy do końca tego wpisu. Kilka podstaw za nami, są to jednak ważne rzeczy. Gratuluję wytrwałości, walcz dalej, będzie tylko ciekawiej!

Your email address will not be published. Required fields are marked *

*