Wszystkie pisane dotychczas na kursie programy mieściły się w jednej klasie. W przypadku prostych programów może to wystarczyć, ale w bardziej złożonych tworzenie nowych klas może być bardzo pomocne a nawet niezbędne. W naszych programach korzystaliśmy też, mniej lub bardziej świadomie, z innych klas dostępnych w bibliotekach Javy. W większości przypadków tworzyliśmy na ich podstawie obiekty, w innych wywoływaliśmy umieszczone w nich metody bezpośrednio.
Terminy „klasa” i „obiekt” były zatem już używane na kursie, ale nie do końca zostało wyjaśnione czym są i jakie są między nimi relacje. Teraz przyszedł na to czas. Potem zajmiemy się tworzeniem własnych klas i obiektów, co pozwoli pisać programy o znacznie wyższym stopniu złożoności niż dotąd.
Czym są klasy i obiekty
Java należy do obiektowych języków programowania co wskazuje na kluczową rolę jaką odgrywają w niej obiekty. Zrozumienie czym są obiekty i czym są klasy oraz jakie relacje wiążą jedne z drugimi jest kluczowe dla zrozumienia i efektywnego wykorzystania Javy. Zacznijmy od obiektów.
Obiekty w języku programowania mają wiele cech wspólnych z obiektami znanymi w realnym świecie. Jako przykład niech nam posłuży telefon komórkowy. Każde urządzenie tego typu posiada pewne cechy i przechowuje pewne informacje. Cechy to np. marka, model, kolor, rodzaj ekranu, informacje to numer telefonu (o ile posiada kartę SIM), kontakty, przechowywane zdjęcia itd. Przy opisie telefonu komórkowego zarówno cechy ja i przechowywane informacje można określić jako dane telefonu. Poza tym używając telefonu można wykonywać pewne czynności, na przykład wykonać połączenie, odebrać je, wysłać SMS, odebrać SMS, wyszukać kontakt, dodać kontakt itp.
Jeśli chcielibyśmy stworzyć w programie „wirtualny telefon”, musielibyśmy stworzyć coś, co z jednej strony przechowywałoby definiowane przez nas dane określonego typu, a z drugiej pozwalałoby na wykonanie pewnych czynności. To „coś” to właśnie obiekt. Obiekt jest więc pewną całością pozwalającą przechowywać dane i przeprowadzać operacje, także na przechowywanych przez siebie danych.
Przypomnij sobie na przykład w jaki sposób sprawdzaliśmy czy dwa ciągi znaków do siebie pasują:
String haslo = "przebiegle#HASLO!237";
String podajHaslo = "admin123"
if (haslo.equals(podajHaslo))
System.out.println("Zapraszam do skarbca");
else
System.out.println("Uwaga Intruz!");
W powyższym przykładzie najpierw zostały stworzone dwa obiekty typu String
, każdy z nich przechowywał inny ciąg znaków. Później jeden z nich wykonał czynność sprawdzenia, czy przechowywany przez niego ciąg znaków jest taki sam jak ciąg znaków przechowywany przez drugi obiekt i w zależności od wyniku testu zwrócił wartość true
lub false
.
Wróćmy teraz do telefonów komórkowych. Projektując wirtualną „komórkę” należy umożliwić przechowywanie przez niej danych a także wykonywanie wymaganych operacji. Nietrudno się domyślić, że do pierwszego zadania przydadzą się zmienne oraz bardziej złożone struktury, jak np. tablice, a do drugiego będą służyć metody. Jedno i drugie należy umieścić w odpowiedniej klasie. Klasa jest czymś w rodzaju projektu na podstawie którego tworzony jest obiekt. Elementy klasy, które służą do przechowywania danych (zmienne, tablice itd.) nazywamy polami.
Mówimy, że tworzony obiekt jest więc „typu” klasy na podstawie której jest tworzony, na przykład jeśli napiszemy klasę Telefon
i na podstawie jej stworzymy obiekt, to będzie on obiektem typu Telefon
. Analogicznie, kiedy tworzyliśmy obiekt typu String
, używaliśmy do tego klasy String
dostępnych gdzieś w bibliotekach Javy.
Na podstawie jednej klasy można stworzyć wiele obiektów, będą one mieć takie same pola i metody, ale każdy z nich będzie odrębną jednostką i może różnić się od pozostałych przechowywanymi danymi. Zauważ, że z podobną sytuacją mamy do czynienia w realnym świecie. Mówiąc „telefon jest użytecznym urządzeniem” mamy na myśli pewien abstrakcyjny obraz urządzenia, który posiada pewne cechy i można nim wykonywać określone czynności. Natomiast konkretny przedmiot, którego używamy do dzwonienia jest rzeczywistym „ucieleśnieniem” idei telefonu i posiada już konkretną markę, kolor, numer fabryczny itp.
Tworzymy wirtualne telefony
Mając już pewne pojęcie czym są klasy i obiekty stwórzmy kilka wirtualnych telefonów.
Jeśli w naszym programie będziemy używać wielu klas, muszą się one „widzieć”. Niektóre klasy Javy, które zainstalowaliśmy wraz z Java Development Kit, są widoczne w pisanych przez nas klasach automatycznie, inne należy dołączyć za pomocą instrukcji import
(np. klasę Scanner
). W przypadku klas umieszczonych w bibliotekach z poza dystrybucji Javy sprawa jest (nieco) bardziej skomplikowana i jeszcze do niej wrócimy. Na razie skupmy się na klasach, które sami będziemy pisać. Najprościej będzie jeśli zostaną one umieszczone w jednym pakiecie, wtedy będą mogły korzystać z siebie wzajemnie bez dodatkowych czynności.
Utwórz, jak zwykle, w NetBeans nowy projekt i pakiet, nazwij go telefony
. W pakiecie powinny się znaleźć dwie klasy: zawierająca metodę main
klasa Telefony
oraz druga klasa o nazwie Telefon
, której będziemy używać do tworzenia obiektów.
Nową klasę możemy dodać do pakietu klikając prawym klawiszem myszy w ikonę oznaczającą pakiet, następnie wybieramy New->Java Class i pokazuje się menu w którym uzupełniamy nazwę klasy i klikamy na „Finish”.
Najpierw zajmijmy się klasą Telefon
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
package telefony; public class Telefon { // pola String marka; String kolor; int numer; // metody public void wyslijSMS(int nr, String tekst) { System.out.println("\n* Wysyłam SMS na nr: " + nr); System.out.println("* O treści: " + tekst); } public String odbierzSMS() { return "\n* SMS: Witaj telefonie!"; } public void zadzwon(Telefon tel) { System.out.println("\n* Ja, telefon marki " + marka + " o numerze " + numer); System.out.println("* Dzwonię na nr: " + tel.numer); // Wywołanie metody na obiekcie tel tel.odbierzPolaczenie(numer); } public void odbierzPolaczenie(int nr) { System.out.println("\n# Ja, telefon marki "+ marka + " o numerze " + numer); System.out.println("# Odbieram połączenie od nr: " + nr); } } |
Klasa jest bardzo prosta, zawiera trzy pola oraz cztery metody. Metody symulują wysyłanie i odbieranie SMS-ów oraz wykonywanie i odbieranie połączeń. Działanie dwóch pierwszych metod nie wymaga specjalnych wyjaśnień, do pozostałych wrócę za chwilę, kiedy poznamy sposób ich wykorzystania. Zauważ, że nie ma tu metody main
.
Klasa Telefony
wygląda następująco:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
package telefony; public class Telefony { public static void main(String[] args) { // Tworzenie dwóch obiektów typu Telefon Telefon tel1 = new Telefon(); Telefon tel2 = new Telefon(); // Ustawianie wartości pól tel1.marka = "LG"; tel1.kolor = "niebieski"; tel1.numer = 123456789; tel2.marka = "Apple"; tel2.kolor = "biały"; tel2.numer = 987654321; // Pobranie zawartości pól System.out.println("\n> Jestem produktem firmy " + tel1.marka + " mam kolor " + tel1.kolor + " i numer " + tel1.numer); System.out.println("\n> A ja jestem produktem firmy " + tel2.marka + " mam kolor " + tel2.kolor + " i numer " + tel2.numer); // Wywołanie metod tel1.wyslijSMS(222333444, "Pozdrowienia z kursu Javy"); System.out.println(tel1.odbierzSMS()); // Przekazanie do metody obiektu tel2 jako argumentu tel1.zadzwon(tel2); } } |
Początek to utworzenie dwóch obiektów typu „Telefon”: tel1
oraz tel2
. Jak widać, wygląda to tak:
NazwaKlasy nazwaObiektu = new nazwaKlasy();
Następnie w obu zostają przypisane wartości polom wg. schematu:
nazwaObiektu.pole = wartość;
Pobranie wartości pola jest równie banalne:
nazwaObiektu.pole;
Zauważ, że każdy z obiektów ma inny zestaw wartości pól.
Dalsza część kodu to wywoływanie metod, które wygląda tak:
nazwaObiektu.metoda(argumenty);
Jeśli na danym obiekcie wywołujemy metodę, która korzysta z pól obiektu to oczywiście ich wartości odpowiadają tym przypisanym do tego konkretnego obiektu.
W wyniku uruchomienia kodu powinniśmy otrzymać taki rezultat:
Jestem produktem firmy LG mam kolor niebieski i numer 123456789
A ja jestem produktem firmy Apple mam kolor biały i numer 987654321
- Wysyłam SMS na nr: 222333444
- O treści: Pozdrowienia z kursu Javy
-
SMS: Witaj telefonie!
-
Ja, telefon marki LG o numerze 123456789
- Dzwonię na nr: 987654321
Ja, telefon marki Apple o numerze 987654321
Odbieram połączenie od nr: 123456789
Warto szczególnie przyjrzeć się wywołaniu metody zadzwon
. Przyjmuje ona jako argument obiekt typu Telefon
. Okazuje się więc, że poszczególne obiekty w naszym programie mogą się komunikować i wykorzystywać się wzajemnie. W zasadzie nie powinno to być niczym zaskakującym, przecież robiliśmy to już wcześniej, na przykład porównując dwa łańcuchy znaków. Teraz jednak możemy przyjrzeć się dokładniej jak ten proces przebiega. Metoda zadzwoń
zostaje wywołana na pewnym konkretnym obiekcie, ma ona więc dostęp do pól tego obiektu, ale ponieważ zostaje jej przekazany inny obiekt, ma również dostęp do jego pól oraz jego metod. Dzięki temu metoda zadzwon
w obiekcie tel1
może uzyskać dostęp do danych w przekazanym jej obiekcie oraz wywołać na nim metody.
Przekazywanie obiektów jako argumentów
Uwaga! Jeśli przekazujesz obiekt jako argument, to pamiętaj o tym, że mamy to do czynienia z typem odnośnikowym. Co to w praktyce oznacza? Przypomnij sobie pierwszą lekcję poświęconą zmiennym. Napisałem tam, że na przykład int
, double
, boolean
czy char
są typami zmiennych (danych) typu podstawowego (prymitywnego), natomiast w przypadku tablic i innych obiektów mówimy o typach odnośnikowych (referencyjnych). To rozróżnienie okazało się istotne kiedy chcieliśmy w jednej z lekcji poświęconej tablicom, skopiować tablicę. Kiedy zastosowaliśmy sposób tablica2 = tablica1
otrzymaliśmy dwa odnośniki wskazujące na tą samą tablicę, wszelkie modyfikacje w tablicy1
wpływały na tablicę2
i odwrotnie. Podobny efekt uzyskamy przekazując obiekt jako argument do metody. Metoda nie będzie operowała na kopii obiektu ale na odnośniku obiektu, który przekazaliśmy jako argument. Jeśli więc dokonamy na nim jakichkolwiek modyfikacji, będą one widoczne także w kodzie metody z której przekazaliśmy obiekt. Najlepiej pokazać to na przykładzie.
Zmodyfikuj nieco klasę Telefon
:
- w miejscu gdzie znajdują się definicje pól dodaj linijkę:
int liczbaPolaczen = 0;
. W ten sposób zostało dodane nowe pole, które będzie zliczać liczbę odebranych połączeń. Na początku zmienna ma wartość0
. - na końcu metody
odbierzPolaczenie
dodaj komendę:liczbaPolaczen++;
. Przy odebranym połączeniu wartość pola będzie się zwiększała o 1.
Teraz zmień końcówkę metody main
w klasie Telefony
na taką:
System.out.println("\n liczba połączeń: "+ tel2.liczbaPolaczen);
tel1.zadzwon(tel2);
System.out.println("\n liczba połączeń: "+ tel2.liczbaPolaczen);
Po uruchomieniu programy, końcowa część komunikatów będzie wyglądała następująco:
...
liczba połączeń: 0
- Ja, telefon marki LG o numerze 123456789
- Dzwonię na nr: 987654321
Ja, telefon marki Apple o numerze 987654321
Odbieram połączenie od nr: 123456789
liczba połączeń: 1
Co się stało?
Obiekt tel2
, a właściwie odnośnik do niego, został przekazany do metody zadzwon
w obiekcie tel1
. Tam z kolei została na przekazanym obiekcie wywołana metoda odbierzPolaczenie
, która m. in. zmieniła wartość pola liczbaPolaczen
. Ponieważ cały czas operacje były wykonywane z wykorzystaniem odnośnika do obiektu tel1
a nie na jego kopii, rezultat jest widoczny także w metodzie main
klasy Telefony
.
Zadanie
Stwórz program w którym będzie utworzonych kilka obiektów typu Gatunek
Klasa Gatunek
powinna zawierać:
- pola przechowujące:
- nazwę rodzaju
- nazwę gatunkową
- liczbę chromosomów 2n
- podstawową liczbę chromosomów x
- opis
- metody:
- podającą pełną nazwę (Rodzaj + gatunek)
- podającą haploidalną liczbę chromosomów n
- wypisującą wszystkie dane
- klonującą obiekt – metoda powinna zwracać odnośnik do nowego obiektu typu
Gatunek
o wartościach pól takich samych jak w obiekcie, w którym została wywołana.
W programie powinny być użyte wszystkie metody.
W klasie Telefony w linii 29 mamy:
tel1.odbierzSMS();
jednak w wyniku wywołania tej metody nie ma efektu na ekranie gdyż zwraca ona String a nie printuje go.
Zeby był widoczny efekt należałoby wiec to napisać tak jak jest w linii 31:
System.out.println(tel1.odbierzSMS());
Słusznie, w zasadzie ta linia jest zbędna (usunąłem), prawidłowa linia kodu jest poniżej (obecnie 30).
Dziękuję za zwrócenie uwagi.
Pozdrawiam serdecznie
Grzegorz
Mam prośbę o wyjaśnienie tej linii:
NazwaKlasy nazwaObiektu = new nazwaKlasy();
Przyznam że jest to dla mnie trochę niejasne.
Dlaczego po słowie „new” wywołujemy metodę nazwaKlasy() skoro ta klasa nie zawiera metody o takiej nazwie??
Odpowiedź znajduje się na samym początku kolejnej lekcji :-)
http://ggoralski.pl/?p=1782