728 x 90

i

Kurs programowania Java. Część 4: Programowanie obiektowe

Kurs programowania Java. Część 4: Programowanie obiektowe

Ta część kursu Java skupi się na podstawach programowania obiektowego, które jest bardzo ważnym aspektem programowania!

Działanie klas i obiektów pozwala modelować świat rzeczywisty, w którym mamy z nimi do czynienia na codzień. Przede wszystkim należy zrozumieć czy jest klasa. W życiu codziennym często słyszymy o towarze wysokiej klasy, na przykład o telewizorze wysokiej klasy. Jest to telewizor, który w domniemaniu posiada pewien zestaw cech – jest elegancki, ma dużo funkcji, jest drogi. Mówiąc również o dowolnym telewizorze na myśl przychodzą różne urządzenia, które spełniają znane wszystkim warunki: składają się z ekranu (LCD, CRT, OLED), elektroniki, złącza antenowego, tunera, czujki na podczerwień i pilota, jednocześnie możemy wyobrazić sobie całą gamę tego typu urządzeń.

W poniższych zadaniach skupimy się na definiowaniu Figur geometrycznych, które również są ogólnym pojęciem, opisującym całą gamę obiektów do których należą chociażby narysowany na kartce kwadrat, prostokąt narysowany kredą na tablicy, czy okrąg wyświetlany na ekranie komputera. Trzeba tutaj zauważyć że wszystkie te obiekty należą do klasy figur geometrycznych (bo tak możemy je nazwać i posiadają ten odpowiedni zestaw cech), jednak kwadrat na kartce i kwadrat na tablicy to dwie różne instancje tej klasy – dwa różne obiekty!.

1. Tworzenie obiektów

Utworzyć plik Figura.java z definicją klasy zawierającej własności zmiennoprzecinkowe: pole, Obwod oraz wymiar, a także jedną własność nazwa typu String. Klasa powinna zawierać bezargumentowy konstruktor inicjujący pola wartościami 0. Ponadto, należy zaimplementować metodę wypisz(), wypisującą na ekran (standardowe wyjście) nazwę figury i wartość jej pola oraz obwodu. W metodzie main(-) utworzyć obiekt klasy Figura, zainicjować go odpowiednio i wypisać informację na ekran.

2. Rozwiązanie

Należy utworzyć plik \texttt{Figura.java} zawierający następującą treść. Stosując się do konwencji notacyjnej nazwy klas rozpoczynamy z dużej litery.

public class Figura {
public double pole;
public double obwod;
public double wymiar;
public String nazwa;
}

Kolejne pola deklarujemy podając ich modyfikator dostępu, typ i nazwę. Stosując się do konwencji notacyjnej nazwy pól rozpoczynamy z małej litery. W przypadku nazwy wielowyrazowej można skorzystać z tak zwanej notacji wielbłądziej, w której kolejne słowa, nieoddzielone spacjami są oznaczane dużymi pierwszymi literami, na przykład \texttt{nazwaMojejDlugiejZmiennej}.

Konstruktor jest specyficzną metodą nie posiadającą w deklaracji typu zwracanego. Obiekty, które są w rzeczywistości zwracane przez tę metodę to obiekty typu danej klasy. Konstruktor posiada zatem modyfikator dostępu, który w większości wypadków będzie modyfikatorem \texttt{public} oraz nazwę, która jest identyczna jak nazwa klasy.

public Figura(){
pole=0;
obwod=0;
wymiar=0;
nazwa="";
}

W powyższym przypadku zdefiniowany został konstruktor bezargumentowy, który nie posiada żadnych argumentów. W konstruktorze tym zostały zainicjalizowane wszystkie pola zdefiniowane w klasie Figura.

Kolejnym elementem jest zdefiniowanie metody wypisz(), wypisującej na ekran (standardowe wyjście) nazwę figury i wartość jej pola oraz obwodu. Ma to być również metoda bezargumentowa, nie zwracająca żadnej wartości (a więc należy użyć typu \texttt{void})

public void wypisz(){
System.out.printf("Figura: %s \n Pole: %f, Obwód %f",nazwa,pole,obwod);
}

Pełny listing rozwiązania przedstawiony jest poniżej

public class Figura {
public double pole;
public double obwod;
public double wymiar;
public String nazwa;

public Figura(){
pole=0;
obwod=0;
wymiar=0;
nazwa="";
}

public void wypisz(){
System.out.printf("Figura: %s \nPole: %f, Obwód %f\n",nazwa,pole,obwod);
}
}

Aby przetestować działanie klasy, należy stworzyć nową klasę zawierającą funkcję \texttt{main}, w której zainicjalizowany zostanie obiekt klasy Figura i wykonane zostaną jego metody.

