Cyfroteka.pl

klikaj i czytaj online

Cyfro
Czytomierz
00401 008831 10444125 na godz. na dobę w sumie
Java. Programowanie obiektowe - książka
Java. Programowanie obiektowe - książka
Autor: Liczba stron: 264
Wydawca: Helion Język publikacji: polski
ISBN: 83-246-0290-9 Data wydania:
Lektor:
Kategoria: ebooki >> komputery i informatyka >> programowanie >> java - programowanie
Porównaj ceny (książka, ebook, audiobook).

Doskonałe wprowadzenie w świat obiektowości

Programowanie obiektowe to technologia, która zdobyła już bardzo mocną pozycję wśród twórców oprogramowania. Nadal jednak wielu programistów, którzy zdobywali doświadczenie, używając języków proceduralnych, ma problemy z jej zrozumieniem i wszechstronnym stosowaniem. Wiele języków programowania określanych mianem 'obiektowe' wywodzi się z języków proceduralnych, co ogranicza możliwości wykorzystywania wszystkich zalet obiektowości. Ograniczeń tych pozbawiona jest Java -- stworzony od podstaw, nowoczesny, bezpieczny, niezależny od typu komputera i systemu operacyjnego, w pełni obiektowy język programowania.

Książka 'Java. Programowanie obiektowe' opisuje wszystkie aspekty programowania obiektowego w Javie. Początkujący użytkownicy tego języka znajdą w niej wyjaśnienia nawet najbardziej skomplikowanych mechanizmów obiektowości, a ci, którzy posiadają już pewne doświadczenie, mogą wykorzystać ją w charakterze podręcznego kompendium wiedzy. Można znaleźć w niej omówienie zarówno podstawowych zagadnień, jak i zaawansowanych technik obsługi błędów, programowania wielowątkowego i sterowanego zdarzeniami. W książce przedstawiono również metody tworzenia wydajnie działających programów, które do uruchomienia nie wymagają maszyn o potężnej mocy obliczeniowej.

Poznaj możliwości technologii obiektowej w praktyce.

Znajdź podobne książki Ostatnio czytane w tej kategorii

Darmowy fragment publikacji:

IDZ DO IDZ DO PRZYK£ADOWY ROZDZIA£ PRZYK£ADOWY ROZDZIA£ SPIS TREŒCI SPIS TREŒCI KATALOG KSI¥¯EK KATALOG KSI¥¯EK KATALOG ONLINE KATALOG ONLINE ZAMÓW DRUKOWANY KATALOG ZAMÓW DRUKOWANY KATALOG TWÓJ KOSZYK TWÓJ KOSZYK DODAJ DO KOSZYKA DODAJ DO KOSZYKA CENNIK I INFORMACJE CENNIK I INFORMACJE ZAMÓW INFORMACJE ZAMÓW INFORMACJE O NOWOŒCIACH O NOWOŒCIACH ZAMÓW CENNIK ZAMÓW CENNIK CZYTELNIA CZYTELNIA FRAGMENTY KSI¥¯EK ONLINE FRAGMENTY KSI¥¯EK ONLINE Wydawnictwo Helion ul. Chopina 6 44-100 Gliwice tel. (32)230-98-63 e-mail: helion@helion.pl Java. Programowanie obiektowe Autor: Marek Wierzbicki ISBN: 83-246-0290-9 Format: B5, stron: 264 Doskona³e wprowadzenie w œwiat obiektowoœci (cid:127) Podstawowe zasady programowania obiektowego (cid:129) Programowanie sterowane zdarzeniami (cid:129) Obs³uga wyj¹tków i wielow¹tkowoœci Programowanie obiektowe to technologia, która zdoby³a ju¿ bardzo mocn¹ pozycjê wœród twórców oprogramowania. Nadal jednak wielu programistów, którzy zdobywali doœwiadczenie, u¿ywaj¹c jêzyków proceduralnych, ma problemy z jej zrozumieniem i wszechstronnym stosowaniem. Wiele jêzyków programowania okreœlanych mianem „obiektowe” wywodzi siê z jêzyków proceduralnych, co ogranicza mo¿liwoœci wykorzystywania wszystkich zalet obiektowoœci. Ograniczeñ tych pozbawiona jest Java — stworzony od podstaw, nowoczesny, bezpieczny, niezale¿ny od typu komputera i systemu operacyjnego, w pe³ni obiektowy jêzyk programowania. Ksi¹¿ka „Java. Programowanie obiektowe” opisuje wszystkie aspekty programowania obiektowego w Javie. Pocz¹tkuj¹cy u¿ytkownicy tego jêzyka znajd¹ w niej wyjaœnienia nawet najbardziej skomplikowanych mechanizmów obiektowoœci, a ci, którzy posiadaj¹ ju¿ pewne doœwiadczenie, mog¹ wykorzystaæ j¹ w charakterze podrêcznego kompendium wiedzy. Mo¿na znaleŸæ w niej omówienie zarówno podstawowych zagadnieñ, jak i zaawansowanych technik obs³ugi b³êdów, programowania wielow¹tkowego i sterowanego zdarzeniami. W ksi¹¿ce przedstawiono równie¿ metody tworzenia wydajnie dzia³aj¹cych programów, które do uruchomienia nie wymagaj¹ maszyn o potê¿nej mocy obliczeniowej. (cid:129) Cechy programowania obiektowego (cid:129) Obiektowoœæ w Javie (cid:129) Tworzenie i stosowanie klas i obiektów (cid:129) Budowanie pakietów (cid:129) Tworzenie apletów (cid:129) Komunikacja apletów ze skryptami Java Script (cid:129) Obiekty nas³uchuj¹ce i obs³uga zdarzeñ (cid:129) Przechwytywanie wyj¹tków (cid:129) Synchronizacja w¹tków Poznaj mo¿liwoœci technologii obiektowej w praktyce Od autora ......................................................................................... 7 Rozdział 1. Wprowadzenie ................................................................................ 11 1.1. Ogólne cechy programowania obiektowego ...........................................................12 1.1.1. Hermetyzacja ................................................................................................13 1.1.2. Dziedziczenie cech ........................................................................................14 1.1.3. Dziedziczenie metod i polimorfizm ..............................................................16 1.1.4. Nowa jakość działania ..................................................................................17 1.2. Cechy szczególne obiektowości Javy ......................................................................18 1.2.1. Obiekty w Javie .............................................................................................21 1.2.2. Deklaracje dostępności .................................................................................22 1.2.3. Klasy wewnętrzne i zewnętrzne ....................................................................22 1.2.4. Klasy abstrakcyjne ........................................................................................23 1.2.5. Interfejsy .......................................................................................................24 1.2.6. Implementacje ...............................................................................................25 1.2.7. Klasy finalne .................................................................................................25 1.2.8. Metody i klasy statyczne ...............................................................................26 1.2.9. Klasy anonimowe ..........................................................................................27 1.2.10. Obiekty refleksyjne .......................................................................................28 1.2.11. Zdalne wykonywanie metod .........................................................................28 1.2.12. Pakiety ..........................................................................................................29 1.2.13. Zarządzanie pamięcią ...................................................................................30 1.2.14. Konwersja typów ..........................................................................................30 1.3. Podsumowanie .........................................................................................................31 Rozdział 2. Klasy i obiekty w Javie .................................................................... 33 2.1. Klasy ........................................................................................................................33 2.1.1. Tworzenie klas ..............................................................................................33 2.1.2. Pola ................................................................................................................35 2.1.3. Metody ..........................................................................................................35 2.1.4. Hermetyzacja i modyfikator private .............................................................36 2.1.5. Przeciążanie metod .......................................................................................37 2.1.6. Słowo kluczowe this .....................................................................................38 2.1.7. Konstruktor ...................................................................................................39 2.1.8. Przeciążanie konstruktorów ..........................................................................40 2.1.9. Dziedziczenie ................................................................................................43 2.1.10. Inicjator klasy i obiektu ................................................................................44 2.1.11. Kolejność inicjacji klas .................................................................................47 2.1.12. Destruktor .....................................................................................................50 4 Java. Programowanie obiektowe 2.1.13. Przykrywanie metod .....................................................................................51 2.1.14. Odwołanie do klas nadrzędnych ...................................................................52 2.1.15. Odwołanie do pól klas nadrzędnych .............................................................53 2.1.16. Klasy abstrakcyjne ........................................................................................54 2.2. Obiekty ....................................................................................................................55 2.2.1. Rozważania o adresie ....................................................................................55 2.2.2. Jawne użycie obiektów .................................................................................56 2.2.3. Kopiowanie obiektów ...................................................................................58 2.2.4. Niejawne używanie obiektów .......................................................................59 2.2.5. Typ zmiennej i obiektu. Operator instanceof ................................................60 2.2.6. Efekty polimorfizmu .....................................................................................62 2.3. Klasy wewnętrzne i lokalne ....................................................................................63 2.3.1. Dostęp do zmiennych klasy zawierającej .....................................................65 2.3.2. Polimorfizm i zmienne klasy zawierającej ...................................................66 2.3.3. Zmienne lokalne w klasie lokalnej ................................................................68 2.3.4. this w klasach wewnętrznych ........................................................................69 2.3.5. Korzystanie z klas wewnętrznych .................................................................71 2.4. Interfejsy ..................................................................................................................73 2.4.1. Definicja interfejsu ........................................................................................74 2.4.2. Implementacje ...............................................................................................74 2.4.3. Zastosowanie interfejsów ..............................................................................76 2.4.4. Stałe symboliczne .........................................................................................77 2.4.5. Trochę kodu w interfejsie .............................................................................79 2.4.6. Dziedziczenie interfejsów .............................................................................81 2.4.7. Egzemplarz interfejsu ...................................................................................83 2.5. Klasy anonimowe ....................................................................................................84 2.5.1. Klasyczne użycie klasy anonimowej ............................................................85 2.5.2. Jawna klasa anonimowa ................................................................................87 2.5.3. Konstruktor klasy anonimowej .....................................................................88 2.6. Obiekty refleksyjne .................................................................................................89 2.6.1. Obiekt tworzony refleksyjnie ........................................................................89 2.6.2. Ogólne rozpoznawanie klasy ........................................................................91 2.6.3. Przykład użycia refleksji ...............................................................................92 2.6.4. Związek refleksji z obiektowością ................................................................94 2.7. Metody .....................................................................................................................95 2.7.1. Zwracanie wartości przez metodę .................................................................95 2.7.2. Przekazywanie parametrów przez wartość ...................................................96 2.7.3. Zmiana wartości parametru ...........................................................................97 2.7.4. Metody ze zmienną liczbą parametrów ........................................................99 2.7.5. Zakres nazw zmiennych ..............................................................................100 2.8. Pakiety ...................................................................................................................101 2.8.1. Tworzenie pakietów ....................................................................................101 2.8.2. Używanie pakietów .....................................................................................103 2.8.3. Lista pakietów .............................................................................................104 2.9. Modyfikatory .........................................................................................................105 2.9.1. Modyfikatory dostępu .................................................................................106 2.9.2. Pokrywanie modyfikatorów dostępu ..........................................................107 2.9.3. Metody i pola statyczne ..............................................................................109 2.9.4. Pola finalne .................................................................................................111 2.9.5. Metody i klasy finalne .................................................................................112 2.9.6. Pola podlegające zmianie ............................................................................113 2.9.7. Metody synchronizowane ...........................................................................113 2.9.8. Pola ulotne ...................................................................................................114 2.9.9. Metody rodzime ..........................................................................................114 2.10. Podsumowanie ......................................................................................................115 Spis treści 5 Rozdział 3. Aplet jako obiekt na stronie HTML ................................................ 117 3.1. Program na stronie internetowej ............................................................................118 3.1.1. Aplet jako program .....................................................................................118 3.1.2. Osadzenie obiektu na stronie ......................................................................119 3.1.3. Wersja Javy w przeglądarce ........................................................................122 3.2. Predefiniowane składowe apletu ...........................................................................123 3.2.1. Inicjacja apletu ............................................................................................124 3.2.2. Wstrzymanie i wznowienie pracy ...............................................................125 3.2.3. Zamykanie okna przeglądarki .....................................................................125 3.2.4. Wygląd i jego odświeżanie .........................................................................126 3.3. Komunikacja ze światem zewnętrznym ................................................................130 3.3.1. Wyprowadzanie informacji tekstowych ......................................................130 3.3.2. Okienko dialogowe .....................................................................................132 3.3.3. Pobieranie parametrów z pliku HTML .......................................................135 3.3.4. Pobieranie i odtwarzanie plików z serwera .................................................136 3.3.5. Komunikacja między apletami ....................................................................137 3.3.6. Pobieranie informacji z linii adresu ............................................................140 3.4. Aplet a JavaScript ..................................................................................................142 3.4.1. Wywołanie funkcji JavaScript z apletu .......................................................143 3.4.2. Bezpośrednie użycie JavaScriptu ................................................................145 3.4.3. Obsługa rejestru przeglądarki .....................................................................146 3.4.4. Wywołanie Javy z JavaScriptu ...................................................................148 3.5. Aplet jako samodzielna aplikacja ..........................................................................150 3.6. Ograniczenia w apletach .......................................................................................151 3.7. Podsumowanie .......................................................................................................152 Rozdział 4. Programowanie sterowane zdarzeniami .......................................... 153 4.1. Zarys nowej idei ....................................................................................................154 4.2. Klasyczna obsługa zdarzeń ...................................................................................155 4.2.1. Usuwanie klas anonimowych ......................................................................158 4.2.2. Obsługa zdarzeń poza klasą ........................................................................161 4.3. Współdzielenie obiektów nasłuchujących .............................................................163 4.4. Zdarzenia standardowe ..........................................................................................165 4.4.1. Zdarzenie action ..........................................................................................166 4.4.2. Zdarzenie item .............................................................................................169 4.4.3. Zdarzenie adjustment ..................................................................................170 4.4.4. Zdarzenie text ..............................................................................................171 4.4.5. Zdarzenia window .......................................................................................171 4.4.6. Zdarzenia component ..................................................................................172 4.4.7. Zdarzenia mouse .........................................................................................173 4.4.8. Zdarzenia mouseMotion .............................................................................174 4.4.9. Zdarzenia key ..............................................................................................176 4.4.10. Zdarzenia focus ...........................................................................................178 4.4.11. Zdarzenia container ....................................................................................180 4.4.12. Usuwanie obiektów nasłuchujących ...........................................................180 4.4.13. Powiązanie obiektów ze zdarzeniami .........................................................181 4.5. Zdarzenia z parametrem ........................................................................................183 4.5.1 Identyfikacja miejsca pochodzenia komunikatu ..........................................183 4.5.2. Wyniesienie własnych parametrów poza klasę ...........................................186 4.6. Łańcuchy zdarzeń ..................................................................................................188 4.7. Listener kontra Adapter .........................................................................................189 4.8. Obsługa w klasie pochodnej ..................................................................................190 4.8.1. Obsługa zdarzeń w klasie ............................................................................190 4.8.2. Obiekt z wewnętrzną obsługą .....................................................................191 6 Java. Programowanie obiektowe 4.8.3. Rzadko stosowana metoda ..........................................................................192 4.8.4. Powiązanie klas i zdarzeń ...........................................................................193 4.8.5. Wady i zalety wewnętrznej obsługi ............................................................194 4.9. Zaszłości w obsłudze zdarzeń ...............................................................................195 4.10. Podsumowanie ......................................................................................................196 Rozdział 5. Obsługa wyjątków ......................................................................... 197 5.1. Obsługa wyjątków przez program .........................................................................198 5.1.1. Wyjątek jako obiekt ....................................................................................198 5.1.2. Konstrukcja podstawowa try – catch ..........................................................202 5.1.3. Przechwytywanie różnych wyjątków ..........................................................203 5.1.4. Zagnieżdżanie obsługi wyjątków ................................................................204 5.1.5. Słowo kluczowe finally ...............................................................................206 5.1.6. Obsługa wyjątków poza metodą .................................................................208 5.1.7. Programowe generowanie wyjątków ..........................................................210 5.1.8. Wielokrotna obsługa tego samego wyjątku ................................................210 5.2. Własne typy wyjątków ..........................................................................................212 5.3. Obsługa wyjątków przez JVM ..............................................................................214 5.4. Podsumowanie .......................................................................................................217 Rozdział 6. Programowanie wielowątkowe ...................................................... 219 6.1. Techniczna strona wielowątkowości .....................................................................220 6.2. Podstawy realizacji wątków ..................................................................................222 6.2.1. Obiekty zarządzające wątkami ....................................................................222 6.2.2. Obiekty-wątki ..............................................................................................223 6.3. Tworzenie klas wątków .........................................................................................223 6.4. Zarządzanie wątkami .............................................................................................225 6.4.1. Uruchomienie i zatrzymanie wątku ............................................................225 6.4.2. Wstrzymanie pracy wątku ...........................................................................226 6.4.3. Wątki a działalność główna ........................................................................227 6.4.4. Zawieszenie pracy wątku ............................................................................228 6.4.5. Inteligentne wstrzymanie pracy ..................................................................229 6.4.6. Wymuszenie przełączenia wątku ................................................................231 6.4.7. Priorytety wątków .......................................................................................233 6.5. Synchronizacja wątków .........................................................................................236 6.5.1. Praca synchroniczna ....................................................................................236 6.5.2. Przyczyny synchronizacji metod ................................................................237 6.5.3. Metody różnego typu ..................................................................................240 6.5.4. Synchronizacja metod asynchronicznych ...................................................242 6.5.5. Wzajemna blokada ......................................................................................242 6.5.6. Przerywanie metod synchronizowanych .....................................................244 6.6. Podsumowanie .......................................................................................................246 Słowo końcowe ............................................................................ 247 Literatura ..................................................................................... 249 Skorowidz ..................................................................................... 251 Rozdział 2. Poprzedni rozdział wprowadzał ogólnie pojętą ideę programowania obiektowego oraz jej modyfikacje na potrzeby Javy. W tym rozdziale zajmę się tym samym problemem, ale tutaj pokażę środki realizacji idei opisanych wcześniej. Będziesz mógł dowiedzieć się, jak w praktyce realizuje się programowanie obiektowe z użyciem kodu źródłowego w Javie. Poszczególne konstrukcje języka są omówione z perspektywy kodowania oraz działania wirtualnej maszyny Javy, czyli JVM (Java Virtual Machine). Pomijam większość rozważań teoretycznych nad cechami poszczególnych konstrukcji, które opisałem wcześniej, dlatego liczę, że w dostateczny sposób zapoznałeś się z treścią poprzedniego rozdziału. 2.1. Klasy Klasy określają postać, strukturę i działanie obiektów, które są egzemplarzami klas. W związku z zastosowaniem w Javie skrajnie ortodoksyjnego podejścia program na- pisany z użyciem tego języka musi mieć, poza kilkoma wyjątkami (czyli prostymi podstawowymi typami danych), strukturę oraz działanie lub algorytm, który wyko- nuje, zaprojektowane z użyciem klas (a zrealizowane z użyciem ich egzemplarzy, czyli obiektów). 2.1.1. Tworzenie klas Najprostsza możliwa do stworzenia klasa ma postać: class Simple {} Charakteryzuje ją słowo kluczowe class, nazwa klasy (w tym wypadku Simple) oraz para nawiasów klamrowych, które reprezentują jej ciało (w tym przypadku są puste). Klasa ta musi być umieszczona w pliku Simple.java. Tak utworzony plik może zostać poddany poprawnej kompilacji i stanowić zupełnie poprawną (choć całkiem nieprzydatną) klasę Javy. Należy pamiętać, że każda klasa publiczna musi być zapisana w osobnym pliku, którego nazwa musi być dokładnie taka sama (oczywiście plus rozszerzenie 34 Java. Programowanie obiektowe .java) jak nazwa klasy zdefiniowanej wewnątrz (włącznie z rozróżnieniem na duże i małe litery). Teoria mówi, że nazwy klas mogą zawierać tak zwane znaki narodowe, ale ze względu na różne standardy kodowania (nawet w obrębie jednego systemu operacyj- nego) nie powinno się stosować liter innych niż łacińskie. Definicja klasy podstawowej musi być tworzona według szablonu zaprezentowanego na listingu 2.1 (elementy ujęte w nawiasy kwadratowe są opcjonalne i nie muszą wy- stępować). Listing 2.1. Szablon definicji klasy [modyfikator] class NazwaKlasy { [modyfikator] typ nazwa_pola_1; ... [modyfikator] typ nazwa_pola_k; [modyfikator] typ nazwa_metody_1([lista_parametrów]) { ciało_metody_1 } ... [modyfikator] typ nazwa_metody_L([lista_parametrów]) { ciało_metody_L } } Klasa może posiadać dowolną liczbę pól i metod (w tym zero, nawet łącznie dla pól i metod, jak pokazałem to wcześniej w najprostszej klasie Simple). Poniżej umieszczam objaśnienie poszczególnych elementów zaprezentowanych w sza- blonie na listingu 2.1. t class — słowo kluczowe określające definicję klasy. t NazwaKlasy — identyfikator określający nazwę klasy. t modyfikator — słowo lub słowa kluczowe oddzielone od siebie spacją określające sposób traktowania elementu, do którego się odnoszą. Modyfikator może też oznaczać ograniczenie lub rozszerzenie dostępu do elementu. Pełne wyjaśnienie znaczenia tego elementu języka znajduje się w podrozdziale 2.9. „Modyfikatory”. t typ — typ pola lub metody — może to być typ prosty (byte, short, int, long, char, float, double lub boolean oraz void — tylko w odniesieniu do metody), klasa bądź tablica (array) elementów jednego typu. t nazwa_pola_x — identyfikator jednoznacznie określający pole konstruowanej klasy. t nazwa_metody_x — identyfikator, który wraz z listą parametrów jednoznacznie określi metodę. t lista_parametrów — lista par rozdzielonych przecinkami składających się z określenia typu i nazwy egzemplarza danego typu. Jeśli nie zamierzamy przekazać do metody żadnych parametrów, jej deklaracja powinna zawierać Rozdział 2. ¨ Klasy i obiekty w Javie 35 parę pustych nawiasów. Zwracam tu uwagę na odstępstwa od C++, które w takim przypadku powinno (zamiast pustych nawiasów) zawierać słowo void, oraz różnice w stosunku do Object Pascala niezawierającego w takim przypadku nawiasów. t ciaco_metody_x — zbiór instrukcji języka Java określający funkcjonalność danej metody. 2.1.2. Pola Pola są to miejsca, w których przechowywane są informacje charakterystyczne dla całej klasy bądź dla jej konkretnego egzemplarza. O polach mówi się też czasami, że są to egzemplarze zmiennych należące do konkretnego egzemplarza klasy. W prakty- ce możemy traktować pola jako lokalne zmienne danej klasy z zastrzeżeniem, że za- kres ich widzialności i zachowania jest określony przez modyfikatory poszczególnych pól. Klasyczna deklaracja pola odbywa się według schematu: [modyfikator] typ nazwa_pola_k; Przykład klasy zawierającej tylko dwa pola pokazany jest na listingu 2.2. Listing 2.2. Klasa posiadająca tylko dwa pola class Point { int x; // położenie na osi 0X int y; // położenie na osi 0Y } W przykładzie tym pola są zmiennymi prostymi. Nie ma jednak żadnych przeciw- wskazań, żeby były zmiennymi złożonymi, w tym również obiektami. 2.1.3. Metody Inaczej niż inne języki obiektowe takie jak C++ czy Object Pascal, Java nie tylko gromadzi wszystkie informacje w plikach jednego rodzaju (tekstowych, z rozszerze- niem .java), ale również stara się je przechowywać w możliwie najbardziej skoncen- trowany sposób. W C++ istnieją pliki nagłówkowe, które przechowują strukturę obiektów, i właściwe pliki z programem przechowujące między innymi obiekty. W Object Pascalu informacje te są co prawda zawarte w jednym pliku, jednak część jest w sekcji inter- face, część w implementation. W Javie wszystko jest w jednym miejscu. Cała infor- macja o metodzie zawarta jest tuż przed jej ciałem, tak jak to widać na listingu 2.3. Listing 2.3. Szablon definicji metody [modyfikator] typ nazwa_metody([lista_parametrów]) { // blok instrukcji } 36 Java. Programowanie obiektowe Jeśli typ metody jest różny od void (czyli funkcja zwraca jakąś wartość), powinna ona być zakończona wierszem: return wyliczonaWartosc; gdzie wyliczonaWartosc musi być takiego samego typu jak typ metody. Po zaprezentowaniu schematu tworzenia klas mogę przystąpić do przedstawienia przykładu prostej klasy, która umożliwia przechowywanie informacji o położeniu punktu na płaszczyźnie wraz z metodami umożliwiającymi określenie położenia po- czątkowego punktu i przemieszczenia go. Klasa ta pokazana jest na listingu 2.4. Listing 2.4. Klasa opisująca punkt class Point { int x; // położenie na osi 0X int y; // położenie na osi 0Y // ustawienie nowej pozycji public void newPosition(int newX, int newY) { x = newX; y = newY; } // przemieszczenie punktu public void changePosition(int dX, int dY) { x = x+dX; y = y+dY; } } W dalszej części tego rozdziału będę rozszerzał definicję tej klasy i precyzował jej znaczenie. 2.1.4. Hermetyzacja i modyfikator private Wprowadzając ideę programowania obiektowego, zwracałem uwagę na jej podsta- wową cechę (i zarazem bardzo ważną zaletę), czyli hermetyzację. Klasa (a wraz z nią obiekt) miała gromadzić w jednym miejscu dane i procedury ich obsługi. Jednak miało to być zgromadzone w taki sposób, aby osoba używająca obiektu miała jak najmniejszy dostęp do danych (tylko do tych niezbędnych). Miało to zapewnić zarówno zmniej- szenie liczby błędów popełnianych w czasie kodowania, jak i podniesienie przejrzy- stości programu. Przedstawiona wcześniej klasa Point nie stanowiła idealnej repre- zentacji hermetycznej klasy, gdyż udostępniała na zewnątrz wszystkie, a nie tylko niezbędne elementy. Aby uniemożliwić dostęp do pól, które w idei klasy nie muszą być dostępne z zewnątrz, należy je oznaczyć modyfikatorem private. Na listingu 2.5 przedstawiam poprawioną, bardziej hermetyczną wersję klasy Point. Listing 2.5. Poprawiona klasa opisująca punkt class Point { private int x; // położenie na osi 0X private int y; // położenie na osi 0Y Rozdział 2. ¨ Klasy i obiekty w Javie 37 // odczyt wartości public int get p b return x; } public int getY p b return y; } // ustawienie nowej pozycji public void newPosition(int newX, int newY) { x = newX; y = newY; } // przemieszczenie punktu public void changePosition(int dX, int dY) { x = x+dX; y = y+dY; } } Na listingu 2.5 wytłuściłem różnice w stosunku do wcześniejszej wersji klasy, czyli ukrycie bezpośrednich wartości x i y oraz udostępnienie w zamian ich wartości przez metody getX i getY. Zaleta takiego rozwiązania jest widoczna. Nie można, nawet przez przypadek, odwołać się bezpośrednio do x i y, dzięki czemu nie może nastąpić przypadkowa ich modyfikacja. Aby je odczytać, trzeba jawnie wywołać getX lub getY. Aby je ustawić, trzeba jawnie wywołać newPosition (można też utworzyć metody setX i setY, aby ustawiać te parametry pojedynczo). Dopiero tak skonstruowana klasa spełnia warunki hermetyzacji. 2.1.5. Przeciążanie metod Istnieją sytuacje, w których niektórzy programiści uważają, że wskazane jest, aby można było utworzyć kilka metod o tych samych nazwach, lecz o różnym zestawie parametrów. Jako przykład można pokazać kolejne rozszerzenie naszej klasy Point o nową wersję metody newPosition. Rozszerzenie to pokazane jest na listingu 2.6. Listing 2.6. Kolejna wersja klasy opisującej punkt class Point { private int x; // położenie na osi 0X private int y; // położenie na osi 0Y // ustawienie nowej pozycji public void newPosition(int newX, int newY) { x = newX; y = newY; } // ustawienie nowej pozycji na (0,0) public void newPosition() { x = 0; y = 0; } // pozostałe metody klasy Point // ... } 38 Java. Programowanie obiektowe Pokazana na listingu 2.6 klasa ma dwie metody newPosition. Jedna, wywołana z pa- rametrami, ustawia współrzędne punktu na wartości podane jako parametry. Druga, bez parametrów, ustawia współrzędne punktu na wartość domyślną (0,0). Można pró- bować wyobrazić sobie sytuację, w której nie da się zastosować innego rozwiązania. Często jednak przeciążanie nie jest konieczne. Osobiście uważam, że kiedy tylko nie ma takiej potrzeby, nie powinno się go stosować. Jednak w standardowych bibliote- kach Javy wiele funkcji jest przeciążonych, co powoduje, że programiści chętnie trzymają się takiego standardu kodowania. Na przykład w projektowanej przez nas klasie zamiast przeciążania metody newPosition można by zastosować dwie różne metody — newPosition oraz defaultPosition. Jeżeli jednak decydujemy się na prze- ciążanie metod, powinniśmy pamiętać o następujących uwagach: t Metody rozróżniane są wyłącznie na podstawie liczby i typów przekazywanych do nich parametrów. Wywołanie metody powinno odbyć się z właściwym zestawem parametrów, gdyż w przeciwnym wypadku kompilator zgłosi błąd. t Metody nie są rozróżniane na podstawie nazw parametrów formalnych, w związku z tym próba stworzenia dwóch metod o tym samym zestawie typów parametrów i różnych ich nazwach zakończy się błędem. t Metody nie są również rozróżniane na podstawie typów zwracanej wartości. W związku z tym dwie metody o takim samym zestawie parametrów, lecz o różnym typie zwracanego wyniku zostaną potraktowane jak jedna metoda i kompilator również zgłosi błąd. t Jak wszędzie, w Javie wielkość liter ma znaczenie. W związku z tym istnienie metod newPosition i NewPosition nie jest żadnym przeciążeniem, gdyż mają one różne nazwy (według mnie stosowanie nazw różniących się wyłącznie wielkością liter to bardzo zły pomysł). 2.1.6. Słowo kluczowe this Java zawiera w swojej składni ciekawe, choć pozornie nieprzydatne słowo this. Z punktu widzenia formalnego wszystkie odwołania do własnych pól i metod są dokonywane w stosunku do tej klasy, w której się znajdujemy (czyli po angielsku właśnie this). Podobny mechanizm stosowany jest na przykład w Object Pascalu, który domyślnie zakłada, że wszystkie nieprzekierowane odwołania wykonywane są w stosunku do siebie (w Pascalu do przekierowań używa się słowa Self). Przykład wcześniej uży- wanej metody newPosition może być (a w zasadzie z punktu widzenia skrajnego for- malizmu powinien być) zapisany w postaci zaprezentowanej na listingu 2.7. Listing 2.7. Bardzo formalna definicja metody w klasie opisującej punkt public void newPosition(int newX, int newY) { this.x = newX; this.y = newY; } Oczywiście nikt tego nie robi, gdyż poza niepotrzebnym nakładem pracy nie zyskuje się w ten sposób żadnego ciekawego efektu. Nie zawsze jednak stosowanie tego przedrostka nie daje żadnego efektu. Istnieją sytuacje, kiedy kod źródłowy programu Rozdział 2. ¨ Klasy i obiekty w Javie 39 bez słowa this nie determinuje poprawnie elementu, do którego zamierzaliśmy się odwołać. Dzieje się tak wtedy, gdy parametry metody mają takie same nazwy jak pola klasy. Na listingu 2.8 przedstawiam zmodyfikowaną wersję metody newPosition, w której użycie słowa this jest już jak najbardziej uzasadnione. Listing 2.8. Uzasadnione użycie słowa this w klasie opisującej punkt public void newPosition(int x, int y) { this.x = x; this.y = y; } Patrick Naughton, jeden ze współtwórców Javy, uważa, że taka konstrukcja upraszcza tekst źródłowy oraz czyni go bardziej przejrzystym i mniej podatnym na błędy. W związku z taką tezą stawianą przez współautora języka wiele osób nagminnie stosuje takie konstrukcje. Według mnie jest to niepotrzebny manieryzm, który zaciemnia obraz sytuacji i jest przyczyną dużej liczby drobnych i zupełnie niepotrzebnych błędów. Warto po- patrzeć na hipotetyczną metodę newPosition pokazaną na listingu 2.9, która przelicza przed ustawieniem wartość położenia z cali na centymetry, aby można było zobaczyć, że łatwo jest się pomylić, stosując te same nazwy dla parametrów i pól klasy. Listing 2.9. Przykład popełnienia błędu zasięgu zmiennych classFloatPoint { float x, y; public void newPosition(float x, float y) { float xcm, ycm; xcm = 2.51*x; ycm = 2.51*y; x = xcm; // zły zakres y = ycm; // zły zakres } } Oczywiście kompilator nie zgłosi żadnego błędu, gdyż konstrukcja jest jak najbar- dziej poprawna, a my będziemy się zastanawiać, dlaczego pola obiektu nie są inicjo- wane we właściwy sposób. Otóż w wierszach oznaczonych na listingu 2.9 komenta- rzem zły zakres podstawiamy wartości do zmiennych, które posłużyły nam do przekazania wartości do metody, a które nie są widoczne na zewnątrz od niej (przy- kryły nazwy pól). Rozszerzenie użycia słowa this pokazałem w paragrafach 2.1.8. „Przeciążenie kon- struktorów”, 2.4.3. „Zastosowanie interfejsów” oraz 2.3.4. „this w klasach wewnętrznych”. 2.1.7. Konstruktor Mimo iż zaprezentowana klasa Point jest w pełni funkcjonalna w zakresie, jakiego od niej oczekujemy, w praktyce brakuje jej elementu, który znacznie ułatwiłby jej (i każ- dej innej klasy) wykorzystanie. Otóż bezpośrednio po utworzeniu obiektu, czyli eg- zemplarza tej klasy (co przedstawię w dalszej części tego rozdziału), położenie nowego 40 Java. Programowanie obiektowe punktu jest nieokreślone. Dopiero po użyciu metody newPosition, która jawnie dekla- ruje nowe położenie punktu, przestaje ono być nieokreślone, a zaczyna być takie, jak to zostało w niej ustawione. W związku z tym po każdorazowym utworzeniu takiego obiektu należałoby pamiętać o zainicjowaniu jego położenia. Znacznie wygodniej byłoby, gdyby inicjacja położenia punktu odbywała się automatycznie w czasie two- rzenia obiektu. Jest to możliwe, pod warunkiem że skorzystamy z możliwości stoso- wania specjalnej metody zwanej konstruktorem, wywoływanej automatycznie w cza- sie tworzenia egzemplarza klasy. Od zwykłej metody odróżniają konstruktor dwie kwestie — nazwa zgodna z nazwą klasy oraz brak typu. W stosunku do konstruktora można stosować deklaracje zasięgu, przy czym dobra praktyka sugeruje, aby zasięg widzialności konstruktora był dokładnie taki sam jak samej klasy. Byłoby to bowiem dużym błędem, gdyby klasa była widziana, a jej konstruktor nie. Przykładowy kon- struktor dla klasy Point pokazywanej wcześniej będzie miał postać zaprezentowaną na listingu 2.10. Listing 2.10. Konstruktor klasy opisującej punkt // konstruktor klasy Point Point(int newX, int newY) { x = newX; y = newY; } Brak typu w deklaracji konstruktora wynika z tego, że w praktyce zwraca on wartość typu dokładnie takiego samego jak klasa, w której jest umieszczony, czyli domyślnie jego typ jest dokładnie taki jak nazwa klasy. Gdyby więc twórcy Javy chcieli być bardzo pedantyczni, deklaracja konstruktora powinna wyglądać jak na listingu 2.11. Listing 2.11. Hipotetyczna deklaracja konstruktora // teoretyczna deklaracja konstruktora // (uwaga: błędna formalnie) Point Point(int newX, int newY) { x = newX; y = newY; } Na szczęście nie ma potrzeby, aby tak utrudniać sobie życie. 2.1.8. Przeciążanie konstruktorów O ile przeciążenia metod można uniknąć, stosując różne nazwy metod (na przykład dodając różne przyrostki), o tyle przeciążenie konstruktorów może okazać się nie- zbędne. Konstruktor to specyficzna, wywoływana w czasie tworzenia obiektu metoda o nazwie zgodnej z nazwą klasy. Ograniczenie takie (niewystępujące na przykład w Object Pascalu, gdzie konstruktor może mieć dowolną nazwę) wymusza stosowanie przeciążenia konstruktorów, jeśli chcemy korzystać z nich w sposób bardziej uniwer- salny. Jako przykład weźmy pokazywaną wcześniej klasę Point. Sugeruję dodanie do niej drugiego konstruktora bez parametrów, który będzie ustawiał położenie punktu na początku układu współrzędnych (0,0), tak jak na listingu 2.12. Rozdział 2. ¨ Klasy i obiekty w Javie 41 Listing 2.12. Deklaracja dwóch konstruktorów o tej samej nazwie class Point { private int x; // położenie na osi 0X private int y; // położenie na osi 0Y // pierwszy konstruktor klasy Point Point() { x = 0; y = 0; } // drugi konstruktor klasy Point Point(int newX, int newY) { x = newX; y = newY; } //... } W tym przypadku nie jest możliwe ominięcie przeciążenia ze względu na koniecz- ność zastosowania dla obu konstruktorów tej samej nazwy (czyli Point). Udogodnienie wprowadzone przez mechanizm przeciążania metod wprowadza bocz- nymi drzwiami możliwość zastosowania metod nazywających się tak samo jak klasy. Na pierwszy rzut oka wydaje się, że będziemy mieli do czynienia z konstruktorem, choć w rzeczywistości będzie to zwykła metoda o nazwie takiej jak klasa. W szcze- gólnym przypadku możemy więc zastosować konstrukcję pokazaną na listingu 2.13. Listing 2.13. Deklaracja metody i klasy o tej samej nazwie class Klasa { Klasa(){ /* konstruktor Klasa*/ } // metoda o nazwie Klasa: public int Klasa(int i) { return i; } } Użycie konstruktora i metody (trochę wybiegam tu w przyszłość, lecz mam nadzieję, że mi to wybaczysz) będzie miało postać jak na listingu 2.14. Listing 2.14. Użycie konstruktora i metody o tej samej nazwie // wykorzystanie konstruktora Klasa k = new Klasa(); // wykorzystanie metody int i = k.Klasa(11); Jakkolwiek taka konstrukcja jest możliwa, nie polecam jej ze względu na wysoką po- datność na generowanie błędów w tym miejscu. Jeśli użyjemy kompilatora z opcją pedantycznej kompilacji (na przykład JIKES), w czasie przetwarzania tej konstrukcji zgłosi on co do niej zastrzeżenie, lecz wykona proces kompilowania. Oto przykład błędnego użycia zaprezentowanej klasy: Klasa k = new Klasa(11); 42 Java. Programowanie obiektowe Na pierwszy rzut oka wydaje się, że wszystko jest w porządku. Odwołanie takie nie skutkuje jednak wywołaniem konstruktora, tylko metody. Dlatego jak wcześniej napi- sałem, nie powinno się stosować tej konstrukcji, chyba że szczególne zależy nam na zaciemnieniu struktury programu (na przykład w celu utrudnienia dekompilacji). Warto zauważyć, że stosowanie konstruktora i metody o tej samej nazwie jest pewną nieścisłością w stosunku do kwestii przeciążania metod. Zwykłe metody nie są roz- różniane na podstawie typu zwracanego wyniku. Natomiast konstruktor i metoda o tej samej nazwie i tym samym zestawie parametrów są dla kompilatora różne. Dzięki temu możliwe jest totalne zaciemnienie kodu klasy, jak to pokazałem na listingu 2.15. Listing 2.15. Metoda udająca domyślny konstruktor class Klasa { public int Klasa() { return 1; } } Pokazana na listingu 2.15 metoda umożliwia napisanie fragmentu programu zapre- zentowanego na listingu 2.16. Listing 2.16. Użycie konstruktora i metody o takiej samej liście parametrów // domyślny, bezparametrowy konstruktor Klasa k = new Klasa(); // metoda zwracająca wynik typu int int i = k. Klasa(); Jakkolwiek są osoby, które lubują się w stosowaniu takich konstrukcji, twierdząc że jest to esencja programowania obiektowego, ja uważam to za złe rozwiązanie. Na marginesie przeciążenia konstruktorów można pokazać użycie słowa kluczowego this w formie innej, niż pokazano w paragrafie 2.1.6. „Słowo kluczowe this”. Otóż odwołanie do samego tego słowa jest równoważne odwołaniu do konstruktora klasy, w której się znajdujemy. Oczywiście ma to sens jedynie w przypadku, gdy klasa ma kilka przeciążonych konstruktorów i jeden z nich, zamiast jawnie wykonywać jakiś blok instrukcji, odwołuje się do innego. Na listingu 2.17 przedstawiam ten sam frag- ment klasy Point, jednak z użyciem wywołania jednego z konstruktorów przez drugi za pomocą słowa this. Listing 2.17. Użycie słowa this zamiast konstruktora class Point { private int x; // położenie na osi 0X private int y; // położenie na osi 0Y // pierwszy konstruktor klasy Point Point() { this(0,0p; } // drugi konstruktor klasy Point Point(int newX, int newY) { Rozdział 2. ¨ Klasy i obiekty w Javie 43 x = newX; y = newY; } //... } Takie zastosowanie this rzeczywiście upraszcza kod źródłowy i czyni go bardziej przejrzystym. 2.1.9. Dziedziczenie Zanim przejdziemy dalej, należy wprowadzić pojęcie dziedziczenia. Jak zwracałem na to uwagę w poprzednim rozdziale, dziedziczenie jest jedną z podstawowych cech programowania obiektowego. Mechanizm ten umożliwia rozszerzanie możliwości wcześniej utworzonych klas bez konieczności ich ponownego tworzenia. Zasada dziedziczenia w Javie ma za podstawę założenie, że wszystkie klasy dostępne w tym języku bazują w sposób pośredni lub bezpośredni na klasie głównej o nazwie Object. Wszystkie klasy pochodzące od tej oraz każdej innej są nazywane, w stosunku do tej, po której dziedziczą, podklasami. Klasa, po której dziedziczy własności dana klasa, jest w stosunku do niej nazywana nadklasą. Jeśli nie deklarujemy w żaden sposób nadklasy, tak jak jest to pokazane w przykładowej deklaracji klasy Point, oznacza to, że stosujemy domyślne dziedziczenie po klasie Object. Formalnie deklaracja klasy Point mogłaby mieć postać zaprezentowaną na listingu 2.18. Listing 2.18. Dziedziczenie po klasie głównej class Point extends Object { // ... // ciało klasy Point // ... } Wytłuszczony fragment listingu 2.18 deklaruje dziedziczenie po klasie Object. Jak wcześniej pisałem, jest ono opcjonalne, to znaczy, że jeśli go nie zastosujemy, Point również będzie domyślnie dziedziczył po Object. Przedstawiony sposób jest używany w przypadku dziedziczenia po innych klasach, tak jak na listingu 2.19. Listing 2.19. Praktyczne użycie dziedziczenia class Figura extends Point { ... } class Wielokat extends Figura { ... } 44 Java. Programowanie obiektowe W przykładzie tym klasa Wielokat dziedziczy po klasie Figura, która z kolei dziedzi- czy po Point, a ta po Object. W Javie nie ma żadnych ograniczeń co do zagnieżdżania poziomów dziedziczenia. Poprawne więc będzie dziedziczenie na stu i więcej pozio- mach. Jakkolwiek takie głębokie dziedziczenie jest bardzo atrakcyjne w teorii, w praktyce wiąże się z niepotrzebnym obciążaniem zarówno pamięci, jak i procesora. To samo zadanie zrealizowane za pomocą płytszej struktury dziedziczenia będzie działało szybciej aż z trzech powodów: t Wywołanie metod będzie wymagało mniejszej liczby poszukiwań ich istnienia w ramach kolejnych nadklas. t Interpreter będzie musiał załadować mniej plików z definicjami klas (i mniej będzie ich później obsługiwał). t System operacyjny (a przez to również interpreter Javy) ma więcej wolnej pamięci, a przez to pracuje szybciej. Ponadto w przypadku apletów możemy liczyć na szybsze ładowanie się strony do przeglądarki, a więc będzie to kolejna pozytywna strona. Poza dziedziczeniem w dowolnie długim łańcuchu od klasy głównej do najniższej klasy potomnej w niektórych językach programowania (na przykład C++) istnieje wielo- krotne dziedziczenie jednocześnie i równorzędnie po kilku klasach. W Javie jest to niemożliwe, to znaczy w definicji każdej klasy może wystąpić co najwyżej jedno słowo extends. Zamiast wielokrotnego dziedziczenia w Javie dostępny jest mechanizm in- terfejsów opisany w podrozdziale 2.4. „Interfejsy”. 2.1.10. Inicjator klasy i obiektu Wróćmy do rozważań na temat tego, co się dzieje w początkach życia obiektu. Poza konstruktorem Java udostępnia dwa inne mechanizmy wspomagające inicjację i two- rzenie zarówno klas, jak i obiektów. Inicjator klasy jest to blok instrukcji wykonywany tylko raz, po załadowaniu przez JVM pliku z klasą przed pierwszym użyciem (jednak klasa musi być użyta, żeby blok ten wykonał się — sama deklaracja użycia bez ini- cjacji nie gwarantuje wykonania inicjatora klasy). Blok ten, zawarty między dwoma nawiasami klamrowymi, musi być poprzedzony słowem kluczowym static (dokład- ne znaczenie tego modyfikatora zostanie wyjaśnione dalej w tym rozdziale). Poza tym klasa może zawierać również inicjator obiektu, czyli egzemplarza klasy. Jest to też blok instrukcji zamknięty w nawiasach klamrowych, ale bez żadnego kwalifikatora. Zarówno inicjator klasy, jak i obiektu może wystąpić w każdej klasie kilkukrotnie. Jeśli jest ich większa ilość, zostaną wykonane zgodnie z kolejnością pojawienia się w ko- dzie źródłowym. Poniżej przedstawiony jest listing 2.20 z apletem, który zawiera różne elementy inicjacyjne wraz z instrukcjami umożliwiającymi sprawdzenie kolejności ich wykonywania się (szczegóły działania apletów zostaną wprowadzone w rozdziale 3. „Aplet jako obiekt na stronie HTML”). W komentarzach zaznaczono priorytet ważno- ści od 1 (najważniejsze, wykonywane najpierw) do 3 (najmniej ważne, wykonywane na końcu). Elementy inicjacyjne o tym samym priorytecie wykonywane są zgodnie z kolejnością wystąpienia. Warto zauważyć, że inicjator obiektu i inicjator pól obiektu mają ten sam priorytet i wykonywane są zgodnie z kolejnością wystąpienia w klasie. Rozdział 2. ¨ Klasy i obiekty w Javie 45 Listing 2.20. Inicjatory klasy import java.applet.*; public class Applet2 extends Applet { int i = setInt(1); // priorytet 2 static { // priorytet 1 System.err.println( class init ); } public Applet2() { // priorytet 3 System.err.println( konstruktor ); } { // priorytet 2 System.err.println( instance init ); } // dodatkowa funkcja wyświetlająca private int setInt(int i) { System.err.println( set int: + i); return i; } int j = setInt(2); // priorytet 2 } Zaprezentowany aplet generuje na konsolę Javy w przeglądarce zestaw komunikatów pokazanych na rysunku 2.1. Rysunek 2.1. Wydruk generowany przez program 2.20 class init set int: 1 instance init set int: 2 konstruktor Rozszerzenie tego tematu znajduje się w paragrafie 2.1.11. „Kolejność inicjacji klas”. Istnienie inicjatorów może uprościć tworzenie niektórych klas, zwłaszcza tych bar- dziej skomplikowanych. Wyobraźmy sobie, że tworzymy klasę z dużą liczbą przecią- żonych konstruktorów. W każdym z nich poza działaniami związanymi z ich charakte- rystyczną pracą zależną od zestawu przekazanych parametrów należy wykonać czynności inicjujące — wspólne dla wszystkich konstruktorów. Można by to zrobić poprzez jawne wywołanie wspólnej metody inicjującej, jak to pokazałem na listingu 2.21. Listing 2.21. Jawne użycie jednej metody inicjującej class cultiKonstruktor { public cultiKonstruktor() { wspolnyInit(); } public cultiKonstruktor(int i) { wspolnyInit(); // przetwarzanie i } public cultiKonstruktor(String s) { 46 Java. Programowanie obiektowe wspolnyInit(); // przetwarzanie s } void wspolnyInit() { /*inicjacja*/ } } } Działanie takie jest poprawne, ale wymaga od nas pamiętania o dodaniu jednego wy- wołania metody w każdym kolejnym konstruktorze oraz zwiększa wielkość kodu wy- nikowego. Każdy konstruktor zawiera bowiem wywołanie tej metody. Zastosowanie inicjatora obiektu uwalnia nas od konieczności każdorazowego dodawania tego wy- wołania oraz usuwa ten fragment kodu z konstruktora. Biorąc pod uwagę to, że aplet jest programem ładowanym do komputera użytkownika przez internet (czasami, gdy korzysta się z dość wolnej linii telefonicznej), każde kilkadziesiąt czy kilkaset bajtów może być ważne (ten sam problem dotyczy telefonów komórkowych, które są najczę- ściej wyposażone w bardzo małą pamięć). Jeśli tylko jest to możliwe, wspólny kod inicjacyjny powinno umieszczać się w bloku inicjacyjnym. Dla klasy z listingu 2.21 rozwiązanie takie miałoby postać zaprezentowaną na listingu 2.22. Listing 2.22. Kod inicjacyjny we wspólnym bloku class MultiKonstruktor { public MultiKonstruktor(int i) { // przetwarzanie i } public MultiKonstruktor(String s) { // przetwarzanie s } { /* tu wspólna inicjacja */ } } Warto zauważyć, że zaoszczędziliśmy nie tylko na dwóch wywołaniach metody, ale mogliśmy zrezygnować nawet z bezparametrowego konstruktora, którego jedyną pracą było uruchomienie procedury wspólnej inicjacji. Tak więc argumentem na stosowanie takiego rozwiązania jest nie tylko dbanie o użytkowników wdzwaniających się do in- ternetu, ale również elegancja i prostota, które prawie zawsze skutkują zmniejszeniem liczby popełnianych błędów. Warto zwrócić uwagę na to, że (inaczej niż w przypadku apletów) w przypadku pro- gramów, które mogą egzystować samodzielnie, przed jakąkolwiek inicjacją obiektu przeprowadzana jest inicjacja statyczna, a następnie wykonywana jest metoda main i to dopiero w niej może być tworzony egzemplarz obiektu. Dzieje się tak, gdyż metoda ta jest statyczna i publiczna i może być użyta przed stworzeniem egzemplarza obiektu (dokładniejsze wyjaśnienia co do natury metod statycznych zostaną zamieszczone dalej w tym rozdziale). Maszyna wirtualna Javy jest tak skonstruowana, że wywołuje tę metodę jako pierwszą. W przypadku samodzielnych programów należy więc wziąć pod uwagę ten aspekt i zmodyfikować kolejność wywoływania bloków inicjacyjnych. Rozdział 2. ¨ Klasy i obiekty w Javie 47 2.1.11. Kolejność inicjacji klas Powróćmy do kwestii kolejności, w jakiej wykonuje się inicjacja klas. Załóżmy, że nasze dziedziczące klasy będą skonstruowane według schematu pokazanego na listingu 2.23. Listing 2.23. Bloki inicjujące w klasach dziedziczących class A { A() { System.err.println( konstruktor A ); } { System.err.println( inicjator obiektu A ); } static { System.err.println( inicjator klasy A ); } } class B extends A { B() {System.err.println( konstruktor B ); } { System.err.println( inicjator obiektu B ); } static { System.err.println( inicjator klasy B ); } } Pierwsze użycie pokazanej na listingu 2.23 klasy B, przy założeniu, że wcześniej nie używaliśmy klasy A, spowoduje wyświetlenie kolejnych napisów, które pokazane są na rysunku 2.2. Rysunek 2.2. Wydruk generowany przez program z listingu 2.23 inicjator klasy A inicjator klasy B inicjator obiektu A konstruktor A inicjator obiektu B konstruktor B Jak więc widać, najpierw — w kolejności dziedziczenia — inicjowane są klasy. Po nich następuje sekwencja charakterystyczna dla inicjacji obiektów typu klasy nad- rzędnej. Obiekty te nazywam egzemplarzami wirtualnymi. W praktyce JVM rezerwuje od razu pamięć na cały rzeczywisty obiekt, jednak tworzenie go jest przeprowadzane sekwencyjnie. Najpierw inicjowany jest wirtualny obiekt bazowy, później uzupeł- niane są braki przez inicjacje kolejnych klas pochodnych. Inicjacja obiektów wirtual- nych, zaznaczona na rysunku 2.2 kursywą, jest blokiem nie do rozłączenia. Jeśli klasa używająca B ma blok inicjujący klasę, to zostanie on wykonany przed inicjatorem kla- sy A. Sytuacja nie zmieni się, jeśli w pierwszej linii konstruktora klasy B dodamy jawne wywołanie konstruktora klasy nadrzędnej (z użyciem słowa super), czyli klasy A, tak jak pokazałem to na listingu 2.24. Odwołanie do klasy nadrzędnej dokładnie zostanie wyjaśnione w paragrafie 2.1.14. „Odwołanie do klas nadrzędnych”. Listing 2.24. Rozszerzenie inicjacji klas z listingu 2.23 class B extends A { B() { super(); System.err.println( konstruktor B ); } { System.err.println( inicjator obiektu B ); } static { System.err.println( inicjator klasy B ); } } 48 Java. Programowanie obiektowe Zgodnie z tym, co wcześniej napisałem, konstruktor klasy nadrzędnej wywoływany jest domyślnie jako pierwsze działanie konstruktora danej klasy. Z sekwencyjnym tworzeniem i inicjacją obiektów dziedziczących związany jest pe- wien ważny problem. Pokażę go na przykładzie klasy nadrzędnej o postaci zaprezen- towanej na listingu 2.25. Listing 2.25. Klasa, po której trudno dziedziczyć public class A { private Object o; public A(Object o) { this.o = o; } } Klasa taka nie umożliwia utworzenia dziedziczenia w postaci zaprezentowanej na li- stingu 2.26. Listing 2.26. Błędne dziedziczenie po klasie z listingu 2.25 public class B extends A { Object oo = new Object(); public B() { super(oo); // błąd } } W takim przypadku konstruktor klasy nadrzędnej, reprezentowany przez linię super(oo), jest wywoływany, zanim utworzony zostanie egzemplarz oo klasy Object. Przed kon- struktorem klasy nadrzędnej nie może bowiem być wykonywana żadna inna akcja po- za ewentualnym wywołaniem innego konstruktora przeciążonego, który wywoła kon- struktor super. Podobnie niepoprawna będzie też konstrukcja pokazana na listingu 2.27. Listing 2.27. Błędne dziedziczenie po klasie z listingu 2.25 public class B extends A { public B() { super(this); // błąd } } Błąd wynika z tego, że egzemplarz obiektu tej klasy, reprezentowany przez this, będzie znany dopiero po jego utworzeniu, a więc najwcześniej po zakończeniu pracy kon- struktora klasy nadrzędnej. Mimo takiego podejścia, to znaczy kolejnego tworzenia egzemplarzy obiektów klas dziedziczących, metody tych klas są formalnie dostępne w obiektach nawet przed ich utworzeniem. Może to spowodować powstanie błędnego, przynajmniej w naszym pojęciu, działania niektórych konstruktorów. Na listingu 2.28 zaprezentowane zostały dwie klasy — A i B. W klasach tych metoda doSth została zadeklarowana i wykorzy- stana niepoprawnie. Rozdział 2. ¨ Klasy i obiekty w Javie 49 Listing 2.28. Błędne deklaracje metody w klasach dziedziczących class A { A() { doSth(); } void doSth() { System.err.println( A.doSth ); } } class B extends A { B(){ super(); doSth(); } void doSth() { System.err.println( B.doSth ); } } Jeśli zadeklarujemy użycie klasy B i utworzenie z niej obiektu B b = new B(); otrzymamy niespodziewany dla większości osób wynik (wydruk na konsoli Javy): B.doSth B.doSth Zaobserwowany efekt działania jest jednak poprawny. Jest on skutkiem działania po- limorfizmu. W zaprezentowanym przykładzie konstruktor w klasie A wywołuje metodę doSth tworzonego obiektu (czyli klasy B). Tak więc to metodę tej klasy wywoła kon- struktor klasy A, mimo iż twórca miał zapewne co innego na myśli. Aby wywołanie doSth zawsze dotyczyło własnej klasy, metoda ta musi być prywatna (modyfikator private). Warto na to zwrócić uwagę, gdyż może to być przyczyną wielu podobnych nieporozumień. Inicjacja klasy: B b = new B(3); której definicja pokazana jest na listingu 2.29, może przynieść nieoczekiwany efekt. Listing 2.29. Użycie metod w konstruktorze public class A { public A() { System.out.println( wewnątrz konstruktora A ); doSth(); } public void doSth() { System.out.println( nic nie robię ); } } public class B extends A { private int p1; public B(int p) { 50 Java. Programowanie obiektowe p1 = p; System.out.println( wewnątrz konstruktora B ); } public void doSth() { System.out.println( p1= + p1); // obliczenia z użyciem p1 } } Pozornie nieoczekiwany wynik działania klasy z listingu 2.29 zaprezentowałem na rysunku 2.3. Rysunek 2.3. Wydruk generowany przez program 2.29 wewnątrz konstruktora A p1=0 wewnątrz konstruktora B Czyli tak jak napisałem wcześniej, przed uruchomieniem konstruktora klasy B (czyli przed powstaniem egzemplarza tej klasy) system potrafi już użyć jego metody doSth. Oczywiście skoro dzieje się to przed uruchomieniem konstruktora B, prywatne pole p1 nie jest jeszcze zainicjowane, więc jest równe zero. Więcej na temat polimorfizmu znajdziesz w paragrafie 2.2.6. „Efekty polimorfizmu”. 2.1.12. Destruktor Podchodząc formalnie do specyfikacji JVM, można powiedzieć, że klasy Javy nie wymagają stosowania specjalizowanych metod zwalniających zajętą przez siebie pa- mięć. Wynika to z założenia przyjętego w czasie tworzenia tego systemu, a mianowi- cie braku jawnego zwalniania pamięci zajmowanej przez obiekty. Podejście klasyczne stosowane w C++ i Object Pascalu zakłada, że to programista, w chwili kiedy uznaje to za stosowne lub gdy wymusza to struktura programu, zwalnia pamięć, korzystając z jawnych funkcji systemowych bądź specjalizowanych metod wbudowanych w obiekty (destruktory). Podejście takie ma tę zaletę, że umożliwia zwalnianie pamięci na- tychmiast, kiedy jest to możliwe. Ma jednak tę wadę, że może powodować próby odwołania się do obiektu omyłkowo i przedwcześnie zwolnionego. W Javie zrezy- gnowano więc z tego mechanizmu. Nie oznacza to jednak braku metody, która byłaby namiastką destruktora. Jest nią metoda finalize wywoływana w trakcie czyszczenia pamięci. Ma ona za zadanie wykonać wszystkie konieczne działania przed całkowitym zastopowaniem oraz zwolnieniem pamięci przez obiekt, do którego należy (jednak zwalnianie pamięci nie należy już do obowiązków tej metody). Typowa deklaracja funkcji kończącej działanie obiektu powinna mieć postać pokazaną na listingu 2.30. Listing 2.30. Przykładowa deklaracja destruktora public void finalize() { zatrzymajWatki(); usunPowiazania(); } Rozdział 2. ¨ Klasy i obiekty w Javie 51 W ramach działania tej metody poza zatrzymaniem wątków przynależnych do tego obiektu (o wątkach napiszę szerzej w rozdziale 6. „Programowanie wielowątkowe”) powinno się dokonać wszelkiego sprzątania po działającym obiekcie. Jeśli obiekt sam siebie wyświetlał na ekranie, należy ten obraz usunąć (w przeciwnym razie może po- zostać tam na zawsze). Jeśli obiekt był wykorzystywany jako nasłuchujący w procesie zarządzania zdarzeniami, należy go usunąć z kolejki obsługi zdarzeń obiektu, który był przez niego obsługiwany. Jeśli z działaniem obiektu związana była obsługa pli- ków bądź dostęp do baz danych, należy zakończyć tę obsługę, aby pliki nie były blo- kowane przez nieistniejący już obiekt. Po zakończeniu tej metody sterowanie odda-
Pobierz darmowy fragment (pdf)

Gdzie kupić całą publikację:

Java. Programowanie obiektowe
Autor:

Opinie na temat publikacji:


Inne popularne pozycje z tej kategorii:


Czytaj również:


Prowadzisz stronę lub blog? Wstaw link do fragmentu tej książki i współpracuj z Cyfroteką: