DostępnośćW poprzednim wpisie pokazałem jak używać modyfikatorów w czasie deklaracji klasy.

Dzisiaj przyjrzymy się składowym klas, czyli metodom, zmiennych. Ponownie pojawią się tutaj modyfikatory dostępu, jak i inne.

 

Cele certyfiatu - Objectives 1.3 i 1.4

Exam Icon

1.3. Develop code that declares, initializes, and uses primitives, arrays, enums, and objects as static, instance, and local variables. Also, use legal identifiers for variable names.

Napisz kod, w którym deklarujesz, inicjujesz oraz używasz zmiennych statycznych, instancyjnych oraz lokalnych typów prymitywnych, tablicowych, wyliczeniowych oraz obiektowych. Użyj poprawnych identyfikatorów dla nazw zmiennych.

1.4 Develop code that declares both static and non-static methods, and - if appropriate - use method names that adhere to the JavaBeans naming standards. Also develop code that declares and uses a variable-length argument list."

Napisz kod deklarujący statyczna i niestatyczną metodę zgodnie ze standardami JavaBeans. Stwórz też kod, który używa listy argumentów.

Modyfikatory dostępu

Metody i zmienne też mają swoje poziomy dostępu. Opiszę tutaj wszystkie 4 poziomy dostępu.

  • public
  • protected
  • “” (domyślny)
  • private

Domyślny dostęp jest określany poprzez nie pisanie żadnego słowa kluczowego. Domyślny poziom i chroniony poziom jest zazwyczaj na tym samym poziomie ,ale mają jedną różnicę.

Co oznaczają modyfikatory dostępu dla składowych jednej klasy ,gdy druga próbuje uzyskać dostęp. Dla uproszczenia zapomnij o podstawowej różnicy pomiędzy metodą  a zmienną w tym wypadku będzie chodzić o to samo. Jeśli jednak klasa nie może uzyskać dostępu do danego elementu drugiej klasy kompilator  o tym przypomni. A program Eclipse dosyć szybko. W końcu spróbowałeś uzyskać dostęp do czegoś, co dla tej klasy nie istnieje.

Są dwie istotne sprawy, jeśli chodzi o dostępność:

  • Czy metoda w jednej klasie może być dostępna w innym elemencie, w innej klasie?
  • Czy klasa pochodna może dziedziczyć element klasy bazowej (super klasy)

Przyjrzymy się dokładnie tym dwóm sprawom.

Aby uzyskać dostęp do zmiennej, czy metody jednej klasy w innej klasie trzeba użyć operatora kropki “.” aby wywołać daną metodę albo sprawdzić zmienną.

public class Posąg {
    public String PatrzNaMnie(){
        return "Fajne dzieło sztuki";
    }
}
public class Muzeum 
{
    public void uzyjPosagu()
    {
        Posąg posag = new Posąg();
        //Klasa muzeum ma dostęp do klasy Posąg  
        //ale czy ma dostęp do jej składowych         

        System.out.println(posag.PatrzNaMnie());
        //jak widać tak  
        //metoda "PatrzNaMnie" jest w końcu publiczna 
    }
}

W drugim przypadku powinieneś wiedzieć, kiedy jedna klas może dziedziczyć elementy po drugiej klasie. Pamiętaj, jeśli klasa dziedziczy element klasy to tak jakby ona zadeklarowała dokładnie taki sam element. Czyli jeśli klasa pochodna dziedziczy element, to klasa pochodna ma ten element.

public class Posąg 
{
    public String PatrzNaMnie()
    {
        return "Fajne dzieło sztuki";
    }
}
public class Muzeum extends Posąg
{
    public void muzeowaMetoda()
    {
        //czy klasa Muzeum odziedziczyła metody z Posąg         
        System.out.print(this.PatrzNaMnie());
        //odpowiedź brzmi tak ponieważ inaczej ta linijka 
        //by nie zadziała  
        //Czy instancja Muzeum może wywołać PatrzNaMnie z 
        //instancji Posągu tak         

        Posąg posąg = new Posąg();
        System.out.print(posąg.PatrzNaMnie());
    }
}

