Kto może dotykać twojego kodu
Modyfikator dostępu to słowo kluczowe przed klasą, polem, metodą lub konstruktorem, które decyduje, kto jeszcze może z niego korzystać. To podstawa hermetyzacji: ukrywasz zawiłe wnętrze klasy i udostępniasz tylko te części, które inny kod może bezpiecznie wywoływać.
Java ma cztery poziomy, od najbardziej otwartego do najbardziej zamkniętego: public, protected, domyślny (bez żadnego słowa kluczowego) i private. Gdy dobierzesz je dobrze, klasę można później bezpiecznie zmieniać, bo nic poza nią nie zależy od szczegółów, których nigdy nie obiecywałeś utrzymać.
private: widoczne tylko wewnątrz klasy
private to najściślejszy poziom. Składowej private może używać tylko kod napisany w tej samej klasie: nie podklasy, nie inne klasy z tego samego pakietu, nikt inny. Tu powinna trafić większość twoich pól.
balance jest private, więc jedynym sposobem na jego zmianę jest deposit, które odrzuca ujemne kwoty. Gdyby balance było public, dowolny kod mógłby napisać account.balance = -9999 i całkowicie pominąć sprawdzenie. Właśnie ten warunek jest powodem, dla którego ukrywa się pole.
Spróbuj dodać w main wiersz account.balance = 500;, a dostaniesz błąd kompilacji: balance has private access in BankAccount.
public: widoczne wszędzie
public to drugi biegun: każdy i wszędzie może używać składowej. Metody, które inny kod ma wywoływać, są zwykle public. Tak samo sama klasa, gdy musi być dostępna z innych pakietów.
Typowy wzorzec, nazywany czasem publicznym API klasy, to prywatne pola, publiczne metody. Pola przechowują stan, w którym nikt inny nie powinien grzebać, a metody są kontrolowanymi drzwiami do środka i na zewnątrz. getBalance i deposit powyżej działają dokładnie tak.
default (package-private): bez słowa kluczowego
Jeśli nie napiszesz żadnego modyfikatora, dostajesz dostęp domyślny, nazywany też package-private. Składowa jest widoczna dla każdej klasy w tym samym pakiecie i niewidoczna poza nim. Ten poziom nie ma słowa kluczowego: to właśnie jego brak jest tym poziomem.
class Invoice { // bez modyfikatora -> widoczne tylko w tym pakiecie
int amount; // bez modyfikatora -> pole package-private
}
To częsta pułapka dla początkujących: pominięcie public nie czyni niczego private, tylko package-private. Pole, które miało być ukryte, nadal może czytać i zmieniać każda klasa z tego samego pakietu. Jeśli chcesz je naprawdę ukryć, musisz jawnie napisać private.
Dostęp domyślny jest naprawdę przydatny dla klas pomocniczych, które są szczegółem implementacji jednego pakietu: nie eksportujesz ich, ale klasy współpracujące w obrębie pakietu mogą swobodnie z nich korzystać.
protected: pakiet i podklasy
protected jest o stopień luźniejsze niż dostęp domyślny. Składowa protected jest widoczna wszędzie tam, gdzie domyślna (w tym samym pakiecie), a także w podklasach, nawet w podklasach z innego pakietu. Służy do rzeczy, którymi chcesz się dzielić z klasami rozszerzającymi twoją klasę, ale nie z całym światem.
Dog sięga do name i sound(), bo są protected, a Dog jest podklasą. Zewnętrzny kod, który nie jest podklasą i nie należy do pakietu, nadal nie ma do nich dostępu. Sięgaj po protected, gdy projektujesz klasę przeznaczoną do rozszerzania; w innych przypadkach wybieraj private.
Cztery poziomy w skrócie
Oto pełny obraz, od najbardziej restrykcyjnego do najbardziej otwartego:
| Modyfikator | Ta sama klasa | Ten sam pakiet | Podklasa (inny pakiet) | Wszędzie |
|---|---|---|---|---|
private | tak | nie | nie | nie |
| domyślny | tak | tak | nie | nie |
protected | tak | tak | tak | nie |
public | tak | tak | tak | tak |
Prosta zasada: zaczynaj od private i poluzowuj dostęp tylko wtedy, gdy coś naprawdę go potrzebuje. Znacznie łatwiej jest później rozszerzyć dostęp, niż go zawęzić, gdy inny kod zacznie już zależeć od danej składowej.
Modyfikatory przy samych klasach
Modyfikatory dostępu dotyczą też klas, nie tylko składowych, ale klasa najwyższego poziomu (niezagnieżdżona w innej) może być tylko public albo domyślna. Nie da się napisać klasy najwyższego poziomu private ani protected.
public class Order { ... } // dostępna z każdego pakietu
class LineItem { ... } // default: widzi ją tylko ten pakiet
Klasa najwyższego poziomu public musi być w pliku nazwanym tak jak ona (Order.java). Klasy zagnieżdżone mogą natomiast używać wszystkich czterech modyfikatorów i dlatego w przykładach powyżej klasy wewnętrzne mogły być private albo domyślne. Jedna subtelność: metoda public zwracająca typ package-private niewiele daje zewnętrznym wywołującym, więc dbaj o spójność publicznego interfejsu.
Dalej: składowe statyczne
Do tej pory każde pole i każda metoda należały do instancji: tworzysz obiekt przez new, a potem wywołujesz na nim metody. Czasem jednak wartość lub zachowanie należy do klasy jako całości i jest wspólne dla wszystkich instancji, na przykład licznik utworzonych obiektów. Do tego służy słowo kluczowe static, które omówimy w następnej kolejności.
Najczęściej zadawane pytania
Jakie są cztery modyfikatory dostępu w Javie?
public, private, protected i domyślny (bez słowa kluczowego, nazywany też package-private). public jest widoczny wszędzie, private tylko w tej samej klasie, protected w pakiecie oraz w podklasach, a domyślny tylko w tym samym pakiecie.
Czym różni się public od private w Javie?
public oznacza, że dowolny kod w dowolnym miejscu ma dostęp do składowej. private oznacza, że dostęp ma tylko kod w tej samej klasie: podklasy i inne klasy w ogóle jej nie widzą. Zwykle pola są private, a udostępnia się je przez metody public.
Co oznacza brak modyfikatora dostępu przy polu w Javie?
Brak słowa kluczowego oznacza dostęp domyślny (package-private): składowa jest widoczna dla każdej klasy w tym samym pakiecie, ale niewidoczna poza nim. To poziom ostrzejszy niż public, ale luźniejszy niż private. Łatwo go uzyskać przez przypadek, więc wybieraj go świadomie.