public class Zdanie1 {

public static void main(String[] args) {
Figura f = new Figura();
f.nazwa="Koło";
f.wymiar=1;
f.pole=3.14;
f.obwod=6.28;
f.wypisz();
}
}

W trzeciej linii kodu jest zadeklarowana nowa zmienna typu \texttt{Figura}. W tej samej linii jest stworzony nowy obiekt typu \texttt{Figura} i jego referencja jest przypisana do tej zmiennej.

W liniach 4-7 modyfikowana jest wartość pól obiektu. Po nazwie obiektu następuje kropka a po kropce nazwa konkretnego pola. Przypisanie wartości w ten sposób jest możliwe, ponieważ pola mają modyfikatory \texttt{public }

 

3. Metody klasy

 

Dopisać do klasy Figura metodę ustaw(-,-) z dwoma parametrami, ustawiającymi wartość własności pola i obwodu. Dopisać także metodę toString(), która będzie zwracała obiekt klasy String zawierający nazwę figury wraz z wartościami jej pola i obwodu, w przypadku gdy są one większe od zera. W metodzie main(-) utwórz dwa obiekty klasy Figura dla kwadratu i koła. Przetestuj metody tworząc obiekt, modyfikując jego własności i wypisując go na ekran bez jawnego wywołania metody toString().

 

3.1. Rozwiązanie

Wyobraźmy sobie sytuację, że korzystamy z telewizora (nie ważne jakiego, ważne że jest to obiekt typu telewizor). Mamy przy tym dostęp do wszystkich jego części wewnętrznych (czyli składowych obiekti), które możemy dowolnie modyfikować. I tak możemy sięgnąć do środka obudowy i ręcznie regulować potencjometr i tuner. O ile takie działanie może być zaakceptowane, to możemy równie dobrze wyjąć procesor lub zrobić spięcie na płycie głównej – a tego raczej producent telewizora by nie chciał. Dlatego producenci telewizorów stosują sprytny zabieg, który polega na włożeniu telewizora w plastikową obudowę i pozostawienie dostępu tylko do niezbędnych do sterowania nim funkcji. I na przykład za wybór programu odpowiedzialne są dwa przyciski, które dopiero wewnętrznie modyfikują ustawienie tunera.

 

Podobny zabieg nazywamy w programowaniu enkapsulacją pozwala na takie zdefiniowanie elementów wewnętrznych klasy, żeby nie były one dostępne bezpośrednio, oraz przygotowanie metod, które pozwolą na ich pośrednią modyfikację.

 

W przypadku Figury ograniczamy dostęp do pól \texttt{pole} i \texttt{obwod}.

private double pole;

private double obwod;

Od tej pory nie jest możliwe użycie konstrukcji \texttt{nazwaObiektu.pole}. Następnie dodajemy taką metodę \textsl{ustaw}, która wewnętrznie zmodyfikuje te pola.

public void ustaw(double pole, double obwod){

this.pole=pole;

this.obwod=obwod;

}

Ponieważ w metodzie tej jej argumenty nazywają się tak jak pola klasy Figura, pola zostały przesłonięte. Wówczas instrukcja \texttt{pole=2.3;} spowodowałaby jedynie zmodyfikowanie wartości argumentu przekazanego do metody. W szczególności nie miałaby sensu instrukcja \texttt{pole=pole;}. Dlatego należy skorzystać tutaj z referencji \texttt{this}, wskazującej na obiekt, na którym wywołaliśmy metodę. Oznacza to, że jeśli metodę ustaw wywołamy na obiekcie \texttt{f}, to wewnątrz tej metody \texttt{this} będzie się również odnosił do obiektu \texttt{f}.

Do klasy figura dodajemy również metodę \texttt{toString} zawierającą prostą instrukcję warunkową.

W metodzie tej trzeba stworzyć tymczasowy obiekt typu String (czyli ciąg znaków), do którego będą stopniowo doklejane kolejne informacje opisującej obiekt. Konkatenacji Stringów dokonuje się za pomocą operatora \texttt{+}.

public String toString(){

String opis ="Figura: " + nazwa + "\n";

if (pole>0) opis += "Pole: "+ pole + "\n";

if (obwod>0) opis += "Obwod: "+ obwod + "\n";

return opis;

}

Metoda \texttt{toString} ma jeszcze jedną ważną właściwość. Jeśli wykorzystamy obiekt w jakimś kontekście, który będzie wymagał ciągu znaków, a nie obiektu, zostanie automatycznie wywołana metoda \texttt{toString}. Widać to na poniższym przykładzie zmodyfikowanej funkcji \texttt{main}.

public class Zdanie2 {

public static void main(String[] args) {

Figura kolo = new Figura();

kolo.nazwa="Koło";

kolo.wymiar=1;

kolo.ustaw(3.14,6.28);



Figura kwadrat = new Figura();

kwadrat.nazwa="Kwadrat";

kwadrat.wymiar=1;

kwadrat.ustaw(1,4);



System.out.println(kolo);

System.out.println(kwadrat);

}

}

 

 