W dziedziczeniu dostępność obydwu klas ma znaczenie, omówiłem to wcześniej. Po prostu trzeba pamiętać ,że jeśli klasa Muzeum nie ma dostępu do klasy Posąg to nie ma ona także dostępu do jej zawartości, co jest całkowicie logicznie.

Musisz jednak poznać wszystkie kombinacje dostępności klas, jak i ich elementów. Przykładowo kombinacja dostępności domyślnej klasy ,a elementu, który jest zadeklarowany publicznie. Oczywiście najpierw najważniejsza jest dostępność klasy. Jeśli klasa nie jest widoczna dla innej klasy to znaczy ,że jej elementy też. Dlatego najpierw trzeba spojrzeć na dostępność klasy ,a później elementu.

Elementy Publiczne

Kiedy metoda albo zmienna jest zadeklarowana publicznie to znaczy ,że wszystkie inne klasy bez względu na to, czy są w tej samej paczce, czy nie - mogą uzyskać dostęp do tego elementu.(zakładając, że klasa z tym elementem też jest widoczna).

Jednym słowem publiczne metody i zmienne są wszędzie dostępne, co ilustruje obrazek poniżej.

public class-02

Przyszedł czas na przykład w kodzie. Czy różnica w paczkach ma znaczenie w metodach publicznych?

image

package innaPaczka;
import filmyPaczka.*;

//import wszystkich klas z paczki filmyPaczka 
public class Odtwarzacz 
{
    public void Metoda()
    {
        Film film = new Film();
        film.pokazTytul("Commando");
    }
}

A tak wygląda klasa "Film" z metodą “pokazTytul”:

package filmyPaczka;

public class Film 
{
    public void pokazTytul(String t) 
    {
        System.out.print(t);
    }
}

Jak widzisz, pomimo iż klasa "Film" i klasa "Odtwarzacz" są w innych paczkach nie było problemu z wywołaniem metody ponieważ jest ona publiczna.

Metoda publiczna też jest dziedziczona bez żadnych problemów, oto przykład.

public class SerialTelewizyjny 
{
    public void pokazCzołowke(String s)
    {
        System.out.print(s);
    }
}

Odziedziczona metoda może być użyta w każdym momencie w  klasie pochodnej.

public class SerialKomediowy extends SerialTelewizyjny 
{

    public void testCzolowki(String s){
        pokazCzołowke(s);
    }
}
public class SerialKomediowy extends SerialTelewizyjny 
{

    public void testCzolowki(String s){
        pokazCzołowke(s);
    }
}

Odziedziczona metoda jest wywoływana bez referencji i bez operatora kropki, ponieważ ta metoda jest zawarta w klasie pochodnej w wyniku dziedziczenia. Czyli jeśli metoda jest wywoływana w ten sposób oznacza to ,że ta metoda bądź zmienna należy do tej klasy. Oznacza to też ,że metoda ta może być  uzyskana przez słowo  kluczowe this, które referuje się do obecnej klasy w kodzie.

Metoda publiczna też jest dostępna poprzez wywołanie referencji innej klasy np. tak:

public class Lew 
{
    public void ryk(){}
}
public class Zoo 
{
    
    public void odglosyZoo()
    {
        Lew lew = new Lew();
        lew.ryk();
    }
    
}

Podsumowując metoda publiczna i zmienna publiczna jest zawsze i wszędzie dostępna.

Elementy Prywatne

Elementy oznaczone jako prywatne nie są dostępne nigdzie za wyjątkiem klasy gdzie one powstały.

Wracając do poprzednich przykładów co się stanie jeśli spróbuje złamać tę regułę, np. co jeśli teraz metoda ryk() w klasie Lew jest prywatna.

image

Metoda ryk() teraz jest prywatna  co znaczy, że inne klasy nie mogą jej użyć. Próba wywołania tej metody w innej klasie zwraca błąd w kompilatorze.

Dla innych klas metoda ta będzie niewidoczna. Jest ona widoczna tylko w klasie, gdzie ona powstała. Co ilustruje przykład poniżej.

public class Lew 
{
    private void ryk(){}
    
    public void glos(){
        ryk();
    }
}

