java_03.pdf

(210 KB) Pobierz
Microsoft Word - roz_3.doc
Jacek Rumi ń ski - J ę zyk JAVA – Rozdzia ł 3
Rozdział 3 Programowanie obiektowe
3. 1 Obiekty
3.2 Klasy abstrakcyjne
3.2 Klasy abstrakcyjne
3.3 Interfejsy, specyfikatory dost ę pu
3.3 Interfejsy, specyfikatory dost ę pu
3.4 Statyczne klasy wewn ę trzne
3.4 Statyczne klasy wewn ę trzne
3.5 Klasy anonimowe
3.5 Klasy anonimowe
3.6 Adaptery
3.6 Adaptery
3.7 Dziedziczenie
3.7 Dziedziczenie
3.8 Niszczenie obiektów – zwalnianie pami ę ci
3.8 Niszczenie obiektów – zwalnianie pami ę ci
3.9 Tablice
3.9 Tablice
3.1 Obiekty
Człowiek różnymi metodami modeluje otaczający do świat, jego właściwości i
procesy w nim zachodzące. W opisie rzeczywistości wprowadzono pojęcia
kategoryzujące elementy świata. Do najbardziej znanych pojęć tego typu należy
zaliczyć „byt” jako coś co istnieje. Starożytni filozofowie dokonywali różnych
podziałów bytów. Wprowadzono podział na bytu ogólne i jednostkowe. Arystoteles
wprowadził ciekawy opis bytu używając terminów forma i materia. Tak rozpoczęto
klasyfikować byty pod względem ich form, czy też gatunku lub inaczej typu albo
wreszcie klasy. Można więc przedstawić klasę takich bytów, które posiadają wspólne
właściwości (formę). Klasa (forma) opisuje byt, jej połączenie z materią tworzy byt.
Można więc powiedzieć, że materia połączona z określoną formą tworzy jednostkowy
byt - czyli obiekt. W ten sposób uzyskano podstawę założeń programowania
obiektowego: Obiekt jest jednostkowym wystąpieniem Klasy go opisującej. Oznacza
to, że za pomocą programowania obiektowego tworzony jest model bytu, a nie jego
opis ilościowy, tak jak to jest wykonywane w programowaniu proceduralnym. Opis
obiektu w klasie odbywa się poprzez modelowanie jego zachowania (metody) i stanu
(pola). Jak wspomniano już na początku tego opracowania może istnieć wiele
obiektów danej klasy. Każdy z nich jest jednak jednostkowy i istnieje w pamięci
komputera. Dostęp do obiektu jest możliwy za pomocą „uchwytu” (odwołanie do
pamięci - stery - gdzie przechowywany jest obiekt). Nowy obiekt danej klasy
tworzony jest za pomocą instrukcji new z podaniem nazwy klasy, np.:
Rycerz l luke = new Rycerz();
oznacza, że tworzony jest nowy obiekt typu Rycerz, do którego przywiązany jest
„uchwyt” luke. Tworzenie obiektu danej klasy bardzo dobrze ilustruje stosowany już w
tej pracy kod obsługujący ładowanie klas:
C l lass c = C l lass.forName(„Rycerz”);
Rycerz l luke = c.newInstance();
Kod ten wskazuje, że dla potrzeb tworzenia obiektów klasy Rycerz pobierany jest
kod tej klasy a następnie tworzone jest nowe wystąpienie tej klasy. Oczywiście
znacznie prostsze jest stosowanie instrukcji new.
Jeżeli temu samemu „uchwytowi” przypisany zostanie inny obiekt, wówczas obiekt,
na który pierwotnie wskazywał „uchwyt” ginie. Nie ma więc potrzeby w Javie
usuwania nieużywanych obiektów, gdyż jest to wykonywane automatycznie. Każdy
3-3
Jacek Rumi ń ski - J ę zyk JAVA –
3. 1 Obiekty
3.2 Klasy abstrakcyjne
3.3 Interfejsy, specyfikatory dost ę pu
3.4 Statyczne klasy wewn ę trzne
3.5 Klasy anonimowe
3.6 Adaptery
3.7 Dziedziczenie
3.8 Niszczenie obiektów – zwalnianie pami ę ci
Jacek Rumi ń ski - J ę zyk JAVA – Rozdzia ł 3
obiekt jest więc jednostkowym wystąpieniem klasy i ma charakter dynamiczny.
Można jednak wyróżnić elementy niezmienne dla obiektów tej samej klasy.
Przykładowo liczba Rycerzy nie jest własnością bytu (obiektu) lecz klasy, która
opisuje byty tego typu. Oznacza to, konieczność definicji nie dynamicznych lecz
statycznych (niezmiennych) pól i metod klasy, do których odwołanie odbywa się nie
przez obiekty lecz przez nazwę klasy obiektów. Wszystkie pola i metody statyczne
muszą być wyróżnialne poprzez oznacznik static. Określenie pola klasy jako static
oznacza, że jego stan jest jednostkowy dla wszystkich obiektów danej klasy - jest
własnością klasy a nie obiektów. Obiekt zmieniając stan pola statycznego zmienia go
dla wszystkich innych obiektów. Przykładowo:
Przykład 3.1
//Republika.java
class Rycerz{
static int liczbaRycerzy=0;
int numerRycerza =0;
Rycerz (String imie){
System.out.println(„Rada Jedi głosi: „+ imie+” jest nowym Rycerzem Jedi”);
}
}
public class Republika{
public static void main (String args[]){
Rycerz yoda = new Rycerz(„Yoda”);
Rycerz anakin = new Rycerz(„Anakin”);
Rycerz luke = new Rycerz(„Luke”);
yoda.numerRycerza=1;
yoda.liczbaRycerzy++;
System.out.println(„Liczba rycerzy wg. Yody: „+yoda.liczbaRycerzy);
System.out.println(„Liczba rycerzy wg. Anakina: „+anakin.liczbaRycerzy);
System.out.println(„Liczba rycerzy wg. Luke’a: „+luke.liczbaRycerzy);
anakin.numerRycerza=2;
anakin.liczbaRycerzy++;
System.out.println(„Liczba rycerzy wg. Yody: „+yoda.liczbaRycerzy);
System.out.println(„Liczba rycerzy wg. Anakina: „+anakin.liczbaRycerzy);
System.out.println(„Liczba rycerzy wg. Luke’a: „+luke.liczbaRycerzy);
luke.numerRycerza=3;
luke.liczbaRycerzy++;
System.out.println(„Liczba rycerzy wg. Yody: „+yoda.liczbaRycerzy);
System.out.println(„Liczba rycerzy wg. Anakina: „+anakin.liczbaRycerzy);
System.out.println(„Liczba rycerzy wg. Luke’a: „+luke.liczbaRycerzy);
}
}// koniec public class Republika
3-4
Jacek Rumi ń ski - J ę zyk JAVA –
Jacek Rumi ń ski - J ę zyk JAVA – Rozdzia ł 3
Powyższy przykład ukazuje brak zależności zmiennej liczbaRycerzy od
poszczególnych obiektów. Statyczne pola klas są więc doskonałymi elementami
przechowującymi informację o stanie klasy (np. ilość otwartych okien, plików, itp.).
Możliwe jest również stworzenie statycznej metody, której działanie jest wywoływane
bez konieczności tworzenia obiektu klasy, w ciele której zdefiniowano statyczną
metodę. Przykładowa metoda statyczna może zwracać liczbę rycerzy (z przykładu
4.1) przechowywaną jako pole statyczne poprzez:
pub l l i ic stat i ic i int l l i iczbaR(){
i int l lR=Rycerz. l l i iczbaRycerzy;
System.out.pr i int l ln(„L i iczba Rycerzy Jed i i w Repub l l i ice wynos i i: „+ l lR);
return l lR;
Należy pamiętać, co zostało już przedstawione wcześniej, tworzenie obiektu
powoduje wywołanie procedury jego inicjowania zwanej konstruktorem. Konstruktor
jest metodą o tej samej nazwie co nazwa klasy, dla której tworzony jest obiekt.
Konstruktor jest wywoływany automatycznie przy tworzeniu obiektu. Stosuje się go
do podawania argumentów obiektowi, oraz do potrzebnej z punktu widzenia danej
klasy grupy operacji startowych. Wywołanie konstruktora powoduje zwrócenie
referencji do obiektu danej klasy. Nie można więc deklarować konstruktora z typem
void. Kod konstruktora może zawierać wywołanie innego konstruktora tej samej klasy
lub wywołanie konstruktora nadklasy. Kolejność wołania konstruktorów w kodzie
danego konstruktora jest następująca:
NazwaK l lasy(argumety){
th i is(argumenty 1 1); //wywo ł ł łan i ie i innego konstruktora te j j same j j k l lasy
super(argumenty 1 1); //wywo ł ł łan i ie konstruktora nadk l lasy
kod;
Jeżeli programista jawnie nie zdefiniował konstruktora to jest on tworzony domyślnie,
jako kod pusty bez argumentów , np.:
NazwaK l lasy(){
}
Rozważania dotyczące klas zamieszczono na początku tego opracowania. W Javie
istnieje możliwość sprawdzenia czy dany obiekt jest wystąpieniem danej klasy. Do
tego celu służy operator instanceof, np. luke instanceof Rycerz. Efektem działania
operatora jest wartość logiczna true - jeśli obiekt jest danej klasy, lub false - w
przeciwnym przypadku.
3.2 Klasy abstrakcyjne
Kolejnym bardzo ważnym elementem programowania obiektowego jest
odwołanie się do rozważań w filozofii nad możliwością istnienia bytów ogólnych
(pojęć ogólnych, uniwersalii, itp.). Przykładowe pytanie tych rozważań może być
następujące: Czy może istnieć obiekt ogólny Rycerz? Teoretycznie w Javie nie może
3-5
Jacek Rumi ń ski - J ę zyk JAVA –
}
}
Jacek Rumi ń ski - J ę zyk JAVA – Rozdzia ł 3
istnieć taki obiekt, lecz istnieje obiekt klasy Class związany i reprezentujący daną
klasę (np.: Class c = Class.forName(„Rycerz”);). Obiekt taki jest wykorzystywany
przez Javę do obsługi klas (szczególnie w zarządzaniu ładowaniem klas do pamięci).
W Javie istnieje jeszcze jeden typ klas, dla których nie można inicjować obiektów
metodą new. Ponieważ nie można stworzyć obiektów takiej klasy, klasa taka nie ma
rzeczywistego wykorzystania w programowaniu obiektowym i jest oznaczana jako
abstrakcyjna - abstract. Klasa abstrakcyjna zawiera w opisie obiektu również metody
abstrakcyjne. W celu stworzenia obiektu, dla którego opis znajduje się w klasie
abstrakcyjnej należy stworzyć klasę pochodną klasy abstrakcyjnej i podać
rzeczywisty opis (realizację) wszystkim metodom abstrakcyjnym zdefiniowanym w
klasie abstrakcyjnej. Klasy abstrakcyjne stosuje się wówczas, gdy na danym stopniu
opisu ogólnego możliwych obiektów nie można podać rzeczywistej realizacji jednej
lub wielu metod. Przykładowo tworząc klasę Statek nie można podać metody
obliczającej pole powierzchni statku, gdyż ta zależy od danego typu statku
kosmicznego, dla którego dany jest wzór na pole powierzchni. Tak więc klasa
pochodna typu statku np.: GwiezdnyNiszczyciel, może zdefiniować w swym ciele
metodę o tej samej nazwie co w klasie Statek, lecz określonej implementacji
(realizacji). Dzięki temu wszystkie typy statków opisywane własnymi klasami będą
miały podobny opis ogólny, ułatwiający wymianę informacji. Problem ten zilustrowano
przykładem 3.2:
Przykład 3.2:
//Flota.java
abstract class Statek{
int numerStatku;
int liczbaDzial;
long predkoscMax;
public abstract int polePowierzchni();
public void informacje(){
System.out.println("Liczba dział = "+liczbaDzial);
System.out.println("Prędkość maksymalna = "+predkoscMax);
System.out.println("Numer identyfikacyjny = "+numerStatku);
}
} // koniec abstract class Statek{
class GwiezdnyNiszczyciel extends Statek{
int wysTrojkata;
int dlgPodstawy;
GwiezdnyNiszczyciel(int numer){
numerStatku=numer;
}
public int polePowierzchni(){
return (wysTrojkata*dlgPodstawy/2);
}
}// koniec class GwiezdnyNiszczyciel
class GwiezdnySokol extends Statek{
int szer;
int dlg;
GwiezdnySokol(int numer){
3-6
Jacek Rumi ń ski - J ę zyk JAVA –
Jacek Rumi ń ski - J ę zyk JAVA – Rozdzia ł 3
numerStatku=numer;
}
public int polePowierzchni(){
return (dlg*szer);
public class Flota{
public static void main(String args[]){
GwiezdnyNiszczyciel gw1 = new GwiezdnyNiszczyciel(1);
gw1.wysTrojkata=200;
gw1.dlgPodstawy=500;
gw1.liczbaDzial=6;
gw1.predkoscMax=100;
GwiezdnySokol gs1 = new GwiezdnySokol(1);
gs1.dlg=40;
gs1.szer=15;
gs1.liczbaDzial=3;
gs1.predkoscMax=120;
gw1.informacje();
System.out.println("Pole powierzchni Niszczyciela to: " + gw1.polePowierzchni() +" m(2)");
gs1.informacje();
System.out.println("Pole powierzchni Sokola to: " + gs1.polePowierzchni() +" m(2)");
}
}// koniec public class Flota
W powyższym przykładzie określono 4 klasy wśród których jedna jest abstrakcyjną
(Statek) w ciele której zawarto określone pola, metodę abstrakcyjną
polePowierzchni() oraz metodę zdefiniowaną informacje(). Dwie klasy
GwiezdnyNiszczyciel oraz GwiezdnySokol są klasami pochodnymi (dziedziczą – o
czy później w tym rozdziale) klasy abstrakcyjnej i dokonują definicji metody
abstrakcyjnej dziedziczonej klasy. Ostatnia klasa Flota zawiera wywołanie obiektów
zdefiniowanych klas GwiezdnyNiszczyciel oraz GwiezdnySokol. Warto zauważyć, że
następuje odniesienie do metody informacje() klasy abstrakcyjnej tak, jakby była to
metoda obiektu klasy implementującej klasę abstrakcyjną (czyli GwiezdnyNiszczyciel
albo GwiezdnySokol).
3.3 Interfejsy, specyfikatory dostępu
Klasa abstrakcyjna oprócz metod abstrakcyjnych ma również metody
zdefiniowane. Abstrakcja wprowadzana przez taką klasę jest więc tylko częściowa.
Można sobie oczywiście wyobrazić sytuację gdzie wszystkie metody będą
abstrakcyjne. Wprowadzenie samych metod abstrakcyjnych, a więc tylko ich nazw,
argumentów wywołania i zwracanych typów umożliwia stworzenie uniwersalnego
zbioru funkcji możliwych do wykorzystania w różnych programach bez znajomości
realizacji danej metody. Realizacja danej metody może być wykonywana w różny
sposób w zależności od potrzeb bądź od otoczenia sprzętowo-programowego (np.
odczyt z portu, kodowanie wiadomości, itp.). Zbiór metod abstrakcyjnych jest więc
3-7
Jacek Rumi ń ski - J ę zyk JAVA –
}
} // koniec class GwiezdnySokol
Zgłoś jeśli naruszono regulamin