4. Przeciążanie metod

 

Dopisać przeciążoną metodę ustaw(-) tylko z jednym parametrem przypisywanym do własności wymiaru danej figury. Następnie zaimplementować kolejną przeciążoną metodę ustaw(-) dla obiektu typu String zmieniającą własność nazwy. Zastanów się, dlaczego nie możesz zrobić analogicznych metod dla własności pola i obwodu. Zaproponuj własne rozwiązanie dla tych własności z wykorzystaniem kolejnej, dwuargumentowej i przeciążonej metody ustaw(-,-), której drugim argumentem będzie zmienna boolowska.

3.1. Rozwiązanie

Jeżeli zajdzie taka potrzeba można tworzyć wiele metod o tej samej nazwie, ale różnych parametrach. Na przykład w przypadku telewizora metoda \texttt{ciszej(int)} może modyfikować głośność o jeden stopień w dół, a metoda \texttt{ciszej()} może całkowicie wyłączać głos.

W klasie figura dodane zostaną teraz kolejne metoda ustaw(), które pozwoli na modyfikację nazwy i wymiaru.

public void ustaw(double wymiar){

this.wymiar=wymiar;

}



public void ustaw(String nazwa){

this.nazwa=nazwa;

}

Jest to możliwe, ponieważ w każdym z trzech przypadków mamy inny zestaw argumentów (który pozwala kompilatorowi określić, o którą dokładnie metodą nam chodzi, wywołując ją). W tych przypadkach są to zestawy \texttt{ustaw(double,double)}, \texttt{ustaw(double)} oraz \texttt{ustaw(String)}. Nie jest przy tym istotne, czy dane metody różnią się typem zwracanej wartości. Dlatego nie jest możliwe dodanie kolejnej metody ustawiającej oddzielnie własność pola.

Reszta zadnia pozostaje do samodzielnego przemyślenia.

 

 

5. Składowe statyczne

Do klasy Figura dopisać całkowitoliczbowe pole statyczne \texttt{licznik} zainicjowane na 0. Następnie utworzyć metodę modyfikującą oraz wypisującą wartość tego pola, odpowiednio o nazwach \texttt{ustawLicznik(-)} oraz \texttt{wypiszLicznik()}. Stworzyć dwa obiekty typu Figura i sprawdzić czy zmiana wartości Licznik w jednym zaowocuje zmianą wartości Licznik w drugim. Zmodyfikuj tak klasę aby każdy jej obiekt zawierał własny unikalny identyfikator liczbowy, zawarty w tekście zwracanym przez metodę \texttt{toString()}.

3.1. Rozwiązanie

Składowe statyczne są szczególnego typu składowymi. Nie są one związane z obiektem, a z klasą obiektów. Nie są więc unikalne dla konkretnych obiektów. Oznacza to, że pamięć, w której znajduje się wartość składowej statycznej jest inicjalizowana tylko raz i jest wspólna dla wszystkich obiektów danej klasy. Jednym z najprostszych zastosowań jest możliwość zliczania zainicjalizowanyc obiektów danej klasy. W tym przypadku należy stworzyć w danej klasie pole statyczn a następnie podczas inicjalizacji obiektów modyfikować to pole. Ideę prezentuje poniższy przykład, w którym w konstruktorze pobierany jest z pola statycznego \texttt{licznik} numer aktualnie tworzonego obiektu.

 

Na początek należy zaimplementować obsługę licznika w postaci pola statycznego i dwóch metod.

private static int licznik;
public int pobierzLicznik(){return licznik;}
public void ustawLicznik(int l){licznik=l;}

 

Kolejny krok, to dodanie do pól klasy \texttt{Figura} pola \texttt{int id}

private int id;

oraz modyfikacja konstruktora

public Figura(){
pole=0;
obwod=0;
wymiar=0;
nazwa="";
id=pobierzLicznik();
ustawLicznik(id+1);
}

i metody \texttt{toString()}.

public String toString(){
String opis ="Figura: " + nazwa + " nr "+ id +"\n";
if (pole>0) opis += "Pole: "+ pole + "\n";
if (obwod>0) opis += "Obwod: "+ obwod + "\n";
return opis;
}

Poniższy kod zaowocuje stworzeniem dwóch figur o identyfikatorach 0 i 1

public class Zdanie4 {
public static void main(String[] args) {
Figura kolo = new Figura();
kolo.ustaw("Koło");
Figura kwadrat = new Figura();
kolo.ustaw("Koło");
System.out.println(kolo);
System.out.println(kwadrat);
}
}

 

Leave a Comment

Your email address will not be published. Required fields are marked with *

Cancel reply

Inne artykuły