Modyfikatory dostępu w Javie
Poprzednio przyjrzeliśmy się bliżej metodom, konstruktorom oraz sposobom przekazywania danych do metod.
Modyfikatory dostępu do pól
W dzisiejszej lekcji skupimy się na modyfikatorach dostępu klas, metod oraz pól. Zaczniemy od ich wylistowania.
- public – oznacza widoczność bez ograniczeń.
- protected – widoczność w obrębie klas dziedziczonych.
- private – widoczność tylko w obrębie danej klasy.
- brak modyfikatora dostępu – widoczność w obrębie danego pakietu.
Modyfikatory dostępu określają widoczność elementów, takich jak pola, metody, klasy i interfejsy. To od nich zależy w jaki sposób można uzyskać dane oraz czy będą bezpieczne. Z modyfikatorami dostępu dużo wspólnego ma zasada hermetyzacji danych. Poniżej przyjrzymy się bliżej co konkretne słowo oznacza w różnych przypadkach.
Modyfikator public
Ten modyfikator zapewnia swobodny dostęp do elementu – klasy, interfejsu, pola lub metody. Ogólnie możemy przyjąć, że metody w Javie powinny być publiczne. Natomiast jeśli jakaś metoda będzie użyta wyłącznie w obrębie danej klasy można nadać jej modyfikator private. Podobnie jeśli chcemy ukryć tę metodę, aby nie była widoczna przy działaniu na obiekcie w innej klasie.
Modyfikator public a zachowanie pól
Poniżej pokażemy przykład jak w praktyce modyfikator public wpływa na zachowanie pól w klasie.
1 2 3 4 5 6 |
public class Samochod { public int przebieg = 0; public int pokazPrzebieg(){ return przebieg; } } |
1 2 3 4 5 6 7 8 9 |
Public class Main{ Public static void main(String[] args){ Samochod car = new Samochod(); System.out.println(„Przebieg: ” + car.przebieg); //przebieg == 0 car.przebieg = 1000; System.out.println(„Przebieg: ” + car.przebieg); //przebieg == 1000 System.out.println(„Przebieg: ” + car.pokazPrzebieg()); //ten sam efekt, który jest powyżej } } |
Przy użyciu modyfikatora public możemy bezpośrednio wpisywać i pobierać wartości z obiektu. Czemu zatem na siłę utrudniać sobie życie i nie uczynić wszystkich pól publicznych? Ponieważ w ten sposób złamiemy zasadę hermetyzacji danych.
1 2 3 |
car.przebieg = 1000; System.out.println(„Przebieg: ” + ++car.przebieg); //1001 System.out.println(„Przebieg: ” + car.przebieg); //1001 |
Jak widać powyżej chcąc wyświetlić zmodyfikowaną wartość przez przypadek została zmodyfikowana wartość w obiekcie.
Modyfikator public przy metodach
Skupmy się teraz na modyfikatorze metody. Na powyższym przykładzie mamy dwie klasy – Main
i Samochod
. Dzięki temu, że metoda pokazPrzebieg()
jest publiczna możemy jej użyć na obiekcie, który znajduje się w ciele metody main()
w klasie Main
.
Co by się zmieniło gdyby przed metodą zamiast modyfikatora public napisać private? Otóż metoda byłaby widoczna jedynie w obrębie klasy Samochod
i nie można byłoby wywołać jej w klasie Main
.
Modyfikator private
Ten modyfikator jest przeciwieństwem public, gdyż jest najbardziej restrykcyjny jeśli chodzi o dostęp do danych. Dostęp do danych wymaga specjalnych metod. Nie można go stosować do klas poza jednym wyjątkiem – klas wewnętrznych.
Modyfikator private a dostęp do pól
Zamieńmy teraz modyfikator w polu przebieg i zobaczmy jak on wpłynie na działanie programu.
1 2 3 4 5 6 |
Public class Samochod { private int przebieg = 0; public int pokazPrzebieg(){ return przebieg; } } |
1 2 3 4 5 6 7 8 |
Public class Main{ Public static void main(String[] args){ Samochod car = new Samochod(); System.out.println(„Przebieg: ” + car.przebieg); //czy to zadziała? car.przebieg = 1000; //czy to zadziała? System.out.println(„Przebieg: ” + car.pokazPrzebieg()); // } } |
W przypadku próby dostępu do pola przebieg bezpośrednio bez użycia metody pokazPrzebieg()
kompilator zwróci błąd.
Modyfikacja oraz dostęp do pól prywatnych
Jak zatem zapisać wartość w polu o tak restrykcyjnym modyfikatorze dostępu? Należy użyć metody, która to zrobi i wywołać ją na rzecz danego obiektu (tzw. getter lub setter). Poniższy listing pokaże przykładową implementację takiej metody.
1 2 3 |
Public void ustawPrzebieg(int p){ Przebieg = p; } |
Oczywiście możemy ją opracować w inny sposób np. dodając wartość podaną w argumencie.
1 2 3 |
Public void dodajPrzebieg(int p){ Przebieg += p; } |
Metoda dostępu różni się tym, że musi zwracać wartość typu, którego jest pole.
1 2 3 |
public int getPrzegieg(){ return przebieg; } |
Modyfikator private dla metod
W przypadku użycia modyfikatora private do metody będzie do niej dostęp będzie widoczny jedynie w obrębie danej klasy. Jest to wygodne rozwiązanie, gdy nie chcemy, aby ktoś mógł wywołać metodę poza konkretną klasą.
Modyfikator protected
Wykorzystywany jest w przypadku dziedziczenia i dostępie z klas podrzędnych (podklas). Zrozumienie tego modyfikatora wymaga poznania pojęcia dziedziczenia – czyli jednego z filarów paradygmatu programowania obiektowego. Spójrzmy na poniższy kod.
1 2 3 |
public class Samochod { protected int przebieg; } |
1 2 3 4 5 6 |
class SamochodElektryczny extends Samochod{ private int pojemnoscAkumulatora; public int pokazPrzebieg(){ return przebieg; } } |
Modyfikator protected a dostęp do pól
Dzięki zastosowaniu modyfikatora protected w podklasie można zdefiniować metodę, która się do pola zdefiniowanego w nadklasie.
Modyfikator protected dla metod
Podobnie jak w przypadku pól zachowuje się widoczność metod. W podklasie można z nich korzystać, a nawet przesłonić własną definicją. Aby wywołać metodę z nad klasy należy użyć słowa kluczowego super.
1 2 3 4 5 6 |
public class Samochod { protected int przebieg; protected int pokazPrzebieg(){ return przebieg; } } |
1 2 3 4 5 6 7 8 |
public class SamochodElektryczny extends Samochod{ private int pojemnoscAkumulatora; //przesłonięta metoda public int pokazPrzebieg(){ //wywołanie metody nadklasy przy użyciu słowa kluczowego super return super.pokazPrzebieg(); } } |
Gdybyśmy nie użyli słowa kluczowego super, to metoda wywołałaby samą siebie, a więc wywoła się rekurencyjnie. Rezultatem będzie przepełnienie stosu oraz błąd StackOverflowError.
1 2 3 4 |
Exception in thread "main" java.lang.StackOverflowError at pl.programistajava.SamochodElektryczny.pokazPrzebieg(SamochodElektryczny.java:7) at pl.programistajava.SamochodElektryczny.pokazPrzebieg(SamochodElektryczny.java:7) at pl.programistajava.SamochodElektryczny.pokazPrzebieg(SamochodElektryczny.java:7) |
Tego modyfikatora nie można użyć przed klasą.
Brak modyfikatora dostępu
W przypadku pominięcia uzupełnienia modyfikatora dostęp do danych będzie widoczny wyłącznie w obrębie pakietu, co bywa problematyczne i nie jest zalecane.
Podobnie jest w przypadku klas – gdy znajdują się w innych pakietach nie są dla siebie widoczne – nawet jeśli zaimportujemy pakiet.
Porady do używania modyfikatorów dostępu
Zazwyczaj należy stosować zasadę, że wszystkie pola i metody, które są wykorzystywane wyłącznie w klasie powinny być prywatne, a pozostałe metody i klasy – publiczne.
Pakiety
Pakiety podobnie jak foldery są jednostką porządkującą. Ułatwiają znalezienie konkretnej klasy oraz zwalniają z obowiązku pilnowania czy klasa nie znajduje się już w jakiejś bibliotece. Jeśli klasa o tej samej nazwie znajduje się w dwóch różnych pakietach, to nie wystąpi żaden konflikt nazw. Do przypisania klasy do pakietu służy słowo kluczowe package.
Słowo kluczowe import
Jeżeli chcemy skorzystać z osobnego pakietu (klasy są widoczne w obrębie jednego pakietu), to należy zaimportować inny pakiet słowem import.
Zaimportować można zarówno cały pakiet korzystając z operatora * lub konkretną klasę – wtedy należy napisać całą nazwę pakietu i klasy.
1 2 |
import java.util.* import java.util.Scanner |
Import całej biblioteki java.util nie wpływa negatywnie na rozmiar kodu, ani na wydajność. Natomiast utrudnia zidentyfikowanie, które klasy zostały użyte w programie.
Konflikt nazw
Jeśli w naszym pakiecie zaimplementujemy klasę o tej samej nazwie, która znajduje się w innym pakiecie, to kompilator będzie miał problem z użyciem odpowiedniej klasy. W związku z tym należy wskazać kompilatorowi za każdym razem klasę, którą chcemy użyć. Załóżmy, że w naszym pakiecie mamy klasę Scanner, która robi inną rzecz niż na z biblioteki java.util. Poniższy listing pokaże jak użyć odpowiedniej klasy.
1 2 3 |
import java.util.* … java.util.Scanner = new java.util.Scanner; |
Nazewnictwo pakietów
Aby nie spodować konfliktu nazw warto stosować się do konwencji nazewczniczej. domena.mudułAplikacji np.
pl.programistajava.samochod
Wszystkie pakiety z biblioteki standardowej znajdują się w pakietach java i javax.
W kolejnej części przyjrzymy się słowu kluczowemu static oraz metodzie main.