Teraz co się stanie, jeśli klasa pochodna spróbuje odziedziczyć prywatny element super klasy. Otóż nic się nie stanie. Prywatne elementy nie mogą być dziedziczone. Możesz co prawda zadeklarować tę samą metodę w klasie pochodnej, ale nie jest to przysłonięcie,ż metoda nie została odziedziczona.  Jest to zwykła metoda, która po prostu ma taką samą nazwę jak metoda prywatna, o której nie powinieneś nic wiedzieć.


public class SerialTelewizyjny 
{
    private void pokazCzołowke(String s)
    {
        System.out.print(s);
    }
}

Podobnie jak w poprzednim przykładzie próba wywołania metody, której nie ma wywoła błąd w kompilatorze.

image

Eclipse jest inteligentnym programem i sugeruje mi zmianę modyfikatora dostępu z prywatnego na chroniony (protected). Faktycznie rozwiązałoby to problem. Do modyfikatora “protected” jeszcze dojdziemy.

Prywatna metoda jest niewidzialna dla jakiekolwiek kodu poza klasą, gdzie element został utworzony.

Czy metodę prywatną można nadpisać (overridden)? Odpowiedź brzmi "nie" ponieważ ten mechanizm też wymaga odziedziczenia tego elementu.

public class-01

Elementy Chronione i Domyślne

Dostęp chroniony i domyślny dostęp są prawie identyczne ,ale istnieje pewna jedna kolosalna różnica. Element z domyślnym dostępem jest widoczny  tylko, gdy elementy należą do tej samej paczki. Różnica jest taka ,że w trybie chronionym element jest zawsze dostępny w mechanizmie dziedziczenia, nawet jeśli klasa pochodna znajduje się w innej paczce.

Spójrz na znany  przykład, ale z modyfikatorem “protected”.

public class SerialTelewizyjny 
{
    protected void pokazCzołowke(String s)
    {
        System.out.print(s);
    }
}

Teraz nie ma już żadnych błędów kompilacji.

public class SerialKomediowy extends SerialTelewizyjny 
{
    public void testCzolowki(String s)
    {
        pokazCzołowke(s);
    }
}

Chociaż oczywiście modyfikator “protected” nie pozwala na użycie metody w wyniku referencji klasy, chyba że klasa znajduje się w tej same paczce. W tym wypadku jest to paczka “filmPaczka”.

package filmyPaczka;

public class SerialTelewizyjny 
{
    protected void pokazCzołowke(String s)
    {
        System.out.print(s);
    }
}
package filmyPaczka;

public class SerialKomediowy extends SerialTelewizyjny 
{

    public void testCzolowki(String s)
    {
        pokazCzołowke(s);
    }

    public void test()
    {
        SerialTelewizyjny se = new SerialTelewizyjny();
        se.pokazCzołowke("ds");
    }
}

Jednak jeśli klasa znajduje się w innej paczce próba użycia elementu poprzez referencje klasy skończy się błędem.

image

Teraz spójrzmy jak sprawa wygląda przy modyfikatorze “domyślny”.Podobnie jak ze słowem “protected”  problem nie występuje, gdy chcemy odziedziczyć element z klasy oraz, gdy chcemy użyć elementu poprzez referencje, tylko te klasy muszą być w tej samej paczce.

package filmyPaczka;

public class SerialTelewizyjny 
{
    void pokazCzołowke(String s)
    {
        System.out.print(s);
    }
}
package filmyPaczka;

public class SerialKomediowy extends SerialTelewizyjny 
{

    public void testCzolowki(String s){
        pokazCzołowke(s);
    }

    public void Test()
    {
        SerialTelewizyjny se = new SerialTelewizyjny();
        se.pokazCzołowke("ds");
    }
}

Podobnie ja przy słowie “protected” problem występuje, gdy chcemy użyć elementu poprzez referencje klasy, która znajduje się w innej paczce.

java default

Jak na razie nie widać żadnych różnic.

Zachowanie pomiędzy“protected” ,a “default” ma jednak pewną różnicę i widać to w klasach pochodnych.

