131 31 3MB
Polish Pages [226] Year 2014
Java ĆW ICZENIA PRAKTYCZN E R e a liz u j sn y na ia v ie ! • Pakiet JDK i po czątki program ow ania, c z y li ja k szyb k o stw o rzy ć d zia ła ją cy program • O b iekto w o ść w akcji, c zy li na czy m polega n a jw ię k sza zale ta Javy • U n iw ersaln a skład nia, c z y li ja k najlep iej w yk o rzysta ć p rzen o śn o ść tego ję z y k a
Helion
Spis treści W stęp O książce Narzędzia Wersje Javy
7 8 9 10
Rozdział 1.
Krótkie w prow adzenie Instalacja JDK Tryb tekstowy Instalacja w systemie Windows Instalacja w systemie Linux Pierwszy program B-kod, kompilacja i maszyna wirtualna Java a C + + Obiektowy język programowania Struktura programu
11 11 11 13 15 17 19 20 21 22
Rozdział 2.
Zm ienne, operatory i in stru kcje Zmienne Typy podstawowe Deklarowanie zmiennych typów podstawowych Nazewnictwo zmiennych Typy odnośnikowe Deklarowanie zmiennych typów odnośnikowych
23 23 24 25 28 29 29
3
Java
• Ćwiczenia praktyczne
Operatory Operatory arytmetyczne Operatory bitowe Operatory logiczne Operatory przypisania Operatory porównania (relacyjne) Operator warunkowy Priorytety operatorów Instrukcje Instrukcja warunkowa if.. .else Instrukcja wyboru switch Pętla for Pętla while Pętla d o.w h ile Rozszerzona pętla for
32 33 39 41 42 42 43 44 45 45 50 53 57 59 61
Tablice Tworzenie tablic Zapis i odczyt elementów Operacje z użyciem pętli Rozmiar tablicy
63 63 66 68 72
O biekty i klasy Metody Konstruktory Specyfikatory dostępu Pakiety i typy klas Dziedziczenie
75 77 86 94 101 102
Obsługa błędów oraz w yjątki Błędy w programach Instrukcja try .ca tch Zgłaszanie wyjątków Hierarchia wyjątków
109 109 114 117 119
O peracje w ejścia-w yjścia Wyświetlanie danych na ekranie Wczytywanie danych z klawiatury Nowe sposoby wprowadzania danych Obsługa konsoli Operacje na plikach
12 3 124 126 135 139 145
Sp is tr e ś c i
5
Rozdział 7.
Aplety Aplikacja a aplet Pierwszy aplet Jak to działa? Cykl życia apletu Kroje pisma (fonty) Rysowanie grafiki Kolory Wyświetlanie obrazów
15 3 153 154 157 158 158 161 168 172
Rozdział 8.
Interakcja z użytkownikiem Obsługa myszy Rysowanie figur (I) Rysowanie figur (II) Rysowanie figur (III)
179 180 183 188 190
Rozdział 9.
Aplikacje z in terfejsem graficznym Tworzenie okna aplikacji Budowanie menu Wielopoziomowe menu Okna dialogowe
19 5 195 199 206 209
Rozdział 10 . Grafika i kom ponenty Rysowanie elementów graficznych Obsługa komponentów Przyciski JButton Pola tekstowe JTextField Pola tekstowe JTextArea Etykiety JLabel Pola wyboru JCheckBox Listy rozwijane JComboBox „Prawdziwa” aplikacja
215 215 217 218 219 221 224 226 228 230
Wstęp Chyba każdy, kto interesuje się inform atyką, słyszał o Javie. Ten stosunkowo młody (w porównaniu z C + + czy Pascalem ) język pro gramowania wyjątkowo szybko zdobył bardzo dużą popularność i ak ceptację ze strony programistów na całym św iecie. Początkowo wiele osób kojarzyło Javę tylko z apletami zawartymi na stronach WWW. To jednak tylko niew ielka część zastosowań, która dziś straciła już nieco na znaczeniu. Tak naprawdę jest to doskonały obiektowy język programowania, m ający różnorodne zastosowania — od krótkich apletów do pow ażnych aplikacji. Początki były jednak zupełnie inne. Być m oże trudno w to obecnie uwierzyć, ale język ten, pierwotnie znany jako Oak (z ang. dąb), m iał służyć jako narzędzie do sterowa nia tzw. urządzeniam i elektronicznym i powszechnego użytku, czyli wszelkiego rodzaju telewizorami, m agnetow idam i, pralkam i czy ku chenkami mikrofalowymi. Praktycznie — dowolnym urządzeniem , które posiadało m ikroprocesor. I to pierwotne przeznaczenie nie jest w spółcześnie m niej istotne niż kiedyś. W dobie pow szechnej kom puteryzacji i podłączania do sieci rozm aitych urządzeń (w tym także wspomnianych lodówek i pralek) takie zastosowanie wręcz zwiększa, a nie zmniejsza atrakcyjność języka. Stąd też wywodzi się jedna z naj większych zalet Javy — jej przenośność, czyli możliwość uruchamiania jednego programu na w ielu różnych platform ach. Skoro bowiem
7
8
Java
• Ćwiczenia praktyczne
m iała służyć do programowania dla tak w ielu różnorodnych urzą dzeń, m usiała był niezależna od platformy sprzętowo-systemowej. Ten sam program można będzie więc uruchomić, przynajmniej teore tycznie, zarówno na komputerze PC i M acintosh, jak i w W indowsie oraz Uniksie. Historia Oak rozpoczęła się pod koniec 1990 roku. Język został opra cowany jako część projektu o nazwie Green, rozpoczętego przez Patricka Naughtona, M ike’a Sheridana i Jam esa Goslinga w firm ie Sun M icro systems. Język był opracowany już w 1991 roku, jednak do 1994 roku nie udało się go spopularyzować i prace nad projektem zostały zawie szone. Był to jednak czas gwałtownego rozwoju sieci Internet i oka zało się, że Oak doskonale sprawdzałby się w tak różnorodnym środo wisku, jakim jest globalna sieć. W ten oto sposób w 1995 roku światło dzienne ujrzała Java. To, co stało się później, zaskoczyło chyba w szystkich, w tym samych tw órców języka. Java niew iarygodnie szybko została zaakceptow a n a przez społeczność internetow ą i program istów n a całym św iecie. Niewątpliwie bardzo duży w pływ m iała tu um iejętnie prowadzona kampania marketingowa producenta. Niemniej decydujące były z pew nością wyjątkowe zalety tej technologii. Java to bardzo dobrze skon struowany język programowania, który programistom zwykle przypada do gustu już przy pierwszym kontakcie. W każdym razie o Javie m ów ią i piszą wszyscy, pojaw iają się setki książek i stron interneto wych, pow stają w końcu napisane w niej programy. O becnie to już dojrzała, choć w ciąż rozw ijana technologia, która w rosła n a dobre w świat informatyki.
O książce Celem niniejszej publikacji jest przedstawienie nie wszystkich aspek tów programowania w Javie, ale jedynie pewnego wycinka tego za gadnienia. Omówione zostały podstawowe zasady programowania, zam ieszczono przykłady tworzenia apletów, czyli programów osa dzanych w stronach WWW, a także zaprezentowano podstawy tworze nia aplikacji z graficznym interfejsem oraz operacje w ejścia-w yjścia. Niestety, ze względu na ograniczoną ilość m iejsca nie m ożna było przedstawić w ielu ciekawych i bardziej zaawansowanych zagadnień, dlatego też będzie to raczej w ycieczka po programowaniu w Javie niż
W stęp
9
m etodyczny kurs opisujący całość zagadnienia. Jak jednak wskazuje sam tytuł, ta publikacja to ćw iczenia, które m ają pozw olić na szybkie zapoznanie się z podstaw owym i konstrukcjam i języka, niezbędnym i do rozpoczęcia programowania. N iniejsze ćw iczenia będą w ięc za równo doskonałym podręcznikiem dla osób, które by szybko chciały zapoznać się ze strukturą języka, jak i uzupełnieniem bardziej m eto dycznego kursu, jakim jest np. publikacja Praktyczny kurs Java (http://helion.pl/ksiazki/pkjav4.htm). Z agadnienia zaawansowane, np. tworzenie aplikacji sieciowych lub bazodanowych, zostały też przed stawione w publikacji Java. Ćwiczenia zaawansowane (http://helion.pl/ ksiazki/czjav2.htm).
Narzędzia Aby rozpocząć programowanie w Javie, niezbędne są odpowiednie narzędzia. Konkretnie — kom pilator oraz m aszyna wirtualna, która interpretuje skompilowane programy. Będziemy opierać się tu na pakiecie Java Developm ent Kit (JDK). Można skorzystać z w ersji sy gnowanej przez oficjalnego producenta Javy — firmę O racle1 — lub rozwijanej na zasadach wolnego oprogramowania wersji OpenJDK. Wer sja oficjalna dostępna jest pod adresami http://www.oracle.com/java/ , http://java.sun.com (w tym przypadku nastąpi przekierowanie do domeny oracle.com ) oraz http://www.java.com, a OpenJDK pod ad resem http://openjdk.java.net/. Najlepiej korzystać z m ożliw ie nowej w ersji JDK, tzn. 1.7 (7.0), 1.8 (8.0) lub wyższej (o ile taka będzie do stępna), choć podstawowe przykłady będą działać naw et na bardzo wiekowej już wersji 1.1. Przy ćwiczeniach om awiających tworzenie apletów można skorzystać z dowolnej przeglądarki internetow ej obsługującej język Java lub też dostępnej w JDK aplikacji appletviewer . W iększość obecnie dostęp nych na rynku przeglądarek udostępnia Javę poprzez m echanizm wtyczek, um ożliw iając zastosowanie najnow szych wersji JRE (ang. Java Runtime Environment), czyli środowiska uruchomieniowego. Na leży z tej m ożliw ości skorzystać. 1 Pierwotny producent — Sun Microsystems — został zakupiony przez Oracle w 2009 roku. Tym samym obecnie to Oracle oficjalnie odpowiada za rozwój Javy.
10
Java
• Ćwiczenia praktyczne
Oprócz JDK będzie potrzebny jedynie dowolny edytor tekstowy (ko rzystającym ze środowiska W indows m ożna polecić np. doskonały N o te p a d + + ) pozw alający na wpisywanie tekstu programów i zapi sywanie ich w plikach na dysku. Istnieje co prawda możliwość używa nia zintegrowanych środowisk program istycznych, jednak dla osób początkujących lepszym zestaw em byłby JDK i zwykły edytor tek stowy (najlepiej programistyczny), tak aby najpierw poznać dobrze sam język i jego w łaściw ości, a dopiero później bardziej zaawanso wane narzędzia (oczywiście jeśli ktoś w oli pracę w środow iskach ta k ich jak NetBeans czy Eclipse, m oże ich rów nież z pow odzeniem używać do nauki).
Wersje Javy Pierwsza powszechnie wykorzystywana wersja Javy nosiła numer 1.1 (JDK 1.1 i JRE 1.1). Stosunkow o szybko pojaw iła się jednak kolejna wersja, oznaczona num erem 1.2. Niosła ona ze sobą na tyle znaczące zmiany i usprawnienia, że nadano jej nazwę Platforma Java 2 (ang. Java 2 Platform ). Tym samym wersja poprzednia została nazwana Platform ą Java 1. W ram ach projektu Java 2 powstały trzy wersje na rzędzi JDK i JRE: 1.2, 1.3 i 1.4, a każda z nich m iała od kilku do k il kunastu podwersji. Kolejnym krokiem w rozwoju projektu była wersja 1.5, która ze względów czysto marketingowych została przem iano wana na 5.0. Następnie pojawiły się wersje 6.0 (czyli 1.6), 7 (czyli 1.7) oraz 8 (czyli 1.8). Podczas przygotowywania materiałów do niniej szej książki używane były wersje 6, 7 i 8. Jeśli stosowano konstrukcje języka dostępne wyłącznie w wersji 7 lub 8, było to oznaczane w opi sach przykładów. Warto przy tym w spom nieć, że wewnętrzna num e racja narzędzi (widoczna np. w opcjach kompilatora javac i narzędzia uruchom ieniowego java) w ciąż bazuje na w cześniejszej, logicznej numeracji (czyli Java 6.0 jest tożsama z Java 1.6, Java 7 to inaczej Java 1.7, a Java 8 to inaczej Java 1.8).
1 Krótkie wprowadzenie
Instalacja JDK Instalacja pakietu JDK, który umożliwi nam tworzenie aplikacji, niko mu nie powinna przysporzyć problemów, i to niezależnie od tego, czy wybrana zostanie w ersja dla system u Linux, W indows czy też dla in n ej platform y. JDK instaluje się bow iem podobnie jak każdą in n ą aplikację. Proces ten je st także opisany na stronach internetow ych producenta. Podane niżej przykłady in stalacji odnoszą się do wersji 1.5 - 1.8, dla systemów 32- i 64-bitowych.
Tryb tekstowy Kompilator zawarty w pakiecie JDK pracuje w trybie tekstowym, w ta kim też trybie będziem y urucham iać pierwsze napisane przez nas programy ilustru jące cechy języka. Poniew aż w dobie systemów
11
12
Java
• Ćwiczenia praktyczne
oferujących graficzny interfejs użytkownika z takiego trybu korzysta się coraz rzadziej, zobaczm y, jak uruchom ić go w system ach Linux i W indows. Jeśli pracujemy w systemie Windows, wciskamy na klawiaturze kom binację klawiszy W indow s+R1, w polu Otwórz (w niektórych wersjach systemu — Uruchom ) w pisujem y cmd lub cmd.exe2 i klikamy przycisk O K lub wciskamy klawisz Enter (rysunek 1.1). (W systemach XP i star szych, pole Uruchom jest też dostępne bezpośrednio w m enu Start3). Można też w dowolny inny sposób uruchomić aplikację cmd.exe. Po jaw i się wtedy okno wiersza poleceń (wiersza polecenia), w którym będzie m ożna wydawać kom endy. Jego wygląd dla system ów z ro dziny Windows 8 został przedstawiony na rysunku 1.2 (w innych wer sjach wygląda bardzo podobnie). Rysunek 1.1. Uruchamianie wiersza poleceń w Windows 8
Uruchamianie » -----
0 twórz:
Wpisz nazwę programu, folderu, dokum entu lub zasobu internetowego, a zostanie on otwarty przez system Windnw*. 37771
«
OK
Anuluj
£rzeglądaj...
Rysunek 1.2. Okno konsoli (wiersza poleceń) w systemie Windows 8
Jeśli pracujem y w Linuksie na konsoli tekstowej, nie trzeba oczywi ście wykonywać żadnych dodatkowych czynności. Jeżeli jednak ko
1 Klawisz funkcyjny Windows jest też opisywany jako Start. 2 W starszych systemach (Windows 98, Me) należy uruchomić aplikację command.exe (Start/Uruchom/command.exe).
3 W systemie Windows w wersjach Vista i 7 pole Uruchom standardowo nie jest dostępne w menu startowym, ale można je do niego dodać, korzystając z opcji Dostosuj.
R o z d z i a ł 1. • K r ó t k i e w p r o w a d z e n i e
13
rzystamy z interfejsu graficznego, należy uruchomić program terminala (konsoli). Jego przykładowy wygląd dla dystrybucji Ubuntu (wersja 13.10) został zaprezentowany na rysunku 1.3. Rysunek 1.3. Wygląd konsoli tekstowej w dystrybucji Ubuntu
Q O ffl
jank(® kom p: ~
jd iik flk u n p :
|
Dokładniejsze informacje o posługiwaniu się konsolą systemową moż na znaleźć w dokum entacji systemów operacyjnych.
Instalacja w systemie Windows W ersja instalacyjna dla systemów z rodziny W indows jest dystrybu owana w postaci pliku o nazwie: j dk-wers ja-windows-proc.exe
np .: j d k - 8 - w i n d o ws - i 5 8 6 . e x e j d k - 1 . 7 . 0 - w i nd o ws - i 5 8 6 . e x e
Uruchom ienie tego pliku spowoduje rozpoczęcie typowego procesu in stalacji aplikacji. Uwaga: Java 8 oficjalnie nie współpracuje z sys tem am i W indows poniżej w ersji 7. Po in stalacji dobrze jest dopisać do zm iennej środowiskowej PATH ścieżkę dostępu do katalogu bin środowiska JDK. Aby zm ienić ten parametr dla bieżącej sesji4 konsoli systemowej (po jej uruchom ieniu w sposób opisany w poprzednim punkcie), należy wydać polecenie: p ath = % p ath % ;ścieżk a_ d ostęp u
np .: pat h=%pat h%; c: \Program F i l e s \ j a v a \ j d k 1 . 8 . 0\bi n
4 Ustawienie nie będzie obowiązywało ani w innych oknach konsoli, ani też po zamknięciu bieżącego okna.
14
Java
• Ćwiczenia praktyczne
zakładając, że m am y do czynienia ze standardową in stalacją JDK w wersji 8 (rysunek 1.4). Aby dodać wspomnianą wartość na stałe (tak aby obowiązywała zawsze, w każdej sesji konsoli), należy uruchomić panel w łaściw ości systemu (w sposób odpowiedni dla danej w ersji W indows — typowo M ó j komputer/Właściwości. M ożna też skorzy stać ze skrótu klawiaturowego Windows+Pause), wybrać w nim za kładkę Zaawansowane (lub Zaawansowane ustawienia systemu za leżnie od posiadanej w ersji Windows) i kliknąć znajdujący się w niej przycisk Zm ienne środowiskowe (rysunek 1.4). Rysunek 1.4. Panel właściwości systemu
W oknie Zm ienne środowiskowe w części zatytułowanej Zm ienne sys temowe należy odszukać wpis dotyczący zmiennej Path (rysunek 1.5), zaznaczyć go i kliknąć widoczny poniżej przycisk Edytuj. W kolejnym oknie (rysunek 1.6) możliwa będzie edycja wpisu. Do istniejących danych należy dopisać (po średniku) ścieżkę dostępu do katalogu bin środowiska JDK — podobnie jak w przykładzie przedstawionym wyżej. Uwaga: nie należy kasować istniejących wartości!
R o z d z i a ł 1. • K r ó t k i e w p r o w a d z e n i e
15
Rysunek 1.5. Okno zmiennych środowiskowych systemu
Rysunek 1.6. Edycja zawartości zmiennej systemowej
Instalacja w systemie Linux Do wyboru m am y in stalację za pom ocą dystrybucji źródłowej lub w postaci pakietu RPM. W pierwszym przypadku do dyspozycji jest plik o nazwie: jd k-wersjo-linux-proc.tar.gz
w której wersja jest numerem wersji dystrybucji, a proc — określeniem rodziny procesorów, np.: jdk-8-linux-i586.tar.gz jd k -1 .7 .0 - lin u x -i586.tar.gz
Należy go skopiować lub przenieść do wybranego katalogu na dysku (do katalogu, w którym ma być zainstalowana Java), wydając polecenie (kopiowanie): cp . / j d k - 1 . 8 . 0 - l i n u x - i 5 8 6 . t a r . g z / u s r / j a v a /
16
Java
• Ćwiczenia praktyczne
lub (przenoszenie): mv . / j d k - 1 . 8 . 0 - l i n u x - i 5 8 6 . t a r . g z / u s r / j a v a /
o ile katalogiem docelowym jest /usr/java. Następnie należy przejść do tego katalogu, w ydając komendę: cd / u s r / j a v a
oraz rozpakować plik, w pisując polecenie: t a r xvfz j d k - 1 . 8 . 0 - l i n u x - i 5 8 6 . t a r . g z
W tedy w bieżącym katalogu zostanie utworzony podkatalog o na zwie jdk1.8.0, w którym znajdą się pliki pakietu. Aby usprawnić pracę z JDK, po in stalacji warto do zm iennej środo wiskowej PATH dodać ścieżkę do podkatalogu bin tego pakietu (np. /usr/java/jdk1.8.0/bin/, o ile JDK zostało zainstalow ane w katalogu /usr/java/jdk1.8.0/) — nie będziem y wtedy m usieli za każdym razem podawać pełnej ścieżki dostępu, aby uruchom ić kom pilator czy też inne narzędzie (rysunek 1.7). Aby wykonać tę operację dla bieżącej se sji w przypadku powłoki systemowej bash, wykonujemy polecenie: PATH=$PATH: /ś c i e ż k a _ d o s t ę p u
np .: PATH=$PATH:/usr/java/jdk1.8.0/bin/
Jeżeli zmiany m ają być zapisane na stałe, należy odpowiednie mody fikacje wprowadzić do pliku .bash_profile znajdującego się w katalogu domowym danego użytkownika systemu. Rysunek 1.7. Modyfikacja zmiennej systemowej PATH
ja n k (3 )lc /c a lh o s t:/u s r /ja v a
Plik
Cdycja
Widok
Wyszukiwani«
Terminal
X
Pomoc
J d k 1 .8 . 6 / d b / b in / s ta rtNetwo rkSo rve r .bat j d k l . 0 . 0 / d b / b in / ij .bat ld k l .8 .0 / d b / bin/ N ctw orkS'o rvcrC on t r o i .bat j d k 1 .8 . 0 / d b / bin/ d b lo ok j d k 1 .8 . 0 / d b / b in / se t Netwo rkSe rve rCP 1d k l . 8 . 0 / d b / bin/ setN e tw or k C lie n tC P .b a t j d k 1 . 8 . 0 / d b / b in / so t Notwo rkSo rve rC P .bat j d k 1 . 8 . 0/db/bi n/stopNetw o rkSe rve r .bat j d k l .8.0/db/README JDK.htm l [ j a n k ę lo c a lh o s t ja v a ]$ PATH=$PATH:/ u s r / ja v a / j d k l . 8 . 0 / b in / [ ja n k @ l o c a l h o st
ja v a ]$
h c I iu
$PATH
/us r / l o c a l / b in :/ us r / b i n :/ b in :/ us r / l o c a l / s b i n :/ u s r / s b i n :/home mo/j a n k / b in :/ u sr/ j ava/j d k l . 8 .0/b in / [ ja n k @ lo c a l h o s l j a v a ] $ |
R o z d z i a ł 1. • K r ó t k i e w p r o w a d z e n i e
17
W drugim przypadku (pakiet RPM) do dyspozycji mamy plik o nazwie: jdk-wersja-linux-proc-rpm
np .: jdk-8-linux-i586-rpm
Może on być instalow any przez użytkownika posiadającego upraw nienia adm inistratora systemu (w dystrybucjach, które obsługują pa kiety typu rpm, np. RedHat, Fedora). Instalacja odbywa się przez wy danie polecenia: rpm - i v h j d k - 8 - l i n u x - i 5 8 6 . r p m
Po jej zakończeniu pakiet JDK jest gotowy do użycia.
Pierwszy program Zacznijmy tradycyjnie, tak jak w większości kursów dotyczących pro gramowania. N apiszem y prostą aplikację, której jedynym zadaniem będzie wyśw ietlenie n a ekranie napisu. Ć W I C Z E N I E_________________________________________________________________________________________
H
I«
Aplikacja w yśw ietlająca tekst
Napisz program w yśw ietlający na ekranie dowolny napis. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { System.out.println
( " P i e r ws z y program w J a v i e " ) ;
} }
1. Plik z tekstem programu zapisujem y w w ybranym katalogu roboczym pod nazwą Main.java. W wierszu poleceń zmieniamy katalog bieżący na ten, w którym zapisaliśm y plik. Wydajemy w ięc polecenie: cd d y s k :\ ś c ież k a _ d o stęp u \ n a z w a _ k a ta lo g u
np .: cd c : \ j a v a \ p r o j e k t y
18
Java
• Ćwiczenia praktyczne
w system ie W indows lub: cd ś c ie ż k a _ d o s tę p u /n a z w a _ k a t a lo g u
np .: /home/jank/java/
w system ie Linux. Wielkość liter ma znaczenie! I to zarówno w tekście programu, jak i w nazwie pliku. Jeśli się pomylimy, kompilacja się nie uda.
2. Nie będziem y w tej chw ili analizować sposobu działania tej aplikacji, postaramy się natom iast zobaczyć wyniki jej działania na ekranie. Przystępujemy do kom pilacji, pisząc (przy założeniu, że pakiet JDK jest już poprawnie zainstalow any w system ie, a zmienna systemowa PATH wskazuje ścieżkę dostępu do katalogu bin pakietu JDK5): j a v a c Ma i n . j a v a
3. Jeżeli kom pilacja (bliższe w yjaśnienie tego terminu znajduje się w kolejnym podrozdziale) się powiedzie, na dysku powstanie nowy plik o nazwie Main.class. Aby uruchom ić program, piszem y6: j a v a Main
Efekt działania jest w idoczny na rysunku 1.8. Zwróćm y uwagę, że w przypadku kom pilacji podawaliśmy nazwę pliku z rozszerzeniem (Main.java), natomiast przy uruchamianiu rozszerzenie pominęliśmy (Main).
5Jeżeli zmienna środowiskowa PATH nie zawiera wskazanej ścieżki dostępu, należy ją podać przy wywoływaniu kompilatora javac. Instrukcja kompilująca
program wyglądałaby wtedy np. tak:
/usr/java/jdk1.8.0/bin/javac
Mai n. j a v a l ub tak: c: \Program F i l e s \ j a v a \ j d k 8 \ b i n \ j a v a c Ma i n . j a v a . W Linuksie
może być też konieczne dodanie do nazwy kompilowanego pliku wskazania do katalogu bieżącego (o ile wskazanie tego katalogu nie znajduje się w zmiennej środowiskowej PATH). Wtedy zamiast Main.java trzeba użyć odwołania ./Main.java. 6 Mają tu zastosowanie te same uwagi, które zostały podane w poprzednim przypisie.
R o z d z i a ł 1. • K r ó t k i e w p r o w a d z e n i e
19
Rysunek 1.8. Efekt działania pierwszego programu w Javie
B-kod, kompilacja i maszyna wirtualna Każdy napisany program przed w ykonaniem m usi być przetłum a czony na język zrozumiały dla komputera (procesora), czyli poddany procesow i kom pilacji. W przypadku takich języków jak C, C + + czy Pascal jest to zazwyczaj kom pilacja do kodu natywnego procesora, tzn. kodu, który procesor jest w stanie bezpośrednio wykonać. W przy padku Javy tak jednak być nie może, ponieważ programy nie byłyby wtedy przenośne. Każdy procesor ma przecież inny zestaw instrukcji, a zatem program skompilowany dla jednego, nie byłby zrozumiały dla drugiego. Dlatego też w przypadku Javy efektem kom pilacji jest kod pośredni, tzw. b-kod (ang. b-code, byte-code). Jest on zawarty w plikach z rozszerzeniem class. W naszym przypadku tekst programu (nazywany kodem źródłowym) z pliku Main.java został skompilowany do b-kodu, który zapisano w pliku Main.class. K om pilacja została wykonana przez kom pilator — program javac. Kompilator ten pracuje w wierszu poleceń i przyjmuje w postaci argu mentu jeden plik lub w ięcej plików z kodem źródłowym. Zatem jego schem atyczne wywołanie to: j a v a c n a z w a _ p lik u . j a v a
lub też, jeśli plików jest kilka: javac p l i k i .java p lik 2 . ja va p lik 3 .java
Aby b-kod mógł zostać zrozumiany przez procesor, m usi być ponow nie przetłum aczony. Dokonuje tego tzw. m aszyna w irtualna Javy, czyli interpreter b-kodu dla danego typu procesora (i systemu opera-
20
Java
• Ćwiczenia praktyczne
cyjnego). W ynika z tego, że wystarczy, aby dla każdego system u pow stała dedykowana m u m aszyna w irtualna Javy (środowisko uruchom ieniow e), a będzie m ożna uruchom ić w nim każdy pro gram w Javie bez wykonywania dodatkowych m odyfikacji (to oczy w iście pew ne uproszczenie, nie uruchom im y np. programu graficz nego na konsoli tekstowej). W pierwszych wersjach Javy była to, niestety, również jedna z przyczyn jej powolności. Kod interpretowany jest bow iem w olniejszy od wykonywanego bezpośrednio na danym procesorze. O becnie nie m a to już tak dużego znaczenia jak kiedyś, gdyż są dostępne doskonałe m aszyny wirtualne, zawierające np. kom pilatory JIT (ang. Just In Time), które w locie kom pilują część (lub nawet całość) b-kodu do kodu natywnego procesora — niemniej Java będzie zazwyczaj w olniejsza7 niż czyste C czy C + + 8.
Java a C++ Osobom, które znają C + + , Java zapewne od razu się spodoba. Choćby dlatego, że jej składnia jest do C + + bardzo podobna. Jest to jednak m iejscam i nieco złudne, gdyż ten sam zapis w C + + w cale nie m usi oznaczać dokładnie tego samego w Javie. Mówiąc ściślej, syntaktyka jest podobna, ale semantyka inna. Trzeba jednak dodać, że bardzo szybko wychwytuje się te różnice i choć w początkowym okresie owe różnice mogą nieco przeszkadzać, w niedługim czasie przestają sprawiać jakikolw iek problem. Przedstawiony w ćw iczeniu 1.1 pro gram w Javie w yśw ietlający na ekranie napis wyglądałby w C + + na stępująco: #incl ude i n t main ( i n t a r g c , c h a r * * a r g v ) { c out j a v a M a in p ie r w s z a lic z b a : 10 d ru g a l i c z b a : 20 C: \ jd v d \ p ro j ek ly > _
W artość zm iennej m ożna również przypisać już w trakcie deklaracji, pisząc: ty p _ zm ien n ej nazw a_zm ien n ej = w a r t o ś ć ;
Można również zadeklarować wiele zm iennych danego typu, od dzielając ich nazwy przecinkam i. Część z n ich m oże być też od razu zainicjow ana: ty p _ zm ien n ej n azw ał, nazwa2, nazwa3; ty p _ zm ien n ej nazw ał = w a r t o ś ć ł, nazw a2, nazwa3 = w a r to ś ć 2 ;
Zmienne w Javie, podobnie jak w C czy C + + , ale inaczej niż w Pascalu, można deklarować wedle potrzeb w dowolnym miejscu programu3. Ć W I C Z E N I E ________________________________________________________________________________________
2.2
Jednoczesna deklaracja i inicjacja zmiennych
Zadeklaruj i jednocześnie zain icju j dwie zm ienne typu całkowitego. W ynik wyświetl na ekranie. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) i n t p i e r ws z a L i c z b a = 10; i n t dr uga L i c z b a = 20 ; S y s t e m . o u t . p r i n t l n ("pierwsza l i c z b a : System.out.println
( " dr uga l i c z b a :
{
" + pierwszaLiczba);
" + drugaLiczba);
} }
3 Oczywiście w dowolnym miejscu, w którym ze względów składniowych deklaracja jest dopuszczalna.
28
Java
• Ćwiczenia praktyczne
Ć W I C Z E N I E ________________________________________________________________________________________
2.3
Deklarowanie zmiennych w jednym wierszu
Zadeklaruj w jednym wierszu kilka zmiennych typu całkowitego. Niektóre z n ich zainicjuj. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { i n t p i e r ws z a L i c z b a = 10, dr uga L i c z b a = 2 0 , i , j , k; S y s t e m . o u t . p r i n t l n ("pierwsza l i c z b a : " + pierwszaLiczba); S y s t e m . o u t . p r i n t l n ( " dr uga l i c z b a : " + d r u g a L i c z b a ) ; } }
Nazewnictwo zmiennych Przy nazywaniu zm iennych obow iązują pewne zasady. Otóż nazwa może się składać z w ielkich i m ałych liter oraz cyfr, znaku podkre ślenia i znaku dolara. Nie może się jednak zaczynać od cyfry. Można przy tym używać polskich znaków (a dokładniej — dowolnych znaków Unicode; zatem znaki niem ieckie, portugalskie czy naw et chińskie są również dozwolone). Często jednak korzysta się wyłącznie ze zna ków alfabetu łacińskiego (zależy to jednak od konw encji przyjętej w danym projekcie). Nazwa zm iennej powinna także odzwierciedlać funkcję pełn ion ą w programie. Jeżeli na przykład określa ona liczbę punktów w jakim ś zbiorze, to najlep iej nazw ać ją liczbaPunktow lub naw et liczbaPunktowWZbiorze (poprawne będą też nazwy liczbaPunktów i liczbaPunktówWZbiorze). Mimo że tak długa nazwa może wydawać się dziwna, jednak bardzo poprawia czytelność programu oraz ułatw ia jego analizę. Naprawdę warto ten sposób stosować. Z reguły przyj m uje się też, co również jest bardzo wygodne, że nazwę zm iennej rozpoczynamy m ałą literą, a poszczególne człony tej nazwy (wyrazy, które się na nią składają) w ielk ą literą — dokładnie tak jak w powyż szych przykładach4.
4 Jest to zatem standard Lower Camel Case. Nie jest to jednak zapis obligatoryjny, można oczywiście stosować inne standardy.
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
29
Typy odnośnikowe Typy odnośnikowe (ang. reference types) m ożem y podzielić na dwa umowne rodzaje: □ typy klasowe (ang. class types)5, □ typy tablicow e (ang. array types). Zacznijm y od typów tablicow ych. Tablice są to wektory elementów danego typu i służą do uporządkowanego przechow ywania w artości tego typu. Mogą być jedno- bądź wielowymiarowe. Dostęp do danego elem entu tablicy jest realizow any przez podanie jego indeksu, czyli m iejsca w tablicy, w którym się on znajduje. Dla tablicy jednowymia rowej będzie to po prostu kolejny num er elementu, dla tablicy dwu wymiarowej trzeba już podać zarówno num er wiersza, jak i kolum ny itd. Jeśli chcem y zatem przechow ać w programie 10 liczb całkow i tych, najwygodniej będzie użyć w tym celu 10-elem entowej tablicy typu int. Tablice zostaną bliżej omówione w rozdziale 3. Typy klasowe pozwalają na tworzenie klas i deklarowanie zmiennych obiektowych. Zajm iem y się nim i w rozdziale 4.
Deklarowanie zmiennych typów odnośnikowych Zm ienne typów odnośnikowych deklarujemy podobnie jak w przy padku zm iennych typów podstawowych, tzn. pisząc: ty p _ zm ien n ej n azw a_zm ien n ej ;
lub: ty p _ zm ien n ej n azw a_zm ien n ej_1 , n azw a_zm ien n ej_2, n azw a_zm ien n ej_3 ;
Stosu jąc taki zapis, inaczej niż w przypadku typów prostych, zade klarowaliśm y jednak jedynie tzw. odniesienie (ang. reference ) do obiektu, a nie sam byt, jakim jest obiekt! Takiem u odniesieniu przy pisana jest domyślnie wartość pusta (null), czyli praktycznie nie m o żemy wykonywać na nim żadnej operacji. Dopiero po utworzeniu odpowiedniego obiektu w pam ięci m ożemy pow iązać go z tak zade klarowaną zmienną. Jeśli zatem napiszem y np.: i n t a;
5 Typy klasowe moglibyśmy podzielić z kolei na obiektowe i interfejsowe; są to jednak rozważania, którymi nie będziemy się w niniejszej publikacji zajmować.
Java
30
• Ćwiczenia praktyczne
będziem y m ieli gotową do użycia zm ienną typu całkowitego. M oże my jej przypisać np. wartość 10. Żeby jednak m óc skorzystać z tabli cy, m usim y zadeklarować zm ienną odnośnikową typu tablicowego, utworzyć obiekt tablicy i powiązać go ze zmienną. Dopiero wtedy będziem y mogli swobodnie odwoływać się do kolejnych elementów. Pisząc zatem: i n t t ab l i c a [ ] ;
zadeklarujemy odniesienie do tablicy, która będzie mogła zawierać elementy typu int, czyli 32-bitow e liczby całkow ite. Sam ej tablicy jednak jeszcze wcale nie ma. Przekonamy się o tym, wykonując kolejne ćw iczenia (osoby nieznające pojęcia tablicy mogą pom inąć tę część m ateriału i pow rócić do niej po zapoznaniu się z rozdziałem 3.). Ć W I C Z E N I E_________________________________________________________________________________________
Zadeklaruj tablicę elementów typu całkowitego. Przypisz zerowemu elementowi tablicy dowolną wartość. Spróbuj skompilować i urucho m ić program. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { i n t t ab l i c a [ ] ; t a b l i c a [ 0 ] = 11; S y s t e m . o u t . p r i n t l n ("Zerowy el ement t a b l i c y t o :
" + tablica[0]);
} }
Już przy próbie kom pilacji kom pilator w ypisze na ekranie tekst: variable tablica might not have been in itia liz e d , inform ujący nas, że chcem y odwołać się do zm iennej, która prawdopodobnie nie została zainicjalizow ana (rysunek 2.2). W idzim y też w yraźnie, że w razie w ystąpienia błędu na etapie kom pilacji otrzymujemy kilka ważnych i pomocnych informacji. Przede wszystkim jest to nazwa pliku, w któ rym wystąpił błąd (to ważne, gdyż program może składać się z bardzo w ielu plików), num er w iersza w tym pliku oraz konkretne m iejsce wystąpienia błędu. Na samym końcu kompilator podaje też całkowitą liczbę błędów.
Skoro jednak w ystąpił błąd, należy go natychm iast naprawić. W tym przypadku oznacza to utworzenie tablicy i przypisanie jej zm iennej tablica.
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
31
C:\Windows\system32\cmd.exe r : \ j a v a \ p r o j e lc t y > j a v a r M a in , j a v a M a in .ja v a :4 : e r r o r : v a r ia b le t a h lic a ta b lira L O J = 1 1 ; A 1 e rro r
m ig h t n o t h a v e b e e n i n i t i a l i z e d
' : \ j d v d \ p r o j ek ly > _
Rysunek 2.2. Błąd kompilacji spowodowany niezainicjowaniem zmiennej tablica ĆW I CZ EN
2.5
i e
________________________________________________________________________________________
Deklaracja i utworzenie tablicy
Zadeklaruj i utwórz tablicę elem entów typu całkowitego. Przypisz zerowemu elem entowi tablicy dowolną wartość. Spróbuj wyśw ietlić zawartość tego elem entu na ekranie. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) i n t t a b l i c a [ ] = new i n t [ 1 0 ] ; t a b l i c a [ 0 ] = 11; System.out.println
{
("Zerowy el ement t a b l i c y t o :
" + tablica[0]);
} }
W yrażenie new int[10] oznacza utworzenie nowej, jednowymiarowej tablicy liczb typu int o rozm iarze 10 elementów. Ta nowa tablica zo stała przypisana zm iennej odnośnikowej o nazwie tablica. Po takim przypisaniu możemy odwoływać się do kolejnych elem entów tej ta blicy, pisząc: tablica[intfex]
Zapis tablica[0] = 11; oznacza w ięc: przypisz wartość 11 elementowi tablicy o indeksie 0.
W arto zw rócić szczególną uwagę, że elem enty tablicy są num ero wane od zera, a nie od 1. O znacza to, że pierwszy elem ent tablicy 10-elem entow ej m a indeks 0, a ostatni 9 (a nie 10!). Co się jednak stanie, jeśli — nieprzyzw yczajeni do takiego sposobu indeksowania — odwołamy się do indeksu o numerze 10? Sprawdźmy to, wykonując ćw iczenie 2.6.
32
Java
• Ćwiczenia praktyczne
Ć W I C Z E N I E ________________________________________________________________________________________
2.6
Odwołanie do nieistniejącego indeksu tablicy
Zadeklaruj i zainicjalizuj tablicę 10-elem entow ą. Spróbuj przypisać elem entow i o indeksie 10 dowolną liczbę całkowitą. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { i n t t a b l i c a [ ] = new i n t [ 1 0 ] ; t a b l i c a [ 1 0 ] = 11; S y s t e m . o u t . p r i n t l n ( " D z i e s i ą t y el ement t a b l i c y t o :
" + tablica[10]);
} }
Efekt działania kodu jest widoczny na rysunku 2.3. W brew pozorom nie stało się jednak n ic strasznego. W ystąpił błąd, został on jednak obsłużony przez m aszynę w irtualną Javy. Konkretnie został wygene rowany tzw. wyjątek i program standardowo zakończył działanie. Taki wyjątek możemy jednak przechwycić i tym samym zapobiec niekon trolowanemu zakończeniu aplikacji. Jest to jednak odrębny, aczkol wiek bardzo ważny tem at — zajm iem y się nim nieco później. Godne uwagi jest to, że próba odwołania się do nieistniejącego elem entu zo stała wykryta i to odwołanie tak naprawdę nie wystąpiło! Program nie naruszył w ięc niezarezerwowanego dla niego obszaru pam ięci. C\W indows\system 32\crTKl.exe
: : \ ju v u \ p r o j e k t y > j a v a c M a i n , ja v u ' : \ j a v a \ p r o j e k t y > j a v a Main x c c p t io n an: 10
in
th rea d
''m a in "
j a v a . I a n q . A r r a y lr id e x O u LOl B o u n d s E x c e p t i
at M a in .n a in C M a in .ja v a :4 ) ” : \ ja v a \ p r n jt*k I y > _
Rysunek 2.3. Próba odwołania się do nieistniejącego elementu tablicy
Operatory Poznaliśm y już zm ienne, m usim y jed nak w iedzieć, jakie operacje możemy n a nich wykonywać. Operacje wykonujemy za pom ocą róż nych operatorów, np. odejmowania, dodawania, przypisania itd. Są więc operatory6: 6 Można wydzielić również inne grupy, ale wykracza to poza ramy tematyczne niniejszej publikacji.
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
□
33
arytmetyczne,
□ bitowe, □ logiczne, □ przypisania, □ porównania.
Operatory arytmetyczne Wśród tych operatorów znajdziem y standardowo działające: □ + — dodawanie, □
odejmowanie,
□
* — m nożenie,
□
/ — dzielenie.
Ć W I C Z E N I E_________________________________________________________________________________________
Zadeklaruj dwie zm ienne typu całkow itego. W ykonaj na n ich kilka operacji arytm etycznych. W yniki wyświetl na ekranie. c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) i n t a , b, c; a = 10; b = 25; c = b - a; System.out.println("a = " + a);
{
Sy ste m .o ut. pri ntl n("b = " + b); System.out.println("b - a = " + c); c = a * b; System.out.println("a * b = " + c); } }
Najpierw zostały zadeklarowane trzy zm ienne typu int: a, b i c. Dwie pierwsze otrzymały w artości liczbow e (10 i 25), natom iast trzecia — wartość w ynikającą z odejmowania b - a (będzie to zatem 15). Na stępnie wartości zmiennych zostały wyświetlone na ekranie. W kolej nym kroku zm ienna c otrzymała now ą wartość wynikającą z mnoże nia a * b i ta wartość (250) również została wyświetlona. Efekt działania programu został przedstawiony na rysunku 2.4.
Java
34
• Ćwiczenia praktyczne
□
Rysunek 2.4. Wykonywanie działań arytmetycznych na zmiennych
Do operatorów arytm etycznych należy rów nież znak %, przy czym nie oznacza on obliczania procentów , ale dzielenie m odulo (resztę z dzielenia). Np. w ynik działania 12 % 5 wynosi 2, piątka m ieści się bow iem w dwunastu dwa razy, pozostaw iając resztę 2 (5 * 2 = 10, 10 + 2 = 12). ĆWICZENIE
_________ Dzielenie modulo Zadeklaruj kilka zmiennych. Wykonaj na nich operacje dzielenia mo dulo. W yniki wyświetl na ekranie. c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { i n t a , b, c; a = 10; b = 25; c = b % a; System.out.println("b % a = " + c); System.out.println("a % 3 = " + a % 3); c = a * b; S y s t e m . o u t . p r i n t l n ( " ( a * b) % 120 = " + c % 1 2 0 ) ; }
Pierwsza część kodu jest tu taka sama jak w ćw iczeniu 2.8. Zm iennej c została jednak przypisana w artość dzielenia modulo b % a. Tym samym pierw szą jej w artością będzie 5 (25 % 10 = 5, bo 2 * 10 = 20, 20 + 5 = 25). W ynik tego działania (wartość zmiennej c) został wyświe tlony za pom ocą znanej nam konstrukcji System.out.println. W kolej nej instrukcji na ekran została wyprowadzona wartość działania a % 3, czyli 1 (10 % 3 = 1, bo 3 * 3 = 9, 9 + 1 = 10). N astępnie zm iennej c przypisano wartość m nożenia a * b (tak samo jak w ćwiczeniu 2.7),
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
35
a na ekran został wyprowadzony wynik działania c % 20 (czyli (a * b) % 120). Wynik tego działania to 10 (c = 250, 250 % 120 = 10, bo 2 * 120 = 240, 240 + 10 = 250).
Kolejne operatory typu arytmetycznego to operator inkrementacji i dekrem entacji. Operator inkrem entacji (czyli zwiększania), którego symbolem jest ++, powoduje przyrost wartości zmiennej o jeden. Może występować w formie przyrostkowej (postikrementacyjnej) bądź przed rostkowej (preikrementacyjnej). Oznacza to, że jeśli mamy zmienną, która nazywa się np. x, forma przedrostkowa będzie wyglądać: ++x, natom iast przyrostkowa: x++. Oba te wyrażenia zwiększą wartość zm iennej x o jeden, jednak nie są one równoważne. Otóż operacja x++ zwiększa wartość zm iennej po jej wykorzystaniu, natom iast ++x przed je j w ykorzystaniem . Czasem takie rozróżnienie jest bardzo pom ocne przy pisaniu programu. Ć W I C Z E N I E ________________________________________________________________________________________
2.9
Operator inkrementacji
Przeanalizuj poniższy kod. Nie urucham iaj programu, ale zastanów się, jaki ciąg liczb będzie wyświetlony. Następnie, już po uruchom ie niu kodu, sprawdź swoje przypuszczenia. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] )
/* 1 * / /* 2 * / /* 3 * / /* 4 * / /* 5 * / /* 6 * / /* 7 * / /* 8 * /
{
i n t x = 1, y; System.out.println System.out.println
(++x); (x++);
System.out.println y = x++;
(x);
System.out.println y = ++x;
(y);
System.out.println
(++y);
} }
Dla ułatwienia poszczególne wiersze w programie zostały oznaczone kolejnym i liczbam i (w tym celu użyto komentarzy blokowych złożo nych ze znaków /* i */). W ynikiem działania tego programu będzie ciąg liczb 2, 2, 3, 3, 6. Dlaczego? Na początku zm ienna x przyjm uje war tość 1. W drugim wierszu m etody main w ystępuje operator ++x, za tem najpierw jest ona zw iększana o jed en (x = 2), a dopiero potem
36
Java
• Ćwiczenia praktyczne
wyświetlana na ekranie. W wierszu o numerze 3 jest odwrotnie. Naj pierw wartość zm iennej x jest w yświetlana (x = 2), a dopiero potem zwiększana o 1 (x = 3). W wierszu czwartym po prostu wyświetlamy wartość x (x = 3). W wierszu piątym najpierw zm iennej y jest przypi sywana dotychczasowa w artość x (x = 3, y = 3), a następnie wartość x jest zwiększana o jeden (x = 4). W wierszu szóstym wyświetlamy wartość y (y = 3). W wierszu siódmym najpierw zwiększamy wartość x o jeden (x = 5), a później przypisujemy tę wartość zmiennej y (y = 5). W wierszu ostatnim, ósmym, zwiększamy y o jeden (y = 6) i wyśw ie tlam y na ekranie.
Operator dekrementacji (--) działa analogicznie, z tym że zamiast zwięk szać wartości zm iennych, zm niejsza je, oczywiście zawsze o jeden. Ć W I C Z E N I E ________________________________________________________________________________________
2.10
Operator dekrementacji
Zm ień kod z ćwiczenia 2.9 tak, aby operator ++ został zastąpiony ope ratorem --. Następnie przeanalizuj jego działanie i sprawdź, czy otrzymany wynik jest taki sam jak otrzymany na ekranie po urucho mieniu kodu. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) /* 1 * / i n t x = 1, y; /* 2 * / S y s t e m . o u t . p r i n t l n ( - - x ) ; /* 3 * / S y s t e m . o u t . p r i n t l n ( x - - ) ; /* 4 * / S y s t e m . o u t . p r i n t l n ( x ) ; /* 5 * / y = x - - ; /* 6 * / S y s t e m . o u t . p r i n t l n ( y ) ; /* 7 * / y = - - x ; / * 8 * / System.out.println ( - - y ) ;
{
} }
Kod działa tu analogicznie do przedstawionego w ćwiczeniu 2.9, z tą różnicą, że wartość zmiennych jest zmniejszana. W związku z tym po uruchomieniu programu zostanie uzyskany ciąg liczb: 0, 0, -1, -1, -4.
Działania operatorów arytm etycznych na liczbach całkow itych nie trzeba chyba w yjaśniać, no m oże z dwoma w yjątkam i. Otóż, co się stanie, jeżeli wynik dzielenia dwóch liczb całkow itych nie będzie
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
37
liczb ą całkow itą? Odpowiedź na szczęście jest prosta — zostanie utracona część ułamkowa wyniku. Zatem w ynikiem działania 7 / 2 w arytmetyce liczb całkowitych będzie 3 („prawdziwym” wynikiem jest oczyw iście 3,5, która to wartość zostaje zaokrąglona w dół do najbliższej liczby całkow itej, czyli trzech). Ć W I C Z E N I E ________________________________________________________________________________________
2.11
Dzielenie liczb całkowitych
W ykonaj dzielenie zm iennych typu całkowitego. Sprawdź rezultaty w sytuacji, gdy rzeczyw isty wynik jest ułamkiem. c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { i n t a = 8 , b = 3 , c = 2; System.out.println("a = "+ a ) ; System.out.println("b = "+ b ) ; System.out.println("c = "+ c ) ; System.out.println("a / b = " + a / b); System.out.println("a / c = " + a / c); System.out.println("b / c = " + b / c); } }
Zostały zadeklarowane i jednocześnie zainicjowane trzy zmienne typu int, a ich w artości wypisane na ekranie: a = 8, b = 3 i c = 2. Następ nie zostały wyświetlone w yniki dzielenia całkowitoliczbowego a / b, czyli 2 („prawdziwym” wynikiem jest 2 i dwie trzecie), a / c, czyli 4, i b / c, czyli 1 („prawdziwym” w ynikiem jest 1,5).
Drugim problem em jest to, co się stanie, jeż eli przekroczym y zakres jakiejś zmiennej. Pamiętamy np., że zmienna typu byte jest zapisywana na 8 bitach i może przyjmować w artości od -128 do 127 (patrz tabela 2.1). Spróbujm y zatem przypisać zm iennej tego typu wartość 128. Ć W I C Z E N I E ________________________________________________________________________________________
2.12
Przekroczenie zakresu w trakcie kompilacji
Zadeklaruj zm ienną typu byte. Przypisz jej wartość 128. Spróbuj wy konać kom pilację otrzymanego kodu. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) b y t e zmi enna;
{
38
Java
• Ćwiczenia praktyczne
zmienna = 128; System.out.println(zmienna); } }
Próbując wykonać kom pilację kodu z ćw iczenia, szybko się przeko namy, że nie będzie to m ożliwe. Kompilator nie dopuści do przypi sania zm iennej w artości wykraczającej poza zakres jej typu. Zoba czymy zatem kom unikat o błędzie przedstawiony na rysunku 2.5. a
_
C:\Windows\system32\cmd.exe
c : \ j a v a \ p r o j e k t y > j a vac M a in . j a v a M a in . j a v a : 4 : e r r o r : in c o m p a t ib le fro m i n t t o b y t e z m ie n n a — 1 2 8 ; A 1 e rro r
typ e s:
p o s s ib le
lo s s y
□
c o n v e r s io n
C :\ja v a \p ro je k ty > _
Rysunek 2.5. Próba przekroczenia dopuszczalnej wartości zmiennej
Niestety, kom pilator nie zawsze będzie w stanie wykryć tego typu błąd. Może się bowiem zdarzyć, że zakres przekroczym y w trakcie wykonywania programu. Co wtedy? Ć W I C Z E N I E_________________________________________________________________________________________
Przekroczenie zakresu w trakcie działania kodu Zadeklaruj zm ienną typu int. W ykonaj operacje arytmetyczne prze kraczające dopuszczalną wartość takiej zmiennej. Wynik w yśw ietl na ekranie. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { i n t wa r t o s c = 2 1 4 7 4 8 3 6 4 7 ; i n t war t os c 1 = wa r t o s c + 1; i n t wa r t os c 2 = wa r t o s c + wa r t o s c ; S y s t e m . o u t . p r i n t l n ( " wa r t o s c = " + w a r t o s c ) ; System.out.println System.out.println
( " wa r t o s c + 1 = " + w a r t o s c 1 ) ; ( " wa r t o s c + wa r t o s c = " + w a r t o s c 2 ) ;
} }
Zmiennej wartosc została maksymalna wartość dla typu int (2147483647). Następnie została do niej dodana wartość 1, a wynik trafił do zmiennej pom ocniczej wartosc1. W kolejnym kroku zm iennej wartosc2 przypi-
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
39
sano wynik dodawania wartosc + wartosc, a na końcu wartości wszyst kich zm iennych zostały wyświetlone na ekranie. Jaki zatem będzie w ynik? Otóż po dodaniu 1 do w artości 2147483647 otrzym aliśm y -2147483648, natom iast w wyniku dodawania 2147483647 + 2147483647 powstała wartość -2 (rysunek 2.6). Dlaczego? Otóż, jeżeli jakaś wartość przekracza dopuszczalny zakres swojego typu, jest „zaw ijana” do początku tego zakresu. Obrazowo zilustrowano to na rysunku 2.7. Rysunek 2.6. Wyniki operacji przekraczających dopuszczalne wartości
C:\Windows\system32
:\ja v a \p r o ;je k t y > ja v a M ain a rto sc = 2147483647 a r to sc + 1 = -2 1 4 7 4 8 3 6 4 8 a rto sc + w a rto sc = -2 :\ja v a \p r o je k ty > _
Rysunek 2.7. Przekroczenie dopuszczalnego zakresu dla typu int
Operatory bitowe O peracje te, jak sam a nazw a wskazuje, są dokonywane n a bitach. Przypom nijmy zatem podstawowe w iadom ości o system ach liczbo wych. W system ie dziesiętnym, z którego korzystamy n a co dzień, wykorzystywanych jest dziesięć cyfr — od 0 do 9, w system ie szes nastkowym dodatkowo litery od A do F, a w systemie ósemkowym cyfry od 0 do 7. W system ie dwójkowym będą zatem wykorzystywa ne jedynie dwie cyfry — 0 i 1. Kolejne liczby budowane są z odpo w iednich sym boli dokładnie tak samo jak w system ie dziesiętnym; zostało to przedstawione w tabeli 2.3. W idać wyraźnie, że np. 4 dzie siętnie to 100 dwójkowo, a 10 dziesiętnie to 1010 dwójkowo.
40
Java
• Ćwiczenia praktyczne
Tabela 2.3. Reprezentacja liczb w różnych systemach liczbowych
system dwójkowy
system ósemkowy
system dziesiętny
system szesnastkowy
0
0
0
0
1
1
1
1
10
2
2
2
11
3
3
3
100
4
4
4
101
5
5
5
110
6
6
6
111
7
7
7
1000
10
8
8
1001
11
9
9
1010
12
10
A
1011
13
11
B
1100
14
12
C
1101
15
13
D
1110
16
14
E
1111
17
15
F
Na liczbach możemy dokonywać znanych ze szkoły operacji bitowych AND (iloczyn bitowy), OR (suma bitowa), NOT (negacja bitowa) oraz XOR (bitowa alternatyw a w ykluczająca). Sym bolem operatora AND jest znak & (ampersand), operatora OR znak | (pionowa kreska), operatora NOT znak ~ (tylda), natom iast operatora XOR znak ^ (daszek, kareta). Oprócz tego m ożna również w ykonyw ać operacje przesunięć bitów. Zestawienie występujących w Javie operatorów bitowych zostało przed stawione w tabeli 2.4.
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
41
Tabela 2.4. Operatory bitowe w Javie
Operator
Symbol
AND
&
OR
|
NOT
~
XOR
/\
Przesunięcie bitowe w prawo
>>
Przesunięcie bitowe w lewo
>>
Operatory logiczne Argumentami operacji logicznych m uszą być wyrażenia posiadające wartość logiczną, czyli true lub false (prawda i fałsz). Przykładowo wyrażenie 10 < 20 jest niewątpliwie prawdziwe (10 jest mniejsze od 20), zatem jego wartość logiczna jest równa true. W grupie tej wyróżniam y trzy operatory: □ logiczne AND (&&), □ logiczne OR ( ||), □ logiczne NOT, negacja (!). Warto zauważyć, że w części przypadków stosowania operacji logicz nych aby otrzymać wynik, wystarczy obliczyć tylko pierwszy argument7. Wynika to oczywiście z właściw ości operatorów. Jeśli wynikiem obli czenia pierwszego argumentu jest bowiem wartość true, a wykonujemy operację OR, to niezależnie od stanu drugiego argumentu w artością całego wyrażenia będzie true. Podobnie przy stosowaniu operatora AND— jeżeli wartością pierwszego argumentu będzie false, to wartością całego w yrażenia również będzie false.
7 Ściślej rzecz ujmując, może wystarczyć znajomość wartości jednego dowolnego argumentu.
42
Java
• Ćwiczenia praktyczne
Operatory przypisania O peracje przypisania są dwuargumentowe i powodują przypisanie w artości argumentu znajdującego się z prawej strony do argumentu znajdującego się z lewej strony. Najprostszym operatorem tego typu jest oczyw iście klasyczny znak rów ności. Zapis liczba = 5 oznacza, że zm iennej liczba chcem y przypisać w artość 5. Oprócz tego mamy jeszcze do dyspozycji operatory łączące klasyczne przypisanie z in nym operatorem arytm etycznym bądź bitowym. Zostały one zebrane w tabeli 2.5. Tabela 2.5. Operatory przypisania i ich znaczenie w Javie
Argument 1
Operator
Argument 2
Znaczenie
x
=
y
x = y
x
+=
y
x = x + y
x
-=
y
x = x - y
x
*=
y
x = x * y
x
/=
y
x = x / y
x
%=
y
x = x %y
x
> y
x
>>>=
y
x = x >>> y
x
&=
y
x = x &y
|=
y
x = x I y
y
x = x Ay
x x
/ \_
Operatory porównania (relacyjne) Operatory porównania, czyli relacyjne, służą oczyw iście do porów nywania argumentów. W ynikiem takiego porównania jest wartość logiczna true (jeśli jest ono prawdziwe) lub false (jeśli jest fałszywe). Zatem wynikiem operacji argumentl == argument2 będzie true, jeżeli ar gum enty są sobie równe, lub false, jeż eli argumenty są różne. Czyli
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
43
4 == 5 ma w artość false, a 2 == 2 ma w artość true. Do dyspozycji mamy operatory porównania zawarte w tabeli 2.6. Tabela 2.6. Operatory porównania wJavie
Operator
Opis Jeśli argumenty są sobie równe, wynikiem jest true, w przeciwnym razie wynikiem jest false. Jeśli argumenty są różne, wynikiem jest true, w przeciwnym razie wynikiem jest false.
>
Jeśli argument prawostronny jest mniejszy od lewostronnego, wynikiem jest true, w przeciwnym razie wynikiem jest false.
=
Jeśli argument prawostronny jest mniejszy od lewostronnego lub równy lewostronnemu, wynikiem jest true, w przeciwnym razie wynikiem jest false.
>>
8 Tabela nie zawiera wszystkich operatorów występujących w Javie, a jedynie te omawiane w książce.
R ozd ział 2. • Z m ienn e, o p e ra to ry i in stru k c je
45
Tabela 2.7. Priorytety operatorów w Javie — ciąg dalszy
Grupa operatorów
Symbole
porównania
, =
porównania
==, ! =
bitowe AND
&
bitowe XOR
/\
bitowe OR
|
logiczne AND
&&
logiczne OR
||
warunkowe
?
przypisania
=, +=, -=, *=, /=, %=, >>=, >=, &=, ^=, |=
Instrukcje Instrukcja warunkowa if...else Bardzo często w programie niezbędne jest sprawdzenie jakiegoś wa runku i w zależności od tego, czy jest on prawdziwy, czy fałszywy, wykonanie różnych instrukcji. Do takiego sprawdzania służy w łaśnie instrukcja warunkowa if.e ls e . Ma ona ogólną postać: if
(w y ra ż en ie w arunkowe){ / / instrukcje d o wykonania, je ż eli warunek je s t prawdziwy
} el s e{
/ / instrukcje d o wykonania, je ż eli warunek je s t fałszyw y }
Trzeba przy tym zaznaczyć, że wyrażenie warunkowe, inaczej niż w C i C + + , m usi dać w wyniku wartość typu boolean, tzn. true lub false.
46
Java
• Ćwiczenia praktyczne
Ć W I C Z E N I E ________________________________________________________________________________________
2.15
Użycie instrukcji warunkowej if...else
Wykorzystaj instrukcję warunkową if .e ls e do stwierdzenia, czy war tość zm iennej typu całkowitego jest m niejsza od zera. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { int a = -10; i f (a > 0 ){ S y s t e m . o u t . p r i n t l n ("Zmi enna a j e s t wi ę ks za od z e r a . " ) ; } el s e{ System.out.println
("Zmi enna a n i e j e s t wi ę ks za od z e r a . " ) ;
} } }
W instrukcji warunkowej i f jest badany w arunek a > 0, czyli czy wartość zm iennej a jest w iększa od 0. Jeśli warunek jest prawdziwy (wartość zm iennej a jest w iększa od 0), wykonywana je st instrukcja z bloku if, w przeciwnym razie (wartość zm iennej a nie jest większa od 0) wykonywana jest instrukcja z bloku else. Ponieważ początkowo do a została przypisana wartość -10, po skompilowaniu i uruchom ie niu przykładu na ekranie pojaw i się napis Zmienna a nie je s t większa od zera. Zm iana w artości a na w iększą od 0 i ponow na kom pilacja oraz uruchom ienie spowoduje w yświetlenie drugiego napisu.
Spróbujm y teraz czegoś nieco bardziej skom plikowanego. Z ajm ij m y się klasycznym przykładem liczen ia pierw iastków rów nania kwadratowego. Przypom nijm y, że je śli m am y rów nanie o postaci A x 2 + Bx + C = 0 , to aby obliczyć jego rozw iązanie, liczym y tzw. del
tę (A), która jest równa B 2 - 4 AC . Jeżeli delta jest większa od zera, mamy dwa pierwiastki: Xi =
B + 'N ^A i x2 = — —
2A
2A
. Jeżeli delta jest -B
równa zeru, istnieje tylko jedno rozwiązanie — m ianow icie x = — . 2A W przypadku trzecim , jeżeli delta jest m niejsza od zera, równanie takie nie ma rozwiązań w zbiorze liczb rzeczywistych.
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
47
Skoro jest tutaj tyle warunków do sprawdzenia, jest to doskonały przykład do potrenowania zastosowania instrukcji if .e ls e . Aby nie kom plikować zagadnienia, nie będziem y się w tej chw ili zajmować wczytywaniem parametrów równania z klawiatury (ten tem at zosta nie omówiony w rozdziale 6.), ale podamy je bezpośrednio w kodzie. Ć W I C Z E N I E ________________________________________________________________________________________
2.16
Pierwiastki równania kwadratowego
Wykorzystaj operacje arytm etyczne oraz instrukcję if .e ls e do obli czenia pierwiastków równania kwadratowego o parametrach podanych bezpośrednio w kodzie programu. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { i n t parametrA = 1, parametrB = - 1 , parametrC = - 6 ; System.out.println System.out.println
if
( " Pa r a me t r y r ó w n a n i a : " ) ; ("A: " + parametrA + " B: " C: " + par ame t r C) ;
" + parametrB +
(parametrA == 0 ) { System.out.println
(" To n i e j e s t równani e kwadratowe: A = 0 ! " ) ;
} el s e{ doubl e d e l t a = parametrB * parametrB - 4 * parametrA * parametrC; i f (delta < 0){ System.out.println ("Delta < 0 . " ) ; System.out.println ( " Br a k rozwi ązań w z b i o r z e l i c z b r z e c z y w i s t y c h " ) ; } el s e{ doubl e wynik; i f ( d e l t a == 0 ) { wynik = - parametrB / (2 * par ame t r A) ; S y s t e m . o u t . p r i n t l n ( " Ro z wi ą z a n i e : x = " + wyni k) ; } else{ wynik = ( - paramet rB + M a t h . s q r t ( d e l t a ) ) / (2 * par ame t r A) ; S y s t e m . o u t . p r i n t ( " Ro z wi ą z a n i e : x1 = " + wyni k) ; wynik = ( - paramet rB - M a t h . s q r t ( d e l t a ) ) / (2 * par ame t r A) ; S y s t e m . o u t . p r i n t l n ( " , x2 = " + wyni k) ; } }
} }
}
48
Java
• Ćwiczenia praktyczne
Na początku kodu definiowane są zm ienne określające parametry równania, a ich w artości w yświetlane są na ekranie. Następnie za pom ocą pierwszej instrukcji warunkowej i f badany jest stan parame tru A, czyli zm iennej parametrA. Po stwierdzeniu, że parametr A jest rów ny 0, w yśw ietlana jest inform acja, iż nie m am y do czynienia z równaniem kwadratowym. W przeciwnym razie wykonywane są in strukcje z bloku else. Jest w nim obliczana delta, a następnie wyko nywana kolejna instrukcja i f badająca, czy delta jest m niejsza od 0. Jeśli jest m niejsza, w yświetlana jest inform acja o braku rozwiązań. W przeciw nym razie (delta większa od 0 bądź równa 0) w bloku else wykonywana jest kolejna instrukcja warunkowa badająca tym razem, czy delta jest równa 0. Gdy jest równa 0, jedyny w ynik jest obliczany i wyświetlany na ekranie. W przeciwnym razie (delta w iększa od 0) obliczane są dwa pierwiastki, a w ynik obliczeń w yśw ietlany jest na ekranie.
Jak łatwo zauważyć, instrukcję warunkową m ożna zagnieżdżać, tzn. po jednym i f może występować kolejne, po nim następne itd. Jednak jeżeli zapiszem y to w sposób podany w poprzednim ćw iczeniu, przy wielu zagnieżdżeniach otrzymamy bardzo nieczytelny kod. Aby temu zapobiec, można posłużyć się złożoną in stru k cją w arunkow ą if.e ls e if. Zam iast tworzyć m niej wygodną konstrukcję, taką jak przedsta w iona poniżej: if
(w aru n ek1){ / / instrukcje 1
} else{ i f (w aru n ek2){
/ / instrukcje 2 } else{ i f (w aru n ek3){
/ / instrukcje 3 } else{
/ / instrukcje 4 }
} }
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
49
całość m ożna zapisać dużo prościej i czytelniej w postaci: if
{w oru n ekl ) { / / instrukcje 1
i el s e i f
{w oru n ek2){ / / instrukcje 2
i el s e i f
{w oru n ek3){ / / instrukcje 3
i el s e{
/ / instrukcje 4 i Ć W I C Z E N I E ________________________________________________________________________________________
2.17
Zastosow anie instrukcji if...else if
Napisz kod obliczający pierw iastki rów nania kwadratowego o para m etrach zadanych w programie. Wykorzystaj instrukcję if .e ls e if. c l a s s Main { p u b l i c s t a t i c voi d main { S t r i n g a r g s [ ] ) i n t parametrA = l ,
parametrB = - l ,
{
parametrC = - 6 ;
S y s t e m . o u t . p r i n t l n { " Pa r a me t r y r ó w n a n i a : " ) ; S y s t e m . o u t . p r i nt l n {"A: " + parametrA + " B: " + parametrB + " C: " + par ame t r C) ; if
{parametrA == 0 ) { System.out.println
{" To n i e j e s t równani e kwadratowe: A = 0 ! " ) ;
i el s e{ doubl e d e l t a = parametrB * parametrB - 4 * parametrA * parametrC; doubl e wyni k; if
{delta < 0){ System.out.println {"Delta < 0 . " ) ; System.out.println { " Br a k rozwi ązań w z b i o r z e l i c z b r z e c z y w i s t y c h " ) ;
i el s e i f {del t a == 0) { wynik = - paramet rB / System.out.println
{Z * par amet r A) ;
{ " Ro z wi ą z a n i e : x = " + wyni k) ;
i el s e{ wynik = { - paramet rB + M a t h . s q r t { d e l t a ) ) / {Z * paramet rA) ; S y s t e m . o u t . p r i n t { " Ro z wi ą z a n i e : x l = " + wyni k) ;
50
Java
• Ćwiczenia praktyczne
wynik = ( - paramet rB - M a t h . s q r t ( d e l t a ) ) System.out.println
/
(2 * paramet rA) ;
( " , x2 = " + wyni k) ;
} } } }
Instrukcja wyboru switch Instrukcja switch pozwala w wygodny i przejrzysty sposób spraw dzać wiele warunków i wykonywać różny kod w zależności od tego, czy warunki są prawdziwe, czy fałszywe. Można n ią zastąpić ciąg instrukcji if .e ls e i f . Jeżeli w kodzie mamy przykładową konstrukcję o schem atycznej postaci: if
(w y rażen ie == X ) { / / instrukcja 1 ;
} else i f
(w y rażen ie == Y){ / / instrukcja2;
} else i f
(w y rażen ie == Z ) { / / instrukcja3 ;
} el s e{
/ / instrukcja4 ; }
to m ożem y ją zastąpić następująco: s wi t c h (w y ra ż e n ie){ case X:
/ / in stru kcjal ; break; c a s e Y:
/ / instrukcja2; break; case Z :
/ / instrukcja3 ; break; def aul t :
/ / instrukcja4;
}
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
51
Po kolei jest tu sprawdzane, czy wyrażenie przyjmuje jedną z w artości X, Y i Z. Jeżeli równość zostanie w jednym z przypadków stwierdzona, wykonywane są instrukcje po odpowiedniej klauzuli case. Jeżeli rów ność nie zostanie stwierdzona, wykonywane są instru kcje po słowie default. Instrukcja break powoduje w yjście z bloku switch. W yraże niem jest z reguły po prostu nazwa zm iennej. W w ersjach Javy do 7 (1.7) wartościami wyrażenia (symbole X, Y, Z w kodzie) mogą być tylko w artości typu char, byte, short i int, natom iast w Javie 7 dodatkowo uwzględniany jest też typ String (czyli ciąg znaków). Ć W I C Z E N I E ________________________________________________________________________________________
2.18
Użycie instrukcji wyboru sw itch
Używając instrukcji switch, napisz program sprawdzający, czy w ar tość zadeklarowanej zm iennej jest rów na 1, czy 10. W yśw ietl n a ek ranie stosowny komunikat. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) i n t a = 10; s wi t c h ( a ) {
{
case 1 : S y s t e m . o u t . p r i n t l n ( " a j e s t równe 1 . " ) ; b r e a k; c a s e 10: S y s t e m . o u t . p r i n t l n ( " a j e s t równe 1 0 . " ) ; b r e a k; default: S y s t e m . o u t . p r i n t l n ( " a n i e j e s t równe ani
1, ani
10.");
} } }
Jeżeli w którymś z przypadków case zapomnimy o słowie (instrukcji) break, wykonywanie instrukcji switch będzie kontynuowane aż do osiągnięcia pierwszej instrukcji break lub wykonania całego bloku switch. To może prowadzić do otrzymania niespodziewanych efektów. W szczególności zostanie wtedy wykonany blok instrukcji występujący po default. Może to być oczywiście efekt zamierzony, może też jednak powodować trudne do wykrycia błędy.
52
Java
• Ćwiczenia praktyczne
Ć W I C Z E N I E ________________________________________________________________________________________
2.19
Efekt pominięcia instrukcji break
Zmodyfikuj kod z ćw iczenia 2.18, usuw ając in stru kcję break. Zaob serwuj, jak zm ieniło się działanie programu. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { i n t a = 10; s wi t c h ( a ) { c a s e 1: S y s t e m . o u t . p r i n t l n ( " a j e s t równe 1 . " ) ; c a s e 10: S y s t e m . o u t . p r i n t l n ( " a j e s t równe 1 0 . " ) ; default: S y s t e m . o u t . p r i n t l n ( " a n i e j e s t równe ani
1, ani
10.");
} } }
Po skom pilow aniu i uruchom ieniu kodu zobaczym y widok zapre zentow any na rysunku 2.8. Jak w idać, według naszego programu zm ienna a jest jednocześnie równa 10, jak i różna od 10. Bezpośred nim tego powodem jest oczyw iście usunięcie słowa break. Instrukcja switch działa teraz w ten sposób, że najpierw wartość a jest porówny wana z wartością 1 (pierwszy przypadek case). Równość nie jest stwier dzona, zatem następuje przejście do kolejnego bloku case (case 10). W tym bloku równość zostanie stwierdzona (a = 10), jest więc wyko nywana instrukcja z tego bloku (System.out.println("a = 10");). Po nieważ jednak w bloku case 10 nie ma instrukcji break, zostaną wyko nane wszystkie instrukcje z innych bloków case znajdujących się za case 10 (o ile takie występują) oraz z bloku defaul t, chyba że w którymś z nich w ystąpi jednak instrukcja break. W tym przypadku za case 10 mamy tylko blok default i żadnej instrukcji break, zatem zostanie wy konana instrukcja System .out.println("a nie je s t równe ani 1, ani 1 0 .");. Warto sam odzielnie sprawdzić, co się stanie w programie po przypisaniu zmiennej a na samym początku wartości 1 (zamiast 10). Rysunek 2.8. Ilustracja błędu z ćwiczenia 2.19
C\W¡nüüWi\iyilein32 C: \ j a v a \ p r o je k t y > ja v a c
M a in .ja v a
[':\ j a v a \ p r o j e k f y v j a v a M a i n a je s l r ó w n e 10. a n i e j o s t r ó w n o a n i 1, a n i
c :\ja v a \p ro jc k ty > _
10.
R o z d z i a ł 2. • Z m i e n n e , o p e r a t o r y i i n s t r u k c j e
53
Pętla for Pętle w językach programowania pozw alają n a wykonywanie powta rzających się czynności. Nie inaczej jest w Javie. Jeśli chcem y np. wypisać na ekranie 10 razy napis Java, m ożem y zrobić to, pisząc 10 razy System .out.println("Java");. Jeżeli jednak chcielibyśm y m ieć już 150 takich napisów, to, pom ijając oczyw iście kwestię sensow ności tej czynności, byłby to już problem. Na szczęście z pom ocą przycho dzą nam w łaśnie pętle. Pętla typu for m a następującą składnię: f o r (w y rażen ie p o c z ą tk o w e ; w y ra ż en ie warunkowe; w y ra ż en ie m o d y fik u ją c e ) {
/ / instrukcje d o wykonania }
wyrażenie początkowe jest stosowane do zainicjalizow ania zmiennej używanej jako liczn ik liczby w ykonań pętli, wyrażenie warunkowe określa warunek, który m usi być spełniony, aby dokonać kolejnego przejścia w pętli, wyrażenie modyfikujące jest zwykle używane do m o dyfikacji zm iennej będącej licznikiem . Ć W I C Z E N I E ________________________________________________________________________________________
2.20
Budowa pętli for
Wykorzystując pętlę typu for, napisz program wyświetlający n a ekra nie 10 razy napis Java. c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) f o r ( i n t i = 1; i 4 ) { throw new Ar r ayI nde xOut Of Bounds Exc e pt i o n( " I nde ks poza z a k r e s e m . " ) ; } return tab[index]; } }
Do sprawdzenia, czy został przekroczony indeks tablicy, wykorzysta no zwykłą instrukcję warunkową if. Jeśli przekroczenie nastąpiło, two rzony jest nowy obiekt typu ArraylndexOutOfBoundsException, który jest zgłaszany m aszynie w irtualnej (żargonowo stosuje się też term in wy rzucanie wyjątku) za pom ocą instrukcji throw. Konstruktorowi klasy ArrayIndexOutOfBoundsException został przekazany ciąg znaków w postaci argumentu, zawierający dodatkowy opis w yjątku (nie jest to jednak obligatoryjne, konstruktor może być również bezargumentowy). Jeśli teraz użyjemy takiej klasy Tablica wraz z klasą Main z ćwiczenia 5.5, zobaczymy, że na ekranie pojaw i się kom unikat: Indeks poza zakresem. Zaprezentowano to na rysunku 5.3. C:\Windows\systemJ2\cmd.e r : \ j a v a \ p r o j e lc ty >j nv nr M a i n , j a v a C :\ j a v d X p r o j e k t y > j a v a H a in E x c e p t i o n i n t h r e a d "m a in ** j a v a . l a n g . A r r a y l n d e x O u t O f B o u n d s E x c e p t i o n : ek s poza zak resem . at Iahl i ra.get I-lement ( Iabl ira .java :10) at M.ain. ma in (Main, java: 4)
In —
Rysunek 5.3. Wyjątek z komunikatem wjęzyku polskim
Hierarchia wyjątków W yjątki w Javie są obiektam i. O znacza to, że kiedy w yjątek ma być wygenerowany, tworzony jest obiekt danego typu. W prezentowanych przykładach ten typ to ArrayIndexOutOfBoundsException. Dostęp do takiego
120
Java
• Ćwiczenia praktyczne
obiektu mamy poprzez zadeklarowaną przez nas zm ienną odnośni kową e. Nie będziem y się tym bliżej zajm ować, m usim y tylko pa m iętać, że jednym z efektów takiego stanu rzeczy je st hierarchia wyjątków. Nie je st zatem obojętne, w jakiej k olejn ości będziem y je przechw ytyw ać. Ogólna zasada jest następująca: najpierw przechwytywane są wyjątki bardziej ogólne, a później bardziej szczegółowe. Wyjątki znajdujące się na tym samym poziom ie hierarchii m ogą być natom iast przechw y tywane w dowolnej kolejności. Ć W I C Z E N I E ________________________________________________________________________________________
5.9
Kolejność przechwytywania wyjątków
Zmodyfikuj klasę Main z ćw iczenia 5.7 w taki sposób, aby najpierw przechwytywany był w yjątek ogólny Exception, a następnie wyjątek szczegółowy ArraylndexOutOfBoundsException. Spróbuj dokonać kom pilacji otrzymanego kodu. p u b l i c c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) T a b l i c a t a b = new T a b l i c a ( ) ; i n t v a l u e = 0;
{
try{ v a l ue = t a b . g e t E l e m e n t ( 2 0 ) ; } c a t c h ( E x c e p t i on e ) { System.out.println("Jakiś błąd!"); S y s t e m. e x i t ( - 1 ) ; } c a t c h( Ar r ayl ndexOut Of Bo unds Exc ept i on e ) { S y s t e m . o u t . p r i n t l n ( " N i e ma el ement u o numerze 2 0 ! " ) ; S y s t e m. e x i t ( - 1 ) ; } S y s t e m . o u t . p r i n t l n ( " E l e m e n t nr 20 t o :
" + value);
} }
Jak widać na rysunku 5.4, kom pilacja się nie udała. Ponieważ klasa wyjątku ArraylndexOutOfBoundsException dziedziczy pośrednio po klasie Exception, instrukcja: c a t c h( Ar r ayl nde xOut Of Bo unds Exc e pt i on e)
nie zostałaby nigdy osiągnięta, w przypadku wygenerowania w yjąt ku ArrayIndexOutOfBoundsException zostałby on bowiem przechwycony w cześniej przez instrukcję catch (Exception e).
R o z d z i a ł 5. • O b s ł u g a b ł ę d ó w o r a z w y j ą t k i
121
Rysunek 5.4. Błąd polegający na nieuwzględnieniu hierarchii wyjątków
Aby zatem poprawić kod ćw iczenia tak, aby m ożna było dokonać kom pilacji, a program działał poprawnie, należy zam ienić m iejscam i bloki catch. Wówczas najpierw będzie przechwytywany wyjątek Array ^IndexOutOfBoundsException (szczegółowy), a dopiero za nim w yjątek Exception (ogólny).
6 Operacje wejścia-wyjścia ■ w
■
■ w
■
„Prawdziwa” aplikacja nie obejdzie się bez operacji w ejścia-w yjścia. Dzięki nim możemy np. wprowadzać dane z klawiatury czy dokony wać operacji na plikach. W Javie operacje tego typu opierają się na strum ieniach, czyli obiektach, z których m ożem y odczytywać dane (strumienie wejściowe) lub je do nich zapisywać (strumienie wyjścio we). Standardowo zdefiniowane są trzy strumienie: System.in, System.out i System.err1. System.in to strum ień wejściow y, System.out — w yj ściowy, natomiast System.err to strumień związany z obsługą błędów. Ze strum ienia System.out już korzystaliśm y we w cześniejszych roz działach. Używaliśmy wtedy jednej z jego metod — println — do wy świetlenia lin ii znaków na ekranie.
1 Ściślej rzecz ujmując, chodzi o statyczne obiekty: in (typu InputStream), out (typu PrintStream) i err (typu PrintStream) zdefiniowane w finalnej klasie System.
123
124
Java
• Ćwiczenia praktyczne
Wyświetlanie danych na ekranie Sposób wyświetlania danych na ekranie konsoli został przedstawiony na samym początku książki. Teraz już wiadomo, że to, co było na zywane instrukcją System.out.println, to w rzeczyw istości wywołanie m etody println obiektu in zawartego w klasie System. Typem tego obiektu jest PrintStream (czyli jest to obiekt klasy PrintStream lub — jeszcze dokładniej — obiekt ten jest in stan cją klasy PrintStream), a zatem m ożliwe jest korzystanie z metod klasy PrintStream. Zamiast println m ożna w ięc stosować również metodę print. Działa prawie tak samo, z tą różnicą że nie dodaje do wyświetlanego ciągu znaku koń ca wiersza. To oznacza, że za pom ocą print m ożna wyśw ietlić różne dane w jednym wierszu. Ć W I C Z E N I E ________________________________________________________________________________________
6.1
W yświetlanie danych w jednym wierszu
Zadeklaruj i zainicjuj kilka zm iennych różnych typów. W yśw ietl ich zawartość na ekranie w jednym wierszu. W artość każdej zmiennej wyśw ietlaj za pom ocą osobnej instrukcji programu. p u b l i c c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) i n t l i c z b a 1 = 152; c h a r znak = ' b ' ; doubl e l i c z b a 2 = 2 . 5 4 ;
{
System.out.print(liczba1); System.out.print(" "); System.out.print(znak); System.out.print(" "); System.out.print(liczba2); } }
Zadeklarowano trzy zm ienne: liczba1, znak i liczba2. Pierwsza jest typu int, druga — char, a trzecia — double. N astępnie każda z nich została w yśw ietlona za pom ocą metody print, a pomiędzy zmienne zostały wprowadzone znaki spacji. Ponieważ metoda print nie do daje do danych znaku końca wiersza, wszystkie w artości pojaw ią się w jednej linii.
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
125
Trzeba zauważyć, że w powyższym ćw iczeniu metoda print otrzy mywała w artości różnych typów. W iadom ości podane w rozdziale 4. pozw alają się domyślić, że w takim razie zapewne mamy do czynie nia z przeciążonymi wersjami tej metody. Tak jest w istocie. W klasie PrintStream zdefiniowane są wersje metod print i println dla każdego z typów podstawowych, a także dla typu Object2 oraz dla tablicy zna ków. To z kolei oznacza, że argum entem m oże być zm ienna dowol nego typu. Jeśli pomiędzy w artości przekazywane metodom print i println b ę dziemy wstawiać łańcuchy znakowe (ciągi znaków ujęte w cudzy słów), będziemy mogli je dowolnie łączyć, uzyskując tym samym jeden wiersz tekstu z różnym i wartościam i. Ć W I C Z E N I E ________________________________________________________________________________________
6.2
Konwersja danych na ciągi znaków
Kod z ćw iczenia 6.1 zm ień tak, aby efekt działania był taki sam, ale by w yśw ietlanie w szystkich danych odbywało się tylko w jednym wywołaniu metody print. p u b l i c c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) i n t l i c z b a 1 = 152; c h a r znak = ' b ' ;
{
doubl e l i c z b a 2 = 2 . 5 4 ; S y s t e m . o u t . p r i n t ( l i c z b a 1 + " " + znak + " " + l i c z b a 2 ) ; } }
Należy jednak podkreślić, że konstrukcja przedstaw iona w ćw icze niu 6.2 zadziała zgodnie z założeniami tylko wtedy, gdy między licz bami znajdują się ciągi znaków. Dzieje się tak dlatego, że gdy kom pilator napotka operację dodawania liczby i łańcucha znakowego, nie może wykonać operacji dodawania arytm etycznego. Dlatego naj pierw zam ienia liczbę w ciąg znaków, a następnie łączy łańcuchy znakowe. To oznacza, że operacja typu "abc" + 123 jest zamieniana na "abc" + "123" i dlatego ostatecznym w ynikiem jest "abc123". Nato 2 W przypadku użycia zmiennej referencyjnej jako argumentu metody print lub println w miejsce argumentu zostanie podstawiony ciąg znaków zwrócony za pomocą metody toString obiektu wskazywanego przez tę zmienną.
126
Java
• Ćwiczenia praktyczne
m iast w przypadku dodawania dowolnych typów liczbow ych wyko nywane jest zwykłe dodawanie arytmetyczne. Zostało to zilustrowane w ćw iczeniu 6.3. Ć W I C Z E N I E ________________________________________________________________________________________
6.3
Liczby i łańcuchy znakowe
W yśw ietl na ekranie wynik dodawania dwóch w artości w trzech w ersjach: gdy obie są w postaci liczbow ej, gdy obie są przedstawione jako łańcuchy znakowe i gdy jedna jest łańcuchem , a druga liczbą. p u b l i c c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) System.out.println(123 + 321); System.out.println("123" + "321"); System .o ut. pri ntl n( "12 3" + 321);
{
} }
W efekcie pow staną trzy ciągi znaków: 444, 123321 i 123321 (rysunek 6.1). W pierwszym przypadku będziem y m ieli do czynienia z doda waniem arytmetycznym, w drugim — łączeniem (konkatenacją) ciągów znaków, a w trzecim — konwersją liczby (321) na łań cu ch i łączeniem łańcuchów . Rysunek 6.1. Wynik działania operatora dodawania na różnych typach danych
[':\ j a v a \ p r o j e l c r y > j a v a r M a i n , j a v a C :\ i a v a \ p r o j e k t y > j a v a M a i n 444 123321 123321
C :\ j a v a \ p r o j e k l y > _
Wczytywanie danych z klawiatury Spróbujm y użyć strum ienia w ejściow ego do w czytyw ania danych z klawiatury. W JDK starszych niż 1.5 nie jest to niestety tak proste jak w przypadku wyprowadzania danych na ekran. W celu prawidłowego wczytania w iersza tekstu będziem y m usieli posłużyć się aż trzema
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
127
klasam i: InputStream, której in stan cją je st obiekt System.in, oraz Input ^StreamReader i BufferedReader. System.in jest strum ieniem bajtowym, czyli pozwala na odczytywanie bajtów. Klasa InputStreamReader umoż liwia konwersję bajtów na znaki (czyli dzięki niej otrzymamy strumień znakowy), natom iast BufferedReader realizuje buforow anie danych. Ć W I C Z E N I E_________________________________________________________________________________________
E
M
Odczyt pojedynczego wiersza tekstu
Napisz program w czytujący z klaw iatury wiersz tekstu i w yśw ietla jący go z powrotem na ekranie. import j a v a . i o . * ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { I nput St r e amRe ader i np = new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ; B u f f e r e d R e a d e r i n b r = new B u f f e r e d R e a d e r ( i n p ) ; S y s t e m. o u t . p r i n t l n ( " Wp r o wa d ź wi e r s z t e k s t u : " ) ; try{ String line = inbr.readLine(); S y s t e m. o ut . pr i n t l n ( " Wpr o wa d z o n y wi e r s z t o : " ) ; System.out.printl n ( l i n e ) ; } catch(IOException e ) { System.out.println("Błąd odczytu.") ; } } }
Na początku im portujem y klasy z pakietu ja v a.io — to w nim znaj dują się potrzebne nam definicje. Tworzymy następnie dwa obiekty: inp klasy InputStreamReader oraz inbr klasy BufferedReader. W celu utwo rzenia obiektu inp powiązanego ze standardowym strumieniem w ej ściowym konstruktorowi klasy InputStreamReader przekazaliśmy obiekt System.in. Obiekt inp został natom iast wykorzystany jako parametr konstruktora klasy BufferedReader. Po wykonaniu tych czynności moż na korzystać z metody readLine obiektu inbr (klasy BufferedReader), która zwraca odczytany ze strum ienia wejściowego wiersz tekstu (czyli ciąg znaków zakończony znakiem końca lin ii). Ciąg jest zapi sywany w zmiennej pom ocniczej (line) typu String (czyli takiej, która może przechowywać ciągi znaków).
l i s
Java
• Ćwiczenia praktyczne
Instrukcja w yw ołująca m etodę ReadLine jest ujęta w blok try, dzięki którem u m ożemy obsłużyć sytuację, gdy przy próbie odczytu danych wystąpi błąd. Byłby to błąd typu IOException (błąd w ejścia-w yjścia), dlatego też przechwytywany jest wyjątek tego właśnie typu. Jeśli błąd wystąpi, zostanie wyświetlony stosowny komunikat.
Należy zauważyć, że użyta w ćw iczeniu 6.4 zm ienna inp jest wy łącznie zm ienną pom ocniczą. Dzięki niej kod jest bardziej czytelny. W praktyce często obiekt typu BufferedReader jest tworzony w jednej instrukcji, która w tym przypadku wyglądałaby następująco: B u f f e r e d R e a d e r i n b r = new B u f f e r e d R e a de r ( new I n p u t S t r e a mR e a d e r ( S y s t e m. i n)
W JDK istnieje również klasa DataInputStream zawierająca metodę readLine, która w założeniu ma wykonywać takie samo zadanie. Nie należy jednak stosować tej metody, gdyż może ona niepoprawnie konwertować bajty ze strumienia na znaki. ĆWICZENIE
_________ Wczytywanie danych w pętli Napisz program, który w pętli w czytuje kolejne wiersze tekstu i wy prowadza je na ekran. Program m a zakończyć działanie w m om encie wprowadzenia z klaw iatury ciągu znaków "quit". import j a v a . i o . * ; p u b l i c c l a s s Main j p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { String line = ""; Bu f f e r e d R e a d e r i n b r = new Bu f f e r e d R e a d e r ( new I n p u t S t r e a mR e a d e r ( S y s t e m. i n) ); S y s t e m . o u t . p r i n t l n ( " Wp r o w a d z a j wi e r s z e t e k s t u . " + "Aby zakońc z yć , wpisz q u i t . " ) ; try{ whi l e ( ! l i n e . e q u a l s ( " q u i t " ) ) { line = inbr.readline(); System .out.println(line); } } c atch(IOException e ) { S y s t e m . o u t . p r i n t l n ( " B ł ą d o d c z y t u . ") ;
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
129
} } }
Dane są wprowadzane za pom ocą metody readLine obiektu Buffered ^Reader powiązanego za pośrednictw em klasy InputStreamReader ze standardowym strumieniem wejściowym System. in. Odczyt odbywa się w pętli while, a uzyskany ciąg znaków (wiersz tekstu) jest zapisywany w zmiennej line. Pętla skończy się, gdy zmienna line będzie zawierała ciąg quit. Do porównywania bieżącej wartości line z quit jest używana metoda equals3 (nie należy porównywać ciągów znakowych za pomocą operatorów == i !=). Użyty warunek (korzystający z operatora negacji logicznej !) !line.equals("quit") jest prawdziwy, gdy line nie zawiera ciągu quit, a fałszywy, gdy line zawiera ciąg quit. Taki program będzie działał poprawnie, jeśli jednak strum ień w ej ściowy zostanie przerwany, np. przez w ciśnięcie kom binacji klawiszy C trl+ C (lub C trl+Z+E nter), wówczas wygeneruje wyjątek NullPointer ^Exception. Dzieje się tak dlatego, że przy przerwaniu strumienia m e toda readLine zam iast ciągu znaków zwraca w artość null (widać to na rysunku 6.2). W ram ach zadania do samodzielnego wykonania można się zastanowić, jak zapobiegać tego typu sytuacji. Jedno z rozw iązań zostanie przedstawione w ćw iczeniu 6.13.
3 Jest to możliwe, ponieważ zmienna line jest typu String, a klasa String (reprezentująca ciągi znaków) zawiera metodę equals.
Java
130
• Ćwiczenia praktyczne
Jak w spom niano w opisie ćw iczenia 6.5, do porównywania łań cu chów znakowych nie należy używać operatorów porównywania == i ! = (choć mogłoby się to wydawać oczywistą czynnością). Dlaczego nie należy stosować konstrukcji typu i f (line != "quit")? Otóż znana nam instrukcja warunkowa i f wraz z operatorem po równywania == lub != po prostu nie zadziała zgodnie z intuicyjnym i oczekiwaniami (można się o tym przekonać, wprowadzając ją do kodu z ćw iczenia 6.5). Dzieje się tak, gdyż w takich przypadkach po obu stronach operatora znajdują się referencje do obiektów przechow ują cych ciągi znaków (ciąg znaków umieszczony w cudzysłowie jest trak towany jako obiekt typu String). W arto podkreślić — są to referencje do obiektów, a nie same ciągi znaków! Pisząc zatem np.: if
(line
!= " q u i t " )
porównujemy dwie referencje, a nie dwa napisy. Dlatego w takich sytuacjach stosujem y do porównywania m etodę equals klasy String. Umiemy już wczytywać ciągi znaków, pozostaje nauczyć się, w jaki sposób wczytywać liczby. Można zrobić to, w czytując linię tekstu jak w poprzednich przykładach, a następnie konwertując tekst na liczbę. Zam iast jednak wykonywać taką konwersję ręcznie, lepiej skorzystać z w yspecjalizow anej klasy StreamTokenizer. D zieli ona strum ień w ej ściowy na tokeny, czyli jed n ostki leksykalne. Dla nas jednak najważ niejsze w tej chwili jest to, że można w ten sposób rozpoznawać liczby. Ć W I C Z E N I E ________________________________________________________________________________________
6.6
Wczytywanie wartości liczbowych
Napisz program wczytujący z klawiatury dwie liczby, a następnie wy świetlający je na ekranie. Skorzystaj z klasy StreamTokenizer. import j a v a . i o . * ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) doubl e a , b; Reader r = new Bu f f e r e d R e a d e r ( new I n p u t S t r e a mR e a d e r ( S y s t e m. i n)
{
); S t r e a mT o k e n i z e r inp = new S t r e a m T o k e n i z e r ( r ) ; try{ System.out.println("Podaj a : " ) ; inp.nextToken() ;
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
131
a = inp.nval; System.out.println("Podaj b : " ) ; inp.nextToken(); b = inp.nval; S y s t e m . o u t . p r i n t l n ( " P o d a n e wa r t o ś c i ", b = " + b);
to a = " + a +
} c a t c h ( I OE x c e pt i on e ) { Sy ste m .out.println("Błąd odczytu!" ) ; } } }
Najpierw powstał obiekt klasy BufferedReader powiązany ze standar dowym strum ieniem w ejściow ym (utworzono go w sposób analo giczny do przedstawionego w ćwiczeniu 6.5), a następnie został użyty jako argument konstruktora klasy StreamTokenizer. Tak powstał obiekt inp typu StreamTokenizer, również powiązany ze standardowym stru m ieniem wejściowym . Za pom ocą metody nextToken zostały pobrane dwie kolejne jednostki leksykalne. Ich w artości — o ile były to liczby — zostały odczytane z pola nval (zdefiniowanego w klasie StreamTokenizer) i zapisane w zm iennych a i b. Wartości tych zmiennych zostały następnie w spo sób standardowy wyświetlone na ekranie.
Testując program z ćw iczenia 6.6, m ożna zauważyć, że przy takim rozwiązaniu występują dwa problemy. Po pierwsze pole nval jest typu double, zatem otrzymujemy w wyniku liczbę zm iennoprzecinkową podwójnej precyzji. Co zrobić, jeśli potrzebujemy wartości całkowitej? Oczywiście rozwiązaniem jest tu konwersja typów. Jeśli zatem zmien ne a i b byłyby typu int, to należałoby dokonywać przypisań w na stępujący sposób: a = (int) b = (int)
inp.nval; inp.nval;
Drugi problem jest pow ażniejszy. Co się stanie, je śli przy wprowa dzaniu danych nie podamy liczby, ale dowolny inny ciąg znaków? Oczywiście program nie będzie działał poprawnie. Nie wystąpi wpraw dzie żaden błąd krytyczny, ale pole nval będzie zawierało wartość 0.0. Może to spowodować trudną do wykrycia usterkę w dalszej części
132
Java
• Ćwiczenia praktyczne
aplikacji. Ustrzeże nas przed tym sprawdzenie stanu statycznego4 pola TT_NUMBER klasy StreamTokenizer, które wskazuje, czy ostatnio pobrany token jest liczbą. Ć W I C Z E N I E_________________________________________________________________________________________
^ ^ j^ w y k r y w anie rodzaju wprowadzonej wartości Napisz program wczytujący z klawiatury dwie liczby całkow ite, a na stępnie wyświetlający je na ekranie. W przypadku gdy użytkownik nie poda liczby, aplikacja m a ponawiać prośbę o je j podanie. import j a v a . i o . * ; p u b l i c c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { i n t a , b; Reader r = new Bu f f e r edReader ( new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ) ; St r e a mT o k e n i z e r inp = new S t r e a m T o k e n i z e r ( r ) ; try{ System.out.println("Podaj lic zb ę a : " ) ; w h i l e ( i n p . n e x t T o k e n ( ) ! = StreamTokeni zer. TT_NUMBER){ S y s t e m . o u t . p r i n t l n ( " P o d a j poprawną l i c z b ę a : " ) ; } a = (int)
inp.nval;
System.out.println("Podaj lic zb ę b : " ) ; w h i l e ( i n p . n e x t T o k e n ( ) ! = StreamTokeni zer. TT_NUMBER){ S y s t e m . o u t . p r i n t l n ( " P o d a j poprawną l i c z b ę b : " ) ; } b = (int) inp.nval; S y s t e m . o u t . p r i n t l n ( " P o d a n e wa r t o ś c i
to: a = " + a +
", b = " + b); } c a t c h ( I OE x c e pt i on e ) { S y st em .o ut .p ri n tln("Błąd odczytu!" ) ; } } }
Obiekty klas BufferedReader oraz StreamTokenizer są tworzone analo gicznie do poprzedniego ćw iczenia. Główna różnica jest taka, że m e toda nextToken jest wywoływana w pętli typu while i dzieje się tak 4 Statyczność pola oznacza, że jest ono wspólne dla wszystkich instancji (wystąpień obiektów) danej klasy oraz że istnieje zawsze, i to niezależnie od tego, czy został utworzony jakiś obiekt tej klasy. To zagadnienie wykracza jednak poza ramy tematyczne książki.
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
133
dopóty, dopóki zwrócona przez nią wartość jest różna od w artości zapisanej w StreamTokenizer.TT_NUMBER. A zatem taka pętla zakończy się dopiero wtedy, gdy odczytana zostanie poprawna w artość liczbowa. W sytu acji gdy nie została wprowadzona prawidłowa w artość, na ekranie wyświetlany będzie odpowiedni komunikat. Jeśli wprowadzo ny ciąg jest liczbą, następuje zakończenie pętli, a w artość jest kon wertowana do typu int i przypisywana w łaściw ej zm iennej. ĆWICZENIE
________Wykorzystanie wczytywanych danych do obliczeń Napisz program obliczający pierw iastki rów nania kwadratowego. W czytaj param etry równania z klaw iatury. import j a v a . i o . * ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { doubl e parametrA = O, parametrB = O, parametrC = O; Reader r = new Bu f f e r edReader ( new I n p u t S t r e a m R e a d e r ( S y s t e m . i n ) ) ; S t r e a mT o k e n i z e r inp = new S t r e a m T o k e n i z e r ( r ) ; try{ S y s t e m . o u t . p r i n t ( " P o d a j par a me t r A: " ) ; w h i l e ( i n p . n e x t T o k e n ( ) ! = StreamTokeni zer. TT_NUMBER){ S y s t e m . o u t . p r i n t ( " P o d a j poprawny par ame t r A: " ) ; i parametrA = i n p . n v a l ; S y s t e m . o u t . p r i n t ( " P o d a j par a me t r B: " ) ; w h i l e ( i n p . n e x t T o k e n ( ) ! = StreamTokeni zer. TT_NUMBER){ S y s t e m . o u t . p r i n t ( " P o d a j poprawny par ame t r B: " ) ; i parametrB = i n p . n v a l ; S y s t e m . o u t . p r i n t ( " P o d a j par a me t r C: " ) ; w h i l e ( i n p . n e x t T o k e n ( ) ! = StreamTokeni zer. TT_NUMBER){ S y s t e m . o u t . p r i n t ( " P o d a j poprawny par ame t r C: " ) ; i parametrC = i n p . n v a l ; i c a t c h ( I OE x c e pt i on e ) { S y s t e m . o u t . p r i n t l n ( " B ł ą d odc zyt u! ") ; S y s t e m. e x i t ( - 1 ) ; i System.out.println("Parametry rów nania:");
Java
134
System.out.print("A:
• Ćwiczenia praktyczne " + parametrA + " , ",
if
(parametrA == 0 ) { System.out.println
B:
" + parametrB +
C: " + parametrC + " \ n " ) ;
(" To n i e j e s t równani e kwadratowe: A = 0 ! " ) ;
} el s e{ doubl e d e l t a = parametrB * parametrB - 4 * parametrA * parametrC; doubl e wyni k; if
(delta < 0){ System.out.println System.out.println
("Delta < 0 . " ) ; (" To równani e n i e ma r o z w i ą z a n i a "+ "w z b i o r z e l i c z b r z e c z y w i s t y c h . " ) ;
} el s e i f ( d e l t a == 0) { wynik = - par amet r B / (2 * par ame t r A) ; S y s t e m . o u t . p r i n t l n ( " Ro z wi ą z a n i e : x = " + wyni k) ; } el s e{ wynik = ( - pa r a me t r B + M a t h . s q r t ( d e l t a ) )
/
(2 * par ame t r A) ;
S y s t e m . o u t . p r i n t ( " Ro z wi ą z a n i e : x1 = " + wyni k) ; wynik = ( - pa r a me t r B - M a t h . s q r t ( d e l t a ) ) / (2 * par ame t r A) ; S y s t e m . o u t . p r i n t l n ( " , x2 = " + wyni k) ; } } } }
Postać kodu z tego ćw iczenia nie pow inna być żadnym zaskocze niem . O bliczenia są wykonywane analogicznie do przypadku z ćw i czenia 2.17 w rozdziale 2. Nowością jest wprowadzanie parametrów równania z klaw iatury (rysunek 6.3), podczas gdy poprzednio były one na stałe zapisane w kodzie. Odczyt danych z klawiatury odbywa się jednak na tej samej zasadzie jak w ćw iczeniu 6.7. Cała aplikacja jest w ięc swoistym połączeniem kon cepcji z przykładów 2.17 i 6.7. Rysunek 6.3. Obliczanie rozwiązań równania o parametrach wprowadzanych z klawiatury
^ C : \ ja v a \ p r o j e k t y > j a v a c M a i n . ja v a C : \ j a v a \ p r o j e k t y > i a v a M ain P o d a j p a ra m e tr A: je d e n P o d a i p o p ra w n y p a r a m e t r A : 1 P o d a j p a ra m e tr B : 1 P o d a j p a ra m e tr C : 2 P a r a m e t r y r ó w n a n ia : A: 1 . 0 , B : 1 . 0 C: - 2 .0 R o z w ią z a n ie : x l — 1 . 0 , x 2 — - 2 . 0 C : \ ja v a \ p r o j e k t y > _
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
135
Nowe sposoby wprowadzania danych Jeśli dysponujemy JDK w w ersji co najm niej 1.5, m ożemy również skorzystać z innego sposobu przetwarzania danych ze strum ienia wejściowego. Pozwala na to klasa Scanner z pakietu ja v a .u til. Ma ona bardzo wiele możliwości, ale my skupimy się tylko n a sposobie wczy tania za jej pom ocą liczb całkow itych z klaw iatury. Um ożliw ia to konstrukcja w postaci: S c a n n e r s c = new S c a n n e r ( S y s t e m . i n ) ; int liczba = sc.n extIn t();
W tak prostej formie spowoduje ona jednak, że wprowadzenie ciągu, który nie reprezentuje liczby całkowitej, wygeneruje wyjątek. Jeśli chcem y temu zapobiec, m ożemy zastosować pętlę while oraz metody hasNextInt i next. Pierwsza z nich zwraca wartość true, jeśli kolejny token reprezentuje wartość całkowitą, a fal se w przeciwnym wypadku, natom iast druga powoduje pobranie kolejnego tokena niezależnie od jego typu i zwrócenie go w postaci ciągu znaków (może być więc użyta do pom inięcia tokena). Ć W I C Z E N I E ________________________________________________________________________________________
6.9
Użycie klasy Scanner
Korzystając z klasy Scanner, napisz aplikację, która wczyta z klawiatury liczbę całkowitą i wyświetli ją na ekranie. W przypadku wprowadze nia ciągu znaków, który nie reprezentuje takiej liczby, ponawiaj prośbę o jej podanie. import j a v a . u t i l . * ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { S c a n n e r sc = new S c a n n e r ( S y s t e m . i n ) ; S y s t em . o ut . p ri n t ( " Po d aj l i c z b ę c ał kowi tą: " ) ; w hile(!sc.hasNextInt()){ S y s te m. o u t. p r i n t ( " To nie j e s t l i c z b a cał kowi ta. S y s t e m. o ut . pr in t( " Po d aj l i c z b ę cał kowi tą: " ) ; sc.next(); } int i = s c .n e x t I n t (); S y s t e m. o u t . p r i n t l n ( " Wp r o wa d z o n a l i c z b a t o :
} }
" + i);
");
136
Java
• Ćwiczenia praktyczne
Konstruktorowi klasy Scanner został przekazany obiekt reprezentujący standardowy strum ień w ejściow y. Pętla while została użyta do pom i nięcia wszystkich tokenów, które nie są liczbą całkowitą. Warunkiem jej zakończenia jest bowiem !sc.hasN extInt(). Trw a w ięc tak długo, aż wartość zwrócona przez metodę hasNextInt będzie równa true, czyli gdy na w ejściu pojawi się liczba całkowita. Dopóki liczby całkowitej nie ma, dopóty wyświetlany jest kom unikat o braku w artości całko w itej i wykonywana jest metoda next obiektu sc typu Scanner, a tym samym bieżący token (np. ciąg znaków) je st pom ijany (wywołanie sc.next() powoduje pobranie i zw rócenie kolejnego tokena, ale po nieważ nigdzie go nie używamy, jest to równoznaczne z jego pom i nięciem ). Oczyw iście jeżeli od razu zostanie wprowadzona wartość całkowita, warunek !sc.hasNextInt()będzie fałszywy, a więc pętla się nie wykona. Zostaną natom iast wykonane dalsze instru kcje. Zm iennej i zostanie przypisana w artość zwrócona przez m etodę nextInt, która pobiera ze strum ienia wartość typu int. W artość ta zostanie też w yświetlona na ekranie.
Warto w tym m iejscu zw rócić uwagę, że program z ćw iczenia 6.9 nie jest odporny na przerwanie strum ienia w ejściowego (np. przez kom bin ację C trl+ C lub C trl+ Z + E n te r; podobna sytuacja m iała m iejsce w ćwiczeniu 6.5). Wygenerowany zostanie wtedy wyjątek NoSuchElement ^Exception. Odpow iednia poprawka pozostanie jednak ćw iczeniem do samodzielnego wykonania (można się wzorować na rozwiązaniu z ćw iczenia 6.11). Ć W I C Z E N I E ________________________________________________________________________________________
6.10
Sumowanie wprowadzanych wartości
Korzystając z m ożliw ości klasy Scanner, napisz program, który będzie sumował wprowadzane w artości całkowite. Program m a zakończyć działanie, gdy n a w ejściu zostanie podana wartość in n a niż całkowita (rysunek 6.4). import j a v a . u t i l . * ; p u b l i c c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) i n t suma = 0;
{
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
137
S c a n n e r sc = new S c a n n e r ( S y s t e m . i n ) ; S y s t e m. o u t . p r i n t ( " Wp r o w a d z a j wa r t o ś c i c a ł k o w i t e . Aby z a k o ń c z y ć , " ) ; S y s t e m . o u t . p r i n t l n ( " wpisz wa r t o ś ć n i e b ę d ą c ą l i c z b ą c a ł k o w i t ą . " ) ; w hile(sc.hasNextInt()){ suma += s c . n e x t I n t ( ) ; } S y s t e m . o u t . p r i n t l n ( " O b l i c z o n a suma t o :
" + suma);
} }
Na początku kodu tworzony jest obiekt sc typu Scanner powiązany ze standardowym strum ieniem w ejściow ym oraz zm ienna suma, która będzie przechow ywała sumę wprowadzonych w artości całkowitych. W arunek pętli while to wynik działania metody hasNextInt obiektu sc, a zatem instrukcje wewnątrz pętli będą wykonywane, dopóki na w ejściu nie pojaw i się token niereprezentujący w artości całkowitej. Um ieszczona wewnątrz p ętli in stru kcja suma += sc.n e x tIn t() pow o duje z kolei dodanie do zm iennej suma w artości całkowitej odczytanej ze strum ienia wejściowego. S3
C:\Windows\system32\cmd.exe
L :\ja v a \p ro jc k ty > ja v a c
_
□
M a in .ja v a
C : \ j a v a \ p r o j c k t y > j a v a M a in W p row ad zaj w a r t o ś c i c a ł k o w i t e . d ą rą lic z b ą c a łk o w itą .
Aby zakończyć,
w p is z w a r t o ś ć
n ie b ę
0
?? S4 X
O b l i c z o n a su m a t o :
84
C :\ja v a \p ro je k ty > —
Rysunek 6.4. Program sumujący wartości całkowite wprowadzane z klawiatury
M ożliw ości klasy Scanner nie ograniczają się oczyw iście do przetwa rzania wyłącznie liczb całkowitych. S ą w niej zdefiniowane metody 0 konstrukcji hasNextFFP oraz nextFFP, przetwarzające liczby całkowite, zm iennoprzecinkowe, bajty itp. Przykładowo dla liczb zm iennoprze cinkow ych o podw ójnej precyzji dostępne są m etody hasNextDouble 1 nextDouble. Prezentowaną w cześniej aplikację rozwiązującą równania kwadratowe m oglibyśm y zatem napisać rów nież za pom ocą klasy Scanner.
138
Java
• Ćwiczenia praktyczne
Ć W I C Z E N I E ________________________________________________________________________________________
6.11
K lasa Scanner w realnej aplikacji
W ykonaj zadanie z ćw iczenia 6.8, używając klasy Scanner do w czy tywania w artości z konsoli. import j a v a . u t i l . * ; import j a v a . i o . * ; p u b l i c c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { doubl e parametrA = 0 , parametrB = 0 , parametrC = 0; S c a n n e r sc = new S c a n n e r ( S y s t e m . i n ) ; try{ S y s t e m . o u t . p r i n t ( " P o d a j par a me t r A: " ) ; while( ! s c .ha sNe x tDo ubl e()) { S y s t e m . o u t . p r i n t ( " P o d a j poprawny par ame t r A: " ) ; sc.next(); } parametrA = s c . n e x t D o u b l e ( ) ; S y s t e m . o u t . p r i n t ( " P o d a j par a me t r B: " ) ; while(!sc.hasNextDouble()) { S y s t e m . o u t . p r i n t ( " P o d a j poprawny par ame t r B:
");
sc.next(); } parametrB = s c . n e x t D o u b l e ( ) ; S y s t e m . o u t . p r i n t ( " P o d a j par a me t r C: " ) ; while(!sc.hasNextDouble()){ S y s t e m . o u t . p r i n t ( " P o d a j poprawny par ame t r C: " ) ; sc.next(); } parametrC = s c . n e x t D o u b l e ( ) ; } catch (Exception e ) { S y st em .o ut .p ri n tln("Błąd odczytu!" ) ; S y s t e m. e x i t ( - 1 ) ; }
/ / tutaj dalszy k o d klasy z ćw iczenia 6.8 } }
Przedstaw iony kod je st połączeniem k o n cep cji przedstaw ionych w ćw iczeniu 6.8 oraz przykładów ilustru jących użycie klasy Scanner. Funkcje wczytywania korzystające z klas pośredniczących Buffered eReader i InputStreamReader oraz z klasy przetwarzającej dane (jednostki
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
139
leksykalne) StreamTokenizer zostały zrealizow ane za pośrednictw em obiektu sc typu Scanner. Zastosowany został sposób odczytu liczb rze czywistych podobny do przedstawionego w ćw iczeniu 6.9, z tą róż n icą że użyto metod hasNextDouble i nextDouble. Część kodu związana z wykonaniem obliczeń pozostała taka sam a jak w ćw iczeniu 6.8, dlatego też pom inięto ją na listingu. W szystkie operacje pobierające i przetwarzające dane zostały ujęte w blok try.catch, przechwytujący wyjątki, które mogłyby się pojaw ić podczas tych operacji. W przy padku wystąpienia błędu (np. związanego z przerwaniem działania aplikacji odpowiednią kom binacją klawiszy) użytkownik zobaczy więc stosowny komunikat.
Obsługa konsoli Przedstawione w dwóch pierwszych podrozdziałach sposoby w czy tywania i w yśw ietlania danych za pom ocą standardowych strum ieni (wejściowego i wyjściowego) m ają tę zaletę, że są standardowe i mogą być zastosowane do dowolnych strumieni, np. również plikowych (zo stanie to pokazane w dalszej części rozdziału). W Java 6 (1.6) poja w iła się jednak dodatkowo klasa w yspecjalizowana w obsłudze kon soli. Jej nazwa to — jakżeby inaczej — Console. Dzięki temu możemy pobrać odwołanie do konsoli, na której operuje Java (o ile taka jest udostępniona; nie m usi to być konsola systemowa), i pobierać oraz wyśw ietlać dane. Klasa Console jest dostępna w pakiecie ja v a .io . Aby uzyskać obiekt reprezentujący konsolę, używamy konstrukcji w postaci: Consol e nazwa = S y s t e m . c o n s o l e ( ) ;
Jest to w ięc wywołanie metody console obiektu System. N ajczęściej używane są trzy metody z klasy Console: □
readLine — w czytuje w iersz tekstu (może w yśw ietlać tekst zachęty),
□
readPassword — w czytuje wiersz tekstu z maskowaniem,
□
p rin tf — wyświetla dane z uw zględnieniem różnych sposobów formatowania.
140
Java
• Ćwiczenia praktyczne
Ć W I C Z E N I E ________________________________________________________________________________________
6.12
Dostęp do konsoli
Napisz program w czytujący z konsoli wiersz tekstu i w yświetlający go na ekranie. Do obsługi w ejścia-w yjścia użyj klasy Console. import j a v a . i o . C o n s o l e ; p u b l i c c l a s s Main { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { Consol e con = S y s t e m . c o n s o l e ( ) ; i f ( c o n == n u l l ) { System.out.println("Brak konsoli!"); S y s t e m. e x i t ( - 1 ) ; } S t r i n g l i n e = con. readLi ne( " Wprowadź t e k s t : c on. pr i nt f ( " Wpr owadz ony t e k s t :
");
" + line);
} }
Odwołanie do konsoli (obiekt klasy Console) zostało pobrane w spo sób opisany wyżej — za pom ocą wywołania metody console obiektu System. Ponieważ w przypadku niedostępności konsoli metoda console zw raca wartość null , zostało to sprawdzone w instrukcji warunkowej if. Gdyby zatem nie m ożna było korzystać z konsoli, program za kończy działanie z kodem powrotu równym -15. Jeżeli jednak reprezen tujący konsolę obiekt con jest różny od null, wywoływana jest metoda readLine, której w postaci argumentu został przekazany tekst zachęty. Metoda readLine zw róci tekst (ciąg znaków) wprowadzony przez użytkownika i zapisze go w zm iennej line (w przypadku osiągnięcia koń ca strum ienia będzie to wartość null ), a zawartość tej zm iennej zostanie wyprowadzona na ekran za pom ocą metody printf. Ć W I C Z E N I E ___________________________________________________________________________________
6.13
Wczytywanie danych w pętli
Korzystając z klasy Console, napisz program, który będzie wczytywał z konsoli wiersze tekstu i wyświetlał je z powrotem na ekranie. Pro gram ma zakończyć działanie po wprowadzeniu ciągu quit. import j a v a . i o . C o n s o l e ;
5 Zamiast strumienia wyjściowego out właściwsze byłoby w takim przypadku użycie strumienia błędów err (System.err.println("Brak kon soli!");).
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
141
publ i c c l a s s Mai n { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) Consol e con = S y s t e m . c o n s o l e ( ) ;
{
i f ( c o n == n u l l ) { System.out.println("Brak konsoli!"); S y s t e m. e x i t ( - 1 ) ; } String line = ""; wh i l e ( ! "qui t " . e q u a l s ( l i n e ) ) { l i n e = con. readLi ne( " Wprowadź t e k s t : " ) ; c on. pr i nt f ( " Wpr owa dzony t e k s t : " + l i n e + " \ n " ) ; } } }
O biekt pow iązany z konsolą został utworzony w sposób opisany w ćw iczeniu 6.12. Odczyt wierszy tekstu odbywa się w pętli while. Wprowadzane przez użytkownika ciągi znaków są odczytywane przez metodę readLine i zapisywane w zmiennej line. Następnie tekst zmiennej line jest łączony za pom ocą operatora + z napisem Wprowadzony tek st: oraz znakiem końca lin ii \n (taka technika łączenia ciągów znaków była już stosowana wielokrotnie) i wyprowadzany na ekran za pomocą metody printf. W yrażenie warunkowe w pętli while bada, czy ciąg znaków zawarty w line odpowiada ciągowi quit. Została tu zastosowana przedsta wiona w ćw iczeniu 6.5 metoda equals. To badanie jest jednak „od w rócone” w stosunku do przykładu 6.5, dzięki czem u unikam y wy generowania wyjątku, w przypadku gdy zmienna line zawiera wartość null. Zapis "quit".equ als(line) jest m ożliwy, ponieważ każdy ciąg znaków ujęty w cudzysłów może być traktowany jako obiekt typu String, można w ięc na jego rzecz w yw ołać dow olną m etodę z klasy String (w tym także zastosowaną metodę equals)6.
6 W takim rozwiązaniu, w przypadku gdy line jest równe null, wartość ta zostanie wyświetlona na ekranie (aby to sprawdzić, wystarczy przerwać działanie aplikacji, wciskając kombinację klawiszy Ctrl+C ). Poprawka likwidująca ten defekt jest jednak tak prosta, że można ją pozostawić jako ćwiczenie do samodzielnego wykonania.
142
Java
• Ćwiczenia praktyczne
W ćw iczeniach 6.12 i 6.13 metoda p rin tf była używana tak samo jak println z klasy PrintStream (w w ywołaniach System.out.println). Ma jednak dużo większe m ożliwości. Potrafi formatować łańcuchy zna kowe. Jeżeli w tekście, który chcemy wyświetlić, umieścimy znaczniki formatujące, a wartości, które chcemy wyświetlić, podamy jako kolejne argumenty, będziem y m ogli prezentować dane w rozmaity sposób. Znacznik form atujący składa się ze znaku % oraz litery określającej typ wartości7, np. %s oznacza ciąg znaków, a %d wartość całkowitą. Do puszczalne symbole zostały przedstawione w tabeli 6.1. Schematyczna postać wywołania metody p rintf będzie wtedy następująca (zakła dając, że obiekt con reprezentuje konsolę): c o n . p r i n t f ( " / o r m a t " , w a r t o ś ć l , w a r to ś ć 2
w artośćN );
Tabela 6.1. Znaczniki formatujące dla metody printf
ciąg
Znaczenie
b lub B
Argument będzie traktowany jako wartość boolowska.
h lub H
Ciąg będący wynikiem wykonania funkcji skrótu (hash code) argumentu.
s lub S
Argument będzie traktowany jako ciąg znaków.
c lub C
Argument będzie traktowany jako znak.
d
Argument będzie traktowany jako wartość całkowita dziesiętna.
o
Argument będzie traktowany jako wartość całkowita ósemkowa.
x lub X
Argument będzie traktowany jako wartość całkowita szesnastkowa.
e lub E
Argument będzie traktowany jako wartość rzeczywista w notacji naukowej (wykładniczej).
f
Argument będzie traktowany jako wartość rzeczywista dziesiętna.
g lub G
Argument będzie traktowany jako wartość rzeczywista dziesiętna w notacji zwykłej lub naukowej — w zależności od zastosowanej precyzji.
7 W rzeczywistości ciąg formatujący może mieć dużo bardziej złożoną postać i określać np. precyzję, z jaką ma być wyświetlona wartość. Dokładne dane można znaleźć w dokumentacji technicznej JDK.
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
143
Tabela 6.1. Znaczniki formatujące dla metody printf — ciąg dalszy
ciąg
Znaczenie
a lub A
Argument będzie traktowany jako wartość rzeczywista szesnastkowa.
t lub T
Argument będzie traktowany jako data i (lub) czas. Wymaga określenia dodatkowego formatowania.
%
Znak %.
n
Znak nowego wiersza.
Jeżeli zatem w kodzie zdefiniujem y przykładowe zm ienne: b oo l e an zmi ennal = t r u e ; i n t zmienna2 = 2 5 4 ;
to ich w artości możemy w pleść w w yświetlany na ekranie ciąg zna ków w następujący sposób: c o n . p r i n t f ( " z m i e n n a 1 = %b, zmienna2 = %d", zmi e n na l , zmi enna2) ;
Wtedy w m iejsce ciągu %b zostanie podstaw iona w artość zmiennej zmiennal, a pod %d — wartość zm iennej zmienna2 (oczywiście zmienna con m usi zawierać odwołanie do konsoli). Ć W I C Z E N I E ________________________________________________________________________________________
6.14
Formatowanie ciągów wyjściowych
Napisz program w czytujący z konsoli liczbę całkow itą i w yśw ietlają cy ją w postaci dziesiętnej, ósemkowej i szesnastkowej. Użyj klasy Console. W przypadku gdy odczytana wartość nie może być interpreto wana jako wartość całkowita, program ma ponaw iać prośbę o podanie w łaściw ych danych. import j a v a . i o . C o n s o l e ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d main ( S t r i n g a r g s [ ] ) { Consol e con = S y s t e m . c o n s o l e ( ) ; i f ( c o n == n u l l ) { System.out.println("Brak konsoli!"); S y s t e m. e x i t ( - 1 ) ; } String line = ""; int liczba;
Java
144
con.printf("Podaj
• Ćwiczenia praktyczne
l i c z b ę c ał kowi tą:
");
while(true){ li n e = con.readLine(); try{ liczba = Integer.parselnt(line); } c at c h( NumberFormat Except i on e ) { c o n . p r i n t f ( " T o nie j e s t l i c z b a c ał kowi ta . c o n . p r i n t f ( " P o d a j l i c z b ę cał kowi tą: " ) ; continue;
");
} b r e a k; } c o n . p r i n t f ( " D z i e s i ę t n i e : %d%n", l i c z b a ) ; c o n. pr i n t f ( " Ós e mk o wo : %o%n", l i c z b a ) ; c o n . p r i n t f ( " S z e s n a s t k o w o : %x%n", l i c z b a ) ; } }
W tym przykładzie został zastosowany nowy sposób odczytu warto ści typu int. W arunkiem pętli while jest true, czyli trwa ona dopóty, dopóki nie zostanie przerwana za pom ocą instru kcji break, a ta in strukcja jest wykonywana dopiero wtedy, gdy wprowadzona przez użytkownika wartość może być przetworzona na liczbę całkowitą. Wprowadzane lin ie tekstu są odczytywane standardowo za pom ocą metody readLine i zapisywane w zmiennej line. Później następuje pró ba przekonwertowania ciągu znaków zapisanego w lin e na wartość całkowitą. Jest do tego używana metoda parseInt z klasy Integer: liczba = Integer.parselnt(line);
Rezultatem w ywołania jest w artość całkowita, zapisywana w zm ien nej liczba. W przypadku gdy ciąg zapisany w lin e nie może być przekształcony w liczbę, generowany jest wyjątek NumberFormatException, który jest przechwytywany w instrukcji catch. Jeśli zatem wyjątek się pojawi, oznacza to, że wprowadzone dane są niepraw idłow e. Wtedy w yśw ietlany jest stosow ny kom unikat, a pętla kontynuow ana za pom ocą in stru kcji continue. Ciągi form atujące w m etodzie p rintf zostały skonstruowane zgodnie z przedstawionym wyżej opisem. Znacznik %n to znak nowego wiersza. Zam iast niego m ożna też użyć ciągu specjalnego \n, co będzie miało takie samo znaczenie.
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
145
Operacje na plikach Żadna poważna aplikacja nie obejdzie się bez operacji na plikach — trzeba przecież w jakiś sposób zapisyw ać i odczytywać dane. W tej sekcji omówimy w ięc kilka klas, które pozw alają na wykonywanie tego typu zadań. Wykorzystamy klasy FileInputStream i FileOutputStream służące do przeprow adzania operacji strum ieniow ych n a plikach. Przydatne będą rów nież klasy: BufferedReader, DataInputStream i Data ^OutputStream. ĆW I CZ EN
i e
6.15
________________________________________________________________________________________
Zapis danych do pliku
Napisz program zapisujący do pliku kolejne linie tekstu wczytywane z klawiatury. Program pow inien zakończyć działanie po wprowadze niu ciągu znaków "quit". import j a v a . i o . * ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { String line = ""; Fi l e Ou t pu t S t r e a m f o u t = n u l l ; try{ f o u t = new F i l e O u t p u t S t r e a m ( " t e s t . t x t " ) ; } c a t c h ( F i l eNot FoundExcept i on e ) { S y s t e m . o u t . p r i n t l n ( " B ł ą d przy o t w i e r a n i u p l i k u . " ) ; S y s t e m. e x i t ( - 1 ) ; } DataOutputSt ream out = new D a t a O u t p u t S t r e a m( f o u t ) ; Bu f f e r e d R e a d e r i n b r = new B u f f e r e d R e a de r ( new I n p u t S t r e a mR e a d e r ( S y s t e m. i n) ); try{ whi l e ( t r u e ) { i f ( ( l i n e = inbr.readLine()) li ne.equals("quit")) { b r e a k;
== nul l
||
} out.writeBytes(line + '\n'); } } catch(IOException e ) { System.out.println("Read/Write e r r o r . " ) ;
} } }
146
Java
• Ćwiczenia praktyczne
Na początku tworzony jest obiekt typu FileOutputStream powiązany z plikiem test.txt. Gdyby otwarcie takiego pliku nie było możliwe, zo stanie wygenerowany w yjątek FileNotFoundException przechwytywany przez blok try.catch (mimo nazwy w yjątku plik nie m usi istnieć na dysku — zostanie utworzony przez aplikację). Uwaga! Jeśli plik istnie je, zawarte w nim dane zostaną skasowane. Dane z klaw iatury są odczytyw ane za pom ocą obiektu inbr klasy BufferedReader, podobnie jak miało to m iejsce we wcześniejszych przy kładach. Do zapisu do pliku jest natomiast używana metoda writeBytes z klasy DataOutputStream. Aby utworzyć obiekt klasy DataOutputStream, w jej konstruktorze został podany obiekt fout klasy FileOutputStream powiązany z plikiem test.txt. Odczyt i zapis danych odbywa się w pętli while. Należy zwrócić uwagę na w ystępującą w niej instrukcję warunkową if. Sprawdza ona, czy m etoda readLine zw róciła w artość null (taka sytuacja będzie m iała m iejsce, gdy strum ień w ejściow y zostanie przerwany, np. przez w ci śnięcie klaw iszy C trl+ C ) oraz czy wprowadzony tekst to quit. Użyto złożonej instrukcji: (line = inbr.readLine())
== nul l
w której wartość uzyskana przez wywołanie metody readLine obiektu wskazywanego przez inbr jest najpierw przypisywana zm iennej line, a następnie wartość tej zm iennej jest porównywana z null . Jeżeli jeden z warunków instrukcji i f jest prawdziwy, następuje prze rwanie pętli za pom ocą instrukcji break. W przeciwnym razie odczy tane dane (zawarte w zmiennej line) są zapisywane do pliku. Na końcu każdego zapisywanego w iersza jest dodawany znak końca w iersza symbolizowany przez \n. W ażna jest rów nież kolejność wyrażeń w warunku w ystępującym w if. Wykorzystana została cecha skróconego przetwarzania wyrażeń. Wiadomo przecież (m.in. z ćw iczenia 6.5), że sam zapis line.equals ^("q u it") mógłby spowodować wystąpienie wyjątku Nul l PointerException. Ponieważ jednak w cześniejsza część w yrażenia (występująca przed operatorem ||) w yklucza istn ien ie w artości null w zm iennej line w drugiej części wyrażenia, zapis form alnie jest prawidłowy. W prak tyce lepiej byłoby jednak stosować przedstawiany już zapis "quit".equals ^ ( l ine), który jest bezpieczniejszy i nie spowoduje błędów np. po m odyfikacji kodu i usunięciu pierwszego warunku.
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
147
Ć W I C Z E N I E ________________________________________________________________________________________
6.16
Odczyt danych z pliku
Napisz program w czytujący dane z pliku tekstowego i w yświetlający je na ekranie. import j a v a . i o . * ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { String line = FileInputStream fin = null; try{ f i n = new F i l e I n p u t S t r e a m ( " t e s t . t x t " ) ; } c a t c h ( F i l eNot FoundExcept i on e ) { System.out.println("Brak pliku ."); S y s t e m. e x i t ( - 1 ) ; } Dat aI nput S t r e am out = new D a t a l n p u t S t r e a m ( f i n ) ; Bu f f e r e d R e a d e r i n b r = new Bu f f e r edReader ( new I n p u t S t r e a m R e a d e r ( f i n ) ) ; try{ whi l e ( ( l i n e = i n b r . r e a d L i n e ( ) ) ! = n u l l ) { System.out.println(line) ; } } c atch(IOException e ) { System.out.println("Błąd w e jścia-w y jścia."); } } }
W powyższym ćw iczeniu stosujem y podobne konstrukcje jak w po przednich przykładach. Ponieważ tym razem dane m ają być odczy tywane, a nie zapisywane, zam iast klasy FileOutputStream stosowana jest FileInputStream, a zam iast DataOutputStream — DataInputStream. Obiekt wskazywany przez zm ienną fin wiązany jest w wywołaniu konstruktora klasy FileInputStream z plikiem test.txt, a następnie uży wany do utworzenia obiektu inbr klasy BufferedReader. Odczyt danych odbywa się w pętli while. Zwróćm y uwagę na postać wyrażenia wa runkowego: wh i l e ( ( l i n e = i n b r . r e a d L i n e ( ) )
!= nul l)
Oznacza ona: pobieraj kolejne linie tekstu, zapisuj je w zm iennej line i wykonuj pętlę dopóty, dopóki lin e jest różne od null. T en w arunek jest potrzebny do określenia, kiedy zostanie osiągnięty koniec pliku. W takim wypadku metoda readln zwraca bowiem właśnie wartość null.
148
Java
• Ćwiczenia praktyczne
A plikacja z ćw iczenia 6.16 nie jest jednak w pełni funkcjonalna, gdyż potrafi odczytać jedynie zawartość pliku o nazwie test.txt. W jaki sposób zmienić ją, tak aby można było odczytać zawartość dowolnego pliku tekstowego? Należy skorzystać z argumentu funkcji main: p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] )
Jest to tablica z referencjam i do obiektów typu String, która zawiera wszystkie parametry podane w wierszu poleceń podczas urucham ia nia programu. Można je zatem bez problemów odczytać. Ć W I C Z E N I E ________________________________________________________________________________________
6.17
Param etry wywołania aplikacji
Napisz aplikację w yśw ietlającą jej wszystkie parametry wywołania. publ i c c l a s s Mai n { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { f o r ( i n t i = 0; i < a r g s . l e n g t h ; i + + ) { System .out.println(args[i]); } } }
Jeśli teraz przy wywołaniu programu podamy jakieś parametry, np. użyjem y polecenia: j a v a Main abc 123 xyz
to wszystkie te parametry zostaną w yświetlone (rysunek 6.5). Warto zauważyć, że — odm iennie niż w C /C + + — pierwszym elem entem tablicy nie jest nazwa samego programu. Rysunek 6.5. Parametry wywołania zostały wyświetlone na ekranie
Skoro wiadomo już, jak odczytać argumenty aplikacji, bez proble m ów można napisać program w yśw ietlający zawartość pliku, którego nazwa będzie podawana podczas wywołania: j a v a Main n azw a_ p liku
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
149
Ć W I C Z E N I E ________________________________________________________________________________________
6.18
Wykorzystanie argumentów wywołania aplikacji
Napisz program w yśw ietlający zawartość pliku tekstowego o nazwie podanej jako argument wywołania. import j a v a . i o . * ; publ i c c l a s s Mai n { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { i f (args.length < 1){ S y s t e m . o u t . p r i n t l n ( " W y w o í a n i e programu: Main n a z wa _ p l i k u " ) ; S y s t e m. e x i t ( 0 ) ; i String line = ""; FileInputStream fin = null; try{ f i n = new F i l e I n p u t S t r e a m ( a r g s [ 0 ] ) ; i c a t c h ( F i l e No t F o u n d E x c e p t i on e ) { System.out.println("Brak pliku " + a r g s [ 0 ] ) ; S y s t e m. e x i t ( - 1 ) ; i Bu f f e r e d R e a d e r i n b r = new Bu f f e r e d R e a d e r ( new I n p u t S t r e a mR e a d e r ( f i n ) ); try{ whi l e ( ( l i n e = i n b r . r e a d L i n e ( ) ) System.out.println(line) ;
!= n u l l ) {
i i c atch(IOException e ) { S y st em .o ut .p ri n tln("Błąd w e j ś c i a - w y j ś c i a . " ) ; i i i
Program najpierw sprawdza, czy tablica args zawiera przynajmniej jeden element. Jeśli tak, traktuje go jako nazwę pliku, którego zawar tość ma być wyświetlona, jeśli nie, wyświetla kom unikat inform ujący o sposobie wywoływania aplikacji. Pozostałe czyn n ości są wykony wane w taki sam sposób jak we w cześniej prezentow anych przykła dach, z tym że konstruktor klasy FileInputStream otrzymuje w postaci argumentu wartość pierwszego elem entu tablicy args (args[0]).
Java
150
• Ćwiczenia praktyczne
Skoro wiadom o, jak odczytywać dane z pliku i jak je zapisywać, a także jak odczytać argumenty wywołania programu, to korzystając z przedstawionych technik, można napisać aplikację, która umożliwi kopiowanie plików. Ć W I C Z E N I E ________________________________________________________________________________________
6.19
Kopiowanie plików
Napisz program kopiujący pliki, których nazwy zostały podane jako parametry wywołania. import j a v a . i o . * ; p u b l i c c l a s s Main { p u b l i c s t a t i c voi d m a i n ( S t r i n g a r g s [ ] ) { i f ( a r g s . l engt h < 2) { S y s t e m . o u t . p r i n t l n ( " W y w o i a n i e programu: "+ "Main nazwa_pl i ku_źródj owego n a zwa_pl i ku_doc e l o we g o" ) ; S y s t e m. e x i t ( 0 ) ; } Fi lel nput Stream f i n = n ul l ; Fi l e Ou t pu t S t r e a m f o u t = n u l l ; try{ f i n = new F i l e I n p u t S t r e a m ( a r g s [ 0 ] ) ; } c a t c h ( F i l e No t F o u n d E x c e p t i on e ) { System.out.println("Brak pliku: S y s t e m. e x i t ( - 1 ) ;
" + args[0]);
} try{ f o u t = new F i l e O u t p u t S t r e a m ( a r g s [ 1 ] ) ; } c a t c h ( F i l e No t F o u n d E x c e p t i on e ) { S y s t e m . o u t . p r i n t l n ( " N i e można utworzyć p l i k u : S y s t e m. e x i t ( - 1 ) ; } try{ i n t b; whi l e ( ( b = f i n . r e a d ( ) ) f o u t . wr i t e ( b ) ;
!= - 1 ) {
} System. out. pri ntl n("Kopi owani e zakończone!" ) ; } catch(IOException e ) { S y st em .o ut .p ri n tln("Bjąd w e j ś c i a - w y j ś c i a . " ) ;
} } }
" + args[1]);
R o z d z i a ł 6. • O p e r a c j e w e j ś c i a - w y j ś c i a
151
Obiekty fin typu FileInputStream (powiązany z plikiem wejściowym ) i fout typu FileOutputStream (powiązany z plikiem wyjściowym) są tworzone w standardowy sposób. Dane są odczytywane z pliku źró dłowego za pom ocą metody read obiektu fin (klasy FileInputStream; metoda zwraca jeden odczytany ze strumienia bajt w postaci wartości typu int), natomiast zapisywane za pomocą metody write obiektu fout (klasy FileOutputStream; metoda zapisuje do strumienia jeden bajt prze kazany jej w postaci argumentu typu int). Operacje te są wykonywa ne w pętli while, która działa dopóty, dopóki metoda read nie zwróci w artości -1 oznaczającej osiągnięcie końca pliku. Operacje odczytu i zapisu zostały ujęte w blok try.catch, pozw alający na obsługę sytu acji, w której pojawiłby się błąd w ejścia-w yjścia uniem ożliw iający wykonanie operacji kopiowania (zarówno metoda read, jak i write może wygenerować w yjątek typu IOException).
Należy zwrócić uwagę, że zastosowana w ćwiczeniu 6.19 metoda ko piowania b ajt po bajcie jest mało efektywna. O w iele lepsze rezultaty dałoby kopiowanie większych bloków danych. To jed nak pozostanie zadaniem do samodzielnego wykonania. Zaglądając do dokum entacji JDK, m ożna sprawdzić, która metoda pozwala na kopiowanie w ięcej niż jednego bajtu. Pozwoli to na napisanie dużo szybciej działającego programu.
7 Aplety
Aplikacja a aplet Programy w Javie mogą być m .in. apletami (ang. applet) i aplikacjam i (ang. application). Różnica jest taka, że aplikacja jest programem samo dzielnym, natomiast aplet jest kodem interpretowanym przez maszynę w irtualną w budow aną w przeglądarkę lub inny program. Aplety to zwykle m ałe programy um ieszczane na stronach WWW. A plet ma również bardzo ograniczony dostęp do systemu, np. nie może nic zapi sać na dysku, tak aby złośliw y program ista um ieszczający n ieb ez pieczny kod na stronie WWW nie był w stanie nam zaszkodzić (chyba że został wyposażony w odpowiedni podpis cyfrowy, a użytkownik zezw olił na wykonywanie takich operacji). Z podziału na aplety i aplikacje czasem w ynikają nieporozumienia. W pierw szych latach po wprowadzeniu Javy na rynek w artykułach często można było przeczytać, że jest ona całkow icie bezpieczna, bo nie ma dostępu do dysku (choć to tylko jeden z m echanizm ów bez pieczeństw a), a dwa akapity dalej, że m a być również platformą do budow ania dużych, „prawdziwych” aplikacji. Potem pojaw iały się pytania czytelników, jak napisać np. edytor tekstu bez m ożliw ości
153
Java
154
• Ćwiczenia praktyczne
zapisu na dysku. Otóż oczyw iście w języku programowania Java ist nieje m ożliw ość zarówno zapisu danych na dysku, jak i tworzenia w ielu innych konstrukcji programistycznych. Pozwalają na to bez pośrednio programy pisane jako aplikacje, a po spełnieniu dodatko w ych warunków — również aplety. O becnie aplety straciły na znaczeniu i nie są już tak często używane, wciąż są jednak pow szechne np. na stronach prezentujących noto wania giełdowe, oferujących narzędzia do analizy technicznej związa nej z grą na giełdach papierów wartościowych, a także w aplikacjach ty pu czat czy też niektórych grach działających w przeglądarkach WWW.
Pierwszy aplet W rozdziale 1. książki zaczynaliśmy od programu wypisującego napis na ekranie. Tym razem zrobimy tak samo. Stwórzmy aplet wyświetla jący na ekranie napis Pierwszy aplet w Javie. Aplet może być tworzony na bazie klasy Applet z pakietu java.applet lub też now ocześniejszej klasy pochodnej JApplet zawartej w pakiecie javax.swing. Będziem y korzystać z tej drugiej, choć w przykładach z tego rozdziału różnice m iędzy tymi klasam i nie m ają praktycznego znaczenia. ĆW I CZ EN I E
________Aplet w yśw ietlający zdefiniowany tekst Napisz aplet w yśw ietlający na ekranie napis Pierwszy aplet w Javie. import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . G r a p h i c s ; p u b l i c c l a s s Ap pl e t He l l o e x t e n ds J Ap p l e t { p u b l i c voi d p a i n t ( G r a p h i c s gDC) { gDC. drawSt r i ng ( " P i e r ws z y a p l e t w J a v i e " ,
100,
100);
} }
Co należy z tym programem zrobić? Przede wszystkim um ieścić w pli ku AppletHello.java i skompilować. Uzyskamy wtedy plik z kodem pośrednim AppletHello.class, który m oże być zagnieżdżony w stro nie WWW.
R o z d z i a ł 7. • A p l e t y
155
A by aplet mógł być wyświetlony, trzeba go um ieścić w kodzie strony W W W 1. Dawniej służył do tego znacznik HTML , który w uproszczeniu można przedstawić tak:
O becnie należy używać znacznika :
Kod strony najlepiej zapisać w pliku o rozszerzeniu html, np. index.html. Formalnie w HTML5 w znaczniku zamiast atrybutu code powinien się znajdować atrybut data. Nie był on jednak rozpoznawany przez żadną z wersji aplikacji apletviewer z pakietu JDK dostępnych w trakcie przygotowywania materiałów do książki. Dlatego też na listingach użyty został atrybut code (niepoprawny, ale rozpoznawany przez wszystkie popularne przeglądarki). ĆW I CZ EN I E
Umieszczanie apletu na stronie WWW Napisz kod HTML pozwalający na osadzenie klasy apletu z ćw icze nia 7.1 na stronie WWW.
< t i t l e > M o j a s t r o n a WWW
< / d i v>
< /ht ml > CW I CZ EN I E
Uruchomienie apletu U ruchom aplet przygotowany w ćw iczeniu 7.1. Aby uruchomić aplet, należy um ieścić pliki AppletHello.class (skom pilow any kod z ćw iczenia 7.1) i index.htm l (kod z ćw iczenia 7.2) w jednym katalogu, a następnie otworzyć plik index.html w dowolnej przeglądarce WWW obsługującej Javę (z reguły poprzez m echanizm wtyczek). Druga możliwość to użycie narzędzia o nazwie appletviewer z pakietu JDK. W ystarczy w wierszu poleceń wydać komendę: a p p l e t v i e w e r i ndex. ht ml
Na ekranie pojawi się wtedy widok zaprezentowany na rysunku 7.1. Rysunek 7.1. Aplet wyświetlający napis na ekranie
R o z d z i a ł 7. • A p l e t y
157
Jak to działa? Aplet z ćwiczenia 7.1 dziedziczy po predefiniowanej klasie JApplet, któ ra opisuje zachow ania apletów. Jest ona zawarta w pakiecie javax. swing, dlatego też na początku kodu znajduje się instrukcja: import j a v a x . s w i n g . J A p p l e t ;
Jest w nim zadeklarow ana tylko jedna metoda, m ianow icie paint, która jest zawsze wywoływana przez system, kiedy zachodzi potrzeba odrysowania (odświeżenia) okna apletu, np. gdy zostanie ono scho wane za innym oknem, a następnie ponow nie odsłonięte. Jedynym param etrem tej metody jest referencja do obiektu typu Graphics (defi n icja tej klasy znajduje się w pakiecie java.awt, stąd druga instrukcja import na początku kodu) i jest to tzw. graficzny kontekst urządze n ia (ang. Graphics Device Context), przez niektórych zwany również w ykreślaczem 2. Pozostańmy jednak przy nazwie kontekst urządze nia. Co to jest? Otóż jest to obiekt, dzięki któremu możemy rysować na ekranie. W ydając mu odpowiednie komendy, czyli wywołując zdefiniowane w n im metody, powodujemy w yświetlenie na ekranie różnych obiektów. W przypadku apletu z ćw iczenia 7.1 jest to napis podany jako pierwszy parametr metody drawString. Dwa kolejne pa rametry to współrzędne, od których zacznie się rysowanie tego napisu. Sam skompilowany kod apletu nie wystarczył. Trzeba go było jesz cze um ieścić na stronie WWW, tak aby przeglądarka wiedziała, gdzie go szukać i jak interpretować. Dlatego też w kodzie HTML z ćw icze nia 7.2 pojawił się znacznik z parametrem code wskazującym nazwę pliku ze skompilowanym kodem apletu (AppletHello.class) oraz param etrem type w skazującym , że kod m a być traktow any jako aplet (application/x-java-applet). Apletow i można przekazać dodatkowe parametry, np. dane sterujące jego pracą. Należy wtedy użyć znaczników wewnątrz zn acz nika . Schem atycznie wygląda to następująco:
2 Stosowany jest też termin kontekst rysowniczy.
158
Java
• Ćwiczenia praktyczne
Po takiej definicji aplet będzie m iał dostęp do parametru nazwa1 o war tości wartość1, parametru nazwa2 o wartości wartość2 itd.
Cykl życia apletu Gdy przeglądarka obsługująca Javę odnajdzie w kodzie HTML znacz nik z atrybutem type o wartości application/x-java-applet, w y konuje określoną sekwencję czynności. Zaczyna od sprawdzenia, czy w podanej lokalizacji znajduje się plik zaw ierający kod k lasy wy m ienionej jako wartość argumentu code (lub data) znacznika. Jeśli plik znajduje się we wskazanej lokalizacji, zawarty w nim kod ładowany jest do pam ięci i tworzona jest instancja (czyli obiekt) znalezionej klasy. Tym samym wywoływany jest rów nież konstruktor. Po tych czynnościach wykonywana jest metoda in it utworzonego obiektu, inform ująca go o tym, że został załadowany do systemu. W metodzie in it m ożna zatem um ieścić (o ile zachodzi taka potrzeba) procedury inicjacyjne. Gdy aplet jest gotowy do uruchom ienia, przeglądarka wywołuje jego metodę start, informując, że powinien rozpocząć swoje działanie. Przy pierwszym ładowaniu apletu metoda s ta rt wykonywana jest zawsze po m etodzie in it. W m om encie gdy aplet powinien zakończyć swoje działanie, zostaje wywołana z kolei jego metoda stop.
Kroje pisma (fonty) Napis, który w yśw ietliliśm y w pierwszym aplecie, wyglądał nieco m izernie. Warto by przynajm niej odrobinę go powiększyć i pogrubić. W tym celu m usim y zm ienić font. Aby tego dokonać, trzeba stwo rzyć nowy obiekt typu Font, a następnie użyć metody setFont.
R o z d z i a ł 7. • A p l e t y
159
ĆWICZENIE
Zastosow anie wybranego kroju pisma Napisz aplet w yśw ietlający na ekranie napis pogrubionym krojem SansSerif (bezszeryfowym) o w ielkości 36 punktów (rysunek 7.2). import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; p u b l i c c l a s s Ap pl e t He l l o e x t e n ds J Ap p l e t { p u b l i c voi d p a i n t ( Gr aphi c s gDC) { Font f o n t = new Font ( " S a n s S e r i f " , Font.BOLD, 3 6 ) ; gDC. s e t F ont ( f o n t ) ; gDC. drawSt r i ng ( “H e l l o ! " ,
110,
110);
} }
Rysunek 7 . 2 . Aplet wyświetlający napis wybraną czcionką
W konstruktorze obiektu typu Font podajemy nazwę fontu, a następ nie jej typ oraz w ielkość. Do dyspozycji mamy następujące typy: 1. Font.PLAIN — krój zwykły, 2. Font.BOLD — krój pogrubiony, 3. Font.ITALIC — krój pochylony.
W środowisku Java 2 i wyższych dostępnych jest pięć fontów logicznych: 1. Serif. 2. SansSerif. 3. Monospaced. 4. Dialog. 5. DialogInput.
160
Java
• Ćwiczenia praktyczne
Nastąpiła tu spora zm iana w stosunku do pierwotnych w ersji Javy (JDK 1.1), w których dostępnych było sześć rodzajów fontów (wspólne są jedynie D ialog i D ialogInput). Dom yślnie (jeżeli nie ustawim y własnego kroju) jest używany krój Dialog. W ymienione fonty są do stępne w każdej w ersji JRE i JDK niezależnie od użytego środowiska uruchom ieniowego. Nie oznacza to jednak, że nie m ożna stosować innych czcionek. Można użyć dowolnej czcionki znajdującej się w systemie, z tym że nie mamy wtedy pewności, że ta czcionka będzie dostępna w systemie użytkownika apletu. Jeśli nie będzie dostępna, środowisko uruchomie niowe (JRE) postara się dopasować najbardziej zbliżoną czcionkę. W łasności poszczególnych czcionek m ożna poznać dzięki obiektowi FontMetrics oraz m etodzie getFontMetrics. Obiekt ten ma kilkanaście metod, spośród których bardziej interesujące to: □
charWidth — zwraca szerokość litery podanej jako parametr,
□
getHeight — zwraca typową wysokość wiersza tekstu zapisanego za pom ocą czcionki,
□
getLeading — zwraca odległość pomiędzy wierszam i tekstu zapisanego za pom ocą czcionki,
□
stringWidth — zwraca długość fragmentu tekstu zapisanego za pom ocą czcionki.
C W I C Z E N I E ________________________________________________________________________________________
.5
Dostępne kroje pisma
Napisz aplet prezentujący na ekranie dostępne standardowo kroje (rysunek 7.3). import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; p u b l i c c l a s s Appl e t Fo n t s e x t e n ds J Ap p l e t { Font f o n t S e r i f , f o n t S a n s S e r i f , fontMonospaced, f o nt Di al og, f ontDi al ogI nput; p u b l i c voi d i n i t ( ) { f o n t S e r i f = new F o n t ( " S e r i f " , Font.BOLD, 3 6 ) ; f o n t S a n s S e r i f = new F o n t ( " S a n s S e r i f " , Font.BOLD, 3 6 ) ; f ontMonospaced = new Font ( " Monos pace d" , Font.BOLD, 3 6 ) ; f o n t D i a l o g = new F o n t ( " D i a l o g " , Font. BOLD, 3 6 ) ; f o n t D i a l o g I n p u t = new F o n t ( " D i a l o g I n p u t " , Font.BOLD, 3 6 ) ;
}
R o z d z i a ł 7. • A p l e t y p u b l i c voi d p a i n t
( Gr aphi c s gDC)
161
{
gDC. setFo nt (fo ntS eri f ) ; g DC. drawStr i ng(' 'Ser i f' ' , 110, 3 0 ) ; gDC.setFont(fontSansSerif); g D C . d r a w S t r i n g ( " S a n s S e r i f " , 110, 7 0 ) ; g D C . s e t F o n t ( f o n t Mo n o s p a c e d ) ; gDC. dr awSt r i ng( " Monospa c ed" , 110, 1 1 0 ) ; gDC.setFont(fontDialog); g D C . d r a w S t r i n g ( " D i a l o g " , 110, 1 5 0 ) ; g DC. setF ont (fontDi al ogl nput) ; g D C . d r a w S t r i n g ( " D i a l o g I n p u t " , 110, 1 9 0 ) ; } }
Rysunek 7.3. Aplet prezentujący dostępne kroje
A pplct V iew er A pplctł-onts.dass
Serif
SansSerif M o n o s p a c e d
Dialog D i a l o g l n p u t
W przeciw ieństw ie do poprzedniego ćw iczenia została tu wykorzy stana m etoda in it, od której rozpoczyna się wykonywanie apletu. To w niej tworzone są niezbędne obiekty typu Font. Wprawdzie te czynno ści mogłyby być również wykonywane w m etodzie paint, ale w tym przypadku byłoby to gorsze rozw iązanie. Nie m a bow iem potrzeby tworzenia nowych obiektów typu Font za każdym razem, gdy koniecz ne jest odświeżenie obszaru apletu, czyli przy każdym wywołaniu metody paint. Niepotrzebnie marnowałoby to tylko zasoby systemowe. Dlatego też obiekty tworzymy tylko raz, a w m etodzie paint jedynie się do nich odwołujemy.
Rysowanie grafiki Klasa Graphics umożliwia nam rysowanie prostych figur geometrycz nych, można przy tym rysować zarówno same obrzeża figur, jak i figu ry wypełnione kolorem. Dostępne metody pozwalają na narysowanie:
162
Java
• Ćwiczenia praktyczne
□ linii, □ łuków, □ kół i elips, □ prostokątów, □ wielokątów. Nie m a natom iast dedykowanej metody, która pozwala na rysowanie pojedynczych punktów. Można zam iast tego rysować linie o długo ści jednego piksela. Do rysowania lin ii służy m etoda: d r a wL i n e ( i n t x 1 ,
int y 1 , int x2, int y 2 );
Rysuje ona odcinek zaczynający się w punkcie o współrzędnych x1, y1, a kończący się w punkcie o w spółrzędnych x2, y2. CWICZENIE
Rysowanie w obszarze apletu Napisz aplet w yśw ietlający na ekranie strzałkę (rysunek 7.4). import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . G r a p h i c s ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { p u b l i c voi d p a i n t ( Gr aphi c s gDC) { gDC. drawLine ( 1 2 0 , 120, 160, 4 0 ) ; gDC. drawLine ( 2 0 0 , 120, 160, 40 ) ; gDC. drawLine ( 1 2 0 ,120, 2 0 0 , 1 2 0 ) ; gDC. drawLine ( 1 6 0 , 160, 160, 1 2 0 ) ; } }
Rysunek 7.4. Efekt działania apletu z ćwiczenia 7.6
R o z d z i a ł 7. • A p l e t y
163
CW I CZ EN I E
Rysowanie linii Korzystając z metody drawLine, napisz aplet w yśw ietlający na ekranie prostokąt (rysunek 7.5). import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . G r a p h i c s ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { p u b l i c voi d p a i n t ( Gr aphi c s gDC) { gDC. drawLine ( 1 2 0 , 40 , 2 00, 4 0 ) ; gDC. drawLine ( 2 0 0 , 40 , 2 00, 1 6 0 ) ; gDC. drawLine ( 2 0 0 , 160, 120, 1 6 0 ) ; gDC. drawLine ( 1 2 0 , 160, 120, 4 0 ) ; } }
Rysunek 7.5. Wynik działania apletu rysującego na ekranie prostokąt
Koła i elipsy można rysować za pom ocą m etod: drawOval fillOval
(int x, (int x,
i nt y , int y ,
i n t s z e r o k o ś ć , i n t w ysokość) i n t s z e r o k o ś ć , i n t w ysokość)
Pierwsza rysuje figury bez w ypełnienia, druga — z w ypełnieniem . Podane parametry określają prostokąt okalający dane koło (okrąg) lub elipsę, x i y to współrzędne lewego górnego rogu, a szerokość i wy sokość to odpowiednio szerokość i wysokość (podane w pikselach).
Java
164
• Ćwiczenia praktyczne
Ć W I C Z E N I E __________________________________________________________________________________
7.8
Rysowanie kół i elips
Napisz aplet w yśw ietlający na ekranie koła i elipsy (rysunek 7.6). import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . G r a p h i c s ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { p u b l i c voi d p a i n t ( Gr aphi c s gDC) { gDC. drawOval ( 9 0 , 3 0 , 4 0 , 4 0 ) ; gDC. drawOval ( 1 5 0 , 5 0 , 40 , 2 0 ) ; gDC. f i l l O v a l ( 9 0 , 9 0 , 3 0 , 2 0 ) ; g D C . f i l l O v a l ( l 5 0 , 8 0 , 40 , 4 0 ) ; } }
Rysunek 7.6. Aplet wyświetlający na ekranie koła i elipsy
Prostokąty m ożna w yświetlać za pom ocą metod: drawRect(int x , fi l l R e c t ( i n t x,
int y, int y,
int sz e r o k o ś ć , int sz er o k o ść ,
i n t w ysokość) i n t w ysokość)
Pierwsza rysuje figury bez w ypełnienia, druga — zw ypełnieniem (podobnie jak m iało to m iejsce w przypadku kół i elips). Podane pa rametry określają prostokąt, x i y to współrzędne lewego górnego rogu, a szerokość i wysokość to odpowiednio szerokość i w ysokość (podane w pikselach).
R o z d z i a ł 7. • A p l e t y
165
CWICZENIE
Rysowanie prostokątów Napisz aplet rysujący na ekranie kilka prostokątów, np. ustawionych tak jak na rysunku 7.7. import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . G r a p h i c s ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { p u b l i c voi d p a i n t ( Gr aphi c s gDC) { gDC. drawRect ( 1 2 0 , 40, 39) g D C . f i l l R e c t ( 1 6 0 , 40, 40) g D C. f i l l R e c t (120, 40) gDC. drawRect ( 1 6 0 , 39)
Rysunek 7.7. Aplet rysujący prostokąty
Do rysowania wielokątów służą metody: dr awPol ygon(Pol ygon p) f i l l P o l y g o n ( P o l y g o n p) dr a wP o l y gon ( i n t tab X [ ] , f i l l P o l y g o n ( i n t tab X [ ] ,
i n t ta b Y [ y ] , i n t ta b Y [ y ] ,
i n t n P o in ts) i n t n P o in ts)
Parametr p jest typu Polygon, co oznacza, że należy utworzyć obiekt ta kiego typu. Można tego dokonać za pom ocą następującego konstruk tora: Pol ygon ( i n t x P o i n t s [ ] ,
int y P o i n t s [ ] ,
i n t n P o in ts)
Tablice xPoints i yPoints muszą zawierać współrzędne kolejnych punk tów wielokąta, natomiast parametr nPoints oznacza liczbę tych punktów.
166
Java
• Ćwiczenia praktyczne
W kolejnych dwóch ćwiczeniach użyto zarówno w ersji z argumentem typu Polygon, jak i w ersji korzystających z tablic. Ć W I C Z E N I E ________________________________________________________________________________________
7.10
Rysowanie wielokątów
Napisz aplet w yśw ietlający na ekranie sześciokąt (rysunek 7.8). import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . G r a p h i c s ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { i n t t abX[ ] = { 8 0 , 120, 2 0 0 , 2 4 0 , 2 0 0 , 1 2 0 } ; i n t t a b y [ ] = { l 0 0 , 2 0 , 2 0 , 100, 180, 1 8 0 } ; p u b l i c voi d p a i n t ( Gr aphi c s gDC){ gDC. drawPolygon ( t a b X, tabY, 6 ) ; } }
Rysunek 7.8. Aplet rysujący sześciokąt
ĆWICZENIE
7.11
Rysowanie powtarzających się figur
Napisz aplet generujący na ekranie wzory widoczne na rysunku 7.9. import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { i n t t abX[ ] = { 3 0 , 40 , 6 0 , 40 , 30 , 2 0 , 0 , 2 0 , 3 0 } ; i nt taby[] = {220, 240, 250, 260, 280, 260, 250, 240, 2 20 }; p u b l i c voi d p a i n t ( Gr aphi c s gDC) { f o r ( i n t i = 0 ; i < 30 ; i + + ) { gDC. drawLine (235 + i * 3 , 2 0 , 325 - i * 3 , 2 0 0 ) ; gDC.drawOval ( 60 + i * 2, 100 - i * 2, 150 - i * 4 , 50 - i * 4 ) ; gDC.drawOval ( 360 + i * 2 , 100 - i * 2 , 150 - i * 4 , 50 - i * 4 ) ;
R o z d z i a ł 7. • A p l e t y
167
} bool ean f l a g = f a l s e ; f o r ( i n t i = 0; i < 8; i + + ) { g D C . f i l l Oval ( i * 7 0 , 3 0 0 , 5 0 , 5 0 ) ; g D C . f i l l Oval ( 50 + i * 70 , 3 0 0 , 2 0 , 5 0 ) ; i n t tempTabX[] = new i n t [ 9 ] ; f o r ( i n t j = 0; j < 9; j + + ) { t empTabX[j ] = t a b X [ j ] + i * 72; } Polygon p = new Pol ygon(tempTabX, t abY, 9 ) ; if
( f l ag ) { gDC. drawPol ygon( p) ;
} el s e{ g D C . f i l l Po l y g o n ( p ) ; } flag = !flag; } } }
Rysunek 7.9. Wzoiy generowane przez aplet z ćwiczenia 7.11
W pierwszej pętli for rysow ane są nakładające się na siebie elipsy i przecinające się odcinki. Użyto w tym celu standardowych metod drawOval i drawLine, z tym że ich param etry są w yliczane na podsta w ie w artości zm iennej iteracyjnej i . Dzięki temu otrzym ujem y figury o nieco zm ienionym kształcie i (lub) położeniu.
168
Java
• Ćwiczenia praktyczne
Druga pętla odpowiada za rysowanie dolnego wiersza kół i elips oraz w ielokątów . Koła i elipsy są rysow ane za pom ocą m etody fillO val, a przesunięcie figur jest uzyskiwane również dzięki uwzględnieniu w artości zm iennej i w w artościach parametrów metody. Nieco ina czej jest w przypadku wielokątów. W spółrzędne figury bazowej zo stały opisane w tablicach tabX i tabY. W spółrzędne y pozostają takie same dla wszystkich kopii, natomiast współrzędne x m uszą być prze suwane w prawo, czyli zw iększane. Każdy w ielokąt jest odsuwany o 72 (i * 72) piksele od poprzednika, a jego współrzędne x są zapisy wane w tablicy pom ocniczej tempTabX. Tablic tempTabX i tabY użyto do skonstruowania obiektu typu Polygon, który jest następnie stosowany jako argument metody drawPolygon lub fillPolygon. O tym, która z metod zostanie użyta, decyduje stan zm iennej flag. Gdy jest równa true, rysowany jest tylko obrys w ielokąta, a gdy jest równa false, rysowany jest wielokąt z wypełnionym tłem. Stan zmien nej jest zm ieniany na przeciwny w każdym przebiegu pętli — od powiada za to instrukcja flag = !fla g ;. D zięki temu uzyskujem y na przemiennie w ypełnienie i obrys.
Kolory Do ustalania koloru używamy metody setColor z klasy Graphics, która przyjm uje jako argument obiekt typu Color. Obiekty Color służą do reprezentacji kolorów w form acie RGB. Form at ten zakłada, że każdy kolor jest opisany przez trzy składowe: czerw oną (ang. Red), zieloną (ang. Green) i niebieską (ang. Blue). Każda ze składowych może przyj mować w artości od 0 do 255. W ynika z tego, że pojedynczy kolor jest opisywany przez 24 bity, a m aksym alna ich liczba w ynosi 224, czyli 16 777 216. Kolor czarny to składowe R = 0, G = 0, B = 0, natomiast biały to R = 255, G = 255, B = 255. Składowe RGB dla niektórych wy branych kolorów przedstawiono w tabeli 7.1. Obiekt typu Color m ożemy utworzyć, podając w konstruktorze liczbę całkowitą typu int. Składową R specyfikują bity 16 - 23 tej liczby, składową G — bity 8 - 15, składową B — bity 0 - 7. M ożna również użyć konstruktora przyjmującego jako parametry trzy liczby całkowite odpowiadające poszczególnym składowym.
R o z d z i a ł 7. • A p l e t y
169
Tabela 7.1. Składowe RGB dla wybranych kolorów
Kolor
Składowa R
Składowa G
Składowa B
beżowy
245
245
220
biały
255
255
255
błękitny
0
191
255
brązowy
139
69
19
czarny
0
0
0
czerwony
255
0
0
ciemnoczerwony
139
0
0
ciemnoniebieski
0
0
139
ciemnoszary
169
169
169
ciemnozielony
0
100
0
fioletowy
238
130
238
koralowy
255
127
80
niebieski
0
0
255
oliwkowy
107
142
35
purpurowy
160
32
240
srebrny
192
192
192
stalowoniebieski
70
130
180
szary
128
128
128
zielony
0
255
0
żółtozielony
195
205
50
żółty
255
255
0
Metody getRed, getGreen, getBlue pozw alają na pobranie oddzielnie każdej składowej, natom iast m etoda getRGB na pobranie liczby typu int reprezentującej kolor. Znaczenie poszczególnych bitów jest takie samo, jak opisane wyżej. Klasa Color posiada również predefiniow a ne pola statyczne odzw ierciedlające niektóre kolory. S ą to black
170
Java
• Ćwiczenia praktyczne
(czarny), blue (niebieski), cyjan (cyjan), darkGray (ciemnoszary), grey (szary), green (zielony), lightGray (jasnoszary), magenta (magenta), orange (pomarańczowy), pink (różowy), red (czerwony), white (biały) i yellow (żółty). Te w iadom ości w ystarczą nam już do urozm aicenia grafiki z poprzednich ćw iczeń. Ć W I C Z E N I E ________________________________________________________________________________________
7.12
Kolorowy wielokąt
Napisz aplet rysujący na ekranie sześciokąt wypełniony kolorem czer wonym. import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { i n t t abX[ ] = {BO, 120, 2 0 0 , 2 4 0 , 2 0 0 , 1 2 0 } ; i n t t abY[ ] = { 1 0 0 , 2 0 , 2 0 , 100, 1B0, 1B0} ; p u b l i c voi d p a i n t ( Gr aphi c s gDC) { gDC.setColor(Color.red); g D C . f i l l P o l y g o n ( t a b X, tabY, 6 ) ; } } Ć W I C Z E N I E ___________________________________________________________________________________
7.13
Kolorowanie figur
„Pokoloruj” wzory generowane przez aplet z ćw iczenia 7.11. import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { i n t t abX[ ] = { 3 0 , 40 , 6 0 , 40 , 30 , 2 0 , 0 , 2 0 , 3 0 } ; i n t t abY[ ] = { 2 2 0 , 2 4 0 , 2 5 0 , 2 6 0 , 2B0, 2 6 0 , 2 5 0 , 2 4 0 , 2 2 0 } ; p u b l i c voi d p a i n t ( Gr aphi c s gDC) { Co l o r c o l o r 1 = C o l o r . r e d ; f o r ( i n t i = 0 ; i < 30 ; í + + ) { g DC. s e t Co l o r ( c o l o r 1 ) ; c o l o r 1 = new Co l o r ( c o l o r 1 . g e t R e d ( ) - B, c o l o r 1 . g e t G r e e n ( ) , color1.getBlue()); gDC. drawLine (235 ł i * 3 , 2 0 , 325 - i * 3 , 2 0 0 ) ; gDC. setCol o r ( C o l o r . g r e e n ) ; gDC.drawOval ( 60 ł i * 2, 100 - i * 2, 150 - i * 4 , 50 - i * 4 ) ; gDC. setCol o r ( C o l o r . b l u e ) ; gDC.drawOval ( 360 ł i * 2 , 100 - i * 2 , 150 - i * 4 , 50 - i * 4 ) ;
}
R o z d z i a ł 7. • A p l e t y
171
bool ean f l a g = f a l s e ; Co l o r c o l o r 2 = C o l o r . g r e e n ; Co l o r c o l o r 3 = C o l o r . b l u e ; f o r ( i n t i = 0; i < 8; i + + ) { gDC.setColor(color2); c o l o r 2 = new Co l o r ( c o l o r 2 . g e t R e d ( ) , c o l o r 2 . g e t G r e e n ( ) color2.getBlue() + 20); g D C . f i l l Oval g D C . f i l l Oval
(i * 70, 300, ( 50 + i * 7 0 ,
50, 5 0 ); 3 0 0 , 20 ,
- 20,
50);
i n t tempTabX[] = new i n t [ 9 ] ; f o r ( i n t j = 0; j < 9; j + + ) { t empTabX[j ] = t a b X [ j ] + i * 72; } Polygon p = new Pol ygon(tempTabX, t abY, 9 ) ; g DC. s e t Co l o r ( c o l o r 3 ) ; c o l o r 3 = new Co l o r ( c o l o r 3 . g e t R e d ( ) + 25 , c o l o r 3 . g e t G r e e n ( ) , color3.getBlue() - 25); if
( f l ag ) { gDC. drawPol ygon( p) ;
} el s e{ gDC.fillPolygon(p); } f l a g = ! fl a g ; } } }
Sposób generowania figur jest taki sam jak w ćw iczeniu 7.11, zostały jednak dodane instrukcje zm ieniające kolory. W szystkie odcienie są tworzone na bazie czerwonego (Color.red), niebieskiego (Color.blue) i zielonego (Color.green). Górne elipsy są w ypełniane jednolitym i od cieniam i: zielonym i niebieskim . Kolory odcinków są już m odyfiko wane. Składowa R (czerwona) każdej kolejnej lin ii jest zm niejszana o 8, przy niezm ienionych składowych G (zielona) i B (niebieska). Po nieważ początkowym kolorem był czerwony, w rezultacie otrzymu jemy odcienie od czerwonego do czarnego. Na takiej samej zasadzie tworzone są różne odcienie dla dolnego rzędu kół i elips oraz dla wielokątów. M odyfikowane są tylko inne składowe. Dla kół są to składowe G (zwiększana o 20) i B (zmniejszana o 20), a dla wielokątów — składowe R (zwiększana o 25) i B (zm niej szana o 25). M ożna sam odzielnie poeksperym entow ać z innym i
172
Java
• Ćwiczenia praktyczne
modyfikacjami składowych czy też z ustawieniami kolorów początko wych, pam iętając jednak, że każda ze składowych może się zm ieniać w yłącznie w przedziale od 0 do 255.
Wyświetlanie obrazów Umiemy już rysować proste figury geometryczne, nadszedł w ięc czas, aby wyświetlić na ekranie grafikę zawartą w pliku. Klasa JApplet na szczęście zawiera odpowiednie metody, nie jest to w ięc bardzo skomplikowane zadanie. Do wczytywania obrazów służy m etoda getlmage, której — jako parametr — podajemy lokalizację danego pliku graficznego. Standardowo obsługiwane są tylko trzy formaty plików graficznych: GIF, JPG i PNG. Zakładając, że w katalogu, w którym jest um ieszczony dokument HTML zawierający aplet, znajduje się np. plik obrazek.jpg, możemy w czytać go tak: Image image = ge t l mage ( g e t Do c ume n t Ba s e ( ) ,
"obrazek.jpg")
Jeśli chcem y sam odzielnie podać pełną ścieżkę dostępu do pliku, możemy też użyć konstrukcji: Image image = ge t l mage ( " h t t p : / / a d r e s . d o m e n y / k a t a l o g / o b r a z e k . j p g " )
W pierwszym przypadku do uzyskania ścieżki dostępu (w postaci adresu URL), w której znajduje się dokument z apletem , została użyta metoda getDocumentBase() z klasy JApplet. W obu przypadkach jako rezultat otrzymujemy referencję do nowego obiektu klasy Image. Reprezentuje on dane graficzne, o ile tylko we wskazanej lokalizacji był dostępny prawidłowy plik graficzny. Gdy mamy odwołanie do obiektu obrazu, m ożna go wyśw ietlić na ekra nie. Wystarczy posłużyć się metodą drawImage z klasy Graphics. Należy pam iętać, że obraz nie jest gotowy do w yśw ietlenia od razu po wy wołaniu metody getImage — dane zostaną pobrane dopiero przy próbie wyświetlenia.
R o z d z i a ł 7. • A p l e t y
173
Ć W I C Z E N I E ________________________________________________________________________________________
7.14
W yświetlanie zaw artości pliku graficznego
Napisz aplet wyświetlający na ekranie plik graficzny, np. obraz typu jp g (rysunek 7.10). import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { Image img; p u b l i c voi d i n i t () { img = ge t I ma g e ( g e t Do c u me n t B a s e ( ) , " o b r a z e k . j p g " ) ; } p u b l i c voi d p a i n t
( Gr aphi c s gDC)
gDC.drawImage (img, 0,
0,
{
this);
} }
Rysunek 7.10. Aplet wyświetlający obraz pobrany z pliku graficznego
Obraz jest reprezentowany przez pole img typu Image. Obiekt img two rzony jest z kolei w metodzie init, czyli w trakcie inicjacji apletu. Uży to dwuargumentowej metody getlmage. Zgodnie z wcześniejszym opi sem pierwszy argument wskazuje lokalizację, w której znajdują się pliki z kodem HTML zawierającym aplet (rezultat w yw ołania metody getDocumentBase)3, a drugi — nazwę pliku z grafiką. 3 Alternatywnie można użyć metody getCodeBase — zwraca ona obiekt URL zawierający adres, pod którym znajduje się kod klasy apletu.
Java
174
• Ćwiczenia praktyczne
Metoda drawImage przyjm uje cztery argumenty. Pierwszy to referencja do obiektu typu Image. Dwa następne to współrzędne, od których za cznie się wyświetlanie obrazka. Jako ostatni parametr występuje słowo this, czyli wskazanie na obiekt bieżący (w tym przypadku obiekt klasy MyApplet, która dziedziczy po JApplet).
Czwarty argument metody drawImage nie m usi być obiektem apletu. W rzeczywistości powinna to być referencja do obiektu klasy im ple mentującej interfejs ImageObserver. Niestety, w książce nie m a m iejsca na omówienie interfejsów i klas interfejsow ych. W ażne jest jednak, że jeżeli obiekt implementuje interfejs ImageObserver (a tak jest w przy padku apletów), to w trakcie napływu danych z obrazem wywoływa na jest metoda imageUpdate tego obiektu. To pozwala śledzić postępy ła dowania grafiki. Ta w łaściw ość zostanie w ykorzystana w kolejnym ćw iczeniu. Ć W I C Z EN I E
________Śledzenie postępów ładow ania obrazu Napisz aplet w yśw ietlający na ekranie plik graficzny. Podczas w czy tywania obrazka na pasku stanu przeglądarki pow inien być wyświe tlany napis Trwa ładowanie obrazu..., a po zakończeniu tego procesu — napis Obraz załadowany!. import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; import j a v a . a w t . i m a g e . * ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { Image img; publ i c voi d i n i t ( ) { img = ge t I ma g e ( g e t Do c u me n t B a s e ( ) ,
"obrazek.jpg");
} p u b l i c voi d p a i n t ( Gr aphi c s gDC){ gDC.drawImage (img, 0, 0, t h i s ) ; } p u b l i c bool ean i mageUpdate(Image img, i n t f l a g s , i n t y , i n t wi dt h, i n t h e i g h t ) { i f ( ( f l a g s & ImageObserver. ALLBI TS) == 0 ) { showSt at us ("Trwa ł adowani e o b r a z u . . . " ) ; return true;
i nt x,
R o z d z i a ł 7. • A p l e t y
175
} el s e{ showSt at us ("Obraz z a ł a d o w a n y ! " ) ; repaint(); return f a l s e ; } } }
Metody in it i paint pozostały tu w formie znanej z ćw iczenia 7.14. Za aktualizację statusu odpowiada z kolei metoda imageUpdate. Zgod nie z podanym w cześniej opisem będzie ona wywoływana w trakcie ładowania obrazu (w trakcie napływ ania danych obrazu z sieci). Przyjm uje ona sześć argumentów: img (referencja do rysowanego ob razu), x i y (współrzędne pow ierzchni apletu, w których rozpoczyna się obraz), width i height (wysokość i szerokość obrazu) oraz flags (war tość całkowita inform ująca o aktualnym stanie obrazu). Obraz jest całkow icie załadowany, gdy wynik operacji bitowej flags & ImageObserver.ALLBITS jest różny od 0. Jest to badane w instrukcji warunkowej if. W zależności od wyniku tej operacji zm ieniany jest napis na pasku stanu. W tym celu używa się metody setStatus po chodzącej z klasy JApplet. Po załadowaniu obrazu wywoływana jest metoda repaint(), dzięki której obszar apletu zostanie odświeżony. Należy zw rócić uwagę, że gdy obraz nie jest jeszcze gotowy (czyli konieczne są doładowania danych), m etoda imageUpdate m a zwrócić wartość true, a jeśli jest gotowy — w artość false. Dzięki tak działającemu apletowi w przypadku ładowania dużej grafiki (co może zająć sporo czasu) użytkownik będzie inform ow any o tym, że ten proces trwa i należy poczekać na jego zakończenie.
Na koniec spójrzmy na rysunek 7.11. Zawiera on prostokąt z płynnie zmieniającymi się kolorami (z oczyw istych względów w książce w i doczne są tylko odcienie szarości). Jest to również obraz rysowany za pom ocą m etody drawImage. Nie jest jed nak stworzony w programie graficznym, ale jest generowany w pam ięci komputera. Sposób jego wykonania został podany w treści ćw iczenia 7.16.
176
Java
• Ćwiczenia praktyczne
Rysunek 7.11. Programowo wygenerowana paleta kolorów
A p p le t started.
Ć W I C Z E N I E _______________________________________________________________________
Programowe generowanie obrazów
7.16
Wygeneruj programowo obraz w idoczny na rysunku 7.11. import j a v a x . s w i n g . J A p p l e t ; import j a v a . a w t . * ; import j a v a . a w t . i m a g e . * ; p u b l i c c l a s s MyApplet e x t e n ds J Ap p l e t { Image img; p u b l i c voi d i n i t () { prepareImage(); } p u b l i c voi d p a i n t ( Gr aphi c s gDC) { gDC.drawImage (img, 7 2 , 22 , t h i s ) ; } p u b l i c voi d pr e pa r e I ma ge ( ) { i n t wi dth = 2 5 5 , h e i g h t = 2 5 5 ; i n t p i x [ ] = new i n t [255 * 2 5 5 ] ; i n t i ndex = 0; f o r ( i n t i = 0; i < 255; i + + ) { f o r ( i n t j = 0; j < 255; j + + ) { p i x [ i n d e x + + ] = (255