131 94 42MB
Polish Pages [1114] Year 2005
K o m p le tn y p rz e w o d n ik p o n a jn o w s z e j w e rs ji ję z y k a p ro g ra m o w a n ia e ry in te rn e tu
Kompendium programisty E le m e n ty ję z y k a M e to d o lo g ie p ro g ra m o w a n ia B iblioteki z e w n ę trz n e T w o rze n ie a p le tó w i s e rw le tó w
H e r b e r t S c h ild t
K o m p le tn y p rz e w o d n ik p o n a jn o w sze j w e rsji ję z y k a p ro g ra m o w a n ia e ry internetu
Kompendium programisty E le m en ty ję zyka M eto d o lo g ie p ro g ra m o w a n ia Biblioteki ze w n ę trzn e T w orzenie apletów i se rw le tó w
Tytuł oryginału: Java: The Complete Reference, J2SE 5 Edition Tłumaczenie: Rafał Jońca (rozdz. 0 - 17, 31 - 32, dod. A), Mikołaj Szczepaniak (rozdz. 18 - 25), Jakub Thiele-Wieczorek (rozdz. 26 - 30) ISBN: 83-7361-862-7 Original edition copyright 2005 by The McGraw-Hill Companies. All rights reserved. Polish edition copyright © 2005 by Wydawnictwo Helion. All rights reserved. Polish language edition published by Wydawnictwo Helion. Copyright © 2005 All rights reserved. No part of this book may be reproduced or transmitted in any fonn or by any means, electronic or mechanical, including photocopying, recording or by any information storage retrieval system, without permission from the Publisher. Wszelkie prawa zastrzeżone. Nieautoryzowane rozpowszechnianie całości lub fragmentu niniejszej publikacji w jakiejkolw iek postaci jest zabronione. Wykonywanie kopii metodą kserograficzną, fotograficzną, a także kopiowanie książki na nośniku filmowym, magnetycznym lub innym powoduje naruszenie praw autorskich niniejszej publikacji. Wszystkie znaki występujące w tekście są zastrzeżonymi znakami firmowymi bądź towarowymi ich właścicieli. Autor oraz Wydawnictwo HELION dołożyli wszelkich starań, by zawarte w tej książce informacje były kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie, ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz Wydawnictwo HELION nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe z wykorzystania informacji zawartych w książce. Wydawnictwo HELION ul. Chopina 6. 44-100 GLIWICE tel. (32) 231-22-19, (32) 230-98-63 e-mail: [email protected] WWW: http://helion.pl (księgarnia internetowa, katalog książek)
Drogi Czytelniku! Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres http://helion.pl/user/opinie7javakp Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Printed in Poland.
Spis treści O Autorze ...............................................................................................................21 Przedmowa ............................................................................................................23
Część I
Język Java ......................................................................................... 27
Rozdział 1.
Historia i ewolucja języka Java ............................................................................29 Rodowód Jawy..............................................................................................................................29 Narodziny nowoczesnego języka — C ..................................................................................30 Język C++ — następny krok .................................................................................................31 Podwaliny języka Java...........................................................................................................32 Powstanie języka Java..................................................................................................................32 Powiązanie z językiem C#......................................................................................................34 Dlaczego język Java jest tak ważny dla internetu .....................................................................35 Aplety Javy..............................................................................................................................35 Bezpieczeństwo .......................................................................................................................36 Przenośność.............................................................................................................................36 Magia języka Java — kod bajtowy.............................................................................................36 Hasła języka Java .........................................................................................................................37 Prostota ...................................................................................................................................38 Obiektowość ............................................................................................................................38 Solidność..................................................................................................................................38 Wielowątkowość .....................................................................................................................39 Neutralność architektury.......................................................................................................39 Interpretowalność i wysoka wydajność ................................................................................40 Rozproszenie ...........................................................................................................................40 Dynamika ................................................................................................................................40 Ewolucja Javy...............................................................................................................................40 Rewolucja J2SE 5 .........................................................................................................................41 Kultura innowacji ........................................................................................................................42
Rozdział 2.
Podstawy języka Java ...........................................................................................43 Programowanie obiektowe ....................................................................................................43 Dwa paradygmaty ..................................................................................................................43 Abstrakcja...............................................................................................................................44 Trzy zasady programowania obiektowego...........................................................................44 Pierwszy przykładowy program .................................................................................................49 Wpisanie kodu programu ......................................................................................................50 Kompilacja programów .........................................................................................................50 Bliższe spojrzenie na pierwszy przykładowy program .......................................................51
4
Java. Kompendium programisty Drugi prosty program ..................................................................................................................53 Dwie instrukcje sterujące ............................................................................................................55 Instrukcja if ............................................................................................................................55 Pętla for ...................................................................................................................................57 Bloki kodu .....................................................................................................................................58 Kwestie leksykalne .......................................................................................................................59 Znaki niedrukowalne .............................................................................................................60 Identyfikatory .........................................................................................................................60 Literał ......................................................................................................................................60 Komentarze.............................................................................................................................60 Separatory...............................................................................................................................61 Słowa kluczowe języka Java ..................................................................................................61 Biblioteki klas Javy ......................................................................................................................62
Rozdział 3.
Typy danych, zmienne i tablice ............................................................................63 Java to język ze ścisła kontrola typów........................................................................................63 Typy proste ...................................................................................................................................63 Typy całkowite..............................................................................................................................64 Typ byte...................................................................................................................................65 Typ short .................................................................................................................................65 Typ int .....................................................................................................................................66 Typ long...................................................................................................................................66 Typy zmiennoprzecinkowe ..........................................................................................................67 Typ float ..................................................................................................................................67 Typ double ..............................................................................................................................67 Typ znakowy.................................................................................................................................68 Typ logiczny ..................................................................................................................................69 Bliższe spojrzenie na literały .......................................................................................................70 Literały będące liczbami całkowitymi ..................................................................................70 Literały zmiennoprzecinkowe ...............................................................................................71 Literały logiczne .....................................................................................................................71 Literały znakowe ....................................................................................................................71 Literały tekstowe ....................................................................................................................72 Zmienne.........................................................................................................................................72 Deklaracja zmiennej...............................................................................................................73 Inicjalizacja dynamiczna .......................................................................................................73 Zasięg i czas życia zmiennych................................................................................................74 Konwersja typów i rzutowanie....................................................................................................76 Automatyczna konwersja typów ...........................................................................................76 Rzutowanie dla typów niezgodnych......................................................................................77 Automatyczne rozszerzanie typów w wyrażeniach ...................................................................78 Zasady rozszerzania typu ......................................................................................................79 Tablice ...........................................................................................................................................80 Tablice jednowymiarowe .......................................................................................................80 Tablice wielowymiarowe........................................................................................................83 Alternatywna składnia deklaracji tablicy ............................................................................86 Kilka słów na temat ciągów znaków ...........................................................................................87 Uwaga dla programistów języka C lub C++ na temat wskaźników ........................................87
Rozdział 4.
Operatory ...............................................................................................................89 Operatory arytmetyczne..............................................................................................................89 Podstawowe operatory arytmetyczne ...................................................................................89 Operator reszty z dzielenia ....................................................................................................91 Operatory arytmetyczne z przypisaniem .............................................................................91 Inkrementacja i dekrementacja ............................................................................................92
Spis treści
5 Operatory bitowe..........................................................................................................................94 Podstawowe operatory bitowe...............................................................................................95 Przesuniecie w lewo ................................................................................................................97 Przesuniecie w prawo .............................................................................................................99 Przesuniecie w prawo bez znaku.........................................................................................100 Operatory bitowe z przypisaniem.......................................................................................102 Operatory relacji ........................................................................................................................103 Operatory logiczne .....................................................................................................................104 Operatory logiczne ze skracaniem ......................................................................................105 Operator przypisania .................................................................................................................106 Operator ?:..................................................................................................................................106 Kolejność wykonywania operatorów........................................................................................107 Wykorzystanie nawiasów okrągłych.........................................................................................107
Rozdział 5.
Struktury sterujące ..............................................................................................109 Instrukcje wyboru ......................................................................................................................109 Konstrukcja if .......................................................................................................................109 Konstrukcja switch...............................................................................................................112 Instrukcje iteracji.......................................................................................................................116 Pętla while .............................................................................................................................116 Pętla do-while........................................................................................................................118 Pętla for .................................................................................................................................121 Wersja for-each pętli for......................................................................................................124 Pętle zagnieżdżone................................................................................................................129 Instrukcje skoku.........................................................................................................................130 Instrukcja break ...................................................................................................................130 Instrukcja continue ..............................................................................................................134 Instrukcja return..................................................................................................................135
Rozdział 6.
Wprowadzenie do klas ........................................................................................137 Klasy ............................................................................................................................................137 Ogólna postać klasy..............................................................................................................137 Prosta klasa ...........................................................................................................................139 Tworzenie obiektów ...................................................................................................................141 Bliższe spojrzenie na klasę...................................................................................................142 Przypisywanie zmiennych referencyjnych do obiektów .........................................................143 Metody.........................................................................................................................................144 Dodanie metody do klasy Box .............................................................................................145 Zwracanie wartości ..............................................................................................................146 Dodanie metody przyjmującej parametry .........................................................................148 Konstruktor ................................................................................................................................150 Konstruktor sparametryzowany.........................................................................................152 Słowo kluczowe this....................................................................................................................153 Ukrywanie zmiennych składowych.....................................................................................153 Mechanizm odzyskiwania pamięci............................................................................................154 Metoda finalize().........................................................................................................................154 Klasa stosu ..................................................................................................................................155
Rozdział 7.
Dokładniejsze omówienie metod i klas .............................................................159 Przeciążanie metod.....................................................................................................................159 Przeciążanie konstruktorów ................................................................................................162 Obiekty jako parametry ............................................................................................................164 Dokładniejsze omówienie przekazywania argumentów..........................................................166 Zwracanie obiektów ...................................................................................................................168 Rekurencja..................................................................................................................................169
6
Java. Kompendium programisty Wprowadzenie do sterowania dostępem ..................................................................................171 Składowe statyczne.....................................................................................................................175 Słowo kluczowe final ..................................................................................................................177 Powtórka z tablic........................................................................................................................177 Klasy zagnieżdżone i klasy wewnętrzne ...................................................................................179 Omówienie klasy String .............................................................................................................182 Wykorzystanie argumentów wiersza poleceń ..........................................................................184 Zmienna liczba argumentów .....................................................................................................185 Przeciążanie metod o zmiennej liczbie argumentów .........................................................188 Zmienna liczba argumentów i niejednoznaczności ...........................................................189
Rozdział 8.
Dziedziczenie .......................................................................................................191 Proste dziedziczenie....................................................................................................................191 Dostęp do składowych a dziedziczenie................................................................................193 Bardziej praktyczny przykład.............................................................................................194 Zmienna klasy bazowej może zawierać referencje do obiektu podklasy.........................196 Słowo kluczowe super ................................................................................................................197 Wykorzystanie słowa kluczowego super do wywołania konstruktora klasy bazowej......................................................................197 Drugie zastosowanie słowa kluczowego super ...................................................................200 Tworzenie hierarchii wielopoziomowej....................................................................................201 Kiedy dochodzi do wywołania konstruktorów?.......................................................................204 Przesłanianie metod ...................................................................................................................205 Dynamiczne przydzielanie metod .............................................................................................208 Dlaczego warto przesłaniać metody?..................................................................................209 Zastosowanie przesłaniania metod .....................................................................................210 Klasy abstrakcyjne .....................................................................................................................211 Słowo kluczowe final i dziedziczenie.........................................................................................214 Słowo kluczowe final zapobiega przesłanianiu ..................................................................214 Słowo kluczowe final zapobiega dziedziczeniu ..................................................................215 Klasa Object................................................................................................................................215
Rozdział 9.
Pakiety i interfejsy ...............................................................................................217 Pakiety .........................................................................................................................................217 Definiowanie pakietu............................................................................................................218 Znajdowanie pakietów i ścieżka CLASSPATH .................................................................219 Prosty przykład pakietu.......................................................................................................219 Ochrona dostępu ........................................................................................................................220 Przykład dostępu ..................................................................................................................221 Import pakietów .........................................................................................................................224 Interfejsy .....................................................................................................................................226 Definiowanie interfejsu ........................................................................................................227 Implementacja interfejsu.....................................................................................................227 Zastosowanie interfejsów.....................................................................................................230 Zmienne w interfejsach........................................................................................................233 Interfejsy można rozszerzać ................................................................................................235
Rozdział 10. Obsługa wyjątków ...............................................................................................237 Podstawy obsługi wyjątków.......................................................................................................237 Typy wyjątków ...........................................................................................................................238 Nieprzechwycone wyjątki ..........................................................................................................238 Wykorzystanie konstrukcji try i catch .....................................................................................240 Wyświetlenie opisu wyjątku ................................................................................................241 Wiele klauzul catch ....................................................................................................................241 Zagnieżdżone konstrukcje try ...................................................................................................243
Spis treści
7 Instrukcja throw.........................................................................................................................245 Klauzula throws..........................................................................................................................246 Słowo kluczowe finally ...............................................................................................................248 Wyjątki wbudowane w język Java............................................................................................249 Tworzenie własnej podklasy wyjątków ....................................................................................250 Łańcuch wyjątków .....................................................................................................................252 Wykorzystanie wyjątków...........................................................................................................254
Rozdział 11. Programowanie wielowątkowe ..........................................................................255 Model wątków języka Java........................................................................................................256 Priorytety wątków ................................................................................................................257 Synchronizacja......................................................................................................................257 Przekazywanie komunikatów..............................................................................................258 Klasa Thread i interfejs Runnable......................................................................................258 Watek główny .............................................................................................................................259 Tworzenie wątku ........................................................................................................................261 Implementacja interfejsu Runnable ...................................................................................261 Rozszerzanie klasy Thread ..................................................................................................263 Wybór odpowiedniego podejścia.........................................................................................264 Tworzenie wielu wątków............................................................................................................264 Wykorzystanie metod isAlive() oraz join()...............................................................................266 Priorytety wątków ......................................................................................................................268 Synchronizacja ...........................................................................................................................271 Synchronizacja metod..........................................................................................................271 Konstrukcja synchronized...................................................................................................274 Komunikacja miedzywątkowa ..................................................................................................275 Blokada wzajemna ...............................................................................................................279 Zawieszanie, wznawianie i zatrzymywanie wątków................................................................281 Zawieszanie, wznawianie i zatrzymywanie wątków w Java 1.1 lub starszej...................282 Nowoczesny sposób zawieszania, wznawiania i zatrzymywania wątków ........................284 Korzystanie z wielowątkowości .................................................................................................286 Rozdział 12. Wyliczenia, automatyczne otaczanie typów prostych i metadane .................287 Wyliczenia ...................................................................................................................................287 Podstawy wyliczeń................................................................................................................288 Metody values() i valueOf() .................................................................................................290 Wyliczenia Javy jako typ klasy ...........................................................................................291 Wyliczenia dziedziczą po klasie Enum ...............................................................................293 Inny przykład wyliczenia .....................................................................................................295 Typy otoczkowe ..........................................................................................................................296 Automatyczne otaczanie typów prostych .................................................................................298 Automatyczne otaczanie i metody.......................................................................................299 Automatyczne otaczanie i rozpakowywanie w wyrażeniach ............................................300 Automatyczne otaczanie dla typów znakowych i logicznych............................................302 Automatyczne otaczanie pomaga zapobiegać błędom.......................................................303 Słowo ostrzeżenia..................................................................................................................303 Metadane (notatki) .....................................................................................................................304 Podstawy tworzenia notatek................................................................................................304 Określenie strategii zachowania..........................................................................................305 Pobieranie notatek w trakcie działania programu dzięki refleksji ..................................306 Interfejs AnnotatedElement ................................................................................................310 Wartości domyślne ...............................................................................................................311 Notatki znacznikowe ............................................................................................................312 Notatki jednoelementowe.....................................................................................................313 Wbudowane notatki .............................................................................................................315 Ograniczenia .........................................................................................................................316
8
Java. Kompendium programisty
Rozdział 13. Wejście-wyjście, aplety i inne tematy ................................................................317 Podstawowa obsługa wejścia i wyjścia .....................................................................................317 Strumienie .............................................................................................................................318 Strumienie znakowe i bajtowe.............................................................................................318 Predefiniowane strumienie ..................................................................................................319 Odczyt danych z konsoli ............................................................................................................321 Odczyt znaków......................................................................................................................321 Odczyt ciągów znaków .........................................................................................................322 Wyświetlanie informacji na konsoli..........................................................................................324 Klasa PrintWriter ......................................................................................................................324 Odczyt i zapis plików .................................................................................................................325 Podstawy apletów .......................................................................................................................328 Modyfikatory transient i volatile ..............................................................................................331 Operator instanceof....................................................................................................................332 Modyfikator strictfp...................................................................................................................334 Metody napisane w kodzie rdzennym.......................................................................................335 Problemy z metodami rdzennymi .......................................................................................338 Asercja.........................................................................................................................................338 Opcje włączania i wyłączania asercji..................................................................................341 Import statyczny.........................................................................................................................341 Rozdział 14. Typy sparametryzowane .....................................................................................345 Czym są typy sparametryzowane?............................................................................................346 Prosty przykład zastosowania typów sparametryzowanych ..................................................346 Typy sparametryzowane działają tylko dla obiektów.......................................................350 Typy sparametryzowane różnią się, jeśli mają inny argument typu ...............................350 W jaki sposób typy sparametryzowane zwiększają bezpieczeństwo?..............................350 Klasa sparametryzowana z dwoma parametrami typu ..........................................................353 Ogólna postać klasy sparametryzowanej .................................................................................354 Typy ograniczone .......................................................................................................................354 Zastosowanie argumentów wieloznacznych.............................................................................357 Ograniczony argument wieloznaczny.................................................................................359 Tworzenie metody sparametryzowanej....................................................................................364 Konstruktory sparametryzowane .......................................................................................366 Interfejsy sparametryzowane ....................................................................................................367 Typy surowe i dotychczasowy kod............................................................................................369 Hierarchia klas sparametryzowanych......................................................................................372 Zastosowanie sparametryzowanej klasy bazowej..............................................................372 Podklasa sparametryzowana...............................................................................................374 Porównywanie typów w trakcie działania programu dla hierarchii klas sparametryzowanych.........................................................................375 Rzutowanie............................................................................................................................378 Przesłanianie metod w klasach sparametryzowanych ......................................................378 Znoszenie.....................................................................................................................................379 Metody mostu .......................................................................................................................381 Błędy niejednoznaczności ..........................................................................................................383 Pewne ograniczenia typów sparametryzowanych ...................................................................384 Nie można tworzyć egzemplarza parametru typu.............................................................384 Ograniczenia dla składowych statycznych .........................................................................385 Ograniczenia tablic typów sparametryzowanych..............................................................385 Ograniczenia wyjątków typów sparametryzowanych.......................................................386 Ostatnie uwagi na temat typów sparametryzowanych ...........................................................387
Spis treści
9
Cześć II
Biblioteka języka Java ................................................................... 389
Rozdział 15. Ciągi znaków ........................................................................................................391 Konstruktory klasy String.........................................................................................................392 Konstruktory dodane w wydaniu J2SE 5...........................................................................394 Długość ciągu znaków ................................................................................................................394 Specjalne operacje na ciągach znaków.....................................................................................394 Literały tekstowe ..................................................................................................................395 Konkatenacja ciągów znaków .............................................................................................395 Konkatenacja ciągu znaków z innymi typami danych......................................................396 Konwersja do ciągu znaków i metoda toString()...............................................................396 Wydobycie znaków.....................................................................................................................397 Metoda charAt() ...................................................................................................................398 Metoda getChars()................................................................................................................398 Metoda getBytes().................................................................................................................399 Metoda toCharArray().........................................................................................................399 Porównywanie ciągów znaków..................................................................................................399 Metody equals() i equalsIgnoreCase() ................................................................................399 Metoda regionMatches() ......................................................................................................400 Metody startsWith() i endsWith() .......................................................................................401 Metoda equals() a operator == ............................................................................................401 Metoda compareTo()............................................................................................................402 Wyszukiwanie podciągów znaków............................................................................................403 Modyfikacja ciągu znaków ........................................................................................................405 Metoda substring() ...............................................................................................................405 Metoda concat() ....................................................................................................................406 Metoda replace()...................................................................................................................406 Metoda trim()........................................................................................................................407 Konwersja danych za pomocą metody valueOf() ....................................................................408 Zmiana wielkości liter ciągu znaków........................................................................................408 Dodatkowe metody klasy String ...............................................................................................409 Klasa StringBuffer .....................................................................................................................409 Konstruktory klasy StringBuffer........................................................................................411 Metody length() i capacity().................................................................................................411 Metoda ensureCapacity().....................................................................................................412 Metoda setLength() ..............................................................................................................412 Metody charAt() i setCharAt()............................................................................................412 Metoda getChars()................................................................................................................413 Metoda append()...................................................................................................................413 Metoda insert() .....................................................................................................................414 Metoda reverse()...................................................................................................................414 Metody delete() i deleteCharAt() ........................................................................................415 Metoda replace()...................................................................................................................416 Metoda substring() ...............................................................................................................416 Dodatkowe metody klasy StringBuffer...............................................................................416 Klasa StringBuilder....................................................................................................................418 Rozdział 16. Pakiet java.lang ...................................................................................................419 Otoczki typów prostych .............................................................................................................420 Klasa Number .......................................................................................................................420 Klasy Double i Float .............................................................................................................420 Klasy Byte, Short, Integer i Long........................................................................................424 Klasa Character....................................................................................................................432 Dodatki wprowadzone w celu obsługi rozszerzonych znaków Unicode ..........................434 Klasa Boolean .......................................................................................................................435
10
Java. Kompendium programisty Klasa Void...................................................................................................................................435 Klasa Process ..............................................................................................................................436 Klasa Runtime ............................................................................................................................437 Zarządzanie pamięcią ..........................................................................................................438 Wykonywanie innych programów ......................................................................................440 Klasa ProcessBuilder .................................................................................................................441 Klasa System...............................................................................................................................442 Wykorzystanie metody currentTimeMillis() do obliczania czasu wykonywania programu..................................................................444 Użycie metody arraycopy()..................................................................................................445 Właściwości środowiska.......................................................................................................446 Klasa Object................................................................................................................................446 Wykorzystanie metody clone() i interfejsu Cloneable.............................................................446 Klasa Class ..................................................................................................................................449 Klasa ClassLoader......................................................................................................................452 Klasa Math..................................................................................................................................452 Funkcje trygonometryczne ..................................................................................................452 Funkcje wykładnicze ............................................................................................................453 Funkcje zaokrągleń ..............................................................................................................453 Różnorodne metody klasy Math .........................................................................................454 Klasa StrictMath ........................................................................................................................455 Klasa Compiler...........................................................................................................................455 Klasy Thread i ThreadGroup oraz interfejs Runnable ..........................................................455 Interfejs Runnable................................................................................................................456 Klasa Thread.........................................................................................................................456 Klasa ThreadGroup .............................................................................................................458 Klasy ThreadLocal i InheritableThreadLocal.........................................................................462 Klasa Package.............................................................................................................................463 Klasa RuntimePermission .........................................................................................................463 Klasa Throwable ........................................................................................................................463 Klasa SecurityManager .............................................................................................................463 Klasa StackTraceElement .........................................................................................................464 Klasa Enum.................................................................................................................................465 Interfejs CharSequence .............................................................................................................466 Interfejs Comparable .................................................................................................................467 Interfejs Appendable..................................................................................................................467 Interfejs Iterable.........................................................................................................................467 Interfejs Readable ......................................................................................................................468 Podpakiety pakietu java.lang ....................................................................................................468 Podpakiet java.lang.annotation...........................................................................................468 Podpakiet java.lang.instrument ..........................................................................................469 Podpakiet java.lang.management .......................................................................................469 Podpakiet java.lang.ref ........................................................................................................469 Podpakiet java.lang.reflect ..................................................................................................469
Rozdział 17. Pakiet java.util, cześć 1. — kolekcje ...................................................................471 Wprowadzenie do kolekcji ........................................................................................................472 Zmiany w kolekcjach spowodowane wydaniem J2SE 5 .........................................................473 Typy sparametryzowane w znaczący sposób zmieniają kolekcje ....................................473 Automatyczne otaczanie ułatwia korzystanie z typów prostych ......................................474 Pętla for typu for-each .........................................................................................................474 Interfejsy kolekcji.......................................................................................................................474 Interfejs Collection ...............................................................................................................475 Interfejs List..........................................................................................................................477
Spis treści
11 Interfejs Set ...........................................................................................................................478 Interfejs SortedSet................................................................................................................479 Interfejs Queue .....................................................................................................................479 Klasy kolekcji..............................................................................................................................480 Klasa ArrayList ....................................................................................................................481 Klasa LinkedList ..................................................................................................................484 Klasa HashSet .......................................................................................................................486 Klasa LinkedHashSet...........................................................................................................488 Klasa TreeSet........................................................................................................................488 Klasa PriorityQueue.............................................................................................................489 Klasa EnumSet .....................................................................................................................490 Dostęp do kolekcji za pomocą iteratora ...................................................................................490 Korzystanie z iteratora Iterator ..........................................................................................492 Pętla typu for-each jako alternatywa dla iteratora ...........................................................494 Przechowywanie w kolekcjach własnych klas .........................................................................495 Interfejs RandomAccess ............................................................................................................496 Korzystanie z map ......................................................................................................................496 Interfejsy map.......................................................................................................................497 Klasy map..............................................................................................................................499 Komparatory ..............................................................................................................................505 Wykorzystanie komparatora...............................................................................................505 Algorytmy kolekcji.....................................................................................................................508 Klasa Arrays ...............................................................................................................................513 Dlaczego kolekcje sa sparametryzowane?................................................................................516 Starsze klasy i interfejsy ............................................................................................................519 Interfejs wyliczeń..................................................................................................................520 Klasa Vector..........................................................................................................................520 Klasa Stack............................................................................................................................524 Klasa Dictionary ...................................................................................................................526 Klasa Hashtable....................................................................................................................527 Klasa Properties ...................................................................................................................530 Wykorzystanie metod store() i load()..................................................................................533 Ostatnie uwagi na temat kolekcji..............................................................................................535
Rozdział 18. Pakiet java.util, czesc 2. — pozostałe klasy użytkowe ....................................537 Klasa StringTokenizer ...............................................................................................................537 Klasa BitSet.................................................................................................................................539 Klasa Date ...................................................................................................................................542 Klasa Calendar ...........................................................................................................................543 Klasa GregorianCalendar .........................................................................................................546 Klasa TimeZone..........................................................................................................................548 Klasa SimpleTimeZone..............................................................................................................548 Klasa Locale................................................................................................................................550 Klasa Random ............................................................................................................................551 Klasa Observable........................................................................................................................553 Interfejs Observer ................................................................................................................554 Przykład użycia interfejsu Observer ..................................................................................555 Klasy Timer i TimerTask ..........................................................................................................557 Klasa Currency...........................................................................................................................560 Klasa Formatter .........................................................................................................................561 Konstruktory klasy Formatter............................................................................................561 Metody klasy Formatter ......................................................................................................562 Podstawy formatowania.......................................................................................................562 Formatowanie tekstów i znaków.........................................................................................565
12
Java. Kompendium programisty Formatowanie liczb ..............................................................................................................565 Formatowanie daty i godziny ..............................................................................................567 Specyfikatory %n i %% ......................................................................................................567 Określanie minimalnej szerokości pola ..............................................................................569 Określanie precyzji...............................................................................................................570 Używanie znaczników formatów.........................................................................................571 Wyrównywanie danych wyjściowych .................................................................................572 Znaczniki spacji, plusa, zera i nawiasów............................................................................573 Znacznik przecinka ..............................................................................................................574 Znacznik # .............................................................................................................................574 Opcja wielkich liter ..............................................................................................................574 Stosowanie indeksu argumentu...........................................................................................575 Metoda printf() w Javie........................................................................................................576 Klasa Scanner .............................................................................................................................577 Konstruktory klasy Scanner................................................................................................577 Podstawy skanowania ..........................................................................................................578 Kilka przykładów użycia klasy Scanner.............................................................................580 Ustawianie separatorów.......................................................................................................585 Pozostałe elementy klasy Scanner .......................................................................................586 Podpakiety pakietu java.util......................................................................................................587 java.util.concurrent, java.util.concurrent.atomic oraz java.util.concurrent.locks.........588 java.util.jar............................................................................................................................588 java.util.logging ....................................................................................................................588 java.util.prefs ........................................................................................................................588 java.util.regex........................................................................................................................588 java.util.zip............................................................................................................................588
Rozdział 19. Operacje wejścia-wyjścia: analiza pakietu java.io ...........................................589 Dostępne w Javie klasy i interfejsy obsługujące operacje wejścia-wyjścia ...........................590 Klasa File.....................................................................................................................................590 Katalogi .................................................................................................................................593 Stosowanie interfejsu FilenameFilter .................................................................................594 Alternatywna metoda listFiles() ..........................................................................................595 Tworzenie katalogów ...........................................................................................................596 Interfejsy Closeable i Flushable ................................................................................................596 Klasy strumienia.........................................................................................................................597 Strumienie bajtów ......................................................................................................................597 Klasa InputStream ...............................................................................................................598 Klasa OutputStream ............................................................................................................598 Klasa FileInputStream.........................................................................................................599 Klasa FileOutputStream ......................................................................................................601 Klasa ByteArrayInputStream .............................................................................................602 Klasa ByteArrayOutputStream ..........................................................................................603 Filtrowane strumienie bajtów .............................................................................................605 Buforowane strumienie bajtów ...........................................................................................605 Klasa SequenceInputStream ...............................................................................................609 Klasa PrintStream................................................................................................................610 Klasy DataOutputStream i DataInputStream ...................................................................613 Klasa RandomAccessFile.....................................................................................................615 Strumienie znaków .....................................................................................................................616 Klasa Reader.........................................................................................................................616 Klasa Writer .........................................................................................................................616 Klasa FileReader ..................................................................................................................616 Klasa FileWriter ...................................................................................................................618
Spis treści
13 Klasa CharArrayReader .....................................................................................................619 Klasa CharArrayWriter ......................................................................................................620 Klasa BufferedReader..........................................................................................................621 Klasa BufferedWriter ..........................................................................................................623 Klasa PushbackReader ........................................................................................................623 Klasa PrintWriter.................................................................................................................624 Stosowanie operacji wejścia-wyjścia na strumieniach ............................................................626 Usprawnienie metody wc() przez zastosowanie klasy StreamTokenizer.........................627 Serializacja..................................................................................................................................629 Interfejs Serializable ............................................................................................................630 Interfejs Externalizable .......................................................................................................630 Interfejs ObjectOutput ........................................................................................................631 Klasa ObjectOutputStream.................................................................................................631 Interfejs ObjectInput ...........................................................................................................633 Klasa ObjectInputStream....................................................................................................633 Przykład serializacji .............................................................................................................634 Korzyści wynikające ze stosowania strumieni .........................................................................636
Rozdział 20. Obsługa sieci .......................................................................................................637 Podstawy obsługi sieci ................................................................................................................637 Przegląd gniazd.....................................................................................................................638 Klient-serwer ........................................................................................................................638 Gniazda zastrzeżone.............................................................................................................639 Serwery pośredniczące.........................................................................................................639 Obsługa adresów internetowych .........................................................................................640 Java i usługi sieciowe..................................................................................................................641 Klasy i interfejsy obsługujące komunikacje sieciowa........................................................641 Klasa InetAddress ......................................................................................................................642 Metody fabryczne .................................................................................................................642 Metody klasy .........................................................................................................................643 Klasy Inet4Address oraz Inet6Address ....................................................................................644 Gniazda klientów TCP/IP..........................................................................................................644 Przykład użycia usługi whois...............................................................................................645 URL .............................................................................................................................................646 Format ...................................................................................................................................647 Klasa URLConnection ...............................................................................................................648 Gniazda serwerów TCP/IP ........................................................................................................651 Serwer pośredniczący protokołu HTTP z pamięcią podręczną .............................................651 Kod źródłowy........................................................................................................................652 Datagramy...................................................................................................................................672 Klasa DatagramPacket ........................................................................................................672 Przesyłanie datagramów pomiędzy serwerem a klientem ................................................673 Klasa URI....................................................................................................................................674 Nowe klasy środowiska J2SE 5 .................................................................................................675 Rozdział 21. Klasa Applet .........................................................................................................677 Podstawy apletów .......................................................................................................................677 Klasa Applet..........................................................................................................................678 Architektura apletu....................................................................................................................680 Szkielet apletu.............................................................................................................................681 Inicjalizacja i przerywanie działania apletu ......................................................................682 Przysłanianie metody update()............................................................................................684 Proste metody wyświetlania składników apletów....................................................................684 Żądanie ponownego wyświetlenia .............................................................................................686 Prosty aplet z paskiem reklamowym ..................................................................................688
14
Java. Kompendium programisty Wykorzystywanie paska stanu ..................................................................................................690 Znacznik APPLET języka HTML ............................................................................................691 Przekazywanie parametrów do apletów...................................................................................692 Udoskonalenie apletu z paskiem reklamowym ..................................................................694 Metody getDocumentBase() i getCodeBase() ...........................................................................695 Interfejs AppletContext i metoda showDocument()................................................................696 Interfejs AudioClip ....................................................................................................................698 Interfejs AppletStub...................................................................................................................698 Wyświetlanie danych wyjściowych na konsoli.........................................................................698
Rozdział 22. Obsługa zdarzeń ..................................................................................................699 Dwa mechanizmy obsługi zdarzeń ............................................................................................699 Model obsługi zdarzeń oparty na ich delegowaniu..................................................................700 Zdarzenia ..............................................................................................................................700 Źródła zdarzeń......................................................................................................................701 Obiekty nasłuchujące zdarzeń.............................................................................................702 Klasy zdarzeń .............................................................................................................................702 Klasa ActionEvent................................................................................................................704 Klasa AdjustmentEvent .......................................................................................................704 Klasa ComponentEvent .......................................................................................................705 Klasa ContainerEvent..........................................................................................................706 Klasa FocusEvent .................................................................................................................706 Klasa InputEvent..................................................................................................................707 Klasa ItemEvent ...................................................................................................................708 Klasa KeyEvent ....................................................................................................................709 Klasa MouseEvent................................................................................................................710 Klasa MouseWheelEvent .....................................................................................................712 Klasa TextEvent ...................................................................................................................713 Klasa WindowEvent.............................................................................................................713 Źródła zdarzeń............................................................................................................................714 Interfejsy nasłuchujące zdarzeń................................................................................................715 Interfejs ActionListener.......................................................................................................715 Interfejs AdjustmentListener ..............................................................................................715 Interfejs ComponentListener ..............................................................................................716 Interfejs ContainerListener.................................................................................................717 Interfejs FocusListener ........................................................................................................717 Interfejs ItemListener ..........................................................................................................717 Interfejs KeyListener ...........................................................................................................717 Interfejs MouseListener.......................................................................................................717 Interfejs MouseMotionListener ..........................................................................................718 Interfejs MouseWheelListener ............................................................................................718 Interfejs TextListener ..........................................................................................................718 Interfejs WindowFocusListener..........................................................................................718 Interfejs WindowListener....................................................................................................719 Stosowanie modelu delegowania zdarzeń.................................................................................719 Obsługa zdarzeń generowanych przez mysz......................................................................720 Obsługa zdarzeń generowanych przez klawiaturę ............................................................722 Klasy adapterów.........................................................................................................................726 Klasy wewnętrzne.......................................................................................................................728 Anonimowa klasa wewnętrzna ............................................................................................729 Rozdział 23. Wprowadzenie do AWT: praca z oknami, grafika i tekstem ............................731 Klasy AWT..................................................................................................................................732 Podstawy okien ...........................................................................................................................734 Klasa Component .................................................................................................................734 Klasa Container....................................................................................................................735
Spis treści
15 Klasa Panel............................................................................................................................735 Klasa Window.......................................................................................................................735 Klasa Frame..........................................................................................................................736 Klasa Canvas ........................................................................................................................736 Praca z oknami typu Frame ......................................................................................................736 Ustawianie wymiarów okna.................................................................................................737 Ukrywanie i wyświetlanie okna ...........................................................................................737 Ustawianie tytułu okna ........................................................................................................737 Zamykanie okna typu Frame ..............................................................................................737 Tworzenie okna typu Frame z poziomu apletu........................................................................738 Obsługa zdarzeń w oknie typu Frame ................................................................................740 Tworzenie programu wykorzystującego okna.........................................................................743 Wyświetlanie informacji w oknie ..............................................................................................745 Praca z grafika............................................................................................................................746 Rysowanie prostych..............................................................................................................746 Rysowanie prostokątów .......................................................................................................747 Rysowanie elips, kół i okręgów............................................................................................748 Rysowanie łuków ..................................................................................................................749 Rysowanie wielokątów .........................................................................................................750 Dostosowywanie rozmiarów obiektów graficznych...........................................................751 Praca z klasa Color.....................................................................................................................752 Metody klasy Color ..............................................................................................................753 Ustawianie bieżącego koloru kontekstu graficznego .........................................................754 Aplet demonstrujący zastosowanie klasy Color ................................................................754 Ustawianie trybu rysowania ......................................................................................................755 Praca z czcionkami.....................................................................................................................757 Określanie dostępnych czcionek .........................................................................................757 Tworzenie i wybieranie czcionek ........................................................................................759 Uzyskiwanie informacji o czcionkach.................................................................................761 Zarządzanie tekstowymi danymi wyjściowymi z wykorzystaniem klasy FontMetrics.....................................................................................762 Wyświetlanie tekstu w wielu wierszach ..............................................................................763 Wyśrodkowanie tekstu.........................................................................................................766 Wyrównywanie wielowierszowych danych tekstowych ....................................................767
Rozdział 24. Stosowanie kontrolek AWT, menadżerów układu graficznego oraz menu ..............................................................................................................771 Podstawy kontrolek....................................................................................................................772 Dodawanie i usuwanie kontrolek ........................................................................................772 Odpowiadanie na zdarzenia kontrolek...............................................................................772 Etykiety .......................................................................................................................................773 Stosowanie przycisków...............................................................................................................774 Obsługa zdarzeń przycisków ...............................................................................................775 Stosowanie pól wyboru ..............................................................................................................777 Obsługa zdarzeń pól wyboru...............................................................................................778 CheckboxGroup .........................................................................................................................780 Kontrolki list rozwijanych.........................................................................................................782 Obsługa zdarzeń list rozwijanych .......................................................................................783 Stosowanie list.............................................................................................................................784 Obsługa zdarzeń generowanych przez listy .......................................................................786 Zarządzanie paskami przewijania ............................................................................................787 Obsługa zdarzeń generowanych przez paski przewijania ................................................789 Stosowanie kontrolek typu TextField .......................................................................................790 Obsługa zdarzeń generowanych przez kontrolkę TextField ............................................792 Stosowanie kontrolek typu TextArea .......................................................................................794
16
Java. Kompendium programisty Wprowadzenie do menadżerów układu graficznego komponentów......................................795 FlowLayout ...........................................................................................................................797 BorderLayout........................................................................................................................799 Stosowanie obramowan .......................................................................................................800 GridLayout ...........................................................................................................................801 Klasa CardLayout ................................................................................................................803 Klasa GridBagLayout ..........................................................................................................806 Menu i paski menu .....................................................................................................................811 Okna dialogowe ..........................................................................................................................817 FileDialog ....................................................................................................................................822 Obsługa zdarzeń przez rozszerzanie dostępnych komponentów AWT.................................823 Rozszerzanie kontrolki Button............................................................................................824 Rozszerzanie kontrolki Checkbox.......................................................................................825 Rozszerzanie komponentu grupy pól wyboru....................................................................826 Rozszerzanie kontrolki Choice............................................................................................827 Rozszerzanie kontrolki List .................................................................................................828 Rozszerzanie kontrolki Scrollbar........................................................................................829 Poznawanie środowiska kontrolek, menu i menadżerów układu ..........................................830
Rozdział 25. Obrazy ..................................................................................................................831 Formaty plików ..........................................................................................................................832 Podstawy przetwarzania obrazów: tworzenie, wczytywanie i wyświetlanie .........................832 Tworzenie obiektu obrazu ...................................................................................................832 Wczytywanie obrazu ............................................................................................................833 Wyświetlanie obrazu ............................................................................................................833 Interfejs ImageObserver............................................................................................................835 Podwójne buforowanie...............................................................................................................836 Klasa MediaTracker ..................................................................................................................839 Interfejs ImageProducer............................................................................................................842 Klasa MemoryImageSource ................................................................................................843 Interfejs ImageConsumer..........................................................................................................844 Klasa PixelGrabber..............................................................................................................845 Klasa ImageFilter.......................................................................................................................847 Klasa CropImageFilter ........................................................................................................848 Klasa RGBImageFilter ........................................................................................................850 Animacja poklatkowa ................................................................................................................861 Dodatkowe klasy obsługujące obrazy.......................................................................................863 Rozdział 26. Narzędzia współbieżności ..................................................................................865 Pakiety interfejsu Concurrent API...........................................................................................866 java.util.concurrent ..............................................................................................................866 java.util.concurrent.atomic..................................................................................................867 java.util.concurrent.locks ....................................................................................................867 Korzystanie z obiektów służących do synchronizacji..............................................................868 Semaphore.............................................................................................................................868 Klasa CountDownLatch.......................................................................................................874 CyclicBarrier ........................................................................................................................875 Klasa Exchanger...................................................................................................................878 Korzystanie z egzekutorów........................................................................................................880 Przykład prostego egzekutora .............................................................................................881 Korzystanie z interfejsów Callable i Future.......................................................................883 Obiekty typu TimeUnit ..............................................................................................................885 Kolekcje współbieżne .................................................................................................................887 Blokady........................................................................................................................................887 Operacje atomowe......................................................................................................................890 Pakiet Concurrency Utilities a tradycyjne metody języka Java ............................................891
Spis treści
17
Rozdział 27. System NIO, wyrażenia regularne i inne pakiety ..............................................893 Pakiety głównego API języka Java ...........................................................................................893 System NIO .................................................................................................................................895 Podstawy systemu NIO ........................................................................................................896 Zestawy znaków i selektory .................................................................................................898 Korzystanie z systemu NIO .................................................................................................899 Czy system NIO jest przyszłością operacji wejścia-wyjścia?............................................905 Przetwarzanie wyra)en regularnych.........................................................................................905 Klasa Pattern ........................................................................................................................906 Klasa Matcher.......................................................................................................................906 Składnia wyrażeń regularnych............................................................................................907 Przykład wykorzystania dopasowywania do wzorca ........................................................908 Typy operacji dopasowywania do wzorca..........................................................................913 Przegląd wyrażen regularnych............................................................................................914 Refleksje ......................................................................................................................................914 Zdalne wywoływanie metod (RMI)...........................................................................................917 Prosta aplikacja typu klient-serwer wykorzystująca RMI ...............................................918 Formatowanie tekstu..................................................................................................................921 Klasa DateFormat ................................................................................................................921 Klasa SimpleDateFormat.....................................................................................................923
Część III
Pisanie oprogramowania w języku Java ...................................... 927
Rozdział 28. Java Beans ...........................................................................................................929 Czym jest komponent typu Java Bean? ...................................................................................929 Zalety komponentów Java Beans..............................................................................................930 Introspekcja ................................................................................................................................930 Wzorce właściwości ..............................................................................................................931 Wzorce projektowe dla zdarzeń..........................................................................................932 Metody i wzorce projektowe................................................................................................932 Korzystanie z interfejsu BeanInfo ......................................................................................933 Właściwości ograniczone ...........................................................................................................933 Trwałość ......................................................................................................................................934 Interfejs Customizer...................................................................................................................934 Interfejs Java Beans API ...........................................................................................................934 Klasa Introspector................................................................................................................936 Klasa PropertyDescriptor....................................................................................................937 Klasa EventSetDescriptor....................................................................................................937 Klasa MethodDescriptor......................................................................................................937 Przykład komponentu typu Bean .............................................................................................937 Rozdział 29. Przewodnik po pakiecie Swing ..........................................................................941 Klasa JApplet..............................................................................................................................942 Klasy JFrame i JComponent.....................................................................................................943 Ikony i etykiety ...........................................................................................................................943 Problemy z watkami...................................................................................................................945 Pola tekstowe ..............................................................................................................................947 Przyciski ......................................................................................................................................948 Klasa JButton .......................................................................................................................949 Pola wyboru ..........................................................................................................................951 Przyciski opcji.......................................................................................................................953 Listy kombinowane ....................................................................................................................955 Okna z zakładkami.....................................................................................................................957 Okna przewijane ........................................................................................................................959 Drzewa.........................................................................................................................................961 Przegląd pakietu Swing..............................................................................................................965
18
Java. Kompendium programisty
Rozdział 30. Serwlety ................................................................................................................967 Podstawy .....................................................................................................................................967 Cykl życia serwletu.....................................................................................................................968 Korzystanie ze środowiska Tomcat ..........................................................................................969 Przykład prostego serwletu .......................................................................................................970 Tworzenie i kompilacja kodu źródłowego serwletu ..........................................................971 Uruchamianie środowiska Tomcat .....................................................................................971 Uruchamianie przeglądarki i generowanie ządania..........................................................972 Interfejs Servlet API ..................................................................................................................972 Pakiet javax.servlet ....................................................................................................................972 Interfejs Servlet ....................................................................................................................973 Interfejs ServletConfig.........................................................................................................974 Interfejs ServletContext.......................................................................................................974 Interfejs ServletRequest.......................................................................................................975 Interfejs ServletResponse ....................................................................................................975 Klasa GenericServlet............................................................................................................976 Klasa ServletInputStream ...................................................................................................976 Klasa ServletOutputStream ................................................................................................977 Klasy wyjątków związanych z serwletami..........................................................................977 Odczytywanie parametrów serwletu ........................................................................................977 Pakiet javax.servlet.http ............................................................................................................979 Interfejs HttpServletRequest...............................................................................................979 Interfejs HttpServletResponse ............................................................................................979 Interfejs HttpSession............................................................................................................980 Interfejs HttpSessionBindingListener ................................................................................981 Klasa Cookie .........................................................................................................................981 Klasa HttpServlet .................................................................................................................983 Klasa HttpSessionEvent.......................................................................................................983 Klasa HttpSessionBindingEvent .........................................................................................984 Obsługa żądań i odpowiedzi HTTP ..........................................................................................985 Obsługa żądań HTTP GET .................................................................................................985 Obsługa żądań HTTP POST ...............................................................................................986 Korzystanie ze znaczników kontekstu użytkownika ...............................................................988 Śledzenie sesji..............................................................................................................................990
Część IV
Zastosowanie Javy w praktyce ..................................................... 993
Rozdział 31. Aplety i serwlety finansowe ...............................................................................995 Znajdowanie raty pożyczki........................................................................................................996 Pola apletu.............................................................................................................................999 Metoda init() .......................................................................................................................1000 Metoda actionPerformed() ................................................................................................1002 Metoda paint() ....................................................................................................................1002 Metoda compute()...............................................................................................................1003 Znajdowanie przyszłej wartości inwestycji............................................................................1004 Znajdowanie wkładu początkowego wymaganego do uzyskania przyszłej wartości inwestycji .........................................................................1007 Znalezienie inwestycji początkowej wymaganej do uzyskania odpowiedniej emerytury ................................................................................1011 Znajdowanie maksymalnej emerytury dla danej inwestycji ................................................1015 Obliczenie pozostałej kwoty do spłaty kredytu......................................................................1019 Tworzenie serwletów finansowych..........................................................................................1022 Konwersja apletu RegPay do serwletu .............................................................................1023 Serwlet RegPayS.................................................................................................................1023 Możliwe rozszerzenia ...............................................................................................................1026
Spis treści
19
Rozdział 32. Wykonanie menedżera pobierania plików w Javie ........................................1027 Sposoby pobierania plików z internetu ..................................................................................1028 Omówienie programu ..............................................................................................................1028 Klasa Download........................................................................................................................1029 Zmienne pobierania ...........................................................................................................1033 Konstruktor klasy...............................................................................................................1033 Metoda download().............................................................................................................1033 Metoda run() .......................................................................................................................1033 Metoda stateChanged() ......................................................................................................1037 Metody akcesorowe i działań.............................................................................................1037 Klasa ProgressRenderer ..........................................................................................................1037 Klasa DownloadsTableModel..................................................................................................1038 Metoda addDownload() .....................................................................................................1040 Metoda clearDownload()....................................................................................................1041 Metoda getColumnClass() .................................................................................................1041 Metoda getValueAt() ..........................................................................................................1041 Metoda update() .................................................................................................................1042 Klasa DownloadManager ........................................................................................................1042 Zmienne klasy DownloadManager ...................................................................................1047 Konstruktor klasy...............................................................................................................1048 Metoda verifyUrl() .............................................................................................................1048 Metoda tableSelectionChanged() ......................................................................................1049 Metoda updateButtons() ....................................................................................................1049 Obsługa zdarzeń akcji........................................................................................................1050 Kompilacja i uruchamianie programu...................................................................................1051 Rozszerzanie możliwości programu........................................................................................1051
Dodatki ....... ...................................................................................................... 1053 Dodatek A
Korzystanie z komentarzy dokumentacyjnych Javy ......................................1055 Znaczniki komentarzy dokumentacyjnych ............................................................................1055 Znacznik @author..............................................................................................................1056 Znacznik {@code} ..............................................................................................................1057 Znacznik @deprecated ......................................................................................................1057 Znacznik {@docRoot}........................................................................................................1057 Znacznik @exception .........................................................................................................1057 Znacznik {@inheritDoc} ....................................................................................................1057 Znacznik {@link} ...............................................................................................................1057 Znacznik {@linkplain}.......................................................................................................1058 Znacznik {@literal}............................................................................................................1058 Znacznik @param ..............................................................................................................1058 Znacznik @return ..............................................................................................................1058 Znacznik @see ....................................................................................................................1058 Znacznik @serial................................................................................................................1059 Znacznik @serialData........................................................................................................1059 Znacznik @serialField .......................................................................................................1059 Znacznik @since.................................................................................................................1059 Znacznik @throws .............................................................................................................1059 Znacznik {@value} .............................................................................................................1059 Znacznik @version.............................................................................................................1060 Ogólna postać komentarzy dokumentacyjnych.....................................................................1060 Wynik działania narzędzia javadoc ........................................................................................1060 Przykład korzystający z komentarzy dokumentacyjnych ....................................................1061 Skorowidz ..........................................................................................................1063
Podziękowania Szczególne podziękowania należą się Patrickowi Naughtonowi — jednemu z twórców języka Java. Pomógł mi on w napisaniu pierwszego wydania tej książki. Na przykład większość materiałów z rozdziałów 19., 20. i 25. została oryginalnie dostarczona przez Patricka. Jego rady, wskazówki i energia z pewnością przyczyn iły się do sukcesu książki. Podziękowania należą się również Jo e’owi O ’Neilowi, który dostarczył początkowe szkice rozdziałów od 27. do 30. Joe pomagał mi przy kilku książkach i jak zawsze jestem mu za to niezmiernie wdzięczny. Dziękuję Jamesowi Holmesowi za dostarczenie rozdziału 31. James jest niezwykłym program istą i autorem. Jest współautorem książki Java. Sztuka programowania oraz autorem książki Struts: The Complete Reference. HERBERT SCHILDT 1 listopada 2004 Mahomet w Illinois, USA
O Autorze H erb ert Schildt jest autorytetem w kwestii języków programowania Java, C, C++ i CU, a także doskonałym programistą aplikacji systemu Windows. Jego książki o programo waniu sprzedały się ju ż w ponad trzech milionach egzemplarzy na całym świecie i zo stały przetłumaczone na wszystkie główne języki. Jest autorem takich bestsellerów jak: Java. Sztuka programowania i Java 2: A Beginner's Guide, C + + : The Complete Refe rence , C + + : A Beginner’s Guide, CU: The Complete Reference i CU: A Beginner’s Guide. Schildt otrzymał tytuł magistra informatyki na Uniwersytecie Illinois. Warto odwiedzić jego witrynę internetową — www.HerhSchildt.com.
Przedmowa Ta książka to wszechstronny podręcznik języka Java i jej podstawowych bibliotek, włączając w to nowe elementy, które pojawiły się w wersji J2SE 5. J2SE to skrót od Java 2 Standard Edition, czyli systemu programistycznego wymyślonego przez firmę Sun. J2SE definiuje formę i cechy języka Java. Liczba 5 na końcu oznacza numer wersji — wersja 5. jest aktualnie najnowszą odsłoną języka. Wydanie J2SE 5 dodaje do języka Java wiele nowych elementów. Elementy takie jak szablony klas, automatyczne otaczanie obiektami typów podstawowych i wyliczenia były oczekiwane przez programistów tego języka już od wielu lat. Inne elementy, na przykład metadane, to pomysły, które mogą w przyszłości zwiększyć możliwości całego środowiska programistycznego. Ogólnie nowe elementy języka zmieniają sposób pisa nia kodu w Javie. Wiele technik wykorzystywanych przez programistów w ostatnich latach dezaktualizuje się, gdyż pojawiają się nowe, bardziej elastyczne konstrukcje. W trakcie prac nad nowym wydaniem, firma Sun stosowała nazwę kodową ,,'ł iger” (tygrys). Z pewnością wybór nazwy nie był przypadkowy. W zasadzie same podstawy języka po raz pierwszy zmieniają się w tak znaczący sposób od momentu pierwszego wydania 10 lat temu. Podobnie jak oryginalne wydanie języka Java zrewolucjonizowało sposób programowania na potrzeby intemetu, tak J2SE 5 rewolucjonizuje sposób pisania i projektowania programów komputerowych. Książka opisuje wszystkie aspekty związane z tak zwaną „nową Javą”.
Książka dla wszystkich programistów Książkę kieruję dla wszystkich programistów, niezależnie od tego, czy dopiero zaczynają swoją przygodę z tym językiem, czy są profesjonalistami. Początkujący znajdą dokładne omówienie wszystkich elementów języka wraz z wieloma pożytecznymi przykładami. Szczegółowe opisy bardziej zaawansowanych funkcji języka oraz podstawowych bibliotek, a także omówienie nowych elementów wydania J2SE 5 z pewnością przypadnie do gustu profesjonalistom. Książka ma również za zadanie służyć jako podręczny zbiór infor macji o wszystkich aspektach języka Java w trakcie pisania własnych programów.
24
Java. Kompendium programisty
Zawartość książki Książka omawia wszystkie aspekty związane z programowaniem w języku Java. Część I stanowi doskonałe wprowadzenie do języka. Zaczyna się od całkowitych podstaw, czyli omówienia typów danych, struktur sterujących i klas. Następnie pojawiają się opis> mechanizmu obsługi wyjątków, obsługi wielowątkowości, pakietów i interfejsów. Oczy wiście w części I zostają doskonale omówione nowe elementy języka, takie jak szablony klas (nazywane też typami sparametryzow'anymi), metadane, wyliczenia i automatyczne otaczanie typów prostych. Część II omawia standardową bibliotekę Javy. W zasadzie główna siła języka Java drzemie w jego standardowej bibliotece. W tej części zajmujemy się ciągami znaków, wejściem i wyjściem, siecią, standardowymi narzędziami pomocniczymi, kolekcjami elementów, apletami, kontrolkami związanymi z graficznym interfejsem użytkownika oraz obróbką obrazów. Nowa biblioteka współbieżności (dodana w J2SE 5) została omówiona w rozdziale 26. Część III skupia się na pozostałych elementach związanych z pisaniem programów w środowisku Java: Java Beans, serwletach i bibliotece Swing. Część IV zawiera dwa rozdziały obrazujące sposób pisania programów w języku Java. Pierwszy z tych rozdziałów opisuje tworzenie zarządcy pobierania, który pozwala po bierać pliki z intemetu. Jednym z ciekawych elementów tego programu jest możliwość zatrzymania i późniejszego wznowienia pobierania pliku. Drugi rozdział pokazuje spo sób tworzenia kilku apletów wykonujących różne popularne obliczenia finansowe, na przykład obliczanie równych rat pożyczki lub minimalnej kwoty inwestycji, by na eme ryturze otrzymywać wybraną kwotę przez określony czas. Co więcej, w rozdziale wska zuję również, w jaki sposób zamienić te aplety na serwlety. Oba rozdziały są zmodyfiko wanymi wersjami rozdziałów z książki Java. Sztuka programowania , którą napisałem wraz z Jamesem Holmesem.
Nie zapomnij: kody źródłowe do pobrania Pamiętaj, że kody źródłowe wszystkich przykładów przedstawionych w książce są dostępne całkowicie bezpłatnie pod adresem ftp://ftp.helion.pl/przyklady/javakp zip.
Konwencje typograficzne To je st uwaga. Zawiera ciekawe dodatkowe informacje.
Przedmowa
25
To je s t wskazówka. Zawiera przydatne informacje związane z tematem i często prezentuje inne ważne pojęcia lub praktyki.
To je st ostrzeżenie. Pomaga rozwiązać problemy i uniknąć ich w przyszłości oraz ostrzega przed ewentualnym niepowodzeniem.
Warto przeczytać Niniejsza książka to tylko jedna z wielu pozycji na temat programowania napisanych przez Herberta Schildta. Oto lista innych książek, które mogą się okazać wielce inte resujące. Aby poznać tajniki programowania w języku Java, polecamy następujące książki. ♦ Java 2: A Beginner's Guide ♦ Java. Sztuka programowania Aby dobrze zapoznać się z językiem C++, warto przeczytać niżej wymienione książki. ♦ C ++. The Complete Reference ♦ C++: A Beginner's Guide ♦ C++. Sztuka programowania ♦ Teach Yourself C++ ♦ C++ From the Ground Up ♦ STL Programming From Ground Up Aby nauczyć się programowania w języku C#, sugerujemy przeczytanie następujących książek. ♦ CU: The Complete Reference ♦ C++: A Beginner's Guide Aby lepiej poznać język C stanowiący podstawę wszystkich nowoczesnych języków programowania, warto zapoznać się z następującymi książkami. ♦ C: The Complete Reference ♦ Teach Yourself C Gdy szybko potrzebujesz sprawdzonych odpowiedzi, zajrzyj do książek autorstwa Her berta Schildta. uznanego autorytetu w kwestii programowania komputerów.
26
Java. Kompendium programisty
Część I
Język Java Rozdział 1. — „Historia i ewolucja języka Java” Rozdział 2. — „Podstawy języka Java” Rozdział 3. — „Typy danych, zmienne i tablice” Rozdział 4. — „Operatory” Rozdział 5. — „Struktury sterujące” Rozdział 6. — „Wprowadzenie do klas” Rozdział 7. — „Dokładniejsze omówienie metod i klas” Rozdział 8. — „Dziedziczenie” Rozdział 9. — „Pakiety i interfejsy” Rozdział 10. — „Obsługa wyjątków” Rozdział 11.— „Programowanie wielowątkowe” Rozdział 12.— „Wyliczenia, automatyczne otaczanie typów prostych i inetadane” Rozdział 13.— „Wejście-wyjście, aplety i inne tem aty” Rozdział 14. — „Typy sparainetryzowane”
Rozdział
1.
Historia i ewolucja języka Java Aby zrozumieć język Java, trzeba najpierw dobrze zrozumieć powody jego powstania, modelujące go siły i spadek, który odziedziczył. Podobnie jak wcześniejsze popularne języki programowania komputerów, Java jest mieszanką najlepszych elementów swoich poprzedników, połączoną z innowacyjną koncepcją związaną z jej unikatową misją. Kolejne rozdziały niniejszej książki zajmują się praktycznymi aspektami języka — składnią, bibliotekami i aplikacjami. Ten rozdział jest inny, ponieważ wyjaśnia genezę pow stania języka Java, pomaga zrozumieć jego znaczenie i rozwój przez wszystkie kolejne lata. Choć język Java jest bez wątpienia ściśle powiązany ze środowiskiem internetowym, nie należy zapominać o tym, iż Java to przede wszystkim język programowania. Rozwój istniejących języków programowania, a także tworzenie nowych języków wynika z dwóch podstawowych powodów: ♦ chęci dostosowania się do zmieniającego się środowiska i zastosowań. ♦ implementacji udoskonaleń związanych ze sztuką programowania. Jak się przekonasz, oba przedstawione powody miały bardzo podobny wpływ na two rzenie języka Java.
Rodowód Javy Java jest powiązana z językiem C++, który wywodzi się bezpośrednio z języka C. Innymi słowy, Java odziedziczyła swój charakter po tych dwóch językach. Z języka C zaczerpnęła składnię, a z C++ większość elementów związanych z obiektowością. Co więcej, kilka aspektów charakterystyki języka Java powstało w odpowiedzi na jej poprzedników. To jednak nie wszystko — autorzy wyciągnęli wnioski z doświadczeń z wieloma in nymi językami programowania stosowanymi przez kilka ostatnich dziesięcioleci. len
30
Część I ♦ Język Java
podrozdział opisuje ciąg zdarzeń i sił, które doprowadziły do powstania języka Java. Jak się przekonasz, każda innowacja w modelu języka ma na celu rozwiązanie pod stawowego problemu, którego nie udawało się rozwiązać poprzednim językom. Java nie jest tu wyjątkiem.
Narodziny nowoczesnego języka — C Język C zelektryzował cały komputerowy świat. Jego siły oddziaływania nie należy lekceważyć, gdyż w znaczący sposób zmienił podejście do programowania. Powsta nie języka C w ynikało z potrzeby zastosow ania strukturalnego i wydajnego języka wysokiego poziomu, który zastąpiłby kod asemblerowy przy pisaniu programów syste mowych. Gdy projektuje się nowy język programowania, trzeba podjąć pewne ważne decyzje, odpowiadając sobie na poniższe pytania: ♦ Łatwość użycia czy siła? ♦ Bezpieczeństwo czy wydajność? ♦ Sztywność czy rozszerzalność? Przed językiem C programiści musieli wybierać te języki, które były lepiej dostosowane do konkretnych zadań. Na przykład język FORTRAN bardzo dobrze nadawał się do pisania wydajnych programów matematycznych, ale nie radził sobie dobrze z pro gramami systemowymi. Język BASIC był bardzo prosty w nauce, ale nie miał dużych możliwości, a brak strukturalności powodował, że nie nadawał się do pisania złożo nych programów. Języki asemblerowe były bardzo wydajne, ale trudno było się ich nauczyć lub wygodnie przenosić między systemami. Poza tym wyszukiwanie błędów w programie asemblerowym jest niezmiernie trudne. Innym typowym problemem wczesnych języków programowania takich jak BASIC, COBOL lub FORTRAN był brak ich dostosowania do zasad programowania struk turalnego — wszystkie te języki silnie korzystały z instrukcji GOTO jak o instrukcji sterującej. Z tego powodu programy napisane w tych językach miały tendencję do charakteryzowania się tak zwanym „kodem spaghetti” — wieloma dziwnymi skokami i rozgałęzieniami, które czyniły zrozumienie działania programu praktycznie niemoż liwym. Istniejące w tamtym czasie języki strukturalne takie jak Pascal nie zostały za projektowane z myślą o wydajności i brakowało im pewnych elementów pozwalających pisać bardzo różne programy. (Standardowe odmiany języka Pascal dostępne w tam tym czasie nie nadawały się do pisania kodu działającego na poziom ie systemu). Innymi słowy, przed pojawieniem się języka C nie istniał żaden inny język na tyle wszechstronny, by mógł być stosowany niemal wszędzie. Potrzeba zaistnienia takiego języka ciągle wzrastała. Na początku lat 70-tych XX wieku rew olucja komputerowa nabierała rozpędu i popyt na oprogramowanie szybko przerastał możliwości ówcze snych programistów. Szczególnie w środowiskach akademickich pojaw iły się próby stworzenia lepszego języka programowania komputerów. W tym czasie zaczęła po jaw iać się inna, bardzo ważna siła — sprzęt komputerowy pow szedniał i wydawało się, że wkrótce osiągnie masę krytyczną. Nie trzymano już kom puterów pod kłódką. Po raz pierwszy programiści mieli praktycznie swobodny dostęp do mocy obliczeniowych
Rozdział 1. ♦ Historia i ewolucja języka Java
31
(swoich komputerów). Taka wolność zachęca do eksperymentowania. Co więcej, umoż liwia programistom pisanie własnych narzędzi. W „przeddzień” powstania języka C wszystko było przygotowane na przyjęcie nowego języka programowania. Język C został wymyślony i po raz pierwszy zaimplementowany przez Dennisa Ritcliiego na komputerze DEC PDP-11, działającym na systemie operacyjnym UNIX. Język C wykorzystał pewne elementy starszego języka o nazwie BCPL. wymyślonego przez Martina Richardsa. Z kolei język BCPL korzystał z elementów języka B autor stw a Kena Thompsona. Język C powstał na początku lat 70-tych XX wieku. Przez wiele lat standard języka C wyznaczał kompilator dostarczany wraz z systemem opera cyjnym UNIX oraz książka The C Programming Language autorstwa Briana Kerninghana i Dennisa Ritchiego (Prentice-Hall, 1978). Język C doczekał się standaryzacji w grudniu 1989 roku, gdy został uznany przez instytut ANSI. Dla wielu powstanie języka C to początek nowoczesnych języków programowania. Nareszcie nowy język programowania rozwiązał większość problemów z poprzedni mi językami. Powstał użyteczny, wydajny i strukturalny język programowania, który nie był trudny do przyswojenia. Co więcej, był to język kierowany do program istów . Przed pojawieniem się języka C języki komputerowe były projektowane pod kątem zagadnień akademickich lub agend rządowych. Język C jest inny. Został zaprojekto wany, zaimplementowany i wykorzystany przez praktykujących programistów, więc odpowiadał ich sposobowi pracy. Poza tym programiści ci dokładnie przetestowali, przemyśleli i poprawili cały język, gdyż miał być ich głównym środowiskiem pracy. Między innymi z tych powodów język C szybko zyskał wielu zapalonych zwolenni ków i został ciepło przyjęty przez całą społeczność programistów. W skrócie, język C został zaprojektowany przez programistów dla programistów. Jak się przekonasz, z języ kiem Java jest podobnie.
Język C++ — następny krok Na przełomie lat 70-tych i 80-tych XX wieku język C stał się dominującym językiem programowania i w zasadzie jest często stosowany aż do dziś. Ponieważ język C okazał się takim sukcesem, zapewne wiele osób stawia sobie pytanie, dlaczego zaistniała po trzeba wymyślenia czegoś nowego? Odpowiedź jest prosta: złożoność. Ponieważ złożo ność pisanych programów' cały czas u'zrastała, zachodziła potrzeba lepszego zarządzania nią. Język C++ był odpowiedzią na tę potrzebę. Aby zrozumieć, dlaczego zarządzanie złożonością leży u podstaw powstania języka C++, rozważmy następujący przykład. Podejście do programowania uległo znaczącej zmianie od momentu wymyślenia kompu tera. Na przykład początkowo programowanie odbywało się ręcznie przez wpisywanie poszczególnych instrukcji maszynowych za pom ocą przełączników. Gdy programy zawierały tylko do kilkuset instrukcji, takie podejście było wystarczająco dobre. Ponieważ jednak programy rozrastały się, wymyślono języki asemblerowe, aby programista nie musiał korzystać z instrukcji maszynowych, a jedynie ich reprezentacji symbolicznej. Ponieważ programy nadal się rozrastały, powstały języki wysokiego poziomu, gdzie programista miał w'ięcej narzędzi ułatwiających zapanowanie nad całością aplikacji.
32
Część I ♦ Język Java
Pierwszym powszechnie stosowanym językiem był oczywiście FORTRAN. Choć oka zał się olbrzymim krokiem w przód, z pewnością nie zachęcał do pisania łatwych i zro zumiałych programów. W latach 60-tych XX wieku pojawiła się koncepcja p ro g ra mowania strukturalnego. Została ona doprowadzona do perfekcji w takich językach jak C. Dzięki językom strukturalnym programista po raz pierwszy mógł stosunkowo łatwo pisać złożone programy. Niestety, nawet programowanie strukturalne ma swoje granice — po przekroczeniu pewnej złożoności programiści przestawali panować nad całym projektem. Na początku lat 80-tych XX wieku wiele projektów osiągnęło gra nice wytrzymałości podejścia strukturalnego. Aby rozwiązać zaistniały problem, po wstała now a koncepcja — program owanie obiektowe (OOP). Podejście obiektowe dokładniej omawiam w dalszej części książki, ale oto krótkie wprowadzenie: programo wanie obiektow e to metodologia pomagająca organizować złożone programy dzięki w ykorzystaniu dziedziczenia, hermetyzacji i polimorfizmu. Choć w ogólnej analizie język C jest doskonałym językiem programowania, ma sto sunkowo niski próg złożoności, z którą potrafi sobie poradzić. Gdy kod programu zawie ra od 25 do 100 tysięcy wierszy, zarządzanie nim w języku C staje się udręką. Język C++ pozw ala przełamać tę barierę, gdyż ułatwia programiście ogarnięcie większych projektów. Język C++ został opracowany przez B jam e’a Stroustrupa w 1979 roku. gdy pracował on w laboratoriach Bella w Murray Hill w stanie New Jersey w USA. Stroustrup po czątkow o nazw ał swój język „C z klasam i”. Jednak w 1983 zmienił nazwę na C++. Język C++ rozszerza C o elementy obiektowe. Ponieważ bazuje na języku C, dziedziczy po nim wszystkie cechy, atrybuty i zalety. Stanowi to główną podstawę sukcesu języka C++. Tw órca języka C++ nie chciał wymyślać całkowicie nowego języka, a jedynie rozszerzyć ju ż istniejący, doskonały język.
Podwaliny języka Java Na przełomie lat 80-tych i 90-tych XX wieku programowanie obiektowe z wykorzy staniem języka C++ znajdowało się u szczytu. Przez moment wydawało się nawet, że programiści znaleźli wreszcie swój idealny język programowania. Ponieważ język C++ łączył w sobie wydajność i składnię języka C, a dodatkowo wprowadzał obiektowość. pozwalał tworzyć bardzo różne programy. Podobnie jak w przeszłości, zm ie nił się jednak układ sił i trzeba było ponownie pchnąć ew olucję języków program o wania do przodu. W ciągu zaledwie kilku lat strony WWW i ogólnie internet stały się bardzo popularne. To zdarzenie zapowiadało kolejną rewolucję w programowaniu.
Powstanie języka Java Języka Java został wymyślony przez Jamesa Goslinga, Patricka Naughtona. Chrisa Wartha, Eda Franka i M ike’a Sheridana w firmie Sun M icrosystem s w roku 1991. Wykonanie pierwszej działającej wersji zajęło 18 miesięcy. Początkowo język nosił nazwę Oak, ale w 1995 roku został przemianowany na Java. M iędzy początkow ą implementacją Oak pod koniec 1992 roku a publicznym ogłoszeniem języka Java
Rozdział 1. ♦ Historia i ewolucja języka Java
33
wiosną 1995 roku wiele osób uczestniczyło w rozwoju języka. Bill Joy, Arthur van Hoff. Jonathan Payne, Frank Yellin i Tim Lindholm to tylko kilka z osób, które najbardziej przyczyniły się do powstania dojrzałej wersji prototypu języka. Zadziwiające je st to, że oryginalnym celem powstania języka Java nie był internet! Główną motywacją było wymyślenie języka niezależnego od platformy sprzętowej (neutralnego architektonicznie), który mógłby służyć do tw orzenia oprogramowania dla różnych urządzeń domowego użytku, na przykład kuchenek mikrofalowych lub pilotów zdalnego sterowania. Nie jest tajemnicą, że jako kontrolerów używa się róż nych rodzajów procesorów. Problem z językami C i C++ (a także wieloma innymi) polega na tym, że zostały one zaprojektowane do kompilowania dla konkretnej plat formy sprzętowej. Choć kod napisany w języku C++ może zostać skompilowany na praktycznie każdym procesorze, wymaga to zastosowania kompilatora dostosowanego do tego procesora. Kompilatory są kosztowe i czasochłonne do napisania. Potrzebne było prostsze i wydajniejsze kosztowo rozwiązanie. Aby znaleźć odpowiednie rozwiąza nie, Gosling i inni zaczęli prace nad przenośnym, niezależnym od platformy sprzęto wej językiem, który mógłby posłużyć do napisania kodu działającego na różnych pro cesorach i systemach operacyjnych. Efektem tych prac było powstanie języka Java. Gdy zaczęła się faza ustalania szczegółów związanych z Javą, pojawił się drugi, bardzo istotny czynnik, który odegrał bardzo ważną kwestię w kierunku rozwoju Javy. Czyn nikiem tym były strony WWW. Gdyby internet nie zaczął dynamicznie rozwijać się dokładnie w tym samym czasie co implementacja Javy, nowy język być może zostałby zepchnięty do pozycji dziwacznego języka programowania elektroniki użytkowej. Wraz z rozwojem intemetu pojawiła się potrzeba nowego rodzaju języka programowania, który byłby przenośny, gdyż internet nie rozróżnia systemów operacyjnych i rodza jów procesorów. Większość programistów na początku swojej kariery uczy się, że w pełni przenośne programy są równie nieuchwytne, co przydatne. Potrzeba tworzenia wydajnych, prze nośnych (niezależnych od platformy sprzętowej) programów jest niemal tak stara jak samo programowanie, ale często pozostawała z boku z powodu innych, pilniejszych problemów. Ponieważ później świat komputerów podzielił się na trzy części repre zentowane przez obozy Intela, Macintosha i systemów uniksowych, potrzeba przeno śności została znacznie ograniczona, bo programiści rzadko opuszczali swoje obozy. Wraz z pojawieniem się intemetu problem przenośności pojawił się ze wzmożoną siłą, ponieważ stanowi uniwersum skupiające różnej maści komputery, systemy operacyjne, procesory itp. Pomimo tej różnorodności użytkownicy chcą uruchamiać ten sam pro gram niezależnie od tego, gdzie się znajdą. To, co dawniej było irytującym, ale mało istotnym problemem, nagle stało się znaczącą niedogodnością. W roku 1993 dla osób pracujących nad projektem języka Java stało się oczywiste, że problemy przenośności związane z kontrolerami umieszczanymi w urządzeniach elektro nicznych będzie można odnaleźć również w przypadku kodu programów dostępnych przez internet. W zasadzie przy niewielkim nakładzie środków można język przewi dziany do rozwiązywania niewielkich problemów zmodyfikować na potrzeby intemetu (duża skala). Innymi słowy, główny nacisk na rozwój języka przeszedł z elektroniki użytkowej na internet. Gdy więc pojawiła się potrzeba języka programowania nieza leżnego od architektury sprzętowej, język Java stał się naturalnym wyborem.
34
Część I ♦ Język Java
Jak ju ż wcześniej wspomniałem, język Java dziedziczy wiele elementów po językach C i C++. Jest to celowe działanie. Projektanci języka wiedzieli, że zachowanie składni podobnej do języka C oraz zasad obiektowości znanych z języka C++ uczynią nowy język bardzo pociągającym dla całej rzeszy doświadczonych programistów języków C i C++. Poza tymi powierzchownymi podobieństwami, Java posiada również wiele innych cech, które przyczyniły się wcześniej do sukcesu języków C i C++. Po pierwsze, została zaprojektowana, przetestowana i udoskonalona przez praktykujących progra m istów . Język wyrósł z potrzeb i doświadczenia ludzi, którzy go wymyślali. Innymi słow y, jest to język programistów. Po drugie, Java jest językiem spójnym. Po trzecie, język ten daje programiście pełną kontrolę (jeżeli nie uwzględnia się ograniczeń wprowa dzanych przy uruchamianiu programu przez internet). Jeśli programuje się z głową, będzie to wyraźnie widać w napisanych programach. Oczywiście prawdziwa jest rów nież sytuacja odwrotna. Ujmę to inaczej — Java nie jest językiem do nauki pro gramowania, ale językiem dla profesjonalnych programistów'. Z racji podobieństw między językami Java i C++, niektóre osoby sądzą, że Java jest po prostu „internetową wersją języka C++” . Jest to poważny błąd. Java jest inna za równo w kwestiach praktycznych, jak i projektowych. Choć twórcy języka Java ko rzystali z doświadczeń języka C++, Java z pewnością nie stanowi rozszerzenia języka C++. Prosty przykład: Java nie jest w żaden sposób zgodna z językiem C++. Oczywi ście podobieństwa są znaczne i gdy wcześniej programowało się w języku C++, to po przejściu do Javy dany programista poczuje się jak w domu. Java nie została zapro jektow ana, by zastąpić język C++, ale raczej, by rozwiązać pewnego rodzaju proble my. Język C++ rozwiązuje innego rodzaju problemy. Oba języki mogą współistnieć przez wiele lat. Jak wspomniałem na początku rozdziału, języki komputerowe ewoluują z dwóch po wodów: w związku ze zmianami środowiska i chęcią przeniesienia sztuki programo wania na wyższy poziom. Java ma za zadanie zapełnić lukę związaną z pisaniem pro gramów niezależnych od platformy sprzętowej, udostępnianych za pośrednictwem intemetu. Z drugiej strony, wpływa ona także na sposób pisania programów — w szcze gólności rozszerza paradygmat obiektowy wykorzystywany w języku C++. Java nie jest językiem istniejącym w izolacji. Rozwija się stale w ciągu kolejnych lat. Na pewno zasłużyła sobie na szczególne miejsce w historii języków komputerowych. Jest tym dla programowania w intemecie, czym język C był dla programowania systemowego — rewolucyjną siłą zm ieniającą świat.
Powiązanie z językiem C# Wpływ języka Java jest coraz bardziej widoczny w świecie języków programowania. Wiele innowacyjnych cech, konstrukcji i pomysłów stało się częścią innych, nowszych języków. Sukcesu Javy po prostu nie można zignorować. Prawdopodobnie najlepszym przykładem wpływu Javy na inne języki jest język C#, który został stworzony przez finnę Microsoft dla szkieletu .NET Framework. Język C# jest ściśle powiązany z Javą: ma bardzo podobną składnię, obsługuje programowanie rozproszone i korzysta z tego samego modelu danych. Oczywiście istnieją pewne różnice
Rozdział 1. ♦ Historia i ewolucja języka Java
35
między tymi językam i, ale ogólne wrażenie jest bardzo podobne. To zapożyczenie koncepcji z Javy przez język C# jest chyba najlepszym dowodem, że język Java wy wiera ogromny wpływ na sposób myślenia o językach programowania.
Dlaczego język Java jest tak ważny dla internetu Internet stanowił jakby odskocznię, dzięki której Java stała się jednym z głównych ję zyków programowania. Z drugiej strony, język Java nie pozostał dłużny i także wy warł duży wpływ na dzisiejszą postać internetu. Wpływ ten można wytłumaczyć bar dzo prosto: Java znacząco rozszerzyła zbiór obiektów, które można bezproblemowo przesyłać w cyberprzestrzeni. W sieci między serwerem a komputerem klienta przesyła się dwa bardzo różne rodzaje obiektów: informacje pasywne i dynamiczne, aktywne programy. Odczytywanie wiadomości e-mail to przykład przeglądania pasywnych da nych. Nawet jeśli pobiera się program z internetu, to do momentu jego wykonania także jest on reprezentantem danych pasywnych. Pamiętajmy o drugim rodzaju obiektów przesyłanych do komputera — dynamicznych, samowykonujących się programach. Taki program je st aktywnym agentem na komputerze użytkownika, choć został pobrany z serwera. Dobrym przykładem dynamicznego programu jest aplet, który pobiera dane z serwera i wyświetla je w sposób wskazany przez użytkownika. Programy przesyłane w intemecie stanowią poważny problem w kwestii bezpieczeń stwa i przenośności. Przed pojawieniem się języka Java w cyberprzestrzeni istniała tylko połowa różnych bytów, które istnieją tam teraz. Java bardzo dobrze poradziła sobie ze wspomnianym bezpieczeństwem i przenośnością, gdyż otwarła drzwi dla nowego rodzaju programów — apletów.
Aplety Javy Aplet to specjalny rodzaj programu napisanego w języku Java, który został zapro jektowany do przesyłania w intemecie i automatycznego w ykonyw ania przez prze glądarkę WWW obsługującą Javę. Warto podkreślić, iż aplet je s t pobierany na żąda nie, podobnie jak obrazy, dźwięki czy klipy wideo. Główna różnica polega na tym, że aplet jest inteligentnym programem, a nie tylko animacją lub plikiem multimedial nym. Innymi słowy, aplet to program, który może reagować na dane od użytkownika i zmieniać się dynamicznie, zamiast stale odtwarzać tę samą anim ację lub dźwięk. Choć aplety są bardzo interesujące, nie osiągnęłyby dużego sukcesu, gdyby język Java nie zajął się dwiema istotnymi kwestiami: bezpieczeństw em i przenośnością. Zanim przejdę dalej, zdefiniuję oba terminy pod kątem internetu.
36
Część I ♦ Język Java
Bezpieczeństwo Zapewne każdy jest świadom tego, że pobierając dowolny program z intemetu, ryzykuje zainfekowanie swojego komputera wirusem. Zanim pojawił się język Java, większość użytkowników nie chciała często pobierać programów wykonywalnych, a Ci, który to robili, zawsze sprawdzali te aplikacje skanerami antywirusowymi. Pomimo tych wszystkich zabezpieczeń użytkownik nadal nie miał gwarancji, że do infekcji nie doj dzie. Oczywiście poza wirusami istnieją i inne niebezpieczne programy, na przykład konie trojańskie, które potrafią wykradać tajne informacje, jak numer karty kredytowej, stan rachunku w banku, hasła i zawartość dowolnych plików. Java rozwiązuje ten problem, ponieważ zakłada coś na zasadzie zapory między aplikacją pobraną z inter netu a systemem operacyjnym komputera. Gdy korzysta się z przeglądarki WWW obsługującej Javę, można bezpiecznie pobie rać aplety Javy i nie martwić się o możliwość zawirusowania komputera. System wy konawczy Javy otacza środowisko uruchomionego apletu od innych części komputera (wkrótce dokładniej wyjaśnię, jak działa to zabezpieczenie). Bezpieczeństwo zapew niane przez Javę jest jedną z jej najważniejszych zalet, gdyż niebezpieczne programy po prostu nie m ają dostępu do istotnych elementów systemu komputerowego.
Przenośność Jak już wcześniej wspomniałem, na świecie stosuje się wiele różnych systemów ope racyjnych i typów komputerów. Wiele z nich jest podłączonych do intemetu. Aby ten sam program m ógł zostać dynamicznie pobrany i uruchomiony za pośrednictwem in temetu na różnych platformach komputerowych, trzeba zapewnić wygenerowanie możli wie najbardziej przenośnego kodu. Jak wkrótce pokażę, ten sam mechanizm, który pomaga zapewnić bezpieczeństwo, pomaga również przy przenośności. Rozwiązanie obu przed stawionych problemów zaproponowane przez Javę jest eleganckie i wydajne.
Magia języka Java — kod bajtowy Kluczem pozwalającym Javie rozwiązać problemy bezpieczeństwa i przenośności jest fakt, że wyjściem generowanym przez kompilator języka Java nie jest kod wykonywalny, ale kod bajtowy. Kod bajtowy to wysoce zoptymalizowany zbiór instrukcji zaprojekto wanych do wykonywania przez system wykonawczy Javy nazywany maszyną wirtualną Javy (JVM, z ang. The Java Virtual Machine). Maszyna wirtualna to tak naprawdę interpreter kodu bajtowego. Jest to zaskakujące, ponieważ większość nowocze snych języków programowania jest kompilowana do kodu wykonywalnego i nie jest interpretowana ze względu na wydajność. Z drugiej strony, rozw iązanie zapropono wane przez Javę (uruchamianie programów dzięki maszynie w irtualnej) rozwiązuje główne problemy związane z pobieraniem programów z intemetu. O to wyjaśnienie. Przetłumaczenie programu do kodu bajtowego ułatwia uruchamianie go w wielu róż nych środowiskach. Powód jest bardzo prosty: dla każdej platform y komputerowej należy jedynie zaimplementować maszynę wirtualną. Gdy istnieje pakiet uruchomieniowy
Rozdział 1. ♦ Historia i ewolucja języka Java
37
dla danego systemu, można na nim uruchomić dowolny program napisany w Javie. Choć szczegóły związane z maszyną wirtualną nie muszą być takie same w każdym systemie, wszystkie maszyny potrafią poprawnie zinterpretować ten sam kod bajtowy. Jeśli program w języku Java byłby kompilowany do postaci wykonywalnej, trzeba by wykonać wiele wersji tego programu dla różnych systemów podłączonych do inter n e ts Oczyw iście jest to mało wygodne rozwiązanie. Z tego powodu wykonywanie kodu bajtow ego przez maszynę wirtualną Javy jest najlepszym podejściem, gdyż za pewnia największą przenośność aplikacji. Fakt, iż program jest wykonywany przez maszynę wirtualną Javy, pomaga zapewnić odpowiedni poziom bezpieczeństwa. Ponieważ to maszyna wirtualna wszystkim steruje, może zablokować pewne działania programu powodujące negatywne efekty w syste mie. Co więcej, można określić, jakie zadania może, a jakich nie może wykonywać dany program języka Java. Ogólnie, jeśli program zostanie skompilowany do postaci pośredniej i będzie interpreto wany przez maszynę wirtualną, z pewnością będzie działał wolniej niż program skom pilowany do postaci wykonywalnej. Na szczęście w przypadku Javy różnica w szybkości nie jest znacząca. Ponieważ kod bajtowy jest wysoce zoptymalizowany, maszyna wirtu alna potrafi wykonywać go znacznie szybciej, niż mogłoby się wydawać. Choć Java została początkowo zaprojektowana jako język interpretowany, w zasadzie nie istnieją przeciwwskazania techniczne uniemożliwiające kompilację „w locie' z kodu bajtowego na kod wykonywalny w celu zwiększenia wydajności. Z tego powodu fir ma Sun zaczęła wykorzystywać technologię HotSpot ju ż wkrótce po pierwszym wy daniu Javy. HotSpot dostarcza kompilator typu JIT (Just-In-Time) dla kodu bajtowego. Gdy kompilator JIT stanowi część maszyny wirtualnej Javy, wybrane fragmenty kodu bajtowego są kompilowane w czasie rzeczywistym do postaci wykonywalnej. Warto pamiętać o tym, iż nie jest możliwe skompilowanie całego programu Javy do postaci wykonywalnej, ponieważ maszyna wirtualna musi przeprowadzać różne sprawdzenia możliwe do wykonania tylko w trakcie pracy programu. Zamiast tego kompilator JIT kompiluje tylko fragmenty kodu, gdy jest to naprawdę przydatne. Innymi słowy, nie jest kompilowany cały program, a jedynie te fragmenty, które naprawdę mogą zyskać na kompilacji. Pozostały kod jest po prostu interpretowany. Pomimo tych ograniczeń, kompilacja JIT zapewnia znaczące przyspieszenie działania aplikacji. Nawet w przy padku kompilacji kodu bajtowego przenośność i bezpieczeństwo są nadal zachowane, ponieważ to maszyna wirtualna zarządza całym środowiskiem.
Hasła języka Java Żaden opis historii języka Java nie może się obejść bez podania haseł przyświecają cych powstaniu tego języka. Choć głównymi motorami związanymi z powstaniem tego języka są przenośność i bezpieczeństwo, inne czynniki także wpływały na ostateczny kształt Javy. Poniżej przedstawiam listę podstawowych haseł, którymi kierował się zespół opracowujący język Java: ♦ prostota, ♦ bezpieczeństwo,
38
Część I ♦ Język Java
♦ przenośność, ♦ obiektowość, ♦ solidność, ♦ wielowątkowość, ♦ neutralność architektury, ♦ interpretowalność, ♦ wysoka wydajność, ♦ rozproszenie, ♦ dynamika. Dwa z tych haseł — bezpieczeństwo i przenośność — zostały już omówione. Pokrótce wyjaśnię więc pozostałe.
Prostota Java została tak zaprojektowana, by była prosta do przyswojenia przez profesjonalnego programistę i by jednocześnie m ogła być używana wydajnie. Gdy ma się jakiekol wiek doświadczenie programistyczne, opanowanie języka Java nie stanowi większego problemu. Jeśli dodatkowo dobrze rozumie się programowanie obiektowe, nauka Javy przebiegnie jeszcze szybciej. Co więcej, gdy wcześniej programowało się w języku C++, przestawienie się na język Java zajmuje tylko kilka dni. Ponieważ Java dziedzi czy składnię po językach C i C++ a elementy obiektowe po języku C++, większość programistów nie ma najmniejszych problemów z jej opanowaniem.
Obiektowość Choć na powstanie Javy miały wpływ inne języki, nie była ona projektowana w taki sposób, by jej kod źródłowy był zgodny z jakimkolwiek innym językiem. Zespół pro jektowy nie był obarczony dziedzictwem przeszłości i w ten sposób powstało czyste, użyteczne i pragmatyczne podejście do obiektowości. Liberalne pożyczanie pomysłów od wielu innych wcześniejszych środowisk obiektowych pozwoliło językowi Java zacho wać równowagę między podejściem purystów („wszystko jest obiektem”) a podejściem pragmatyków („zejdź mi z drogi”). Model obiektów w Javie jest prosty i rozszerzalny, natomiast typy proste (na przykład liczby całkowite) ze względów wydajnościowych nie są obiektami.
Solidność Wieloplatformowe środowisko internetowe stawia niezwykłe żądania programom, po nieważ m uszą one działać bez przeszkód na różnych systemach. Z tego powodu nadano duży priorytet takiemu zaprojektowaniu języka, by pisane w nim programy były solidne.
Rozdział 1. ♦ Historia i ewolucja języka Java
39
Java ogranicza programistę w kilku kwestiach, ale jest to związane tylko z wymusza niem szybszego znajdowania błędów. Z drugiej strony, pisząc program w języku Java, nie trzeba martwić się, że popełni się typowe błędy programistyczne. Ponieważ Java jest językiem ze ścisłą kontrolą typów, sprawdza kod w trakcie kompilacji. Jednocze śnie sprawdza również kod w trakcie wykonywania programu. W zasadzie w Javie nie udaje się wykonać wielu błędów, które są trudne do wykrycia, bo objaw iają się tylko czasami. Zapewnienie przewidywalnego sposobu działania aplikacji przez sam język jest jednym z kluczowych aspektów Javy. Aby lepiej zrozumieć efekt solidności Javy, warto rozważyć dwa główne powody po wstawania błędów w programach: błędy związane z zarządzaniem pamięcią i źle ob służone wyjątki (błędy wykonania programu). Zarządzanie pamięcią jest trudnym i niewdzięcznym zadaniem w tradycyjnych językach programowania. Na przykład w językach C i C++ programista musi ręcznie alokować i zwalniać dynamicznie przy dzielaną pamięć. Czasami prowadzi to do problemów, ponieważ programiści albo za pominają zwolnić zaalokowaną wcześniej pamięć, albo, co gorsza, zwalniają pamięć, która jest jeszcze uży wana przez inny fragment aplikacji. Java praktycznie eliminuje te problemy, gdyż zarządza alokacją i zwalnianiem pamięci za programistę. (W zasadzie zwalnianie pamięci jest w pełni automatyczne, gdyż Java zawiera mechanizm zwal niania nieużywanej pamięci). Wyjątki w tradycyjnych środowiskach wynikają często z próby dzielenia przez zero lub błędu „nie znaleziono pliku" i muszą być wyłapywane w niezbyt prostych konstrukcjach. Java pomaga rozwiązać tego rodzaju problemy, zapewniając obiektową obsługę wyjątków. W dobrze napisanym programie w języku Java wszystkie błędy wykonania mogą — a w zasadzie powinny — być obsłużone przez program.
Wielowątkowość Java została zaprojektowana tak, by spełnić rzeczywiste wymagania związane z two rzeniem interaktywnych, sieciowych aplikacji. W tym celu w sam język zostały wbu dowane mechanizmy programowania wielowątkowego, aby program mógł wykonywać wiele zadań jednocześnie. System wykonawczy Javy dostarczany jest z eleganckim, choć złożonym rozwiązaniem synchronizacji międzyprocesowej, który umożliwia two rzenie sprawnie działających aplikacji wielowątkowych. Dzięki wbudowaniu wszyst kiego w sam język, programista nie musi martwić się podsystemem wiełozadaniowości, a jedynie odpowiednim zachowaniem programu.
Neutralność architektury Główny nacisk w trakcie projektowania języka Java położono na żywotność i przeno śność. Jednym z wielu problemów programistów je st to, iż nie ma gwarancji, że napi sany dziś program będzie poprawnie działał także jutro (nawet na tym samym kompute rze). Pojawiają się coraz to nowsze systemy operacyjne, procesory, dostępne zasoby, które mogą spowodować, że pogram przestanie poprawnie funkcjonować. Projektanci języka Java podjęli kilka konkretnych decyzji i tak zmodyfikowali język oraz maszy nę wirtualną, aby można było uniknąć opisanych wcześniej sytuacji. Oto założenie projektantów języka: „napisz raz, uruchom gdziekolwiek, kiedykolwiek, w iecznie”. Można powiedzieć, że w dużej mierze udało się spełnić to założenie.
40
Część I ♦ Język Java
Interpretowalność i wysoka wydajność Jak wspomniałem wcześniej, język Java um ożliw ia tworzenie aplikacji wieloplatformowych, dzięki kompilacji kodu źródłowego na reprezentację pośrednią nazywaną kodem bajtowym. Kod ten może być wykonywany na dowolnym systemie, dla którego napi sano maszynę wirtualną Javy. Większość wcześniejszych rozwiązań wieloplatformow ych znacząco traciła na wydajności. Jak wcześniej napisałem, kod bajtowy został starannie zaprojektowany w taki sposób, by był łatwy do konwersji na kod maszyno wy za pomocą wydajnego kompilatora JIT. Systemy wykonawcze Javy zapewniające ten kompilator potrafią być równie szybkie ja k zwykły kod maszynowy.
Rozproszenie Java została zaprojektowana dla rozproszonego środowiska internet, ponieważ obsługuje protokół TCP/IP. W zasadzie dostęp do zasobu za pomocą adresu URL niewiele różni się od dostępu do pliku. Java obsługuje zdalne wywoływanie metod (RM1, z ang. Remote M ethod Invocation), co umożliwia wywoływanie metod na odległych komputerach.
Dynamika Programy napisane w języku Java zaw ierają znaczną ilość informacji przydatnych w czasie wykonywania aplikacji, które są używane do weryfikacji i dostępu do obiektów. Umożliwia to dynamiczne dołączanie kodu w bezpieczny i przewidywalny sposób. Jest to ważny element środowiska apletów, których fragmenty m ogą być dynamicznie aktualizowane w trakcie pracy systemu.
Ewolucja Javy Pierwsze wydanie Javy na pewno można nazwać rewolucją, ale na tym nie zakończył się rozwój całego języka. W odróżnieniu od innych systemów, które na ogół rozwijają się bardzo powoli w sposób inkrementacyjny, Java ewoluuje w niezm iernie szybkim tempie. Niedługo po wydaniu Java 1.0 projektanci mieli już gotową wersję 1.1. Liczba wprowadzonych zmian była tak duża, że z pewnością nie oddaje jej tak niewielka zmiana w numeracji wersji. W Java 1.1 pojawiło się wiele nowych elementów biblioteki, ponownie zdefiniowano obsługę zdarzeń przez aplety i zmieniono wiele elementów wcześniejszej biblioteki. Wycofano (uznano za przestarzałe) kilka elem entów orygi nalnie zdefiniowanych w Java 1.0. Innymi słowy, Java 1.1 zarówno dodała, jak i odjęła pewne elementy oryginalnej specyfikacji. Kolejnym głównym wydaniem Javy była Java 2, gdzie liczba 2 oznaczała „drugą gene rację”. Pojawienie się tego wydania przeniosło Javę do „nowej ery” i było naprawdę ważnym wydarzeniem. Pierwsze wydanie Javy 2 nosi numer 1.2. M oże się to wydawać nieco dziwne, ale w ynika po prostu z odmiennego num erow ania w ersji wewnątrz
Rozdział 1. ♦ Historia i ewolucja języka Java
41
bibliotek, a innego dla samych nazw wydań. W raz z tym wydaniem firma Sun zmie niła nazwę produktu na J2SE (Java 2 Standard Edition) i numer wersji zaczął być do dawany właśnie do niej. Java 2 przyniosła wiele nowych elementów, między innymi bibliotekę Swing i kolek cje (Collections Framework), a także rozszerzenie maszyny wirtualnej Javy i innych narzędzi. Java 2 wprowadziła też kilka wycofań. Najważniejszym z nich było zabro nienie wykorzystywania metod suspend(), resume() i stopC) klasy Thread. Następne znaczące wydanie Javy to J2SE 1.3. Było to pierwsze znaczące uaktualnienie od wersji 1.2 i polegało głównie na rozbudowaniu istniejącej funkcjonalności oraz skon kretyzowaniu środowiska uruchomieniowego. Ogólnie istnieje zgodność kodu źródło wego programu napisanego w wersji 1.2 i wersji 1.3. Choć w tym wydaniu nie pojawiło się wiele zmian w samej składni języka, było ono nie mniej ważne od pozostałych. Wydanie J2SE 1.4 ponownie zwiększyło możliwości Javy, gdyż zawierało kilka istot nych uaktualnień, poprawek i dodatków. Na przykład wprowadzono nowe słowo klu czowe assert, łańcuch wyjątków i kanałowy system wejścia-wyjścia. Dokonano także zmian w klasach kolekcji i sieci, a także wielu innych pomniejszych zmian. Mimo tych wszystkich poprawek wersja 1.4 zachowała niemalże stuprocentow ą zgodność kodu źródłowego z poprzednimi wersjami. Najnowszym wydaniem Javy jest J2SE 5, które ponownie można nazwać rewolucją.
Rewolucja J2SE 5 Wydanie J2SE 5 z pewnością jest jednym z najważniejszych momentów z życia języka Java. W odróżnieniu od poprzednich uaktualnień, które były istotne, ale miały naturę inkrementacyjną. J2SE 5 w zasadniczy sposób rozszerza zasięg, użyteczność i za kres języka. W zasadzie tak poważne zmiany nie wystąpiły jeszcze nigdy wcześniej w dziesięcioletniej historii języka. Aby zrozumieć rozmiar zmian w wersji J2SE 5, warto przyjrzeć się liście dodanych nowych elementów języka: ♦ typy sparametryzowane, ♦ metadane, ♦ automatyczne otaczanie i wydobywanie typów prostych, ♦ wyliczenia, ♦ rozszerzona pętla for typu for-each, ♦ zmienna liczba argumentów, ♦ import statyczny, ♦ formatowane wejście-wyjście,
42
Część I ♦ Język Java
♦ narzędzia związane ze współbieżnością, ♦ unowocześnienie interfejsu programistycznego. Przedstawiona lista nie zawiera jakichś tam pomniejszych usprawnień. Każdy element listy reprezentuje znaczące rozszerzenie języka Java. Niektóre elementy, jak typy sparametryzowane, rozszerzona pętla for i zmienna liczba argumentów, to nowe elementy składni. Inne elementy, na przykład automatyczne otaczanie typów prostych, zmie niają semantykę języka. Metadane dodają całkowicie nowy wymiar programowania. We wszystkich przypadkach wpływ poszczególnych elementów wykracza poza ich bez pośrednie oddziaływanie. To wydanie zmienia charakter Javy. Istotność wszystkich funkcji została podkreślona przez zmianę wersji na numer „5” . Tradycyjnie now a wersja powinna przyjąć numer 1.5. Jednak przejście z wersji 1.4 na 1.5 nie oddawałoby stopnia wprowadzonych zmian, więc zdecydowano się na przejście od razu do wersji 5. Aktualna wersja produktu nosi nazwę J2SE 5. a nazwa platformy programistycznej — JDK 5. Aby jednak zachować jednolitość w numeracji wersji biblio teki, num er w ew nętrzny nowego w ydania to 1.5. Innymi słowy, zewnętrzny numer wydania to 5, a wewnętrzny to 1.5. Ponieważ firma Sun używa numeru 1.5 jako wewnętrznej nazwy nowej wersji języka, kompilator po poproszeniu o podanie własnej wersji wyświetli wartość 1.5 za miast 5. Podobnie, w dokumentacji do języka dostępnej na witrynie firmy Sun ist nieje numer 1.5. Po prostu każde wystąpienie numeru 1.5 oznacza tak naprawdę, że jest to 5. wydanie języka Java.
Kultura innowacji Już od samego początku Java była centrum kultury innowacji. Jej pierwsze wydanie zmieniło podejście do programowania dla intemetu. Wirtualna maszyna Javy oraz kod bajtowy zmieniły sposób myślenia o bezpieczeństwie i przenośności. Aplet (a później także serwlet) spowodował, że strony WWW ożyły. System JCP (Java Community Process) zmienił sposób wprowadzania nowych elementów do języka. Świat Javy w za sadzie nigdy się nie zatrzymuje. Wraz z wydaniem J2SE 5 Java ponownie została dostosowana do ciągle zmieniającego się świata programowania. Ponownie przoduje w kwestii oryginalnego projektu języka komputerowego.
Rozdział 2.
Podstawy języka Java Podobnie jak we wszystkich innych językach programowania, elementy Javy nie istnieją w izolacji, ale raczej współpracują ze sobą w celu uformowania całościowego obrazu języka. Powoduje to jednak, że bardzo ciężko jest opisać jeden aspekt języka bez zna jomości kilku innych aspektów. Często dyskusja na temat jednej funkcji wymaga wcze śniejszego zapoznania się z innymi funkcjami. Z tego powodu niniejszy rozdział za wiera krótkie i szybkie wprowadzenie do podstawowych zagadnień języka Java. Przedstawiony m ateriał pozwoli dowiedzieć się, jak pisać bardzo proste programy. Większość przedstawionych tutaj tematów zostanie szczegółowo omówiona w kolej nych rozdziałach pierwszej części książki.
Programowanie obiektowe Programowanie obiektowe stanowi rdzeń języka Java. W zasadzie w-szystkie programy napisane w tym języku są programami obiektowymi. Obiektowość jest tak silnie związa na z Javą, że warto zrozumieć podstawowe zasady programowania obiektowego, za nim nawet napisze się swój pierwszy program. Z tego względu niniejszy rozdział roz poczyna się od omówienia teoretycznych aspektów programowania obiektowego.
Dwa paradygmaty Jak zapewne każdy wie, wszystkie programy komputerowe składają się z dwóch elemen tów: kodu i danych. Co więcej, program można koncepcyjnie zorganizować wokół da nych lub wokół kodu. Inaczej mówiąc, jedne programy są zorganizowane u'okół pytania „co się dzieje”, a inne wokół pytania „na kogo wpływamy” . Te dwa paradygmaty wpływają na sposób konstrukcji programu. Pierwszy ze sposobów nazywany jest mode lem zorientowanym na procesy i charakteryzuje się postrzeganiem programu jako serii liniowych kroków (czyli kodu). Model ten można w najprostszy sposób opisać jako kod działający na danych. Języki proceduralne takie jak C doskonale wykorzy stują ten model. Jednak zgodnie z tym, co powiedziałem w rozdziale 1., problemy z takim podejściem pojawiają się, gdy staje się bardzo duży i złożony.
44
Część I ♦ Język Java
Aby zarządzać zwiększającą się złożonością, wykorzystuje się drugie podejście, nazy wane programowaniem obiektow ym . Programowanie obiektowe organizuje program wokół zaw artych w nim danych (czyli obiektów ) i jasno określa interfejs dostępu do tych danych. Program obiektow y m ożna scharakteryzować jako dane sterujące dostę pem do kodu. W krótce pokażę, że przejście na sterowanie danymi ma swoje zalety organizacyjne.
Abstrakcja Podstawowym elementem program ow ania obiektowego jest abstrakcja. Ludzie po trafią radzić sobie ze złożonością właśnie dzięki abstrakcji. Na przykład, nikt nie myśli o samochodzie jako o zbiorze setek różnych części. Traktuje się go raczej jak o jeden obiekt o ściśle określonym zachowaniu. Taka abstrakcja nie przeszkadza ludziom korzy stać z samochodu i jechać nim na przykład do sklepu — innymi słowy, aby korzystać z samochodu, nie trzeba wiedzieć, jak działa i z ilu części się składa. Wystarczy tylko kilka prostych poleceń sterujących i ju ż można zarządzać samochodem jako całością. Użytecznym sposobem zarządzania abstrakcją jest wykorzystanie klasyfikacji hierar chicznej. Pozwala to tworzyć warstwy semantyczne złożonych systemów i rozbijać je na prostsze, łatwiejsze do przyswojenia części. Z zewnątrz samochód jest pojedynczym obiektem. Gdy wejdzie się do jeg o środka, widzi się kilka podsystemów: sterowanie, hamulce, system dźwięku, pasy bezpieczeństwa, ogrzewanie, pedał gazu itd. Każdy z tych podsystemów składa się z wielu części. Na przykład system dźwięku składa się z radia, odtwarzacza CD lub kasetowego, głośników i anteny. Łatwo zauważyć, że złożoność samochodu zastępowana jest kolejnymi hierarchicznymi abstrakcjami. Hierarchiczne abstrakcje złożonych systemów można zastosować również w progra mach komputerowych. Dane z tradycyjnego programu zorientowanego na procesy można dzięki abstrakcji przekształcić na obiekty komponentowe. Kolejne kroki procesu zamienia się na zbiór komunikatów przekazywanych między obiektami. Każdy z obiektów określa pewne unikatowe zachowanie. Obiekty odpowiadają na komunikaty informujące o po trzebie w ykonania pewnego zadania. Jest to esencja programowania obiektowego. Koncepcje obiektowe wykorzystywane w języku Java w zasadzie odpowiadają sposo bowi rozumowania ludzi. Trzeba jednak zrozumieć, jak te koncepcje przekładają się na programy. Programowanie obiektowe jest użytecznym i naturalnym paradygmatem do tyczącym tworzenia programów, które potrafią przetrwać niezliczoną liczbę zmian zwią zanych z cyklem życia dowolnego systemu komputerowego. Jeżeli dobrze zdefiniowało się interfejsy poszczególnych obiektów i ich zachowanie, bez większego strachu można modyfikować fragmenty starego systemu (przy zachowaniu jednolitego interfejsu).
Trzy zasady programowania obiektowego Wszystkie języki obiektowe zapewniają mechanizmy pomagające zaimplementować model obiektowy. Mechanizmami tymi są: hermetyzacja, dziedziczenie i polimorfizm. Przyjrzyjmy się po kolei tym koncepcjom.
Rozdział 2. ♦ Podstawy języka Java
Hermetyzacja H erm etyzacja to mechanizm łączący kod i modyfikowane przez niego dane, a także zabezpieczenie przed niepowołanym dostępem z zewnątrz celowo lub z powodu złego użytkowania. Hermetyzację warto traktować jako zabezpieczającą otoczkę, która chroni dane i kod przed nieautoryzowanym dostępem z poziomu kodu znajdującego się poza otoczką. Dostęp do kodu i danych znajdujących się w ewnątrz otoczki jest ściśle okre ślony przez interfejs obiektu. Oto porównanie do rzeczywistego świata: zajmijmy się automatyczną skrzynią biegów samochodu. Zawiera ona setki bitów informacji na temat silnika, przyspieszenia, nachylenia powierzchni, położenia dźwigni skrzyni biegów itp. Jednak my, jak o użytkownicy tej skrzyni biegów, m am y dostęp jedynie do dźwigni skrzyni biegów. Nie zmienimy biegu włączając kierunkowskaz lub wycieraczki. Innymi słowy, dźwignia skrzyni biegów jest dobrze zdefiniowanym interfejsem skrzyni biegów'. Co więcej, to co dzieje się wewnątrz skrzyni biegów nie wpływa na inne obiekty. Na przykład zm iana biegu nie powoduje włączenia świateł mijania! Dzięki hermetyzacji różni producenci skrzyń biegów stosują w nich własne rozwiązania. Z punktu widzenia kierowcy dźwignia zawsze wygląda tak samo. Dokładnie takie samo podejście stosuje się w programowaniu. Siła hermetyzacji objawia się tym, że każdy wie, w jaki sposób używać danego obiektu i nie musi przejmować się szczegółam i implementacji oraz efektami ubocznymi. W Javie podstaw ą hermetyzacji je st klasa. Choć klasy om aw iam dokładniej dopiero w dalszej części książki, przedstawię tutaj bardzo krótkie wprowadzenie. Klasa definiuje strukturę i zachowanie (dane i kod), które będą współdzielone przez zbiór obiektów. Każdy obiekt wybranej klasy ma dokładnie taką samą strukturę i zachowanie, jak gdyby był odbitką zrobioną z tej samej pieczątki. Z tego powodu obiekty nazywa się często egzemplarzami klasy. Klasa to konstrukcja logiczna; obiekt to rzeczywisty byt. Gdy tworzy się klasę, trzeba podać kod i dane składające się na tę klasę. Elementy te nazywa się składowymi klasy. Dane zdefiniowane dla klasy nazywa się zmiennymi składowymi lub zmiennymi członkowskimi. Kod działający na tak określonych danych nazywamy metodami składowymi lub po prostu metodami. (Jeśli wcześniej progra mowało się w języku C lub C++, to warto wiedzieć, iż to, co w tych językach nazy wało się funkcją, w Javie nazywa się metodą). W poprawnie napisanych programach metody definiują sposób dostępu do zmiennych składowych. Innymi słowy, zachowanie i interfejs danej klasy określają metody działające na danych egzemplarza klasy. Ponieważ zadaniem klasy jest ukrywanie złożoności, istnieje mechanizm chowania szcze gółów implementacji klasy. Każda metoda lub zmienna klasy może zostać oznaczona jako publiczna lub prywatna. Interfejs publiczny klasy reprezentuje te elementy, o któ rych powinni wiedzieć zewnętrzni użytkownicy. Metody i dane prywatne są dostępne tylko z poziomu kodu zawartego w danej klasie. Innymi słowy, dowolny inny kod, który nie należy do klasy, nie może uzyskać bezpośredniego dostępu do metody lub zmiennej prywatnej. Dostęp do tych elementów klasy odbywa się w sposób pośredni dzięki meto dom publicznym klasy. Wynika z tego, że należy tak zaprojektować interfejs publiczny, aby nie odsłaniał on zbyt dużo szczegółów działania klasy (patrz rysunek 2.1).
45
46
Część I ♦ Język Java
Rysunek 2.1. H e rm e ty za c ja — d o stę p d o p r y w a tn y c h danych o d b y w a się je d y n ie p r z e z m e to d y p u b lic zn e
Klasa Publiczne * zmienne składowe (niezalecane)
Metody publiczne
Metody prywatne
Prywatne ^ zmienne składowe
Dziedziczenie Dziedziczenie to proces, w którym jeden obiekt otrzymuje właściwości innego obiektu. Jest to bardzo ważny element, ponieważ wspiera koncepcję klasyfikacji hierarchicznej. Jak już wspomniałem, taka klasyfikacja znacznie ułatwia ogarnięcie całości wiedzy. Na przykład Golden Retriever jest częścią klasyfikacji pies , która to zawiera się w klasie ssak, a ta z kolei zaw iera się w szerszej klasie zwierzę (patrz rysunek 2.2). Bez korzy stania z hierarchii trzeba by jawnie zdefiniować całą charakterystykę obiektu. Dzięki dziedziczeniu wystarczy wskazać tylko te cechy, które czynią go unikatowym w jego nadzbiorze, ponieważ ogólne atrybuty uzyskuje się po jego przodku. Mechanizm dzie dziczenia czyni możliwym sytuację, w której obiekt jest konkretnym egzemplarzem bar dziej ogólnego przypadku. Przyjrzyjmy się dokładniej całemu procesowi. łysunek 2.2.
Zwierzę
lie ra rc h ia dziedziczenia fla G o ld e n R etrievera
Ssak
Pies
IS.Udomowiony I
Retriever I
Golden
|
Labrador
Pudel
Gad
Rozdział 2. ♦ Podstawy języka Java
Większość ludzi uważa za naturalne tworzenie różnorakich hierarchii obiektów, na przy kład hierarchii zwierzę, ssak, pies. Jeśli chciałoby się opisać zwierzęta w abstrakcyjny sposób, zapewne podałoby się dla nich następujące atrybuty: rozmiar, inteligencję, rodzaj szkieletu. Zwierzęta charakteryzują się pewnymi szczególnymi zachowaniami: jedzą, oddychają i śpią. Ten opis atrybutów i zachowania stanowi definicję klasy zwierząt. Jeśli chce się dokładniej opisać pewien podzbiór zwierząt, na przykład ssaki, trzeba okre ślić dodatkowe atrybuty, takie jak rodzaj uzębienia i liczbę sutków. W takim przypad ku mówimy o podklasic zwierząt, czyli ssakach. Dla ssaków klasa zwierzę jest klasą nadrzędną. Ponieważ ssaki są po prostu dokładniej zdefiniowanymi zwierzętami, dziedziczą wszyst kie atrybuty klasy zwierzę. Głęboko zagnieżdżona podklasa dziedziczy wszystkie atry buty swoich przodów w hierarchii klas. Dziedziczenie współpracuje z hermetyzacją. Jeśli dana klasa hermetyzuje pewne atry buty, dowolna jej podklasa także będzie miała te atrybuty plus własne atrybuty stano wiące część specjalizacji (patrz rysunek 2.3). Dzięki tej koncepcji złożoność progra mów obiektowych rośnie liniowo, zamiast geometrycznie. Nowa podklasa dziedziczy wszystkie elementy przodków. Nie działa w żaden nieprzewidziany sposób z pozo stałym kodem systemu.
Polimorfizm Polimorfizm (z greckiego, oznacza „wiele form”) to cecha, dzięki której jeden interfejs może być stosowany do wykonania różnych zadań. Konkretne zadania zależą od ak tualnej sytuacji obiektu. Rozważmy stos (ostatni wchodzi, pierwszy wychodzi). Pewien program potrzebuje trzech rodzajów stosu: jednego dla liczb całkowitych, drugiego dla liczb zmiennoprzecinkowych i trzeciego dla znaków. Algorytm implementujący każdy ze stosów je st taki sam, nawet jeśli przechowywane dane są innego typu. W języku nieobiektowym trzeba by wykonać trzy różne zestawy procedur dla stosu o różnych nazwach. Jednak w języku Java dzięki polimorfizmowi można napisać ogólny zbiór procedur o takich samych nazwach. Koncepcję polimorfizmu często tłumaczy się w następujący sposób: J e d e n interfejs, wiele metod” . Oznacza to, że potrafimy zaprojektować ogólny interfejs dla grupy powią zanych ze sobą aktywności. Zm niejsza to złożoność całego zagadnienia, gdyż interfejs jest stosowany dla ogólnej klasy akcji. Kompilator ma za zadanie wybrać konkretną akcję (czyli metodę) odpowiednią dla danej sytuacji. Programista nie musi wykonywać tego zadania ręcznie — wystarczy, że korzysta z ogólnego interfejsu. Wróćmy do analogii z psami. Zmysł węchu psa jest polimorficzny. Jeśli pies wyczuje kota. szczeknie i pobiegnie w stronę kota. Jeśli pies wyczuje jedzenie, zamerda ogonem i pobiegnie do swej miski. W obu przypadkach zadziałał ten sam zmysł węchu. Róż nica polegała na wyczutym zapachu i to właśnie ten rodzaj danych był przetwarzany przez nos psa! Tę samą ogólną koncepcję można zaimplementować w języku Java, tyle że zostanie ona zastosowana do metod programu komputerowego.
47
48
Część I ♦ Język Java
Rysunek 2.3. Labrador d zie d zic zy
Zwierzę
Pleć Wiek
elem en ty h erm etyza cji sw o ich klas n a d rzędn ych
Waga
Ssak Okres ciąży
Wielkość miotu
P ie s
Umiejętności 'o .v ie c k ie
Długość ogona
Udomowiony
Przyzwyczajony do smyczy" lub podwórkowy
Wytrenowany do polowania na kaczki? .
Retriever
Labrador Certyfikat AKC? Labrador Wiek Pięć Waga Wielkość miotu
Okres ciąży Umiejętności łowieckie Długość ogona Domowy lub podwórkowy
Przyzwyczajony do smyczy? Wytrenowany do polowania na kaczki? Certyfikat AKC?
Połączenie polimorfizmu, hermetyzacji i dziedziczenia Poprawne zastosowanie połączenia polimorfizmu, hermetyzacji i dziedziczenia pozwala uzyskać zdecydowanie bardziej skalowalne i pewniejsze środow isko programistyczne niż w' przypadku programowania strukturalnego. Dobrze zaprojektow ana hierarchia klas pozwala wielokrotnie wykorzystywać ten sam kod, w który tylko raz wystarczy zainwestować swój czas. Hermetyzacja zapewnia łatwą modyfikację implementacji bez zmieniania kodu, który korzysta jedynie z publicznego interfejsu klasy. Polimorfizm ułatwia tworzenie czystego, sensownego i czytelnego kodu. Z dwóch przedstawionych wcześniej przykładów, samochód znacznie lepiej ilustruje siłę projektowania obiektowego. Przykład psów dobrze sprawdza się pod kątem dzie dziczenia, ale to samochody bardziej przypominają rzeczywiste programy. W szyscy kierowcy wykorzystują fakt, iż wszystkie samochody dziedziczą po wspólnym przodku.
Rozdział 2. ♦ Podstawy języka Java
Niezależnie od tego. czy jest to szkolny autobus, sedan, Porsche, czy rodzinny minivan, kierowcy potrafią sterować takimi pojazdami, gdyż mają one wspólne elem enty takie jak kierownica, pedały i dźwignię biegów. Oczywiście istnieje rozróżnienie na auto matyczną i manualną skrzynię biegów, ale większość kierowców nie problemów z prze stawieniem się na którekolwiek z tych rozwiązań. Ludzie cały czas korzystają z interfejsu udostępnianego im przez samochody. Pedały gazu, sprzęgła i hamulca są bardzo proste i można nimi sterować nogami, ale tak na prawdę ukrywają niesamowicie dużą złożoność. Rodzaj silnika, typ hamulców i roz miar opon nie mają wpływu na sposób korzystania z pedałów. Polimorfizm bardzo łatwo zauważyć w samochodach. Ich producenci potrafią dostar czyć bardzo różne wyposażenie bez zmieniania ogólnego wyglądu auta. Na przykład można otrzymać system ABS lub tradycyjne hamulce ze wspomaganiem, zwykły układ kierowniczy lub ze wspomaganiem, silnik spalinowy lub diesel. Mimo tych wszystkich różnic nadal używa się pedału hamulca do hamowania, a kierownicy do skręcania. Ten sam interfejs służy do sterowania niezliczoną liczbą implementacji. Jak się łatwo przekonać, elementy hermetyzacji, dziedziczenia i polimorfizmu można odnaleźć na każdym kroku, nie tylko w zlepku części przekształconym w obiekt zwany samochodem. Podobnie wygląda to w kwestii programowania. Dzięki stosowaniu zasad obiektowości w złożonych programach znacznie łatwiej uzyskać porządek, elastycz ność i łatwość modyfikacji kodu. Jak już wcześniej wspomniałem, każdy program napisany w języku Java jest progra mem obiektowym. Innymi słowy, każdy program tego języka wykorzystuje hennetyzację, dziedziczenie i polimorfizm. Choć krótkie przykłady prezentowane w tym i kolejnych rozdziałach mogą nie eksponować wszystkich zasad obiektowości, niemniej korzysta się z nich intensywnie. Wiele cech Javy dostarcza wbudowana biblioteka, która intensyw nie korzysta z podstawowych elementów obiektowości — hermetyzacji, dziedziczenia i polimorfizmu.
Pierwszy przykładowy program Ponieważ mamy już za sobą omówienie podstawowych kwestii związanych z obiektowością, warto przyjrzeć się przykładowemu programowi napisanemu w języku Java. Dodatkowo w tym miejscu przedstawię sposób kompilacji i uruchamiania programów. Wymaga to trochę więcej pracy, niż może się początkowo wydawać. f-k
To je st przykładowy program wjęzyku Java Nadaj plikowi naz\vę "Example java"
*/ cla ss Example {
// Program rozpoczyna się od wywołania metody matn(). public s ta tic void m ain(Stnng args[]) { System.out .printlnt"To j e s t przykładowy program w języku Ja va ."):
} }
49
50
Część I ♦ Język Java
Przedstawiany tutaj opis dotyczy standardowej wersji JDK 5 (J2SE 5 Developer's Kit) dostępnej do pobrania na witrynie Sun Microsostems. Inne środowiska programi styczne Javy mogę wymagać innej procedury kompilacji i uruchamiania programów napisanych w Javie. Należy wtedy zajrzeć do dokumentacji danego środowiska pro gramistycznego.
Wpisanie kodu programu W większości języków programowania nazwa pliku przechowującego kod źródłowy programu nie ma żadnego znaczenia. Jednak w Javie jest inaczej. Zawsze należ)' pamię tać o tym, iż nazwa nadawana plikowi źródłowemu w Javie ma duże znaczenie. Przed stawiony przykładowy program musi zostać umieszczony w pliku Example.java. Oto wyjaśnienie dlaczego. W Javie plik źródłowy nazywany jest oficjalnie jednostką kompilacji. Jednostka kom pilacji jest plikiem tekstowym zawierającym jedną lub kilka definicji klas. Zauważ, że rozszerzenie nazwy pliku jest czteroliterowe. Z tego powodu system operacyjny musi obsługiwać długie nazwy plików. Innymi słowy, systemy operacyjne DOS i Windows 3.1 nie poradzą sobie z obsługą Javy. Potrzebny jest system Windows 95, 98, NT, 2000 lub XP. W przykładowym programie definiowana klasa nosi nazwę Example. Zbieżność nie jest przypadkowa. W Javie cały kod musi się znajdować wewnątrz klasy. N azw a klasy powinna odpowiadać nazwie przechowującego ją pliku. Co więcej, nie może być nawet różnic w wielkości liter w obu nazwach. Wynika to z faktu, iż Java zwraca uwagę na wielkość liter. Dla prostych programów wymuszanie takiej zgodności nazwy klasy i na zwy pliku wydaje się niepotrzebne, ale w większych projektach staje się nieocenioną pomocą, gdyż ułatwia organizację plików.
Kompilacja programów Aby skompilować program Example, należy uruchomić kompilator Javy o nazwie javac i jako parametr podać nazwę pliku źródłowego. Oto przykład. C:\javac Example.java
Kompilator utworzy plik o nazwie Example.class, który zawiera program przetwo rzony do kodu bajtowego. Jak ju ż tłumaczyłem wcześniej, kod bajtowy Javy to postać pośrednia programu zawierająca instrukcje, które ma wykonać maszyna wirtualna Javy. Kompilator nie tworzy kodu, który można bezpośrednio uruchomić. Aby uruchomić skompilowaną aplikację, trzeba wykorzystać aplikację uruchomieniową Javy o nazwie java. Jako argument tej aplikacji podaje się nazwę klasy, w tym przy padku Example. C:\java Example
Po uruchomieniu programu na wyjściu powinien pojawić się następujący komunikat.
Rozdział 2. ♦ Podstawy języka Java
To je s t przykładowy program w języku Java.
W trakcie kompilacji kodu źródłowego kompilator umieszcza każdą klasę w osobnym pliku o nazwie składającej się z nazwy klasy i rozszerzenia .class. Między innymi z tego powodu warto nazywać pliki źródłowe tak samo jak klasy — nazwa pliku źródłowego będzie wtedy odpowiadała nazwie wygenerowanego pliku .class. Aplikacja urucho mieniowa java przyjmuje jako parametr nazwę klasy i automatycznie wyszukuje plik o takiej samej nazwie jak klasa, ale z dodanym rozszerzeniem .class. Jeśli znajdzie taki plik. wykonuje zawarty w nim kod.
Bliższe spojrzenie na pierwszy przykładowy program Choć przykładowy program Example.java jest bardzo krótki, zawiera kilka istotnych elementów wspólnych dla wszystkich programów języka Java. Przyjrzyjmy się bliżej poszczególnym wierszom kodu. Program rozpoczyna się od następujących wierszy. /* Toje st przykładowy program w języku Java. Nadaj plikowi nazwę "Example.java".
*/
Jest to komentarz. Podobnie jak większość innych języków programowania, także Java umożliwia oznaczenie fragmentów kodu, które mają zostać pominięte przez kompilator w trakcie analizy. Ponieważ kompilator pom ija ten kod, na ogół umieszcza się w nim komentarze opisujące sposób działania programu osobie, która czyta kod źródłowy. W tym przypadku komentarz informuje o tym, by plikowi źródłowemu nadać nazwę Example.java. Oczywiście w rzeczywistych programach komentarze są bardziej szcze gółowe i omawiają konkretne części programu lub konkretne funkcje. Java obsługuje trzy rodzaje komentarzy. Ten przedstawiony wcześniej nazywany jest komentarzem wielowierszowym. Musi się rozpoczynać od znaków /* a kończyć zna kami */. Kompilator ignoruje wszystko, co pojawi się między tymi kombinacjami. Jak sama nazwa wskazuje, ten rodzaj komentarza może rozciągać się na w iele wierszy. Oto następny wiersz przykładowego programu, class Example { Pojawienie się na początku słowa kluczowego c la s s oznacza, że rozpoczyna się defi nicja nowej klasy. Tekst Example to identyfikator nowotworzonej klasy. Cała definicja klasy znajdzie się między nawiasem klamrowym otwierającym ({) a nawiasem klam rowym zamykającym (}). Na razie nie trzeba zwracać zbyt dużej uwagi na klasę, ale w arto zapamiętać, że w Javie wszystkie działania programów odbyw ają się wewnątrz klas. Choćby z tego powodu wszystkie programy języka Java są obiektowe (przynajm niej w maleńkim stopniu). Następny wiersz programu zawiera komentarz jednowierszowy. / / Program rozpoczyna się od wywołania metody mainf).
51
52
Część I ♦ Język Java
Jest to drugi rodzaj kom entarzy obsługiw any przez Javę. Komentarz jednowierszowy rozpoczyna się od znaków / / a kończy w raz z końcem wiersza. Ogólnie programiści używają kom entarzy wielowierszowych dla dłuższych opisów, natomiast komentarzy jednowierszowych do krótkich wskazówek. Trzeci rodzaj komentarza, komentarz do kumentujący, zostanie dokładniej omówiony w dalszej części rozdziału w podrozdziale „Komentarze”. Kolejny wiersz program u wygląda następująco, public static void mamCString args[]) { W iersz rozpoczyna definicję metody main(). Wcześniejszy komentarz sugerował, że działanie programu rozpoczyna się właśnie od tej metody. Wszystkie aplikacje Javy zaraz po uruchomieniu wywołują metodę main(). Bardzo trudno w tym miejscu wytłuma czyć dokładnie znaczenie tego wiersza, poniew aż wymaga to dobrej znajomości zasad hermetyzacji stosowanych w Javie. Ponieważ jednak większość przy kładów z niniejszej książki posiada ten wiersz kodu, spróbuję dokonać prostego wyjaśnienia już teraz. Słowo kluczowe p u blic to modyfikator dostępności, który pozwala programiście ste rować widocznością członków klasy. Gdy ja k iś członek klasy został oznaczony jako public, jest wtedy dostępny dla kodu spoza klasy, w której został zdefiniowany. (Prze ciwieństwem do p u b lic jest modyfikator p riv a te , który oznacza, że dany członek nie jest dostępny z zewnątrz klasy). W tym przypadku metoda mainO musi zostać zdefinio wana jako public, ponieważ trzeba ją wykonać spoza kodu samej klasy w momencie uruchamiania programu. Słowo kluczowe s t a t i c oznacza, że można wywołać metodę mainO bez tworzenia konkretnego egzemplarza klasy. Jest to niezmiernie ważne, po nieważ maszyna wirtualna Javy wywołuje tę metodę zanim jeszcze zostaną utworzone jakiekolwiek obiekty. Słowo kluczowe void informuje kompilator, że metoda nie zwraca żadnej wartości. Metody mogą zwracać wartości, ale tę kwestię przećwiczymy w dal szej części książki. Jak już wspomniałem, metoda mainO jest wywoływana automatycznie w momencie uruchamiania programu napisanego w Javie. Pamiętaj o tym, że Java zwraca uwagę na wielkość liter, więc Mam to co innego niż main. Kompilator poprawnie skompiluje klasę, która nie zawiera metody main(), ale wtedy aplikacja java nie będzie potrafiła uruchomić napisanego programu. Jeżeli więc zamiast mam wpisze się Main, kompilator skompi luje program, ale w momencie jego uruchamiania aplikacja java zgłosi błąd, gdyż nie odnajdzie metody mainO. Wszystkie informacje przekazywane do metody otrzymywane są przez zmienne podane wewnątrz nawiasów okrągłych znajdujących się po nazwie metody. Zmienne te nazy wamy param etram i. Nawet jeśli do danej metody nie przekazuje się żadnych para metrów. nadal trzeba dołączyć puste nawiasy. Metoda mainO przyjmuje tylko jeden parametr, ale za to bardzo złożony. Konstrukcja String args[] deklaruje parametr o na zwie args zawierający tablicę egzemplarzy klasy Strng. (Tablice są kolekcjami obiek tów tego samego typu). Obiekty typu String przechowują ciągi znaków (teksty). W tym konkretnym przypadku parametr args otrzymuje argumenty' wpisane w wierszu poleceń w momencie uruchamiania programu. Przedstawiony kod nie korzysta z tych infor macji, ale późniejsze przykłady będą używały tych danych.
Rozdział 2. ♦ Podstawy języka Java
53
Ostatnim znakiem w wierszu jest znak {, który oznacza początek ciała metody mainO. Cały kod związany z metodą znajduje się pomiędzy nawiasem klamrowym otwierają cym i zamykającym. Ważna uwaga: metoda main() to po prostu pierwsza metoda wywoływana po urucho mieniu programu. Złożone programy zawierają dziesiątki klas. ale tylko jedna z tych klas posiada metodę mainO, która wszystko uruchamia. Jeżeli tworzy się aplety — programy Javy uruchamiane w przeglądarkach internetowych — w ogóle nie korzysta się z metody mam(), ponieważ przeglądarki korzystają z innych sposobów urucha miania apletów. Poniżej przedstawiamy kolejny wiersz kodu. Zauważ, że występuje on wewnątrz meto dy mainO. System.o u t.p r in tln("To jest przykładowy program w języku Ja v a ."):
Wykonanie wiersza powoduje wyświetlenie na ekranie tekstu „To jest przykładowy program w języku Java.” oraz przejście do nowego wiersza. Wyświetleniem zajmuje się tak naprawdę wbudowana metoda p n n tln O , która przekazuje na wyjście tekst prze kazany jako parametr. Metoda ta służy również do wyświetlania bardziej złożonych informacji. Cały wiersz rozpoczyna się od System.out. Choć wyjaśnianie wszystkich szczegółów w tym miejscu jest zbyt skomplikowane, w spom nę tylko, że System to predefiniowana klasa zapewniająca dostęp do systemu, a out to strumień wyjściowy powiązany z konsolą sytemu operacyjnego. Zapewne nikogo nie zdziwi, że w rzeczywistych programach i apletach Javy stosunko wo rzadko korzysta się z wyświetlania danych na konsoli lub pobierania z niej danych. Ponieważ na ogół dostarcza się użytkownikom interfejsy graficzne (okienka), z kon soli korzystają najczęściej proste programy narzędziowe lub demonstracyjne. W dal szej części książki dowiesz się, w jaki sposób wygenerować w yjście za pomocą Javy. Na razie pozostaniemy przy tych prostych metodach konsolowych. Zauważ, że instrukcja p n n tln () kończy się znakiem średnika (;). Wszystkie instrukcje w języku Java muszą kończyć się właśnie tym znakiem. Pozostałe wiersze programu nie mają na końcu średników, gdyż tak naprawdę nie są instrukcjami. Pierwszy znak } oznacza koniec ciała funkcji mainO a drugi znak } — koniec definicji klasy Example.
Drugi prosty program Prawdopodobnie najważniejszym elementem każdego języka programowania jest zmienna. Zmienna to nazwane miejsce w pamięci komputera, któremu program może przypisać wartość. Wartość zmiennej może ulegać zmianie w trakcie działania programu. Następny program obrazuje, w jaki sposób zadeklarować zmienną i przypisać jej wartość. Do datkowo w kodzie pojaw-iają się nowe konstrukcje związane z wyświetlaniem danych na konsoli. Zgodnie z komentarzem na początku programu, cały kod źródłowy należy umieścić w pliku Example2.java.
54
Część I ♦ Język Java
/* Kolejny p ro sty program . ^ Nadaj plikowi nazwę "Example2.ja\a".
*/ class Example2 { public s t a t ic v o id m ain(String a rg s[]) { i nt num: / / deklaracja zmiennej o nazwie num num ■ 100: II przypisanie zmiennej num wartości 100 S y s te m .o u t. println("W artość num wynosi M + num): num = num * 2: System .o u t . p r i n t ("W artość
2
* num wynosi " ) :
System .o u t p r in t ln ( n u m ) ;
} }
Po uruchomieniu program u na wyjściu pojawi się następujący tekst. Wartość num wynosi 100 Wartość 2 * num wynosi
200
Przyjrzyjmy się dokładniej, w jaki sposób został wygenerowany przedstawiony powy żej wynik. Pierwszy nowy element programu to poniższy wiersz. int num: I I deklaracja zmiennej o nazwie num Wiersz deklaruje zmienną typu całkowitego o nazwie num. Java (podobnie jak więk szość języków programowania) wymaga, aby zmienne zostały zadeklarowane przed ich użyciem. Poniżej przedstawiam ogólną postać deklaracji zmiennej. typ nazwa-zmiennej:
Element typ określa typ deklarowanej zmiennej, natomiast nazwa-zmiennej określa na zwę zmiennej. Jeżeli chce się zadeklarować kilka zmiennych tego samego typu, można użyć listy, w której poszczególne nazwy zmiennych są oddzielone przecinkami. Java definiuje kilka typów danych, między innymi liczby całkowite, liczby zmiennoprze cinkowe i znaki. Słowo kluczowe in t oznacza, że jest to liczba całkowita. Przeanalizujmy kolejny wiersz. num - 100: II przypisanie zmiennej num wartości 100
Następuje tutaj przypisanie zmiennej num wartości 100. W Javie operator przypisania to pojedynczy znak równości. Kolejny wiersz kodu powoduje wyświetlenie zawartości zmiennej num poprzedzonej tekstem „Wartość num wynosi”. System.o u t.p rin tln (" W a rto ś ć num wynosi " + num):
Rozdział 2. ♦ Podstawy języka Java
55
W tej instrukcji znak plus (+) powoduje dołączenie wartości zmiennej num do wcześniej szego tekstu. Następnie połączony tekst jest wysyłany na standardowe wejście. (Tak naprawdę zmienna nurn jest najpierw konwertowana z typu całkowitego na ciąg zna ków, a dopiero później dołączana do wcześniejszego tekstu. Cały proces jest dokładniej omówiony w dalszej części książki). Przedstawione podejście można uogólnić — ope rator + potrafi złączyć dowolną ilość elementów, które mają zostać wyświetlone za pomocą metody p rin tln O . Kolejny wiersz kodu przypisuje zmiennej num wynik mnożenia tej zmiennej przez 2. Podobnie jak w większości innych języków programowania, operator * służy do mnoże nia wartości. Po wykonaniu tego wiersza w zmiennej num znajdzie się wartość 200. Oto dwa kolejne wiersze programu. System.o u t.p rin t("Wartość 2 * num wynosi "): System.out .p rin tln(num):
Pojawia się tutaj kilka nowych elementów. Po pierwsze, metoda p rin t() służy do wy świetlenia tekstu „Wartość 2 * num wynosi”, ale bez przechodzenia do nowego wiersza. Oznacza to, że po wyświetleniu tekstu kursor pozostanie w tym samym wierszu. Metoda p n n tO działa dokładnie tak samo jak metoda p rin tln O , ale nie wstawia na końcu znaku przejścia do nowego wiersza. Drugi wiersz zawiera wywołanie metody p n n tln ( ). Zauważ, że jedynym parametrem jest zmienna num. Metody p rin tO i p r in t ln ( ) służą do wyświetlania wartości zawartych w dowolnych zmiennych typów prostych.
Dwie instrukcje sterujące Choć w rozdziale 5. dokładniej omówię wszystkie rodzaje struktur sterujących, przedstawię tutaj dwie z nich, ponieważ pozwoli to lepiej zrozumieć przykłady zawarte w rozdzia łach 3. i 4. Pomogą one również zilustrować ważny aspekt języka Java — bloki kodu.
Instrukcja if Instrukcja i f języka Java działa bardzo podobnie do instrukcji IF z innych języków programowania. Co więcej, jej syntaktyka jest dokładnie taka sama jak w językach C, C++ i C#. Najprostsza postać instrukcji i f jest następująca. i f ( warunek) instrukcja :
Element warunek to wyrażenie logiczne. Jeśli warunek jest prawdziwy, zostanie wykona na instrukcja. W przeciwnym przypadku instrukcja zostanie pominięta. Oto przykład. i f ( num < 100) p rin tln O n u m j e s t m niejsze od 100"):
Jeśli wartość zmiennej numjest mniejsza od 100, warunek będzie prawdziwy, więc zo stanie wykonana instrukcja p rin tln O . Jeżeli zmienna num zawiera wartość większą lub równą 100, wtedy instrukcja p rin tln O nie zostanie wykonana.
56
Część I ♦ Język Java
W rozdziale 4. przedstawię pełną listę operatorów relacyjnych zdefiniowanych w języku Java, które służą do tworzenia w arunków. Kilka z nich przedstawia tabela 2.1. Tabela 2.1. Podstawowe operatory porównań Operator
Znaczenie
Większy od
==
Równy Zauważ, że równość dwóch elem entów sprawdza się podwójnym znakiem równości Oto kod programu ilustrującego instrukcję if . /* Ilustracja wykorzystania instrukcji if. Nadaj plikowi nazwę "IfSample.java".
*/ cla ss IfSample { p u b lic s ta t ic void main(String a rg s[]) { in t x. y: x - 10: y - 20: if ( x < y) System .out.printlnC'x je s t mniejsze od y "): x - x * 2: if ( x - - y) System .out.printlnO 'teraz x je st równe y "): x - x * 2: if ( x > y) S ystem .ou t.prin tln C teraz x jest większe od y"):
/ / poniższa instrukcja pnntlnf) nie zostanie wykonana if ( x - - y) System .out.println("tego nie zobaczysz"):
} }
Wyjście generowane przez program jest następujące. x je s t mniejsze od y teraz x je s t równe y teraz x je s t większe od y
Zauważ w programie pewną nowinkę, a mianowicie poniższy wiersz. in t x. y:
W jednym wierszu kodu deklarujemy dwie zmienne, x i y, stosując listę oddzieloną przecinkami.
Rozdział 2. ♦ Podstawy języka Java
Pętla for Pętle są niezwykle ważnym elementem każdego języka programowania. Java nie jest tu wyjątkiem. W rozdziale 5. pokażę, że Java posiada całkiem imponujący zestaw różnych pętli. Prawdopodobnie najbardziej elastyczną z tych pętli jest pętla fo r. W języku Java działa ona dokładnie tak samo jak w językach C, C++ i CU, więc gdy zna się te języki, nie powinna ona sprawić najmniejszych problemów. Nawet jeśli po raz pierw szy w idzi się ten rodzaj pętli, nauczenie się jej też nie jest trudne. Poniżej znajduje się najprostsza postać pętli for. fo rU n lc ja liz a c J a : warunek: ite ra c ja ) in stru kcja:
Najczęściej element im c ja liza c ja służy do ustawienia wartości początkowej zmien nej sterującej pętlą. Element warunek to wyrażenie logiczne, które sprawdza zmienną sterującą. Jeśli warunek jest prawdziwy, pętla jest wykonywana. W przeciwnym razie dochodzi do zakończenia wykonywania pętli. Element iteracja określa sposób zmiany zmiennej sterującej w każdej iteracji pętli. Poniżej znajduje się prosty program ilustrujący działanie pętli for. /* Ilustracja użycia pętli for. Nadaj plikowi nazwę "ForTestjava".
*/ c la s s ForTest { public s ta tic void main(String args[]) { in t x: for(x - 0: x:
} } Przyjrzyjmy się bliżej rozszerzaniu typów występującym w poniższym wierszu pro gramu. double result * ( f * b) + ( i / c) - (d * s);
80
Część I ♦ Język Java
W pierwszym podwyrażeniu, f * b, b zostaje rozszerzone do typu flo a t, więc całe podwyrażenie jest typu flo a t. W następnym podwyrażeniu, i / c, c zostaje rozsze rzone do typu in t. więc całe podw yrażenie je st typu in t. W kolejnym podwyrażeniu, d * s, s zostaje rozszerzone do typu double, więc całe podwyrażenie jest typu double. Następnie rozważane są trzy wartości pośrednie typów flo a t, in t oraz double. Wyni kiem dodania typu flo a t do typu in t jest flo a t. Na końcu wynikiem odejmowania typu double od typu flo a t jest rozszerzenie do typu double, co w efekcie powoduje zwró cenie całego wyrażenia typu double.
Tablice Tablica to zbiór zmiennych tego samego typu, do których odwołujemy się przy użyciu wspólnej nazwy. Można tworzyć tablice dowolnego typu o jednym lub wielu wymia rach. Konkretny element tablicy jest dostępny poprzez swój indeks. Tablice na ogół służą do grupowania pow-iązanych ze sobą informacji. Jeżeli dobrze zna się język C lub C++ należy uważać, ponieważ w Javie tablice działają inaczej niż we wcześniej wymienionych językach.
Tablice jednowymiarowe Tablica jednowymiarowa to po prostu lista zmiennych tego samego typu. Aby utwo rzyć tablicę, trzeba najpierw zadeklarować zmienną tablicową odpowiedniego typu. Ogólna postać deklaracji tablicy jednowymiarowej jest następująca. typ nazwa -zmiennej[J:
Element typ określa typ bazowy tablicy, czyli typ poszczególnych elementów przecho wywanych w tablicy. Innymi słowy, typ bazowy określa, jakiego rodzaju dane będą mogły być przechowywane w tablicy. Poniższa deklaracja tablicy o nazwie month_days będzie przechowywać liczby całkowite typu in t. int monthdaysC]:
Choć deklaracja określa, że nazwa month_days oznacza zmienną tablicową, tak na prawdę nie powstała jeszcze żadna tablica. Java ustawi wartość zmiennej mcnth_days na w artość n u li, co oznacza, że tablica niczego nie przechowuje. Aby połączyć mon th days z rzeczywistą tablicą liczb całkowitych, trzeba ją najpierw zaalokować za pomocąl)peratora new. Operator ten powoduje alokację odpowiedniej ilości pamięci. O perator new zostanie dokładniej omówiony w kolejnych rozdziałach. Na razie wy starczy wiedzieć jedynie, iż alokuje on pamięć dla tablic. Poniżej znajduje się ogólna po stać w ykorzystania operatora do utworzenia tablicy jednowymiarowej. nazwa-zmiennej = new typLrozrmar ];
Rozdział 3. ♦ Typy danych, zmienne i tablice
81
Element typ oznacza typ danych, dla których powstaje tablica. Element rozmiar określa liczbę elementów tablicy, a element nazwa-zmiennej to nazwa zadeklarowanej wcze śniej zmiennej tablicowej. Wynika z tego, że do alokacji tablicy potrzebna jest infor macja na temat typu i liczby elementów, które mają się w niej znaleźć. Elementy tablicy alokowane operatorem new są automatycznie zerowane. Poniższy kod alokuje 12-elementową tablicę liczb całkowitych i przypisuje ją do zmiennej month_days. month days = new int[12]:
Po wykonaniu tej instrukcji zmienna month_days odnosi się do tablicy 12 liczb całko witych. Co więcej, wszystkie elementy tablicy przyjęły wartość 0. Podsumujmy: utworzenie tablicy jest procesem dwuetapowym. Najpierw deklaruje się zmienną odpowiedniego typu tablicowego. Następnie alokuje się za pomocą operatora new pamięć, która będzie przechowywała elementy tablicy, po czym przypisuje się ją do zmiennej tablicowej. Wynika z tego, że w Javie wszystkie tablice są alokowane dynamicznie. Jeśli koncepcja alokacji dynamicznej nic Czytelnikowi nie mówi, nie nale ży się przejmować, ponieważ zostanie ona opisana dokładniej w dalszej części książki. Po zaalokowaniu tablicy do jej poszczególnych elementów odwołujemy się, podając indeks elementu zawarty w nawiasach kwadratowych. Indeksy wszystkich tablic roz poczynają się od 0. Poniższy kod przypisuje wartość 28 drugiem u elementowi tablicy month_days. month days[l] - 28;
Poniższy kod spowoduje wyświetlenie wartości przechowywanej w elemencie o in deksie 3. % System.o u t.p r in tln(month_days[3]):
Po złożeniu wszystkich części otrzymujemy program, który tw orzy tablicę liczby dni w każdym z miesięcy. / / Przykład tablicy jednowymiarowej. class Array { public s t a t ic void main(String args[]) { in t month_days[]: month_days = new int[12]: month_days[0] - 31: month_days[l] - 28: month_days[2] - 31: month_days[3] - 30: month_days[4] = 31: month_days[5] = 30: month_days[6] - 31: month_days[7] - 31: month days[8] - 30: month_days[9] - 31: month_days[10] - 30: month_days[ll] - 31: System .out.printlnC'Kwiecien ma " + month_days[3] + " d n i . ” ):
}
}
82
Część I ♦ Język Java
Uruchom ienie programu spowoduje wyświetlenie liczby dni w miesiącu kwietniu. Jak wspomniano, indeksy tablic w Javie zaczynają się od zera, więc liczbę dni dla kwietnia odczytujem y jako month_days[3 ]. M ożna połączyć deklarację zmiennej tablicowej z alokacją pamięci. Oto przykład. in t month days[] • new in t[1 2 ]:
Na ogół w profesjonalnie napisanych programach stosuje się takie właśnie rozwiązanie. Tablice można inicjalizować w momencie deklaracji. Sposób wykonania tego zadania nie różni się znacząco od inicjalizacji typów prostych. Inicjalizacja tablicy polega na podaniu listy wyrażeń oddzielonych przecinkami, zawartej wewnątrz nawiasów klam rowych. Przecinki rozdzielają wartości poszczególnych elementów tablicy. Zostanie automatycznie utworzona tablica na tyle duża, aby pomieściła wszystkie przekazane elementy. Nie trzeba w takiej sytuacji stosować operatora new. Poniżej znajduje się zmo dyfikowana wersja poprzedniego przykładu. Tym razem jednak do określenia liczby dni w miesiącach stosuje inicjalizację tablicy. / / Ulepszona wersja poprzedniego programu. c la s s AutoArray { p u b lic s t a t ic void m ain(String args[]) { in t month_days[] - { 31. 28. 31. 30. 31. 30. 31. 31. 30. 31. 30. 31 }: System.o u t.p rin tln t"K w iecień ma " + month_days[3] + " dm. ") :
} } Po uruchomieniu programu pojawi się dokładnie taki sam komunikat jak dla poprzed niego przykładu. Java zawsze sprawdza, czy nie próbuje się zapisać lub odczytać wartości spoza zakresu tablicy. Innymi słowy, system wykonawczy Javy sprawdza, czy wszystkie stosowane indeksy znajdują się w poprawnym zakresie. Na przykład przy każdym odwołaniu do elem entu tablicy month_days maszyna wirtualna sprawdzi, czy indeks to wartość z za kresu od 0 do 11. Gdy zostanie przekazana wartość spoza zakresu (liczba ujemna lub powyżej długości tablicy), program zgłosi błąd wykonania. Kolejny program w nieco bardziej zaawansowany sposób korzysta z tablicy jedno wymiarowej, ponieważ oblicza średnią z kilku wartości. / / Średnia wartości znajdujący ch się w tablicy. c la s s Average { p u b lic s ta tic void mainCString args[]) { double nums[) * {10.1. 11.2. 12.3. 13.4. 14.5}: double result * 0: in t i : for(i=0; i twoDCO] - new twoD[l] - new twoD[2] - new twoD[3] - new
new i n t [ 4 ] [ ] : in t [ 5 ] : in t [ 5 ] : in t [5 ] ; in t [5 ] ;
Choć w tej sytuacji sposób ręcznej alokacji tablicy nie ma żadnej przewagi nad alokacją automatyczną, są sytuacje, w których warto go stosować. Przykładem może być sytu acja, w której poszczególne wymiary mają posiadać różną liczbę elementów. Ponie waż tablica w ielow ym iarow a jest tak naprawdę tablicą tablic, mamy pełną swobodę w dobieraniu rozmiarów podtablic. Poniższy program obrazuje, w jaki sposób wykonać dwuwymiarową tablicę, w której liczba elementów w drugim wymiarze zmienia się. / / Ręczna alokacja różnych rozmiarów dla drugiego wymiaru. c l a s s TwoDAgain {
public s ta tic void main(String args[]) { in t twoD[][] - new in t [ 4 ] [ ] : twoD[0] - new i n t [ l ] : twoD[l] = new in t [ 2 ] ; twoD[2] - new in t [ 3 ] : twoD[3] - new in t [ 4 ] : in t i . j , k - 0; fo r(i-0 : i 0) { ProcessDataO: bytesAvailabie -■ n; } else waitForMoreDataO:
W tym kodzie, jeśli wartość zmiennej bytesAvai labie jest większa od 0, zostaną wy konane obie instrukcje w bloku i f . Niektórzy programiści zawsze stosują nawiasy klamrowe dla konstrukcji if , nawet jeśli nie jest to wymagane, bo każda klauzula zawiera tylko jedną instrukcję. Takie podejście ułatwia dodanie kolejnych instrukcji w przyszłości i nie zapomina się wtedy o nawia sach. W zasadzie zapominanie o dodawaniu bloków kodu, gdy są potrzebne, to częsty powód błędów. Rozważmy następujący fragment kodu. in t bytesAvailabie;
//
...
i f (bytesAvailabie > 0) { ProcessDataO: bytesAvailabie -- n;
111
Rozdział 5. ♦ Struktury sterujące
} else waitForMoreDataO: bytesAvailable - n: Dla programisty oczywistym jest, że instrukcja bytesA vailable = n; ma zostać wy konana dla klauzuli e lse z powodu zastosowania wcięcia. Niestety, kompilator me zwraca uwagi na białe spacje, więc nie zdaje sobie sprawy z zastosowania wcięcia. Kod zostanie skompilowany bez problemów, ale nie będzie działał zgodnie z zamie rzeniami. Poprawny zapis powyższego kodu jest następujący. int bytesAvailable: / / ... i f (bytesAvailable > 0) { ProcessData(): bytesAvailable -■ n: } else { waitForMoreOatat) ; bytesAvailable - n: }
Zagnieżdżanie konstrukcji if Zagnieżdżone konstrukcje i f to takie konstrukcje if , które znajdują się we wnętrzu innej klauzuli i f lub else. Zagnieżdżenia warunków w ystępują w programowaniu niezmiernie często. Warto pamiętać o tym, że instrukcja e lse jest zawsze związana z naj bliższą, wcześniejszą konstrukcją i f z tego samego bloku kodu, która nie posiada jeszcze instrukcji e lse . Oto przykład. i f (i -- 10) { i f (j < 20) a - b: i f (k > 100) C = d: II ta instrukcja if je s t powiązana else a = c: / / z tą instrukcją else else a - d:
I I ta instrukcja else dotyczy if(i
== 10)
Zgodnie z komentarzami, ostatnia klauzula e ls e nie jest pow iązana z if(j< 2 0 ), po nieważ nie znajduje się w tym samym bloku (choć byłaby to najbliższa instrukcja i ez else). Końcowe e ls e jest powiązane z i f (i ==10). Wewnętrzne e ls e dotyczy if(k>100), ponieważ jest najbliższym i f wewnątrz tego samego bloku.
Konstrukcje if-else if Typową konstrukcją, która bazuje na ciągu zagnieżdżonych konstrukcji if , jest kon strukcja if - e ls e i f . Ogólna składnia takiego rozwiązania jest następująca. i f (warunek) instrukcja: else if (warunek) instrukcja: else if (warunek) instrukcja:
112
Część I ♦ Język Java
else instrukcja:
Instrukcje i f wykonuje się z góry do dołu. Java szuka pierwszej konstrukcji if , dla której spełniony jest warunek. Jeśli zostanie znaleziona, zostaną wykonane zawarte w niej instrukcje, a pozostała część ciągu zostanie pominięta. Jeśli żaden z warunków nie będzie prawdziwy, zostaną wykonane instrukcje z klauzuli e lse znajdującej się na samym końcu ciągu. Ostatnia instrukcja else działa jak warunek domyślny, który zosta nie wykonany, gdy wszystkie poprzednie warunki będą fałszywe. Poniższy program używa ciągu konstrukcji i f w celu określenia pory roku związanej z danym miesiącem. / / Przykład użycia ciągu konstrukcji if-else if class IfE ls e { public static void m a in (S trin g a r g s []) { int month - 4: / / Kwiecień S trin g season: i f (month — 12 | | month - season = “zima":
1 | | month — 2 )
else i f (month — 3 | | month == 4 || month == 5) season = "wiosna” ;
else i f (month — 6 | | month == 7 || month == 8) season = " la to " :
else if(m o n th == 9 11 month *==10 || month == 11) season = " je s ie ń " ;
el se season = "Biedny m ie s ią c ": System.out .p r in t ln O M ie s ią c kwiecień n ależy do pory roku " + season +
} } Program powoduje wyświetlenie następującego komunikatu. M ie s ią c kw iecień n ależy do pory roku wiosna.
Warto poeksperymentować z tym programem. Niezależnie od tego, który miesiąc się ustawi, i tak zawsze zostanie wykonana tylko jedna instrukcja przypisania z ciągu konstrukcji if .
Konstrukcja switch Konstrukcja switch to wielościeżkowe rozgałęzienie. Ułatwia skierowanie działania program u do odpowiedniego fragmentu kodu w zależności od wartości w yrażenia. Z tego powodu, gdy tylko jest to możliwe, warto używać jej zamiast ciągu konstrukcji i f-e l se i f. Oto ogólna postać konstrukcji switch. switch(wyrażen/e){ case wartości: II ciąg instrukcji break:
113
Rozdział 5. ♦ Struktury sterujące
case
wartośćż:
II ciąg instrukcji break:
case
wartośćN:
/ / ciąg instrukcji b re a k ; d e f a u lt:
/ / domyślny ciąg instrukcji
Wyrażenie musi być typu byte, s h o rt, in t lub char, a każdy z elem entów wartość z case musi być typu zgodnego z wyrażeniem. (Do sterow ania konstrukcją sw itch można również wykorzystać wyliczenie, ale więcej informacji na ten temat znajduje się w rozdziale 12.). Każda z wartości case musi być unikatowym literałem (czyli musi być stałą). Powtórzenie wartości w dwóch klauzulach case je st niedozwolone. Konstrukcja switch działa następująco: wartość wyrażenia jest porównywana z po szczególnymi literałami znajdującymi się w klauzulach case. Jeżeli znaleziono dopa sowanie, wykonywana jest część kodu znajdująca się za klauzulą case. Gdy nie udało się dopasować wyrażenia, zostają wykonane instrukcje podane za klauzulą d e fa u lt. Klauzula d e fa u lt jest opcjonalna. Jeśli nie występuje i nie udało się dopasować wyra żenia, nie są wykonywane żadne instrukcje zawarte w konstrukcji switch. Instrukcja break wewnątrz konstrukcji switch służy jako zakończenie ciągu instrukcji. Jej wykonanie powoduje przeniesienie sterowania do pierwszego wiersza za całą kon strukcją switch. Uzyskuje się efekt wyjścia z konstrukcji switch. Oto prosty przykład, który używa konstrukcji switch. II Prosty przykład użycia konstrukcji switch. class SampleSwitch { p u b lic s t a t i c void m ainCString a r g s [ ] ) { f o r t i n t i-0: i 0) { S y s te m .o u t.p r in tln C ta k t " + n ); n--:
} } } Program po uruchomieniu odmierzy 10 taktów. ta k t 10 ta k t 9 ta k t 8 ta k t ta k t ta k t ta k t ta k t ta k t ta k t
7 6 5 4 3 2 1
Ponieważ pętla while sprawdza wyrażenie warunkowe na początku, ciało pętli nie wy kona się ani razu, jeśli wyrażenie od samego początku jest fałszywe (zwraca wartość fa 's e ). Na przykład w poniższym kodzie instrukcja p r in tln O nie zostanie nigdy wywołania. int a - 10. b ■ 20: while(a > b)
System.o u t.p r in tln ( " T o
h
nigdy n ie z o s ta n ie w y ś w ie tlo n e ."):
Ciało pętli while (a także pozostałych rodzajów pętli języka Java) może być puste, ponieważ pusta instrukcja (czyli taka zawierająca tylko średnik) jest w Javie instrukcją w pełni poprawną. Rozważmy następujący program. // Ciało pętli może być puste. class NoBody { public static void m a in (S trin g a r g s [ ] ) { int i. j:
118
Część I ♦ Język Java
i - 100: j - 200: / / znajduje środek między i oraz j while(++i < --j) : U brak ciała pętli
System.out.printlrU"Środek to wartość “ + i): } } Program znajduje środek między wartościami zmiennych i oraz j i generuje następu jący wynik. Środek to wartość 150 Oto sposób działania pętli while z programu. W każdej iteracji dochodzi do zmniejsze nia o jeden wartości zmiennej j i do inkrementacji zmiennej i. Następnie dochodzi do porównania obu wartości. Jeśli i nadal je st mniejsze od j, dochodzi do wykonania kolejnej iteracji. Gdy obie wartości są sobie równe, pętla kończy swoje działanie. Po wyjściu z pętli zmienna i zawiera środkową wartość dla oryginalnych wartości zmiennych i oraz j. (Oczywiście cała procedura działa tylko wtedy, gdy na początku i jest mniej sze od j). Jak łatwo zauważyć, ciało pętli jest zbędne, ponieważ wszystkie potrzebne działania występują w części warunkowej. W profesjonalnie napisanych programach Javy często pojaw iają się pętle bez ciała, jeśli właściwe działanie można zawrzeć w części warunkowej.
Pętla do-while Jak już wcześniej wspomniałem, jeśli warunek sterujący pętlą while jest fałszywy już na samym początku, ciało funkcji nie zostanie wykonane ani jeden raz. Czasem je d nak zachodzi potrzeba wykonania ciała pętli przynajmniej raz nawet wtedy, gdy wa runek jest fałszywy. Innymi słowy, chcemy sprawdzić warunek zakończenia pętli na jej końcu, zamiast na początku. Na szczęście Java udostępnia konstrukcję odpowied nią do tego celu — pętlę do-while. Pętla ta wykonuje swoje ciało przynajmniej raz, ponieważ warunek działania pętli testowany jest na samym końcu. Ogólna postać tej pętli jest następująca. do { // ciało pętli } w hile (warunek):
W każdej iteracji pętli do-while najpierw dochodzi do wykonania ciała pętli, a dopie ro później sprawdza warunek zakończenia. Jeżeli wyrażenie warunkowe jest prawdzi we, pętla wykonuje się ponownie. W przeciwnym przypadku dochodzi do jej zakoń czenia. Podobnie jak dla wszystkich innych pętli w Javie, wyrażenie musi zwracać wartość typu boolean. Poniżej znajduje się zmodyfikowana wersja programu z taktami, która używa pętli do-while. Wynik działania programu jest taki sam jak poprzednio.
119
Rozdział 5. ♦ Struktury sterujące
/ / Przykład działania pętli do-while. class OoWhile { p u b lic s t a t ic void mairUString args[]) { in t n - 10: do {
System.out.printlnCtakt " + n): n --: } while(n > 0):
} } Pętlę z powyższego programu można napisać zwięźlej w następujący sposób, do {
System.out.printlnCtakt " + n): } w hile(--n > 0 ) :
Wyrażenie (--n > 0) łączy w sobie dekrementację n oraz sprawdzenie, czy n jest większe od zera. Najpierw dochodzi do zmniejszenia wartości zmiennej n o jeden, a następnie sprawdzenia, czy nowa wartość n nadal jest większa od zera. Jeśli tak, dochodzi do następnej iteracji pętli. Jeśli nie, następuje wyjście z pętli. Pętla typu do-while przydaje się na przykład do przetwarzania menu, ponieważ naj pierw wyświetla się menu, a dopiero później dokonuje sprawdzenia warunku i ewen tualnego ponowienia menu. Rozważmy następujący program, który implementuje bardzo prosty system pomocy dla instrukcji wyboru i iteracji. / / Wykorzystanie pętli do-while do przetwarzania menu - prosty system pomocy. class Menu { public s ta t ic void main(String args[]) throws java.io.IOException { char choice: do {
System.out.printlnC'Pomcc na temat:"): System.out.printlnC 1. if" ): System.out.printlnC 2. switch"): System .out.printlnC
3. w h ile"):
System.out .printlnC 4. do-w hile"); System.out.printlnC 5. for\n"): System.out.printlnCWybierz jedna opcję:"): choice = (char) System.in.readO: } while( choice < '1' || choice > '5 '): System.out.println("\n") :
switch(choice) { case '1 ': System.out.printlnC"Konstrukcja i f :\n ") ; System out. printlnC "if(warunek) instrukcja:") : System, out. printlnC "else instrukcja:"): break: case ' 2' : System.o u t.pri n tln ("Konstrukcja sw itch:\n "): System, o u t. pri ntl n( "swi tch (wyrażenie) {"):
120
Część I ♦ Język Java
System.out.printlnC" case stała:"): System.out.println(" ciąg instrukcji"): System.out.println(" break:"): System.out.printlnC" / / ..." ) : System.o u t.p rin tln("}"): break: case ' 3' :
System.out.println("Pętla whileAn"): System.out.println("while(warunek) instrukcja:"): break: case ' 4 ' :
System.out.print1n("Pętla do-while:\n"); System.out.printlnC "do {"): System.o ut.pri n tln (" i nstrukcja:"): System.out.printlnC"} while (warunek):”): break: case ' 5' :
System.out.println("Pętla fo r:\n "): System .out.print("for(inicjalizacja; warunek; iteracja)"): System.out.printlnC" instrukcja:”); break:
} } } Oto przykładowy efekt uruchomienia programu i wybrania opcji 4. Pomoc na temat: 1. if 2 . switch 3. while 4 do-while 5. for
Wybierz jedną opcję: 4
Pętla do-while: do {
instrukcja: } while (warunek):
Program wykorzystuje pętlę do-while do sprawdzenia, czy użytkownik dokonał popraw nego wyboru. Jeśli nie wpisał wartości od 1 do 5, jest proszony o ponowne wpisanie wartości. Ponieważ menu musi zostać wyświetlone co najmniej raz, pętla do-while jest idealnym rozwiązaniem. Kilka dodatkowych uwag dotyczących tego rozdziału. Zauważ, że znaki z klawiatury odczytuje się za pomocą wywołania System, in. read(). Jest to jedna z metod standar dowego wejścia konsoli. Choć dokładne omówienie metod wejścia-wyjścia znajduje się dopiero w rozdziale 12., metodę System, in. read O stosujemy ju ż tutaj. Odczyty wane znaki są typu in t, więc trzeba je dodatkowo rzutować na typ char. Domyślnie standardowe wejście jest buforowane wierszami, co oznacza, że trzeba nacisnąć kla wisz Enter, aby wpisane znaki zostały przesłane do programu. Pobieranie danych z konsoli w Javie nie jest zadaniem najprostszym. Ponieważ jednak większość programów Javy posiada interfejs graficzny, w tej książce nie poświęcam zbyt dużo uwagi obsłudze konsoli. W przedstawionym kontekście użycie konsoli jest
Rozdział 5. ♦ Struktury sterujące
121
jak najbardziej logiczne. W arto zwróć uwagą na jeszcze jedną kwestię. Z powodu w yw ołania metody System, in . read O, program musi jaw nie określić, iż może zgłosić wyjątek — fragment throws ja v a .i o . IOException. Stanowi to element obsługi wy jątków w Javie. Ten temat zostanie dokładniej omówiony w rozdziale 10.
Pętla for Podstawowa postać pętli fo r została już omówiona w rozdziale 2. Pętla ta jest wyjąt kowo użyteczna i elastyczna. Od wersji J2SE 5 istnieją w Javie dwie postacie pętli f o r . Pierwsza podstawowa wer sja istnieje od początku Javy, druga to pętla typu „for-each” . Oto ogólna postać tradycyjnej wersji pętli. for ( inicjałizacja: warunek: iteracja) { / / ciało pętli } Jeśli pętla zawiera tylko jedną instrukcję, nie trzeba stosować nawiasów klamrowych. Pętla fo r działa w następujący sposób: przy uruchamianiu pętli dochodzi do wykonania części lm cja liza cja . Najczęściej część ta służy do ustawienia wartości początkowej zm iennej sterującej pętlą. Zmienna ta na ogół traktowana jest jako licznik. Warto pamiętać o tym, że część inicjalizująca wykonywana jest tylko raz. Następnie docho dzi do sprawdzenia części warunek, która musi zwrócić wartość typu boolean. W tej części na ogół znajduje się warunek zakończenia pętli. Jeśli jest on prawdziwy, do chodzi do kolejnej iteracji pętli. W przeciwnym przypadku pętla kończy swoje dzia łanie. Część iteracja jest wykonywana w każdej iteracji po przejściu przez ciało pę tli. Innymi słowy, w kolejnych iteracjach najpierw' dochodzi do sprawdzenia warunku zakończenia pętli, następnie jest wykonywane ciało funkcji, a na końcu dochodzi do wyliczenia wyrażenia iteracji. Cała sytuacja powtarza się wielokrotnie aż do momen tu, gdy warunek pętli stanie się fałszywy. Oto wersja programu „takt” stosująca pętlę for. / / Przykład pętli for. cla ss ForTick { public static void main(String args[]) { in t n:
for(n=10: n>0: n--) System.out.println(“takt " + n): } }
Deklaracja zmiennej sterującej wewnątrz pętli for Często zmienna sterująca pętlą fo r jest potrzebna tylko wewnątrz samej pętli — nie jest stosowana nigdzie poza nią. W takim przypadku warto zadeklarować zmienną wewnątrz części inicjacyjnej pętli. Poniższa modyfikacja poprzedniego programu dekla ruje zmienną sterującą n typu i n t wewnątrz pętli for.
122
Część I ♦ Język Java
/ / Deklaracja zmiennej sterującej wewnątrz p ę tli. c la s s ForTick { p u b lic s t a t ic void m ain(String args[]) {
/ / tutaj n je st deklarowane wewnątrz pętli fo r fo r ( in t n-10: n>0; n--) System.o u t.p r in t ln C t a k t " + n):
} } G dy deklaruje się zm ienną wewnątrz pętli fo r, należy pamiętać o tym, że zasięg takiej zmiennej kończy się w raz z pętlą. Zasięg zmiennej jest ograniczony do pętli for. Poza pętlą zmienna po prostu przestaje istnieć. Jeśli zmienna sterująca musi zostać użyta w innej części programu, trzeba ją zadeklarować przed pętlą. Ponieważ najczęściej zmienna sterująca nie je st potrzeba w innych częściach progra mu, większość program istów języka Java umieszcza jej deklarację wewnątrz pętli for. Poniżej znajduje się kod programu, który sprawdza, czy podana liczba jest liczbą pierw szą. Zauważ, że deklaracja zmiennej sterującej i znajduje się wewnątrz pętli, ponieważ nie jest ona potrzebna w innych częściach programu. / / Poszukiwanie liczb pierwszych. c la s s FindPrime { p ublic s ta tic void m ain(String arg$[]) { in t num: boolean isPrime = true: num = 14; fo r( in t i»2: i wyrażeniu warunkowym powoduje / / je g o automatyczne rozpakowanie. i f (b) System .out.printlnC b wynosi true"): l i automatyczne otoczenie typu char
Character ch = 'x ‘ : II otoczenie char char ch2 = Ch: / / rozpakowanie char System .out.println("ch2 wynosi " + ch2):
} } Program generuje następujące wyniki. b wynosi true ch2 wynosi x
W tym programie warto przede wszystkim zauważyć, iż zachodzi automatyczne roz pakowanie b w wyrażeniu warunkowym instrukcji if. Każde wyrażenie instrukcji i f musi dać w wyniku wartość typu boolean. Ponieważ b zawiera wartość logiczną, do chodzi do automatycznego rozpakowania i użycia tej wartości jako wyniku wyrażenia. Z tego względu wspomniana instrukcja jest dla kompilatora w pełni poprawna. Dzięki automatycznemu rozpakowywaniu można stosować obiekt Boolean w struktu rach sterujących. Wewnątrz pętli while, for i do-while dochodzi do automatycznego rozpakowania obiektu do typu boolean. Poniższy kod jest w pełni poprawny.
Rozdział 12. ♦ Wyliczenia, automatyczne otaczanie typów prostych i matadane
303
Boolean b: / / ...
while(b) { / / ...
Automatyczne otaczanie pomaga zapobiegać błędom Automatyczne otaczanie, poza samą wygodą, pomaga zapobiegać błędom programistów. Rozważmy następujący program. / / Błąd przy ręcznym rozpakowywaniu. class UnboxingError { public static void main(String args[]) { Integer iOb “ 1000: / / automatyczne otoczenie wartości ł 000 i nt i *= iOb.byteValueO: / / ręczne wydobycie wartości!!! System, out. p r in t ln t i ):
// nie spowoduje wyświetlenia wartości 1000!
)
.
} Program nie wyświetli oczekiwanej wartości 1000, ale -24! Wynika to z faktu, iż rozpa kowanie zostało wykonane metodą byteValue(), która spowodowała obcięcie wartości do zakresu dostępnego dla typu byte. A utom atyczne rozpakowywanie zabezpiecza przed tego rodzaju błędami, ponieważ Java automatycznie zastosuje metodę zgodną z typem in t. Ponieważ automatyczne otaczanie zawsze tworzy odpowiedni obiekt a automatyczne wydobywanie zawsze pobiera odpowiednią wartość, nie można doprowadzić do błęd nego doboru typu i wartości. W tych rzadkich przypadkach, gdy jednak chce się do konać zmiany typu, zawsze można zastosować ręczne otaczanie lub rozpakowywanie. Oczywiście traci się wtedy zalety automatyzacji. Ogólnie nowo tworzony kod języka Java powinien stosować automatyczne otaczanie.
Słowo ostrzeżenia Ponieważ teraz Java dokonuje automatycznego otaczania i rozpakowywania, niektóre osoby mogą chcieć porzucić stare typy i stosować tylko i wyłącznie obiekty Integer lub Double. Można na przykład napisać następujący kod. / / Złe użycie automatycznego otaczania! Double a. b. c: a - 10.0: b - 4 0: c = Math.sqrt(a*a + b*b): System.out.print InCPrzeciwprostokątna ma wartość " + c):
304
Część I ♦ Język Java
Obiekty typu Double przechowują wartości używane do obliczenia przeciwprostokątnej trójkąta prostokątnego. Choć kod jest poprawny od strony technicznej i wykonuje swoje zadanie bez problemów, stanowi bardzo zły przykład użycia automatycznego otaczania. Dużo wydajniejsze obliczeniowo będzie w tym przypadku zastosowanie typu prostego double. Wynika to z faktu, iż automatyczne otaczanie wprowadza narzut, który' nie ist nieje dla typów prostych. Ogólnie należy stosować typy otoczkowe tylko wtedy, gdy potrzebna jest obiektowa reprezentacja typów prostych. Automatyczne otaczanie nie zostało dodane do Javy, by po cichu wyeliminować typy proste.
Metadane (notatki) J2SE 5 dodaje do Javy nowy element nazwany metadanymi. Umożliwia on dołączanie do kodu źródłowego dodatkowych informacji. Informacje te, nazywane notatkami, nie wpływają na działanie programu (nie zmieniają semantyki programu). Są za to stoso wane przez różne narzędzia w trakcie tworzenia i wdrażania oprogramowania. Na przy kład notatka może zostać przetworzona przez generator kodu źródłowego. Choć firma Sun nazywa now ą cechę języka metadanymi, być może lepszą nazw ą byłby termin mechanizm tworzenia notatek w programie.
Podstawy tworzenia notatek Notatki tworzy się przez mechanizm bazujący na interfejsie. Zacznijmy od przykładu, który deklaruje notatkę MyAnno. / / Przykład prostej notatki. ^interface MyAnno { String s t r ( ): in t val ():
} Zauważ znak @poprzedzający słowo kluczowe interface. Infonnuje on kompilator o two rzeniu notatki. W ew nątrz notatki znajdują się dwie deklaracje metod s tr ( ) i v a l(). Wszystkie notatki składają się tylko z deklaracji metod. Nie podaje się kodu deklarowa nych metod, Java sama je implementuje. Co więcej, metody te zachowują się jak zmienne. Notatka nie może zawierać klauzuli extends. Z drugiej strony, wszystkie notatki auto matycznie rozszerzają interfejs Annotation. Deklaracja tego głównego interfejsu znajduje się w pakiecie ja v a . la n g .annotation. Przesłania on metody hashCodeC), equals ( ) i toS tringO zdefiniowane w klasie Object. Dodatkowo zawiera metodę annotationType(), która zwraca obiekt Class reprezentujący wywołaną notatkę. Po zadeklarowaniu notatki można zacząć jej używać do dodawania metadanych do innych deklaracji. W zasadzie metadane można dołączać do dowolnych deklaracji: klas, metod, zmiennych, parametrów i stałych wyliczeniowych. Można nawet dodać metadane do no tatki. We wszystkich przypadkach treść notatki musi się znaleźć przed właściwą deklaracją.
Rozdział 12. ♦ Wyliczenia, automatyczne otaczanie typów prostych i matadane
Określanie treści notatki polega na nadaniu wartości jej składowym. Oto przykład określenia notatki MyAnno dla metody. // Treść notatki dla metody. @MyAnno(str = "Przykład notatki". val « 100) public s ta tic void myMethO { // ...
Notatka jest powiązana z metodą myMethO. Warto dobrze przyjrzeć się stosowanej składni. Nazwa rodzaju notatki jest poprzedzona znakiem a w nawiasach znajduje się lista ini cjalizująca składowe. Aby nadać składowej wartość, wystarczy podać nazwę składowej, znak równości i wartość. W tym przykładzie tekst „Przykład notatkiv zostaje przypi sany do składowej str. Zauważ, że w przypisaniu po nazwie składowej nie pojaw iają się nawiasy. W tym kontekście metoda wygląda jakby była zmienną składową.
Określenie strategii zachowania Przed przejściem do szczegółowego omówienia notatek, warto wspomnieć o strateg ii zachowania notatek. Określa ona, w którym momencie notatki są pomijane. Java defi niuje trzy takie strategie — znajdują się one w wyliczeniu java . lang.annotation.RetentionPol icy. Nazwy tych strategii to SOURCE, CLASS i RUNTIME. Notatka ze strategią SOURCE je st przechowywana jedynie w pliku kodu źródłowego. Innymi słowy, jest pomijana w trakcie kompilacji kodu. Notatka ze strategią CLASS zostaje umieszczona w pliku .class w trakcie kompilacji. Nie jest jednak dostępna dla maszyny wirtualnej w trakcie wykonywania programu. Notatka ze strategią RUNTIME zostaje umieszczona w pliku .class w trakcie kompilacji i jest dostępna dla maszyny wirtualnej w trakcie wykonywania programu. Oznacza to, iż ta strategia zapewnia największą trwałość notatki. Strategię zachowania dla notatki określa się, używając jednej z wbudowanych nota tek: ^Retention. Oto ogólna postać tej notatki. (PRetent i on (s trategia -zachowani a )
Element strategia -zachowania musi być jedną z wcześniej omówionych stałych wy liczeniowych. Jeżeli nie określi się jaw nie żadnej strategii, domyślnie zostanie użyta strategia CLASS. Poniższa wersja notatki MyAnno używa ^Retention do określenia strategii zachowania RUNTIME. Oznacza to, że notatka MyAnno będzie dostępna dla maszyny wirtualnej w trak cie wykonywania programu. @Retention(RetentionPol icy. RUNTIME) ^interface MyAnno { String s tr(): in t val():
}
.
305
306
Część I ♦ Język Java
Pobieranie notatek w trakcie działania programu dzięki refleksji Choć notatki zostały zaprojektowane przede wszystkim dla narzędzi programistycznych i wdrożeniowych, można je pobierać w trakcie działania programu, korzystając z refleksji. Refleksja umożliwia pobranie informacji na temat klasy w trakcie działania programu. Istnieje wiele zastosowań tego mechanizmu, więc nie będę ich tu wszystkich wymieniał. Przedstawię jedynie kilka przykładów związanych z notatkami. Pierwszy krok wykorzystania refleksji polega na pobraniu obiektu Class reprezentującego klasę, której notatki chce się uzyskać. Klasa Class to jedna z klas wbudowanych w język Java i znajdujących się w pakiecie java. 1ang. Jest ona szczegółowo omówiona w części II książki. Istnieje wiele sposobów uzyskania obiektu Class. Jednym z nich jest zastoso wanie metody getCl ass ( ) zdefiniowanej w klasie Object. Oto ogólna postać metody. fin a l Class getClassO
Metoda zwraca obiekt Class reprezentujący klasę obiektu, dla którego odbyło się wy wołanie. Mając dostęp do obiektu Class, można użyć jego metod do uzyskania informacji na temat jego elementów, także notatek. Aby pobrać notatkę związaną z konkretnym elementem zadeklarowanym w klasie, trzeba najpierw pobrać obiekt reprezentujący ten element. Klasa Class zawiera między innymi metody getMethodO, getFieldO i getConstruct o r ( ), które pobierają informacje odpowiednio o metodzie, polu i konstruktorze. Metody zwracają obiekty typu Method, Field i Constructor. Aby lepiej prześledzić cały proces, najlepiej wykonać przykład pobierający notatki zwią zane z metodą. W tym celu trzeba pobrać obiekt Class reprezentujący klasę a następnie wy wołać dla niego metodę getMethodC), przekazując nazwę metody jako parametr. Ogólna postać metody getMethodC) jest następująca. Method getMethodCString nazwaMetody. Class . .. typyParametrów)
Nazwę metody przekazuje się w elemencie nazwaMetody. Jeśli metoda przyjmuje argu menty, trzeba przekazać obiekty Class reprezentujące typy parametrów (element typy Parametrów). Zauważ, że typyParametrów to parametr o zmiennej liczbie argumentów. Oznacza to, iż można przekazać dowolną liczbę typów parametrów, również zero. Metoda getMethodO zwraca obiekt Method reprezentujący metodę. Jeśli nie uda się odnaleźć metody, zostanie zgłoszony wyjątek NoSuchMethodException. Obiekty Class, Method, Field i Constructor posiadają metodę getAnnotationO, dzięki której pobiera się notatki związane z danym elementem. Ogólna postać tej metody jest następująca. Annotation getAnnotationCClass typNotatki)
Element typNotatki to obiekt Class reprezentujący notatkę, którą jesteśm y zaintere sowani. Metoda zwraca referencję do notatki. Używając jej, można pobrać wartości zwią zane z poszczególnymi składowymi notatki.
Rozdział 12. ♦ Wyliczenia, automatyczne otaczanie typów prostych i matadane
307
Poniżej znajduje się program łączący w sobie wszystkie wcześniejsze fragmenty układanki i używający refleksji do wyświetlenia notatki związanej z metodą. import ja v a .Tang.annotation.*: import ja v a .la n g .re fle ct.* :
/ / Deklaracja typu notatki. @Retent i on(Retent i onPo1i c y .RUNTIME) ^interface MyAnno { String s tr( ): in t val();
} cla ss Meta {
/ / Utworzenie notatki dla metody. @MyAnno(str = "Przykładowa notatka". val - 100) public s ta tic void myMethO { Meta ob - new MetaO:
// Pobranie notatki dla tej metody i / / wyświetlenie wartości je j składowych. try {
/ / Najpierw pobierz obiekt Class reprezentujący I I aktualną klasę. Class c - ob.getClassO; / / Pobierz obiekt Method reprezentujący I I aktualną metodę.
Method m - c.getMethodCmyMeth” ):
/ / Następnie pobierz notatkę dla tej metody. MyAnno anno - m.getAnnotation(MyAnno.c la s s ) ;
/ / Wyświetl wartości notatki. System .out.println(anno.str() + " " + anno.valO ): } catch (NoSuchMethodException exc) { System .out.printlnC'Nie znaleziono metody."):
} } public s ta tic void main(String args[]) { myMethO:
} } Wynik działania programu jest następujący. Przykładowa notatka 100
Program używa refleksji do pobrania i wyświetlenia wartości elementów s t r i val notatki MyAnno przypisanej do metody myMethO klasy Meta. Warto zwrócić szczególną uwagę na dwa wiersze. Oto pierwszy z nich. MyAnno anno = m.getAnnotation(MyAnno.c l a s s ) :
308
Część I ♦ Język Java
Zauważ wyrażenie MyAnno.class. które powoduje zwrócenie obiektu Class dla typu MyAnno. Taką konstrukcję nazywamy literałem klasow ym . Można go użyć zawsze wtedy, gdy potrzebny jest obiekt Class znanej klasy. Poniższa instrukcja służy do pobrania obiektu Class klasy Meta. Class c - Meta.c la s s :
Oczywiście takie rozwiązanie działa tylko wtedy, gdy z wyprzedzeniem zna się nazwę klasy. Nie zawsze w ystępuje taka sytuacja. Ogólnie literał klasowy można pobrać dla klasy, interfejsu, typu prostego i tablicy. Drugi wiersz, którym warto się zainteresować, dotyczy sposobu pobierania wartości elementów s t r i val w momencie ich wyświetlania. System .out.println(anno.strO + " " + anno.valO );
Zauważ, że jest to zwyczajne wywołanie metody o takiej samej nazwie jak element. Oczywiście to samo podejście służy do pobierania wartości notatki i przypisywania jej do zmiennej.
Drugi przykład refleksji W poprzednim programie metoda myMethO nie miała żadnych parametrów. W wy wołaniu getM ethod() wystarczyło więc przekazać jedynie nazwę metody. Aby pobrać metodę z parametrami, trzeba dodatkowo przekazać do metody getMethodO obiekty klas reprezentujące typy parametrów. Oto odrobinę zmieniona wersja wcześniejszego programu. import ja v a .la n g .annotation.*: import ja v a .la n g .re fle c t.* ; PRetent i on(Retent i onPo 1i c y . RUNT1ME) P in te rface MyAnno { String s t r ( ): in t v a l():
] class Meta {
/ / Metoda myMeth ma teraz dwa parametry. PMyAnnotstr - "Dwa parametry” . val - 19) public s t a t ic void myMethCString s tr . in t i)
{ Meta ob = new MetaO: try { Class c - ob.getClassO :
/ / Tutaj pojawia się określenie typów parametrów. Method m - c.getMethod("myMeth", S trin g .class, in t . cla ss): MyAnno anno = m.getAnnotation(MyAnno.c la s s ): System .out.println(anno.str() + ” " + anno.valO): } catch (NoSuchMethodException exc) {
Rozdział 12. ♦ Wyliczenia, automatyczne otaczanie typów prostych i matadane
309
System .out.println("Nie znaleziono metody.” ):
} } public s ta tic void main(String args[]) { myMethCtest” . 10):
1 } Wynik działania tej wersji programu jest następujący. Dwa parametry 19
Metoda myMeth() przyjmuje dwa parametry — jeden typu String i drugi typu in t. W celu pobrania informacji na temat metody konieczne jest wywołanie poniższej wersji metody getMethodC). Method m - c.getMethodC'myMeth". S tring, cla ss, in t. cl ass):
Jako dodatkowe argumenty trzeba przekazać obiekty Class reprezentujące klasę String i typ in t.
Pobranie wszystkich notatek Do pobrania wszystkich notatek związanych z elementem należy użyć metody getAnnotations() tego elementu. Oto ogólna postać metody. Annotation[] getAnnotations!)
Zwrócona zostaje tablica notatek. M etodę można wywołać dla obiektów typu Class, Method, Constructor i Field. Poniżej znajduje się kolejny przykład wykorzystania refleksji do pobrania wszystkich notatek związanych z klasą i metodą. Kod deklaruje dwa typy notatek. Są one używane do dołączenia dodatkowych informacji do klasy i metody. / / Przedstawienie wszystkich notatek klasy i metody. import ja v a .lang.annotation.*: import ja v a .lang.re fle c t.* : PRetenti on(Retenti onPolicy.RUNTIME) Pin ter face MyAnno { String s tr(): in t val():
} PRetention(Retenti onPolicy.RUNTIME) ^interface What { String d e scrip tio n !);
} PWhat(description * "Klasa testowa notatek") PMyAnno(str - "Meta2". val - 99) class Meta2 {
310
Część I ♦ Język Java
@What(d e scrip tio n = "Metoda testowa notatek") @MyAnno(str - "Testowanie " . val - 100) p u b lic s t a t ic void myMethO { Meta2 ob - new Meta2(): try { Annotation annos[] = ob.getClassO .getAnnotationsO:
/ / Wyświetlenie wszystkich notatek Meta2. System .out.pnntlnO W szystkie notatki Meta2:"): fo r (Annotation a : annos) System.out.p rin tln (a ): S ystem .out.pnntlnO ;
/ / Wyświetlenie w systkich notatek myMeth. Method m - ob.getClass( ) .getMethodCmyMeth"); annos - m.getAnnotations() : System .out.println("W szystkie notatki myMeth:"); for(Annotation a : annos) S ystem .ou t.p rin tln (a): } catch (NoSuchMethodException exc) { System.o u t.println("Metody nie znaleziono."):
} } p u b lic s t a t ic void main(String args[]) { myMeth( ) :
} } Wynik działania programu jest następujący. Wszystkie notatki Meta2: @What(description-Klasa testowa notatek) @MyAnno(str=Meta2. val=99) Wszystkie notatki myMeth: @What(description=Metoda testowa notatek) @MyAnno(str=Testowanie. val-100)
Program używa metody getAnnotationsO do pobrania tablicy notatek związanych z kla są Meta2 i m etodą myMethO. Metoda ta zwraca tablicę obiektów Annotation. Przypo mnę, iż klasa Annotation stanowi interfejs bazowy dla wszystkich notatek i przesłania metodę toS tringO klasy Object. Z tego powodu próba wyświetlenia notatki powoduje wywołanie metody to S trin g (), która zwraca pełny opis notatki.
Interfejs AnnotatedElement Metody getAnnotationO i getAnnotationsO użyte we wcześniejszych przykładach są zdefiniowane w nowym interfejsie AnnotatedElement z pakietu java. lang. reflect. Inter fejs ten wykorzystuje refleksję do pobierania notatek i jest zaimplementowany przez klasy Method, Field, Constructor, Class i Package.
Rozdział 12. ♦ Wyliczenia, automatyczne otaczanie typów prostych i matadane
311
Poza wspomnianymi wcześniej metodami interfejs definiuje jeszcze dwie inne metody. Pierwszą z nich jest getDeclaredAnnotations() o następującej ogólnej postaci. Annotation[] getDeclaredAnnotationst)
Powoduje ona zwrócenie wszystkich niedziedziczonych notatek istniejących w wywo łanym obiekcie. Druga metoda to isAnnotationPresent() o następującej postaci. boolean isAnnotationPresenUClass typNotatki)
M etoda zwraca wartość true, jeśli z wywoływanym obiektem je st skojarzona notatka typu typNotatki. W przeciwnym przypadku zwraca wartość false. M etody get Annotation O i isAnnotationPresentO korzystają z typów sparametryzowanych w celu zapewnienia bezpieczeństwa typów. Ponieważ tem at typów sparametryzowanych (szablonów) będzie omawiany dopiero w rozdziale 14., sygnatury me tod w tym rozdziale nie będą omawiane zbyt szczegółowo.
Wartości domyślne Elementom notatki można nadać wartości domyślne, które zostaną zastosowane, jeśli przypinana notatka nie będzie ich zawierała. Wartości domyślne dodaje się przez zasto sowanie klauzuli default w deklaracji elementu. Oto ogólna postać deklaracji. typ skladowaO default wartość:
W skazywana wartość musi być zgodna typem z typ. Oto przykład notatki @MyAnno z dodanymi wartościami domyślnymi. / / Deklaracja typu notatkow ego zaw ierającego wartości domyślne. @Retention(RetentionPol i cy. RUNTIME) @iinterface MyAnno { S trin g s tr() default "Testowanie"; int v a l() default 9000:
} Element s tr będzie przyjmował domyślną wartość „Testowanie” a element val domyślną wartość 9000. Oznacza to, że w momencie przypinania notatki do klasy lub metody nie trzeba podawać żadnych parametrów. W razie potrzeby można każdemu elementowi przypisać wartość inną niż domyślną. Oto cztery możliwe wersje notatki OMyAnno. @MyAnno() / / obie wartości domyślne @MyAnno(Str = "pewien te k st") / / domyślne tylko val @MyAnno(val = 100) // domyślne tylko str @MyAnno(str - 'Testowanie". val = 100) / / brak wartości domyślnych
Poniższy program ilustruje wykorzystanie wartości dom yślnych notatek. import java.lang.annotation.*: import ja va .lan g .re fle c t.* :
312
Część I ♦ Język Java
/ / Deklaracja typu notatkowego zawierającego wartości domyślne. @Retention(RetentionPolicy.RUNTIME) ^interface MyAnno { String s t r ( ) d efau lt "Testowanie": in t v a l( ) d e fa u lt 9000:
} class Meta3 {
/ / Notatka dla metody używa wartości domyślnych. @MyAnno() public s t a t ic void myMethO { Meta3 ob = new Meta3():
/ / Pobranie notatki dla metody / / / wyświetlenie wartości je j składowych. tr y { C lass c - ob.getClassO ; Method m - c.getMethodCmyMeth"): MyAnno anno - m.getAnnotation(MyAnno.c la s s ) : System .out.println(anno.str() + " " + anno.valO) } catch (NoSuchMethodException exc) { S ystem .o u t.p rin tln t"Metody nie znaleziono.");
} p ublic s t a t ic void main(String args[]) { myMeth();
} } Program po uruchomieniu wyświetli następujący wynik Testowanie 9000
Notatki znacznikowe N otatka znacznikow a to specjalna notatka, która nie zawiera składowych. Jej jedynym zadaniem jest znakowanie deklaracji — wystarczy sama jej obecność przy jakimś elemen cie. Najlepszym sposobem sprawdzenia, czy istnieje notatka znacznikowa, jest zastoso wanie m etody isAnnotationPresentO zdefiniowanej w interfejsie AnnotatedElement. Oto przykład korzystający z notatki znacznikowej. Ponieważ interfejs notatki nie zawiera składowych, wystarczy sama obecność notatki (lub jej brak). import ja v a .la n g .annotation.*: import ja v a .la n g .reflect.* :
/ / Notatka znacznikowa. @Retention(RetentionPolicy. RUNTIME) ^ interface MyMarker { }
Rozdział 12. ♦ Wyliczenia, automatyczne otaczanie typów prostych i matadane
c la s s Marker {
/ / Użycie znacznika dla metody. I I Zauważ, iż nie są wymagane nawiasy
@MyMarker p u b lic s ta tic void myMethO { Marker ob - new MarkerO: try { Method m - ob.getClassO.getMethodCmyMeth");
/ / Sprawdzenie, czy notatka znacznikowa istnieje. i f(m.isAnnotationPresent(MyMarker.c la s s ) ) S ystem .ou t.p rin tln C Istn ieje znacznik MyMarker."): } catch (NoSuchMethodException exc) { System.o u t.p rin tln ("Metody nie znaleziono."):
} } p u b lic s ta tic void ma-in (St ring args[]) { myMeth():
} } Wynik działania programu potwierdza, iż notatka istnieje. Is tn ie je znacznik MyMarker.
Zauważ, że nie trzeba umieszczać nawiasów okrągłych po nazwie notatki znacznikowej @MyMarker. Ponieważ notatka nie ma żadnych elementów, wystarczy podać samą jej nazwę. @myMarker
Zastosowanie pustych nawiasów nie jest błędem, ale nie są one wymagane.
Notatki jednoelementowe N otatki jednoelementowe zawierają tylko jedną składową. Działają dokładnie tak samo jak ogólne notatki, ale umożliwiają zastosowanie skróconej wersji przypisania wartości do składowej. W momencie przypinania notatki wystarczy użyć samej wartości skła dowej — nie trzeba podawać jej nazwy. Aby jednak móc skorzystać z takiego skrótu, składowa musi mieć nazwę value. Poniżej znajduje się przykład wykorzystujący notatkę jednoelementową. import java.lang.annotation.*: import java.lang.r e fle c t .*: II Notatka jednoelementową.
@Retention(RetentionPolicy.RUNTIME) ^interface MySingle { in t valu eO ; // składowa musi mieć nazwę value
}
313
314
Część I ♦ Język Java
c la s s S in g le { I I Opatrzenie metody notatką
@MySingle(100) p u b lic s t a t ic void myMethO { S ingle ob - new S in g le d : try { Method m - ob.getClass() .getMethodC’myMeth"); MySingle anno - m .getAnnotation(MySingle.class): S ystem .out.println(anno.valued): I I wyświetla 100 } catch (NoSuchMethodException exc) { S ystem .out.println("Metody nie znaleziono."):
} } p u b lic s t a t ic void main(String args[]) { myMeth():
} } Zgodnie z oczekiwaniami, program po uruchomieniu wyświetla wartość 100. Notatka przypisana do metody myMeth() ma następującą postać. GMySingle(lOO)
Zauważ brak va 1ue =. Skróconą składnię można zastosować również wtedy, gdy notatka zawiera inne skła dowe, ale wszystkie one mają przypisane wartości domyślne. Poniższy kod deklaruje notatkę z dodatkow ą składową xyz o wartości domyślnej 0. G interface SomeAnno { in t v a lu e d : in t x y z d d efau lt 0:
1 W szędzie tam, gdzie chce się zastosować domyślną wartość dla xyz, można użyć po niższej postaci notatki. Wystarczy podać wartość składowej value zgodnie ze składnią notatki jednoelementowej. GSomeAnno(88)
W takiej sytuacji xyz zostanie ustawione na wartość domyślną 0, natomiast value otrzy ma wartość 88. Gdy chce się ustawić wartości obu składowych, trzeba jawnie podać obie nazwy. GSomeAnno(value = 88. xyz = 99)
Pamiętaj, że składnia notatki jednoelementowej wymaga, by pojedyncza składowa miała nazwę value.
Rozdział 12. ♦ Wyliczenia, automatyczne otaczanie typów prostych i matadane
315
Wbudowane notatki Java definiuje siedem wbudowanych notatek. Cztery z nich znajdują się w pakiecie j a v a . la n g .an n o ta tio n : ©Retention, ©Documented, ©Target i © Inherited. Pozostałe trzy znajdują się w pakiecie ja v a , lang: ©Override, ©Deprecated i ©SuppressWarnings.
Notatka @Retention Notatka ta została zaprojektowana tylko jako notatka zmieniająca działanie innych nota tek. Określa ona strategię zachowania (patrz jeden z wcześniejszych podrozdziałów).
Notatka @Documented Ta notatka jest notatką znacznikową, która informuje narzędzia, iż inną notatkę należy udokumentować. Powinna być stosowana jedynie przy deklaracji innych notatek.
Notatka @Target Notatka ta określa typy deklaracji, do których może być używana definiowana po niej no tatka. Powinna być stosowana jedynie przy deklaracji innych notatek. Notatka ©Target przyjmuje jeden argument, który musi być stałą wyliczenia Element Type. Argument określa, dla jakich typów deklaracji można używać notatki. Tabela 12.1 zawiera opisy poszczególnych stałych. Tabela 12.1. Stałe -wyliczenia E lem enlType Stałe notatki ©Target
Notatkę można stosować dla
ANNOTATIONTYPE
Innej notatki
CONSTRUCTOR
Konstruktora
FIELD
Zmiennej składowej
LOCAL VARIABLE
Zmiennej lokalnej
METHOD
Metody
PACKAGE
Pakietu
PARAMETER
Parametru
TYPE
Klasy, interfejsu lub wyliczenia W notatce ©Target można wskazać więcej niż jedną ze stałych, ale trzeba je oddzielić prze cinkami i umieścić w nawiasach klamrowych. Aby wskazać, że kolejna notatka dotycz\ tylko zmiennych lokalnych i składowych, należy użyć poniższej wersji notatki ©Target. ©TargetC ( ElementType.FIELD. ElementType.LOCALVARIABLE } )
Notatka @lnherited Jest to notatka znacznikowa, która może być stosowana tylko przed deklaracją innej notatki. Co więcej, wpływa ona tylko na notatki, które są używane przed definicjami klas. Notatka powoduje, iż notatka klasy bazowej jest dziedziczona przez podklasę.
316
Część I ♦ Język Java
Jeśli pojawi się żądanie notatki podklasy, Java sprawdza, czy została zdefiniowana dla podklasy. Jeśli nie została, ale klasa bazowa posiada notatkę, to właśnie ona zostanie przekazana do kodu żądającego notatki. Warunkiem uzyskania takiego działania jest zastosowanie notatki ^In h erited .
Notatka @Override Jest to notatka znacznikowa, która może być stosowana tylko przed deklaracją metody. Metoda z taką notatką musi przesłaniać metodę klasy bazowej. Jeśli tego nie uczyni, kompilator zgłasza błąd. W ten sposób zapewnia się, iż przypadkowo nie nastąpi prze ciążenie m etody zamiast jej przesłonięcia.
Notatka @SuppressWarnings Notatka oznacza, iż można pominąć jedno lub kilka ostrzeżeń zgłaszanych przez kompi lator. W notatce trzeba podać nazwę pomijanego ostrzeżenia. Notatkę można stosować dla dowolnych deklaracji.
Ograniczenia Z deklaracjami notatek związanych jest kilka ograniczeń. Po pierwsze, żadna notatka nie może dziedziczyć po innej notatce. Po drugie, wszystkie metody deklarowane w notatce musza być bezparametrowe. Co więcej, muszą zwracać jeden z poniższych typów: ♦ typ prosty, na przykład in t lub double, ♦ obiekt typu String lub Class, ♦ typ wyliczeniowy, ♦ typ innej notatki, ♦ tablicę jednego z wymienionych wcześniej typów. Notatki nie mogą korzystać z typów sparametryzowanych (ich dokładne omówienie znajduje się w rozdziale 14.). Nie mogą też zawierać klauzuli throws.
Rozdział 13.
Wejście-wyjście, aplety i inne tematy Niniejszy rozdział zawiera wprowadzenie do dwóch najważniejszych pakietów: to oraz applet. Pakiet i o zawiera podstawowy podsystem wejścia-wyjścia stosowany w Javie, włącznie z obsługą plików. Pakiet applet dotyczy obsługi apletów. Obsługa obu elemen tów nie stanowi części samego rdzenia języka Java, ale je st zawarta w podstawowych bibliotekach. Z tego powodu dokładne omówienie tego tem atu znajduje się w II czę ści książki, która zajmuje się interfejsem programistycznym podstawowych klas Javy. Ten rozdział to tylko wprowadzenie do obu tematów, które ma pokazać integrację pod systemów z rdzeniem języka i ogólnym kontekstem programowania i wykonywania programów. Niniejszy rozdział zawiera również omówienie ostatnich słów kluczowych Javy: tra n s ie n t, v o la tile , instan ceo f, nativ e, s t r i c t f p i a s s e rt.
Podstawowa obsługa wejścia i wyjścia W poprzednich 12 rozdziałach w zasadzie nie pojawiały się żadne informacje na temat obsługi wejścia-wyjścia. Przykładowe programy korzystały tylko i wyłącznie z metod print O oraz p rin tln O . Powód jest bardzo prosty: niewiele rzeczywistych programów Javy działa w trybie tekstowym. Są to raczej programy z graficznym interfejsem użyt kownika korzystającym z systemów A W T (Abstract W indow Toolkit) i Swing. Choć programy tekstowe doskonale nadają się do nauki program ow ania, bardzo rzadko wy stępują w rzeczywistym świecie. Korzystanie z konsoli w Javie jest ograniczone i nieco dziwaczne — nawet w prostych programach. Po prostu konsola nigdy nie była bardzo ważnym elementem programowania w języku Java. Oczywiście poprzedni akapit nie neguje, iż Java zawiera bardzo elegancką i elastyczną obsługę wejścia-wyjścia związanego z plikami i siecią. System we-wy Javy jest zwarty i spójny. Wystarczy poznać jego podstawy, aby niezwykle szybko opanować pozostałe zagadnienia. W wydaniu J2SE 5 pojawiły się nowe metody ułatwiające niektóre rodzaje operacji związanych z konsolą. Ich om ówienie znajduje się w rozdziale 19.
318
Część I ♦ Język Java
Strumienie Programy Java komunikują się ze światem za pomocą strumieni. Strum ień to pewien abstrakcyjny byt, który produkuje lub konsumuje informacje. Strumień łączy się z fi zycznym urządzeniem dzięki systemowi we-wy Javy. Strumienie zachowują się zaw sze tak samo, niezależnie od tego, jakiego urządzenia fizycznego dotyczą. Oznacza to, iż te same klasy i metody wejścia-wyjścia służą do obsługi różnych urządzeń: strumień w ejściow y służy do odczytu danych z pliku, klawiatury lub gniazdka sieciowego, na tomiast strumień wyjściowy wysyła dane do pliku, na konsole lub do innego komputera. Strum ienie to wręcz doskonała abstrakcja, gdyż poszczególne części kodu nie muszą znać różnicy między klawiaturą i siecią. Implementacja strumieni w Javie opiera się na hierarchii klas zawartych w pakiecie ja va .to.
Strumienie znakowe i bajtowe Java definiuje dwa rodzaje strumieni: znakowe i bajtowe. Strum ień bajtów stanowi w ygodny sposób obsługi w ejścia i wyjścia bajtowego. Używa się go na przykład do zapisu i odczytu danych binarnych. Strum ień znaków zapewnia wygodny sposób prze kazywania znaków. Strumień korzysta ze standardu Unicode, więc obsługuje wiele róż nych języków . W pewnych zastosowaniach strumienie znaków są bardziej wydajne od strumieni bajtów. Oryginalne wydanie Javy (1.0) nie zawierało strumieni znaków, więc cała komunikacja odbywała się bajtowo. Strumienie znaków pojawiły' się w Javie 1.1, a pewne klasy i me tody związane z bajtami zostały zabronione. Właśnie z tego powodu warto uaktualnić kod, który jeszcze nie korzysta ze strumieni znaków. Trzeba pamiętać o tym, iż na najniższym poziomie cała komunikacja we-wy odbywa się bajtowo. Strumienie znaków po prostu ułatwiają przekazywanie znaków. Kolejne podpunkty omawiają dostępne strumienie bajtów i znaków.
Klasy strumieni bajtów Strum ienie bajtów korzystają z dwóch hierarchii klas. Na samym szczycie tych hie rarchii znajdują się dwde klasy abstrakcyjne: InputStream i OutputStream. Każda z tych klas abstrakcyjnych posiada wiele konkretnych podklas obsługujących różnego rodzaju urządzenia, na przykład pliki, połączenia sieciowe, a nawet bufory pamięci. Tabela 13.1 zaw iera listę klas strumieni bajtów. Niektóre z klas zostaną omówione w niniejszym rozdziale; pozostałe są opisane w części II. Pamiętaj, że korzystanie z klas strumieni w ym aga zaimportowania pakietu java.lo. K lasy abstrakcyjne InputStream i OutputStream definiują kilka kluczowych metod im plem entowanych przez pozostałe klasy strumieni. Najważniejszymi metodami są read() i w rite (), które odpowiednio odczytują i zapisują bajty danych. Obie metody są zadeklarowane jako abstrakcyjne w klasach InputStream i OutputStream. Klasy pochodne przesłaniają je konkretnymi implementacjami.
Rozdział 13. ♦ Wejście-wyjście, aplety i inne tematy
319
Tabela 13.1. Klasy strumieni bajtowych Klasa strumienia
Znaczenie
Buf fe red Ir pu t St ream
B u fo r o w a n y str u m ie ń w e j ś c io w y
BufferedOutputStream
B u fo r o w a n y str u m ie ń w y jś c io w y
ByteArraylnputStream
S tru m ień w e j ś c io w y c z y ta ją c y z ta b lic y b a jtó w
ByteArrayOutputStream
S tru m ień w y j ś c io w y z a p isu ją c y d o t a b lic y b a jtó w
DatalnputStream
S tru m ień w e j ś c i o w y z a w ie r a ją c y m e t o d y d o o d c z y ty w a n ia p o d s t a w o w y c h ty p ó w d a n y c h J a v y
DataOutputStream
S tru m ień w y j ś c io w y z a w ie r a ją c y m e t o d y d o z a p is y w a n ia p o d s t a w o w y c h ty p ó w d a n y c h J a v y
FileInputStream
S tru m ień w e j ś c i o w y o d c z y tu ją c y z p lik u
FileOutputStream
S tru m ień w y j ś c io w y z a p isu ją c y d o p lik u
Fi lte rln p u tS tream
Im p le m e n tu je I nputSt ream
FilterOutputStream
Im p le m e n tu je OutputStream
InputStream
K la sa a b str a k c y jn a o p isu ją c a s tr u m ie ń w e j ś c i o w y
Object InputStream
S tru m ień w e j ś c i o w y d la o b ie k tó w
ObjectOutputStream
S tru m ień w y j ś c io w y d la o b ie k tó w
PutputStream
K la sa a b str a k c y jn a o p isu ją c a s tr u m ie ń w y j ś c io w y
PipedlnputStream
P o to k w e j ś c io w y
PipedOutputStream
P o to k w y jś c io w y
PrintStream
S tru m ień w y j ś c io w y z a w ie r a ją c y m e t o d y p r in t ( ) i p rin tln ( )
PushbackInputStream
S tru m ień w e j ś c i o w y o b s łu g u j ą c y w y c o f a n i e j e d n e g o bajta z p o w r o te m d o stru m ien ia
RandomAccessFile SequencelnputStream
O b słu g a s w o b o d n e g o d o stę p u d o p lik u S tru m ień w e j ś c i o w y łą c z ą c y k ilk a in n y c h str u m ie n i w e j ś c io w y c h o d c z y ty w a n y c h jed en p o d ru g im
Klasy strumieni znaków Strumienie znaków korzystają z dwóch hierarchii klas. Na samym szczycie tych hie rarchii znajdują się dwie klasy abstrakcyjne: Reader i W riter. Obie klasy obsługują stru mienie znaków Unicode. Istnieje kilka konkretnych podklas wspomnianych klas abs trakcyjnych. Tabela 13.2 zawiera listę klas strumieni znaków. Klasy abstrakcyjne Reader i W riter definiują kilka kluczowych metod implementowa nych przez pozostałe klasy strumieni. Najważniejszymi metodami są readO i w riteO , które odpowiednio odczytują i zapisują znaki danych. Klasy pochodne przesłaniają je konkretnymi implementacjami.
Predefiniowane strumienie Wszystkie programy Javy automatycznie importują pakiet ja v a. lang. Pakiet ten defi niuje klasę o nazwie System dotyczącą kilku aspektów środowiska, w którym działa aplikacja. Na przykład przy użyciu jej metod można pobrać aktualny czas lub ustawienia
320
Część I ♦ Język Java
Tabela 13.2. K la s y stru m ien i zn aków Klasa strumienia
Znaczenie
BufferedReader
B u fo r o w a n y str u m ie ń w e j ś c i o w y z n a k ó w
BufferedWriter
B u fo r o w a n y str u m ie ń w y j ś c io w y z n a k ó w
CharArrayReader
S tr u m ień w e j ś c i o w y c z y t a ją c y z t a b lic y z n a k ó w
CharArrayWnter
S tru m ień w y j ś c io w y z a p is u ją c y d o t a b lic y z n a k ó w
F il eReader
S tru m ień w e j ś c i o w y c z y t a ją c y z p lik u
FileW riter
S tru m ień w y j ś c io w y z a p is u j ą c y d o p lik u
FilterReader
F iltr o w a n y str u m ie ń w e j ś c i o w y
F ilte rW rite r
F iltr o w a n y str u m ie ń w y jś c io w y '
InputStreamReader
S tru m ień w e j ś c i o w y z a m ie n ia ją c y b a jty n a z n a k i
LineNumberReader
S tru m ień w e j ś c i o w y z l ic z a ją c y w ie r s z e
OutputStreamWriter
S tru m ień w y j ś c io w y z a m ie n ia ją c y z n a k i n a b a jty
PipedReader
P o to k w e j ś c io w y
PnpedWnter
P o to k w y j ś c io w y
PrintW riter
S tru m ień w y j ś c io w y z a w ie r a ją c y m e t o d y p rin tO i p rin tln O
Pushbac. Jest to ogólna składnia stosowana zawsze wtedy, gdy trzeba zadeklarować param etr typu. Ponieważ Gen używa parametru typu, jest klasą sparam etryzow aną. Następnie T służy do zadeklarowania obiektu o nazwie ob. T ob: II deklaracja obiektu typu T
Pamiętaj, iż T jest nazw ą zastępczą dla rzeczywistego typu, który zostanie przekazany w momencie tworzenia obiektu klasy Gen. Innymi słowy, ob będzie obiektem typu prze kazanego do T. Jeśli do T zostanie przekazany typ String, zmienna referencyjna ob będzie dotyczyła obiektów S tn ng. O to konstruktor klasy. Gen(T o) { ob - o:
} Zauważ, że parametr o jest typu T. Podobnie jak wcześniej, oznacza to, iż rzeczywisty typ o będzie typem przekazanym do T w momencie tworzenia obiektu Gen. Przypisanie o do ob jest poprawne, gdyż w przedstawionym szablonie klasy obie zmienne korzystają z tego samego typu tymczasowego. Parametru typu T można również użyć do określenia typu zwracanego przez metodę. Oto kod metody getob(). T getobO { return ob: } Ponieważ ob jest typu T, może zostać bez problemów zwrócony przez metodę. Metoda showTypeO wyświetla typ T, wywołując metodę getNameO obiektu Class zwró conego przez wywołanie getClassO dla obiektu ob. Metoda getClassO jest zdefinio wana w klasie Object, więc jest dostępna we wszystkich innych klasach. Metoda zwraca obiekt Class odpowiadający typowi obiektu, dla którego została wywołana. Klasa Class definiuje metodę getName(), która zwraca tekstową reprezentację nazwy klasy. Klasa GenDemo obrazuje wykorzystanie klasy Gen. Najpierw tworzy wersję Gen dla liczb całkowitych. Gen i Ob:
W arto dobrze przyjrzeć się tej deklaracji. Po pierwsze, zauważ, iż typ Integer został podany w nawiasach ostrych zaraz po nazwie klasy Gen. W tym przypadku Integer to argument typu, który zostanie przekazany do parametru typu T. Spowoduje to powstanie wersji klasy Gen, w której wszystkie referencje do T zostały zastąpione referencjami do Integer — deklaracja ob będzie dotyczyła typu Integer, a metoda getob() będzie zwra cała typ Integer.
Rozdział 14. ♦ Typy sparametryzowane
349
Zanim przejdziemy dalej, warto podkreślić, iż kompilator nie utworzy tak naprawdę róż nych wersji Gen ani żadnej innej klasy sparametryzowanej. Choć warto właśnie w ten sposób postrzegać parametryzację, rzeczywistość jest całkowicie inna. W rzeczywistości kompilator usuwa wszelkie informacje o ogólnym typie, zastępując je wymaganymi rzutowaniami, aby kod zachowywał się tak, jakby powstała konkretna wersja klasy Gen. Oznacza to, iż w programie tak naprawdę istnieje tylko jedna wersja klasy Gen. Proces usuwania informacji o typie sparametryzowanym nosi nazwę znoszenia. Powró cimy do tego tematu w dalszej części rozdziału. Kolejny wiersz kodu przypisuje referencji i Ob wersję In te g e r klasy Gen. i Ob = new Gen(88):
Zauważ, że wywołanie konstruktora klasy Gen także wymaga podania argumentu typu Integer. Trzeba użyć takiej konstrukcji, ponieważ typ zmiennej referencyjnej 10t? to Gen, więc operator new również musi zwrócić typ Gen. W przeciw nym przypadku zostanie zgłoszony błąd kompilacji. Gdyby zastąpić powyższy wiersz poniższym, nie udałoby się wykonać kompilacji programu. i Ob - new GenOoub1e>(88.0): // Błąd!
Ponieważ i Ob dotyczy typu Gen nie można go użyć do przechowywania obiektu typu Gen. Sprawdzanie typów to jedna z głównych zalet typów sparametryzowanych. Zgodnie z komentarzem umieszczonym w programie, poniższy wiersz: i Ob - new Gen(88):
korzysta z automatycznego otaczania typów prostych — wartość 88 typu in t zostaje otoczona obiektem Integer. Wszystko działa poprawnie, gdyż konstruktor Gen przyjmuje obiekty typu Integer — Java wie, iż może bezpiecznie dokonać wspomnianego otoczenia. Oczywiście przypisanie m ożna również zapisać w bardziej jawny sposób. iOb - new Gen(new Integer (88)):
Zastosowanie tej wersji nie przynosi żadnych korzyści. Następnie program wyświetla typ obiektu ob znajdującego się w i Ob, czyli typ Integer, po czym program pobiera wartość ob, używając następującej instrukcji. in t v = iOb.getobO:
Ponieważ zwracany typ T został w metodzie getob() zastąpiony typem Integer w trakcie tworzenia obiektu "iOb, Java potrafi bez problemów dokonać automatycznego rozpa kowania wartości do typu in t i przypisania jej do v. Nie trzeba rzutować typu zwraca nego przez getobO na Integer. O czyw iście nie trzeba stosow ać automatycznego roz pakowywania — poniższa forma rów nież jest poprawna. in t v - i Ob. getobO. intValueO :
Automatyczne rozpakowywanie czyni kod bardziej zwartym .
350
Część I ♦ Język Java
Następnie klasa GenDemo deklaruje obiekt typu Gen. Gen strOb - new Gen("Test typów sparanetryzowanych");
Ponieważ argument typu to String, wszystkie wystąpienia T w klasie Gen są zastępo wane przez String. Powstaje więc wersja S tring klasy Gen i wszystkie kolejne wiersze kodu są do niej dostosowane.
Typy sparametryzowane działają tylko dla obiektów W momencie deklaracji typu sparametryzowanego, przekazywany argument typu musi być klasą. Nie można użyć typu prostego, na przykład in t lub char. Klasa Gen może przyjąć dowolny typ klasy jako T, ale nie w olno zastosować typu prostego. Poniższa deklaracja nie jest poprawna. Gen StrOb = new Gen(53): // Błąd. nie można użyć typu prostego
Oczywiście brak możliwości zastosowania typu prostego nie jest znaczącym utrudnie niem. ponieważ zawsze można użyć typu otoczkow ego (patrz wcześniejszy przykład) i w nim umieścić typ prosty. Co więcej, automatyczne otaczanie i rozpakowywanie czyni typ otoczkowy praktycznie niewidocznym.
Typy sparametryzowane różnią się, jeśli mają inny argument typu W ato pamiętać o tym, iż konkretna wersja typu sparametryzowanego nie jest zgodna z inną konkretną wersją tego samego typu sparametryzowanego. Dla wcześniej przed stawionego programu, poniższe przypisanie nie jest poprawne. i Ob - strOb: // Błąd!
Choć zarówno i Ob, jak i strOb wywodzi się z tego samego ogólnego typu Gen, to jednak są innymi typami, ponieważ mają inne argumenty typu. Takie rozwiązanie typów sparametryzowanych zwiększa bezpieczeństwo i pozwala unikać błędów.
W jaki sposób typy sparametryzowane zwiększają bezpieczeństwo? W tym momencie niektóre osoby mogą sobie zadawać pytanie: skoro taką samą funk cjonalność, którą posiada klasa sparametryzowana Gen, można osiągnąć przez użycie typu Object i odpowiednie rzutowania, po co stosować parametryzację? Oto odpowiedź: ponieważ typy sparametryzowane automatycznie zapewniają bezpieczeństwo typów dla wszystkich operacji przeprowadzanych przy użyciu Gen. Dodatkowo oznacza to eli minację potrzeby ręcznego rzutowania i sprawdzania typów. Aby lepiej zrozumieć zalety typów sparametryzowanych, warto rozważyć wersję klasy Gen, która stosuje tradycyjne podejście.
Rozdział 14. ♦ Typy sparametryzowane
/ / A'onGen je st funkcjonalnie równoważne Gen. / / ale me używa typów sparatnetryzowanych. c la s s NonGen { Object Ob: / / objest teraz typu Object I I Konstruktor przyjmuje referencję 11 do typu Object
NonGen(Object o) { ob - o:
} / / Zwrócenie typu Object. Object getobO { return ob:
} / / Wyświetlenie typu ob. void showTypeO { System .out.prntln("Typ ob to " + ob.getClass() ,getName() ) :
} } / / Przykład użycia klasy bez typów sparametryzowanych. c la s s NonGenDemo { p u b lic s ta tic void m ain(String args[]) { NonGen iOb:
/ / Tworzenie obiektu NonGen i zapamiętanie II w nim typu Integer. Automatyczne otaczanie nadal działa.
iOb * new NonGen(88):
/ / Wyświetlenie typu danych używanego przez iOb. i Ob. showTypeO:
/ / Pobranie wartości iOb. II Tym razem konieczne je st rzutowanie.
in t v - (Integer) iOb.getobO: System.out.println("wartość: ” + v): System.out.print ln ( ) ;
/ / Utworzenie innego obiektu NonGen II i zapamiętanie w nim typu String NonGen strOb = new NonGenCTest dla braku param etryzacji"):
/ / Wyświetlenie typu danych używanego przez strOb. strOb. showTypeO:
/ / Pobranie wartości strOb. / / Tym razem również konieczne je s t rzutowanie. String s tr - (String) strO b.getobO : System .out.printlnCw artość: - + s tr) ; II Ten kod uda się skompilować, choć je st błędny koncepcyjnie!
iOb - strOb:
V = (Integer) iOb.getobO: / / Błąd w trakcie działania programu1 }
351
352
Część I ♦ Język Java
Ta wersja programu zaw iera kilka interesujących fragmentów. Po pierwsze, w klasie NonGen typ O bject zastąpił stosowany wcześniej typ T. W ten sposób NonGen może prze chowywać dowolny typ obiektów, podobnie ja k wersja sparametryzowana. Niestety, takie rozwiązanie powoduje, iż kompilator nie potrafi jednoznacznie stwierdzić, co jest w rzeczywistości przechowywane w obiekcie NonGen. To podejście jest złe z dwóch powodów: trzeba stosować jawne rzutowanie, aby pobrać przechowywane dane, i wiele problemów pojawia się dopiero po uruchomieniu programu. Przyjrzyjmy się dokładniej obu powodom. Zwróć uwagę na następujący wiersz. in t v - (Integer) iOb. getobO:
Ponieważ metoda g e to b O zwraca obiekt typu O b je c t, konieczne jest rzutowanie na typ Integer, aby Java dokonała automatycznego rozpakowania i przypisania wartości do v. Gdy usunie się rzutowanie, w trakcie kompilacji pojawi się błąd. W wersji sparametryzowanej rzutowanie odbywało się w sposób niejawny. W tej wersji musi się odbyć jawnie. Nie jest to tylko niedogodność, ale i źródło potencjalnych problemów. Teraz zajmijmy się następującą sekwencją instrukcji znajdującą się pod koniec pro gramu. / / Ten kod uda się skompilować, choć je st błędny koncepcyjnie! iOb - strOb: v “ (Integer) i0b.getOb(): // Błąd w trakcie działania programu1
Zmienna strO b została przypisana do zmiennej iOb. Zmienna s trO b dotyczy jednak obiektu przechowującego ciąg znaków, a nie wartość całkowitą. Syntaktycznie przy pisanie jest poprawne, ponieważ wszystkie referencje NonGen są takie same (do do wolnej referencji NonGen można przypisać dowolny obiekt NonGen). Semantycznie przypi sanie jest błędne, co bardzo dobrze obrazuje kolejny wiersz. Zachodzi próba rzutowania typu zwróconego przez metodę getobO na In te g e r i przypisania wyniku do zmiennej v. Niestety, problem w tym, iż 10b odnosi się teraz do obiektu przechowującego obiekt S t r in g zamiast obiektu In te g e r. Ponieważ nie stosujemy typów sparametryzowanych, kompilator nie potrafi dostrzec błędu. W momencie próby rzutowania na typ Integer następuje zgłoszenie wyjątku wykonania. Wystąpienie takiego wyjątku bardzo źle świad czy o napisanym kodzie! Przedstawiona sekwencja zdarzeń nie wystąpiłaby, gdyby zastosowano typy sparametryzowane — kompilator zgłosiłby błąd ju ż na etapie kompilacji, więc wykonanie nigdy by nie wystąpiło. Wychwytywanie błędów ju ż na etapie kompilacji to jedna z najważ niejszych zalet parametryzacji typów. Choć zastosowanie referencji O b je ct zawsze bę dzie możliwe, taki kod nie będzie bezpieczny pod kątem typów, a jeg o niepoprawne użycie doprowadzi do zgłoszenia wyjątku wykonania. Typy sparametryzowane pozwa lają zamienić błędy wykonania na błędy kompilacji — to duży krok naprzód.
Rozdział 14. ♦ Typy sparametryzowane
353
Klasa sparametryzowana z dwoma parametrami typu Dla klasy można zastosować więcej niż jeden parametr typu. Aby określić dwa lub w ię cej parametrów typu, po prostu oddziela się przecinkami poszczególne nazwy. Poniż sza klasa TwoGen to odmiana klasy Gen z dwoma parametrami typu. / / Przykładowa klasa sparametryzowana z dwoma / / parametrami typu: T i V. class TwoGen { T obi: V ob2:
/ / Przekazanie do konstruktora referencji do / / obiektów typu T i V. TwoGend o l. V o2) { obi - ol: 0b2 - o2:
} / / Wyświetlenie typów T i V. void showTypesO { System.o ut.printlnC'Typ T to " + obl.getC lassO .getNameO): System.o ut.printlnC'Typ V to ” + ob2.getClass().getNameO):
} T getoblO { return obi:
} V getob2() { return ob2:
} } / / Przykład użycia klasy TwoGen. class SimpGen {
public s t a t ic void main(String a rg s[]) { TwoGen tgObj new TwoGen(88. "Parametryzacja"):
/ / Wyświetlenie typów. tgObj. showTypesO:
/ / Pobranie i wyświetlenie wartości. in t v - tgO bj.getoblO : System .out.printlnCw artość: " + v): String s tr - tgObj.getob2();
354
Część I ♦ Język Java
System .out.printlnCw artość: " + s t r ) ;
} } Wynik działania programu jest następujący. Typ T to Typ V to wartość: wartość:
java.lang.Integer ja v a .la n g .S trin g 88 Parametryzacja
Zauw aż sposób deklaracji klasy TwoGen. c la s s TwoGen {
Dwa parametry typu, T i V są oddzielone przecinkiem. Ponieważ istnieją dwa parametry, w trakcie tworzenia obiektu typu TwoGen trzeba przekazać dwa argumenty typu. TwoGen tgObj ■ new TwoGen(88. ''Parametryzacja” ):
W tym przypadku Integer zastępuje typ T, natomiast String zastępuje typ V. Choć w przykładzie oba argumenty są różne, nic nie stoi na przeszkodzie, by były takie same. Poniższy kod je st całkowicie poprawny. TwoGen tgObj - new TwoGen("A'’ . "B");
Teraz T i V będą typu String. Oczywiście, gdyby zawsze argumenty typu były takie same, stosowanie dwóch parametrów typu nie miałoby dużego sensu.
Ogólna postać klasy sparametryzowanej Składnię przedstawionych klas sparametryzowanych można uogólnić. Oto szablon two rzenia takiej klasy. c la s s
nazwa-klasy
{ I I ...
O to składnia deklaracji referencji do klasy sparametryzowanej. c la s s
nazwa -kl asy nazwa-zmiennej
-
new nazwa -kl asy(. l i sta-argumentów-stałych)
Typy ograniczone W poprzednich przykładach parametry typu mogły zostać zastąpione dowolnym typem klasy. W wielu zastosowaniach takie podejście jest poprawne, ale czasami zachodzi potrzeba ograniczenia typów, które można przekazać do parametru typu. Wyobraźmy
Rozdział 14. ♦ Typy sparametryzowane
355
sobie, iż chcemy napisać ogólną klasę, która oblicza i zwraca średnią wartości zawartych w tablicy. Wartości inogą być dowolnego typu liczbowego, na przykład liczbą całko witą lub zmiennoprzecinkową. Z tego względu warto użyć parametru typu. Klasa reali zująca to zadanie mogłaby wyglądać następująco. // // // // U //
Nieudana próba wykonania klasy Stals jako klasy sparametryzowanej. która oblicza średnią elementów wskazanego typu znajdujących się wprzekazanej tablicy. Klasa zawiera błąd!
c la s s Stats {
T[ ] nums: / / nums to tablica typu T
/ / Przekazanie do konstruktora referencji do / / tablicy typu T. S tats(T[] O) { nums = o;
) / / Zawsze zwracanie średniej jako typu double. double averageO { double sum - 0.0: f o r ( i n t i-O: i < nums.length:
i++) sum += nums[i].doubleValue(): // Błąd!!!
return sum / nums.length:
} } W klasie metoda average() próbuje pobrać wersję double każdej z liczb z tablicy nums za pomocą metody doubleValue(). Ponieważ wszystkie klasy numeryczne, na przykład Integer lub Double, są podklasami klasy Number, która definiuje metodę doubleValue(), klasa powinna działać dla wszystkich otoczek numerycznych. Problem w tym, iż kom pilator nie wie o tym, że chcemy tworzyć obiekty Stats tylko dla typów numerycznych. Próba kompilacji klasy powoduje zgłoszenie błędu, poniew aż nie jest znana metoda doubl eVa 1ue() . Aby rozwiązać tę kwestię, trzeba w jakiś sposób poinformować kom pilator, iż parametr typu T będzie zawierał tylko typy num eryczne. Co więcej, trzeba zapewnić, iż użytkownik będzie mógł przekazać tylko i w yłącznie typy numeryczne. W celu rozwiązania tego rodzaju problemów Java korzysta z typów ograniczonych. W momencie określania parametru typu podaje się klasę bazową, z której muszą się wywodzić wszystkie argumenty typu. W tym celu stosuje się słowo kluczowe extends po nazwie parametru typu.
Taki zapis oznacza, iż T może być zastąpione jedynie przez k l asa -bazowa lub jedną z jej podklas. Innymi słowy, k lasd-bdzcwa określa górny limit dopuszczalnej hierarchii klas. Ograniczenie górne rozwiązuje problem z klasą Stats, gdyż można określić, iż dopusz czalne sąjedynie obiekty klasy Number lub jej podklas.
356
Część I ♦ Język Java
II W tej wersji klasy Slats argumentem typu T / / musi być klasa Number lub jedna z j e j podklas.
class Stats { T[] nums; I I ta b lic a N um ber lub j e j p o d k la s y
/ / Przekazanie d o konstruktora referencji do II tablicy typu Number lub je j podklasy. Stats(T[] O) { nums = o:
} / / Zawsze zwracanie średniej jako typu double. double averaged { double sum - 0.0: f o r ( i n t i- 0 ;
i < nums.length; 1++) sum += nums[i],doubleValue():
return sum / nums.length;
} } / / Przykład użycia klasy Stats. cla ss BoundsDemo { p ublic s t a t ic void main(String a rgs[]) { Integer inums[] - { 1 . 2 . 3. 4. 5 }: Stats iob - new Stats(inums): double v = iob.average!): System.out.p rin tln C Ś re d n ia dla iob wynosi " + v): Double dnums[] - { 1.1. 2.2. 3.3. 4.4. 5.5 }; Stats dob = new Stats(dnums); double w - dob.average!): System.out.p rin tln C Ś re d n ia dla dob wynosi " + w);
/ / Tego fragmentu me uda się skompilować, ponieważ String / / nie je st podklasą klasy Number. II //
S trin g s tr s [ ] - { ” 1". ”2". "3". M4". "5" }; Stats strob - new Stats< String> (strs):
// //
double x - strob.average!); System.out .p rin tln C Ś re d n ia dla strob wynosi " + v):
} } Program po uruchomieniu wyświetla następujące wyniki. Średnia dla iob wynosi 3.0 Średnia dla dob wynosi 3.3
Zauważ sposób deklaracji klasy Stats, cla ss Stats {
Ponieważ teraz typ T-jest ograniczony do klasy Number, kompilator wie, iż wszystkie obiekt)' typu T posiadają metodę doubleValueO, gdyż jest ona zadeklarowana w klasie Number. Jest to znaczaće udogodnienie. Co więcej, to rozwiązanie zapobiega utworzeniu
Rozdział 14. ♦ Typy sparametryzowane
357
obiektów klasy Stats o typie innym od numerycznego. Po usunięciu znaczników ko mentarza z końcówki programu i próbie kompilacji programu, kompilator zgłosi błąd, gdyż klasa String nie jest podklasą klasy Number.
Zastosowanie argumentów wieloznacznych Choć bezpieczeństwo typów jest niezmiernie użyteczne, czasem uniemożliwia wykona nie w pełni poprawnych konstrukcji. Załóżmy, iż chcemy dodać do klasy Stats z wcze śniejszego przykładu metodę o nazwie sameAvgO, która sprawdza, czy dwa obiekty Stats zaw ierają tablice dające w wyniku identyczną średnią (nie ma znaczenia typ danych numerycznych z każdej z tablic). Jeśli jeden z obiektów przechowuje tablicę wartości double 1,0, 2,0 i 3,0 a drugi tablicę wartości in t 2, 1 i 3, średnia w obu przy padkach będzie taka sama. Jednym ze sposobów implementacji metody sameAvg() jest przekazanie do niej obiektu Stats, a następnie porównanie obliczonej dla niego średniej ze średnią obiektu, dla którego została wywołana m etoda sameAvgO. W artość true zostanie zwrócona tylko wtedy, gdy obie średnie będą identyczne. Oto przykład kodu, który mógłby korzystać z metody sameAvg(). Integer inums[] - { 1 . 2. 3. 4. 5 } : Double dnums[] - { 1.1. 2.2. 3.3. 4.4. 5.5 }: Stats iob = new Stats(1nums): Stats dob = new Stats(dnums); if(iob.sameAvg(dob)) System.o u t.p rin tlnOŚredm e są ta k ie same."): else System .out.printlnf"Średnie są różne.” ):
Na pierwszy rzut oka napisanie kodu metody sameAvgO wydaje się łatwe. Skoro Stats to klasa sparametryzowana a je j metoda average!) może działać dla dowolnego typu obiektu Stats, nie powinny pojawić się żadne problemy. Niestety, problemy pojawiają się w momencie próby deklaracji parametru jako typu Stats. Ponieważ Stats jest typem sparametryzowanym, co należy wpisać w deklaracji jak o parametry typu? Początkowo może się wydawać, iż poprawnym rozwiązaniem jest zastosowanie jako parametru typu elementu T. / / Ten kod nie będzie działał poprawnie! II Sprawdza, czy dwie średnie są identyczne. boolean sameAvg(Stats ob) { if(average() — ob.average!)) return true: return fa lse:
}
358
Część I ♦ Język Java
Przedstawione rozw iązanie działa poprawnie tylko wtedy, gdy przekazywany obiekt S tats jest takiego sam ego typu jak obiekt, dla którego została wywołana metoda. Jeśli wywoływany obiekt je st typu S tats< In teg er> to parametr ob również musi być typu S ta ts< In teg ers. N ie można więc w tym rozw iązaniu porównać średnich obiektów Stats i Stats. To podejście nie obejmuje więc wszystkich dopuszczal nych sytuacji — nie je st rozwiązaniem ogólnym . Aby bardziej uogólnić metodę sameAvgO należy skorzystać z innej cechy typów sparametryzowanych Javy — argum entu wieloznacznego. Taki argument reprezentuje nie znany typ i oznacza się go znakiem zapytania (?). Oto wersja metody sameAvgO korzy stająca z argumentu wieloznacznego. / / S praw dź, c z y d w ie śre d n ie s ą identyczne. / / Z auw aż u ży c ie a rg u m en tu wieloznacznego.
sameAvg(Stats ob) { if(a v e ra g e () == ob. averageO) r e t u r n tr u e ;
b o o le a n
re tu rn
*a ls e :
} Konstrukcja S tats oznacza dowolny obiekt S tats, co umożliwia porównanie śred nich dwóch obiektów S ta ts różnych typów. O to pełny kod programu korzystającego z argumentu wieloznacznego. / / Z a sto so w a n ie a rg u m en tu w ieloznacznego.
class Stats { T [ ] nums: / /
ta b lic a N um ber lub j e j p o d k la sy
I I P rzek a za n ie d o kon stru ktora referen cji d o I I ta b licy ty p u N u m b er lub j e j podklasy’.
S ta ts (T [] O) { nums = o;
} / / Z aw sze zw ra c a n ie śre d n iej ja k o typu double. d o u b le
averageO { sum = 0 . 0 :
d o u b le
fo r d n t i= 0 : i < nums.length: 1++) sum += n um s[i] .doubleValueO : re tu rn
sum / nums.length:
} / / Spraw dź, c z y d w ie śred n ie są identyczne. / / Z auw aż u ży c ie argu m en tu wieloznacznego.
sameAvg(Stats ob) { i f (averageO == o b . average(j) r e t u r n tr u e :
b o o le a n
re tu rn
} }
fa ls e :
Rozdział 14. ♦ Typy sparametryzowane
359
/i Przykład użycia argumentu wieloznacznego. c la s s WildcardDemo { p u b lic s ta tic void main(String args[]) { Integer inums[] - { 1. 2. 3. 4. 5 }: Stats iob - new Stats(1nums): double v = iob.average() : System .out.printlnCŚrednia iob wynosi " + v): Double dnunis[] - { 1.1. 2.2. 3.3. 4.4, 5.5 }: Stats dob - new Stats(dnums): double w = dob.average(): System.out.println("Średni a dob wynosi " + w):
•
Float fnums[] - { 1.0F. 2 .OF. 3.OF. 4 .OF. 5 .OF }: Stats fob - new Stats(fnums): double x = fob.average(): System.out.p rin tln C Śred n ia fob wynosi " + x): // Sprawdź, które tablice mają takie same średnie. System.out.print("Średnie iob i dob są ”): i f (i ob.sameAvg(dob)) System.out.pri nt 1n( "tak ie same."): e lse System.out.pri nt 1n( " różne."): System.out.printCŚrednie iob i fob są "): if(iob.sameAvg(fob)) System.out.println ("ta k ie same."): else System .out.println("różne.");
} } Wynik działania programu jest następujący. Średnia Średnia Średnia Średnie Średnie
iob dob fob iob iob
wynosi 3.0 wynosi 3.3 wynosi 3.0 i dob są różne. i fob są takie same.
Ważna uwaga: argument wieloznaczny nie wpływa na typ obiektów Stats, które można utworzyć. Tym zajmuje się klauzula extends w deklaracji klasy Stats. Argument wielo znaczny oznacza po prostu dowolny p o praw ny obiekt S tats.
Ograniczony argument wieloznaczny Argument wieloznaczny można ograniczyć w podobny sposób, jak ogranicza się para metr typu. Ograniczanie wieloznaczności jest szczególnie ważne wtedy, gdy tworzy się typ sparametryzowany korzystający z hierarchii klas. Aby to lepiej zrozumieć, prze śledźmy przykład. Rozważmy następującą hierarchię klas zawierającą współrzędne. // Współrzędne dwuwymiarowe. c la s s TwoD { in t x. y:
360
Część I ♦ Język Java
TwoDdnt a. in t b) { x - a: y - b:
} } / / Współrzędne trójwymiarowe. class ThreeD extends TwoD { in t z: ThreeD(int a. in t b. in t c) { super(a. b): z - c :
} } // Współrzędne czterowymiarowe. class FourO extends ThreeD { in t t: FourD(int a. in t b. in t c. in t d) { super(a. b. c): t - d:
} } Na szczycie hierarchii znajduje się klasa TwoD hermetyzująca współrzędne dwuwymiarowe. Klasa ThreeD dziedziczy po klasie TwoD i dodaje trzeci wymiar do dwóch pozostałych. Klasa FourD dziedziczy po klasie ThreeD i dodaje czwarty wymiar (czas) do trzech pozostałych. Następnie pojaw ia się klasa sparametryzowana Coords przechowująca tablicę współ rzędnych. // Klasa przechowuje tablicę obiektów współrzędnych. class Coords {
T[] coords: Coords(T[] o) { coords - o: }
} Zauważ, iż klasa Coords ogranicza parametry typu do klasy TwoD. Oznacza to, iż tablica będzie służyła tylko i wyłącznie do przechowywania obiektów klasy TwoD lub jednej z jej podklas. Chcemy teraz napisać metodę, która wyświetla współrzędne X i Y wszystkich elementów znajdujących się w tablicy obiektu Coords. Ponieważ wszystkie typy obiektów Coords mają przynajmniej dwie współrzędne, wystarczy zastosować argument wieloznaczny. s ta tic void showXY(Coords c) {
System.out.println("Współrzędne X Y:"): fo r( in t i-0; i < c .coords.length; 1++) System.ou t.p rin tln tc .c o o rd s [ i] .x + " " + c .c o o rd s [i].y ):
System.out.printlnO: }
Rozdział 14. ♦ Typy sparametryzowane
361
Ponieważ klasa sparametryzowana Coords została ograniczona od góry do parametrów typu TwoD, wszystkie obiektu służące do utworzenia klasy będą m usiały być tablicą obiektów klasy TwoD lub jej podklas. Z tego względu metoda showXYO potrafi wyświe tlić zawartość dowolnego obiektu Coords. Co trzeba zrobić, aby wykonać metodę wyświetlającą współrzędne X, Y i Z obiektu ThreeD lub FourD? Problem polega na tym, iż nie wszystkie obiekty Coords muszą po siadać trzy współrzędne — obiekt Coords będzie zawierał tylko współrzędne X i Y. W jaki sposób napisać metodę wyświetlającą trzy współrzędne, aby działała dla obiek tów Coords i Coords, ale uniemożliwiała jej zastosowanie dla obiektów Coords? Wystarczy użyć ograniczonego argum entu w ieloznacznego. Ograniczony argument wieloznaczny określa górne lub dolne ograniczenie typu argu mentu. Umożliwia ograniczenie typów obiektów, dla których metoda będzie działać po prawnie. Najczęściej stosuje się ograniczenie górne wieloznaczności — używa się wtedy klauzuli extends w dokładnie ten sam sposób, jak w przypadku ograniczania typu. Zastosowanie ograniczenia wieloznaczności ułatwia wykonanie metody, która wyświetla współrzędne X, Y i Z tylko dla tych obiektów Coords, które rzeczywiście posiadają trzy współrzędne. Poniższa metoda showXYZO wyświetla wszystkie trzy współrzędne elemen tów znajdujących się w obiekcie Coords, o ile elementy te są klasy ThreeD lub jej podklasy. s ta t ic void showXYZ(Coords