Jeśli słowo kluczowe “protected”jest użyte jako modyfikator dostępu elementu , wtedy ten element jest dostępny dla każdej klasy w mechanizmie dziedziczenia. Niezależnie od paczki ten element zawsze będzie widoczny w dziedziczeniu.

W przeciwieństwie do zachowania “default” element ten będzie widoczny, tylko gdy jest on w tej samej paczce. Nie możemy uzyskać dostępu do elementu “default”, jeśli obie klasy nie są w tej samej paczce. Bez wyjątków.

Natomiast przy protected dostęp do elementu jest wokół paczki, jak i poza nią ,ale tylko w mechanizmie dziedziczenia. Najprościej mówiąc protected to “paczka” + “dzieci”. Klasa pochodna może zawsze odziedziczyć element protected. To jest właśnie ta jedna różnica pomiędzy dostępem domyślnym a chronionym.

Jednak tak jak wykazałem wcześniej przy protected i default element nie może być użyty  w wyniku referencji poza paczką.

Więcej przykładów

Nic tak nie ułatwia procesu zapamiętywania, jak przykład w kodzie. Oto jak dziedziczenie poza paczką zachowuje się przy modyfikatorze protected.

package czerwonaPaczka;

public class Rodzic 
{
    protected double liczba = 3.14;
}

Klasa "rodzic" posiada jedną zmienną, która jest chroniona a sama klasa znajduje się w “czerwonejPaczce”. Zgodnie z tym, co napisałem kilka linijek temu element ten powinien być dostępny w  wyniku dziedziczenia nawet jeśli klasa znajduje się w innej paczce.

package innaPaczka;
import czerwonaPaczka.Rodzic;

public class Dziecko extends Rodzic 
{
    public void test()
    {
        liczba = -liczba;
        //nie żadnych problemów 
    }
}

Rzeczywiście tak jest.

Warto też zapamiętać ,że zachowanie protected zawsze zostaje bez względu na to ile klas odziedziczy ten element. Co mam na myśli. Powiedzmy ,że mam klasę “Kolega”, który chce w wyniku referencji użyć zmiennej “liczba” z klasy Dziecko.

java protected

Oczywiście klasa Kolega nie może tego zrobić, pomimo iż jest w tej samej paczce co klasa Dziecko, ponieważ zachowanie protected od klasy "Rodzic" zostało zachowane. Rodzic wciąż jest w innej paczce więc nie ma mowy o uzyskaniu dostępu do zmiennej w ten sposób.

Wracając do ogólnego przykładu  co się stanie, gdy zmienię modyfikator zmiennej  z protected na default w klasie Rodzic. Jak napisałem wcześniej w tym wypadku powinien otrzymać błąd kompilacji.

package czerwonaPaczka;

public class Rodzic 
{
    double liczba = 3.14;
}

deafult java 2

Oczywiście tak jest. Oto lista rozwiązań, które zasugerował mi Eclipse do rozwiązania tego problemu. Główną sugestią jest zmiana modyfikatora na protected.

Podsumowując działanie modyfikatora “default”  działa tak.

public class-03

A modyfikatora “protected”.

public class-04

Lokalne zmienne i modyfikator dostępu

Czy do zmiennych lokalnych można stosować modyfikator dostępu?

Nie można.

Zmienne lokalne istnieją tylko w danym bloku w kodzie jak np. w metodzie. Po wykonaniu tego bloku zmienna ta znika. Nie można  do niej dodać modyfikatora dostępu, ponieważ po prostu  nie ma to sensu i jest to traktowane jako błąd. Istnieje tylko jeden modyfikator, który może być użyty do zmiennej lokalnej i jest to modyfikator final.

image

To wszystko, jeśli modyfikatory dostępu składowych klas.

WidocznośćPublicProtectedDefaultPrivate
Do tej samej klasyTakTakTakTak
Do każdej innej klasy z tej samej paczkiTakTakTakNie
Do klasy pochodnej z tej samej paczkiTakTakTakNie
Do klasy pochodnej z innej paczkiTakTakNieNie
Do innej klasy, która nie jest klasą pochodną i znajduje się w innej paczce.TakNieNieNie


Następnym razem omówię inne modyfikatory elementów.

Spis treści: