You are on page 1of 862

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.

pl

Tytu oryginau: Core Java Volume I--Fundamentals (9th Edition)


Tumaczenie: ukasz Piwko
ISBN: 978-83-246-7761-0
Authorized translation from the English language edition, entitled CORE JAVA
VOLUME I FUNDAMENTALS, 9TH EDITION; ISBN 0137081898; by Cay S. Horstmann;
and Gary Cornell; published by Pearson Education, Inc, publishing as Prentice Hall.
Copyright 2013 by Oracle and/or its affiliates, 500 Oracle Parkway, Redwood Shores, CA 94065.
All rights reserved. No part of this book may be reproduced or transmitted in any form
or by any means, electronic or mechanical, including photocopying, recording or by any
information storage retrieval system, without permission from Pearson Education Inc.
Polish language edition published by HELION S.A. Copyright 2013.
Wszelkie prawa zastrzeone. Nieautoryzowane rozpowszechnianie caoci
lub fragmentu niniejszej publikacji w jakiejkolwiek postaci jest zabronione.
Wykonywanie kopii metod kserograficzn, fotograficzn, a take kopiowanie
ksiki na noniku filmowym, magnetycznym lub innym powoduje naruszenie
praw autorskich niniejszej publikacji.
Wszystkie znaki wystpujce w tekcie s zastrzeonymi znakami firmowymi
bd towarowymi ich wacicieli.
Wydawnictwo HELION dooyo wszelkich stara, by zawarte w tej ksice
informacje byy kompletne i rzetelne. Nie bierze jednak adnej odpowiedzialnoci
ani za ich wykorzystanie, ani za zwizane z tym ewentualne naruszenie praw patentowych
lub autorskich. Wydawnictwo HELION nie ponosi rwnie adnej odpowiedzialnoci
za ewentualne szkody wynike z wykorzystania informacji zawartych w ksice.
Wydawnictwo HELION
ul. Kociuszki 1c, 44-100 GLIWICE
tel. 32 231 22 19, 32 230 98 63
e-mail: helion@helion.pl
WWW: http://helion.pl (ksigarnia internetowa, katalog ksiek)
Pliki z przykadami omawianymi w ksice mona znale pod adresem:
ftp://ftp.helion.pl/przyklady/javpd9.zip
Drogi Czytelniku!
Jeeli chcesz oceni t ksik, zajrzyj pod adres
http://helion.pl/user/opinie/javpd9_ebook
Moesz tam wpisa swoje uwagi, spostrzeenia, recenzj.

Pole ksik na Facebook.com


Kup w wersji papierowej
Oce ksik

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Ksigarnia internetowa
Lubi to! Nasza spoeczno

Spis treci
Wstp ........................................................................................................................................................13
Podzikowania .........................................................................................................................................19
Rozdzia 1. Wstp do Javy ........................................................................................................................21
1.1.
1.2.

1.3.
1.4.
1.5.

Java jako platforma programistyczna ................................................................... 21


Sowa klucze biaej ksigi Javy ............................................................................ 22
1.2.1. Prosty .................................................................................................. 23
1.2.2. Obiektowy ............................................................................................ 24
1.2.3. Sieciowy .............................................................................................. 24
1.2.4. Niezawodny .......................................................................................... 24
1.2.5. Bezpieczny ........................................................................................... 25
1.2.6. Niezaleny od architektury ..................................................................... 26
1.2.7. Przenony ............................................................................................ 26
1.2.8. Interpretowany ..................................................................................... 27
1.2.9. Wysokowydajny ..................................................................................... 27
1.2.10. Wielowtkowy ....................................................................................... 28
1.2.11. Dynamiczny .......................................................................................... 28
Aplety Javy i internet .......................................................................................... 28
Krtka historia Javy ............................................................................................ 30
Gwne nieporozumienia dotyczce Javy .............................................................. 32

Rozdzia 2. rodowisko programistyczne Javy ................................................................................... 37


2.1.

2.2.
2.3.
2.4.

Instalacja oprogramowania Java Development Kit ................................................. 38


2.1.1. Pobieranie pakietu JDK ......................................................................... 38
2.1.2. Ustawianie cieki dostpu ................................................................... 39
2.1.3. Instalacja bibliotek i dokumentacji ......................................................... 41
2.1.4. Instalacja przykadowych programw ...................................................... 42
2.1.5. Drzewo katalogw Javy .......................................................................... 42
Wybr rodowiska programistycznego .................................................................. 43
Uywanie narzdzi wiersza polece ...................................................................... 44
2.3.1. Rozwizywanie problemw ..................................................................... 45
Praca w zintegrowanym rodowisku programistycznym .......................................... 47
2.4.1. Znajdowanie bdw kompilacji .............................................................. 49

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Java. Podstawy
2.5.
2.6.

Uruchamianie aplikacji graficznej ......................................................................... 50


Tworzenie i uruchamianie apletw ....................................................................... 53

Rozdzia 3. Podstawowe elementy jzyka Java .................................................................................... 57


3.1.
3.2.
3.3.

Prosty program w Javie ....................................................................................... 58


Komentarze ....................................................................................................... 61
Typy danych ...................................................................................................... 62
3.3.1. Typy cakowite ...................................................................................... 62
3.3.2. Typy zmiennoprzecinkowe ...................................................................... 63
3.3.3. Typ char ............................................................................................... 65
3.3.4. Typ boolean ......................................................................................... 66
3.4. Zmienne ........................................................................................................... 66
3.4.1. Inicjacja zmiennych ............................................................................... 68
3.4.2. Stae ................................................................................................... 68
3.5. Operatory .......................................................................................................... 69
3.5.1. Operatory inkrementacji i dekrementacji ................................................. 71
3.5.2. Operatory relacyjne i logiczne ................................................................. 71
3.5.3. Operatory bitowe .................................................................................. 72
3.5.4. Funkcje i stae matematyczne ................................................................ 73
3.5.5. Konwersja typw numerycznych ............................................................. 74
3.5.6. Rzutowanie .......................................................................................... 75
3.5.7. Nawiasy i priorytety operatorw .............................................................. 76
3.5.8. Typ wyliczeniowy ................................................................................... 77
3.6. acuchy .......................................................................................................... 77
3.6.1. Podacuchy ......................................................................................... 78
3.6.2. Konkatenacja ....................................................................................... 78
3.6.3. acuchw nie mona modyfikowa ....................................................... 79
3.6.4. Porwnywanie acuchw ...................................................................... 79
3.6.5. acuchy puste i acuchy null .............................................................. 81
3.6.6. Wsprzdne kodowe znakw i jednostki kodowe .................................... 81
3.6.7. API String ............................................................................................. 83
3.6.8. Dokumentacja API w internecie .............................................................. 85
3.6.9. Skadanie acuchw ............................................................................ 86
3.7. Wejcie i wyjcie ................................................................................................ 89
3.7.1. Odbieranie danych wejciowych ............................................................. 89
3.7.2. Formatowanie danych wyjciowych ......................................................... 91
3.7.3. Zapis i odczyt plikw ............................................................................. 96
3.8. Przepyw sterowania ........................................................................................... 98
3.8.1. Zasig blokowy ..................................................................................... 98
3.8.2. Instrukcje warunkowe ............................................................................ 99
3.8.3. Ptle ................................................................................................. 101
3.8.4. Ptle o okrelonej liczbie powtrze ..................................................... 106
3.8.5. Wybr wielokierunkowy instrukcja switch .......................................... 109
3.8.6. Instrukcje przerywajce przepyw sterowania ......................................... 111
3.9. Wielkie liczby ................................................................................................... 114
3.10. Tablice ............................................................................................................ 116
3.10.1. Ptla typu for each .............................................................................. 117
3.10.2. Inicjowanie tablic i tworzenie tablic anonimowych .................................. 118
3.10.3. Kopiowanie tablicy .............................................................................. 119
3.10.4. Parametry wiersza polece .................................................................. 120
3.10.5. Sortowanie tablicy .............................................................................. 121

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Spis treci

3.10.6. Tablice wielowymiarowe ...................................................................... 124


3.10.7. Tablice postrzpione ........................................................................... 127

Rozdzia 4. Obiekty i klasy ......................................................................................................................131


4.1.

4.2.

4.3.

4.4.

4.5.
4.6.

4.7.

4.8.
4.9.

Wstp do programowania obiektowego .............................................................. 132


4.1.1. Klasy ................................................................................................. 132
4.1.2. Obiekty .............................................................................................. 133
4.1.3. Identyfikacja klas ................................................................................ 134
4.1.4. Relacje midzy klasami ....................................................................... 135
Uywanie klas predefiniowanych ........................................................................ 137
4.2.1. Obiekty i zmienne obiektw ................................................................. 137
4.2.2. Klasa GregorianCalendar ..................................................................... 139
4.2.3. Metody udostpniajce i zmieniajce warto elementu ........................ 141
Definiowanie wasnych klas .............................................................................. 148
4.3.1. Klasa Employee .................................................................................. 148
4.3.2. Uywanie wielu plikw rdowych ........................................................ 151
4.3.3. Analiza klasy Employee ....................................................................... 151
4.3.4. Pierwsze kroki w tworzeniu konstruktorw ............................................. 152
4.3.5. Parametry jawne i niejawne ................................................................. 153
4.3.6. Korzyci z hermetyzacji ........................................................................ 155
4.3.7. Przywileje klasowe .............................................................................. 157
4.3.8. Metody prywatne ................................................................................ 157
4.3.9. Stae jako pola klasy ........................................................................... 158
Pola i metody statyczne .................................................................................... 158
4.4.1. Pola statyczne .................................................................................... 159
4.4.2. Stae statyczne ................................................................................... 159
4.4.3. Metody statyczne ................................................................................ 160
4.4.4. Metody fabryczne ................................................................................ 161
4.4.5. Metoda main ...................................................................................... 162
Parametry metod ............................................................................................. 164
Konstruowanie obiektw .................................................................................. 171
4.6.1. Przecianie ....................................................................................... 171
4.6.2. Inicjacja pl wartociami domylnymi ................................................... 171
4.6.3. Konstruktor bezargumentowy ............................................................... 172
4.6.4. Jawna inicjacja pl .............................................................................. 172
4.6.5. Nazywanie parametrw ....................................................................... 174
4.6.6. Wywoywanie innego konstruktora ........................................................ 174
4.6.7. Bloki inicjujce ................................................................................... 175
4.6.8. Niszczenie obiektw i metoda finalize ................................................... 179
Pakiety ............................................................................................................ 180
4.7.1. Importowanie klas .............................................................................. 180
4.7.2. Importy statyczne ............................................................................... 182
4.7.3. Dodawanie klasy do pakietu ................................................................ 182
4.7.4. Zasig pakietw ................................................................................. 185
cieka klas .................................................................................................... 187
4.8.1. Ustawianie cieki klas ....................................................................... 189
Komentarze dokumentacyjne ............................................................................ 190
4.9.1. Wstawianie komentarzy ....................................................................... 190
4.9.2. Komentarze do klas ............................................................................ 191
4.9.3. Komentarze do metod ......................................................................... 191
4.9.4. Komentarze do pl ............................................................................. 192

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Java. Podstawy
4.9.5. Komentarze oglne ............................................................................. 192
4.9.6. Komentarze do pakietw i oglne ........................................................ 194
4.9.7. Generowanie dokumentacji .................................................................. 194
4.10. Porady dotyczce projektowania klas ................................................................. 195

Rozdzia 5. Dziedziczenie .......................................................................................................................199


5.1.

5.2.

5.3.

5.4.
5.5.
5.6.
5.7.

5.8.

Klasy, nadklasy i podklasy ................................................................................ 200


5.1.1. Hierarchia dziedziczenia ...................................................................... 206
5.1.2. Polimorfizm ........................................................................................ 207
5.1.3. Wizanie dynamiczne .......................................................................... 209
5.1.4. Wyczanie dziedziczenia klasy i metody finalne ................................ 211
5.1.5. Rzutowanie ........................................................................................ 212
5.1.6. Klasy abstrakcyjne .............................................................................. 214
5.1.7. Ochrona dostpu ................................................................................ 219
Klasa bazowa Object ........................................................................................ 220
5.2.1. Metoda equals ................................................................................... 221
5.2.2. Porwnywanie a dziedziczenie .............................................................. 222
5.2.3. Metoda hashCode .............................................................................. 225
5.2.4. Metoda toString ................................................................................. 228
Generyczne listy tablicowe ................................................................................ 233
5.3.1. Dostp do elementw listy tablicowej ................................................... 236
5.3.2. Zgodno pomidzy typowanymi a surowymi listami tablicowymi ............. 239
Osony obiektw i autoboxing ............................................................................ 241
Metody ze zmienn liczb parametrw ............................................................... 244
Klasy wyliczeniowe ........................................................................................... 245
Refleksja ......................................................................................................... 247
5.7.1. Klasa Class ....................................................................................... 248
5.7.2. Podstawy przechwytywania wyjtkw .................................................... 250
5.7.3. Zastosowanie refleksji w analizie funkcjonalnoci klasy ......................... 252
5.7.4. Refleksja w analizie obiektw w czasie dziaania programu .................... 257
5.7.5. Zastosowanie refleksji w generycznym kodzie tablicowym ...................... 261
5.7.6. Wywoywanie dowolnych metod ............................................................ 264
Porady projektowe dotyczce dziedziczenia ........................................................ 268

Rozdzia 6. Interfejsy i klasy wewntrzne ...........................................................................................271


6.1.

6.2.
6.3.
6.4.

6.5.

Interfejsy ......................................................................................................... 272


6.1.1. Wasnoci interfejsw ......................................................................... 276
6.1.2. Interfejsy a klasy abstrakcyjne ............................................................. 279
Klonowanie obiektw ....................................................................................... 280
Interfejsy a sprzenie zwrotne ......................................................................... 286
Klasy wewntrzne ............................................................................................ 289
6.4.1. Dostp do stanu obiektu w klasie wewntrznej ..................................... 289
6.4.2. Specjalne reguy skadniowe dotyczce klas wewntrznych ..................... 293
6.4.3. Czy klasy wewntrzne s potrzebne i bezpieczne? ................................. 294
6.4.4. Lokalne klasy wewntrzne ................................................................... 296
6.4.5. Dostp do zmiennych finalnych z metod zewntrznych ........................... 297
6.4.6. Anonimowe klasy wewntrzne .............................................................. 300
6.4.7. Statyczne klasy wewntrzne ................................................................ 303
Klasy proxy ...................................................................................................... 306
6.5.1. Wasnoci klas proxy .......................................................................... 311

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Spis treci

Rozdzia 7. Grafika .................................................................................................................................313


7.1.
7.2.
7.3.

7.4.
7.5.
7.6.
7.7.
7.8.

Wprowadzenie do pakietu Swing ....................................................................... 314


Tworzenie ramki ............................................................................................... 318
Pozycjonowanie ramki ...................................................................................... 321
7.3.1. Wasnoci ramek ................................................................................ 322
7.3.2. Okrelanie rozmiaru ramki ................................................................... 323
Wywietlanie informacji w komponencie ............................................................ 327
Figury 2D ........................................................................................................ 332
Kolory ............................................................................................................. 340
Czcionki .......................................................................................................... 343
Wywietlanie obrazw ...................................................................................... 351

Rozdzia 8. Obsuga zdarze .................................................................................................................355


8.1.

8.2.
8.3.
8.4.

Podstawy obsugi zdarze ................................................................................. 355


8.1.1. Przykad obsuga kliknicia przycisku ............................................... 357
8.1.2. Nabywanie biegoci w posugiwaniu si klasami wewntrznymi .............. 362
8.1.3. Tworzenie suchaczy zawierajcych jedno wywoanie metody ................... 364
8.1.4. Przykad zmiana stylu ..................................................................... 366
8.1.5. Klasy adaptacyjne ............................................................................... 369
Akcje .............................................................................................................. 373
Zdarzenia generowane przez mysz ..................................................................... 380
Hierarchia zdarze w bibliotece AWT .................................................................. 387
8.4.1. Zdarzenia semantyczne i niskiego poziomu ........................................... 388

Rozdzia 9. Komponenty Swing interfejsu uytkownika ......................................................................391


9.1.

9.2.

9.3.

9.4.

9.5.

Swing a wzorzec projektowy Model-View-Controller .............................................. 392


9.1.1. Wzorce projektowe .............................................................................. 392
9.1.2. Wzorzec Model-View-Controller ............................................................. 393
9.1.3. Analiza MVC przyciskw Swing ............................................................. 397
Wprowadzenie do zarzdzania rozkadem ........................................................... 398
9.2.1. Rozkad brzegowy ............................................................................... 400
9.2.2. Rozkad siatkowy ................................................................................ 402
Wprowadzanie tekstu ....................................................................................... 406
9.3.1. Pola tekstowe .................................................................................... 406
9.3.2. Etykiety komponentw ........................................................................ 408
9.3.3. Pola hase .......................................................................................... 410
9.3.4. Obszary tekstowe ............................................................................... 410
9.3.5. Panele przewijane ............................................................................... 411
Komponenty umoliwiajce wybr opcji .............................................................. 413
9.4.1. Pola wyboru ....................................................................................... 413
9.4.2. Przeczniki ........................................................................................ 415
9.4.3. Obramowanie ..................................................................................... 419
9.4.4. Listy rozwijalne ................................................................................... 423
9.4.5. Suwaki .............................................................................................. 426
Menu .............................................................................................................. 432
9.5.1. Tworzenie menu ................................................................................. 432
9.5.2. Ikony w elementach menu ................................................................... 435
9.5.3. Pola wyboru i przeczniki jako elementy menu ...................................... 436
9.5.4. Menu podrczne ................................................................................. 437
9.5.5. Mnemoniki i akceleratory .................................................................... 438

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Java. Podstawy

9.6.

9.7.

9.5.6. Aktywowanie i dezaktywowanie elementw menu .................................. 440


9.5.7. Paski narzdzi .................................................................................... 444
9.5.8. Dymki ................................................................................................ 446
Zaawansowane techniki zarzdzania rozkadem .................................................. 448
9.6.1. Rozkad GridBagLayout ....................................................................... 449
9.6.2. Rozkad grupowy ................................................................................. 459
9.6.3. Nieuywanie adnego zarzdcy rozkadu ................................................ 468
9.6.4. Niestandardowi zarzdcy rozkadu ........................................................ 469
9.6.5. Kolejka dostpu ................................................................................. 472
Okna dialogowe ............................................................................................... 474
9.7.1. Okna dialogowe opcji .......................................................................... 474
9.7.2. Tworzenie okien dialogowych ............................................................... 484
9.7.3. Wymiana danych ................................................................................. 489
9.7.4. Okna dialogowe wyboru plikw ............................................................. 495
9.7.5. Okna dialogowe wyboru kolorw ........................................................... 505

Rozdzia 10. Przygotowywanie apletw i aplikacji do uytku ..............................................................511


10.1. Pliki JAR .......................................................................................................... 512
10.1.1. Manifest ............................................................................................ 512
10.1.2. Wykonywalne pliki JAR ........................................................................ 514
10.1.3. Zasoby .............................................................................................. 515
10.1.4. Piecztowanie pakietw ...................................................................... 518
10.2. Java Web Start ................................................................................................ 519
10.2.1. Piaskownica ....................................................................................... 522
10.2.2. Podpisywanie kodu ............................................................................. 523
10.2.3. API JNLP ............................................................................................ 525
10.3. Aplety ............................................................................................................. 533
10.3.1. Prosty aplet ........................................................................................ 533
10.3.2. Znacznik applet i jego atrybuty ............................................................. 537
10.3.3. Znacznik object .................................................................................. 540
10.3.4. Parametry przekazujce informacje do apletw ...................................... 541
10.3.5. Dostp do obrazw i plikw audio ........................................................ 546
10.3.6. rodowisko dziaania apletu ................................................................ 547
10.4. Zapisywanie preferencji uytkownika .................................................................. 549
10.4.1. Mapy wasnoci .................................................................................. 550
10.4.2. API Preferences .................................................................................. 555

Rozdzia 11. Wyjtki, dzienniki, asercje i debugowanie .......................................................................563


11.1. Obsuga bdw ............................................................................................... 564
11.1.1. Klasyfikacja wyjtkw .......................................................................... 565
11.1.2. Deklarowanie wyjtkw kontrolowanych ................................................ 567
11.1.3. Zgaszanie wyjtkw ........................................................................... 569
11.1.4. Tworzenie klas wyjtkw ...................................................................... 570
11.2. Przechwytywanie wyjtkw ................................................................................ 571
11.2.1. Przechwytywanie wielu typw wyjtkw ................................................. 574
11.2.2. Powtrne generowanie wyjtkw i budowanie acuchw wyjtkw .......... 575
11.2.3. Klauzula finally ................................................................................... 576
11.2.4. Instrukcja try z zasobami ..................................................................... 580
11.2.5. Analiza danych ze ledzenia stosu ....................................................... 581
11.3. Wskazwki dotyczce stosowania wyjtkw ....................................................... 584

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Spis treci

11.4. Asercje ........................................................................................................... 587


11.4.1. Wczanie i wyczanie asercji ............................................................. 588
11.4.2. Zastosowanie asercji do sprawdzania parametrw ................................ 589
11.4.3. Zastosowanie asercji do dokumentowania zaoe ................................ 590
11.5. Dzienniki ......................................................................................................... 591
11.5.1. Podstawy zapisu do dziennika .............................................................. 592
11.5.2. Zaawansowane techniki zapisu do dziennika ......................................... 592
11.5.3. Zmiana konfiguracji menedera dziennikw ........................................... 594
11.5.4. Lokalizacja ......................................................................................... 596
11.5.5. Obiekty typu Handler ........................................................................... 596
11.5.6. Filtry .................................................................................................. 600
11.5.7. Formatery .......................................................................................... 600
11.5.8. Przepis na dziennik ............................................................................. 601
11.6. Wskazwki dotyczce debugowania ................................................................... 609
11.7. Wskazwki dotyczce debugowania aplikacji z GUI ............................................. 614
11.7.1. Zaprzganie robota AWT do pracy ........................................................ 617
11.8. Praca z debugerem .......................................................................................... 621

Rozdzia 12. Programowanie oglne ....................................................................................................627


12.1. Dlaczego programowanie oglne ....................................................................... 628
12.1.1. Dla kogo programowanie oglne .......................................................... 629
12.2. Definicja prostej klasy oglnej ........................................................................... 630
12.3. Metody oglne ................................................................................................. 632
12.4. Ograniczenia zmiennych typowych ..................................................................... 633
12.5. Kod oglny a maszyna wirtualna ....................................................................... 635
12.5.1. Translacja wyrae generycznych ......................................................... 637
12.5.2. Translacja metod oglnych .................................................................. 637
12.5.3. Uywanie starego kodu ....................................................................... 639
12.6. Ograniczenia i braki ......................................................................................... 641
12.6.1. Nie mona podawa typw prostych jako parametrw typowych .............. 641
12.6.2. Sprawdzanie typw w czasie dziaania programu
jest moliwe tylko dla typw surowych .................................................. 641
12.6.3. Nie mona tworzy tablic typw oglnych .............................................. 642
12.6.4. Ostrzeenia dotyczce zmiennej liczby argumentw ............................... 642
12.6.5. Nie wolno tworzy egzemplarzy zmiennych typowych .............................. 643
12.6.6. Zmiennych typowych nie mona uywa w statycznych kontekstach
klas oglnych ..................................................................................... 645
12.6.7. Obiektw klasy oglnej nie mona generowa ani przechwytywa ............ 646
12.6.8. Uwaaj na konflikty, ktre mog powsta po wymazaniu typw ............... 648
12.7. Zasady dziedziczenia dla typw oglnych ........................................................... 649
12.8. Typy wieloznaczne ............................................................................................ 650
12.8.1. Ograniczenia nadtypw typw wieloznacznych ....................................... 652
12.8.2. Typy wieloznaczne bez ogranicze ........................................................ 655
12.8.3. Chwytanie typu wieloznacznego ............................................................ 655
12.9. Refleksja a typy oglne .................................................................................... 658
12.9.1. Zastosowanie parametrw Class<T> do dopasowywania typw .............. 659
12.9.2. Informacje o typach generycznych w maszynie wirtualnej ........................ 659

Rozdzia 13. Kolekcje .............................................................................................................................665


13.1. Interfejsy kolekcyjne ......................................................................................... 665
13.1.1. Oddzielenie warstwy interfejsw od warstwy klas konkretnych ................ 666
13.1.2. Interfejsy Collection i Iterator ............................................................... 668

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

10

Java. Podstawy
13.2. Konkretne klasy kolekcyjne ............................................................................... 674
13.2.1. Listy powizane ................................................................................. 674
13.2.2. Listy tablicowe .................................................................................. 684
13.2.3. Zbir HashSet ................................................................................... 684
13.2.4. Zbir TreeSet .................................................................................... 688
13.2.5. Porwnywanie obiektw ..................................................................... 689
13.2.6. Kolejki Queue i Deque ....................................................................... 694
13.2.7. Kolejki priorytetowe ........................................................................... 696
13.2.8. Mapy ................................................................................................ 697
13.2.9. Specjalne klasy Set i Map .................................................................. 702
13.3. Architektura kolekcji ......................................................................................... 706
13.3.1. Widoki i obiekty opakowujce ............................................................. 709
13.3.2. Operacje zbiorcze .............................................................................. 717
13.3.3. Konwersja pomidzy kolekcjami a tablicami ......................................... 718
13.4. Algorytmy ........................................................................................................ 718
13.4.1. Sortowanie i tasowanie ...................................................................... 720
13.4.2. Wyszukiwanie binarne ........................................................................ 722
13.4.3. Proste algorytmy ................................................................................ 723
13.4.4. Pisanie wasnych algorytmw .............................................................. 725
13.5. Stare kolekcje ................................................................................................. 726
13.5.1. Klasa Hashtable ................................................................................ 726
13.5.2. Wyliczenia ......................................................................................... 727
13.5.3. Mapy wasnoci ................................................................................. 728
13.5.4. Stosy ................................................................................................ 729
13.5.5. Zbiory bitw ...................................................................................... 729

Rozdzia 14. Wielowtkowo ...............................................................................................................735


14.1. Czym s wtki ................................................................................................. 736
14.1.1. Wykonywanie zada w osobnych wtkach ............................................ 741
14.2. Przerywanie wtkw ......................................................................................... 746
14.3. Stany wtkw .................................................................................................. 749
14.3.1. Wtki NEW ........................................................................................ 749
14.3.2. Wtki RUNNABLE ............................................................................... 750
14.3.3. Wtki BLOCKED i WAITING ................................................................. 750
14.3.4. Zamykanie wtkw ............................................................................ 752
14.4. Wasnoci wtkw ........................................................................................... 752
14.4.1. Priorytety wtkw ............................................................................... 752
14.4.2. Wtki demony ................................................................................... 753
14.4.3. Procedury obsugi nieprzechwyconych wyjtkw .................................... 754
14.5. Synchronizacja ................................................................................................ 756
14.5.1. Przykad sytuacji powodujcej wycig ................................................... 756
14.5.2. Wycigi ............................................................................................. 760
14.5.3. Obiekty klasy Lock ............................................................................. 762
14.5.4. Warunki ............................................................................................ 765
14.5.5. Sowo kluczowe synchronized ............................................................. 769
14.5.6. Bloki synchronizowane ....................................................................... 774
14.5.7. Monitor ............................................................................................. 775
14.5.8. Pola ulotne ....................................................................................... 776
14.5.9. Zmienne finalne ................................................................................ 777
14.5.10. Zmienne atomowe ............................................................................. 777
14.5.11. Zakleszczenia .................................................................................... 778

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Spis treci

11

14.5.12. Zmienne lokalne wtkw .................................................................... 781


14.5.13. Testowanie blokad i odmierzanie czasu ............................................... 782
14.5.14. Blokady odczytu-zapisu ...................................................................... 783
14.5.15. Dlaczego metody stop i suspend s wycofywane .................................. 784
14.6. Kolejki blokujce ............................................................................................. 786
14.7. Kolekcje bezpieczne wtkowo ........................................................................... 794
14.7.1. Szybkie mapy, zbiory i kolejki ............................................................. 794
14.7.2. Tablice kopiowane przy zapisie ........................................................... 796
14.7.3. Starsze kolekcje bezpieczne wtkowo ................................................. 796
14.8. Interfejsy Callable i Future ................................................................................ 797
14.9. Klasa Executors ............................................................................................... 802
14.9.1. Pule wtkw ..................................................................................... 803
14.9.2. Planowanie wykonywania ................................................................... 807
14.9.3. Kontrolowanie grup zada .................................................................. 808
14.9.4. Szkielet rozgazienie-zczenie .......................................................... 809
14.10. Synchronizatory ............................................................................................... 812
14.10.1. Semafory ......................................................................................... 812
14.10.2. Klasa CountDownLatch ..................................................................... 813
14.10.3. Bariery ............................................................................................. 814
14.10.4. Klasa Exchanger ............................................................................... 814
14.10.5. Kolejki synchroniczne ........................................................................ 815
14.11. Wtki a biblioteka Swing .................................................................................. 815
14.11.1. Uruchamianie czasochonnych zada .................................................. 816
14.11.2. Klasa SwingWorker ........................................................................... 820
14.11.3. Zasada jednego wtku ...................................................................... 827

Dodatek A. Sowa kluczowe Javy .........................................................................................................829


Skorowidz ..............................................................................................................................................831

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

12

Java. Podstawy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Wstp
Do Czytelnika
Jzyk programowania Java pojawi si na scenie pod koniec 1995 roku i od razu zyska sobie
reputacj gwiazdy. Ta nowa technologia miaa si sta uniwersalnym cznikiem pomidzy
uytkownikami a informacj, bez wzgldu na to, czy informacje te pochodziy z serwera sieciowego, bazy danych, serwisu informacyjnego, czy jakiegokolwiek innego rda. I rzeczywicie, Java ma niepowtarzaln okazj spenienia tych wymaga. Ten zaprojektowany
z niezwyk starannoci jzyk zyska akceptacj wszystkich najwikszych firm z wyjtkiem
Microsoftu. Wbudowane w jzyk zabezpieczenia dziaaj uspokajajco zarwno na programistw, jak i uytkownikw programw napisanych w Javie. Dziki wbudowanym funkcjom
zaawansowane zadania programistyczne, takie jak programowanie sieciowe, czno pomidzy bazami danych i wielowtkowo, s znacznie prostsze.
Do tej pory pojawio si ju osiem wersji pakietu Java Development Kit. Przez ostatnich osiemnacie lat interfejs programowania aplikacji (ang. Application Programming Interface API)
rozrs si z okoo 200 do ponad 3000 klas. API to obejmuje obecnie tak rne aspekty tworzenia aplikacji, jak konstruowanie interfejsu uytkownika, zarzdzanie bazami danych, internacjonalizacja, bezpieczestwo i przetwarzanie XML.
Ksika, ktr trzymasz w rce, jest pierwszym tomem dziewitego wydania ksiki Java.
Podstawy. Kada edycja tej ksiki nastpuje najszybciej, jak to tylko moliwe, po wydaniu
kolejnej wersji pakietu Java Development Kit. Za kadym razem uaktualnialimy tekst ksiki
z uwzgldnieniem najnowszych narzdzi dostpnych w Javie. To wydanie opisuje Java Standard Edition (SE) 7.
Tak jak w przypadku poprzednich wyda tej ksiki, to rwnie przeznaczone jest dla powanych programistw, ktrzy chc wykorzysta technologi Java w rzeczywistych projektach.
Zakadamy, e odbiorca naszego tekstu jest programist posiadajcym due dowiadczenie
w programowaniu w innym jzyku ni Java. Ponadto prno tu szuka dziecinnych przykadw
(jak tostery, zwierzta z zoo czy rozbiegany tekst). Nic z tych rzeczy tutaj nie znajdziesz.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

14

Java. Podstawy
Naszym celem byo przedstawienie wiedzy w taki sposb, aby Czytelnik mg bez problemu
w peni zrozumie zasady rzdzce jzykiem Java i jego bibliotek, a nie tylko myla, e
wszystko rozumie.
Ksika ta zawiera mnstwo przykadw kodu, obrazujcych zasady dziaania niemal kadej opisywanej przez nas funkcji i biblioteki. Przedstawiane przez nas przykadowe programy
s proste, poniewa chcielimy si w nich skoncentrowa na najwaniejszych zagadnieniach.
Niemniej znakomita wikszo z nich zawiera prawdziwy, nieskrcony kod. Powinny dobrze
suy jako punkt wyjcia do pisania wasnych programw.
Wychodzimy z zaoenia, e osoby czytajce t ksik chc (albo wrcz pragn) pozna
wszystkie zaawansowane cechy Javy. Oto kilka przykadowych zagadnie, ktre opisujemy
szczegowo:

programowanie obiektowe,

mechanizm refleksji (ang. reflections) i obiekty proxy,

interfejsy i klasy wewntrzne,

delegacyjny model obsugi zdarze,

projektowanie graficznego interfejsu uytkownika za pomoc pakietu narzdzi


Swing UI,

obsuga wyjtkw,

programowanie generyczne,

kolekcje,

wspbieno.

Ze wzgldu na niebyway wrcz rozwj biblioteki klas Javy opisanie w jednym tomie wszystkich wasnoci tego jzyka, ktrych potrzebuje powany programista, graniczyoby z cudem.
Z tego powodu postanowilimy podzieli nasz ksik na dwa tomy. Pierwszy, ktry trzymasz w rku, opisuje podstawy jzyka Java oraz najwaniejsze zagadnienia zwizane z programowaniem interfejsu uytkownika. Tom drugi (ktry niebawem si ukae) zawiera informacje dotyczce bardziej zaawansowanych tematw oraz opisuje zoone zagadnienia zwizane
z programowaniem interfejsu uytkownika. Poruszane w nim tematy to:

pliki i strumienie,

obiekty rozproszone,

bazy danych,

zaawansowane komponenty GUI,

metody rodzime,

przetwarzanie dokumentw XML,

programowanie sieciowe,

zaawansowana obrbka grafiki,

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Wstp

obsuga wielu jzykw,

JavaBeans,

adnotacje.

15

Podczas pisania ksiki nie da si unikn drobnych bdw i wpadek. Bardzo chcielibymy
by o nich informowani. Jednak kad tak informacj wolelibymy otrzyma tylko jeden raz.
W zwizku z tym na stronie http://horstmann.com/corejava zamiecilimy list najczciej
zadawanych pyta, obej i poprawek do bdw. Formularz sucy do wysyania informacji
o bdach i propozycji poprawek zosta celowo umieszczony na kocu strony z errat, aby
zachci potencjalnego nadawc do wczeniejszego zapoznania si z istniejcymi ju informacjami. Nie naley zraa si, jeli nie odpowiemy na kade pytanie lub nie zrobimy tego
natychmiast. Naprawd czytamy wszystkie przychodzce do nas listy i doceniamy wysiki
wszystkich naszych Czytelnikw wkadane w to, aby przysze wydania naszej ksiki byy
jeszcze bardziej zrozumiae i zawieray jeszcze wicej poytecznych informacji.

O ksice
Rozdzia 1. stanowi przegld waciwoci jzyka Java, ktre wyrniaj go na tle innych
jzykw programowania. Wyjaniamy, co projektanci chcieli zrobi, a co si im udao. Nastpnie krtko opisujemy histori powstania Javy oraz sposb, w jaki ewoluowaa.
W rozdziale 2. opisujemy proces pobierania i instalacji pakietu JDK (ang. Java Development
Kit) oraz doczonych do tej ksiki przykadw kodu. Nastpnie opisujemy krok po kroku
kompilacj i uruchamianie trzech typowych programw w Javie: aplikacji konsolowej, aplikacji graficznej i apletu. Naszymi narzdziami s czyste rodowisko JDK, edytor tekstowy
obsugujcy Jav i zintegrowane rodowisko programowania (ang. Integrated Development
Environment IDE) dla Javy.
Od rozdziau 3. zaczynamy opis jzyka programowania Java. Na pocztku zajmujemy si podstawami: zmiennymi, ptlami i prostymi funkcjami. Dla programistw jzykw C i C++ bdzie
to buka z masem, poniewa Java i C w tych sprawach w zasadzie niczym si nie rni.
Programici innych jzykw, takich jak Visual Basic, powinni bardzo starannie zapozna
si z treci tego rozdziau.
Obecnie najpopularniejsz metodologi stosowan przez programistw jest programowanie
obiektowe, a Java to jzyk w peni obiektowy. W rozdziale 4. wprowadzamy pojcie hermetyzacji (ang. encapsulation), ktra stanowi jeden z dwch filarw programowania obiektowego, oraz piszemy o mechanizmach Javy sucych do jej implementacji, czyli o klasach
i metodach. Poza opisem zasad rzdzcych jzykiem Java dostarczamy take informacji na
temat solidnego projektowania programw zorientowanych obiektowo. Na kocu powicamy
nieco miejsca doskonaemu narzdziu o nazwie javadoc, sucemu do konwersji komentarzy
zawartych w kodzie na wzajemnie poczone hiperczami strony internetowe. Osoby znajce jzyk C++ mog przejrze ten rozdzia pobienie. Programici niemajcy dowiadczenia
w programowaniu obiektowym musz si liczy z tym, e opanowanie wiedzy przedstawionej
w tym rozdziale zajmie im troch czasu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

16

Java. Podstawy
Klasy i hermetyzacja to dopiero poowa koncepcji programowania zorientowanego obiektowo.
Rozdzia 5. wprowadza drug, czyli dziedziczenie. Mechanizm ten umoliwia modyfikacj
istniejcych ju klas do wasnych specyficznych potrzeb. Jest to podstawowa technika programowania zarwno w Javie, jak i C++, a oba te jzyki maj pod tym wzgldem wiele ze sob
wsplnego. Dlatego te programici C++ mog rwnie w tym rozdziale skupi si tylko na
rnicach pomidzy tymi dwoma jzykami.
W rozdziale 6. nauczymy si posugiwa interfejsami w Javie, ktre wykraczaj poza prosty model dziedziczenia opisywany w poprzednim rozdziale. Opanowanie technik zwizanych z interfejsami da nam peny dostp do moliwoci, jakie stwarza w peni obiektowe
programowanie w Javie. Ponadto opisujemy bardzo przydatne w Javie klasy wewntrzne
(ang. inner classes). Pomagaj one w pisaniu bardziej zwizego i przejrzystego kodu.
Od rozdziau 7. zaczynamy powane programowanie. Jako e kady programista Javy powinien zna si na programowaniu GUI, w tym rozdziale opisujemy podstawy tego zagadnienia.
Nauczysz si tworzy okna, rysowa w nich, rysowa figury geometryczne, formatowa tekst
przy zastosowaniu rnych krojw pisma oraz wywietla obrazy.
W rozdziale 8. szczegowo opisujemy model zdarze AWT (ang. Abstract Window Toolkit).
Nauczymy si pisa programy reagujce na zdarzenia, takie jak kliknicie przyciskiem myszy
albo nacinicie klawisza na klawiaturze. Dodatkowo opanujesz techniki pracy nad takimi
elementami GUI jak przyciski i panele.
Rozdzia 9. zawiera bardzo szczegowy opis pakietu Swing. Pakiet ten umoliwia tworzenie
niezalenych od platformy graficznych interfejsw uytkownika. Nauczysz si posugiwa
rnego rodzaju przyciskami, komponentami tekstowymi, obramowaniami, suwakami, polami
list, menu i oknami dialogowymi. Niektre zaawansowane komponenty zostay opisane dopiero
w drugim tomie.
Rozdzia 10. zawiera informacje na temat wdraania programw jako aplikacji lub apletw.
Nauczysz si pakowa programy do plikw JAR oraz udostpnia aplikacje poprzez internet
za pomoc mechanizmw Java Web Start i apletw. Na zakoczenie opisujemy, w jaki sposb
Java przechowuje i wyszukuje informacje na temat konfiguracji ju po ich wdroeniu.
Rozdzia 11. powicilimy obsudze wyjtkw doskonaemu mechanizmowi pozwalajcemu radzi sobie z tym, e z dobrymi programami mog dzia si ze rzeczy. Wyjtki s skutecznym sposobem na oddzielenie kodu normalnego przetwarzania od kodu obsugujcego
bdy. Oczywicie nawet zabezpieczenie w postaci obsugi wszystkich sytuacji wyjtkowych
nie zawsze uchroni nas przed niespodziewanym zachowaniem programu. W drugiej czci tego
rozdziau zawarlimy mnstwo wskazwek dotyczcych usuwania bdw z programu. Na
kocu opisujemy krok po kroku ca sesj debugowania.
W rozdziale 12. przedstawiamy zarys programowania oglnego najwaniejszej nowoci
w Java SE 5.0. Dziki tej technice mona tworzy atwiejsze do odczytu i bezpieczniejsze
programy. Przedstawiamy sposoby stosowania cisej kontroli typw oraz pozbywania si
szpetnych i niebezpiecznych konwersji. Ponadto nauczysz si radzi sobie w sytuacjach, kiedy
trzeba zachowa zgodno ze starszymi wersjami Javy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Wstp

17

Tematem rozdziau 13. s kolekcje. Chcc zebra wiele obiektw, aby mc ich pniej uy,
najlepiej posuy si kolekcj, zamiast po prostu wrzuca wszystkie obiekty do tablicy. W rozdziale tym nauczysz si korzysta ze standardowych kolekcji, ktre s wbudowane w jzyk
i gotowe do uytku.
Koczcy ksik rozdzia 14. zawiera opis wielowtkowoci, ktra umoliwia programowanie w taki sposb, aby rne zadania byy wykonywane jednoczenie (wtek to przepyw
sterowania w programie). Nauczysz si ustawia wtki i panowa nad ich synchronizacj.
Jako e wielowtkowo w Java 5.0 ulega powanym zmianom, opisujemy wszystkie nowe
mechanizmy z ni zwizane.
Dodatek zawiera list sw zarezerwowanych w jzyku Java.

Konwencje typograficzne
Podobnie jak w wielu ksikach komputerowych, przykady kodu programw pisane s
czcionk o staej szerokoci znakw.
Tak ikon opatrzone s uwagi.

T ikon opatrzone s wskazwki.

Takiej ikony uywamy, aby ostrzec przed jakim niebezpieczestwem.

W ksice pojawia si wiele uwag wyjaniajcych rnice pomidzy Jav a jzykiem


C++. Jeli nie znasz si na programowaniu w C++ lub na myl o przykrych wspomnieniach z nim zwizanych dostajesz gsiej skrki, moesz je pomin.

Java ma bardzo du bibliotek programistyczn, czyli API (ang. Application Programming


Interface). Kiedy po raz pierwszy uywamy jakiego wywoania API, na kocu sekcji umieszczamy jego krtki opis. Opisy te s nieco nieformalne, ale staralimy si, aby zawieray wicej
potrzebnych informacji ni te, ktre mona znale w oficjalnej dokumentacji API w internecie. Liczba znajdujca si za nazw klasy, interfejsu lub metody odpowiada wersji JDK,
w ktrej opisywana wasno zostaa wprowadzona.
Interfejs programowania aplikacji 1.2

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

18

Java. Podstawy
Programy, ktrych kod rdowy mona znale w internecie, s oznaczane jako listingi, np.:

Listing 1.1. inputTest/InputTest.java

Przykady kodu
W witrynie towarzyszcej tej ksice, pod adresem www.helion.pl/ksiazki/javpd9.htm,
opublikowane s w postaci skompresowanego archiwum wszystkie pliki z przykadami kodu
rdowego. Mona je wypakowa za pomoc dowolnego programu otwierajcego paczki
ZIP albo przy uyciu narzdzia jar dostpnego w zestawie Java Development Kit. Wicej
informacji na temat tego pakietu i przykady kodu mona znale w rozdziale 2.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Podzikowania
Pisanie ksiki to zawsze ogromny wysiek, a pisanie kolejnego wydania nie wydaje si duo
atwiejsze, zwaszcza kiedy wemie si pod uwag cige zmiany zachodzce w technologii
Java. Aby ksika moga powsta, potrzeba zaangaowania wielu osb. Dlatego te z wielk
przyjemnoci chciabym podzikowa za wspprac caemu zespoowi Core Java.
Wiele cennych uwag pochodzi od osb z wydawnictwa Prentice Hall, ktrym udao si pozosta w cieniu. Chciabym, aby wszystkie te osoby wiedziay, e bardzo doceniam ich prac.
Jak zawsze gorce podzikowania kieruj do mojego redaktora z wydawnictwa Prentice
Hall Grega Doencha za przeprowadzenie tej ksiki przez proces pisania i produkcji
oraz za to, e pozwoli mi pozosta w bogiej niewiadomoci istnienia wszystkich osb pracujcych w zapleczu. Jestem wdziczny Julie Nahil za doskonae wsparcie w dziedzinie produkcji, oraz Dmitryemu i Alinie Kirsanovom za korekt i skad. Dzikuj rwnie mojemu
wspautorowi poprzednich wyda tej ksiki Garyemu Cornellowi, ktry podj inne
wyzwania.
Dzikuj wszystkim Czytelnikom poprzednich wyda tej ksiki za informacje o enujcych
bdach, ktre popeniem, i komentarze dotyczce ulepszenia mojej ksiki. Jestem szczeglnie wdziczny znakomitemu zespoowi korektorw, ktrzy czytajc wstpn wersj tej
ksiki i wykazujc niebywa czuo na szczegy, uratowali mnie przed popenieniem
jeszcze wikszej liczby bdw.
Do recenzentw tego wydania i poprzednich edycji ksiki nale: Chuck Allison (Utah Valley
University), Lance Andersen (Oracle), Alec Beaton (IBM), Cliff Berg, Joshua Bloch, David
Brown, Corky Cartwright, Frank Cohen (PushToTest), Chris Crane (devXsolution), dr Nicholas
J. De Lillo (Manhattan College), Rakesh Dhoopar (Oracle), David Geary (Clarity Training),
Jim Gish (Oracle), Brian Goetz (Oracle), Angela Gordon, Dan Gordon (Electric Cloud), Rob
Gordon, John Gray (University of Hartford), Cameron Gregory (olabs.com), Marty Hall
(coreservlets.com, Inc.), Vincent Hardy (Adobe Systems), Dan Harkey (San Jose State University), William Higgins (IBM), Vladimir Ivanovic (PointBase), Jerry Jackson (CA Technologies), Tim Kimmet (Walmart), Chris Laffra, Charlie Lai (Apple), Angelika Langer, Doug
Langston, Hang Lau (McGill University), Mark Lawrence, Doug Lea (SUNY Oswego),
Gregory Longshore, Bob Lynch (Lynch Associates), Philip Milne (konsultant), Mark Morrissey (The Oregon Graduate Institute), Mahesh Neelakanta (Florida Atlantic University),

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

20

Java. Podstawy
Hao Pham, Paul Philion, Blake Ragsdell, Stuart Reges (University of Arizona), Rich Rosen
(Interactive Data Corporation), Peter Sanders (ESSI University, Nicea, Francja), dr Paul Sanghera (San Jose State University, Brooks College), Paul Sevinc (Teamup AG), Devang Shah
(Sun Microsystems), Bradley A. Smith, Steven Stelting (Oracle), Christopher Taylor, Luke
Taylor (Valtech), George Thiruvathukal, Kim Topley (StreamingEdge), Janet Traub, Paul
Tyma (konsultant), Peter van der Linden (Motorola Mobile Devices), Burt Walsh, Dan Xu
(Oracle) i John Zavgren (Oracle).
Cay Horstmann
San Francisco, Kalifornia
wrzesie 2012

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Wstp do Javy
W tym rozdziale:

Java jako platforma programistyczna

Sowa klucze biaej ksigi Javy

Aplety Javy i internet

Krtka historia Javy

Najczstsze nieporozumienia dotyczce Javy

Pierwsze wydanie Javy w 1996 roku wywoao ogromne emocje nie tylko w prasie komputerowej, ale take w takich mediach nalecych do gwnego nurtu, jak The New York
Times, The Washington Post czy Business Week. Jzyk Java zosta jako pierwszy i do
tej pory jedyny jzyk programowania wyrniony krtk, bo trwajc 10 minut, wzmiank
w National Public Radio. Kapita wysokiego ryzyka w wysokoci 100 000 000 dolarw zosta
zebrany wycznie dla produktw powstaych przy zastosowaniu okrelonego jzyka komputerowego. Wracanie dzisiaj do tych wietnych czasw jest bardzo zabawne, a wic w tym
rozdziale krtko opisujemy histori jzyka Java.

1.1. Java jako platforma programistyczna


W pierwszym wydaniu tej ksiki napisalimy o Javie takie oto sowa:
Ten cay szum wok Javy jako jzyka programowania jest przesadzony. Java to z pewnoci
dobry jzyk programowania. Nie ma wtpliwoci, e jest to jedno z najlepszych narzdzi
dostpnych dla powanych programistw. Naszym zdaniem mogaby by wspaniaym jzykiem programowania, ale na to jest ju chyba zbyt pno. Kiedy przychodzi do rzeczywistych
zastosowa, swoj gow podnosi ohydna zmora zgodnoci z istniejcym ju kodem.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

22

Java. Podstawy
Za ten akapit na naszego redaktora posypay si gromy ze strony kogo bardzo wysoko postawionego w firmie Sun Microsystems, kogo nazwiska wolimy nie ujawnia. Jednak z perspektywy czasu wydaje si, e nasze przewidywania byy suszne. Java ma mnstwo bardzo poytecznych cech, ktre opisujemy w dalszej czci tego rozdziau. Ma te jednak pewne wady,
a najnowsze dodatki do jzyka ze wzgldu na zgodno nie s ju tak eleganckie jak kiedy.
Jak jednak napisalimy w pierwszym wydaniu tej ksiki, Java nigdy nie bya tylko jzykiem.
Istnieje bardzo duo jzykw programowania, a tylko kilka z nich zrobio furor. Java to
caa platforma z du bibliotek zawierajc ogromne iloci gotowego do wykorzystania kodu
oraz rodowisko wykonawcze, ktre zapewnia bezpieczestwo, przenono midzy rnymi
systemami operacyjnymi oraz automatyczne usuwanie nieuytkw (ang. garbage collecting).
Jako programici damy jzyka o przyjaznej skadni i zrozumiaej semantyce (a wic nie C++).
Do tego opisu pasuje Java, jak rwnie wiele innych dobrych jzykw programowania. Niektre z nich oferuj przenono, zbieranie nieuytkw itd., ale nie maj bogatych bibliotek,
przez co zmuszeni jestemy pisa wasne, kiedy chcemy wykona obrbk grafiki, stworzy
aplikacj sieciow bd czc si z baz danych. C, Java ma to wszystko jest to dobry
jzyk, ktry oddaje do dyspozycji programisty wysokiej jakoci rodowisko wykonawcze wraz
z ogromn bibliotek. To wanie to poczenie sprawia, e tak wielu programistw nie potrafi
oprze si urokowi Javy.

1.2. Sowa klucze biaej ksigi Javy


Twrcy jzyka Java napisali bardzo wpywow bia ksig, w ktrej opisali swoje cele
i osignicia. Dodatkowo opublikowali krtkie streszczenie zorganizowane wedug nastpujcych 11 sw kluczowych:
1.

prosty,

2. obiektowy,
3. sieciowy,
4. niezawodny,
5. bezpieczny,
6. niezaleny od architektury,
7. przenony,
8. interpretowany,
9. wysokowydajny,
10. wielowtkowy,
11. dynamiczny.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 1.

Wstp do Javy

23

W tej czci rozdziau:

Krtko podsumujemy, posikujc si fragmentami z biaej ksigi, co projektanci


Javy maj do powiedzenia na temat kadego ze sw kluczowych.

Wyrazimy wasne zdanie na temat kadego z tych sw kluczowych, opierajc


si na naszych dowiadczeniach zwizanych z aktualn wersj Javy.
W trakcie pisania tej ksiki biaa ksiga Javy bya dostpna pod adresem
http://www.oracle.com/technetwork/java/langenv-140151.html.

1.2.1. Prosty
Naszym celem byo zbudowanie takiego systemu, ktry mona zaprogramowa bez
ukoczenia tajemnych szkole, a ktry podtrzymywaby obecne standardowe praktyki.
W zwizku z tym mimo e w naszym przekonaniu jzyk C++ nie nadawa si do tego
celu Java pod wzgldem projektowym jest do niego podobna, jak to tylko moliwe.
Dziki temu nasz system jest bardziej zrozumiay. Java jest pozbawiona wielu rzadko
uywanych, sabo poznanych i wywoujcych zamieszanie funkcji, ktre zgodnie z naszymi
dowiadczeniami przynosz wicej zego ni dobrego.
Skadnia Javy rzeczywicie jest oczyszczon wersj skadni jzyka C++. Nie ma potrzeby
doczania plikw nagwkowych, posugiwania si arytmetyk wskanikow (a nawet skadni
wskanikow), strukturami, uniami, przecianiem operatorw, wirtualnymi klasami bazowymi itd. (wicej rnic pomidzy Jav a C++ mona znale w uwagach rozmieszczonych
na kartach tej ksiki). Nie jest jednak tak, e projektanci pozbyli si wszystkich waciwoci
jzyka C++, ktre nie byy eleganckie. Na przykad nie zrobiono nic ze skadni instrukcji
switch. Kady, kto zna jzyk C++, z atwoci przeczy si na skadni jzyka Java.
Osoby przyzwyczajone do rodowisk wizualnych (jak Visual Basic) przy nauce Javy bd
napotyka trudnoci. Trzeba poj mnstwo dziwnych elementw skadni (cho nie zabiera
to zbyt duo czasu). Wiksze znaczenie ma to, e w Javie trzeba znacznie wicej pisa.
Pikno jzyka Visual Basic polega na tym, e dua cz infrastruktury aplikacji jest automatycznie dostarczana przez rodowisko programistyczne. Wszystko to w Javie trzeba napisa
wasnorcznie, a to z reguy wymaga do duej iloci kodu. Istniej jednak rodowiska
udostpniane przez niezalenych producentw, ktre umoliwiaj programowanie w stylu
przecignij i upu.
Wyznacznikiem prostoty s take niewielkie rozmiary. Jednym z celw Javy
jest umoliwienie tworzenia oprogramowania dziaajcego niezalenie na maych
urzdzeniach. Rozmiar podstawowego interpretera i obsugi klas wynosi okoo
40 kilobajtw. Podstawowe standardowe biblioteki i obsuga wtkw (w zasadzie
jest to samodzielne mikrojdro) to dodatkowe 175 K.
W tamtych czasach byo to niebywae wrcz osignicie. Oczywicie od tamtej pory biblioteka
Javy rozrosa si do nieprawdopodobnych rozmiarw. W zwizku z tym powstaa oddzielna
wersja Javy o nazwie Java Micro Edition z mniejsz bibliotek, ktra nadaje si do stosowania na maych urzdzeniach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

24

Java. Podstawy

1.2.2. Obiektowy
Mwic krtko, projektowanie obiektowe to technika programowania, ktrej punktem
centralnym s dane (czyli obiekty) oraz interfejsy dajce dostp do tych obiektw.
Przez analogi obiektowy stolarz byby przede wszystkim zainteresowany krzesem,
ktre ma zrobi, a potrzebne do tego narzdzia stawiaby na drugim miejscu.
Nieobiektowy stolarz z kolei na pierwszym miejscu stawiaby swoje narzdzia. Narzdzia
zwizane z programowaniem obiektowym w Javie s w zasadzie takie jak w C++.
Obiektowa metoda programowania udowodnia swoj warto w cigu ostatnich 30 lat. Jest
nie do pomylenia, aby jakikolwiek nowoczesny jzyk z niej nie korzysta. Rzeczywicie
waciwoci Javy, dziki ktrym mona nazywa j jzykiem obiektowym, s podobne do
jzyka C++. Gwna rnica pomidzy tymi dwoma jzykami objawia si w wielodziedziczeniu, ktre w Javie zostao zastpione prostszymi interfejsami, oraz w modelu metaklas
Javy (ktry jest opisany w rozdziale 5.).
Osoby, ktre nie miay stycznoci z programowaniem obiektowym, powinny bardzo
uwanie przeczyta rozdziay 4. 6. Zawieraj one opis technik programowania
obiektowego oraz wyjanienie, czemu ta technika lepiej nadaje si do wyrafinowanych
projektw ni tradycyjne jzyki proceduralne, takie jak C lub Basic.

1.2.3. Sieciowy
Java ma bogat bibliotek procedur wspomagajcych prac z takimi protokoami
TCP/IP jak HTTP i FTP. Aplikacje w tym jzyku mog uzyskiwa dostp poprzez sie
do obiektw z tak sam atwoci, jakby znajdoway si one w lokalnym systemie plikw.
W naszej ocenie funkcje sieciowe Javy s zarwno solidne, jak i atwe w uyciu. Kady, kto
kiedykolwiek sprbowa programowania sieciowego w innym jzyku programowania, bdzie
zachwycony tym, jak proste s tak niegdy uciliwe zadania jak poczenia na poziomie
gniazd (ang. socket connection); programowanie sieciowe opisalimy w drugim tomie. Mechanizm zdalnych wywoa metod umoliwia komunikacj pomidzy obiektami rozproszonymi
(take opisany w drugim tomie).

1.2.4. Niezawodny
Java zostaa stworzona do pisania programw, ktre musz by niezawodne w rozmaitych
sytuacjach. Duo uwagi powicono wczesnemu sprawdzaniu moliwoci wystpienia
ewentualnych problemw, pniejszemu sprawdzaniu dynamicznemu (w trakcie
dziaania programu) oraz wyeliminowaniu sytuacji, w ktrych atwo popeni bd.
Najwiksza rnica pomidzy Jav a C/C++ polega na tym, e model wskanikowy
tego pierwszego jzyka jest tak zaprojektowany, aby nie byo moliwoci nadpisania
pamici i zniszczenia w ten sposb danych.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 1.

Wstp do Javy

25

Jest to take bardzo przydatna funkcja. Kompilator Javy wykrywa wiele bdw, ktre w innych
jzykach ujawniyby si dopiero po uruchomieniu programu. Wracajc do wskanikw, kady,
kto spdzi wiele godzin na poszukiwaniu uszkodzenia w pamici spowodowanego bdnym
wskanikiem, bdzie bardzo zadowolony z Javy.
Osoby programujce do tej pory w jzyku takim jak Visual Basic, w ktrym nie stosuje si jawnie wskanikw, pewnie zastanawiaj si, czemu s one takie wane. Programici jzyka C
nie maj ju tyle szczcia. Im wskaniki potrzebne s do uzyskiwania dostpu do acuchw,
tablic, obiektw, a nawet plikw. W jzyku Visual Basic w adnej z wymienionych sytuacji
nie stosuje si wskanikw ani nie trzeba zajmowa si przydzielaniem dla nich pamici.
Z drugiej jednak strony w jzykach pozbawionych wskanikw trudniej jest zaimplementowa wiele rnych struktur danych. Java czy w sobie to, co najlepsze w obu tych podejciach. Wskaniki nie s potrzebne do najczciej uywanych struktur, jak acuchy czy tablice.
Moliwoci stwarzane przez wskaniki s jednak cay czas w zasigu rki przydaj si na
przykad w przypadku list dwukierunkowych (ang. linked lists). Ponadto cay czas jestemy
w peni bezpieczni, poniewa nie ma moliwoci uzyskania dostpu do niewaciwego wskanika, popenienia bdu przydzielania pamici ani koniecznoci wystrzegania si przed wyciekami pamici.

1.2.5. Bezpieczny
Java jest przystosowana do zastosowa w rodowiskach sieciowych i rozproszonych.
W tej dziedzinie pooono duy nacisk na bezpieczestwo. Java umoliwia tworzenie
systemw odpornych na wirusy i ingerencj.
W pierwszym wydaniu naszej ksiki napisalimy: Nigdy nie mw nigdy i okazao si, e
mielimy racj. Niedugo po wydaniu pakietu JDK zesp ekspertw z Princeton University
znalaz ukryte bdy w zabezpieczeniach Java 1.0. Firma Sun Microsystems zaprosia programistw do zbadania zabezpiecze Javy, udostpniajc publicznie specyfikacj i implementacj wirtualnej maszyny Javy oraz biblioteki zabezpiecze. Wszystkie znane bdy zabezpiecze zostay szybko naprawione. Dziki temu przechytrzenie zabezpiecze Javy jest nie
lada sztuk. Znalezione do tej pory bdy byy cile zwizane z techniczn stron jzyka
i byo ich niewiele.
Od samego pocztku przy projektowaniu Javy starano si uniemoliwi przeprowadzanie
niektrych rodzajw atakw, takich jak:

przepenienie stosu wykonywania czsto stosowany atak przez robaki i wirusy;

niszczenie pamici poza swoj wasn przestrzeni procesow;

odczyt lub zapis plikw bez zezwolenia.

Pewna liczba zabezpiecze zostaa dodana do Javy z biegiem czasu. Od wersji 1.1 istnieje pojcie klasy podpisanej cyfrowo (ang. digitally signed class), ktre opisane jest w drugim tomie.
Dziki temu zawsze wiadomo, kto napisa dan klas. Jeli ufamy klasie napisanej przez
kogo innego, mona da jej wiksze przywileje.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

26

Java. Podstawy

W konkurencyjnej technologii firmy Microsoft, opartej na ActiveX, jako zabezpieczenie stosuje si tylko podpisy cyfrowe. To oczywicie za mao kady uytkownik produktw firmy Microsoft moe potwierdzi, e programy od znanych dostawcw
psuj si i mog powodowa szkody. Model zabezpiecze Javy jest o niebo lepszy ni
ten oparty na ActiveX, poniewa kontroluje dziaajc aplikacj i zapobiega spustoszeniom, ktre moe ona wyrzdzi.

1.2.6. Niezaleny od architektury


Kompilator generuje niezaleny od konkretnej architektury plik w formacie obiektowym.
Tak skompilowany kod mona uruchamia na wielu procesorach, pod warunkiem
e zainstalowano Java Runtime System. Kompilator dokonuje tego, generujc kod
bajtowy niemajcy nic wsplnego z adnym konkretnym procesorem. W zamian kod
ten jest tak konstruowany, aby by atwy do interpretacji na kadym urzdzeniu i aby
mona go byo z atwoci przetumaczy na kod maszynowy w locie.
Nie jest to adna nowo. Ju ponad 30 lat temu Niklaus Wirth zastosowa t technik
w swoich oryginalnych implementacjach systemw Pascal i UCSD.
Oczywicie interpretowanie kodu bajtowego musi by wolniejsze ni dziaanie instrukcji
maszynowych z pen prdkoci i nie wiadomo, czy jest to tak naprawd dobry pomys.
Jednak maszyny wirtualne mog tumaczy czsto wykonywany kod bajtowy na kod maszynowy w procesie nazywanym kompilacj w czasie rzeczywistym (ang. just-in-time compilation). Metoda ta okazaa si tak efektywna, e nawet firma Microsoft zastosowaa maszyn
wirtualn na swojej platformie .NET.
Maszyna wirtualna Javy ma take inne zalety. Zwiksza bezpieczestwo, poniewa moe
kontrolowa dziaanie sekwencji instrukcji. Niektre programy tworz nawet kod bajtowy
w locie, tym samym dynamicznie zwikszajc moliwoci dziaajcego programu.

1.2.7. Przenony
W przeciwiestwie do jzykw C i C++ Java nie jest w aden sposb uzaleniona
od implementacji. Rozmiary podstawowych typw danych s okrelone, podobnie
jak wykonywane na nich dziaania arytmetyczne.
Na przykad typ int w Javie zawsze oznacza 32-bitow liczb cakowit. W C i C++ typ int
moe przechowywa liczb cakowit 16-, 32-bitow lub o dowolnym innym rozmiarze,
jaki wymyli sobie twrca kompilatora. Jedyne ograniczenie polega na tym, e typ int nie
moe by mniejszy ni typ short int i wikszy ni long int. Ustalenie rozmiarw typw
liczbowych spowodowao zniknicie gwnego problemu z przenoszeniem programw. Dane
binarne s przechowywane i przesyane w ustalonym formacie, dziki czemu unika si nieporozumie zwizanych z kolejnoci bajtw. acuchy s przechowywane w standardowym formacie Unicode.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 1.

Wstp do Javy

27

Biblioteki wchodzce w skad systemu definiuj przenone interfejsy. Dostpna


jest na przykad abstrakcyjna klasa Window i jej implementacje dla systemw Unix,
Windows i Mac OS X.
Kady, kto kiedykolwiek prbowa napisa program, ktry mia wyglda dobrze w systemie Windows, na komputerach Macintosh i w dziesiciu rnych odmianach Uniksa, wie,
jak ogromny jest to wysiek. Java 1.0 wykonaa to heroiczne zadanie i udostpnia prosty
zestaw narzdzi, ktre odwzorowyway elementy interfejsu uytkownika na kilku rnych
platformach. Niestety, w wyniku tego powstaa biblioteka, ktra przy duym nakadzie pracy
dawaa ledwie akceptowalne w rnych systemach rezultaty (dodatkowo na rnych platformach wystpoway rne bdy). Ale to byy dopiero pocztki. W wielu aplikacjach od
piknego interfejsu uytkownika waniejsze s inne rzeczy wanie takie aplikacje korzystay na pierwszych wersjach Javy. Obecny zestaw narzdzi do tworzenia interfejsu uytkownika jest napisany od nowa i nie jest uzaleniony od interfejsu uytkownika hosta. Wynikiem jest znacznie spjniejszy i w naszym odczuciu atrakcyjniejszy interfejs ni ten, ktry
by dostpny we wczesnych wersjach Javy.

1.2.8. Interpretowany
Interpreter Javy moe wykona kady kod bajtowy Javy bezporednio na urzdzeniu,
na ktrym interpreter ten zainstalowano. Jako e czenie jest bardziej inkrementalnym
i lekkim procesem, proces rozwoju moe by znacznie szybszy i bardziej odkrywczy.
czenie narastajce ma swoje zalety, ale opowieci o korzyciach pyncych z jego stosowania w procesie rozwoju aplikacji s przesadzone. Pierwsze narzdzia Javy rzeczywicie
byy powolne. Obecnie kod bajtowy jest tumaczony przez kompilator JIT (ang. just-in-time
compiler) na kod maszynowy.

1.2.9. Wysokowydajny
Mimo e wydajno interpretowanego kodu bajtowego jest zazwyczaj wicej ni
wystarczajca, zdarzaj si sytuacje, w ktrych potrzebna jest wiksza wydajno.
Kod bajtowy moe by tumaczony w locie (w trakcie dziaania programu) na kod
maszynowy przeznaczony dla okrelonego procesora, na ktrym dziaa aplikacja.
Na pocztku istnienia Javy wielu uytkownikw nie zgadzao si ze stwierdzeniem, e jej
wydajno jest wicej ni wystarczajca. Jednak najnowsze kompilatory JIT s tak dobre,
e mog konkurowa z tradycyjnymi kompilatorami, a czasami nawet je przeciga, poniewa
maj dostp do wikszej iloci informacji. Na przykad kompilator JIT moe sprawdza, ktra
cz kodu jest najczciej wykonywana, i zoptymalizowa j pod ktem szybkoci. Bardziej
zaawansowana technika optymalizacji polega na eliminacji wywoa funkcji (ang. inlining).
Kompilator JIT wie, ktre klasy zostay zaadowane. Moe zastosowa wstawianie kodu
funkcji w miejsce ich wywoa, kiedy biorc pod uwag aktualnie zaadowane kolekcje
klas okrelona funkcja nie jest przesonita i moliwe jest cofnicie tej optymalizacji
w razie potrzeby.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

28

Java. Podstawy

1.2.10. Wielowtkowy
Korzyci pynce z wielowtkowoci to lepsza interaktywno i dziaanie w czasie
rzeczywistym.
Kady, kto prbowa programowania wielowtkowego w innym jzyku ni Java, bdzie mile
zaskoczony tym, jak atwe jest to w Javie. Wtki w Javie mog korzysta z systemw wieloprocesorowych, jeli podstawowy system operacyjny to umoliwia. Problem w tym, e
implementacje wtkw na rnych platformach znacznie si rni, a Java nic nie robi pod
tym wzgldem, aby zapewni niezaleno od platformy. Taki sam w rnych urzdzeniach
pozostaje tylko kod sucy do wywoywania wielowtkowoci. Implementacja wielowtkowoci jest w Javie zrzucana na system operacyjny lub bibliotek wtkw. Niemniej atwo,
z jak przychodzi korzystanie z niej w Javie, sprawia, e jest ona bardzo kuszc propozycj,
jeli chodzi o programowanie po stronie serwera.

1.2.11. Dynamiczny
Java jest bardziej dynamicznym jzykiem ni C i C++ pod wieloma wzgldami. Zostaa
zaprojektowana tak, aby dostosowywa si do ewoluujcego rodowiska. Do bibliotek
mona bez przeszkd dodawa nowe metody i zmienne egzemplarzy, nie wywierajc
adnego wpywu na klienty. Sprawdzanie informacji o typach w Javie nie sprawia
trudnoci.
Cecha ta jest wana w sytuacjach, kiedy trzeba doda kod do dziaajcego programu.
Najwaniejszy przykad takiej sytuacji to pobieranie kodu z internetu w celu uruchomienia
w przegldarce. W Javie 1.0 sprawdzenie typu w czasie dziaania programu byo proste, ale
w aktualnej wersji Javy programista ma moliwo penego wgldu zarwno w struktur, jak
i dziaanie obiektw. Jest to niezwykle wane, zwaszcza dla systemw, w ktrych konieczne
jest analizowanie obiektw w czasie pracy, takich jak kreatory GUI Javy, inteligentne debugery,
komponenty zdolne do podczania si w czasie rzeczywistym oraz obiektowe bazy danych.
Niedugo po pocztkowym sukcesie Javy firma Microsoft wydaa produkt o nazwie
J++. By to jzyk programowania i maszyna wirtualna udzco podobne do Javy.
Obecnie Microsoft nie zajmuje si ju tym projektem i zwrci si w stron innego
jzyka C#, ktry rwnie przypomina Jav. Istnieje nawet jzyk J# sucy do migracji
aplikacji napisanych w J++ na maszyn wirtualn uywan przez C#. W ksice tej nie
opisujemy jzykw J++, C# i J#.

1.3. Aplety Javy i internet


Zaoenie jest proste: uytkownik pobiera kod bajtowy z internetu i uruchamia go na wasnym
urzdzeniu. Programy napisane w Javie, ktre dziaaj na stronach internetowych, nosz nazw
apletw Javy. Aby uywa apletw, wystarczy mie przegldark obsugujc Jav, w ktrej mona uruchomi kod bajtowy tego jzyka. Nie trzeba niczego instalowa. Dziki temu,

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 1.

Wstp do Javy

29

e firma Sun udziela licencji na kod rdowy Javy i nie zezwala na wprowadzanie adnych
zmian w jzyku i bibliotece standardowej, kady aplet powinien dziaa w kadej przegldarce reklamowanej jako obsugujca Jav. Najnowsz wersj oprogramowania pobiera si
w trakcie odwiedzin strony internetowej zawierajcej aplet. Najwaniejsze jest jednak to, e
dziki zabezpieczeniom maszyny wirtualnej nie trzeba si obawia atakw ze strony zoliwego kodu.
Pobieranie apletu odbywa si w podobny sposb jak wstawianie obrazu na stron internetow.
Aplet integruje si ze stron, a tekst otacza go ze wszystkich stron jak obraz. Rnica polega
na tym, e ten obraz jest ywy. Reaguje na polecenia uytkownika, zmienia wygld oraz przesya dane pomidzy komputerem, na ktrym zosta uruchomiony, a komputerem, z ktrego
pochodzi.
Rysunek 1.1 przedstawia dobry przykad dynamicznej strony internetowej, na ktrej wykonywane s skomplikowane obliczenia. Aplet Jmol wywietla budow czsteczek. Wywietlon
czsteczk mona za pomoc myszy obraca w rne strony, co pozwala lepiej zrozumie jej
budow. Tego typu bezporednia manipulacja obiektami nie jest moliwa na statycznych stronach WWW, ale w apletach tak (aplet ten mona znale na stronie http://jmol.sourceforge.net).

Rysunek 1.1. Aplet Jmol

Kiedy aplety pojawiy si na scenie, wywoay niemae poruszenie. Wielu ludzi uwaa, e to
wanie dziki zaletom apletw Java zyskaa tak du popularno. Jednak pocztkowe zauroczenie przemienio si szybko w rozczarowanie. Rne wersje przegldarek Netscape i Internet
Explorer dziaay z rnymi wersjami Javy. Niektre z nich byy przestarzae. Ze wzgldu na
t przykr sytuacj tworzenie apletw przy wykorzystaniu najnowszych wersji Javy byo
coraz trudniejsze. Obecnie wikszo dynamicznych efektw na stronach internetowych jest
realizowana za pomoc JavaScriptu i technologii Flash. Java natomiast staa si najpopularniejszym jzykiem do tworzenia aplikacji dziaajcych po stronie serwera, ktre generuj
strony internetowe i stanowi ich zaplecze logiczne.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

30

Java. Podstawy

1.4. Krtka historia Javy


Podrozdzia ten krtko opisuje histori ewolucji Javy. Informacje tu zawarte pochodz z rnych rde (najwaniejsze z nich to wywiad z twrcami Javy opublikowany w internetowym magazynie SunWorld w 1995 roku).
Historia Javy siga 1991 roku, kiedy zesp inynierw z firmy Sun, ktrego przewodniczcymi byli Patrick Naughton i (wszdobylski geniusz komputerowy) James Gosling, piastujcy
jedno z najwyszych stanowisk w firmie o nazwie Sun Fellow, postanowi zaprojektowa
niewielki jzyk programowania nadajcy si do uytku w takich urzdzeniach konsumenckich jak tunery telewizji kablowej. Jako e urzdzenia te nie dysponuj du moc ani pamici, zaoono, e jzyk musi by bardzo niewielki i powinien generowa zwizy kod. Ponadto
ze wzgldu na fakt, e producenci mog w swoich urzdzeniach stosowa rne procesory,
jzyk ten nie mg by zwizany tylko z jedn architektur. Projekt otrzyma kryptonim Green.
Ch utworzenia kompaktowego i niezalenego od platformy kodu doprowadzia zesp do
wskrzeszenia modelu znanego z implementacji Pascala z wczesnych dni istnienia komputerw osobistych. Pionierski projekt przenonego jzyka generujcego kod poredni dla hipotetycznej maszyny nalea do Niklausa Wirtha wynalazcy Pascala (maszyny te nazywane
s czsto wirtualnymi, std nazwa wirtualna maszyna Javy). Ten kod poredni mona
byo nastpnie uruchamia na wszystkich urzdzeniach, ktre miay odpowiedni interpreter.
Inynierowie skupieni wok projektu Green take posuyli si maszyn wirtualn, rozwizujc w ten sposb swj gwny problem.
Jako e pracownicy firmy Sun obracali si w rodowisku uniksowym, swj jzyk oparli na
C++, a nie na Pascalu. Stworzony przez nich jzyk by obiektowy, a nie proceduralny. Jak
jednak mwi w wywiadzie Gosling: Przez cay czas jzyk by tylko narzdziem, a nie celem.
Gosling zdecydowa si nazwa swj jzyk Oak (db), prawdopodobnie dlatego e lubi
widok dbu stojcego za oknem jego biura w Sun. Pniej odkryto, e jzyk programowania
o tej nazwie ju istnia, i zmieniono nazw na Java. Okazao si to strzaem w dziesitk.
W 1992 roku inynierowie skupieni wok projektu Green przedstawili swoje pierwsze dzieo
o nazwie *7. By to niezwykle inteligentny pilot zdalnego sterowania (mia moc stacji SPARC
zamknit w pudeku o wymiarach 151010 centymetrw). Niestety, nikt w firmie Sun nie
by nim zainteresowany, przez co inynierowie musieli znale inny sposb na wypromowanie swojej technologii. Jednak adna z typowych firm produkujcych elektronik uytkow nie wykazaa zainteresowania. Nastpnym krokiem zespou by udzia w przetargu na
utworzenie urzdzenia TV Box obsugujcego takie nowe usugi telewizji kablowej jak filmy
na danie. Nie dostali jednak kontraktu (co zabawne, umow podpisa ten sam Jim Clark,
ktry zaoy firm Netscape firma ta miaa duy wkad w sukces Javy).
Inynierowie pracujcy nad projektem Green (przechrzczonym na First Person, Inc.) spdzili cay rok 1993 i poow 1994 na poszukiwaniu kupca dla ich technologii nie znaleli
nikogo (Patrick Naughton, ktry by jednym z zaoycieli zespou i zajmowa si promocj
jego produktw, twierdzi, e uzbiera 300 000 punktw Air Miles, prbujc sprzeda ich
technologi). Projekt First Person przesta istnie w 1994 roku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 1.

Wstp do Javy

31

Podczas gdy w firmie Sun miay miejsce te wszystkie wydarzenia, sie oglnowiatowa bdca
czci internetu cay czas si rozrastaa. Kluczem do sieci jest przegldarka, ktra interpretuje hipertekst i wywietla wynik na ekranie monitora. W 1994 roku wikszo uytkownikw internetu korzystaa z niekomercyjnej przegldarki o nazwie Mosaic, ktra powstaa
w 1993 roku w centrum komputerowym uniwersytetu Illinois (pracowa nad ni midzy
innymi Marc Andreessen, ktry by wtedy studentem tego uniwersytetu i dostawa 6,85 dolara
za godzin. Andreessen zdoby saw i pienidze jako jeden ze wspzaoycieli i szef dziau
technologii firmy Netscape).
W wywiadzie dla SunWorld Gosling przyzna, e w poowie 1994 roku projektanci jzyka
zdali sobie spraw, i mogli stworzy naprawd dobr przegldark. Bya to jedna z niewielu aplikacji klient-serwer nalecych do gwnego nurtu, wymagajca tych dziwnych rzeczy, ktre zrobilimy, czyli niezalenoci od architektury, pracy w czasie rzeczywistym, niezawodnoci i bezpieczestwa. W wiecie stacji roboczych pojcia te nie miay wielkiego
znaczenia. Postanowilimy wic napisa przegldark internetow.
Budow przegldarki, ktra przeobrazia si w przegldark o nazwie HotJava, zajli si
Patrick Naughton i Jonathan Payne. Przegldark HotJava naturalnie napisano w jzyku
Java, poniewa jej celem byo zaprezentowanie ogromnych moliwoci, ktre stwarza ten
jzyk. Programici pamitali jednak te o czym, co obecnie nazywamy apletami, i dodali
moliwo uruchamiania kodu wbudowanego w strony internetowe. 23 maja 1995 roku owoc
tej pracy, majcej na celu udowodnienie wartoci Javy, ujrza wiato dzienne w magazynie
SunWorld. Sta si on kamieniem wgielnym szalonej popularnoci Javy, ktra trwa do
dzisiaj.
Pierwsze wydanie Javy firma Sun opublikowaa na pocztku 1996 roku. Szybko zorientowano
si, e Java 1.0 nie stanie si narzdziem wykorzystywanym do tworzenia powanych aplikacji. Oczywicie mona byo za jej pomoc stworzy nerwowo poruszajcy si tekst w obszarze roboczym przegldarki, ale nie byo ju na przykad moliwoci drukowania. Mwic
szczerze, Java 1.0 nie bya gotowa na wielkie rzeczy. W kolejnej wersji, Java 1.1, uzupeniono najbardziej oczywiste braki, znacznie ulepszono refleksj i dodano model zdarze dla
programowania GUI. Jednak nadal moliwoci byy raczej ograniczone.
Wielkim wydarzeniem na konferencji JavaOne w 1998 roku byo ogoszenie, e niebawem
pojawi si Java 1.2. Zastpiono w niej dziecinne narzdzia do obrbki grafiki i tworzenia
GUI wyrafinowanymi i skalowalnymi wersjami, ktre znacznie przybliay spenienie obietnicy: Napisz raz, uruchamiaj wszdzie w stosunku do poprzednich wersji. Trzy dni po jej
wydaniu (!), w grudniu 1998 roku, dzia marketingu firmy Sun zmieni nazw Java 1.2 na
bardziej chwytliw Java 2 Standard Edition Software Development Kit Version 1.2.
Poza wydaniem standardowym opracowano jeszcze dwa inne: Micro Edition dla urzdze
takich jak telefony komrkowe oraz Enterprise Edition do przetwarzania po stronie serwera.
Ta ksika koncentruje si na wersji standardowej.
Kolejne wersje Java 1.3 i Java 1.4 to stopniowe ulepszenia w stosunku do pocztkowej wersji
Java 2. Jednoczenie rozrastaa si biblioteka standardowa, zwikszaa si wydajno i oczywicie poprawiono wiele bdw. W tym samym czasie ucicha wrzawa wok apletw i aplikacji dziaajcych po stronie klienta, a Java staa si najczciej wybieran platform do tworzenia aplikacji dziaajcych po stronie serwera.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

32

Java. Podstawy
Pierwsza wersja Javy, w ktrej wprowadzono znaczce zmiany w jzyku programowania
Java w stosunku do wersji 1.1, miaa numer 5 (pierwotnie by to numer 1.5, ale na konferencji JavaOne w 2004 roku podskoczy do pitki). Po wielu latach bada dodano typy sparametryzowane (ang. generic types), ktre mona z grubsza porwna do szablonw w C++.
Sztuka polegaa na tym, aby przy dodawaniu tej funkcji nie zmienia nic w maszynie wirtualnej. Niektre z dodanych funkcji zostay zaczerpnite z jzyka C#: ptla for each, moliwo automatycznej konwersji typw prostych na referencyjne i odwrotnie (ang. autoboxing)
oraz metadane.
Wersja 6 (bez przyrostka .0) ujrzaa wiat pod koniec 2006 roku. Tym razem rwnie nie
wprowadzono adnych zmian w jzyku, ale zastosowano wiele usprawnie zwizanych
z wydajnoci i rozszerzono bibliotek.
W centrach danych zaczto rzadziej korzysta ze specjalistycznego sprztu serwerowego,
przez co firma Sun Microsystems wpada w tarapaty i w 2009 roku zostaa wykupiona przez
Oracle. Rozwj Javy zosta na duszy czas wstrzymany. Jednak w 2011 roku firma Oracle
opublikowaa kolejn wersj jzyka z drobnymi ulepszeniami o nazwie Java 7. Powaniejsze zmiany przeoono do wersji Java 8, ktrej ukazanie si jest planowane na 2013 rok.
Tabela 1.1 przedstawia ewolucj jzyka Java i jego biblioteki. Jak wida, rozmiar interfejsu
programistycznego (API) rs w rekordowym tempie.

Tabela 1.1. Ewolucja jzyka Java


Wersja

Rok

Nowe funkcje jzyka

Liczba klas i interfejsw

1.0

1996

Powstanie jzyka

211

1.1

1997

Klasy wewntrzne

477

1.2

1998

Brak

1524

1.3

2000

Brak

1840

1.4

2002

Asercje

2723

5.0

2004

Klasy sparametryzowane, ptla for each, atrybuty


o zmiennej liczbie argumentw (varargs), enumeracje,
statyczny import

3279

2006

Brak

3793

2011

Instrukcja switch z acuchami, operator diamentowy,


literay binarne, udoskonalenia mechanizmu obsugi
wyjtkw

4024

1.5. Gwne nieporozumienia dotyczce Javy


Koczymy ten rozdzia kilkoma uwagami zwizanymi z nieporozumieniami dotyczcymi Javy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 1.

Wstp do Javy

33

Java jest rozszerzeniem jzyka HTML.


Java jest jzykiem programowania, a HTML to sposb opisu struktury stron internetowych.
Nie maj ze sob nic wsplnego z wyjtkiem tego, e w HTML s dostpne rozszerzania
umoliwiajce wstawianie apletw Javy na strony HTML.
Uywam XML, wic nie potrzebuj Javy.
Java to jzyk programowania, a XML jest sposobem opisu danych. Dane w formacie XML
mona przetwarza za pomoc wielu jzykw programowania, ale API Javy ma doskonae
narzdzia do przetwarzania XML. Ponadto wiele znaczcych narzdzi XML jest zaimplementowanych w Javie. Wicej informacji na ten temat znajduje si w drugiej czci tej ksiki.
Java jest atwa do nauki.
aden jzyk programowania o tak duych moliwociach jak Java nie jest atwy do nauczenia
si. Trzeba odrnia, jak atwo napisa program do zabawy i jak trudno napisa powan
aplikacj. Warto zauway, e opisowi jzyka Java w tej ksice powicone zostay tylko
cztery rozdziay. Pozostae rozdziay w obu czciach opisuj sposoby wykorzystania tego
jzyka przy uyciu bibliotek Javy. Biblioteki te zawieraj tysice klas i interfejsw oraz
dziesitki tysicy funkcji. Na szczcie nie trzeba ich wszystkich zna, ale trzeba zapozna
si z zaskakujco du ich liczb, aby mc zrobi cokolwiek dobrego w Javie.
Java stanie si uniwersalnym jzykiem programowania dla wszystkich platform.
Teoretycznie jest to moliwe i praktycznie wszyscy poza firm Microsoft chcieliby, aby tak
si stao. Jednak wiele aplikacji, ktre bardzo dobrze dziaaj na komputerach biurkowych,
nie dziaaoby prawidowo na innych urzdzeniach lub w przegldarkach. Ponadto aplikacje te
zostay napisane w taki sposb, aby maksymalnie wykorzysta moliwoci procesora i natywnej biblioteki interfejsowej, oraz zostay ju przeniesione na wszystkie najwaniejsze
platformy. Do tego typu aplikacji nale procesory tekstu, edytory zdj i przegldarki internetowe. Wikszo z nich zostaa napisana w jzykach C i C++, a ponowne napisanie ich
w Javie nie przyniosoby uytkownikom adnych korzyci.
Java jest tylko kolejnym jzykiem programowania.
Java to bardzo przyjazny jzyk programowania. Wikszo programistw przedkada go nad
C, C++ czy C#. Jednak przyjaznych jzykw programowania jest bardzo duo, a nigdy nie
zyskay one duej popularnoci, podczas gdy jzyki zawierajce powszechnie znane wady,
jak C++ i Visual Basic, ciesz si ogromnym powodzeniem.
Dlaczego? Powodzenie jzyka programowania jest bardziej uzalenione od przydatnoci jego
systemu wsparcia ni od elegancji skadni. Czy istniej przydatne i wygodne standardowe
biblioteki funkcji, ktre chcesz zaimplementowa? Czy s firmy produkujce doskonae
rodowiska programistyczne i wspomagajce znajdywanie bdw? Czy jzyk i jego narzdzia integruj si z reszt infrastruktury komputerowej? Sukcesu Javy naley upatrywa w tym,
e mona w niej robi z atwoci takie rzeczy, ktre kiedy byy bardzo trudne mona
tu zaliczy na przykad wielowtkowo i programowanie sieciowe. Dziki zmniejszeniu
liczby bdw wynikajcych z uywania wskanikw programici wydaj si bardziej produktywni, co jest oczywicie zalet, ale nie stanowi rda sukcesu Javy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

34

Java. Podstawy
Po pojawieniu si jzyka C# Java idzie w zapomnienie.
Jzyk C# przej wiele dobrych pomysw od Javy, jak czysto jzyka programowania,
maszyna wirtualna czy automatyczne usuwanie nieuytkw. Jednak z niewiadomych przyczyn wielu dobrych rzeczy w tym jzyku brakuje, zwaszcza zabezpiecze i niezalenoci
od platformy. Dla tych, ktrzy s zwizani z systemem Windows, jzyk C# wydaje si dobrym
wyborem. Sdzc jednak po ogoszeniach dotyczcych oferowanej pracy, Java nadal stanowi
wybr wikszoci deweloperw.
Java jest wasnoci jednej firmy i dlatego naley jej unika.
Po utworzeniu Javy firma Sun Microsystems udzielaa darmowych licencji na Jav dystrybutorom i uytkownikom kocowym. Mimo e firma ta sprawowaa pen kontrol nad Jav,
w proces tworzenia nowych wersji jzyka i projektowania nowych bibliotek zostao zaangaowanych wiele firm. Kod rdowy maszyny wirtualnej i bibliotek by zawsze oglnodostpny, ale tylko do wgldu. Nie mona go byo modyfikowa ani ponownie rozdziela.
Do tej pory Java bya zamknita, ale dobrze si sprawowaa.
Sytuacja ulega radykalnej zmianie w 2007 roku, kiedy firma Sun ogosia, e przysze wersje
Javy bd dostpne na licencji GPL, tej samej otwartej licencji, na ktrej dostpny jest system
Linux. Firma Oracle zobowizaa si pozostawi Jav otwart. Jest tylko jedna rysa na tej
powierzchni patenty. Na mocy licencji GPL kady moe uywa Javy i j modyfikowa, ale
dotyczy to tylko zastosowa desktopowych i serwerowych. Jeli kto chce uywa Javy w ukadach wbudowanych, musi mie inn licencj, za ktr najpewniej bdzie musia zapaci. Jednak
patenty te w cigu najbliszych kilku lat wygasn i wwczas Java bdzie cakowicie darmowa.
Java jest jzykiem interpretowanym, a wic jest zbyt powolna do powanych zastosowa.
Na pocztku Java bya interpretowana. Obecnie poza platformami skali mikro (jak telefony
komrkowe) maszyna wirtualna Javy wykorzystuje kompilator czasu rzeczywistego. Najczciej uywane czci kodu dziaaj tak szybko, jakby byy napisane w C++, a w niektrych przypadkach nawet szybciej.
Java ma pewien narzut w stosunku do C++. Uruchamianie maszyny wirtualnej zajmuje sporo
czasu, poza tym GUI w Javie s wolniejsze od ich natywnych odpowiednikw, poniewa
zostay przystosowane do pracy na rnych platformach.
Przez wiele lat ludzie skaryli si, e Java jest powolna. Jednak dzisiejsze komputery s duo
szybsze od tych, ktre byy dostpne w czasach, gdy zaczto si na to skary. Powolny
program w Javie i tak dziaa nieco szybciej ni niewiarygodnie szybkie programy napisane
kilka lat temu w C++. Obecnie te skargi brzmi jak echo dawnych czasw, a niektrzy zaczli
dla odmiany narzeka na to, e interfejsy uytkownika w Javie s brzydsze ni wolniejsze.
Wszystkie programy pisane w Javie dziaaj na stronach internetowych.
Wszystkie aplety Javy dziaaj wewntrz przegldarki. Takie s z zaoenia aplety s to
programy napisane w Javie, ktre dziaaj wewntrz okna przegldarki. Jednak wikszo
programw pisanych w Javie to samodzielne aplikacje, dziaajce poza przegldark internetow. W rzeczywistoci wiele programw w Javie dziaa po stronie serwera i generuje kod
stron WWW.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 1.

Wstp do Javy

35

Programy w Javie s zagroeniem bezpieczestwa.


Na pocztku istnienia Javy opublikowano kilka raportw opisujcych bdy w systemie zabezpiecze Javy. Wikszo z nich dotyczyo implementacji Javy w okrelonej przegldarce.
Badacze potraktowali zadanie znalezienia wyrw w murze obronnym Javy i zamania siy
oraz wyrafinowania modelu zabezpiecze apletw jako wyzwanie. Znalezione przez nich
techniczne usterki zostay szybko naprawione i wedug naszej wiedzy adne rzeczywiste
systemy nie zostay jeszcze zamane. Spjrzmy na to z innej perspektywy w systemie
Windows miliony wirusw atakujcych pliki wykonywalne i makra programu Word spowodoway bardzo duo szkd, ale wywoay niewiele krytyki na temat saboci atakowanej
platformy. Take mechanizm ActiveX w przegldarce Internet Explorer moe by dobr
poywk dla naduy, ale jest to tak oczywiste, e z nudw niewielu badaczy publikuje
swoje odkrycia na ten temat.
Niektrzy administratorzy systemu wyczyli nawet Jav w przegldarkach firmowych,
a pozostawili moliwo pobierania plikw wykonywalnych i dokumentw programu Word,
ktre s o wiele bardziej grone. Nawet 15 lat od momentu powstania Java jest znacznie
bardziej bezpieczna ni jakakolwiek inna powszechnie uywana platforma.
Jzyk JavaScript to uproszczona wersja Javy.
JavaScript, skryptowy jzyk stosowany na stronach internetowych, zosta opracowany przez
firm Netscape i pocztkowo jego nazwa brzmiaa LiveScript. Skadni JavaScript przypomina Jav, ale poza tym jzyki te nie maj ze sob nic wsplnego (oczywicie wyczajc
nazw). Podzbir JavaScriptu jest opublikowany jako standard ECMA-262. Jzyk ten jest
cilej zintegrowany z przegldarkami ni aplety Javy. Programy w JavaScripcie mog wpywa na wygld wywietlanych dokumentw, podczas gdy aplety mog sterowa zachowaniem tylko ograniczonej czci okna.
Dziki Javie mog wymieni mj komputer na terminal internetowy za 1500 zotych.
Po pierwszym wydaniu Javy niektrzy ludzie gotowi byliby postawi due pienidze, e tak
si stanie. Od pierwszego wydania tej ksiki utrzymujemy, e twierdzenie, i uytkownicy
domowi zechc zastpi wszechstronne komputery ograniczonymi urzdzeniami pozbawionymi pamici, jest absurdalne. Wyposaony w Jav komputer sieciowy mgby by prawdopodobnym rozwizaniem umoliwiajcym wdroenie strategii jednokrotnego ustawienia
opcji konfiguracyjnych bez potrzeby pniejszego wracania do nich (ang. zero administration initiative). Umoliwioby to zmniejszenie kosztw ponoszonych na utrzymanie komputerw w firmach, ale jak na razie nie wida wielkiego ruchu w tym kierunku. W aktualnie
dostpnych tabletach Java nie jest wykorzystywana.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

36

Java. Podstawy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

rodowisko
programistyczne Javy
W tym rozdziale:

Instalacja oprogramowania Java Development Kit

Wybr rodowiska programistycznego

Korzystanie z narzdzi wiersza polece

Praca w zintegrowanym rodowisku programistycznym

Uruchamianie aplikacji graficznej

Budowa i uruchamianie apletw

W tym rozdziale nauczysz si instalowa oprogramowanie Java Development Kit (JDK) oraz
kompilowa i uruchamia rne typy programw: programy konsolowe, aplikacje graficzne
i aplety. Narzdzia JDK s uruchamiane za pomoc polece wpisywanych w oknie interpretera
polece. Wielu programistw woli jednak wygod pracy w zintegrowanym rodowisku programistycznym. Opisalimy jedno dostpne bezpatnie rodowisko, w ktrym mona kompilowa i uruchamia programy napisane w Javie. Mimo niewtpliwych zalet, takich jak
atwo nauki, takie rodowiska pochaniaj bardzo duo zasobw i bywaj nieporczne przy
pisaniu niewielkich aplikacji. Prezentujemy zatem kompromisowe rozwizanie w postaci
edytora tekstowego, ktry umoliwia uruchamianie kompilatora Javy i programw napisanych w tym jzyku. Jeli opanujesz techniki opisywane w tym rozdziale i wybierzesz odpowiednie dla siebie narzdzia programistyczne, moesz przej do rozdziau 3., od ktrego
zaczyna si opis jzyka programowania Java.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

38

Java. Podstawy

2.1. Instalacja oprogramowania


Java Development Kit
Najpeniejsze i najnowsze wersje pakietu JDK dla systemw Linux, Mac OS X, Solaris
i Windows s dostpne na stronach firmy Oracle. Istniej te wersje w rnych fazach rozwoju dla wielu innych platform, ale podlegaj one licencjom i s rozprowadzane przez firmy
produkujce te platformy.

2.1.1. Pobieranie pakietu JDK


Aby pobra odpowiedni dla siebie pakiet Java Development Kit, trzeba przej na stron
internetow www.oracle.com/technetwork/java/javase/ i rozszyfrowa cae mnstwo argonowych poj (zobacz zestawienie w tabeli 2.1).
Tabela 2.1. Pojcia specyficzne dla Javy
Nazwa

Akronim

Objanienie

Java Development Kit

JDK

Oprogramowanie dla programistw, ktrzy chc pisa programy


w Javie.

Java Runtime Environment JRE

Oprogramowanie dla klientw, ktrzy chc uruchamia programy


napisane w Javie.

Standard Edition

SE

Platforma Javy do uytku na komputerach biurkowych i w przypadku


prostych zastosowa serwerowych.

Enterprise Edition

EE

Platforma Javy przeznaczona do skomplikowanych zastosowa


serwerowych.

Micro Edition

ME

Platforma Javy znajdujca zastosowanie w telefonach komrkowych


i innych maych urzdzeniach.

Java 2

J2

Przestarzay termin okrelajcy wersje Javy od 1998 do 2006 roku.

Software Development Kit

SDK

Przestarzay termin, ktry oznacza pakiet JDK od 1998


do 2006 roku.

Update

Termin okrelajcy wydanie z poprawionym bdem.

NetBeans

Zintegrowane rodowisko programistyczne firmy Oracle.

Znamy ju skrt JDK oznaczajcy Java Development Kit. eby nie byo za atwo, informujemy, e wersje od 1.2 do 1.4 tego pakietu miay nazw Java SDK (ang. Software Development Kit). Wci mona znale odwoania do tej starej nazwy. Jest te Java Runtime
Environment (JRE), czyli oprogramowanie zawierajce maszyn wirtualn bez kompilatora.
Jako programici nie jestemy tym zainteresowani. Ten program jest przeznaczony dla uytkownikw kocowych, ktrym kompilator nie jest potrzebny.
Kolej na wszdobylski termin Java SE. Jest to Java Standard Edition, w odrnieniu od
Java EE (ang. Enterprise Edition) i Java ME (ang. Micro Edition).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

39

Czasami mona te spotka termin Java 2, ktry zosta ukuty w 1998 roku przez dzia marketingu w firmie Sun. Uwaano, e zwikszenie numeru wersji o uamek nie oddaje w peni
postpu, jakiego dokonano w JDK 1.2. Jednak jako e pniej zmieniono zdanie zdecydowano si zachowa numer 1.2. Kolejne wydania miay numery 1.3, 1.4 i 5.0. Zmieniono
jednak nazw platformy z Java na Java 2. W ten sposb powsta pakiet Java 2 Standard
Edition Software Development Kit Version 5.0, czyli J2SE SDK 5.0.
Inynierowie mieli problemy z poapaniem si w tych nazwach, ale na szczcie w 2006 roku
zwyciy rozsdek. Bezuyteczny czon Java 2 zosta usunity, a aktualna wersja Java Standard Edition zostaa nazwana Java SE 6. Nadal mona sporadycznie spotka odwoania do
wersji 1.5 i 1.6, ale s one synonimami wersji 5 i 6.
Na zakoczenie trzeba doda, e mniejsze zmiany wprowadzane w celu naprawienia usterek
przez firm Oracle nazywane s aktualizacjami (ang. updates). Na przykad pierwsza aktualizacja pakietu programistycznego dla Java SE 7 ma oficjaln nazw JDK 7u1, ale jej
wewntrzny numer wersji to 1.7.0_01. Aktualizacje nie musz by instalowane na bazie starszych wersji zawieraj najnowsze wersje caego pakietu JDK.
Czasami firma Oracle udostpnia paczki zawierajce zarwno pakiet Java Development Kit,
jak i zintegrowane rodowisko programistyczne. Jego nazwy kilkakrotnie si zmieniay; do tej
pory mona si byo spotka z Forte, Sun ONE Studio, Sun Java Studio i NetBeans. Trudno
zgadn, jak nazw nadadz mu nastpnym razem nadgorliwcy z dziau marketingu. Na razie
zalecamy wic zainstalowanie jedynie pakietu JDK. Jeli zdecydujesz si pniej na uywanie
rodowiska firmy Sun, pobierz je ze strony http://netbeans.org.
W trakcie instalacji sugerowany jest domylny katalog na pliki, w ktrego nazwie
znajduje si numer wersji pakietu JDK, np. jdk1.7.0. Na pierwszy rzut oka wydaje
si to niepotrzebn komplikacj, ale spodobao nam si to z tego wzgldu, e w ten
sposb o wiele atwiej mona zainstalowa nowe wydanie JDK do testowania.
Uytkownikom systemu Windows odradzamy akceptacj domylnej cieki ze spacjami
w nazwie, jak C:\Program Files\jdk1.7.0. Najlepiej usun z tej cieki cz Program
Files.
W tej ksice katalog instalacji okrelamy mianem jdk. Kiedy na przykad piszemy o katalogu jdk/bin, mamy na myli ciek typu /usr/local/jdk1.7.0/bin lub C:\jdk1.7.0\bin.

2.1.2. Ustawianie cieki dostpu


Po instalacji pakietu JDK trzeba wykona jeszcze jedn czynno: doda katalog jdk/bin do
cieki dostpu, czyli listy katalogw, ktre przemierza system operacyjny w poszukiwaniu
plikw wykonywalnych. Postpowanie w tym przypadku jest inne w kadym systemie operacyjnym.

W systemie Unix (wliczajc Linux, Mac OS X i Solaris) sposb edycji cieki dostpu
zaley od uywanej powoki. Uytkownicy powoki Bourne Again (ktra jest
domylna dla systemu Linux) musz na kocu pliku ~/.bashrc lub ~/.bash.profile
doda nastpujcy wiersz:
export PATH=jdk/bin:$PATH

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

40

Java. Podstawy

W systemie Windows naley zalogowa si jako administrator. Przejd do Panelu


sterowania, przecz na widok klasyczny i kliknij dwukrotnie ikon System.
W systemie Windows XP od razu otworzy si okno Waciwoci systemu.
W systemie Windows Vista i Windows 7 naley klikn pozycj Zaawansowane
ustawienia systemu (zobacz rysunek 2.1). W oknie dialogowym Waciwoci
systemu kliknij kart Zaawansowane, a nastpnie przycisk Zmienne rodowiskowe.
W oknie Zmienne rodowiskowe znajd zmienn o nazwie Path. Kliknij przycisk
Edytuj (zobacz rysunek 2.2). Dodaj katalog jdk\bin na pocztku cieki i wpis ten
oddziel od reszty wpisw rednikiem, jak poniej:
jdk\bin;inne wpisy

Rysunek 2.1. Otwieranie okna waciwoci systemu w systemie Windows Vista

Sowo jdk naley zastpi ciek do katalogu instalacyjnego Javy, np. c:\jdk1.7.0_02.
Jeli zainstalowae Jav w folderze Program Files, ca ciek wpisz
w cudzysowie: "c:\Program Files\jdk1.7.0_02\bin";inne wpisy.
Zapisz ustawienia. Kade nowe okno konsoli bdzie wykorzystywa prawidow
ciek.
Oto jak mona sprawdzi, czy powysze czynnoci zostay wykonane prawidowo: otwrz
okno konsoli i wpisz ponisze polecenie:
javac -version

a nastpnie nacinij klawisz Enter. Na ekranie powinien pojawi si nastpujcy tekst:


javac 1.7.0_02

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

41

Rysunek 2.2. Ustawianie zmiennej rodowiskowej Path w systemie Windows Vista

Jeli zamiast tego ukae si komunikat typu javac: polecenie nie zostao znalezione lub

Nazwa nie jest rozpoznawana jako polecenie wewntrzne lub zewntrzne, program wykonywalny lub plik wsadowy, trzeba wrci do pocztku i dokadnie sprawdzi swoj instalacj.
Aby otworzy okno konsoli w systemie Windows, naley postpowa zgodnie z nastpujcymi wskazwkami: w systemie Windows XP kliknij opcj Uruchom w menu
Start i wpisz polecenie cmd. W systemach Windows Vista i 7 wystarczy wpisa cmd w polu
Rozpocznij wyszukiwanie w menu Start. Nastpnie nacinij klawisz Enter.
Osobom, ktre nigdy nie miay do czynienia z oknem konsoli, zalecamy zapoznanie si
z kursem objaniajcym podstawy korzystania z tego narzdzia dostpnym pod adresem
http://www.horstmann.com/bigj/help/windows/tutorial.html.

2.1.3. Instalacja bibliotek i dokumentacji


Kod rdowy bibliotek w pakiecie JDK jest dostpny w postaci skompresowanego pliku
o nazwie src.zip. Oczywicie, aby uzyska dostp do tego rda, trzeba niniejszy plik rozpakowa. Gorco do tego zachcamy. Wystarczy wykona nastpujce czynnoci:
1.

Upewnij si, e po zainstalowaniu pakietu JDK katalog jdk/bin znajduje si


w ciece dostpu.

2. Otwrz okno konsoli.


3. Przejd do katalogu jdk (np. cd /usr/local/jdk1.7.0 lub cd c:\jdk1.7.0).
4. Utwrz podkatalog src.
mkdir src
cd src

5. Wykonaj polecenie:
jar xvf ../src.zip

albo jar xvf ..\src.zip w systemie Windows.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

42

Java. Podstawy

Plik src.zip zawiera kod rdowy wszystkich bibliotek publicznych. Wicej rde
(dla kompilatora, maszyny wirtualnej, metod rodzimych i prywatnych klas pomocniczych) mona znale na stronie http://jdk7.java.net.

Dokumentacja znajduje si w oddzielnym, skompresowanym pliku. Mona j pobra ze strony


www.oracle.com/technetwork/java/javase/downloads. Sprowadza si to do wykonania kilku
prostych czynnoci:
1.

Upewnij si, e po zainstalowaniu pakietu JDK katalog jdk/bin znajduje si


w ciece dostpu.

2. Pobierz plik archiwum zip zawierajcy dokumentacj i zapisz go w katalogu jdk.

Plik ten ma nazw jdk-wersja-apidocs.zip, gdzie wersja to numer wersji, np. 7.


3. Otwrz okno konsoli.
4. Przejd do katalogu jdk.
5. Wykonaj ponisze polecenie:
jar xvf jdk-wersja-apidocs.zip

gdzie wersja to odpowiedni numer wersji.

2.1.4. Instalacja przykadowych programw


Naley te zainstalowa przykadowe programy z tej ksiki. Mona je pobra ze strony
http://horstmann.com/corejava. Programy te znajduj si w pliku archiwum ZIP o nazwie
corejava.zip. Naley je wypakowa do oddzielnego katalogu polecamy utworzenie katalogu o nazwie JavaPodstawy. Oto zestawienie wymaganych czynnoci:
1.

Upewnij si, e po zainstalowaniu pakietu JDK katalog jdk/bin znajduje si


w ciece dostpu.

2. Utwrz katalog o nazwie JavaPodstawy.


3. Pobierz z internetu i zapisz w tym katalogu plik corejava.zip.
4. Otwrz okno konsoli.
5. Przejd do katalogu JavaPodstawy.
6. Wykonaj ponisze polecenie:
jar xvf corejava.zip

2.1.5. Drzewo katalogw Javy


Zagbiajc si w Jav, zechcesz sporadycznie zajrze do plikw rdowych. Bdziesz te
oczywicie zmuszony do pracy z dokumentacj techniczn. Rysunek 2.3 obrazuje drzewo
katalogw JDK.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

Struktura katalogw

Opis

jdk

Moe to by jedna z kilku nazw, np. jdk1.7.0_02


bin

Kompilator i inne narzdzia

demo

Przykadowe programy

docs

Dokumentacja biblioteki w formacie HTML


(po rozpakowaniu pliku j2sdkwersja-doc.zip)

include

Pliki potrzebne do kompilacji metod rodzimych (zobacz drugi tom)

jre

Pliki rodowiska uruchomieniowego Javy

lib

Pliki biblioteki

src

rda biblioteki (po rozpakowaniu pliku src.zip)

43

Rysunek 2.3. Drzewo katalogw Javy

Dla uczcych si Javy najwaniejsze s katalogi docs i src. Katalog docs zawiera dokumentacj biblioteki Javy w formacie HTML. Mona j przeglda za pomoc dowolnej przegldarki internetowej, jak chociaby Firefox.
W swojej przegldarce dodaj do ulubionych stron docs/api/index.html. W trakcie poznawania platformy Java bdziesz do niej czsto zaglda.

Katalog src zawiera kod rdowy publicznych bibliotek Javy. W miar zdobywania wiedzy
na temat Javy by moe bdziesz chcia uzyska wicej informacji, ni dostarcza niniejsza
ksika i dokumentacja. W takiej sytuacji najlepszym miejscem do rozpoczcia poszukiwa
jest kod rdowy Javy. wiadomo, e zawsze mona zajrze do kodu rdowego, aby
sprawdzi, jak faktycznie dziaa dana funkcja biblioteczna, ma w duym stopniu dziaanie
uspokajajce. Jeli chcemy na przykad zbada wntrze klasy System, moemy zajrze do
pliku src/java/Lang/System.java.

2.2. Wybr rodowiska programistycznego


Osoby, ktre do tej pory pracoway w rodowisku Microsoft Visual Studio, s przyzwyczajone do rodowiska z wbudowanym edytorem tekstu i menu, udostpniajcymi opcje kompilacji i uruchamiania programu oraz zintegrowanego debugera. Podstawowy pakiet JDK
nie oferuje nawet zblionych moliwoci. Wszystko robi si poprzez wpisywanie odpowiednich polece w oknie konsoli. Brzmi strasznie, niemniej jest to nieodzowna umiejtno
programisty. Po zainstalowaniu Javy moe by konieczne usunicie usterek dotyczcych tej
instalacji, a dopiero potem mona zainstalowa rodowisko programistyczne. Ponadto dziki
wykonaniu podstawowych czynnoci we wasnym zakresie mona lepiej zrozumie, co rodowisko programistyczne robi za naszymi plecami.
Po opanowaniu podstawowych czynnoci kompilowania i uruchamiania programw w Javie
zechcemy jednak przenie si do profesjonalnego rodowiska programistycznego. W cigu

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

44

Java. Podstawy
ostatnich lat rodowiska te stay si tak wygodne i wszechstronne, e nie ma sensu mczy
si bez nich. Dwa z nich zasuguj na wyrnienie: Eclipse i NetBeans. Oba s dostpne bezpatnie. W tym rozdziale opisujemy, jak rozpocz prac w rodowisku Eclipse, jako e jest
ono nieco lepsze od NetBeans, cho to drugie szybko dogania swojego konkurenta. Oczywicie do pracy z t ksik mona uy take dowolnego innego rodowiska.
Kiedy do pisania prostych programw polecalimy edytory tekstowe, takie jak Emacs, JEdit
czy TextPad. Ze wzgldu na fakt, e zintegrowane rodowiska s ju bardzo szybkie i wygodne, teraz zalecamy uywanie wanie nich.
Podsumowujc, naszym zdaniem kady powinien zna podstawy obsugi narzdzi JDK, a po
ich opanowaniu przej na zintegrowane rodowisko programistyczne.

2.3. Uywanie narzdzi wiersza polece


Zacznijmy od mocnego uderzenia: kompilacji i uruchomienia programu w Javie w wierszu
polece.
1.

Otwrz okno konsoli.

2. Przejd do katalogu JavaPodstawy/t1/r02/Welcome (katalog JavaPodstawy

to ten, w ktrym zapisalimy kod rdowy programw prezentowanych w tej


ksice, o czym bya mowa w podrozdziale 2.1.4, Instalacja przykadowych
programw).
3. Wpisz nastpujce polecenia:
javac Welcome.java
java Welcome

Wynik w oknie konsoli powinien by taki jak na rysunku 2.4.


Rysunek 2.4.
Kompilacja
i uruchamianie
programu
Welcome.java

Gratulacje! Wanie skompilowalimy i uruchomilimy nasz pierwszy program w Javie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

45

Co si wydarzyo? Program o nazwie javac to kompilator Javy. Skompilowa plik o nazwie


Welcome.java na plik Welcome.class. Program java uruchamia wirtualn maszyn Javy.
Wykonuje kod bajtowy zapisany w pliku klasy przez kompilator.
Jeli w poniszym wierszu pojawi si komunikat o bdzie:
for (String g : greeting)

to znaczy, e uywasz bardzo starej wersji kompilatora Javy. Uytkownicy starszych wersji
Javy musz zastpi powysz ptl nastpujc:
for (int i = 0; i < greeting.length; i++)
System.out.println(greeting[i]);

Program Welcome jest niezwykle prosty. Wywietla tylko wiadomo w konsoli. Jego kod rdowy przedstawia listing 2.1 (sposb dziaania tego kodu opisujemy w nastpnym rozdziale).
Listing 2.1. Welcome/Welcome.java
/**
* Program ten wywietla wiadomo powitaln od autorw.
* @version 1.20 2004-02-28
* @author Cay Horstmann
*/
public class Welcome
{
public static void main(String[] args)
{
String[] greeting = new String[3];
greeting[0] = "Witaj, czytelniku!";
greeting[1] = "Pozdrowienia od Caya Horstmanna";
greeting[2] = "i Gary'ego Cornella";
for (String g : greeting)
System.out.println(g);
}
}

2.3.1. Rozwizywanie problemw


W erze wizualnych rodowisk programistycznych wielu programistw nie potrafi uruchamia programw w oknie konsoli. Wiele rzeczy moe nie pj zgodnie z planem, co prowadzi do rozczarowa.
Zwr uwag na nastpujce rzeczy:

Jeli wpisujesz program rcznie, zwracaj baczn uwag na wielko liter.


W szczeglnoci pamitaj, e nazwa klasy to Welcome, a nie welcome lub WELCOME.

Kompilator wymaga nazwy pliku (Welcome.java). Aby uruchomi program,


naley poda nazw klasy (Welcome) bez rozszerzenia .java lub .class.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

46

Java. Podstawy

Jeli pojawi si komunikat typu ze polecenie lub za nazwa pliku bd javac:


polecenie nie zostao znalezione, naley wrci i dokadnie sprawdzi swoj
instalacj, zwaszcza ustawienia cieki dostpu.

Jeli kompilator javac zgosi bd typu cannot read: Welcome.java, naley sprawdzi,
czy plik ten znajduje si w odpowiednim katalogu.
W systemie Unix naley sprawdzi wielko liter w nazwie pliku Welcome.java.
W systemie Windows naley uy polecenia dir w oknie konsoli, nie w Eksploratorze.
Niektre edytory tekstu (zwaszcza Notatnik) dodaj na kocu nazwy kadego
pliku rozszerzenie .txt. Jeli program Welcome.java by edytowany za pomoc
Notatnika, to zosta zapisany jako Welcome.java.txt. Przy domylnych ustawieniach
systemowych Eksplorator dziaa w zmowie z Notatnikiem i ukrywa rozszerzenie
.txt, poniewa naley ono do znanych typw plikw. W takim przypadku trzeba
zmieni nazw pliku za pomoc polecenia ren lub zapisa go ponownie, ujmujc
nazw w cudzysowy: "Welcome.java".

Jeli po uruchomieniu programu pojawi si komunikat o bdzie java.lang.


NoClassDefFoundError, dokadnie sprawd nazw klasy, ktra sprawia problemy.
Jeli bd dotyczy nazwy welcome (pisanej ma liter), naley jeszcze raz wyda
polecenie java Welcome z wielk liter W. Jak zawsze w Javie wielko liter ma
znaczenie.
Jeli bd dotyczy Welcome/java, oznacza to, e przypadkowo wpisano polecenie
java Welcome.java. Naley jeszcze raz wpisa polecenie java Welcome.

Jeli po wpisaniu polecenia java Welcome maszyna wirtualna nie moe znale
klasy Welcome, naley sprawdzi, czy kto nie ustawi w systemie zmiennej
rodowiskowej CLASSPATH (ustawianie na poziomie globalnym nie jest dobrym
pomysem, ale niektre sabej jakoci instalatory oprogramowania w systemie
Windows tak wanie robi). Zmienn t mona usun tymczasowo w oknie
konsoli za pomoc polecenia:
set CLASSPATH=

To polecenie dziaa w systemach Windows oraz Unix i Linux z powok C.


W systemach Unix i Linux z powok Bourne/bash naley uy polecenia:
export CLASSPATH=

W doskonaym kursie znajdujcym si pod adresem http://docs.oracle.com/


javase/tutorial/getStarted/ mona znale opisy znacznie wikszej liczby puapek,
w ktre wpadaj pocztkujcy programici.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

47

2.4. Praca w zintegrowanym


rodowisku programistycznym
W tym podrozdziale nauczysz si kompilowa programy w zintegrowanym rodowisku programistycznym o nazwie Eclipse, ktre mona nieodpatnie pobra ze strony http://eclipse.org.
Program ten zosta napisany w Javie, ale ze wzgldu na uyt w nim niestandardow bibliotek okien nie jest on tak przenony jak sama Java. Niemniej istniej jego wersje dla systemw Linux, Mac OS X, Solaris i Windows.
Dostpnych jest jeszcze kilka innych IDE, ale Eclipse cieszy si obecnie najwiksz popularnoci. Oto podstawowe kroki pocztkujcego:
1.

Po uruchomieniu programu Eclipse kliknij opcj File/New Project.

2. W oknie kreatora wybierz pozycj Java Project (zobacz rysunek 2.5). Te zrzuty

zostay zrobione w wersji 3.3 Eclipse. Nie jest to jednak wymg i moesz uywa
innej wersji tego rodowiska.
Rysunek 2.5.
Okno dialogowe
New Project
w Eclipse

3. Kliknij przycisk Next. Wprowad nazw projektu Welcome i wpisz pen ciek

katalogu, ktry zawiera plik Welcome.java (zobacz rysunek 2.6).


4. Zaznacz opcj Create project from existing source (utwrz projekt z istniejcego

rda).
5. Kliknij przycisk Finish (zakocz), aby utworzy projekt.
6. Aby otworzy projekt, kliknij znajdujcy si w lewym panelu obok okna projektu

symbol trjkta. Nastpnie kliknij symbol trjkta znajdujcy si obok napisu


Default package (domylny pakiet). Kliknij dwukrotnie plik o nazwie Welcome.java.
Powinno si pojawi okno z kodem rdowym programu (zobacz rysunek 2.7).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

48

Java. Podstawy

Rysunek 2.6.
Konfiguracja
projektu
w Eclipse

Rysunek 2.7.
Edycja kodu
rdowego
w Eclipse

7. W lewym panelu kliknij prawym przyciskiem myszy nazw projektu (Welcome).

Kliknij opcj Run/Run As/Java Application. Okno z wynikami programu znajduje


si na dole okna Eclipse (zobacz rysunek 2.8).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

49

Rysunek 2.8.
Uruchamianie
programu
w rodowisku
Eclipse

2.4.1. Znajdowanie bdw kompilacji


Ten program nie powinien zawiera adnych literwek ani innych bdw (przecie to tylko
kilka wierszy kodu). Zamy jednak na nasze potrzeby, e czasami zdarzy nam si zrobi
w kodzie literwk (a nawet bd skadniowy). Zobaczmy, co si stanie. Celowo zepsujemy
nasz program, zmieniajc wielk liter S w sowie String na ma:
String[] greeting = new string[3];

Ponownie uruchamiamy kompilator. Pojawia si komunikat o bdzie dotyczcy nieznanego


typu o nazwie string (zobacz rysunek 2.9). Wystarczy klikn komunikat bdu, aby kursor zosta przeniesiony do odpowiadajcego mu wiersza w oknie edycji. Moemy poprawi
nasz bd. Takie dziaanie rodowiska umoliwia szybkie poprawianie tego typu bdw.
Bdy w Eclipse s czsto oznaczane ikon arwki. Aby przejrze list sugerowanych rozwiza problemu, naley t ikon klikn.

Te krtkie instrukcje powinny wystarczy na pocztek pracy w rodowisku zintegrowanym.


Opis debugera Eclipse znajduje si w rozdziale 11.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

50

Java. Podstawy

Rysunek 2.9.
Komunikaty
o bdach
w Eclipse

2.5. Uruchamianie aplikacji graficznej


Program powitalny nie naley do najbardziej ekscytujcych. Kolej na aplikacj graficzn.
Ten program jest przegldark plikw graficznych, ktra aduje i wywietla obrazy. Najpierw
skompilujemy i uruchomimy j z poziomu wiersza polece.
1.

Otwrz okno konsoli.

2. Przejd do katalogu JavaPodstawy/t1/r02/ImageViewer.


3. Wpisz ponisze polecenia:
javac ImageViewer.java
java ImageViewer

Pojawi si nowe okno aplikacji ImageViewer (zobacz rysunek 2.10).


Nastpnie kliknij opcj Plik/Otwrz, aby otworzy plik (kilka plikw do otwarcia znajduje
si w katalogu z klas). Aby zamkn program, naley klikn pozycj Zakocz w menu
Plik albo krzyyk w prawym grnym rogu okna przegldarki.
Rzumy okiem na kod rdowy tego programu. Jest on znacznie duszy od poprzedniego,
ale biorc pod uwag to, ile wierszy kodu trzeba by byo napisa w jzykach C i C++, aby

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

51

Rysunek 2.10.
Dziaanie
aplikacji
ImageViewer

stworzy podobn aplikacj, trzeba przyzna, e nie jest zbyt skomplikowany. Oczywicie
atwo taki program napisa (a raczej przecign i upuci) w Visual Basicu. JDK nie umoliwia wizualnego budowania interfejsw, a wic cay kod widoczny na listingu 2.2 trzeba napisa rcznie. Pisaniem takich programw graficznych zajmiemy si w rozdziaach od 7. do 9.
Listing 2.2. ImageViewer/ImageViewer.java
import
import
import
import

java.awt.EventQueue;
java.awt.event.*;
java.io.*;
javax.swing.*;

/**
* Program do przegldania obrazw.
* @version 1.22 2007-05-21
* @author Cay Horstmann
*/
public class ImageViewer
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ImageViewerFrame();
frame.setTitle("ImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka z etykiet wywietlajca obraz.
*/
class ImageViewerFrame extends JFrame
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

52

Java. Podstawy
private JLabel label;
private JFileChooser chooser;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
public ImageViewerFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Uycie etykiety do wywietlenia obrazw.
label = new JLabel();
add(label);
// Dodawanie opcji wyboru obrazu.
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// Pasek menu.
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
JMenuItem openItem = new JMenuItem("Otwrz");
menu.add(openItem);
openItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Wywietlenie okna dialogowego wyboru pliku.
int result = chooser.showOpenDialog(null);
// Jeli plik zosta wybrany, ustawiamy go jako ikon etykiety.
if (result == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
label.setIcon(new ImageIcon(name));
}
}
});
JMenuItem exitItem = new JMenuItem("Zakocz");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

53

2.6. Tworzenie i uruchamianie apletw


Pierwsze dwa programy zaprezentowane w ksice s samodzielnymi aplikacjami. Jak jednak
pamitamy z poprzedniego rozdziau, najwicej szumu wok Javy spowodowaa moliwo
uruchamiania apletw w oknie przegldarki internetowej. Pokaemy, jak si kompiluje
i uruchamia aplety z poziomu wiersza polece. Nastpnie zaadujemy nasz aplet do dostpnej
w JDK przegldarki apletw. Na zakoczenie wywietlimy go w przegldarce internetowej.
Otwrz okno konsoli, przejd do katalogu JavaPodstawy/t1/r02/WelcomeApplet i wpisz nastpujce polecenia:
javac WelcomeApplet.java
appletviewer WelcomeApplet.html

Rysunek 2.11 przedstawia okno przegldarki apletw.


Rysunek 2.11.
Aplet
WelcomeApplet
w oknie
przegldarki
apletw

Pierwsze polecenie ju znamy suy do uruchamiania kompilatora Javy. W tym przypadku skompilowalimy plik z kodem rdowym o nazwie WelcomeApplet.java na plik
z kodem bajtowym o nazwie WelcomeApplet.class.
Jednak tym razem nie uruchamiamy programu java, tylko program appletviewer. Jest to specjalne narzdzie dostpne w pakiecie JDK, ktre umoliwia szybkie przetestowanie apletu.
Program ten przyjmuje na wejciu pliki HTML, a nie pliki klas Javy. Zawarto pliku WelcomeApplet.html przedstawia listing 2.3.
Listing 2.3. WelcomeApplet.html
<html>
<head>
<title>WelcomeApplet</title>
</head>
<body>
<hr/>
<p>
Ten aplet pochodzi z ksiki
<a href="http://www.horstmann.com/corejava.html">Java. Podstawy</a>,
ktrej autorami s <em>Cay Horstmann</em> i <em>Gary Cornell</em>,
wydanej przez wydawnictwo Helion.
</p>
<applet code="WelcomeApplet.class" width="400" height="200">
<param name="greeting" value ="Witaj, czytelniku!"/>

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

54

Java. Podstawy
</applet>
<hr/>
<p><a href="WelcomeApplet.java">rdo</a></p>
</body>
</html>

Osoby znajce HTML rozpoznaj kilka standardowych elementw tego jzyka oraz znacznik
applet, nakazujcy przegldarce apletw, aby zaadowaa aplet, ktrego kod znajduje si w pliku
WelcomeApplet.class. Przegldarka apletw bierze pod uwag tylko znacznik applet.
Oczywicie aplety s przeznaczone do uruchamiania w przegldarkach internetowych, ale niestety w wielu z nich obsuga tych obiektw jest standardowo wyczona. Informacje dotyczce
konfiguracji najpopularniejszych przegldarek do obsugi Javy mona znale pod adresem
http://java.com/en/download/help/enable_browser.xml. Majc odpowiednio skonfigurowan
przegldark, moesz w niej uruchomi nasz aplet.
1.

Uruchom przegldark.

2. Z menu Plik wybierz opcj Otwrz (lub co w tym rodzaju).


3. Przejd do katalogu JavaPodstawy/t1/r02/WelcomeApplet. Zaaduj plik o nazwie

WelcomeApplet.html.
4. Przegldarka wywietli aplet wraz z dodatkowym tekstem. Rezultat bdzie podobny

do tego na rysunku 2.12.


Rysunek 2.12.
Dziaanie apletu
WelcomeApplet
w przegldarce
internetowej

Jak wida, aplikacja ta jest zdolna do interakcji z internetem. Kliknicie przycisku Cay Horstmann powoduje przejcie do strony internetowej Caya Horstmanna. Kliknicie przycisku Gary
Cornell powoduje wywietlenie okna wysyania poczty e-mail z adresem Garyego Cornella
wstawionym w polu adresata.
Zauwa, e aden z tych przyciskw nie dziaa w przegldarce apletw. Nie ma ona moliwoci wysyania poczty e-mail ani wywietlania stron internetowych, wic ignoruje nasze

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 2.

rodowisko programistyczne Javy

55

dania w tym zakresie. Przegldarka apletw nadaje si do testowania apletw w izolacji,


ale do sprawdzenia, jak aplety wsppracuj z przegldark internetow i internetem, potrzebna
jest przegldarka internetowa.
Aplety mona take uruchamia w edytorze lub zintegrowanym rodowisku programistycznym. W Eclipse naley w tym celu uy opcji Run/Run As/Java Applet
(uruchom/uruchom jako/aplet Java).

Kod apletu przedstawia listing 2.4. Na razie wystarczy rzuci tylko na niego okiem. Do pisania
apletw wrcimy w rozdziale 10.
Listing 2.4. WelcomeApplet.java
import
import
import
import

java.awt.*;
java.awt.event.*;
java.net.*;
javax.swing.*;

/**
* Aplet ten wywietla powitanie autorw.
* @version 1.22 2007-04-08
* @author Cay Horstmann
*/
public class WelcomeApplet extends JApplet
{
public void init()
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
setLayout(new BorderLayout());
JLabel label = new JLabel(getParameter("greeting"), SwingConstants.
CENTER);
label.setFont(new Font("Serif", Font.BOLD, 18));
add(label, BorderLayout.CENTER);
JPanel panel = new JPanel();
JButton cayButton = new JButton("Cay Horstmann");
cayButton.addActionListener(makeAction("http://www.horstmann.com"));
panel.add(cayButton);
JButton garyButton = new JButton("Gary Cornell");
garyButton.addActionListener(makeAction("mailto:gary_cornell@
apress.com"));
panel.add(garyButton);
add(panel, BorderLayout.SOUTH);
}
});
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

56

Java. Podstawy
private ActionListener makeAction(final String urlString)
{
return new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
try
{
getAppletContext().showDocument(new URL(urlString));
}
catch (MalformedURLException e)
{
e.printStackTrace();
}
}
};
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Podstawowe elementy
jzyka Java
W tym rozdziale:

Prosty program w Javie

Komentarze

Typy danych

Zmienne

Operatory

acuchy

Wejcie i wyjcie

Kontrola przepywu sterowania

Wielkie liczby

Tablice

Do tego rozdziau naley przej dopiero wtedy, gdy z powodzeniem zainstalowao si pakiet
JDK i uruchomio przykadowe programy z rozdziau 2. Poniewa czas zacz programowanie, w rozdziale tym zapoznasz si z podstawowymi pojciami programistycznymi Javy,
takimi jak typy danych, instrukcje warunkowe i ptle.
Niestety, napisanie w Javie programu z graficznym interfejsem uytkownika nie jest atwe
wymaga duej wiedzy na temat sposobw tworzenia okien, dodawania do nich pl tekstowych,
przyciskw, ktre reaguj na zawarto tych pl itd. Jako e opis technik pisania programw
GUI w Javie znacznie wykracza poza nasz cel przedstawienia podstaw programowania w tym
jzyku, przykadowe programy w tym rozdziale s bardzo proste. Komunikuj si za porednictwem okna konsoli, a ich przeznaczeniem jest tylko ilustracja omawianych poj.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

58

Java. Podstawy
Dowiadczeni programici jzyka C++ mog tylko przejrze ten rozdzia, koncentrujc si na
ramkach opisujcych rnice pomidzy Jav a C++. Programici innych jzykw, jak Visual
Basic, bd zna wikszo omawianych poj, ale odkryj, e skadnia Javy jest cakiem inna
od znanych im jzykw. Te osoby powinny bardzo uwanie przeczyta ten rozdzia.

3.1. Prosty program w Javie


Przyjrzyjmy si uwanie najprostszemu programowi w Javie, jaki mona napisa takiemu,
ktry tylko wywietla komunikat w oknie konsoli:
public class FirstSample
{
public static void main(String[] args)
{
System.out.println("Nie powiemy Witaj, wiecie!");
}
}

Warto powici troch czasu i nauczy si tego fragmentu na pami, poniewa wszystkie
aplikacje s oparte na tym schemacie. Przede wszystkim w Javie wielko liter ma znaczenie.
Jeli w programie bdzie literwka (jak np. sowo Main zamiast main), to program nie zadziaa.
Przestudiujemy powyszy kod wiersz po wierszu. Sowo kluczowe public nosi nazw modyfikatora dostpu (ang. access modifier). Okrela ono, jaki rodzaj dostpu do tego kodu maj
inne czci programu. Wicej informacji na temat modyfikatorw dostpu zawarlimy w rozdziale 5. Sowo kluczowe class przypomina, e wszystko w Javie naley do jakiej klasy.
Poniewa klasami bardziej szczegowo zajmujemy si w kolejnym rozdziale, na razie
bdziemy je traktowa jako zbiory mechanizmw programu, ktre s odpowiedzialne za
jego dziaanie. Jak pisalimy w rozdziale 1., klasy to bloki, z ktrych skadaj si wszystkie
aplikacje i aplety Javy. Wszystko w programie w Javie musi si znajdowa wewntrz
jakiej klasy.
Po sowie kluczowym class znajduje si nazwa klasy. Reguy dotyczce tworzenia nazw
klas w Javie s dosy liberalne. Nazwa klasy musi si zaczyna od litery, po ktrej moe
znajdowa si kombinacja dowolnych znakw i cyfr. Nie ma w zasadzie ogranicze, jeli
chodzi o dugo. Nie mona stosowa sw zarezerwowanych Javy (np. public lub class)
lista wszystkich sw zarezerwowanych znajduje si w dodatku.
Zgodnie ze standardow konwencj nazewnicz (ktrej przykadem jest nazwa klasy
FirstSample) nazwy klas powinny si skada z rzeczownikw pisanych wielk liter. Jeli

nazwa klasy skada si z kilku sw, kade z nich powinno by napisane wielk liter; notacja
polegajca na stosowaniu wielkich liter wewntrz nazw jest czasami nazywana notacj wielbdzi (ang. camel case lub CamelCase).
Plik zawierajcy kod rdowy musi mie tak sam nazw jak klasa publiczna oraz rozszerzenie .java. W zwizku z tym nasz przykadowy kod powinien zosta zapisany w pliku o nazwie
FirstSample.java (przypominam, e wielko liter ma znaczenie take tutaj nie mona
napisa firstsample.java).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

59

Jeli plik ma prawidow nazw i nie ma adnych literwek w kodzie rdowym, w wyniku
jego kompilacji powstanie plik zawierajcy kod bajtowy tej klasy. Kompilator automatycznie
nada skompilowanemu plikowi nazw FirstSample.class i zapisze go w tym samym katalogu,
w ktrym znajduje si plik rdowy. Program uruchamiamy za pomoc nastpujcego polecenia (nie zapomnij o pominiciu rozszerzenia .class):
java FirstSample

Po uruchomieniu program ten wywietla w konsoli acuch Nie powiemy Witaj, wiecie!.
Polecenie:
java NazwaKlasy

zastosowane do skompilowanego programu powoduje, e wirtualna maszyna Javy zaczyna


wykonywanie od kodu zawartego w metodzie main wskazanej klasy (terminem metoda
okrela si to, co w innych jzykach jest funkcj). W zwizku z tym metoda main musi si
znajdowa w pliku rdowym klasy, ktr chcemy uruchomi. Mona oczywicie doda
wasne metody do klasy i wywoywa je w metodzie main. Pisanie metod omawiamy w nastpnym rozdziale.
Zgodnie ze specyfikacj jzyka Java (oficjalnym dokumentem opisujcym ten jzyk,
ktry mona pobra lub przeglda na stronie http://docs.oracle.com/javase/specs)
metoda main musi by publiczna (public).
Jednak niektre wersje maszyny wirtualnej Javy uruchamiay programy w Javie, ktrych
metoda main nie bya publiczna. Pewien programista zgosi ten bd. Aby si o tym przekona, wejd na stron http://bugs.sun.com/bugdatabase/index.jsp i wpisz numer
identyfikacyjny bdu 4252539. Bd ten zosta oznaczony jako zamknity i nie do
naprawy (ang. closed, will not be fixed). Jeden z inynierw pracujcych w firmie Sun
wyjani (http://docs.oracle.com/javase/specs/jvms/se7/html), e specyfikacja maszyny
wirtualnej Javy nie wymaga, aby metoda main bya publiczna, w zwizku z czym naprawienie tego bdu moe spowodowa problemy. Na szczcie signito po rozum do
gowy i od wersji 1.4 Java SE metoda main jest publiczna.

Ta historia pozwala zwrci uwag na kilka rzeczy. Z jednej strony rozczarowuje nas sytuacja, e
osoby odpowiadajce za jako s przepracowane i nie zawsze dysponuj wystarczajc wiedz specjalistyczn z zakresu najbardziej zaawansowanych zagadnie zwizanych z Jav.
Przez to nie zawsze podejmuj trafne decyzje. Z drugiej strony trzeba zauway, e firma Sun
zamieszcza raporty o bdach na stronie internetowej, aby kady mg je zweryfikowa. Taki
spis bdw jest bardzo wartociowym rdem wiedzy dla programistw. Mona nawet gosowa na swj ulubiony bd. Bdy o najwikszej liczbie gosw maj najwiksz szans
poprawienia w kolejnej wersji pakietu JDK.
Zauwa, e w kodzie rdowym uyto nawiasw klamrowych. W Javie, podobnie jak w C
i C++, klamry oddzielaj poszczeglne czci (zazwyczaj nazywane blokami) kodu programu. Kod kadej metody w Javie musi si zaczyna od otwierajcej klamry {, a koczy
zamykajc klamr }.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

60

Java. Podstawy
Styl stosowania nawiasw klamrowych wywoa niepotrzebn dyskusj. My stosujemy styl
polegajcy na umieszczaniu dopeniajcych si klamer w tej samej kolumnie. Jako e kompilator ignoruje biae znaki, mona stosowa dowolny styl nawiasw klamrowych. Wicej do
powiedzenia na temat stosowania klamer bdziemy mieli przy okazji omawiania ptli.
Na razie nie bdziemy si zajmowa znaczeniem sw static void traktuj je jako co, czego
potrzebujesz do kompilacji programu w Javie. Po rozdziale czwartym przestanie to by tajemnic. Teraz trzeba tylko zapamita, e kady program napisany w Javie musi zawiera metod
main zadeklarowan w nastpujcy sposb:
public class NazwaKlasy
{
public static void main(String[] args)
{
instrukcje programu
}
}

Programici jzyka C++ doskonale znaj pojcie klasa. Klasy w Javie s pod
wieloma wzgldami podobne do tych w C++, ale jest te kilka rnic, o ktrych
nie mona zapomina. Na przykad w Javie wszystkie funkcje s metodami jakiej klasy
(w standardowej terminologii s one nazywane metodami, a nie funkcjami skadowymi).
W zwizku z tym w Javie konieczna jest obecno klasy zawierajcej metod main. Programici C++ pewnie znaj te statyczne funkcje skadowe. S to funkcje zdefiniowane
wewntrz klasy, ktre nie wykonuj adnych dziaa na obiektach. Metoda main w Javie
jest zawsze statyczna. W kocu sowo kluczowe void, podobnie jak w C i C++, oznacza,
e metoda nie zwraca wartoci. W przeciwiestwie do jzyka C i C++ metoda main w Javie
nie zwraca adnego kodu wyjcia (ang. exit code) do systemu operacyjnego. Jeli metoda
main zakoczy dziaanie w normalny sposb, program ma kod wyjcia 0, ktry oznacza
pomylne zakoczenie. Aby zakoczy dziaanie programu innym kodem wyjcia, naley
uy metody System.exit.

Teraz kierujemy nasz uwag na poniszy fragment:


{
System.out.println("Nie powiemy Witaj, wiecie!");
}

Klamry oznaczaj pocztek i koniec ciaa metody. Ta metoda zawiera tylko jedn instrukcj.
Podobnie jak w wikszoci jzykw programowania, instrukcje Javy mona traktowa jako
zdania tego jzyka. Kada instrukcja musi by zakoczona rednikiem. Przede wszystkim
naley pamita, e znak powrotu karetki nie oznacza koca instrukcji, dziki czemu mog
one obejmowa nawet kilka wierszy.
W treci metody main znajduje si instrukcja wysyajca jeden wiersz tekstu do konsoli.
W tym przypadku uylimy obiektu System.out i wywoalimy na jego rzecz metod println.
Zwr uwag na kropki zastosowane w wywoaniu metody. Oglna skadnia stosowana
w Javie do wywoania jej odpowiednikw funkcji jest nastpujca:
obiekt.metoda(parametry)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

61

W tym przypadku wywoalimy metod println i przekazalimy jej argument w postaci acucha. Metoda ta wywietla zawarto parametru w konsoli. Nastpnie koczy wiersz wyjciowy, dziki czemu kade wywoanie metody println wywietla dane w oddzielnym wierszu.
Zwr uwag, e w Javie, podobnie jak w C i C++, acuchy naley ujmowa w cudzysowy wicej informacji na temat acuchw znajduje si w dalszej czci tego rozdziau.
Metody w Javie, podobnie jak funkcje w innych jzykach programowania, przyjmuj zero,
jeden lub wicej parametrw (czsto nazywanych argumentami). Nawet jeli metoda nie
przyjmuje adnych parametrw, nie mona pomin stojcych po jej nazwie nawiasw. Na
przykad metoda println bez adnych argumentw drukuje pusty wiersz. Wywouje si j
nastpujco:
System.out.println();

Na rzecz obiektu System.out mona take wywoywa metod print, ktra nie
dodaje do danych wyjciowych znaku nowego wiersza. Na przykad wywoanie
System.out.print("Witaj") drukuje napis Witaj bez znaku nowego wiersza. Kolejne dane
zostan umieszczone bezporednio po sowie Witaj.

3.2. Komentarze
Komentarze w Javie, podobnie jak w wikszoci jzykw programowania, nie s uwzgldniane w programie wykonywalnym. Mona zatem stosowa je w dowolnej iloci bez obawy,
e nadmiernie zwiksz rozmiary kodu. W Javie s trzy rodzaje komentarzy. Najczciej
stosowana metoda polega na uyciu znakw //. Ten rodzaj komentarza obejmuje obszar od
znakw // do koca wiersza, w ktrym si znajduj.
System.out.println("Nie powiemy Witaj, wiecie!");

// Czy to nie sodkie?

Dusze komentarze mona tworzy poprzez zastosowanie znakw // w wielu wierszach lub
uycie komentarza w stylu /* */. W ten sposb w komentarzu mona uj cay blok treci
programu.
Wreszcie, trzeci rodzaj komentarza suy do automatycznego generowania dokumentacji. Ten
rodzaj komentarza zaczyna si znakami /** i koczy */. Jego zastosowanie przedstawia
listing 3.1. Wicej informacji na temat tego rodzaju komentarzy i automatycznego generowania dokumentacji znajduje si w rozdziale 4.
Listing 3.1. FirstSample.java
/**
* Jest to pierwszy przykadowy program w rozdziale 3.
* @version 1.01 1997-03-22
* @author Gary Cornell
*/
public class FirstSample
{
public static void main(String[] args)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

62

Java. Podstawy
System.out.println("Nie powiemy Witaj, wiecie!");
}
}

Komentarzy /* */ nie mona zagnieda. Oznacza to, e nie mona dezaktywowa


fragmentu kodu programu, otaczajc go po prostu znakami /* i */, poniewa kod ten
moe zawiera znaki */.

3.3. Typy danych


Java jest jzykiem o cisej kontroli typw. Oznacza to, e kada zmienna musi mie okrelony typ. W Javie istnieje osiem podstawowych typw. Cztery z nich reprezentuj liczby
cakowite, dwa liczby rzeczywiste, jeden o nazwie char zarezerwowano dla znakw reprezentowanych przez kody liczbowe nalece do systemu Unicode (patrz punkt 3.3.3), za
ostatni jest logiczny (boolean) przyjmuje on tylko dwie wartoci: true albo false.
W Javie dostpny jest pakiet do oblicze arytmetycznych na liczbach o duej precyzji.
Jednak tak zwane due liczby (ang. big numbers) s obiektami, a nie nowym
typem w Javie. Sposb posugiwania si nimi zosta opisany w dalszej czci tego
rozdziau.

3.3.1. Typy cakowite


Typy cakowite to liczby pozbawione czci uamkowej. Zaliczaj si do nich take wartoci ujemne. Wszystkie cztery dostpne w Javie typy cakowite przedstawia tabela 3.1.
Tabela 3.1. Typy cakowite Javy
Typ

Liczba bajtw

Zakres (z uwzgldnieniem wartoci brzegowych)

int

od 2 147 483 648 do 2 147 483 647 (nieco ponad 2 miliardy)

short

od 32 768 do 32 767

long

od 9 223 372 036 854 775 808 do


9 223 372 036 854 775 807

byte

od 128 do 127

Do wikszoci zastosowa najlepiej nadaje si typ int. Aby zapisa liczb mieszkacw
naszej planety, trzeba uy typu long. Typy byte i short s uywane do specjalnych zada,
jak niskopoziomowa praca nad plikami lub due tablice, kiedy pami jest na wag zota.
Zakres wartoci typw cakowitych nie zaley od urzdzenia, na ktrym uruchamiany jest
kod Javy. Eliminuje to gwny problem programisty, ktry chce przenie swj program

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

63

z jednej platformy na inn lub nawet z jednego systemu operacyjnego do innego na tej samej
platformie. W odrnieniu od Javy, jzyki C i C++ uywaj najbardziej efektywnego typu
cakowitego dla kadego procesora. W wyniku tego program prawidowo dziaajcy na procesorze 32-bitowym moe powodowa bd przekroczenia zakresu liczby cakowitej na procesorze 16-bitowym. Jako e programy w Javie musz dziaa prawidowo na wszystkich
urzdzeniach, zakresy wartoci rnych typw s stae.
Due liczby cakowite (typu long) s opatrzone modyfikatorem L lub l (na przykad
4000000000L). Liczby w formacie szesnastkowym maj przedrostek 0x (na przykad 0xCAFE).
Liczby w formacie semkowym poprzedza przedrostek 0. Na przykad liczba 010 w zapisie
semkowym to 8 w zapisie dziesitnym. Oczywicie zapis ten moe wprowadza w bd,
w zwizku z czym odradzamy jego stosowanie.
W Java 7 wprowadzono dodatkowo moliwo zapisu liczb w formacie binarnym, do
czego suy przedrostek 0b. Przykadowo 0b1001 to inaczej 9. Ponadto rwnie od tej wersji
jzyka mona w literaach liczbowych stosowa znaki podkrelenia, np. 1_000_000 (albo
0b1111_0100_0010_0100_0000) milion. Znaki te maj za zadanie uatwi czytanie kodu
ludziom. Kompilator Javy je usuwa.
W jzykach C i C++ typ int to liczba cakowita, ktrej rozmiar zaley od urzdzenia docelowego. W procesorach 16-bitowych, jak 8086, typ int zajmuje 2 bajty
pamici. W procesorach 32-bitowych, jak Sun SPARC, s to wartoci czterobajtowe.
W przypadku procesorw Intel Pentium rozmiar typu int zaley od systemu operacyjnego:
w DOS-ie i Windows 3.1 typ int zajmuje 2 bajty pamici. W programach dla systemu Windows dziaajcych w trybie 32-bitowym typ int zajmuje 4 bajty. W Javie wszystkie typy
numeryczne s niezalene od platformy.
Zauwa, e w Javie nie ma typu unsigned.

3.3.2. Typy zmiennoprzecinkowe


Typy zmiennoprzecinkowe su do przechowywania liczb z czci uamkow. Dwa dostpne
w Javie typy zmiennoprzecinkowe przedstawia tabela 3.2.
Tabela 3.2. Typy zmiennoprzecinkowe
Typ

Liczba bajtw

Zakres

float

okoo 3,40282347E+38F (6 7 znaczcych cyfr dziesitnych)

double

okoo 1,79769313486231570E+308 (15 znaczcych cyfr dziesitnych)

Nazwa double (podwjny) wynika z tego, e typ ten ma dwa razy wiksz precyzj ni typ
float (czasami liczby te nazywa si liczbami o podwjnej precyzji). W wikszoci przypadkw do reprezentacji liczb zmiennoprzecinkowych wybierany jest typ double. Ograniczona
precyzja typu float czsto okazuje si niewystarczajca. Siedem znaczcych (dziesitnych)
cyfr moe wystarczy do precyzyjnego przedstawienia naszej pensji w zotwkach i groszach, ale moe by ju to za mao precyzyjne do przechowywania liczby okrelajcej zarobki

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

64

Java. Podstawy
naszego szefa. W zwizku z tym powodw do stosowania typu float jest niewiele; moe to
by sytuacja, w ktrej zaley nam na nieznacznym zwikszeniu szybkoci poprzez zastosowanie liczb o pojedynczej precyzji lub kiedy chcemy przechowywa bardzo du ich ilo.
Liczby typu float maj przyrostek F lub f (na przykad 3.14F). Liczby zmiennoprzecinkowe
pozbawione tego przyrostka (na przykad 3.14) s zawsze traktowane jako typ double. Mona
te poda przyrostek D lub d (na przykad 3.14D).
Liczby zmiennoprzecinkowe mona podawa w zapisie szesnastkowym. Na przykad 0,125, czyli 23, mona zapisa jako 0x1.0p-3. Wykadnik potgi w zapisie
szesnastkowym to p, a nie e (e jest cyfr szesnastkow). Zauwa, e mantysa jest
w notacji szesnastkowej, a wykadnik w dziesitnej. Podstaw wykadnika jest 2, nie 10.

Wszelkie obliczenia arytmetyczne wykonywane na liczbach zmiennoprzecinkowych s zgodne


ze standardem IEEE 754. Istniej trzy szczeglne wartoci pozwalajce okreli liczby, ktrych wartoci wykraczaj poza dozwolony zakres bdu:

dodatnia nieskoczono,

ujemna nieskoczono,

NaN nie liczby (ang. Not a Number).

Na przykad wynikiem dzielenia dodatniej liczby przez zero jest dodatnia nieskoczono.
Dziaanie dzielenia zero przez zero lub wycigania pierwiastka kwadratowego z liczby
ujemnej daje w wyniku NaN.
Stae Double.POSITIVE_INFINITY, Double.NEGATIVE_INFINITY i Double.NaN (oraz
ich odpowiedniki typu float) reprezentuj wymienione specjalne wartoci, ale s
rzadko uywane. Nie mona na przykad wykona takiego sprawdzenia:
if (x == Double.NaN)

// Nigdy nie jest true.

aby dowiedzie si, czy dany wynik jest rwny staej Double.NaN. Wszystkie tego typu
wartoci s rne. Mona za to uywa metody Double.isNaN:
if (Double.isNaN(x))

// Sprawdzenie, czy x jest nie liczb.

Liczby zmiennoprzecinkowe nie nadaj si do oblicze finansowych, w ktrych niedopuszczalny jest bd zaokrglania (ang. roundoff error). Na przykad instrukcja
System.out.println(2.0 - 1.1) da wynik 0.8999999999999999 zamiast spodziewanego 0.9. Tego typu bdy spowodowane s tym, e liczby zmiennoprzecinkowe s
reprezentowane w systemie binarnym. W systemie tym nie ma dokadnej reprezentacji
uamka 1/10, podobnie jak w systemie dziesitnym nie istnieje dokadna reprezentacja
uamka 1/3. Aby wykonywa precyzyjne obliczenia numeryczne bez bdu zaokrglania,
naley uy klasy BigDecimal, ktra jest opisana w dalszej czci tego rozdziau.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

65

3.3.3. Typ char


Typ char suy do reprezentacji pojedynczych znakw. Najczciej s to stae znakowe. Na
przykad 'A' jest sta znakow o wartoci 65. Nie jest tym samym co "A" acuchem
zawierajcym jeden znak. Kody Unicode mog by wyraane w notacji szesnastkowej,
a ich wartoci mieszcz si w zakresie od \u0000 do \uFFFF. Na przykad kod \u2122 reprezentuje symbol , a \u03C0 to grecka litera .
Poza symbolem zastpczym \u oznaczajcym zapis znaku w kodzie Unicode jest jeszcze
kilka innych symboli zastpczych umoliwiajcych zapisywanie rnych znakw specjalnych. Zestawienie tych znakw przedstawia tabela 3.3. Mona je stosowa zarwno w staych znakowych, jak i w acuchach, np. 'u\2122' albo "Witaj\n". Symbol zastpczy \u
jest jedynym symbolem zastpczym, ktrego mona uywa take poza cudzysowami otaczajcymi znaki i acuchy. Na przykad zapis:
public static void main(String\u005B\u005D args)

jest w peni poprawny kody \u005B i \u005D oznaczaj znaki [ i ].


Tabela 3.3. Symbole zastpcze znakw specjalnych
Symbol zastpczy

Nazwa

Warto Unicode

\b

Backspace

\u0008

\t

Tabulacja

\u0009

\n

Przejcie do nowego wiersza

\u000a

\r

Powrt karetki

\u 000d

\"

Cudzysw

\u 0022

\'

Apostrof

\u0027

\\

Lewy ukonik

\u005c

Aby w peni zrozumie typ char, trzeba pozna system kodowania znakw Unicode. Unicode opracowano w celu pozbycia si ogranicze tradycyjnych systemw kodowania. Przed
powstaniem systemu Unicode istniao wiele rnych standardw: ASCII w USA, ISO 8859-1
dla jzykw krajw Europy Zachodniej, ISO-8859-2 dla jzykw rodkowo- i wschodnioeuropejskich (w tym polskiego), KOI-8 dla jzyka rosyjskiego, GB18030 i BIG-5 dla jzyka
chiskiego itd. Powoduje to dwa problemy: jeden kod moe oznacza rne znaki w rnych systemach kodowania, a poza tym kody znakw w jzykach o duej liczbie znakw
maj rne rozmiary niektre czsto uywane znaki zajmuj jeden bajt, a inne potrzebuj dwch bajtw.
Unicode ma za zadanie rozwiza te problemy. Kiedy w latach osiemdziesitych XX wieku
podjto prby unifikacji, wydawao si, e dwubajtowy stay kod by wicej ni wystarczajcy do zakodowania znakw uywanych we wszystkich jzykach wiata. W 1991 roku
wiato dzienne ujrza Unicode 1.0. Wykorzystywana w nim bya prawie poowa wszystkich
dostpnych 65 536 kodw. Java od samego pocztku uywaa znakw 16-bitowego systemu
Unicode, co dawao jej du przewag nad innymi jzykami programowania, ktre stosoway znaki omiobitowe.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

66

Java. Podstawy
Niestety z czasem nastpio to, co byo nieuchronne. Unicode przekroczy liczb 65 536
znakw, gwnie z powodu dodania bardzo duych zbiorw ideogramw uywanych w jzykach chiskim, japoskim i koreaskim. Obecnie 16-bitowy typ char nie wystarcza do opisu
wszystkich znakw Unicode.
Aby wyjani, jak ten problem zosta rozwizany w Javie, zaczynajc od Java SE 5.0, musimy
wprowadzi nieco nowej terminologii. Wsprzdna kodowa znaku (ang. code point) to
warto zwizana ze znakiem w systemie kodowania. W standardzie Unicode wsprzdne
kodowe znakw s zapisywane w notacji szesnastkowej i s poprzedzane acuchem U+, np.
wsprzdna kodowa litery A to U+0041. Wsprzdne kodowe znakw systemu Unicode s
pogrupowane w 17 przestrzeniach numeracyjnych (ang. code planes). Pierwsza z nich,
nazywana podstawow przestrzeni wielojzyczn (ang. Basic Multilingual Plane BMP),
zawiera klasyczne znaki Unicode o wsprzdnych kodowych z przedziau od U+0000 do
U+FFFF. Pozostae szesnacie przestrzeni o wsprzdnych kodowych znakw z przedziau
od U+10000 do U+10FFFF zawiera znaki dodatkowe (ang. supplementary characters).
Kodowanie UTF-16 to sposb reprezentacji wszystkich wsprzdnych kodowych znakw
za pomoc kodw o rnej dugoci. Znaki w podstawowej przestrzeni s 16-bitowymi
wartociami o nazwie jednostek kodowych (ang. code units). Znaki dodatkowe s kodowane jako kolejne pary jednostek kodowych. Kada z wartoci nalecych do takiej pary
naley do zakresu 2048 nieuywanych wartoci BMP, zwanych obszarem surogatw
(ang. surrogates area) zakres pierwszej jednostki kodowej to U+D800 U+DBFF, a drugiej
U+DC00 U+DFFF. Jest to bardzo sprytne rozwizanie, poniewa od razu wiadomo, czy jednostka kodowa reprezentuje jeden znak, czy jest pierwsz lub drug czci znaku dodatkowego. Na przykad matematyczny symbol oznaczajcy zbir liczb cakowitych ma wsprzdn kodow U+1D56B i jest kodowany przez dwie jednostki kodowe U+D835 oraz U+DD6B (opis
algorytmu kodowania UTF-16 mona znale na stronie http://en.wikipedia.org/wiki/UTF-16).
W Javie typ char opisuje jednostk kodow UTF-16.
Zdecydowanie odradzamy posugiwania si w programach typem char, jeli nie ma koniecznoci wykonywania dziaa na jednostkach kodowych UTF-16. Prawie zawsze lepszym rozwizaniem jest traktowanie acuchw (ktre opisujemy w podrozdziale 3.6, acuchy)
jako abstrakcyjnych typw danych.

3.3.4. Typ boolean


Typ boolean (logiczny) moe przechowywa dwie wartoci: true i false. Suy do sprawdzania warunkw logicznych. Wartoci logicznych nie mona konwertowa na wartoci cakowitoliczbowe.

3.4. Zmienne
W Javie kada zmienna musi mie okrelony typ. Deklaracja zmiennej polega na napisaniu
nazwy typu, a po nim nazwy zmiennej. Oto kilka przykadw deklaracji zmiennych:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

67

W jzyku C++ zamiast wartoci logicznych mona stosowa liczby, a nawet wskaniki. Warto 0 jest odpowiednikiem wartoci logicznej false, a warto rna od
zera odpowiada wartoci true. W Javie tak nie jest. Dziki temu programici Javy maj
ochron przed popenieniem bdu:
if (x = 0)

// ups miaem na myli x == 0

W C++ test ten przejdzie kompilacj i bdzie mona go uruchomi, a jego wartoci
zawsze bdzie false. W Javie testu tego nie bdzie mona skompilowa, poniewa wyraenia cakowitoliczbowego x = 0 nie mona przekonwertowa na warto logiczn.
double salary;
int vacationDays;
long earthPopulation;
boolean done;

Naley zauway, e na kocu kadej deklaracji znajduje si rednik. Jest on wymagany


z tego wzgldu, e deklaracja zmiennej jest instrukcj w Javie.
Nazwa zmiennej musi si zaczyna liter oraz skada si z liter i cyfr. Zwrmy uwag, e
pojcia litera i cyfra w Javie maj znacznie szersze znaczenie ni w wikszoci innych
jzykw. Zgodnie z definicj litera to jeden ze znakw 'A' 'Z', 'a' 'z', '_' lub kady
znak Unicode bdcy liter jakiego jzyka. Na przykad polscy programici mog w nazwach
zmiennych uywa liter z ogonkami, takich jak . Grek moe uy litery . Podobnie cyfry
nale do zbioru '0' '9' oraz s nimi wszystkie znaki Unicode, ktre oznaczaj cyfr
w jakim jzyku. W nazwach zmiennych nie mona stosowa symboli typu '+' czy ani spacji.
Wszystkie znaki uyte w nazwie zmiennej oraz ich wielko maj znaczenie. Dugo nazwy zmiennej jest w zasadzie nieograniczona.
Aby sprawdzi, ktre znaki Unicode s w Javie literami, mona uy metod
isJavaIdentifierStart i isJavaIdentifierPart, ktre s dostpne w klasie
Character.

Mimo e znak $ jest w Javie traktowany jak zwyka litera, nie naley go uywa
w swoim kodzie. Jest stosowany w nazwach generowanych przez kompilator i inne
narzdzia Javy.

Dodatkowo nazwa zmiennej w Javie nie moe by taka sama jak sowo zarezerwowane (list
sw zarezerwowanych zawiera dodatek).
Kilka deklaracji mona umieci w jednym wierszu:
int i, j;

// Obie zmienne s typu int.

Nie polecamy jednak takiego stylu pisania kodu. Dziki deklarowaniu kadej zmiennej oddzielnie programy s atwiejsze do czytania.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

68

Java. Podstawy

Jak wiemy, w nazwach s rozrniane mae i wielkie litery. Na przykad nazwy


hireday i hireDay to dwie rne nazwy. W zasadzie nie powinno si stosowa
nazw zmiennych rnicych si tylko wielkoci liter, chocia czasami trudno jest wymyli dobr nazw. Wielu programistw w takich przypadkach nadaje zmiennej tak sam
nazw jak nazwa typu:
Box box;

// Box to nazwa typu, a box to nazwa zmiennej.

Inni wol stosowa przedrostek a:


Box aBox;

3.4.1. Inicjacja zmiennych


Po zadeklarowaniu zmiennej trzeba j zainicjowa za pomoc instrukcji przypisania nie
mona uy wartoci niezainicjowanej zmiennej. Na przykad ponisze instrukcje w Javie
s bdne:
int vacationDays;
System.out.println(vacationDays);

// Bd zmienna nie zostaa zainicjowana.

Przypisanie wartoci do zadeklarowanej zmiennej polega na napisaniu nazwy zmiennej po


lewej stronie znaku rwnoci (=) i wyraenia o odpowiedniej wartoci po jego prawej stronie.
int vacationDays;
vacationDays = 12;

Zmienn mona zadeklarowa i zainicjowa w jednym wierszu. Na przykad:


int vacationDays = 12;

Wreszcie, deklaracje w Javie mona umieszcza w dowolnym miejscu w kodzie. Na przykad poniszy kod jest poprawny:
double salary = 65000.0;
System.out.println(salary);
int vacationDays = 12;

// Zmienna moe by zadeklarowana w tym miejscu.

Do dobrego stylu programowania w Javie zalicza si deklarowanie zmiennych jak najbliej


miejsca ich pierwszego uycia.
W jzykach C i C++ rozrnia si deklaracj i definicj zmiennej. Na przykad:
int i = 10;

jest definicj zmiennej, podczas gdy:


extern int i;

to deklaracja. W Javie deklaracje nie s oddzielane od definicji.

3.4.2. Stae
Stae oznaczamy sowem kluczowym final. Na przykad:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

69

public class Constants


{
public static void main(String[] args)
{
final double CM_PER_INCH = 2.54;
double paperWidth = 8.5;
double paperHeight = 11;
System.out.println("Rozmiar papieru w centymetrach: "
+ paperWidth * CM_PER_INCH + " na " + paperHeight * CM_PER_INCH);
}
}

Sowo kluczowe final oznacza, e mona tylko jeden raz przypisa warto i nie bdzie mona
ju jej zmieni w programie. Nazwy staych piszemy zwyczajowo samymi wielkimi literami.
W Javie chyba najczciej uywa si staych, ktre s dostpne dla wielu metod jednej klasy.
S to tak zwane stae klasowe. Tego typu stae definiujemy za pomoc sowa kluczowego
static final. Oto przykad uycia takiej staej:
public class Constants2
{
public static final double CM_PER_INCH = 2.54;

public static void main(String[] args)


{
double paperWidth = 8.5;
double paperHeight = 11;
System.out.println("Rozmiar papieru w centymetrach: "
+ paperWidth * CM_PER_INCH + " na " + paperHeight * CM_PER_INCH);
}

Zauwamy, e definicja staej klasowej znajduje si na zewntrz metody main. W zwizku


z tym staa ta moe by uywana take przez inne metody tej klasy. Ponadto, jeli (jak w naszym przykadzie) staa jest zadeklarowana jako publiczna (public), dostp do niej maj
take metody innych klas jak w naszym przypadku Constants2.CM_PER_INCH.
Sowo const jest sowem zarezerwowanym w Javie, ale obecnie nie jest do niczego
uywane. Do deklaracji staych trzeba uywa sowa kluczowego final.

3.5. Operatory
Znane wszystkim operatory arytmetyczne +, , * i / su w Javie odpowiednio do wykonywania operacji dodawania, odejmowania, mnoenia i dzielenia. Operator / oznacza dzielenie
cakowitoliczbowe, jeli obie liczby s typu cakowitoliczbowego, oraz dzielenie zmiennoprzecinkowe w przeciwnym przypadku. Operatorem reszty z dzielenia (dzielenia modulo)
jest symbol %. Na przykad wynikiem dziaania 15/2 jest 7, a 15%2 jest 1, podczas gdy
15.0/2 = 7.5.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

70

Java. Podstawy
Pamitajmy, e dzielenie cakowitoliczbowe przez zero powoduje wyjtek, podczas gdy wynikiem dzielenia zmiennoprzecinkowego przez zero jest nieskoczono lub warto NaN.
Binarne operatory arytmetyczne w przypisaniach mona wygodnie skraca. Na przykad zapis:
x+= 4;

jest rwnowany z zapisem:


x = x + 4

Oglna zasada jest taka, e operator powinien si znajdowa po lewej stronie znaku rwnoci,
np. *= czy %=.
Jednym z gwnych celw, ktre postawili sobie projektanci Javy, jest przenono.
Wyniki oblicze powinny by takie same bez wzgldu na to, ktrej maszyny wirtualnej uyto. Uzyskanie takiej przenonoci jest zaskakujco trudne w przypadku dziaa
na liczbach zmiennoprzecinkowych. Typ double przechowuje dane liczbowe w 64 bitach
pamici, ale niektre procesory maj 80-bitowe rejestry liczb zmiennoprzecinkowych.
Rejestry te w swoich obliczeniach porednich stosuj zwikszon precyzj. Przyjrzyjmy
si na przykad poniszemu dziaaniu:
double w = x * y/z;

Wiele procesorw Intel warto wyraenia x * y zapisuje w 80-bitowym rejestrze. Nastpnie wykonywane jest dzielenie przez z, a wynik z powrotem obcinany do 64 bitw. Tym
sposobem otrzymujemy dokadniejsze wyniki i unikamy przekroczenia zakresu wykadnika.
Ale wynik moe by inny, ni gdyby obliczenia byy cay czas wykonywane w 64 bitach.
Z tego powodu w pierwszych specyfikacjach wirtualnej maszyny Javy by zapisany wymg,
aby wszystkie obliczenia porednie uyway zmniejszonej precyzji. Nie przepadaa za tym
caa spoeczno programistyczna. Obliczenia o zmniejszonej precyzji mog nie tylko
powodowa przekroczenie zakresu, ale s te wolniejsze ni obliczenia o zwikszonej
precyzji, poniewa obcinanie bitw zajmowao czas. W zwizku z tym opracowano aktualizacj jzyka Java majc na celu rozwiza problem sprzecznych wymaga dotyczcych optymalizacji wydajnoci i powtarzalnoci wynikw. Projektanci maszyny wirtualnej
mog obecnie stosowa zwikszon precyzj w obliczeniach porednich. Jednak metody
oznaczone sowem kluczowym strictfp musz korzysta ze cisych dziaa zmiennoprzecinkowych, ktre daj powtarzalne wyniki.
Na przykad metod main mona oznaczy nastpujco:
public static strictfp void main(String[] args)

W takim przypadku wszystkie instrukcje znajdujce si w metodzie main uywaj ograniczonych oblicze zmiennoprzecinkowych. Jeli oznaczymy w ten sposb klas, wszystkie
jej metody bd stosowa obliczenia zmiennoprzecinkowe o zmniejszonej precyzji.
Sedno problemu ley w dziaaniu procesorw Intel. W trybie domylnym obliczenia porednie mog uywa rozszerzonego wykadnika, ale nie rozszerzonej mantysy (chipy
Intela umoliwiaj obcinanie mantysy niepowodujce strat wydajnoci). W zwizku z tym
gwna rnica pomidzy trybem domylnym a cisym jest taka, e obliczenia cise
mog przekroczy zakres, a domylne nie.
Musz jednak uspokoi tych, u ktrych na ciele wystpia gsia skrka w trakcie lektury tej
uwagi. Przekroczenie zakresu liczby zmiennoprzecinkowej nie zdarza si na co dzie
w zwykych programach. W tej ksice nie uywamy sowa kluczowego strictfp.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

71

3.5.1. Operatory inkrementacji i dekrementacji


Programici doskonale wiedz, e jednym z najczciej wykonywanych dziaa na zmiennych
liczbowych jest dodawanie lub odejmowanie jedynki. Java, podobnie jak C i C++, ma
zarwno operator inkrementacji, jak i dekrementacji. Zapis n++ powoduje zwikszenie wartoci zmiennej n o jeden, a n-- zmniejszenie jej o jeden. Na przykad kod:
int n = 12
n++;

zwiksza warto przechowywan w zmiennej n na 13. Jako e operatory te zmieniaj warto


zmiennej, nie mona ich stosowa do samych liczb. Na przykad nie mona napisa 4++.
Operatory te wystpuj w dwch postaciach. Powyej widzielimy postaci przyrostkowe,
ktre jak wskazuje nazwa umieszcza si po operandzie. Druga posta to posta
przedrostkowa ++n. Obie zwikszaj warto zmiennej o jeden. Rnica pomidzy nimi
ujawnia si, kiedy zostan uyte w wyraeniu. W przypadku zastosowania formy przedrostkowej warto zmiennej jest zwikszana przed obliczeniem wartoci wyraenia, a w przypadku
formy przyrostkowej warto zmiennej zwiksza si po obliczeniu wartoci wyraenia.
int
int
int
int

m
n
a
b

=
=
=
=

7;
7;
2 * ++m;
2 * n++;

// a ma warto 16, a m 8
// b ma warto 14, a n 8

Nie zalecamy stosowania operatora ++ w innych wyraeniach, poniewa zaciemnia to kod


i czsto powoduje irytujce bdy.
(Jak powszechnie wiadomo, nazwa jzyka C++ pochodzi od operatora inkrementacji, ktry jest
te winowajc powstania pierwszego dowcipu o tym jzyku. Przeciwnicy C++ zauwaaj,
e nawet nazwa tego jzyka jest bdna: Powinna brzmie ++C, poniewa jzyka tego
chcielibymy uywa tylko po wprowadzeniu do niego poprawek.)

3.5.2. Operatory relacyjne i logiczne


Java ma peny zestaw operatorw relacyjnych. Aby sprawdzi, czy dwa argumenty s rwne,
uywamy dwch znakw rwnoci (==). Na przykad wyraenie:
3 == 7

zwrci warto false.


Operator nierwnoci ma posta !=. Na przykad wyraenie:
3 != 7

zwrci warto true.


Dodatkowo dostpne s operatory wikszoci (>), mniejszoci (<), mniejszy lub rwny (<=)
oraz wikszy lub rwny (>=).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

72

Java. Podstawy
Operatorem koniunkcji logicznej w Javie, podobnie jak w C++, jest &&, a alternatywy logicznej ||. Jak nietrudno si domyli, znajc operator !=, znak wykrzyknika (!) jest operatorem
negacji. Wartoci wyrae z uyciem operatorw && i || s obliczane metod na skrty.
Warto drugiego argumentu nie jest obliczana, jeli ostateczny rezultat wynika ju z pierwszego. Jeeli midzy dwoma wyraeniami postawimy operator &&:
wyraenie1 && wyraenie2

i warto logiczna pierwszego z nich okae si false, to warto caego wyraenia nie moe
by inna ni false. W zwizku z tym warto drugiego wyraenia nie jest obliczana. Mona
to wykorzysta do unikania bdw. Jeli na przykad warto zmiennej x w wyraeniu:
x != 0 && 1/x > x + y

// Unikamy dzielenia przez zero.

jest rwna zero, druga jego cz nie bdzie obliczana. Zatem dziaanie 1/x nie zostanie
wykonane, jeli x = 0, dziki czemu nie wystpi bd dzielenia przez zero.
Podobnie warto wyraenia wyraenie1 || wyraenie2 ma automatycznie warto true, jeli
pierwsze wyraenie ma warto true. Warto drugiego nie jest obliczana.
W Javie dostpny jest te czasami przydatny operator trjargumentowy w postaci ?:. Wartoci wyraenia:
warunek ? wyraenie1 : wyraenie2

jest wyraenie1, jeli warunek ma warto true, lub wyraenie2, jeli warunek ma warto false.
Na przykad wynikiem wyraenia:
x < y ? x : y

jest x lub y w zalenoci od tego, ktra warto jest mniejsza.

3.5.3. Operatory bitowe


Do pracy na typach cakowitoliczbowych mona uywa operatorw dajcych dostp bezporednio do bitw, z ktrych si one skadaj. Oznacza to, e za pomoc techniki maskowania mona dobra si do poszczeglnych bitw w liczbie. Operatory bitowe to:
& (bitowa koniunkcja) | (bitowa alternatywa) ^ (lub wykluczajce) ~(bitowa negacja)

Operatory te dziaaj na bitach. Jeli na przykad zmienna n jest typu int, to wyraenie:
int fourthBitFromRight = (n & 8) / 8;

da wynik 1, jeli czwarty bit od prawej w binarnej reprezentacji wartoci zmiennej n jest
jedynk, lub 0 w przeciwnym razie. Dziki uyciu odpowiedniej potgi liczby 2 mona
zamaskowa wszystkie bity poza jednym.
Operatory & i | zastosowane do wartoci logicznych zwracaj wartoci logiczne.
S one podobne do operatorw && i ||, tyle e do obliczania wartoci wyrae z ich
uyciem nie jest stosowana metoda na skrty. A zatem wartoci obu argumentw s
zawsze obliczane przed zwrceniem wyniku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

73

Mona te uywa tak zwanych operatorw przesunicia, w postaci >> i <<, ktre przesuwaj liczb o jeden bit w prawo lub w lewo. Czsto przydatne s przy tworzeniu cigw
bitw uywanych przy maskowaniu:
int fourthBitFromRight = (n & (1 << 3)) >> 3;

Ostatni z operatorw bitowych >>> odpowiada za przesunicie bitowe w prawo z wypenieniem zerami, podczas gdy operator >> przesuwa bity w prawo i do ich wypenienia uywa
znaku liczby. Nie ma operatora <<<.
Argument znajdujcy si po prawej stronie operatorw przesunicia jest redukowany modulo do 32 bitw (chyba e argument po lewej stronie jest typu long;
w takim przypadku argument z prawej strony jest redukowany modulo do 64 bitw).
Na przykad warto wyraenia 1 << 35 jest taka sama jak 1 << 3, czyli 8.

W jzykach C i C++ nie ma gwarancji, e operator >> wykonuje przesunicie arytmetyczne (wypenienie bitem znaku), a nie przesunicie logiczne (wypenienie
zerami). Implementatorzy mog na wasn rk wybra takie dziaanie, ktre jest bardziej efektywne. Oznacza to, e operator >> w C++ jest zdefiniowany tylko dla liczb nieujemnych. Java jest wolna od tej wieloznacznoci.

3.5.4. Funkcje i stae matematyczne


Klasa Math zawiera zestaw funkcji matematycznych, ktre mog by bardzo przydatne przy
pisaniu niektrych rodzajw programw.
Do wycigania pierwiastka stopnia drugiego z liczby suy metoda sqrt:
double x = 4;
double y = Math.sqrt(x);
System.out.println(y);
// wynik 2.0

Midzy metodami println i sqrt jest pewna rnica. Pierwsza dziaa na obiekcie
System.out, ktry jest zdefiniowany w klasie System. Druga natomiast nie dziaa na
adnym obiekcie. Tego typu metody nosz nazw metod statycznych. Wicej na ich
temat dowiesz si w rozdziale 4.

W Javie nie ma operatora podnoszcego liczb do potgi. Do tego celu trzeba uy metody
pow dostpnej w klasie Math. Wyraenie:
double y = Math.pow(x, a);

ustawia warto zmiennej y na liczb x podniesion do potgi a (xa). Metoda pow przyjmuje
parametry typu double i zwraca wynik tego samego typu.
Klasa Math udostpnia take metody obliczajce funkcje trygonometryczne:
Math.sin
Math.cos

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

74

Java. Podstawy
Math.tan
Math.atan
Math.atan2

a take funkcj wykadnicz i jej odwrotno, czyli logarytm naturalny, oraz logarytm
dziesitny:
Math.exp
Math.log
Math.log10

Dostpne s te dwie stae okrelajce w maksymalnym przyblieniu stae matematyczne i e:


Math.PI
Math.E

Mona unikn stosowania przedrostka Math przed metodami i staymi matematycznymi, umieszczajc poniszy wiersz kodu na pocztku pliku rdowego:
import static java.lang.Math.*;

Na przykad:
System.out.println("Pierwiastek kwadratowy z \u03C0 wynosi " + sqrt(PI));

Importy statyczne opisujemy w rozdziale 4.

Funkcje klasy Math uywaj procedur z jednostki liczb zmiennoprzecinkowych


komputera w celu osignicia jak najlepszej wydajnoci. Jeli od prdkoci waniejsze s dokadne wyniki, naley posuy si klas StrictMath. Implementuje ona
algorytmy z biblioteki, ktr mona nieodpatnie rozpowszechnia, o nazwie fdlibm,
a ktra gwarantuje identyczne wyniki na wszystkich platformach. Kod rdowy tych algorytmw mona znale na stronie http://www.netlib.org/fdlibm (dla kadej funkcji fdlibm,
ktra ma wicej ni jedn definicj, klasa StrictMath uywa wersji zgodnej ze standardem IEEE 754, ktrej nazwa zaczyna si od litery e).

3.5.5. Konwersja typw numerycznych


Czsto konieczna jest konwersja z jednego typu liczbowego na inny. Rysunek 3.1 przedstawia dozwolone rodzaje konwersji.
Sze typw konwersji (rysunek 3.1) niepowodujcych strat danych oznaczono strzakami
cigymi. Konwersje, ktre mog spowodowa utrat czci danych, oznaczono strzakami
przerywanymi. Na przykad dua liczba cakowita, jak 123 456 789, skada si z wikszej
liczby cyfr, ni moe si zmieci w typie float. Po konwersji tej liczby cakowitej na liczb
typu float stracimy nieco na precyzji:
int n = 123456789;
float f = n;
// f ma warto 1.23456792E8

Jeli operatorem dwuargumentowym poczymy dwie wartoci (np. n + f, gdzie n to liczba


cakowita, a f liczba zmiennoprzecinkowa), zostan one przekonwertowane na wsplny typ
przed wykonaniem dziaania.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

75

Rysunek 3.1.
Dozwolone
konwersje
pomidzy typami
liczbowymi

Jeli ktry z operandw jest typu double, drugi rwnie zostanie


przekonwertowany na typ double.

W przeciwnym razie, jeli ktry z operandw jest typu float, drugi zostanie
przekonwertowany na typ float.

W przeciwnym razie, jeli ktry z operandw jest typu long, drugi zostanie
przekonwertowany na typ long.

W przeciwnym razie oba operandy zostan przekonwertowane na typ int.

3.5.6. Rzutowanie
W poprzednim podrozdziale dowiedzielimy si, e wartoci typu int s w razie potrzeby
automatycznie konwertowane na typ double. S jednak sytuacje, w ktrych chcemy przekonwertowa typ double na typ int. W Javie moliwe s takie konwersje, ale oczywicie
mog one pociga za sob utrat informacji. Konwersje, w ktrych istnieje ryzyko utraty
informacji, nazywaj si rzutowaniem (ang. casting). Aby wykona rzutowanie, naley przed
nazw rzutowanej zmiennej postawi nazw typu docelowego w okrgych nawiasach. Na
przykad:
double x = 9.997;
int nx = (int) x;

W wyniku tego dziaania zmienna nx bdzie miaa warto 9, poniewa rzutowanie liczby
zmiennoprzecinkowej na cakowit powoduje usunicie czci uamkowej.
Aby zaokrgli liczb zmiennoprzecinkow do najbliszej liczby cakowitej (co w wikszoci przypadkw bardziej si przydaje), naley uy metody Math.round:
double x = 9.997;
int nx = (int) Math.round(x);

Teraz zmienna nx ma warto 10. Przy zaokrglaniu za pomoc metody round nadal konieczne
jest zastosowanie rzutowania, tutaj (int). Jest to spowodowane tym, e metoda round zwraca
warto typu long, a tego typu warto mona przypisa zmiennej typu int wycznie na
drodze jawnego rzutowania, poniewa istnieje ryzyko utraty danych.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

76

Java. Podstawy

Wynikiem rzutowania na okrelony typ liczby, ktra nie mieci si w jego zakresie,
jest obcicie tej liczby i powstanie cakiem nowej wartoci. Na przykad rzutowanie
(byte) 300 da w wyniku liczb 44.

Nie mona wykona rzutowania pomidzy wartociami liczbowymi i logicznymi.


Zapobiega to powstawaniu wielu bdw. W nielicznych przypadkach, kiedy wymagana jest konwersja wartoci logicznej na warto liczbow, mona uy wyraenia
warunkowego, np. b ? 1 : 0.

3.5.7. Nawiasy i priorytety operatorw


Tabela 3.4 przedstawia zestawienie operatorw z uwzgldnieniem ich priorytetw. Jeli nie
ma nawiasw, kolejno wykonywania dziaa jest taka jak kolejno operatorw w tabeli.
Operatory o takim samym priorytecie s wykonywane od lewej do prawej, z wyjtkiem
tych, ktre maj wizanie prawostronne, podane w tabeli. Poniewa operator && ma wyszy
priorytet od operatora ||, wyraenie:
a && b || c

jest rwnoznaczne z wyraeniem:


(a && b) || c

Tabela 3.4. Priorytety operatorw


Operator

Wizanie

[] . () (wywoanie metody)

lewe

! ~++ -- + (jednoargumentowy) () (rzutowanie) new

prawe

* / %

lewe

+ -

lewe

<< >> >>>

lewe

< <= > >= instanceof

lewe

== !=

lewe

&

lewe

lewe

lewe

&&

lewe

||

lewe

?:

prawe

= += -= *= /= %= &= /= ^= <<= >>= >>>=

prawe

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

77

Ze wzgldu na fakt, e operator += ma wizanie lewostronne, wyraenie:


a += b += c

jest rwnowane z wyraeniem:


a += (b += c)

To znaczy, e warto wyraenia b += c (ktra wynosi tyle co b po dodawaniu) zostanie


dodana do a.
W przeciwiestwie do jzykw C i C++ Java nie ma operatora przecinka. Jednak
w pierwszym i trzecim argumencie instrukcji for mona uywa list wyrae oddzielonych przecinkami.

3.5.8. Typ wyliczeniowy


Czasami zmienna moe przechowywa tylko ograniczon liczb wartoci. Na przykad kiedy
sprzedajemy pizz albo ubrania, moemy mie rozmiary may, redni, duy i ekstra duy.
Oczywicie mona te rozmiary zakodowa w postaci cyfr 1, 2, 3 i 4 albo liter M, S, D i X.
To podejcie jest jednak podatne na bdy. Zbyt atwo mona zapisa w zmiennej nieprawidow warto (jak 0 albo m).
Mona te definiowa wasne typy wyliczeniowe (ang. enumerated type). Typ wyliczeniowy
zawiera skoczon liczb nazwanych wartoci. Na przykad:
enum Rozmiar { MAY, REDNI, DUY, EKSTRA_DUY };

Teraz moemy deklarowa zmienne takiego typu:


Rozmiar s = Rozmiar.REDNI;

Zmienna typu Rozmiar moe przechowywa tylko jedn z wartoci wymienionych w deklaracji
typu lub specjaln warto null, ktra oznacza, e zmienna nie ma w ogle adnej wartoci.
Bardziej szczegowy opis typw wyliczeniowych znajduje si w rozdziale 5.

3.6. acuchy
W zasadzie acuchy w Javie skadaj si z szeregu znakw Unicode. Na przykad acuch
"Java\u2122" skada si z piciu znakw Unicode: J, a, v, a i . W Javie nie ma wbudowanego typu String. Zamiast tego standardowa biblioteka Javy zawiera predefiniowan klas
o takiej wanie nazwie. Kady acuch w cudzysowach jest obiektem klasy String:
String e = "";
// pusty acuch
String greeting = "Cze!";

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

78

Java. Podstawy

3.6.1. Podacuchy
Aby wydoby z acucha podacuch, naley uy metody substring klasy String. Na
przykad:
String greeting = "Cze!";
String s = greeting.substring(0, 3);

Powyszy kod zwrci acuch "Cze".


Drugi parametr metody substring okrela pooenie pierwszego znaku, ktrego nie chcemy
skopiowa. W powyszym przykadzie chcielimy skopiowa znaki na pozycjach 0, 1 i 2
(od pozycji 0 do 2 wcznie). Z punktu widzenia metody substring nasz zapis oznacza: od
pozycji zero wcznie do pozycji 3 z wyczeniem.
Sposb dziaania metody substring ma jedn zalet: atwo mona obliczy dugo podacucha. acuch s.substring(a, b) ma dugo b - a. Na przykad acuch "Cze" ma
dugo 3 - 0 = 3.

3.6.2. Konkatenacja
W Javie, podobnie jak w wikszoci innych jzykw programowania, mona czy (konkatenowa) acuchy za pomoc znaku +.
String expletive = "brzydkie sowo";
String PG13 = "usunito";
String message = expletive + PG13;

Powyszy kod ustawia warto zmiennej message na acuch "brzydkiesowousunito"


(zauwa brak spacji pomidzy sowami). Znak + czy dwa acuchy w takiej kolejnoci,
w jakiej zostay podane, nic w nich nie zmieniajc.
Jeli z acuchem zostanie poczona warto niebdca acuchem, zostanie ona przekonwertowana na acuch (w rozdziale 5. przekonamy si, e kady obiekt w Javie mona
przekonwertowa na acuch). Na przykad kod:
int age = 13;
String rating = "PG" + age;

ustawia warto zmiennej rating na acuch "PG13".


Funkcjonalno ta jest czsto wykorzystywana w instrukcjach wyjciowych. Na przykad kod:
System.out.println("Odpowied brzmi " + answer);

jest w peni poprawny i wydrukowaby to, co potrzeba (przy zachowaniu odpowiednich odstpw, gdy po sowie brzmi znajduje si spacja).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

79

3.6.3. acuchw nie mona modyfikowa


W klasie String brakuje metody, ktra umoliwiaaby zmian znakw w acuchach. Aby
zmieni komunikat w zmiennej greeting na Czekaj, nie moemy bezporednio zamieni
trzech ostatnich znakw na kaj. Programici jzyka C s w takiej sytuacji zupenie bezradni. Jak zmodyfikowa acuch? W Javie okazuje si to bardzo proste. Naley poczy
podacuch, ktry chcemy zachowa, ze znakami, ktre chcemy wstawi w miejsce tych
wyrzuconych.
greeting = greeting.substring(0, 3) + "kaj";

Ta deklaracja zmienia warto przechowywan w zmiennej greeting na "Czekaj".


Jako e w acuchach nie mona zmienia znakw, obiekty klasy String w dokumentacji
jzyka Java s okrelane jako niezmienialne (ang. immutable). Podobnie jak liczba 3 jest
zawsze liczb 3, acuch "Cze!" zawsze bdzie szeregiem jednostek kodowych odpowiadajcych znakom C, z, e, , i !. Nie mona zmieni tych wartoci. Mona jednak, o czym
si przekonalimy, zmieni zawarto zmiennej greeting, sprawiajc, aby odwoywaa si
do innego acucha. Podobnie moemy zadecydowa, e zmienna liczbowa przechowujca
warto 3 zmieni odwoanie na warto 4.
Czy to nie odbija si na wydajnoci? Wydaje si, e zmiana jednostek kodowych byaby
prostsza ni tworzenie nowego acucha od pocztku. Odpowied brzmi: tak i nie. Rzeczywicie generowanie nowego acucha zawierajcego poczone acuchy "Cze" i "kaj" jest
nieefektywne, ale niezmienialno acuchw ma jedn zalet: kompilator moe traktowa
acuchy jako wspdzielone.
Aby zrozumie t koncepcj, wyobramy sobie, e rne acuchy s umieszczone w jednym wsplnym zbiorniku. Zmienne acuchowe wskazuj na okrelone lokalizacje w tym
zbiorniku. Jeli skopiujemy tak zmienn, zarwno oryginalny acuch, jak i jego kopia wspdziel te same znaki.
Projektanci jzyka Java doszli do wniosku, e korzyci pynce ze wspdzielenia s wiksze ni straty spowodowane edycj acuchw poprzez ekstrakcj podacuchw i konkatenacj. Przyjrzyj si swoim wasnym programom zapewne w wikszoci przypadkw
nie ma w nich modyfikacji acuchw, a gwnie rne rodzaje porwna (jest tylko jeden
dobrze znany wyjtek skadanie acuchw z pojedynczych znakw lub krtszych acuchw przychodzcych z klawiatury bd pliku; dla tego typu sytuacji w Javie przewidziano specjaln klas, ktr opisujemy w podrozdziale 3.6.9, Skadanie acuchw).

3.6.4. Porwnywanie acuchw


Do sprawdzania, czy dwa acuchy s identyczne, suy metoda equals. Wyraenie:
s.equals(t)

zwrci warto true, jeli acuchy s i t s identyczne, lub false w przeciwnym przypadku.
Zauwamy, e s i t mog by zmiennymi acuchowymi lub staymi acuchowymi. Na
przykad wyraenie:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

80

Java. Podstawy

Programici jzyka C, ktrzy po raz pierwszy stykaj si z acuchami w Javie,


nie mog ukry zdumienia, poniewa dla nich acuchy s tablicami znakw:
char greeting[] = "Cze!";

Jest to nieprawidowa analogia. acuch w Javie mona porwna ze wskanikiem char*:


char* greeting = "Cze!";

Kiedy zastpimy komunikat greeting jakim innym acuchem, Java wykona z grubsza
takie dziaania:
char* temp = malloc(6);
strncpy(temp, greeting, 3);
strncpy(temp + 3, "kaj", 3);
greeting = temp;

Teraz zmienna greeting wskazuje na acuch "Czekaj". Nawet najbardziej zatwardziay


wielbiciel jzyka C musi przyzna, e skadnia Javy jest bardziej elegancka ni szereg
wywoa funkcji strncpy. Co si stanie, jeli wykonamy jeszcze jedno przypisanie do
zmiennej greeting?
greeting = "Cze!";

Czy to nie spowoduje wycieku pamici? Przecie oryginalny acuch zosta umieszczony
na stercie. Na szczcie Java automatycznie usuwa nieuywane obiekty. Jeli dany blok
pamici nie jest ju potrzebny, zostanie wyczyszczony.
Typ String Javy duo atwiej opanowa programistom jzyka C++, ktrzy uywaj klasy
string zdefiniowanej w standardzie ISO/ANSI tego jzyka. Obiekty klasy string w C++
take automatycznie przydzielaj i czyszcz pami. Zarzdzanie pamici odbywa si
w sposb jawny za porednictwem konstruktorw, operatorw przypisania i destruktorw. Poniewa w C++ acuchy s zmienialne (ang. mutable), mona zmienia w nich
poszczeglne znaki.
"Cze!".equals(greeting")

jest poprawne. Aby sprawdzi, czy dwa acuchy s identyczne, z pominiciem wielkoci
liter, naley uy metody equalsIgnoreCase.
"Cze!".equalsIgnoreCase("cze!")

Do porwnywania acuchw nie naley uywa operatora ==! Za jego pomoc mona tylko
stwierdzi, czy dwa acuchy s przechowywane w tej samej lokalizacji. Oczywicie skoro
acuchy s przechowywane w tym samym miejscu, to musz by rwne. Moliwe jest
jednak te przechowywanie wielu kopii jednego acucha w wielu rnych miejscach.
String greeting = "Cze!";
// Inicjacja zmiennej greeting acuchem.
if (greeting == "Cze!") . . .
// prawdopodobnie true
if (greeting.substring(0, 3) == "Cze") . . .
// prawdopodobnie false

Gdyby maszyna wirtualna zawsze traktowaa rwne acuchy jako wspdzielone, mona by
byo je porwnywa za pomoc operatora ==. Wspdzielone s jednak tylko stae acuchowe. acuchy bdce na przykad wynikiem operacji wykonywanych za pomoc operatora + albo metody substring nie s wspdzielone. W zwizku z tym nigdy nie uywaj

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

81

operatora == do porwnywania acuchw, chyba e chcesz stworzy program zawierajcy


najgorszy rodzaj bdu pojawiajcy si od czasu do czasu i sprawiajcy wraenie, e
wystpuje losowo.
Osoby przyzwyczajone do klasy string w C++ musz zachowa szczegln ostrono przy porwnywaniu acuchw. Klasa C++ string przesania operator == do
porwnywania acuchw. W Javie do niefortunnie nadano acuchom takie same
wasnoci jak wartociom liczbowym, aby nastpnie nada im waciwoci wskanikw,
jeli chodzi o porwnywanie. Projektanci tego jzyka mogli zmieni definicj operatora
== dla acuchw, podobnie jak zrobili z operatorem +. C, kady jzyk ma swoje wady.
Programici jzyka C nigdy nie uywaj operatora == do porwnywania acuchw. Do
tego suy im funkcja strcmp. Metoda Javy compareTo jest dokadnym odpowiednikiem
funkcji strcmp. Mona napisa:
if (greeting.compareTo("Cze!") == 0) . . .

ale uycie metody equals wydaje si bardziej przejrzystym rozwizaniem.

3.6.5. acuchy puste i acuchy null


Pusty acuch "" to acuch o zerowej dugoci. Aby sprawdzi, czy acuch jest pusty, mona
uy instrukcji:
if (str.length() == 0)

lub
if (str.equals(""))

Pusty acuch jest w Javie obiektem zawierajcym informacj o swojej dugoci (0) i pust
tre. Ponadto zmienna typu String moe te zawiera specjaln warto o nazwie null,
oznaczajc, e aktualnie ze zmienn nie jest powizany aden obiekt (wicej informacji na
temat wartoci null znajduje si w rozdziale 4.). Aby sprawdzi, czy wybrany acuch jest
null, mona uy nastpujcej instrukcji warunkowej:
if (str == null)

Czasami trzeba te sprawdzi, czy acuch nie jest ani pusty, ani null. Wwczas mona si
posuy ponisz instrukcj warunkow:
if (str != null && str.length() != 0)

Najpierw naley sprawdzi, czy acuch nie jest null, poniewa wywoanie metody na
wartoci null jest bdem, o czym szerzej napisano w rozdziale 4.

3.6.6. Wsprzdne kodowe znakw i jednostki kodowe


acuchy w Javie s cigami wartoci typu char. Jak wiemy z podrozdziau 3.3.3, Typ
char, typ danych char jest jednostk kodow reprezentujc wsprzdne kodowe znakw
Unicode w systemie UTF-16. Najczciej uywane znaki Unicode maj reprezentacje

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

82

Java. Podstawy
skadajce si z jednej jednostki kodowej. Reprezentacje znakw dodatkowych skadaj si
z par jednostek kodowych.
Metoda length zwraca liczb jednostek kodowych, z ktrych skada si podany acuch
w systemie UTF-16. Na przykad:
String greeting = "Cze!";
int n = greeting.length();

// wynik = 6

Aby sprawdzi rzeczywist dugo, to znaczy liczb wsprzdnych kodowych znakw,


naley napisa:
int cpCount = greeting.codePointCount(0, greeting.length());

Wywoanie s.charAt(n) zwraca jednostk kodow znajdujc si na pozycji n, gdzie n ma


warto z zakresu pomidzy 0 a s.length() - 1. Na przykad:
char first = greeting.charAt(0);
char last = greeting.charAt(4);

// Pierwsza jest litera 'C'.


// Pity znak to ''.

Aby dosta si do i-tej wsprzdnej kodowej znaku, naley uy nastpujcych instrukcji:


int index = greeting.offsetByCodePoints(0, i);
int cp = greeting.codePointAt(index);

W Javie, podobnie jak w C i C++, wsprzdne i jednostki kodowe w acuchach


s liczone od 0.

Dlaczego robimy tyle szumu wok jednostek kodowych? Rozwamy ponisze zdanie:
oznacza zbir liczb cakowitych

Znak

wymaga dwch jednostek kodowych w formacie UTF-16. Wywoanie:

char ch = sentence.charAt(1)

nie zwrci spacji, ale drug jednostk kodow znaku . Aby unikn tego problemu, nie
naleao uywa typu char. Dziaa on na zbyt niskim poziomie.
Jeli nasz kod przemierza acuch i chcemy zobaczy kad wsprzdn kodow po kolei,
naley uy poniszych instrukcji:
int cp = sentence.codePointAt(i);
if (Character.isSupplementaryCodePoint(cp)) i += 2;
else i++;

Mona te napisa kod dziaajcy w odwrotn stron:


i--;
if (Character.isSurrogate(sentence.charAt(i))) i--;
int cp = sentence.codePointAt(i);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

83

3.6.7. API String


Klasa String zawiera ponad 50 metod. Zaskakujco wiele z nich jest na tyle uytecznych,
e moemy si spodziewa, i bdziemy ich czsto potrzebowa. Poniszy wycig z API
zawiera zestawienie metod, ktre w naszym odczuciu s najbardziej przydatne.
Takie wycigi z API znajduj si w wielu miejscach ksiki. Ich celem jest przyblienie czytelnikowi API Javy. Kady wycig z API zaczyna si od nazwy klasy, np.
java.lang.String znaczenie nazwy pakietu java.lang jest wyjanione w rozdziale 4.
Po nazwie klasy znajduj si nazwy, objanienia i opis parametrw jednej lub wikszej
liczby metod.
Z reguy nie wymieniamy wszystkich metod nalecych do klasy, ale wybieramy te, ktre
s najczciej uywane, i zamieszczamy ich zwize opisy. Pen list metod mona
znale w dokumentacji dostpnej w internecie (zobacz podrozdzia 3.6.8, Dokumentacja API w internecie).
Dodatkowo podajemy numer wersji Javy, w ktrej zostaa wprowadzona dana klasa.
Jeli jaka metoda zostaa do niej dodana pniej, ma wasny numer wersji.
java.lang.String 1.0

char charAt(int index)

Zwraca jednostk kodow znajdujc si w okrelonej lokalizacji. Metoda ta jest


przydatna tylko w pracy na niskim poziomie nad jednostkami kodowymi.

int codePointAt(int index) 5.0

Zwraca wsprzdn kodow znaku, ktra zaczyna si lub koczy w okrelonej


lokalizacji.

int offsetByCodePoints(int startIndex, int cpCount) 5.0

Zwraca indeks wsprzdnej kodowej, ktra znajduje si w odlegoci cpCount


wsprzdnych kodowych od wsprzdnej kodowej startIndex.

int compareTo(String other)

Zwraca warto ujemn, jeli acuch znajduje si przed innym (other) acuchem
w kolejnoci sownikowej, warto dodatni, jeli znajduje si za nim, lub 0,
jeli acuchy s identyczne.

boolean endsWith(String suffix)

Zwraca warto true, jeli na kocu acucha znajduje si przyrostek suffix.

boolean equals(Object other)

Zwraca warto true, jeli acuch jest identyczny z acuchem other.

boolean equalsIgnoreCase(String other)

Zwraca warto true, jeli acuch jest identyczny z innym acuchem


przy zignorowaniu wielkoci liter.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

84

Java. Podstawy

int indexOf(String str)

int indexOf(String str, int fromIndex)

int indexOf(int cp)

int indexOf(int cp, int fromIndex)

Zwraca pocztek pierwszego podacucha podanego w argumencie str


lub wsprzdnej kodowej cp, szukanie zaczynajc od indeksu 0, pozycji
fromIndex czy te -1, jeli napisu str nie ma w tym acuchu.

int lastIndexOf(String str)

int lastIndexOf(String str, int fromIndex)

int lastindexOf(int cp)

int lastindexOf(int cp, int fromIndex)

Zwraca pocztek ostatniego podacucha podanego w argumencie str


lub wsprzdnej kodowej cp. Szukanie zaczyna od koca acucha albo pozycji
fromIndex.

int length()

Zwraca dugo acucha.

int codePointCount(int startIndex, int endIndex) 5.0

Zwraca liczb wsprzdnych kodowych znakw znajdujcych si pomidzy


pozycjami StartIndex i endIndex - 1. Surogaty niemajce pary s traktowane
jako wsprzdne kodowe.

String replace(CharSequence oldString, CharSequence newString)

Zwraca nowy acuch, w ktrym wszystkie acuchy oldString zostay zastpione


acuchami newString. Mona poda obiekt String lub StringBuilder
dla parametru CharSequence.

boolean startsWith(String prefix)

Zwraca warto true, jeli acuch zaczyna si od podacucha prefix.

String substring(int beginIndex)

String substring(int beginIndex, int endIndex)

Zwraca nowy acuch skadajcy si ze wszystkich jednostek kodowych


znajdujcych si na pozycjach od beginIndex do koca acucha albo
do endIndex - 1.

String toLowerCase()

Zwraca nowy acuch zawierajcy wszystkie znaki z oryginalnego cigu


przekonwertowane na mae litery.

String toUpperCase()

Zwraca nowy acuch zawierajcy wszystkie znaki z oryginalnego cigu


przekonwertowane na due litery.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

85

String trim()

Usuwa wszystkie biae znaki z pocztku i koca acucha. Zwraca wynik jako
nowy acuch.

3.6.8. Dokumentacja API w internecie


Jak si przed chwil przekonalimy, klasa String zawiera mnstwo metod. W bibliotekach
standardowych jest kilka tysicy klas, ktre zawieraj duo wicej metod. Zapamitanie
wszystkich przydatnych metod i klas jest niemoliwe. Z tego wzgldu koniecznie trzeba si
zapozna z zamieszczon w internecie dokumentacj API, w ktrej mona znale informacje
o kadej metodzie dostpnej w standardowej bibliotece. Dokumentacja API wchodzi te
w skad pakietu JDK. Aby j otworzy, naley w przegldarce wpisa adres pliku docs/api/
index.html znajdujcego si w katalogu instalacji JDK. Stron t przedstawia rysunek 3.2.
Rysunek 3.2.
Trzyczciowe
okno
dokumentacji API

Ekran jest podzielony na trzy czci. W grnej ramce po lewej stronie okna znajduje si lista
wszystkich dostpnych pakietw. Pod ni jest nieco wiksza ramka, ktra zawiera listy wszystkich klas. Kliknicie nazwy jednej z klas powoduje wywietlenie dokumentacji tej klasy
w duym oknie po prawej stronie (zobacz rysunek 3.3). Aby na przykad uzyska dodatkowe
informacje na temat metod dostpnych w klasie String, naley w drugiej ramce znale
odnonik String i go klikn.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

86

Java. Podstawy

Rysunek 3.3.
Opis klasy String

Nastpnie za pomoc suwaka znajdujemy zestawienie wszystkich metod posortowanych


w kolejnoci alfabetycznej (zobacz rysunek 3.4). Aby przeczyta dokadny opis wybranej
metody, kliknij jej nazw (rysunek 3.5). Jeli na przykad klikniemy odnonik compareToIgnoreCase, wywietli si opis metody compareToIgnoreCase.
Dodaj stron docs/api/index.html do ulubionych w swojej przegldarce.

3.6.9. Skadanie acuchw


Czasami konieczne jest zoenie acucha z krtszych acuchw, takich jak znaki wprowadzane z klawiatury albo sowa zapisane w pliku. Zastosowanie konkatenacji do tego celu
byoby wyjciem bardzo nieefektywnym. Za kadym razem, gdy czone s znaki, tworzony
jest nowy obiekt klasy String. Zabiera to duo czasu i pamici. Klasa StringBuilder pozwala
unikn tego problemu.
Aby zoy acuch z wielu bardzo maych czci, naley wykona nastpujce czynnoci.
Najpierw tworzymy pusty obiekt builder klasy StringBuilder (szczegowy opis konstruktorw i operatora new znajduje si w rozdziale 4.):
StringBuilder builder = new StringBuilder();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

Rysunek 3.4.
Zestawienie
metod klasy
String

Rysunek 3.5.
Szczegowy opis
metody klasy
String

Kolejne czci dodajemy za pomoc metody append.


builder.append(ch);
builder.append(str);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

// Dodaje jeden znak.


// Dodaje acuch.

87

88

Java. Podstawy
Po zoeniu acucha wywoujemy metod toString. Zwrci ona obiekt klasy String zawierajcy sekwencj znakw znajdujc si w obiekcie builder.
String completedString = builder.toString();

Klasa StringBuilder zostaa wprowadzona w JDK 5.0. Jej poprzedniczka o nazwie


StringBuffer jest nieznacznie mniej wydajna, ale pozwala na dodawanie lub
usuwanie znakw przez wiele wtkw. Jeli edycja acucha odbywa si w caoci
w jednym wtku (tak jest zazwyczaj), naley uywa metody StringBuilder. API obu tych
klas s identyczne.

Poniszy wycig z API przedstawia najczciej uywane metody dostpne w klasie


StringBuilder.
java.lang.StringBuilder 5.0

StringBuilder()

Tworzy pusty obiekt builder.

int length()

Zwraca liczb jednostek kodowych zawartych w obiekcie builder lub buffer.

StringBuilder append(String str)

Dodaje acuch c.

StringBuilder append(char c)

Dodaje jednostk kodow c.

StringBuilder appendCodePoint(int cp)

Dodaje wsprzdn kodow, konwertujc j na jedn lub dwie jednostki


kodowe.

void setCharAt(int i, char c)

Ustawia i-t jednostk kodow na c.

StringBuilder insert(int offset, String str)

Wstawia acuch, umieszczajc jego pocztek na pozycji offset.

StringBuilder insert(int offset, char c)

Wstawia jednostk kodow na pozycji offset.

StringBuilder delete(int startIndex, int endIndex)

Usuwa jednostki kodowe znajdujce si midzy pozycjami startIndex i endIndex - 1.

String toString()

Zwraca acuch zawierajcy sekwencj znakw znajdujc si w obiekcie


builder lub buffer.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

89

3.7. Wejcie i wyjcie


Aby programy byy bardziej interesujce, powinny przyjmowa dane wejciowe i odpowiednio formatowa dane wyjciowe. Oczywicie odbieranie danych od uytkownika w tworzonych obecnie programach odbywa si za porednictwem GUI. Jednak programowanie interfejsu wymaga znajomoci wielu narzdzi i technik, ktre s nam jeszcze nieznane. Poniewa
naszym aktualnym priorytetem jest zapoznanie si z jzykiem programowania Java, poprzestaniemy na razie na skromnych programach konsolowych. Programowanie GUI opisuj
rozdziay od 7. do 9.

3.7.1. Odbieranie danych wejciowych


Wiadomo ju, e drukowanie danych do standardowego strumienia wyjciowego (tzn. do okna
konsoli) jest atwe. Wystarczy wywoa metod System.out.println. Pobieranie danych ze
standardowego strumienia wejciowego System.in nie jest ju takie proste. Czytanie danych
odbywa si za pomoc skanera bdcego obiektem klasy Scanner przywizanego do strumienia System.in:
Scanner in = new Scanner(System.in);

Operator new i konstruktory zostay szczegowo omwione w rozdziale 4.


Nastpnie dane wejciowe odczytuje si za pomoc rnych metod klasy Scanner. Na przykad metoda nextLine czyta jeden wiersz danych:
System.out.print("Jak si nazywasz? ");
String name = in.nextLine();

W tym przypadku zastosowanie metody nextLine zostao podyktowane tym, e dane na wejciu mog zawiera spacje. Aby odczyta jedno sowo (ograniczone spacjami), naley wywoa ponisz metod:
String firstName = in.next();

Do wczytywania liczb cakowitych suy metoda nextInt:


System.out.print("Ile masz lat? ");
int age = in.nextInt();

Podobne dziaanie ma metoda nextDouble, z tym e dotyczy liczb zmiennoprzecinkowych.


Program przedstawiony na listingu 3.2 prosi uytkownika o przedstawienie si i podanie wieku,
a nastpnie drukuje informacj typu:
Witaj uytkowniku ukasz. W przyszym roku bdziesz mie 32 lata.

Listing 3.2. InputTest.java


import java.util.*;
/**

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

90

Java. Podstawy
* Ten program demonstruje pobieranie danych z konsoli.
* @version 1.10 2004-02-10
* @author Cay Horstmann
*/
public class InputTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
// Pobranie pierwszej porcji danych.
System.out.print("Jak si nazywasz? ");
String name = in.nextLine();
// Pobranie drugiej porcji danych.
System.out.print("Ile masz lat? ");
int age = in.nextInt();
// Wydruk danych w konsoli.
System.out.println("Witaj uytkowniku" + name + ". W przyszym roku bdziesz
mie " + (age + 1) + "lat.");
}
}

Klasa Scanner nie nadaje si do odbioru hase z konsoli, poniewa wprowadzane


dane s widoczne dla kadego. W Java SE 6 wprowadzono klas Console, ktra
suy wanie do tego celu. Aby pobra haso, naley uy poniszego kodu:
Console cons = System.console();
String username = cons.readLine("Nazwa uytkownika: ");
char[] passwd = cons.readPassword("Haso: ");

Ze wzgldw bezpieczestwa haso jest zwracane w tablicy znakw zamiast w postaci


acucha. Po zakoczeniu pracy z hasem powinno si natychmiast nadpisa przechowujc je tablic, zastpujc obecne elementy jakimi wartociami wypeniajcymi (przetwarzanie tablic jest opisane w dalszej czci tego rozdziau).
Przetwarzanie danych wejciowych za pomoc obiektu Console nie jest tak wygodne jak
w przypadku klasy Scanner. Jednorazowo mona wczyta tylko jeden wiersz danych.
Nie ma metod umoliwiajcych odczyt pojedynczych sw lub liczb.

Naley take zwrci uwag na poniszy wiersz:


import java.util.*;

Znajduje si on na pocztku programu. Definicja klasy Scanner znajduje si w pakiecie


java.util. Uycie jakiejkolwiek klasy spoza podstawowego pakietu java.lang wymaga
wykorzystania dyrektywy import. Pakiety i dyrektywy import zostay szczegowo opisane
w rozdziale 4.
java.util.Scanner 5.0

Scanner(InputStream in)

Tworzy obiekt klasy Scanner przy uyciu danych z podanego strumienia


wejciowego.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

91

String nextLine()

Wczytuje kolejny wiersz danych.

String text()

Wczytuje kolejne sowo (znakiem rozdzielajcym jest spacja).

int nextInt()

double nextDouble()

Wczytuje i konwertuje kolejn liczb cakowit lub zmiennoprzecinkow.

boolean hasNext()

Sprawdza, czy jest kolejne sowo.

boolean hasNextInt()

boolean hasNextDouble()

Sprawdza, czy dana sekwencja znakw jest liczb cakowit, czy liczb
zmiennoprzecinkow.
java.lang.System 1.0

static Console console() 6

Zwraca obiekt klasy Console umoliwiajcy interakcj z uytkownikiem


za porednictwem okna konsoli, jeli jest to moliwe, lub warto null
w przeciwnym przypadku. Obiekt Console jest dostpny dla wszystkich
programw uruchomionych w oknie konsoli. W przeciwnym przypadku
dostpno zaley od systemu.
java.io.Console 6

static char[] readPassword(String prompt, Object args)

static String readLine(String prompt, Object args)

Wywietla acuch prompt i wczytuje wiersz danych z konsoli. Za pomoc


parametrw args mona poda argumenty formatowania, o czym mowa
w nastpnym podrozdziale.

3.7.2. Formatowanie danych wyjciowych


Warto zmiennej x mona wydrukowa w konsoli za pomoc instrukcji System.out.print(x).
Polecenie to wydrukuje warto zmiennej x z najwiksz liczb cyfr niebdcych zerami,
ktre moe pomieci dany typ. Na przykad kod:
double x = 10000.0 / 3.0;
System.out.print(x);

wydrukuje:
3333.3333333333335

Problemy zaczynaj si wtedy, gdy chcemy na przykad wywietli liczb dolarw i centw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

92

Java. Podstawy
W pierwotnych wersjach Javy formatowanie liczb sprawiao sporo problemw. Na szczcie
w wersji Java SE 5 wprowadzono zasuon ju metod printf z biblioteki C. Na przykad
wywoanie:
System.out.printf("%8.2f", x);

drukuje warto zmiennej x w polu o szerokoci 8 znakw i z dwoma miejscami po przecinku.


To znaczy, e poniszy wydruk zawiera wiodc spacj i siedem widocznych znakw:
3333,33

Metoda printf moe przyjmowa kilka parametrw. Na przykad:


System.out.printf("Witaj, %s. W przyszym roku bdziesz mie lat %d", name, age);

Kady specyfikator formatu, ktry zaczyna si od znaku %, jest zastpowany odpowiadajcym mu argumentem. Znak konwersji znajdujcy si na kocu specyfikatora formatu okrela
typ wartoci do sformatowania: f oznacza liczb zmiennoprzecinkow, s acuch, a d liczb
cakowit dziesitn. Tabela 3.5 zawiera wszystkie znaki konwersji.
Dodatkowo mona kontrolowa wygld sformatowanych danych wyjciowych za pomoc
kilku znacznikw. Tabela 3.6 przedstawia wszystkie znaczniki. Na przykad przecinek dodaje
separator grup. To znaczy:
System.out.printf("%, .2f", 10000.0 / 3.0);

wydrukuje:
3 333,33

Mona stosowa po kilka znacznikw naraz, na przykad zapis "%,(.2f" oznacza uycie
separatorw grup i ujcie liczb ujemnych w nawiasy.
Za pomoc znaku konwersji s mona formatowa dowolne obiekty. Jeli obiekt
taki implementuje interfejs Formattable, wywoywana jest jego metoda formatTo.
W przeciwnym razie wywoywana jest metoda toString w celu przekonwertowania obiektu
na acuch. Metoda toString opisana jest w rozdziale 5., a interfejsy w rozdziale 6.

Aby utworzy sformatowany acuch, ale go nie drukowa, naley uy statycznej metody
String.format:
String message = String.format("Witaj, %s. W przyszym roku bdziesz mie lat %d",
name, age);

Mimo e typ Date omawiamy szczegowo dopiero w rozdziale 4., przedstawiamy krtki opis
opcji metody printf do formatowania daty i godziny. Stosowany jest format dwuliterowy,
w ktrym pierwsza litera to t, a druga jest jedn z liter znajdujcych si w tabeli 3.7. Na
przykad:
System.out.printf("%tc", new Date());

Wynikiem jest aktualna data i godzina w nastpujcym formacie1:


Pn lis 26 15:47:12 CET 2007
1

Aby program zadziaa, na pocztku kodu rdowego naley wstawi wiersz import java.util.Date;
przyp. tum.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

93

Tabela 3.5. Znaki konwersji polecenia printf


Znak konwersji

Typ

Przykad

Liczba cakowita dziesitna

159

Liczba cakowita szesnastkowa

9f

Liczba cakowita semkowa

237

Liczba zmiennoprzecinkowa

15.9

Liczba zmiennoprzecinkowa w notacji wykadniczej

1.59e+01

Liczba zmiennoprzecinkowa (krtszy z formatw e i f)

Liczba zmiennoprzecinkowa szesnastkowa

0x1.fccdp3

acuch

Witaj

Znak

Warto logiczna

true

Kod mieszajcy

42628b2

tx

Data i godzina

Zobacz tabela 3.7

Symbol procenta

Separator wiersza waciwy dla platformy

Tabela 3.6. Znaczniki polecenia printf


Flaga

Przeznaczenie

Przykad

Oznacza, e przed liczbami zawsze ma si znajdowa znak.

+3333,33

spacja

Oznacza, e liczby nieujemne s poprzedzone dodatkow


spacj.

| 3333,33|

Oznacza dodanie pocztkowych zer.

003333,33

Oznacza, e pole ma by wyrwnane do lewej.

|3333,33 |

Oznacza, e liczby ujemne maj by prezentowane


w nawiasach.

(3333,33)

Oznacza, e poszczeglne grupy maj by rozdzielane.

3 333,33

# (dla formatu f)

Oznacza, e zawsze ma by dodany przecinek dziesitny.

3 333,

# (dla formatu x lub o)

Dodaje odpowiednio przedrostek 0x lub 0.

0xcafe

Okrela indeks argumentu do sformatowania. Na przykad


%1$d %1$x drukuje t sam liczb w notacji dziesitnej
i szesnastkowej.

159 9F

<

Formatuje podobnie jak poprzednia specyfikacja. Na przykad 159 9F


zapis %d %<x wydrukuje liczb w formacie dziesitnym
i szesnastkowym.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

94

Java. Podstawy

Tabela 3.7. Znaki konwersji Date i Time


Znak konwersji

Typ

Przykad

Pena data i godzina

Pn lis 26 15:47:12 CET 2007

Data w formacie ISO 8601

2007-11-26

Data w formacie stosowanym w USA


(miesic/dzie/rok)

11/26/07

Godzina w formacie 24-godzinnym

15:25:10

Godzina w formacie 12-godzinnym

03:52:55 PM

Godzina w formacie 24-godzinnym, bez sekund

15:25

Rok w formacie czterocyfrowym

2007

Dwie ostatnie cyfry roku (z wiodcymi zerami)

07

Dwie pierwsze cyfry roku (z wiodcymi zerami)

20

Pena nazwa miesica

listopad

b lub h

Skrt nazwy miesica

lis

Dwie cyfry oznaczajce numer miesica


(z wiodcym zerem)

02

Numer dnia miesica (z wiodcym zerem)

09

Numer dnia miesica (bez wiodcego zera)

Pena nazwa dnia

poniedziaek

Skrt nazwy dnia

Pn

Dzie roku w formacie trzycyfrowym


(z wiodcymi zerami), od 001 do 366

069

Godzina w formacie dwucyfrowym


(z wiodcym zerem), od 00 do 23

18

Godzina w formacie dwucyfrowym


(bez wiodcego zera), od 00 do 23

18

Godzina w formacie dwucyfrowym


(z wiodcym zerem), od 01 do 12

06

Godzina w formacie dwucyfrowym


(bez wiodcego zera), od 1 do 12

Minuty w formacie dwucyfrowym


(z wiodcym zerem)

05

Sekundy w formacie dwucyfrowym


(z wiodcym zerem)

19

Milisekundy w formacie trzycyfrowym


(z wiodcymi zerami)

046

Nanosekundy w formacie dziewiciocyfrowym


(z wiodcymi zerami)

047000000

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

95

Tabela 3.7. Znaki konwersji Date i Time cig dalszy


Znak konwersji

Typ

Przykad

Symbol oznaczajcy godziny przedpoudniowe


i popoudniowe (wielkie litery)

PM

Symbol oznaczajcy godziny przedpoudniowe


i popoudniowe (mae litery)

pm

Przesunicie wzgldem czasu GMT w standardzie


RFC 822

+0100

Strefa czasowa

CET

Liczba sekund, ktre upyny od daty 1970-01-01,


00:00:00 GMT

1196089646

Liczba milisekund, ktre upyny od daty


1970-01-01, 00:00:00

1196089667265

Jak wida w tabeli 3.7, niektre formaty zwracaj tylko okrelon cz daty, na przykad
tylko dzie albo tylko miesic. Formatowanie kadej czci daty oddzielnie byoby nierozsdnym rozwizaniem. Dlatego w acuchu formatujcym mona poda indeks argumentu,
ktry ma by sformatowany. Indeks musi si znajdowa bezporednio po symbolu % i koczy si symbolem $. Na przykad:
System.out.printf("%1$s %2$te %2$tB %2$tY", "Data:", new Date());

Wynik wykonania tego wyraenia bdzie nastpujcy:


Data: luty 9, 2004

Ewentualnie mona uy flagi <. Oznacza ona, e ten sam argument co w poprzedniej specyfikacji formatu powinien zosta uyty ponownie. Ponisza instrukcja:
System.out.printf("%s %te %<tB %<tY", "Data:", new Date());

da taki sam wynik jak poprzedni fragment kodu.


Wartoci indeksw argumentw zaczynaj si od 1, nie od 0; zapis %1$... dotyczy
pierwszego argumentu. W ten sposb zapobiegnito myleniu ich z flag 0.

Przedstawione zostay wszystkie wasnoci metody printf. Rysunek 3.6 prezentuje schemat opisujcy skadni specyfikatorw formatu.
Niektre z zasad formatowania s zwizane z okrelon lokalizacj. Na przykad
w Niemczech separatorem dziesitnym jest przecinek, a zamiast Poniedziaek
wywietla si Montag. Kontrola funkcji midzynarodowych programu zostaa opisana
w drugim tomie, w rozdziale 5.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

96

Java. Podstawy

Rysunek 3.6. Skadnia specyfikatora format

3.7.3. Zapis i odczyt plikw


Aby odczyta dane z pliku, naley utworzy obiekt Scanner:
Scanner in = new Scanner(Paths.get("mojplik.txt"));

Jeli nazwa pliku zawiera lewe ukoniki, naley pamita o zastosowaniu dla nich symboli
zastpczych: "c:\\mojkatalog\\mojplik.txt".
Po wykonaniu tych czynnoci mona odczyta zawarto pliku za pomoc metod klasy
Scanner, ktre byy opisywane wczeniej.

Aby zapisa dane do pliku, naley posuy si obiektem PrintWriter. Naley poda konstruktorowi nazw pliku:
PrintWriter out = new PrintWriter("mojplik.txt");

Jeli plik nie istnieje, mona uy metod print, println lub printf, podobnie jak w przypadku drukowania do wyjcia System.out.
Obiekt Scanner mona utworzy przy uyciu parametru acuchowego, ale parametr
ten zostanie zinterpretowany jako dane, a nie nazwa pliku. Jeli na przykad
napiszemy:
Scanner in = new Scanner("mojplik.txt");

// Bd?

obiekt klasy Scanner bdzie widzia dane skadajce si z jedenastu znakw: 'm', 'o',
'j' itd. Istnieje due prawdopodobiestwo, e autorowi kodu chodzio o co innego.

Jasne jest zatem, e dostp do plikw jest rwnie atwy jak uywanie wejcia System.in oraz
wyjcia System.out. Jest tylko jedno ale: jeli obiekt klasy Scanner zostanie utworzony
przy uyciu nazwy nieistniejcego pliku albo PrintWriter przy uyciu nazwy, ktrej nie
mona utworzy, wystpi wyjtek. Dla kompilatora Javy wyjtki te maj wiksze znaczenie
ni na przykad wyjtek dzielenia przez zero. Rozmaite techniki obsugi wyjtkw zostay
opisane w rozdziale 11. Na razie wystarczy, jeli poinformujemy kompilator, e wiemy, i
istnieje moliwo wystpienia wyjtku zwizanego z nieodnalezieniem pliku. Robimy to,
dodajc do metody main klauzul throws:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

97

Wzgldne cieki do plikw (np. mojplik.txt, mojkatalog/mojplik.txt lub ../mojplik.txt)


s lokalizowane wzgldem katalogu, w ktrym uruchomiono maszyn wirtualn.
Jeli uruchomimy program z wiersza polece za pomoc polecenia:
java MyProg

katalogiem pocztkowym bdzie aktualny katalog okna konsoli. W zintegrowanym rodowisku programistycznym katalog pocztkowy jest okrelany przez IDE. Lokalizacj tego
katalogu mona sprawdzi za pomoc poniszego wywoania:
String dir = System.getProperty("user.dir");

Jeli nie moesz si poapa w lokalizacji plikw, moesz zastosowa cieki bezwzgldne,
takie jak "c:\\mojkatalog\\mojplik.txt" lub "/home/ja/mojkatalog/mojplik.txt".
public static void main(String[] args) throws FileNotFoundException
{
Scanner in = new Scanner(Paths.get("mojplik.txt"));
. . .
}

Wiemy ju, jak odczytywa i zapisywa pliki zawierajce dane tekstowe. Bardziej zaawansowane zagadnienia, jak obsuga rnych standardw kodowania znakw, przetwarzanie
danych binarnych, odczyt katalogw i zapis plikw archiwum zip, zostay opisane w rozdziale 1. drugiego tomu.
Przy uruchamianiu programu w wierszu polece mona uy waciwej danemu
systemowi skadni przekierowywania w celu dodania dowolnego pliku do wejcia
System.in i wyjcia System.out:
java MyProg < mojplik.txt > output.txt

Dziki temu nie trzeba si zajmowa obsug wyjtku FileNotFoundException.


java.util.Scanner 5.0

Scanner(Path p)

Tworzy obiekt klasy Scanner, ktry wczytuje dane z podanej cieki.

Scanner(String data)

Tworzy obiekt klasy Scanner, ktry wczytuje dane z podanego acucha.


java.io.PrintWriter 1.1

PrintWriter(String fileName)

Tworzy obiekt PrintWriter, ktry zapisuje dane do pliku o podanej nazwie.


java.nio.file.Paths 7.0

static Path get(String pathname)

Tworzy obiekt Path ze cieki o podanej nazwie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

98

Java. Podstawy

3.8. Przepyw sterowania


W Javie, podobnie jak w kadym jzyku programowania, do kontroli przepywu sterowania
uywa si instrukcji warunkowych i ptli. Zaczniemy od instrukcji warunkowych, aby pniej przej do ptli. Na zakoczenie omwimy nieco nieporczn instrukcj switch, ktra
moe si przyda, gdy konieczne jest sprawdzenie wielu wartoci jednego wyraenia.
Instrukcje sterujce Javy s niemal identyczne z instrukcjami sterujcymi w C++.
Rnica polega na tym, e w Javie nie ma instrukcji go to, ale jest wersja instrukcji break z etykiet, ktrej mona uy do przerwania dziaania zagniedonej ptli
(w takich sytuacjach, w ktrych w C prawdopodobnie uylibymy instrukcji go to). Nareszcie dodano wersj ptli for, ktra nie ma odpowiednika w jzykach C i C++. Jest podobna
do ptli foreach w C#.

3.8.1. Zasig blokowy


Zanim przejdziemy do instrukcji sterujcych, musimy pozna pojcie blok.
Blok, czyli instrukcja zoona, to dowolna liczba instrukcji Javy ujtych w nawiasy klamrowe. Blok okrela zasig zmiennych. Bloki mona zagnieda w innych blokach. Poniej
znajduje si blok zagniedony w bloku metody main:
public static void main(String[] args)
{
int n;
. . .
{
int k;
. . .
}
// Definicja zmiennej k jest dostpna tylko do tego miejsca.
}

Nie mona zdefiniowa dwch zmiennych o takiej samej nazwie w dwch zagniedonych
blokach. Na przykad poniszy kod jest bdny i nie mona go skompilowa:
public static void main(String[] args)
{
int n;
. . .
{
int k;
int n;
// Bd nie mona ponownie zdefiniowa zmiennej n w bloku wewntrznym.
. . .
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

99

W C++ mona wewntrz bloku ponownie zdefiniowa zmienn wczeniej zdefiniowan na zewntrz tego bloku. Ta definicja wewntrzna przesania wtedy definicj zewntrzn. Moe to by jednak rdem bdw i z tego powodu operacja taka nie
jest dozwolona w Javie.

3.8.2. Instrukcje warunkowe


W Javie instrukcja warunkowa ma nastpujc posta:
if (warunek) instrukcja

Warunek musi by umieszczony w nawiasach okrgych.


Podobnie jak w wielu jzykach, w Javie czsto po spenieniu jednego warunku konieczne
jest wykonanie wielu instrukcji. W takim przypadku naley zastosowa blok instrukcji
w nastpujcej postaci:
{
instrukcja1;
instrukcja2;
}

Na przykad:
if (yourSales >= target)
{
performance = "rednio";
bonus = 100;
}

Wszystkie instrukcje znajdujce si pomidzy klamrami zostan wykonane, jeli warto


zmiennej yourSales bdzie wiksza lub rwna wartoci zmiennej target (zobacz rysunek 3.7).
Blok (czasami nazywany instrukcj zoon) umoliwia wykonanie wicej ni jednej
instrukcji we wszystkich miejscach, gdzie przewiduje si uycie instrukcji.

Bardziej oglna posta instrukcji warunkowej w Javie jest nastpujca (zobacz rysunek 3.8):
if (warunek) instrukcja1 else instrukcja2

Na przykad:
if (yourSales >=
{
performance =
bonus = 100 +
}
else
{
performance =
bonus = 0;
}

target)
"rednio";
0.01 * (yourSales - target);

"Sabo";

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

100

Java. Podstawy

Rysunek 3.7.
Diagram
przepywu
sterowania
instrukcji if

Rysunek 3.8.
Diagram
przepywu
sterowania
instrukcji if-else

Stosowanie else jest opcjonalne. Dane else zawsze odpowiada najbliszemu poprzedzajcemu je if. W zwizku z tym w instrukcji:
if (x <= 0) if (x == 0) sign = 0; else sign = -1;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

101

else odpowiada drugiemu if. Oczywicie dobrze by byo zastosowa klamry, aby kod by
bardziej czytelny:
if (x <= 0) { if (x == 0) sign = 0; else sign = -1; }

Czsto stosuje si kilka instrukcji else-if jedna po drugiej (zobacz rysunek 3.9). Na przykad:
if (yourSales >= 2 * target)
{
performance = "Znakomicie";
bonus = 1000;
}
else if (yourSales >= 1.5 * target)
{
performance = "Niele";
bonus = 500;
}
else if (yourSales >= target)
{
performance = "rednio";
bonus = 100;
}
else
{
System.out.println("Jeste zwolniony");
}

3.8.3. Ptle
Ptla while wykonuje instrukcj (albo blok instrukcji) tak dugo, jak dugo warunek ma
warto true. Oglna posta instrukcji while jest nastpujca:
while (warunek) instrukcja

Instrukcje ptli while nie zostan nigdy wykonane, jeli warunek ma warto false na
pocztku (zobacz rysunek 3.10).
Program z listingu 3.3 oblicza, ile czasu trzeba skada pienidze, aby dosta okrelon
emerytur, przy zaoeniu, e kadego roku wpacana jest taka sama kwota, i przy okrelonej stopie oprocentowania wpaconych pienidzy.
W ciele ptli zwikszamy licznik i aktualizujemy biec kwot uzbieranych pienidzy, a
ich suma przekroczy wyznaczon kwot.
while (balance < goal)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
years++;
}
System.out.println(years + "lat.");

(Nie naley ufa temu programowi przy planowaniu emerytury. Pominito w nim kilka
szczegw, takich jak inflacja i przewidywana dugo ycia).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

102

Java. Podstawy

Rysunek 3.9.
Diagram
przepywu
sterowania
instrukcji
if-else if (wiele
odgazie)

Ptla while sprawdza warunek na samym pocztku dziaania. W zwizku z tym jej instrukcje
mog nie zosta wykonane ani razu. Aby mie pewno, e instrukcje zostan wykonane co
najmniej raz, sprawdzanie warunku trzeba przenie na sam koniec. Do tego suy ptla
do-while. Jej skadnia jest nastpujca:
do instrukcja while (warunek)

Ta instrukcja najpierw wykonuje instrukcj (ktra zazwyczaj jest blokiem instrukcji), a dopiero
potem sprawdza warunek. Nastpnie znowu wykonuje instrukcj i sprawdza warunek itd.
Kod na listingu 3.4 oblicza nowe saldo na koncie emerytalnym, a nastpnie pyta, czy jestemy gotowi przej na emerytur:
do
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
year++;
// Drukowanie aktualnego stanu konta.
. . .

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

103

Rysunek 3.10.
Diagram
przepywu
sterowania
instrukcji while

// Zapytanie o gotowo do przejcia na emerytur i pobranie danych.


. . .
}
while (input.equals("N"));

Ptla jest powtarzana, dopki uytkownik podaje odpowied N (zobacz rysunek 3.11). Ten program jest dobrym przykadem ptli, ktra musi by wykonana co najmniej jeden raz, poniewa
uytkownik musi zobaczy stan konta, zanim podejmie decyzj o przejciu na emerytur.
Listing 3.3. Retirement.java
import java.util.*;
/**
* Ten program demonstruje sposb uycia ptli <code>while</code>.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class Retirement
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

104

Java. Podstawy

Rysunek 3.11.
Diagram
przepywu
sterowania
instrukcji do-while

public static void main(String[] args)


{
// Wczytanie danych.
Scanner in = new Scanner(System.in);
System.out.print("Ile pienidzy potrzebujesz, aby przej na emerytur? ");
double goal = in.nextDouble();
System.out.print("Ile pienidzy rocznie bdziesz wpaca? ");
double payment = in.nextDouble();
System.out.print("Stopa procentowa w %: ");
double interestRate = in.nextDouble();
double balance = 0;
int years = 0;
// Aktualizacja salda konta, jeli cel nie zosta osignity.
while (balance < goal)
{
// Dodanie tegorocznych patnoci i odsetek.
balance += payment;
double interest = balance * interestRate / 100;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

balance += interest;
years++;
}
System.out.println("Moesz przej na emerytur za " + years + " lat.");
}
}

Listing 3.4. Retirement2.java


import java.util.*;
/**
* Ten program demonstruje uycie ptli <code>do/while</code>.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class Retirement2
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Ile pienidzy rocznie bdziesz wpaca? ");
double payment = in.nextDouble();
System.out.print("Stopa oprocentowania w %: ");
double interestRate = in.nextDouble();
double balance = 0;
int year = 0;
String input;
// Aktualizacja stanu konta, kiedy uytkownik nie jest gotowy do przejcia na emerytur.
do
{
// Dodanie tegorocznych patnoci i odsetek.
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
year++;
// Drukowanie aktualnego stanu konta.
System.out.printf("Po upywie %d lat stan twojego konta wyniesie %,.2f%n",
year, balance);
// Zapytanie o gotowo do przejcia na emerytur i pobranie danych.
System.out.print("Chcesz przej na emerytur? (T/N) ");
input = in.next();
}
while (input.equals("N"));
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

105

106

Java. Podstawy

3.8.4. Ptle o okrelonej liczbie powtrze


Liczba iteracji instrukcji for jest kontrolowana za pomoc licznika lub jakiej innej zmiennej, ktrej warto zmienia si po kadym powtrzeniu. Z rysunku 3.12 wynika, e ponisza ptla drukuje na ekranie liczby od 1 do 10.
for (int i = 1; i <= 10; i++)
System.out.println(i);

Rysunek 3.12.
Diagram
przepywu
sterowania
ptli for

Na pierwszym miejscu z reguy znajduje si inicjacja licznika. Drugie miejsce zajmuje warunek, ktry jest sprawdzany przed kadym powtrzeniem instrukcji ptli. Na trzeciej pozycji
umieszczamy informacj na temat sposobu zmiany wartoci licznika.
Mimo i w Javie, podobnie jak w C++, w rnych miejscach ptli for mona wstawi prawie
kade wyraenie, niepisana zasada gosi, e do dobrego stylu naley, aby w tych miejscach
inicjowa, sprawdza i zmienia warto jednej zmiennej. Nie stosujc si do tej reguy,
mona napisa bardzo zagmatwane ptle.
Jednak nawet w granicach dobrego stylu programowania mona sobie pozwoli na wiele.
Mona na przykad utworzy ptl zmniejszajc licznik:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

107

for (int i = 10; i > 0; i--)


System.out.println("Odliczanie . . . " + i);
System.out.println("Start!");

Naley zachowa szczegln ostrono przy porwnywaniu w ptli liczb zmiennoprzecinkowych. Ptla for w takiej postaci:
for (double x = 0; x != 10; x += 0.1) . . .

moe si nigdy nie skoczy. Warto kocowa nie zostanie osignita ze wzgldu na
bd zwizany z zaokrglaniem. Na przykad w powyszej ptli warto x przeskoczy
z wartoci 9.99999999999998 na 10.09999999999998, poniewa liczba 0,1 nie ma dokadnej reprezentacji binarnej.

Zmienna zadeklarowana na pierwszej pozycji w ptli for ma zasig do koca ciaa tej ptli.
for (int i = 1; i <= 10; i++)
{
. . .
}
// Tutaj zmienna i ju nie jest dostpna.

Innymi sowy, warto zmiennej zadeklarowanej w wyraeniu ptli for nie jest dostpna
poza t ptl. W zwizku z tym, aby mc uy wartoci licznika ptli poza t ptl, trzeba
go zadeklarowa poza jej nagwkiem!
int i;
for (i = 1; i <= 10; i++)
{
. . .
}
// Zmienna i tutaj te jest dostpna.

Z drugiej jednak strony w kilku ptlach for mona zdefiniowa zmienn o takiej samej nazwie:
for (int i = 1; i <= 10; i++)
{
. . .
}
. . .
for (int i = 11; i <= 20; i++)
i.
{
. . .
}

// W tym miejscu dozwolona jest ponowna deklaracja zmiennej

Ptla for jest krtszym sposobem zapisu ptli while. Na przykad:


for (int i = 10; i > 0; i--)
System.out.println("Odliczanie... " + i);

mona zapisa nastpujco:


int i = 10;
while (i > 0)
{
System.out.println("Odliczanie... " + i);
i--;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

108

Java. Podstawy
Listing 3.5 przedstawia typowy przykad zastosowania ptli for.
Ten program oblicza szanse wygrania na loterii. Jeli na przykad loteria polega na wybraniu szeciu liczb z przedziau 1 50, to istnieje (50*49*48*47*46*45)/(1*2*3*4*5*6) moliwych kombinacji, co oznacza, e nasze szanse s jak 1 do 15 890 700. Powodzenia!
W oglnym przypadku losowania k liczb ze zbioru n istnieje:
n (n 1) (n 2) ... (n k 1)
1 2 3 ... k

moliwych wynikw. Ponisza ptla for oblicza t warto:


int lotteryOdds = 1;
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds * (n - i + 1) / i;

W sekcji 3.10.1 znajduje si opis uoglnionej ptli for (zwanej take ptl typu
for each), ktra zostaa dodana w wersji Java SE 5.
Listing 3.5. LotteryOdds.java
import java.util.*;
/**
* Ten program demonstruje zastosowanie ptli <code>for</code>.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class LotteryOdds
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Ile liczb ma by wylosowanych? ");
int k = in.nextInt();
System.out.print("Jaka jest grna granica przedziau losowanych liczb? ");
int n = in.nextInt();
/*
* Obliczanie wspczynnika dwumianowego n*(n1)*(n2)**(nk+1)/(1*2*3**k)
*/
int lotteryOdds = 1;
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds * (n - i + 1) / i;
System.out.println("Twoje szanse to 1 do " + lotteryOdds + ". Powodzenia!");
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

109

3.8.5. Wybr wielokierunkowy instrukcja switch


W sytuacjach gdy jest duo opcji do wyboru, instrukcja warunkowa if-else moe by mao
efektywna. Dlatego w Javie udostpniono instrukcj switch, ktra niczym nie rni si od
swojego pierwowzoru w jzykach C i C++.
Na przykad do utworzenia systemu menu zawierajcego cztery opcje, jak ten na rysunku 3.13,
mona uy kodu podobnego do tego poniej:
Scanner in = new Scanner(System.in);
System.out.print("Wybierz opcj (1, 2, 3, 4) ");
int choice = in.nextInt();
switch (choice)
{
case 1:
. . .
break;
case 2:
. . .
break;
case 3:
. . .
break;
case 4:
. . .
break;
default:
// Nieprawidowe dane.
. . .
break;
}

Wykonywanie programu zaczyna si od etykiety case, ktra pasuje do wybranej opcji, i jest
kontynuowane do napotkania instrukcji break lub koca instrukcji switch. Jeli adna z etykiet nie zostanie dopasowana, nastpi wykonanie czci oznaczonej przez etykiet default
jeli taka istnieje.
Etykiety case mog by:

wyraeniami staymi typu char, byte, short lub int (oraz odpowiednich klas
opakowujcych: Character, Byte, Short i Integer ich opis znajduje si
w rozdziale 4.);

staymi wyliczeniowymi;

acuchami od Java SE 7.

Na przykad:
String input = . . .;
switch (input.toLowerCase())
{
case "tak": // OK od Java SE 7

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

110

Java. Podstawy

Rysunek 3.13.
Diagram
przepywu
sterowania
instrukcji switch

. . .
break;
. . .

Uywajc instrukcji switch ze staymi wyliczeniowymi, nie ma koniecznoci podawania


nazwy wyliczenia w kadej etykiecie jest ona pobierana domylnie z wartoci switch.
Na przykad:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

111

Istnieje ryzyko, e zostanie uruchomionych kilka opcji. Jeli przez przypadek na


kocu jednej z opcji nie znajdzie si instrukcja break, sterowanie zostanie przekazane do kolejnej etykiety case! Taki sposb dziaania jest niebezpieczny i czsto prowadzi
do bdw. Z tego powodu nigdy nie uywamy instrukcji case w swoich programach.
Jeli jednak czujesz do instrukcji switch wiksz sympati ni my, moesz kompilowa swoje programy z uyciem opcji -Xlint:fallthrough:
javac -Xlint:fallthrough Test.java

Dziki temu ustawieniu kompilator bdzie zgasza wszystkie przypadki alternatyw niezawierajcych na kocu instrukcji break.
Gdy bdziesz chcia wykona bloki case po kolei, oznacz otaczajc je metod adnotacj @SuppressWarnings("fallthrough"). Dziki temu dla tej metody nie bd zgaszane ostrzeenia. (Adnotacje to technika przekazywania informacji do kompilatora lub
innego narzdzia przetwarzajcego kod rdowy Java lub pliki klas. Ich szczegowy
opis znajduje si w rozdziale 13. drugiego tomu).
Size sz = . . .;
switch (sz)
{
case SMALL:
// Nie trzeba byo pisa Size.SMALL.
. . .
break;
. . .
}

3.8.6. Instrukcje przerywajce przepyw sterowania


Mimo e projektanci jzyka Java zarezerwowali sowo goto, nie zdecydowali si wcieli go
do jzyka. Instrukcje goto s uwaane za wyznacznik sabego stylu programowania. Zdaniem niektrych programistw kampania skierowana przeciwko instrukcji goto jest przesadzona (zobacz synny artyku Donalda E. Knutha pod tytuem Structured Programming with
goto statements). Ich zdaniem stosowanie instrukcji goto bez ogranicze moe prowadzi
do wielu bdw, ale uycie jej od czasu do czasu w celu wyjcia z ptli moe by korzystne.
Projektanci Javy przychylili si do tego stanowiska i dodali now instrukcj break z etykiet.
Przyjrzyjmy si najpierw instrukcji break bez etykiety. Tej samej instrukcji break, za pomoc
ktrej wychodzi si z instrukcji switch, mona uy do przerwania dziaania ptli. Na przykad:
while (years <= 100)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
if (balance >= goal) break;
years++;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

112

Java. Podstawy
Wyjcie z ptli nastpi, kiedy warto znajdujcej si na samej grze ptli zmiennej years
przekroczy 100 albo znajdujca si w rodku zmienna balance bdzie miaa warto wiksz lub rwn goal. Oczywicie t sam warto zmiennej years mona by byo obliczy
bez uycia instrukcji break:
while (years <= 100 && balance < goal)
{
balance += payment;
double interest = balance * interestRate / 100;
balance += interest;
if (balance < goal)
years++;
}

Naley jednak zauway, e wyraenie sprawdzajce balance < goal jest w tej wersji uyte
dwukrotnie. Aby unikn tego powtrzenia, niektrzy programici stosuj instrukcj break.
W Javie dostpna jest te instrukcja break z etykiet (brak jej natomiast w jzyku C++),
ktra umoliwia wyjcie z kilku zagniedonych ptli. Czasami w gboko zagniedonych
ptlach dziej si dziwne rzeczy. W takiej sytuacji najlepiej jest wyj cakiem na zewntrz.
Zaprogramowanie takiego dziaania za pomoc dodatkowych warunkw w rnych testach
ptli jest rozwizaniem mao wygodnym.
Poniej znajduje si przykadowy kod prezentujcy dziaanie instrukcji break. Naley zauway, e etykieta musi si znajdowa przed najbardziej zewntrzn ptl, z ktrej chcemy wyj.
Ponadto po etykiecie w tym miejscu musi si znajdowa dwukropek.
Scanner in = new Scanner(System.in);
int n;
read_data:
while (. . .)
// Ta ptla jest opatrzona etykiet.
{
. . .
for (. . .)
// Ta zagniedona ptla nie ma etykiety.
{
System.out.print("Podaj liczb >= 0: ");
n = in.nextInt();
if (n < 0)
// To nie powinno mie miejsca nie mona kontynuowa.
break read_data;
// Wyjcie z ptli z etykiet read_data.
. . .
}
}
// Ta instrukcja jest wykonywana bezporednio po przerwaniu ptli.
if (n < 0)
// Sprawdzenie, czy ma miejsce niepodana sytuacja.
{
// Obsuga niechcianej sytuacji.
}
else
{
// Wykonywanie w normalnym toku.
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

113

Jeli zostan podane nieprawidowe dane, instrukcja break z etykiet przeniesie sterowanie
do miejsca bezporednio za blokiem opatrzonym t etykiet. Nastpnie, tak jak w kadym
przypadku uycia instrukcji break, trzeba sprawdzi, czy wyjcie z ptli nastpio w toku
normalnego dziaania, czy zostao spowodowane przez instrukcj break.
Co ciekawe, etykiet mona doda do kadej instrukcji, nawet instrukcji warunkowej if i instrukcji blokowej:
etykieta:
{
. . .
if (warunek) break etykieta;
// Wychodzi z bloku.
. . .
}
// Przechodzi do tego miejsca, jeli zostanie wykonana instrukcja break.

W zwizku z tym, jeli tsknisz za instrukcj goto i moesz umieci blok bezporednio przed miejscem, do ktrego ma nastpi przejcie, moesz uy instrukcji break!
Oczywicie nie polecamy tej metody programowania. Zauwa te, e przejcie jest
moliwe tylko w jedn stron nie mona wskoczy do bloku.

Na zakoczenie zostaa jeszcze instrukcja continue, ktra podobnie jak instrukcja break
zmienia normalny przepyw sterowania. Instrukcja continue przenosi sterowanie do nagwka
najgbiej zagniedonej ptli. Na przykad:
Scanner in = new Scanner(System.in);
while (sum < goal)
{
System.out.print("Podaj liczb: ");
n = in.nextInt();
if (n < 0) continue;
sum += n;
// Wyraenie nie zostanie wykonane, jeli n < 0.
}

Jeli warto zmiennej n jest mniejsza od 0, instrukcja continue powoduje natychmiastowe


przejcie do nagwka ptli, nie dopuszczajc do wykonania reszty instrukcji w biecej
iteracji.
Instrukcja continue uyta w ptli for powoduje przejcie do czci aktualizujcej warto
zmiennej w nagwku tej ptli. Przyjrzyjmy si nastpujcemu przykadowi:
for (count = 1; count <= 100; count++)
{
System.out.print("Podaj liczb (-1 koczy dziaanie programu): ");
n = in.nextInt();
if (n < 0) continue;
sum += n;
// Wyraenie nie zostanie wykonane, jeli n < 0.
}

Jeli n < 0, instrukcja continue powoduje przekazanie sterowania do instrukcji i++.


Istnieje take wersja instrukcji continue z etykiet, ktra powoduje przejcie do nagwka ptli
z odpowiedni etykiet.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

114

Java. Podstawy

Wielu programistw myli instrukcje break i continue. Ich stosowanie nie jest
obowizkowe i to, co mona osign przy ich uyciu, da si zawsze uzyska w inny
sposb. W tej ksice nigdy nie uywamy instrukcji break i continue.

3.9. Wielkie liczby


Jeli precyzja podstawowych typw cakowitoliczbowych i zmiennoprzecinkowych okae si
niezadowalajca, mona zrobi uytek z klas dostpnych w pakietach java.math: BigInteger
i BigDecimal. Klasy te umoliwiaj dziaania na liczbach skadajcych si z dowolnej liczby
cyfr. Klasa BigInteger umoliwia wykonywanie dziaa arytmetycznych o dowolnej precyzji na liczbach cakowitych, a klasa BigDecimal jest jej odpowiednikiem dla liczb zmiennoprzecinkowych.
Do konwersji zwykych liczb na wielkie suy statyczna metoda valueOf:
BigInteger a = BigInteger.valueOf(100);

Niestety w dziaaniach na wielkich liczbach nie mona uywa dobrze nam znanych operatorw arytmetycznych, jak + czy *. Zamiast nich trzeba uywa odpowiednich metod, jak add
i multiply, dostpnych w klasach wielkich liczb:
BigInteger c = a.add(b);
BigInteger d = c.multiply(b.add(BigInteger.valueOf(2)));

// c = a + b
// d = c * (b + 2)

W przeciwiestwie do jzyka C++, Java nie umoliwia przeciania operatorw.


Nie da si z punktu widzenia programisty przeciy operatorw + i *, aby wykonyway dziaania waciwe metodom add i multiply dostpnym w klasie BigInteger.
Projektanci Javy przeciyli operator +, dziki czemu mona czy acuchy. Nie zdecydowali si jednak na przecienie pozostaych operatorw ani nie pozostawili takiej moliwoci programistom.

Listing 3.6 przedstawia zmodyfikowan wersj programu loteryjnego z listingu 3.5. W tej
wersji dziaa ona take po podaniu bardzo duych liczb. Jeli na przykad loteria polega na
wyborze 60 liczb ze zbioru 1 490, program ten poinformuje nas, e nasze szanse wynosz
1 do 716 395 843 461 995 557 415 116 222 540 092 933 411 717 612 789 263 493 493 351
013 459 481 104 668 848. Powodzenia!
Program z listingu 3.5 oblicza warto nastpujcego wyraenia:
lotteryOdds = lotteryOdds * (n - i + 1) / i;

Przy uyciu wielkich liczb odpowiednikiem tej instrukcji jest ponisza instrukcja:
lotteryOdds = lotteryOdds.multiply(BigInteger.valueOf(n - i + 1)).divide(BigInteger.
valueOf(i));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

Listing 3.6. BigIntegerTest.java


import java.math.*;
import java.util.*;
/**
* Ten program wykorzystuje wielkie liczby do obliczenia szans wygrania na loterii.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class BigIntegerTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Ile liczb ma by wylosowanych? ");
int k = in.nextInt();
System.out.print("Jaka jest grna granica przedziau losowanych liczb? ");
int n = in.nextInt();
/*
* Obliczanie wspczynnika dwumianowego n*(n1)*(n2)**(nk+1)/(1*2*3**k)
*/
BigInteger lotteryOdds = BigInteger.valueOf(1);
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds.multiply(BigInteger.valueOf(n - i + 1)).divide(
BigInteger.valueOf(i));

System.out.println("Twoje szanse to 1 do " + lotteryOdds + ". Powodzenia!");

java.math.BigInteger 1.1

BigInteger add(BigInteger other)

BigInteger subtract(BigInteger other)

BigInteger multiply(BigInteger other)

BigInteger divide(BigInteger other)

BigInteger mod(BigInteger other)

Zwraca sum, rnic, iloczyn, iloraz i reszt liczb BigInteger i other.

int compareTo(BigInteger other)

Zwraca warto 0, jeli liczba BigInteger jest rwna liczbie other, warto
ujemn, jeli liczba BigInteger jest mniejsza od liczby other, lub liczb dodatni
w przeciwnym przypadku.

static BigInteger valueOf(long x)

Zwraca wielk liczb o wartoci x.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

115

116

Java. Podstawy
java.math.BigDecimal 1.1

BigDecimal add(BigDecimal other)

BigDecimal subtract(BigDecimal other)

BigDecimal multiply(BigDecimal other)

BigDecimal divide(BigDecimal other, RoundingMode mode) 5.0

Zwraca sum, rnic, iloczyn, iloraz i reszt liczb BigDecimal i other. Obliczenie
ilorazu jest moliwe tylko po podaniu sposobu zaokrglania. Na przykad tryb
RoundingMode.HALF_UP jest znany nam wszystkim ze szkoy (cyfry od 0 do 4
zaokrglamy w d, a od 5 do 9 w gr). Ten sposb zaokrglania jest odpowiedni
do typowych oblicze. Opis pozostaych trybw zaokrglania znajduje si
w dokumentacji API.

int compareTo(BigDecimal other)

Zwraca warto 0, jeli liczba BigDecimal jest rwna liczbie other, warto
ujemn, jeli liczba BigDecimal jest mniejsza od liczby other, lub liczb dodatni
w przeciwnym przypadku.

static BigDecimal valueOf(long x)

static BigDecimal valueOf(long x, int scale)

Zwraca wielk liczb, ktrej warto jest rwna x lub x/10scale.

3.10. Tablice
Tablica jest rodzajem struktury danych bdc zestawem elementw tego samego typu. Dostp
do kadego z tych elementw mona uzyska za pomoc jego indeksu w postaci liczby typu
int. Jeli na przykad a jest tablic liczb cakowitych, to a[i] jest i-tym elementem tej tablicy.
Deklaracja zmiennej tablicowej polega na okreleniu typu tablicy (czyli podaniu typu elementw i nawiasw kwadratowych []) i nazwy zmiennej. Poniej znajduje si przykadowa deklaracja tablicy zdolnej do przechowywania liczb cakowitych:
int[] a;

Powysza instrukcja tylko deklaruje zmienn a. Nie inicjuje jej jednak tablic. Do utworzenia tablicy potrzebny jest operator new.
int[] a = new int[100];

Powysza instrukcja tworzy i inicjuje tablic, w ktrej mona zapisa 100 liczb cakowitych.
Dugo tablicy nie musi by staa, np. instrukcja new int[n] tworzy tablic o dugoci n.
Elementy tablicy s numerowane od 0 (tu od 0 do 99). Tablic mona zapeni wartociami na przykad za pomoc ptli:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

117

int[] a = new int[100];


for (int i = 0; i < 100; i++)
a[i] = i;
// Zapenia tablic wartociami od 0 do 99.

Zmienn tablicow mona zdefiniowa na dwa sposoby:


lub

int[] a;
int a[];

Wikszo programistw stosuje ten pierwszy styl ze wzgldu na eleganckie oddzielenie


typu int[] (w przypadku tablicy liczb cakowitych) od nazwy zmiennej.

W nowych tablicach liczb wszystkie elementy s inicjowane zerami. W tablicach wartoci


logicznych elementom przypisywana jest warto false, a w tablicach na obiekty elementom nadawana jest specjalna warto null, oznaczajca, e nie zawieraj one jeszcze adnych obiektw. Moe to by zaskakujce dla pocztkujcych programistw, np.:
String[] names = new String[10];

Powysza instrukcja tworzy tablic dziesiciu acuchw, z ktrych kady jest null. Jeli
w tablicy maj by zapisane puste acuchy, naley je do niej przekaza:
for (int i = 0; i < 10; i++) names[i] = "";

Prba dostpu do elementu o indeksie 100 (lub jakimkolwiek innym wikszym


od 99) w tablicy zawierajcej 100 elementw zakoczy si spowodowaniem wyjtku
ArrayIndexOutOfBounds (indeks spoza przedziau tablicy).

Informacj o liczbie elementw przechowywanych w tablicy mona uzyska za pomoc


odwoania nazwaTablicy.length. Na przykad:
for (int i = 0; i < a.length; i++)
System.out.println(a[i]);

Rozmiaru tablicy nie mona zmieni (ale mona oczywicie zmienia jej poszczeglne elementy). Jeli konieczne s czste zmiany rozmiaru tablicy w trakcie dziaania programu, naley
uy listy ArrayList (wicej informacji na ten temat znajduje si w rozdziale 5.).

3.10.1. Ptla typu for each


W jzyku Java dostpny jest bardzo uyteczny rodzaj ptli umoliwiajcej przegldanie tablic
(jak rwnie innych rodzajw kolekcji) bez stosowania indeksw.
Ponisza udoskonalona ptla for:
for (zmienna : kolekcja) instrukcja

ustawia podan zmienn na kady element kolekcji i wykonuje instrukcj (ktra oczywicie
moe by blokiem instrukcji). Kolekcja musi by tablic lub obiektem klasy implementujcej
interfejs Iterable, jak np. ArrayList. Listy ArrayList omawiamy w rozdziale 5., a interfejs
Iterable w drugim rozdziale drugiego tomu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

118

Java. Podstawy
Na przykad ponisza ptla:
for (int element : a)
System.out.println(element);

drukuje kady element tablicy a w oddzielnym wierszu.


Ptl t naley czyta nastpujco: Dla kadego elementu w a. Projektanci rozwaali
dodanie do Javy sw kluczowych, jak foreach (dla kadego) i in (w), ale spowodowaoby
to uszkodzenie ju napisanego kodu zawierajcego metody lub zmienne o takich wanie
nazwach (np. System.in).
Ten sam efekt mona oczywicie uzyska za pomoc typowej ptli for:
for (int i = 0; i < a.length; i++)
System.out.println(a[i]);

Ptla typu for each jest jednak bardziej zwiza i mniej podatna na bdy (brak indeksw
pocztkowego i kocowego).
Zmienna ptlowa ptli typu for each przemierza elementy tablicy, nie wartoci
indeksw.

Ptla typu for each jest bardzo miym udoskonaleniem jzyka w stosunku do tradycyjnej
formy, jeli chcemy przetworzy wszystkie elementy tablicy. Nadal jednak ptla for znajduje wiele zastosowa, na przykad kiedy nie chcemy przemierza caej kolekcji danych lub
musimy uy indeksu w ptli.
Istnieje jeszcze prostsza metoda na wydrukowanie wszystkich elementw tablicy.
Polega na uyciu metody toString klasy Arrays. Odwoanie Arrays.toString(a)
zwrci acuch skadajcy si ze wszystkich elementw tablicy ujtych w nawiasy kwadratowe i rozdzielonych przecinkami, np. [2, 3, 5, 7, 11, 13]. Ponisze wywoanie
drukuje zawarto tej tablicy:
System.out.println(Arrays.toString(a));

3.10.2. Inicjowanie tablic i tworzenie tablic anonimowych


Java umoliwia zastosowanie skrconego zapisu pozwalajcego na jednoczesne utworzenie
tablicy i zainicjowanie jej wartociami pocztkowymi. Oto przykad tej skadni:
int[] smallPrimes = { 2, 3, 5, 7, 11, 13 };

Naley zauway, e w przypadku zastosowania tej skadni nie uywa si operatora new.
Mona nawet zainicjowa tablic anonimow:
new int[] { 17, 19, 23, 29, 31, 37 }

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

119

Powysze wyraenie przydziela pami dla nowej tablicy i zapenia j wartociami podanymi midzy klamrami. Sprawdza liczb pocztkowych wartoci i odpowiednio ustawia
rozmiar tworzonej tablicy. Za pomoc tej metody mona ponownie zainicjowa tablic, nie
tworzc przy tym nowej zmiennej. Na przykad zapis:
smallPrimes = new int[] { 17, 19, 23, 29, 31, 37 };

jest skrcon wersj zapisu:


int[] anonymous = { 17, 19, 23, 29, 31, 37 };
smallPrimes = anonymous;

Mona tworzy tablice o rozmiarze 0. Taka tablica moe si okaza przydatna,


kiedy napiszemy metod zwracajc tablic, ktrej wynik jest pusty. Konstrukcja
tablicy o rozmiarze 0 wyglda nastpujco:
new typElementu[0]

Zwrmy uwag, e tablica o rozmiarze 0 nie jest tym samym co null.

3.10.3. Kopiowanie tablicy


Jedn zmienn tablicow mona skopiowa do drugiej, ale w takim przypadku obie zmienne
wskazuj na t sam tablic:
int[] luckyNumbers = smallPrimes;
luckyNumbers[5] = 12;
// Teraz element smallPrimes[5] ma warto 12.

Wynik przedstawia rysunek 3.14. Aby rzeczywicie skopiowa wszystkie elementy jednej
tablicy do innej, naley uy metody copyTo dostpnej w klasie Arrays:
int[] copiedLuckyNumbers = Arrays.copyOf(luckyNumbers, luckyNumbers.length);

Rysunek 3.14.
Kopiowanie
zmiennej
tablicowej

Drugi parametr okrela rozmiar nowej tablicy. Metoda ta jest czsto wykorzystywana do zwikszania rozmiaru tablicy:
luckyNumbers = Arrays.copyOf(luckyNumbers, 2 * luckyNumbers.length);

Dodatkowe elementy s zapeniane zerami, jeli tablica przechowuje liczby, lub wartociami
false, jeli przechowywane s wartoci logiczne. Jeli rozmiar nowej tablicy jest mniejszy
ni pierwotny, kopiowane s elementy z pocztku tablicy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

120

Java. Podstawy

Tablice w Javie nie s tym samym co tablice w C++ na stosie (ang. stack). S
natomiast w zasadzie odpowiednikiem wskanikw do tablic alokowanych na stercie
(ang. heap). To znaczy:
int[] a = new int[100];

// Java

to nie to samo co:


int a[100];

// C++

ale to samo co:


int* a = new int[100];

// C++

W Javie operator [] zajmuje si sprawdzaniem zakresu. Ponadto nie mona wykonywa


dziaa arytmetycznych na wskanikach nie mona inkrementowa zmiennej a, aby
wskazywaa na kolejny element tablicy.

3.10.4. Parametry wiersza polece


Do tej pory widzielimy jeden przykad tablicy w Javie, ktry zosta kilkakrotnie powtrzony.
Kady program w Javie skada si z metody main z parametrem String[] args. Oznacza to, e
metoda main przyjmuje tablic acuchw, czyli argumenty podawane w wierszu polece.
Przyjrzyjmy si poniszemu programowi:
public class Message
{
public static void main(String[] args)
{
if (args[0].equals("-h"))
System.out.print("Witaj, ");
else if (args[0].equals("-g"))
System.out.print("egnaj, ");
// Wydruk pozostaych argumentw wiersza polece.
for (int i = 1; i < args.length; i++)
System.out.print(" " + args[i]);
System.out.println("!");
}
}

Jeli program ten uruchomimy w nastpujcy sposb:


java Message -g okrutny wiecie

tablica args bdzie miaa nastpujc zawarto:


args[0]: "-g"
args[1]: "okrutny"
args[2]: "wiecie"

Program wydrukuje wiadomo:


egnaj, okrutny wiecie!

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

121

W Javie nazwa programu nie jest przechowywana w tablicy args w metodzie main.
Jeli na przykad program zostanie uruchomiony nastpujco:
java Message -h wiecie

element args[0] bdzie zawiera warto parametru "-h", a nie acuch "Message"
czy "java".

3.10.5. Sortowanie tablicy


Do sortowania tablic przechowujcych liczby su metody sort dostpne w klasie Arrays:
int[] a = new int[10000];
. . .
Arrays.sort(a)

Ta metoda korzysta ze zoptymalizowanej wersji algorytmu QuickSort, ktry ma opini bardzo


efektywnego w sortowaniu wikszoci zbiorw danych. W klasie Arrays dostpnych jest
kilka innych metod usprawniajcych prac z tablicami. Opisano je w uwadze o API na kocu
tego podrozdziau.
Program widoczny na listingu 3.7 stanowi przykad praktycznego zastosowania tablic. Jego
dziaanie polega na losowaniu kilku liczb na loterii. Jeli na przykad zagramy w wybr
szeciu liczb z 49, wynik moe by nastpujcy:
Postaw na nastpujce liczby. Dziki nim zdobdziesz bogactwo!
1
10
23
29
31
34

Najpierw zapeniamy tablic numbers liczbami 1, 2, 3, n.


int[] numbers = new int[n];
for (int i = 0; i < numbers.length; i++)
numbers[i] = i + 1;

Listing 3.7. LotteryDrawing.java


import java.util.*;
/**
* Ten program demonstruje zastosowanie tablic.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class LotteryDrawing
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

122

Java. Podstawy
System.out.print("Ile liczb musisz wylosowa? ");
int k = in.nextInt();
System.out.print("Jaka jest najwiksza liczba? ");
int n = in.nextInt();
// Zapenienie tablicy liczbami 1 2 3 . . . n.
int[] numbers = new int[n];
for (int i = 0; i < numbers.length; i++)
numbers[i] = i + 1;
// Losowanie k liczb i zapisanie ich w drugiej tablicy.
int[] result = new int[k];
for (int i = 0; i < result.length; i++)
{
// Losowanie indeksu z zakresu od 0 do n1.
int r = (int) (Math.random() * n);
// Pobranie elementu z losowej lokalizacji.
result[i] = numbers[r];

// Przeniesienie ostatniego elementu do losowej lokalizacji.


numbers[r] = numbers[n - 1];
n--;

// Wydruk zapisanej tablicy.


Arrays.sort(result);
System.out.println("Postaw na nastpujce liczby. Dziki nim zdobdziesz
bogactwo!");
for (int r : result)
System.out.println(r);

Druga tablica przechowuje liczby do wylosowania:


int[] result = new int[k];

Nastpnie losujemy k liczb. Metoda Math.random zwraca losow liczb zmiennoprzecinkow


z zamknitego przedziau 0-1. Dziki pomnoeniu jej wyniku przez n uzyskujemy losow
liczb z przedziau od 0 do n1.
int r = (int) (Math.random() * n);

i-ty wynik bdzie liczb przechowywan w indeksie i. Pocztkowo jest to i+1, ale niebawem si przekonamy, e zawarto tablicy numbers zmienia si po kadym losowaniu.
result[i] = numbers[r];

Trzeba si zabezpieczy, aby nie wylosowa tej samej liczby ponownie wszystkie liczby
na loterii musz by inne. W tym celu zastpujemy element numbers[r] ostatni liczb
w tablicy i zmniejszamy n o 1.
numbers[r] = numbers[n - 1];
n--;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

123

Naszym celem jest to, aby za kadym razem by losowany indeks, a nie rzeczywiste wartoci. Indeks ten wskazuje na element tablicy zawierajcej wartoci, ktre nie zostay jeszcze
wylosowane.
Po wylosowaniu k liczb sortujemy zawarto tablicy result:
Arrays.sort(result);
for (int r : result)
System.out.println(r);
java.util.Arrays 1.2

static String toString(typ[] a) 5.0

Zwraca acuch zoony z elementw tablicy a ujtych w nawiasy kwadratowe


i rozdzielonych przecinkami.
Parametry:

Tablica elementw typu int, long, short, char,


byte, boolean, float lub double.

static typ[] copyOf(typ[] a, int length) 6

static typ[] copyOf(typ[] a, int start, int end) 6

Zwraca tablic tego samego typu co tablica a, majc rozmiar length albo end-start
i zapenion wartociami z tablicy a.
Parametry:

Tablica elementw typu int, long, short, char,


byte, boolean, float lub double.

start

Indeks pocztkowy (wcznie).

end

Indeks kocowy (wycznie). Moe by wikszy


od a.length w takim przypadku puste miejsca
s zapeniane zerami lub wartociami false.

length

Rozmiar kopii. Jeli length jest wiksza od a.length,


puste miejsca s zapeniane zerami lub wartociami
false. W przeciwnym przypadku kopiowanych
jest length wartoci pocztkowych.

static void sort(typ[] a)

Sortuje tablic przy uyciu zoptymalizowanego algorytmu QuickSort.


Parametry:

Tablica elementw typu int, long, short, char,


byte, boolean, float lub double.

static int binarySearch(typ[] a, typ v)

static int binarySearch(typ[] a, int start, int end typ v) 6

Wyszukuje warto v przy uyciu algorytmu wyszukiwania binarnego. W przypadku


powodzenia zwraca indeks znalezionej wartoci. W przeciwnym razie zwraca
ujemn warto r. -r - 1 to miejsce, w ktrym naley wstawi warto v, aby tablica
a pozostaa posortowana.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

124

Java. Podstawy
Parametry:

Tablica elementw typu int, long, short, char,


byte, boolean, float lub double.

start

Indeks pocztkowy (wcznie).

end

Indeks kocowy (wycznie).

Warto tego samego typu co elementy tablicy a.

static void fill(typ[] a, typ v)

Ustawia wszystkie elementy tablicy na warto v.


Parametry:

Tablica elementw typu int, long, short, char, byte,


boolean, float lub double.

Warto tego samego typu co elementy tablicy a.

static boolean equals(typ[] a, typ[] b)

Zwraca warto true, jeli tablice maj takie same rozmiary i jeli wartoci
na odpowiadajcych sobie pozycjach pasuj do siebie.
Parametry:

a, b

Tablice elementw typu int, long, short, char,


byte, boolean, float lub double.

3.10.6. Tablice wielowymiarowe


Tablice wielowymiarowe su do reprezentacji tabel i innych bardziej zoonych struktur
danych. Aby uzyska dostp do elementu tablicy wielowymiarowej, naley uy wicej ni
jednego indeksu. Mona ten podrozdzia pomin i wrci do niego w razie potrzeby.
Przypumy, e chcemy utworzy tabel liczb pokazujc, jaki bdzie zwrot z inwestycji
10 000 z przy rnych stopach procentowych skadanych rocznie. Tabela 3.8 przedstawia
taki scenariusz.
Powysze informacje zapiszemy w tablicy dwuwymiarowej (czyli macierzy) o nazwie
balances.
Deklaracja tablicy dwuwymiarowej w Javie jest bardzo prosta. Wystarczy napisa:
double[][] balances;

Jak zwykle tablicy nie mona uywa, dopki si jej nie zainicjuje za pomoc wyraenia new.
W tym przypadku inicjacja moe wyglda nastpujco:
balances = new double[NYEARS][NRATES];

Jeli znane s elementy tablicy, mona uy skrconej notacji inicjacji tablicy wielowymiarowej, ktra nie wymaga wywoania new. Na przykad:
int[][] magicSquare =
{
{16, 3, 2, 13},
{5, 10, 11, 8},

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

125

Tabela 3.8. Wzrost dochodu z inwestycji przy rnych stopach oprocentowania


10%

11%

12%

13%

14%

15%

10 000,00

10 000,00

10 000,00

10 000,00

10 000,00

10 000,00

11 000,00

11 100,00

11 200,00

11 300,00

11 400,00

11 500,00

12 100,00

12 321,00

12 544,00

12 769,00

12 996,00

13 225,00

13 310,00

13 676,31

14 049,28

14 428,97

14 815,44

15 208,75

14 641,00

15 180,70

15 735,19

16 304,74

16 889,60

17 490,06

16 105,10

16 850,58

17 623,42

18 424,35

19 254,15

20 113,57

17 715,61

18 704,15

19 738,23

20 819,52

21 949,73

23 130,61

19 487,17

20 761,60

22 106,81

23 526,05

25 022,69

26 600,20

21 435,89

23 045,38

24 759,63

26 584,44

28 525,86

30 590,23

23 579,48

25 580,37

27 730,79

30 040,42

32 519,49

35 178,76

{9, 6, 7, 12},
{4, 15, 14, 1}
};

Dostp do elementw takiej tablicy uzyskujemy za pomoc dwch indeksw, np. balances
[i][j].
Przykadowy program zapisuje jednowymiarow tablic o nazwie interest zawierajc stopy
oprocentowania i dwuwymiarow tablic o nazwie balances zawierajc stany rodkw
dla kadego roku i kadej stopy procentowej. Pierwszy wiersz tablicy inicjujemy saldem
pocztkowym:
for (int j = 0; j < balance[0].length; j++)
balances[0][j] = 10000;

Nastpnie obliczamy wartoci w kolejnych wierszach:


for (int i = 1; i < balances.length; i++)
{
for (int j = 0; j < balances[i].length; j++)
{
double oldBalance = balances[i - 1][j];
double interest = . . .;
balances[i][j] = oldBalance + interest;
}
}

Listing 3.8 przedstawia peny kod tego programu.


Listing 3.8. CompoundInterest.java
/**
* Ten program demonstruje przechowywanie danych tabelarycznych w tablicy dwuwymiarowej.
* @version 1.40 2004-02-10
* @author Cay Horstmann
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

126

Java. Podstawy

Ptla typu for each nie sprawdza automatycznie wszystkich elementw tablicy
dwuwymiarowej. Przechodzi tylko przez wiersze, ktre s tablicami jednowymiarowymi. Aby dotrze do wszystkich elementw tablicy dwuwymiarowej a, naley zagniedzi jedn ptl w drugiej:
for (double[] row : a)
for (double value : row)

Dziaania na wartociach

Aby szybko wydrukowa list elementw tablicy dwuwymiarowej, naley wywoa:


System.out.println(Arrays.deepToString(a));

Wynik bdzie nastpujcy:


[[16, 3, 2, 13], [5, 10, 11, 8], [9, 6, 7, 12], [4, 15, 14, 1]]
public class CompoundInterest
{
public static void main(String[] args)
{
final double STARTRATE = 10;
final int NRATES = 6;
final int NYEARS = 10;
// Ustawienie stp oprocentowania na wartoci w przedziale 10 15%.
double[] interestRate = new double[NRATES];
for (int j = 0; j < interestRate.length; j++)
interestRate[j] = (STARTRATE + j) / 100.0;
double[][] balances = new double[NYEARS][NRATES];
// Ustawienie sald pocztkowych na 10 000.
for (int j = 0; j < balances[0].length; j++)
balances[0][j] = 10000;
// Obliczenie odsetek dla przyszych lat.
for (int i = 1; i < balances.length; i++)
{
for (int j = 0; j < balances[i].length; j++)
{
// Pobranie sald z minionego roku z poprzedniego wiersza.
double oldBalance = balances[i - 1][j];
// Obliczenie odsetek.
double interest = oldBalance * interestRate[j];
// Obliczenie tegorocznego salda.
balances[i][j] = oldBalance + interest;
}
}
// Wydruk jednego wiersza stp oprocentowania.
for (int j = 0; j < interestRate.length; j++)
System.out.printf("%9.0f%%", 100 * interestRate[j]);
System.out.println();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

127

// Wydruk tabeli sald.


for (double[] row : balances)
{
// Wydruk wiersza tabeli.
for (double b : row)
System.out.printf("%10.2f", b);
}

System.out.println();

}
}

3.10.7. Tablice postrzpione


Opisane do tej pory rodzaje tablic nie rni si niczym szczeglnym od tych, ktre znamy
z innych jzykw programowania. Jest jednak co, o czym warto wiedzie: w Javie nie ma
prawdziwych tablic wielowymiarowych. S one tylko symulowane przez tablice tablic.
Na przykad tablica balances utworzona w powyszym programie zawiera dziesi elementw, z ktrych kady jest tablic zawierajc sze liczb zmiennoprzecinkowych (zobacz
rysunek 3.15).

Rysunek 3.15. Tablica dwuwymiarowa

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

128

Java. Podstawy
Wyraenie balances[i] odwouje si do i-tej podtablicy, ktra jest i-tym wierszem tablicy.
Wiersz ten sam jest tablic, a wic balances[i][j] odwouje si do j-tego wiersza tej tablicy.
Jako e do poszczeglnych wierszy tablic mona uzyska dostp, mona zamienia je
miejscami!
double[] temp = balances[i];
balances[i] = balances[i + 1];
balances[i + 1] = temp;

Rwnie atwe jest tworzenie tablic postrzpionych (ang. ragged arrays), czyli takich, w ktrych wiersze maj rne dugoci. Oto typowy przykad. Utworzymy tablic, w ktrej element w i-tym wierszu i j-tej kolumnie jest rwny liczbie moliwych wynikw loterii polegajcej na losowaniu j liczb spord i liczb.
1
1
1
1
1
1
1

1
2
3
4
5
6

1
3 1
6 4 1
10 10 5 1
15 20 15 6 1

Jako e j nie moe by wiksze od i, powstaje macierz trjktna. Wiersz i-ty ma i+1 elementw (zezwalamy na niewybranie adnego elementu mona to zrobi tylko w jeden
sposb). Aby utworzy tak tablic postrzpion, naley najpierw alokowa w pamici tablic
przechowujc wiersze.
int[][] odds = new int[NMAX + 1][];

Nastpnie tworzymy wiersze:


for (int n = 0; n <= NMAX; n++)
odds[n] = new int[n + 1];

Po utworzeniu tablicy mona dziaa na jej elementach w normalny sposb, pod warunkiem
e nie przekroczymy zakresu.
for (int n = 0; n < odds.length; n++)
for (int k = 0; k < odds[n].length; k++)
{
// Obliczenie lotteryOdds
. . .
odds[n][k] = lotteryOdds;
}

Listing 3.9 przedstawia kompletny program.


Listing 3.9. LotteryArray.java
/**
* Ten program demonstruje sposb tworzenia tablicy trjktnej.
* @version 1.20 2004-02-10
* @author Cay Horstmann
*/
public class LotteryArray
{
public static void main(String[] args)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 3.

Podstawowe elementy jzyka Java

129

W C++ znana z Javy deklaracja:


double[][] balances = new double[10][6];

// Java

nie jest rwnowana z:


double balances[10][6];

// C++

ani nawet z:
double (*balances)[6] = new double[10][6];

// C++

Zamiast tego tworzona jest tablica 10 wskanikw:


double** balances = new double*[10];

// C++

Nastpnie do kadego elementu w tablicy wskanikw wstawiana jest tablica 6 liczb:


for (i = 0; i < 10; i++)
balances[i] = new double[6];

Na szczcie ptla ta dziaa automatycznie, kiedy tworzymy tablic new double[10][6].


Aby utworzy tablic postrzpion, kady wiersz musimy tworzy oddzielnie.
{
final int NMAX = 10;
// Tworzenie tablicy trjktnej.
int[][] odds = new int[NMAX + 1][];
for (int n = 0; n <= NMAX; n++)
odds[n] = new int[n + 1];
// Zapenienie tablicy trjktnej.
for (int n = 0; n < odds.length; n++)
for (int k = 0; k < odds[n].length; k++)
{
/*
* Obliczenie wspczynnika dwumianowego n*(n1)*(n2)**(nk+1)/(1*2*3**k).
*/
int lotteryOdds = 1;
for (int i = 1; i <= k; i++)
lotteryOdds = lotteryOdds * (n - i + 1) / i;
odds[n][k] = lotteryOdds;
}
// Drukowanie tablicy trjktnej.
for (int[] row : odds)
{
for (int odd : row)
System.out.printf("%4d", odd);
System.out.println();
}
}
}

Wanie poznalimy podstawowe struktury programistyczne Javy. W kolejnym rozdziale


zajmiemy si technikami obiektowymi.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

130

Java. Podstawy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Obiekty i klasy
W tym rozdziale:

Wstp do programowania obiektowego

Uywanie standardowych klas

Definiowanie wasnych klas

Pola i metody statyczne

Parametry metod

Konstrukcja obiektw

Pakiety

cieka klas

Komentarze dokumentacyjne

Porady dotyczce projektowania klas

Osoby, ktre do tej pory nie miay do czynienia z programowaniem obiektowym, powinny
bardzo uwanie przeczyta ten rozdzia. Programowanie obiektowe wymaga innego sposobu
mylenia ni programowanie proceduralne. Przestawienie si bywa czasami trudne, ale kontynuacja nauki Javy bez znajomoci technik obiektowych byaby niemoliwa.
Programici jzyka C++ odkryj w tym rozdziale (podobnie jak w poprzednim) duo podobiestw midzy Jav i C++. Jednak Java i C++ rni si na tyle, e take programici C++
powinni przeczyta ten rozdzia z uwag. W przestawieniu si na nowy jzyk bd pomocne
uwagi dotyczce jzyka C++.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

132

Java. Podstawy

4.1. Wstp do programowania obiektowego


Programowanie obiektowe (ang. Object Oriented Programming OOP) jest obecnie najbardziej rozpowszechnionym paradygmatem programowania i zastpio techniki proceduralne
opracowane jeszcze w latach 70. ubiegego wieku. Java jest jzykiem w peni obiektowym,
a co za tym idzie aby by efektywnym programist Javy, trzeba zna techniki obiektowe.
Program obiektowy skada si z obiektw. Kady obiekt udostpnia okrelony zestaw funkcji,
a ich szczegy implementacyjne s ukryte. Wiele obiektw uywanych w programach pochodzi z biblioteki. Cz z nich programista tworzy jednak wasnorcznie. To, czy programista
zdecyduje si na budow wasnego obiektu, czy skorzysta z ju istniejcego, zaley od jego
czasu i moliwoci. Dopki obiekt spenia wymagania, z reguy nie ma potrzeby zagbiania
si w tajniki jego implementacji. W programowaniu obiektowym implementacja obiektu nie
ma znaczenia, dopki dziaa on zgodnie z oczekiwaniami.
Tradycyjne programowanie proceduralne polega na zaprojektowaniu zbioru procedur (czyli
algorytmw) majcych rozwiza dany problem. Po utworzeniu procedur przychodzi kolej
na zapisanie danych. Dlatego wanie twrca jzyka Pascal, Niklaus Wirth, zatytuowa swoj
synn ksik o programowaniu Algorytmy + struktury danych = programy (WNT, Warszawa 2004). Znamienne jest to, e w tytule tym na pocztku znajduj si algorytmy, a dopiero
po nich struktury danych. Obrazuje to sposb, w jaki pracowali programici w tamtych czasach. Najpierw opracowywali procedury przetwarzajce dane, a nastpnie ujmowali te dane
w takie struktury, ktre uatwiay to przetwarzanie. W programowaniu obiektowym ta kolejno jest odwrcona najpierw s dane, dopiero po nich algorytmy, ktre je przetwarzaj.
W przypadku maych programw podzia na procedury jest bardzo dobrym podejciem.
Obiekty natomiast s najlepsze do pracy nad duymi projektami. Wemy prost przegldark
internetow. Implementacja takiego programu mogaby wymaga okoo 2000 procedur operujcych na jakim zbiorze danych globalnych. Przy zastosowaniu podejcia obiektowego byoby
okoo 100 klas, z ktrych rednio kada zawieraaby 20 metod (zobacz rysunek 4.1). Ta druga
struktura byaby znacznie atwiejsza do ogarnicia przez programist. atwiej te znale
w niej bdy. Wyobramy sobie, e dane okrelonego obiektu znajduj si w nieprawidowym stanie. Znalezienie problemu wrd 20 metod, ktre miay dostp do tych danych, jest
znacznie atwiejsze ni wrd 2000 procedur.

4.1.1. Klasy
Klasa jest szablonem, z ktrego tworzy si obiekty. Jeli klasy s foremkami do robienia
ciastek, to obiekty s samymi ciastkami. Konstruujc obiekt, tworzymy egzemplarz klasy.
Wiemy ju, e wszystko, co piszemy w Javie, znajduje si w jakiej klasie. W standardowej
bibliotece znajduje si kilka tysicy klas o tak rnym przeznaczeniu jak wspomaganie projektowania interfejsu, obsuga dat i kalendarzy czy programowanie sieciowe. Niemniej
konieczne jest tworzenie wasnych klas do opisu obiektw rozwizujcych problemy zwizane z konkretnym programem.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

133

Rysunek 4.1.
Programowanie
proceduralne
a programowanie
obiektowe

Kluczowym pojciem zwizanym z obiektami jest hermetyzacja (inaczej mwic, ukrywanie


danych). Z formalnego punktu widzenia hermetyzacja polega na umieszczaniu danych i operacji
w jednym pakiecie oraz ukrywaniu szczegw implementacyjnych przed uytkownikiem
obiektu. Dane zawarte w obiekcie nazywaj si skadowymi obiektu, a procedury operujce
tymi danymi to metody. Obiekt bdcy egzemplarzem danej klasy ma skadowe o okrelonych wartociach. Zestaw tych wartoci okrela aktualny stan obiektu. Za kadym razem,
gdy wywoywana jest metoda na rzecz obiektu, jego stan moe si zmieni.
Aby hermetyzacja speniaa swoje zadanie, metody nie mog by bezporednio wywoywane
na rzecz skadowych obiektw klas innych ni ich wasna. Dane obiektowe powinny by uywane w programie tylko za porednictwem metod obiektw zawierajcych te dane. Hermetyzacja nadaje obiektowi charakter czarnej skrzynki, co jest kluczowe dla koncepcji wielokrotnego uycia kodu, jak i jego niezawodnoci. Oznacza to, e sposb przechowywania danych
w klasie moe si diametralnie zmieni, ale dopki udostpnia ona te same metody do manipulacji tymi danymi, aden obiekt nie zostanie tym dotknity.
Budow klas w Javie uatwia jeszcze jedna cecha programowania obiektowego: klasy mona
budowa poprzez rozszerzanie (ang. extending) innych klas. Wszystkie klasy w Javie dziedzicz po jednej klasie bazowej o nazwie Object. Wicej informacji na temat tej klasy znajduje
si w rozdziale 5.
Kiedy rozszerzamy istniejc klas, nowo powstaa klasa ma wszystkie cechy i metody klasy
rozszerzanej. Nowe metody i pola s dostpne tylko w nowej klasie. Proces rozszerzania klasy
w celu utworzenia nowej klasy nazywa si dziedziczeniem (ang. inheritance). Szczegowe
informacje na temat dziedziczenia znajduj si w kolejnym rozdziale.

4.1.2. Obiekty
Aby sprawnie porusza si w wiecie programowania obiektowego, naley zna trzy podstawowe cechy obiektu:

Zachowanie obiektu co mona z obiektem zrobi i jakie metody mona


wywoywa na jego rzecz.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

134

Java. Podstawy

Stan obiektu jak obiekt reaguje w odpowiedzi na wywoywane na jego rzecz


metody.

Tosamo obiektu jak odrni obiekt od innych obiektw, ktre mog mie
te same zachowanie i stan.

Wszystkie obiekty bdce egzemplarzami tej samej klasy s do siebie podobne pod tym
wzgldem, e charakteryzuj si takim samym zachowaniem. Zachowanie obiektu definiuj
metody, ktre mona wywoywa.
Kady obiekt przechowuje informacje o tym, jak aktualnie wyglda. Jest to stan obiektu.
Stan obiektu moe si zmienia w czasie, ale nie samoczynnie. Zmiana stanu obiektu musi
by spowodowana wywoaniem metod (jeli stan obiektu zmieni si, mimo e nie wywoano
na jego rzecz adnej metody, oznacza to, e zostaa zamana zasada hermetyzacji).
Stan obiektu nie wystarczy jednak, aby ten obiekt w peni opisa, poniewa istnieje jeszcze
tosamo obiektu. Na przykad w systemie przetwarzania zamwie dwa zamwienia s
odrbne, mimo i dotycz zakupu tego samego produktu. Naley zauway, e poszczeglne
obiekty bdce egzemplarzami tej samej klasy zawsze maj inn tosamo i zazwyczaj
rni si stanami.
Te kluczowe cechy mog midzy sob oddziaywa. Na przykad stan obiektu moe mie
wpyw na jego zachowanie (jeli zamwienie zostao wysane lub opacone, obiekt moe
odmwi wykonania metody, ktra dodaje lub usuwa elementy; podobnie jest w przypadku,
gdy zamwienie jest puste, to znaczy adne produkty nie zostay jeszcze dodane obiekt nie
powinien wwczas zezwoli na jego wysanie).

4.1.3. Identyfikacja klas


Tradycyjny program napisany w technice proceduralnej zaczyna si na samej grze pliku
funkcj main. W systemie obiektowym nie ma gry, przez co nowicjusze czsto maj
problem, od czego zacz. Odpowied jest taka, e najpierw trzeba utworzy klasy, a potem
doda do nich metody.
Prosta zasada dotyczca nadawania nazw klasom nakazuje tworzenie nazw z rzeczownikw
obecnych w analizie problemu. Metody natomiast odpowiadaj czasownikom.
Na przykad w systemie przetwarzania zamwie mog si znale nastpujce rzeczowniki:

Item (produkt),

Order (zamwienie),

Shipping address (adres dostawy),

Payment (patno),

Account (konto)1.

Ze wzgldu na to, e take polscy programici zazwyczaj stosuj angielskie nazwy w swoich programach,
nie tumacz adnych nazw, tylko podaj ich polskie odpowiedniki, gdy jest to uzasadnione przyp. tum.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

135

Z tych rzeczownikw mona utworzy nastpujce nazwy klas: Item, Order, Shipping
Address itd.
Nastpnie szukamy czasownikw. Do zamwienia dodajemy (ang. add) produkty. Zamwienie mona wysa (ang. ship) albo anulowa (ang. cancel). Patnoci s dokonywane
(ang. apply) na rzecz zamwie. Dla kadego z tych czasownikw trzeba znale obiekt, ktry
jest odpowiedzialny za wykonywanie tych dziaa. Jeli na przykad do zamwienia dodawany jest nowy produkt, to powinien w t operacj zaangaowa si obiekt klasy Order,
poniewa ma informacje na temat zapisywania i sortowania produktw. To znaczy, e metoda
add powinna by metod klasy Order i przyjmowa jako parametr obiekt klasy Item.
Oczywicie regua rzeczownika i czasownika jest tylko praktyczn zasad. W podjciu
decyzji, ktre rzeczowniki i czasowniki naley wykorzysta w nazwach przy budowie klasy,
moe pomc tylko dowiadczenie.

4.1.4. Relacje midzy klasami


Najczciej spotykane relacje midzy klasami to:

zaleno (uywa),

agregacja (zawiera),

dziedziczenie (jest).

Zwizek zalenoci (czyli uywa) jest najbardziej oczywisty, a zarazem oglny. Na przykad klasa Order uywa klasy Account, poniewa obiekty klasy Order potrzebuj dostpu do
obiektw Account w celu sprawdzenia wypacalnoci klienta. Natomiast klasa Item nie jest
zalena od klasy Account, poniewa obiekty klasy Item nie potrzebuj informacji o kontach
klientw. Zatem klasa zaley od innej klasy, jeli metody tej pierwszej uywaj obiektw
tej drugiej lub na nich operuj.
Liczb klas wzajemnie zalenych naley ogranicza do minimum. Jeli klasa A nie wie nic
o istnieniu klasy B, to nie maj dla niej znaczenia adne zmiany w klasie B (a to oznacza, e
zmiany wprowadzone w klasie B nie powoduj powstawania bdw w klasie A)! W terminologii inynierii oprogramowania okrela si to mianem skojarzenia, czyli stopniem powizania midzy klasami (ang. coupling).
Agregacja (czyli zwizek zawiera) jest atwa do zrozumienia, poniewa opisuje konkretne
zjawisko. Na przykad obiekt klasy Order zawiera obiekty klasy Item. Innymi sowy, obiekty
klasy A zawieraj obiekty klasy B.
Niektrzy badacze metod programowania traktuj pojcie agregacji pogardliwie
i preferuj bardziej oglny zwizek asocjacji. Z punktu widzenia modelowania jest
to zrozumiae, ale dla programistw zwizek zawiera jest bardzo adekwatnym pojciem. Jest jeszcze jeden powd, dla ktrego wolimy agregacj standardowa notacja oznaczania asocjacji jest mniej jasna (zobacz tabela 4.1).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

136

Java. Podstawy

Tabela 4.1. Oznaczenia powiza midzy klasami jzyka UML


Relacja

Konektor UML

Dziedziczenie
Dziedziczenie interfejsu
Zaleno
Agregacja
Asocjacja
Asocjacja skierowana

Dziedziczenie (czyli zwizek jest) wyraa zwizek pomidzy klas ogln i klas specjaln.
Na przykad klasa RushOrder (szybkie zamwienie) dziedziczy po klasie Order. Wyspecjalizowana klasa RushOrder ma specjalne metody do obsugi priorytetw i inn metod do
obliczania opat za transport, ale pozostae jej metody, jak dodawanie produktw i pobieranie opat, s odziedziczone po klasie Order. Oglnie rzecz biorc, jeli klasa A rozszerza
klas B, klasa A dziedziczy metody po klasie B, ale ma wiksze moliwoci od klasy B (dziedziczenie jest bardzo wanym zagadnieniem i zostao szczegowo opisane w nastpnym
rozdziale).
Wielu programistw rysuje diagramy klas jzyka UML (ang. Unified Modeling Language)
obrazujce powizania midzy klasami. Przykad takiego diagramu przedstawia rysunek 4.2.
Klasy s reprezentowane przez prostokty, a powizania maj posta strzaek z rnymi
dodatkami. Tabela 4.1 przedstawia najczciej uywane w UML typy strzaek.
Rysunek 4.2.
Diagram klas

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

137

4.2. Uywanie klas predefiniowanych


Poniewa w Javie nie mona nic zrobi bez klas, do tej pory uylimy ju kilku z nich. Nie
wszystkie one jednak s typowymi przedstawicielkami programowania obiektowego. Wemy
na przykad klas Math. Wiemy, e mona uywa metod tej klasy jak Math.random i e nie
jest nam do tego potrzebna wiedza na temat szczegw implementacyjnych tych metod.
Potrzebujemy tylko nazwy metody i informacji o jej parametrach. Jest to wynikiem hermetyzacji i z pewnoci dotyczy wszystkich klas. Ale klasa Math hermetyzuje tylko funkcjonalno. Nie potrzebuje ani nie ukrywa danych. Poniewa nie ma adnych danych, nie trzeba si
zajmowa tworzeniem jej obiektw ani inicjacj zmiennych skadowych egzemplarzy
poniewa ich nie ma!
W kolejnym podrozdziale przyjrzymy si bardziej typowej klasie o nazwie Date. Dowiemy
si, jak tworzy obiekty tej klasy i wywoywa jej metody.

4.2.1. Obiekty i zmienne obiektw


Aby mc uy obiektu, trzeba go najpierw utworzy i okreli jego stan pocztkowy. Potem
mona wywoywa na jego rzecz rne metody.
W Javie nowe egzemplarze klas tworzy si za pomoc konstruktorw. Konstruktor to specjalna metoda, ktrej przeznaczeniem jest tworzenie i inicjacja obiektw. Wemy na przykad klas Date, ktra jest zdefiniowana w bibliotece standardowej. Jej obiekty okrelaj punkty
w czasie, np. "31 Grudzie 2007, 23:59:59 GMT".
Konstruktor ma zawsze tak sam nazw jak klasa. Zatem konstruktor klasy Date ma nazw
Date. Aby utworzy obiekt klasy Date, naley uy konstruktora tej klasy i operatora new:
new Date()

To wyraenie tworzy nowy obiekt. Obiekt ten jest inicjowany aktualn dat i godzin.
Niektrzy mog si zastanawia, czemu do reprezentacji dat uywa si klas zamiast
(jak w niektrych jzykach programowania) typu wbudowanego. Na przykad jzyk
Visual Basic ma typ wbudowany, dziki czemu programista moe napisa dat w nastpujcym formacie: #6/1/1995#. Na pierwszy rzut oka wydaje si to cakiem dobrym rozwizaniem zamiast przejmowa si klasami, programista moe uy typu wbudowanego.
Naley jednak zada pytanie, czy rozwizanie zastosowane w jzyku Visual Basic jest
dobre. W niektrych krajach format daty to miesic/dzie/rok, a w innych dzie/miesic/
rok. Czy projektanci jzyka przewidzieli tak ewentualno? Jeli nie wykonaj swojej
pracy dobrze, jzyk bdzie pozostawa w nieadzie, a nieszczliwi programici nie bd
mogli nic z tym zrobi. Przy zastosowaniu klas obowizek projektowania zostaje przerzucony na twrc biblioteki. Jeli klasa ma wady, inni programici mog z atwoci napisa wasn klas rozszerzajc lub zastpujc klas systemow (dowd: biblioteka dat
w Javie ma kilka wad i trwaj prace nad jej popraw zobacz http://jcp.org/en/jsr/
detail?id=310).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

138

Java. Podstawy
Obiekt mona przekaza do metody:
System.out.println(new Date());

Metod mona te wywoa na rzecz tworzonego obiektu. Jedn z metod klasy Date jest
toString. Zwraca ona reprezentacj acuchow daty. Poniej przedstawiono sposb wywoania metody toString na rzecz tworzonego obiektu klasy Date:
String s = new Date().toString();

W przedstawionych przykadach utworzony obiekt by uywany tylko jeden raz. Zazwyczaj


jednak tworzone obiekty s potrzebne do wielokrotnego uytku. Wtedy trzeba zapisa je
w zmiennych:
Date birthday = new Date();

Rysunek 4.3 przedstawia zmienn obiektow birthday, ktra jest referencj do nowo utworzonego obiektu.
Rysunek 4.3.
Tworzenie
nowego obiektu

Midzy obiektami a zmiennymi obiektowymi istnieje bardzo istotna rnica. Na przykad


ponisza instrukcja:
Date deadline;

// Zmienna deadline nie odwouje si do adnego obiektu.

definiuje zmienn obiektow o nazwie deadline, ktra moe si odwoywa do obiektw typu
Date. Koniecznie trzeba pamita, e zmienna deadline nie jest obiektem ani nawet nie
odwouje si jeszcze do adnego obiektu. Obecnie nie mona na jej rzecz wywoywa adnych metod klasy Date. Ponisza instrukcja:
s = deadline.toString();

// jeszcze nie

spowodowaaby bd kompilacji.
Konieczna jest uprzednia inicjacja zmiennej deadline. S dwie moliwoci. Mona oczywicie inicjacji tej dokona za pomoc nowo utworzonego obiektu:
deadline = new Date();

albo zmienn deadline ustawi na istniejcy obiekt:


deadline = birthday;

W tej chwili obie zmienne s referencjami (odwouj si) do tego samego obiektu (zobacz
rysunek 4.4).
Trzeba sobie uwiadomi, e zmienna obiektowa nie jest obiektem, tylko referencj do
obiektu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

139

Rysunek 4.4.
Zmienne
obiektowe
odwoujce si
do tego samego
obiektu

Warto kadej zmiennej obiektowej jest referencj do obiektu, ktry jest przechowywany
gdzie indziej. Warto zwracana przez operator new te jest referencj. Instrukcja typu:
Date deadline = new Date();

skada si z dwch czci. Wyraenie new Date() tworzy obiekt typu Date, a jego wartoci
jest referencja do tego nowo utworzonego obiektu. Referencja ta zostaje zapisana w zmiennej deadline.
Aby zaznaczy, e zmienna obiektowa nie odwouje si do adnego obiektu, naley jej warto ustawi na null.
deadline = null;
. . .
if (deadline != null)
System.out.println(deadline);

Wywoanie metody na rzecz zmiennej zawierajcej warto null spowoduje bd dziaania


programu.
birthday = null;
String s = birthday.toString();

// Bd wykonania programu!

Zmienne obiektowe nie s automatycznie inicjowane wartoci null. Trzeba je wasnorcznie


inicjowa za pomoc operatora new lub ustawia ich warto na null.

4.2.2. Klasa GregorianCalendar


W powyszych przykadach uywalimy klasy Date, ktra naley do standardowej biblioteki.
Egzemplarz tej klasy ma wyznaczony stan, ktrym jest okrelony punkt w czasie.
Chocia nie musimy tego wiedzie, by uywa klasy Date, czas jest reprezentowany przez
liczb milisekund (ujemn lub dodatni), ktry upyny od ustalonego momentu (tak zwanej epoki). Tym momentem jest 1 stycznia 1970 o godzinie 00:00:00 UTC. UTC oznacza
Coordinated Universal Time naukowy standard wyraania czasu, ktry z przyczyn praktycznych jest rwnoznaczny z lepiej znanym GMT, czyli Greenwich Mean Time.
Okazuje si jednak, e klasa Date nie jest zbyt uyteczna przy manipulacji datami. Projektanci jzyka Java ustalili, e zapis daty w formacie 31 grudnia 1999, 23:59:59 jest z gry
przyjt konwencj, okrelan przez kalendarz. Ten konkretny zapis jest zgodny z najbardziej rozpowszechnionym na wiecie kalendarzem gregoriaskim. Ten sam moment w czasie zostaby cakiem inaczej przedstawiony w chiskim lub hebrajskim kalendarzu ksiycowym, nie mwic ju kalendarzu marsjaskim.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

140

Java. Podstawy

Wielu programistw tkwi w bdnym przekonaniu, e zmienne obiektowe w Javie


s odpowiednikami referencji w C++. Jednak w C++ nie ma referencji null i referencji nie mona przypisywa. Zmienne obiektowe Javy naley porwnywa ze wskanikami do obiektw w C++. Na przykad:
Date birthday;

// Java

jest rwnoznaczne z:
Date* birthday;

// C++

Oczywicie wskanik Date* nie jest zainicjowany, dopki nie uyjemy operatora new.
Skadnia w Javie jest prawie taka sama jak w C++.
Date* birthday = new Date();

// C++

Jeli skopiujemy jedn zmienn do innej zmiennej, obie zmienne bd si odwoyway do


tej samej daty bd wskazywa ten sam obiekt. Odpowiednikiem referencji null Javy
jest wskanik NULL w C++.
Wszystkie obiekty w Javie znajduj si na stercie (ang. heap). Jeli obiekt zawiera jak
zmienn obiektow, zmienna ta zawiera tylko wskanik do innego obiektu na stercie.
Wskaniki w C++ s rdem mnstwa bdw. atwo mona utworzy bdny wskanik
albo nieodpowiednio przydzieli pami. W Javie tych problemw nie ma. Jeli uyjemy
niezainicjowanego wskanika, interpreter z pewnoci zgosi bd, zamiast generowa
losowe wyniki. Zarzdzaniem pamici zajmuje si natomiast system zbierania nieuytkw.
C++ umoliwia implementacj obiektw, ktre automatycznie tworz kopie samych
siebie. Su do tego konstruktory kopiujce i operatory przypisania. Na przykad kopi
listy dowiza jest nowa lista dowiza o takiej samej zawartoci, lecz osobnym zbiorze
dowiza. Dziki temu mona projektowa klasy zachowujce si podobnie jak typy
wbudowane. W Javie konieczne jest uyciu metody clone, aby utworzy kopi obiektu.

W historii cywilizacji uywano rozmaitych kalendarzy, przypisujcych datom nazwy


i porzdkujcych cykle soneczne i ksiycowe. Ksika Calendrical Calculations
autorstwa Nachuma Dershowitza i Edwarda M. Reingolda (Cambridge University Press,
2001) w fascynujcy sposb opisuje najprzerniejsze kalendarze wiata, od kalendarza
francuskiej rewolucji po kalendarz Majw.

Projektanci biblioteki postanowili rozdzieli kwestie zapisywania informacji o czasie od


nadawania nazw momentom czasu. W tym celu biblioteka standardowa Javy zawiera dwie
osobne klasy: klas Date, ktra reprezentuje moment w czasie, i klas GregorianCalendar,
ktra wyraa daty w znanej notacji kalendarzowej. W rzeczywistoci klasa GregorianCalendar
dziedziczy po bardziej oglnej klasie Calendar. Standardowa biblioteka Javy zawiera te
implementacje kalendarza buddyjskiego tajskiego i japoskiego imperialnego.
Oddzielenie pomiaru czasu od kalendarzy jest przykadem dobrego podejcia obiektowego.
Uoglniajc, dobrze jest przedstawia rne koncepcje za pomoc osobnych klas.
Klasa Date zawiera kilka metod do porwnywania dwch momentw w czasie. Na przykad
metody before i after informuj, czy dany moment w czasie jest wczeniejszy, czy pniejszy
ni inny moment:
if (today.before(birthday))
System.out.println("Jest jeszcze czas, aby kupi prezent.");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

141

Klasa Date udostpnia te metody getDay, getMonth i getYear, ale ich stosowanie
jest odradzane. Metoda jest odradzana, jeli twrca biblioteki dojdzie do wniosku,
e metoda ta w ogle nie powinna bya powsta.
Metody te naleay do klasy Date, zanim twrcy biblioteki zdali sobie spraw, e lepiej
bdzie utworzy osobne klasy dla kalendarzy. Po wprowadzeniu klas kalendarzy metody
klasy Date zostay oznaczone jako odradzane (ang. deprecated). Mona ich nadal uywa, ale kompilator bdzie zgasza niezbyt eleganckie ostrzeenia. Dobrze jest trzyma
si z dala od odradzanych metod, poniewa mog one zosta w przyszoci usunite
z biblioteki.

Klasa GregorianCalendar zawiera duo wicej metod ni klasa Date. Przede wszystkim ma
kilka przydatnych konstruktorw. Wyraenie:
new GregorianCalendar()

tworzy nowy obiekt reprezentujcy dat i godzin w chwili jego utworzenia.


Mona utworzy obiekt ustawiony na pnoc okrelonej daty, podajc rok, miesic i dzie:
new GregorianCalendar(1999, 11, 31)

Co ciekawe, miesice s numerowane od 0. A zatem grudzie ma numer 11. Aby unikn


nieporozumie, wprowadzono stae typu Calendar.DECEMBER:
new GregorianCalendar(1999, Calendar.DECEMBER, 31)

Mona take ustawi godzin:


new GregorianCalendar(1999, Calendar.DECEMBER, 31, 23, 59, 59)

Oczywicie w wikszoci przypadkw utworzony obiekt powinien by przechowywany


w zmiennej obiektowej:
GregorianCalendar deadline = new GregorianCalendar(. . .);

Obiekt klasy GregorianCalendar zawiera pola przechowujce dat, na ktr obiekt ten zostanie
ustawiony. Dziki hermetyzacji nie sposb odgadn, jakiej reprezentacji uywa ta klasa, nie
zagldajc do jej kodu rdowego, ale oczywicie dziki hermetyzacji nie ma to znaczenia.
Znaczenie maj metody udostpniane przez klas.

4.2.3. Metody udostpniajce i zmieniajce warto elementu


W tym miejscu naley sobie zada pytanie: jak dosta si do aktualnego dnia lub miesica
daty zamknitej w obiekcie klasy GregorianCalendar? A take jak zmieni niektre wartoci?
Odpowiedzi na te pytania mona znale w dokumentacji internetowej i wycigach z API
znajdujcych si na kocu tego podrozdziau. Omwimy tu tylko najwaniejsze metody.
Zadaniem kalendarza jest obliczanie atrybutw, takich jak data, dzie tygodnia, miesica lub
roku, dla okrelonego punktu w czasie. Aby sprawdzi ktre z tych ustawie, naley posuy
si metod akcesora get klasy GregorianCalendar. Dostp do wybranych elementw mona
uzyska za pomoc staych zdefiniowanych w klasie Calendar, takich jak Calendar.MONTH
czy Calendar.DAY_OF_WEEK.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

142

Java. Podstawy
GregorianCalendar now = new GregorianCalendar();
int month = now.get(Calendar.MONTH);
int weekday = now.get(Calendar.DAY_OF_WEEK);

W wycigu z API znajduje si pena lista tych staych.


Stan obiektu mona zmieni za pomoc metody mutatora set:
deadline.set(Calendar.YEAR, 2001);
deadline.set(Calendar.MONTH, Calendar.APRIL);
deadline.set(Calendar.DAY_OF_MONTH, 15);

Rok, miesic i dzie mona te ustawi za pomoc jednego wygodnego wywoania:


deadline.set(2001, Calendar.APRIL, 15);

Dodatkowo do obiektu kalendarza mona doda dowoln liczb dni, tygodni, miesicy itd.:
deadline.add(Calendar.MONTH, 3);

// Przeniesienie terminu o 3 miesice.

Dodanie liczby ujemnej spowoduje przesunicie czasu kalendarza do tyu.


Pomidzy metodami get oraz set i add jest zasadnicza rnica. Pierwsza z nich tylko sprawdza
stan obiektu i zwraca informacje o nim. Metody set i add zmieniaj stan obiektu. Metody, ktre
modyfikuj pola egzemplarza, nazywaj si mutatorami (ang. mutator method), a te, ktre
daj do nich tylko dostp, nosz nazw akcesorw (ang. accessor method).

W jzyku C++ metody akcesora s oznaczane przyrostkiem const. Metoda, ktra


nie zostaa zadeklarowana jako const, jest z zaoenia mutatorem. W Javie nie ma
specjalnej notacji odrniajcej metody akcesorw od mutatorw.

Oglnie przyjta konwencja gosi, aby metody akcesora poprzedza przedrostkiem get,
a mutatora przedrostkiem set. Na przykad klasa GregorianCalendar zawiera metody getTime
i setTime, ktre odpowiednio pobieraj i ustawiaj punkt w czasie reprezentowany przez obiekt:
Date time = calendar.getTime();
calendar.setTime(time);

Metody te maj szczeglne znaczenie przy konwersji pomidzy klasami GregorianCalendar


i Calendar. Oto przykad: majc dany rok, miesic i dzie, chcemy utworzy obiekt klasy
Date. Poniewa klasa Date nie ma adnych danych na temat kalendarzy, najpierw tworzymy
obiekt GregorianCalendar, a nastpnie pobieramy dat za pomoc wywoania metody getTime:
GregorianCalendar calendar = new GregorianCalendar(year, month, day);
Date hireDay = calendar.getTime();

Podobnie, aby sprawdzi rok, miesic lub dzie obiektu klasy Date, naley utworzy obiekt
klasy GregorianCalendar, ustawi czas i wywoa metod get:
GregorianCalendar calendar = new GregorianCalendar();
calendar.setTime(hireDay);
int year = calendar.get(Calendar.YEAR);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

143

Na zakoczenie tego podrozdziau prezentujemy program, ktry demonstruje praktyczne


zastosowanie klasy GregorianCalendar. Ten program wywietla kalendarz biecego miesica,
podobny do poniszego:
Pn
5
12
19
26

Wt
6
13
20
27

r
7
14
21
28

Cz Pt So N
1
2 3
4
8
9 10 11
15 16 17 18
22 23 24 25
29 30*

Biecy dzie jest oznaczony gwiazdk (*). Jak wida, program musi wiedzie, jak obliczy
dugo miesica oraz dzie tygodnia.
Przeanalizujmy najwaniejsze czci tego programu. Najpierw tworzymy obiekt kalendarza,
ktry inicjujemy aktualn dat:
GregorianCalendar d = new GregorianCalendar();

Pobieramy aktualny dzie i miesic za pomoc dwch wywoa metody get:


int today = d.get(Calendar.DAY_OF_MONTH);
int month = d.get(Calendar.MONTH);

Nastpnie ustawiamy zmienn d na pierwszy dzie miesica i pobieramy dzie tygodnia dla
tej daty:
d.set(Calendar.DAY_OF_MONTH, 1);
int weekday = d.get(Calendar.DAY_OF_WEEK);

Zmienna weekday jest ustawiona na warto Calendar.NIEDZIELA, jeli pierwszym dniem


miesica jest niedziela, Calendar.PONIEDZIAEK, jeli jest to poniedziaek itd. (wartoci te s
w rzeczywistoci liczbami cakowitymi: 1, 2, , 7).
Zwr uwag, e pierwszy wiersz kalendarza jest odpowiednio wcity, aby pierwszy dzie
miesica by przyporzdkowany do odpowiedniego dnia tygodnia. Trudnoci mog wynikn z tego, e w USA tydzie zaczyna si od niedzieli i koczy w sobot, a w Europie od
poniedziaku i koczy w niedziel.
Maszyna wirtualna Javy ma dane o lokalizacji uytkownika. Dane te dotycz stosowanych
konwencji, wliczajc pocztek tygodnia i nazwy dni tygodnia.
Aby zobaczy wynik tego programu dla innej lokalizacji, naley w pierwszej linijce
metody main doda poniszy wiersz kodu:
Locale.setDefault(Locale.ITALY);

Metoda getFirstDayOfWeek pobiera pierwszy dzie tygodnia w biecej lokalizacji. Odpowiednie wcicie uzyskujemy poprzez odjcie 1 od dnia w obiekcie kalendarza, a dojdziemy
do pierwszego dnia tygodnia.
int firstDayOfWeek = d.getFirstDayOfWeek();
int indent = 0;
while (weekday != firstDayOfWeek)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

144

Java. Podstawy
{
indent++;
d.add(Calendar.DAY_OF_MONTH, -1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}

Nastpnie drukujemy nagwek kalendarza przedstawiajcy nazwy dni tygodnia. S one


dostpne w klasie DateFormatSymbols.
String [] weekdayNames = new DateFormatSymbols().getShortWeekdays();

Metoda getShortWeekdays zwraca acuch zoony ze skrtw nazw dni tygodnia w jzyku
uytkownika (np. Pn, Wt itd. po polsku). Indeksami w tej tablicy s numery dni tygodnia.
Ponisza ptla drukuje nagwek:
do
{
System.out.printf("%4s", weekdayNames[weekday]);
d.add(Calendar.DAY_OF_MONTH, 1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
while (weekday != firstDayOfWeek);
System.out.println();

Moemy teraz przej do drukowania pozostaej czci kalendarza. Robimy wcicie pierwszego wiersza i ustawiamy obiekt daty z powrotem na pocztek miesica. Wprowadzamy ptl,
w ktrej zmienna d przemierza wszystkie dni miesica.
W kadym powtrzeniu drukowana jest liczba. Jeli warto d odpowiada dzisiejszej dacie,
dodawana jest gwiazdka. Po dojciu do pocztku kolejnego tygodnia najpierw drukujemy
nowy wiersz. Nastpnie warto d ustawiamy na kolejny dzie:
d.add(Calendar.DAY_OF_MONTH, 1);

Kiedy si zatrzymamy? Nie wiadomo, czy miesic ma 31, 30, 29, czy 28 dni. Powtarzamy
ptl, dopki d mieci si w biecym miesicu.
do
{

. . .
}
while (d.get(Calendar.MONTH) == month);

Kiedy d przejdzie do nastpnego miesica, program zostaje zakoczony.


Listing 4.1 przedstawia peny kod tego programu.
Listing 4.1. CalendarTest.java
import java.text.DateFormatSymbols;
import java.util.*;
/**
* @version 1.4 2007-04-07
* @author Cay Horstmann
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

public class CalendarTest


{
public static void main(String[] args)
{
// Konstrukcja i ustawienie obiektu d oraz jego inicjacja aktualn dat.
GregorianCalendar d = new GregorianCalendar();
int today = d.get(Calendar.DAY_OF_MONTH);
int month = d.get(Calendar.MONTH);
// Ustawienie d na pocztek miesica.
d.set(Calendar.DAY_OF_MONTH, 1);
int weekday = d.get(Calendar.DAY_OF_WEEK);
// Pobranie pierwszego dnia tygodnia (poniedziaek w Polsce).
int firstDayOfWeek = d.getFirstDayOfWeek();
// Okrelenie odpowiedniego wcicia pierwszego wiersza.
int indent = 0;
while (weekday != firstDayOfWeek)
{
indent++;
d.add(Calendar.DAY_OF_MONTH, -1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
// Drukowanie nazw dni tygodnia.
String[] weekdayNames = new DateFormatSymbols().getShortWeekdays();
do
{
System.out.printf("%4s", weekdayNames[weekday]);
d.add(Calendar.DAY_OF_MONTH, 1);
weekday = d.get(Calendar.DAY_OF_WEEK);
}
while (weekday != firstDayOfWeek);
System.out.println();
for (int i = 1; i <= indent; i++)
System.out.print("
");
d.set(Calendar.DAY_OF_MONTH, 1);
do
{
// Drukowanie dnia.
int day = d.get(Calendar.DAY_OF_MONTH);
System.out.printf("%3d", day);
// Oznaczenie biecego dnia znakiem *.
if (day == today) System.out.print("*");
else System.out.print(" ");
// Ustawienie d na kolejny dzie.
d.add(Calendar.DAY_OF_MONTH, 1);
weekday = d.get(Calendar.DAY_OF_WEEK);
// Rozpoczcie nowego wiersza na pocztku tygodnia.
if (weekday == firstDayOfWeek) System.out.println();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

145

146

Java. Podstawy
}
while (d.get(Calendar.MONTH) == month);
// Ptla koczy dziaanie, jeli d jest pierwszym dniem nastpnego miesica.
// Wydruk kocowego znaku nowego wiersza w razie potrzeby.
if (weekday != firstDayOfWeek) System.out.println();
}
}

Przekonalimy si, e klasa GregorianCalendar umoliwia pisanie programw kalendarzy


z uwzgldnieniem skomplikowanych problemw, jak dni tygodnia i rne dugoci miesicy.
Nie trzeba wiedzie, jak klasa ta oblicza miesice i dni tygodnia. Programista uywa tylko
interfejsu klasy metod get, set i add.
Ten program ma na celu pokazanie, w jaki sposb mona wykorzysta interfejs klasy do
bardzo zoonych zada bez znajomoci szczegw implementacyjnych.
java.util.GregorianCalendar 1.1

GregorianCalendar()

Tworzy obiekt kalendarza reprezentujcy biec dat i godzin w domylnej


strefie czasowej i lokalizacji.

GregorianCalendar(int year, int month, int day)

GregorianCalendar(int year, int month, int day, int hour, int minutes,
int seconds)

Tworzy kalendarz gregoriaski z podanej daty i godziny.


Parametry:

year

rok

month

miesic wartoci liczone s od zera


(np. stycze ma numer 0)

day

dzie

hour

godzina (od 0 do 23)

minutes

minuta (od 0 do 59)

seconds

sekunda (od 0 do 59)

int get(int field)

Pobiera warto okrelonego elementu.


Parametry:

field

Calendar.ERA, Calendar.YEAR, Calendar.MONTH,


Calendar.WEEK_OF_YEAR,
Calendar.WEEK_OF_MONTH,
Calendar.DAY_OF_MONTH, Calendar.DAY_OF_YEAR,
Calendar.DAY_OF_WEEK,
Calendar.DAY_OF_WEEK_IN_MONTH,

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

Calendar.AM_PM, Calendar.HOUR,
Calendar.HOUR_OF_DAY,
Calendar.MINUTE, Calendar.SECOND,
Calendar.MILLISECOND,
Calendar.ZONE_OFFSET, Calendar.DST_OFFSET

void set(int field, int value)

Ustawia warto okrelonego elementu.


Parametry:

field

Jedna ze staych przyjmowanych przez metod get

value

Nowa warto

void set(int year, int month, int day)

void set(int year, int month, int day, int hour, int minutes, int seconds)

Ustawia nowe wartoci elementw.


Parametry:

year

rok

month

miesic wartoci liczone s od zera


(np. stycze ma numer 0)

day

dzie

hour

godzina (od 0 do 23)

minutes

minuty (od 0 do 59)

seconds

sekundy (od 0 do 59)

void add(int field, int amount)

Metoda wykonujca dziaania arytmetyczne na datach. Dodaje okrelon ilo


czasu do okrelonego pola czasu. Aby na przykad doda 7 dni do biecej daty
w kalendarzu, naley zastosowa wywoanie: c.add(Calendar.DAY_OF_MONTH, 7).
Parametry:

field

Element, ktry ma by zmodyfikowany


(za pomoc jednej ze staych metody get).

amount

Liczba, o jak ma by zmieniona warto elementu


(moe by ujemna).

int getFirstDayOfWeek()

Pobiera pierwszy dzie tygodnia dla lokalizacji uytkownika, na przykad


Calendar.SUNDAY w USA.

void setTime(Date time)

Ustawia kalendarz na podany moment w czasie.


Parametr:

time

punkt w czasie

Date getTime()

Pobiera punkt w czasie reprezentowany przez aktualn warto obiektu kalendarza.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

147

148

Java. Podstawy
java.text.DateFormatSymbols 1.1

String[] getShortWeekdays()

String[] getShortMonths()

String[] getWeekdays()

String[] getMonths()

Pobiera nazwy dni tygodnia lub miesicy dla obecnej lokalizacji. Jako indeksy
wykorzystuje stae dnia tygodnia i miesica klasy Calendar.

4.3. Definiowanie wasnych klas


W rozdziale 3. pisalimy ju proste klasy. Wszystkie one jednak zawieray tylko jedn metod
main. Teraz nauczysz si pisa klasy z prawdziwego zdarzenia, ktre bdzie mona wykorzysta do bardziej wyszukanych zada. Klasy te z reguy nie maj metody main. W zamian
maj wasne pola i metody. Kompletny program skada si z kilku klas. Jedna z nich musi
zawiera metod main.

4.3.1. Klasa Employee


Konstrukcja najprostszej klasy w Javie wyglda nastpujco:
class NazwaKlasy
{
pole1
pole2
. . .
konstruktor1
konstruktor2
. . .
metoda1
metoda2
. . .
}

Przyjrzyjmy si poniszej, bardzo uproszczonej wersji klasy Employee, ktr mona wykorzysta w firmie do napisania systemu obsugi listy pac.
class Employee
{
// pola
private String name;
private double salary;
private Date hireDay;
// konstruktor
public Employee(String n, double s, int year, int month, int day)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

149

{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
// metoda
public String getName()
{
return name;
}
// kolejne metody
. . .
}

Zanim przejdziemy do dogbnej analizy klasy Employee, pokaemy jej zastosowanie w programie, ktry przedstawia listing 4.2.
Listing 4.2. EmployeeTest/EmployeeTest.java
import java.util.*;
/**
* Ten program sprawdza dziaanie klasy Employee.
* @version 1.11 2004-02-19
* @author Cay Horstmann
*/
public class EmployeeTest
{
public static void main(String[] args)
{
// Wstawienie trzech obiektw pracownikw do tablicy staff.
Employee[] staff = new Employee[3];
staff[0] = new Employee("Jarosaw Rybiski", 75000, 1987, 12, 15);
staff[1] = new Employee("Katarzyna Remiszewska ", 50000, 1989, 10, 1);
staff[2] = new Employee("Krystyna Kuczyska ", 40000, 1990, 3, 15);
// Zwikszenie pensji wszystkich pracownikw o 5%.
for (Employee e : staff)
e.raiseSalary(5);
// Drukowanie informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()
+ ",hireDay="
+ e.getHireDay());
}
}
class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

150

Java. Podstawy
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// W klasie GregorianCalendar stycze ma numer 0.
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}

W programie tym tworzymy tablic o nazwie Employee i wstawiamy do niej trzy obiekty
reprezentujce pracownikw:
Employee[] staff = new Employee[3];
staff[0] = new Employee("Jarosaw Rybiski", . . .);
staff[1] = new Employee("Katarzyna Remiszewska", . . .);
staff[2] = new Employee("Krystyna Kuczyska", . . .);

Nastpnie podnosimy zarobki kadego z pracownikw o 5% za pomoc metody raiseSalary:


for (Employee e : staff)
e.raiseSalary(5);

Ostatecznie drukujemy informacje o kadym pracowniku, wywoujc metody getName,


getSalary i getHireDay:
for (Employee e : staff)
System.out.println("name=" + e.getName()
+ ",salary=" + e.getSalary()
+ ",hireDay=" + e.getHireDay());

Zauwa, e ten przykadowy program skada si z dwch klas: Employee i EmployeeTest ze


specyfikatorem dostpu public. Metoda main zawierajca opisane wczeniej instrukcje znajduje si w klasie EmployeeTest.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

151

Plik rdowy ma nazw EmployeeTest.java, poniewa nazwa pliku musi by taka sama jak
nazwa klasy publicznej. W jednym pliku moe by tylko jedna klasa publiczna i dowolna
liczba klas niepublicznych.
W trakcie kompilacji tego pliku kompilator utworzy dwa pliki klas: EmployeeTest.class
i Employee.class.
Aby uruchomi program, naley interpreterowi kodu bajtowego poda nazw klasy zawierajcej metod main:
java EmployeeTest

Interpreter zaczyna wykonywanie kodu od metody main w klasie EmployeeTest. Program ten
z kolei tworzy trzy nowe obiekty Employee i pokazuje ich stan.

4.3.2. Uywanie wielu plikw rdowych


Program z listingu 4.2 zawiera dwie klasy w jednym pliku. Wielu programistw woli jednak
kad klas umieci w oddzielnym pliku. Na przykad klasa Employee mogaby si znale
w pliku o nazwie Employee.java, a EmployeeTest w pliku EmployeeTest.java.
W przypadku tego stylu programowania s dwa sposoby kompilacji programu. Mona wywoa kompilator Javy z symbolem wieloznacznym:
javac Employee*.java

Wszystkie pliki rdowe pasujce do symbolu wieloznacznego zostan skompilowane do


plikw klas. Mona te po prostu napisa:
javac EmployeeTest.java

Ta druga opcja moe si wydawa nieco zaskakujca, biorc pod uwag, e plik Employee.java
nie jest w ogle jawnie kompilowany. Kiedy kompilator napotyka klas Employee w pliku
EmployeeTest.java, szuka pliku o nazwie Employee.class. Jeli go nie znajdzie, pobiera plik
o nazwie Employee.java i kompiluje go. Kompilator robi nawet co wicej: jeli znacznik
czasu pliku Employee.java jest nowszy ni pliku Employee.class, kompilator automatycznie
dokona jego ponownej kompilacji.
Osoby znajce uniksowe narzdzie make (lub jego odpowiednik w Windowsie, jak
np. nmake), mog traktowa kompilator Javy tak, jakby mia wbudowan funkcjonalno tego narzdzia.

4.3.3. Analiza klasy Employee


Zajmiemy si teraz szczegow analiz klasy Employee. Zaczniemy od jej metod. W kodzie
rdowym wida, e klasa ta ma jeden konstruktor i cztery metody:
public Employee(String n, double s, int year, int month, int day)
public String getName()

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

152

Java. Podstawy
public double getSalary()
public Date getHireDay()
public void raiseSalary(double byPercent)

Wszystkie metody tej klasy s publiczne. Sowo kluczowe public oznacza, e dan metod
moe wywoa kada inna metoda z kadej klasy (cztery dostpne specyfikatory dostpu s
opisane w tym i kolejnym rozdziale).
Dane, ktrymi bdziemy manipulowa w obiekcie klasy Employee, s przechowywane w trzech
polach:
private String name;
private double salary;
private Date hireDay;

Sowo kluczowe private oznacza, e do pl klasy Employee maj dostp tylko metody tej
klasy. adna metoda zewntrzna nie moe odczyta ani zmodyfikowa tych wartoci.
Pola w klasie mona oznaczy sowem kluczowym public, ale nie jest to zalecane.
Ich wartoci mogyby by odczytane i zmodyfikowane z kadego miejsca programu,
a to jest cakowicie sprzeczne z ide hermetyzacji. Kada metoda z kadej klasy moe
zmodyfikowa publiczne pole z naszego dowiadczenia wynika, e tak si dzieje
zawsze w najmniej oczekiwanym momencie. Zalecamy stosowanie zawsze specyfikatora
private dla pl klas.

Zauwamy take, e dwa z pl same s obiektami: pola name i hireDay s referencjami do


obiektw klas String i Date. Jest to typowa sytuacja klasy czsto zawieraj pola typw
innych klas.

4.3.4. Pierwsze kroki w tworzeniu konstruktorw


Przyjrzymy si konstruktorowi klasy Employee:
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}

Jak wida, konstruktor ma tak sam nazw jak klasa. Konstruktor ten dziaa, kiedy tworzony
jest obiekt klasy Employee, i nadaje polom okrelone przez programist pocztkowe wartoci.
Jeli na przykad utworzymy obiekt klasy Employee w poniszy sposb:
new Employee("James Bond", 100000, 1950, 1, 1);

wartoci pl bd nastpujce:
name = "James Bond";
salary = 100000;
hireDay = January 1, 1950;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

153

Midzy konstruktorami a pozostaymi metodami jest zasadnicza rnica. Konstruktor mona wywoa tylko przy uyciu operatora new. Nie mona za pomoc konstruktora zmieni
wartoci pl. Na przykad poniszy zapis spowoduje bd kompilacji:
james.Employee("James Bond", 250000, 1950, 1, 1);

// bd

Wicej informacji na temat konstruktorw znajduje si w dalszej czci tego rozdziau. Na


razie naley zapamita, e:

konstruktor musi mie tak sam nazw jak klasa;

klasa moe mie wicej ni jeden konstruktor;

konstruktor moe przyjmowa zero lub wicej parametrw;

konstruktor nie zwraca wartoci;

konstruktor jest zawsze wywoywany przy uyciu operatora new.

Konstruktory w Javie dziaaj tak samo jak w C++. Nie naley jednak zapomina,
e obiekty w Javie s przechowywane na stercie i e konstruktor musi by wywoywany przy uyciu operatora new. Programici C++ czsto zapominaj o tym operatorze,
programujc w Javie:
Employee number007("James Bond", 100000, 1950, 1, 1);
// C++, nie Java.

Powyszy kod zadziaa w jzyku C++, ale nie w Javie.

Naley pamita, aby nie utworzy zmiennej lokalnej o takiej samej nazwie jak pole
klasy. Na przykad w poniszym kodzie wysoko pensji nie zostanie ustawiona:
public Employee(String n, double s, . . .)
{
String name = n;
// bd
double salary = s;
// bd
. . .
}

Konstruktor deklaruje dwie zmienne lokalne o nazwach name i salary. S one dostpne
wycznie w tym konstruktorze. Przesaniaj pola klasy o takich samych nazwach. Niektrzy programici wliczajc autorw niniejszej ksiki pisz szybciej, ni myl,
poniewa maj nawyk dodawania nazwy typu. Jest to bardzo nieprzyjemny rodzaj bdu,
ktry trudno wytropi. Trzeba pamita, aby nie nada adnej zmiennej w metodzie
takiej samej nazwy jak zmiennej pola klasy.

4.3.5. Parametry jawne i niejawne


Metody dziaaj na obiektach i maj dostp do zmiennych skadowych tych obiektw.
Na przykad ponisza metoda:
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

154

Java. Podstawy
salary += raise;
}

ustawia now warto zmiennej skadowej salary obiektu, na rzecz ktrego zostanie wywoana. Spjrzmy na ponisze wywoanie:
number007.raiseSalary(5);

Spowoduje ono zwikszenie o 5% wartoci zmiennej skadowej number007.salary. A dokadniej powysze wywoanie wykonuje nastpujce instrukcje:
double raise = number007.salary * 5 / 100;
number007.salary += raise;

Metoda raiseSalary pobiera dwa parametry. Pierwszy z nich, zwany parametrem niejawnym
(ang. implicit parameter), to obiekt klasy Employee, ktry znajduje si przed nazw metody.
Drugi parametr, liczba w nawiasach za nazw metody, to parametr jawny (ang. explicit
parameter).
Jak wida, parametry jawne s wypisane w deklaracji metody, na przykad double byPercent.
Parametr niejawny nie pojawia si w deklaracji metody.
Sowo kluczowe this odnosi si we wszystkich metodach do parametru niejawnego. Metod
raiseSalary mona by byo napisa nastpujco:
public void raiseSalary(double byPercent)
{
double raise = this.salary * byPercent / 100;
this.salary += raise;
}

Niektrzy programici wol ten styl pisania kodu, poniewa wyranie odrnia on zmienne
skadowe od zmiennych lokalnych.
W jzyku C++ metody z reguy deklaruje si poza klas:
void Employee::raiseSalary(double byPercent)
{
. . .
}

// C++, nie Java.

Metoda zdefiniowana w klasie staje si automatycznie metod wstawian (ang. inline


method).
class Employee
{
. . .
int getName() { return name; }
}

// inline w C++

W Javie wszystkie metody s zdefiniowane w klasie. Nie s jednak z tego powodu metodami wstawianymi. Znalezienie okazji do wstawienia treci metody jest zadaniem maszyny
wirtualnej. Kompilator JIT szuka wywoa krtkich metod, ktre s czsto uywane, ale
nie s przesaniane, i optymalizuje je.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

155

4.3.6. Korzyci z hermetyzacji


Przyjrzyjmy si teraz uwaniej niezbyt skomplikowanym metodom getName, getSalary
i getHireDay:
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}

S to oczywicie przykady metod akcesora. Poniewa zwracaj wartoci skadowych obiektw, czasami nazywane s metodami dostpu do pl (ang. field accessor).
Czy nie byoby prociej, gdyby pola name, salary i hireDay byy publiczne? Wtedy nie
trzeba by byo tworzy dla nich oddzielnych metod.
Chodzi o to, e pole name jest tylko do odczytu. Po ustawieniu jego wartoci za pomoc konstruktora nie ma sposobu, aby t warto zmieni. W ten sposb zyskujemy gwarancj, e
pole name nigdy nie zostanie uszkodzone.
Pole salary nie jest tylko do odczytu, ale jego warto mona zmieni wycznie przy uyciu
metody raiseSalary. Jeli warto tego pola jest nieprawidowa, wiadomo, e trzeba poszuka bdu w tej metodzie. Gdyby pole salary byo publiczne, rdo problemw z nim mogoby si znajdowa wszdzie.
Czasami konieczne jest pobranie i ustawienie wartoci skadowej obiektu. Do tego celu potrzebne
s trzy rzeczy:

prywatne pole,

publiczna metoda akcesora,

publiczna metoda mutatora.

To oznacza wicej pracy ni w przypadku utworzenia publicznego pola danych, ale metoda
ta ma te zalety.
Po pierwsze, przy wprowadzaniu zmian wewntrz implementacji wystarczy tylko zmiana
w kodzie metod klasy. Jeli na przykad sposb przechowywania nazwisk zmieni si na
nastpujcy:
String firstName;
String lastName;

metod getName mona zmodyfikowa w taki sposb, aby zwracaa:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

156

Java. Podstawy
firstName + " " + lastName

Zmiana ta jest niewidoczna dla reszty programu.


Oczywicie metody akcesora i mutatora mog mie duo pracy z konwersj ze starej reprezentacji danych na now. Prowadzi to jednak do drugiej korzyci: metody mutatora mog
sprawdza bdy, podczas gdy kod, ktry po prostu przypisuje warto polu, takiej moliwoci nie ma. Na przykad metoda setSalary moe pilnowa, aby pensja pracownika nie bya
nisza od zera.
Pamitaj, aby nie pisa metod akcesora, ktre zwracaj referencje do obiektw
zmienialnych. Zamalimy t zasad w klasie Employee, w ktrej metoda getHireDay
zwraca obiekt klasy Date:
class Employee
{
private Date hireDay;
. . .
public Date getHireDay()
{
return hireDay;
}
. . .
}

W ten sposb amiemy zasad hermetyzacji! Spjrzmy na poniszy niesforny fragment kodu:
Employee harry = . . .;
Date d = harry.getHireDay();
double tenYearsInMilliSeconds = 10 * 365.25 * 24 * 60 * 60 * 1000;
d.setTime(d.getTime() - (long) tenYearsInMilliSeconds);
// Dodajmy Harryemu 10 lat stau pracy.

Powd nie jest oczywisty. Zarwno zmienna d, jak i harry.hireDay odwouj si do tego
samego obiektu (zobacz rysunek 4.5). Wywoanie metody mutatora na rzecz obiektu d
automatycznie powoduje modyfikacj prywatnego stanu obiektu klasy Employee!
Jeli konieczne jest zwrcenie referencji do modyfikowalnego obiektu, naley najpierw
sklonowa ten obiekt. Klon jest wiern kopi obiektu i jest przechowywany w osobnej
lokalizacji. Szczegowy opis technik klonowania znajduje si w rozdziale 6. Poniej znajduje
si poprawny kod:
class Employee
{
. . .
public Date getHireDay()
{
return hireDay.clone();
}
. . .
}

Naley pamita, aby do tworzenia kopii modyfikowalnych obiektw uywa metody clone.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

157

Rysunek 4.5. Zwracanie referencji do zmienialnej skadowej

4.3.7. Przywileje klasowe


Wiadomo, e kada metoda ma dostp do prywatnych danych obiektu, na rzecz ktrego zostaa
wywoana. Jednak dla wielu osb zaskakujce jest to, e kada metoda ma dostp do prywatnych danych wszystkich obiektw swojej klasy. Wemy na przykad metod equals,
za pomoc ktrej porwnamy dwch pracownikw:
class Employee
{
. . .
boolean equals(Employee other)
{
return name.equals(other.name);
}
}

Typowe wywoanie tej metody wyglda nastpujco:


if (harry.equals(boss)) . . .

Metoda ta ma dostp do prywatnych skadowych obiektu harry, co nie jest adnym zaskoczeniem. Uzyskuje jednak te dostp do prywatnych skadowych obiektu boss. Jest to dozwolone, poniewa obiekt boss jest typu Employee, a metody klasy Employee maj dostp do prywatnych pl wszystkich obiektw tej klasy.
W C++ obowizuje ta sama zasada. Kada metoda danej klasy ma dostp do
prywatnych skadowych tej samej klasy, nie tylko parametru niejawnego.

4.3.8. Metody prywatne


Implementujc klas, wszystkie jej pola oznaczamy sowem kluczowym private, poniewa
dane publiczne mog by niebezpieczne. Ale co z metodami? Podczas gdy w wikszoci
metody s publiczne, w okrelonych warunkach uywa si metod prywatnych. Czasami

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

158

Java. Podstawy
korzystne moe by rozbicie kodu wykonujcego obliczenia na kilka osobnych metod
pomocniczych. Zazwyczaj nie powinny one wchodzi w skad interfejsu publicznego
mog by zbyt blisko biecej implementacji lub wymaga specjalnego protokou albo specjalnej kolejnoci wywoywania. Takie metody najlepiej implementowa jako prywatne.
Aby utworzy prywatn metod, naley zamiast sowa kluczowego public uy sowa private.
Jeli metoda jest prywatna, nie ma obowizku dba o jej dostpno w przypadku zmiany
implementacji. Po wprowadzeniu zmian w sposobie reprezentacji danych jej implementacja
moe si okaza trudniejsza lub zupenie niepotrzebna. Chodzi o to, e jeli metoda jest
prywatna, wiadomo, e nikt jej nie uywa poza klas, i mona si jej bez obawy pozby.
Jeli metoda jest publiczna, nie mona jej usun, poniewa moe z niej korzysta jaki inny
fragment programu.

4.3.9. Stae jako pola klasy


W deklaracji pola klasy mona uy sowa kluczowego final. Tego typu pole musi by
zainicjowane przy tworzeniu obiektu. To znaczy, e przed zakoczeniem dziaania konstruktora warto takiego pola musi zosta ustawiona. Po utworzeniu obiektu warto tej skadowej nie moe by zmieniana. Na przykad pole name klasy Employee mona zadeklarowa
przy uyciu sowa kluczowego final, poniewa jego warto po utworzeniu obiektu nigdy
si nie zmienia nie ma metody setName.
class Employee
{
. . .
private final String name;
}

Modyfikator final jest szczeglnie przydatny do deklaracji pl o typach podstawowych lub


klas niezmiennych (ang. immutable class) klasa niezmienna to taka, ktrej metody nie
zmieniaj stanu swoich obiektw. Przykadem takiej klasy jest klasa String. Modyfikator
final zastosowany do pl klasy zmiennej (ang. mutable class) moe wprowadza w bd.
Na przykad zapis:
private final Date hiredate;

oznacza tylko, e referencja do obiektu przechowywana w zmiennej hiredate nie moe si


zmieni po utworzeniu tego obiektu. Nie oznacza to, e obiekt hiredate jest stay. Kada
metoda moe wywoa mutator setTime na rzecz obiektu, do ktrego odwouje si zmienna
hiredate.

4.4. Pola i metody statyczne


We wszystkich ogldanych do tej pory przykadach metoda main jest opatrzona modyfikatorem static. Nadszed czas na wyjanienie, do czego suy ten modyfikator.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

159

4.4.1. Pola statyczne


W klasie moe by tylko jeden egzemplarz danego pola, ktre jest okrelone jako statyczne.
W przeciwiestwie do tego kady obiekt ma swoj wasn kopi kadego pola klasy. Zamy
na przykad, e kademu pracownikowi chcemy przypisa unikalny numer identyfikacyjny.
W tym celu dodajemy do klasy Employee pole niestatyczne id i pole statyczne nextId:
class Employee
{
private static int nextId = 1;

private int id;


. . .

Kady obiekt klasy Employee bdzie mia wasne pole id, ale pole nextId bdzie wspdzielone
przez wszystkie obiekty tej klasy. Innymi sowy, jeli zostanie utworzonych 1000 obiektw
klasy Employee, powstanie 1000 skadowych obiektu o nazwie id po jednej dla kadego
obiektu, ale pole statyczne o nazwie nextId bdzie tylko jedno. Pole to bdzie istniao, nawet
jeli nie bdzie ani jednego obiektu klasy Employee. Pole to naley do klasy, a nie do konkretnego obiektu.
W niektrych obiektowych jzykach programowania pola statyczne s nazywane
polami klasowymi (ang. class field). Termin statyczny jest niezbyt udan pozostaoci po jzyku C++.

Przeanalizujmy implementacj prostej metody:


public void setId()
{
id = nextId;
nextId++;
}

Ustawmy numer identyfikacyjny pracownika dla obiektu harry:


harry.setId();

Pole id obiektu harry zostaje ustawione na aktualn warto pola statycznego nextId, po
czym warto tego pola jest zwikszana o 1:
harry.id = Employee.nextId;
Employee.nextId++;

4.4.2. Stae statyczne


Zmienne statyczne spotyka si dosy rzadko. Znacznie czciej zdarzaj si stae statyczne.
Na przykad w klasie Math znajduje si definicja poniszej staej statycznej:
public class Math
{
. . .

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

160

Java. Podstawy

public static final double PI = 3.14159265358979323846;


. . .

Dostp do tej staej mona uzyska, piszc Math.PI.


Gdyby sowo kluczowe static zostao pominite, PI byoby zwykym polem klasy Math.
To znaczy, e dostp do niego prowadziby poprzez obiekt klasy Math i kady obiekt tej
klasy miaby skadow PI.
Inn czsto uywan sta statyczn jest System.out. Jej deklaracja w klasie System wyglda
nastpujco:
public class System
{
. . .
public static final PrintStream out = . . .;
. . .
}

Wielokrotnie ju sygnalizowalimy, e nie naley deklarowa pl jako publicznych, poniewa kady moe je zmodyfikowa. Natomiast nie mamy nic przeciwko staym publicznym
(tzn. polom opatrzonych sowem kluczowym final). Ze wzgldu na to, e out to staa, nie
mona przypisa do niej innego strumienia:
System.out = new PrintStream(. . .);

// Bd out to staa.

W klasie System dostpna jest metoda setOut, ktra umoliwia ustawienie staej
System.out na inny strumie. Jak to moliwe? Metoda setOut jest metod rodzim,
ktrej implementacja zostaa napisana w innym ni Java jzyku programowania. Metody
rodzime mog obchodzi mechanizmy kontrolne jzyka Java. Rozwizanie to jest jednak
bardzo rzadko stosowane i nie naley si nim posugiwa.

4.4.3. Metody statyczne


Metody statyczne nie dziaaj na obiektach. Przykadem takiej metody jest metoda pow
dostpna w klasie Math. Wyraenie:
Math.pow(x, a)

oblicza warto dziaania xa. Nie jest do tego potrzebny aden obiekt klasy Math. Innymi
sowy, nie ma parametru niejawnego.
Metody statyczne mona zapamita jako takie, ktre nie maj parametru this (w metodzie
niestatycznej parametr this odwouje si do parametru niejawnego metody zobacz podrozdzia 4.3.5, Parametry jawne i niejawne).
Poniewa metody statyczne nie dziaaj na obiektach, za ich pomoc nie mona operowa
na skadowych obiektw. Maj natomiast dostp do pl statycznych swoich klas. Poniej
znajduje si przykad takiej metody statycznej:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

161

public static int getNextId()


{
return nextId;
// Zwraca warto pola statycznego.
}

Wywoanie tej metody wymaga podania nazwy jej klasy:


int n = Employee.getNextId();

Czy mona w tej metodzie pomin sowo kluczowe static? Tak, ale wtedy do jej wywoania potrzebna by bya referencja do obiektu typu Employee.
Do wywoania metody statycznej mona uy obiektu. Jeli na przykad harry jest
obiektem klasy Employee, zamiast wywoania Employee.getNextId() trzeba by byo
uy harry.getNextId(). Taki sposb zapisu wydaje nam si mylcy. Metoda getNextId
w ogle nie bierze pod uwag obiektu harry przy obliczaniu wyniku. Zalecamy wywoywanie metod statycznych przy uyciu nazwy klasy, a nie nazwy obiektu.

Metody statyczne maj dwojakie zastosowanie:

kiedy metoda nie wymaga dostpu do stanu obiektu, poniewa wszystkie potrzebne
jej parametry s dostarczane w postaci parametrw jawnych (na przykad Math.pow);

kiedy metoda potrzebuje dostpu tylko do pl statycznych (na przykad


Employee.getNextId).

Pola i metody statyczne w Javie maj takie same przeznaczenie jak w jzyku C++.
Rnica pomidzy tymi jzykami polega w tym przypadku na skadni. W C++ dostp
do pola statycznego lub metody statycznej poza jej zakresem uzyskuje si przy uyciu
operatora ::, np. Math::PI.
Termin statyczny (ang. static) ma ciekaw histori. Zosta on po raz pierwszy uyty
w jzyku C do okrelenia zmiennej lokalnej, ktra nie znikaa po wyjciu z bloku. Wtedy
nazwa ta miaa sens zmienna pozostawaa w pamici i bya dostpna po ponownym
wejciu do bloku. Drugie znaczenie sowa kluczowego static dotyczyo zmiennych i funkcji
globalnych, do ktrych nie byo dostpu z innych plikw. Powodem uycia tego sowa bya
ch uniknicia wprowadzania nowego sowa kluczowego. W kocu w jzyku C++ sowo
static zyskao swoje trzecie znaczenie oznacza zmienne i funkcje, ktre nale do danej
klasy, ale nie nale do jej obiektw. To samo znaczenie ma niniejsze sowo w Javie.

4.4.4. Metody fabryczne


Oto jeszcze jeden czsto spotykany sposb uycia metod statycznych. Klasa NumberFormat
tworzy obiekty formatujce dla rnych stylw formatowania przy uyciu metod fabrycznych (ang. factory method).
NumberFormat currencyFormatter = NumberFormat.getCurrencyInstance();
NumberFormat percentFormatter = NumberFormat.getPercentInstance();
double x = 0.1;
System.out.println(currencyFormatter.format(x));
// Drukuje 0.10 dol.
System.out.println(percentFormatter.format(x));
// Drukuje 10%

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

162

Java. Podstawy
Dlaczego klasa NumberFormat nie wykorzystuje konstruktora? S ku temu dwa powody:

Konstruktorom nie mona zmienia nazw. Konstruktor zawsze nazywa si tak samo
jak klasa. W tym przypadku jednak byy potrzebne dwie nazwy dla przypadku
stylu walutowego (currency) i procentowego (percent).

Typ obiektu tworzonego za pomoc konstruktora nie moe by zmieniony. Natomiast


metody fabryczne zwracaj obiekty klasy DecimalPoint podklasy, ktra dziedziczy
po klasie NumberFormat (wicej informacji na temat dziedziczenia znajduje si
w rozdziale 5.).

4.4.5. Metoda main


Zauwamy, e metody statyczne mona wywoywa, nie majc adnych obiektw. W ten
sposb na przykad wywoujemy metod Math.pow.
Z tego wanie powodu metoda main jest statyczna.
public class Application
{
public static void main(String[] args)
{
// Konstruowanie obiektw.
. . .
}
}

Metoda main nie dziaa na adnym obiekcie kiedy program rozpoczyna dziaanie, nie ma
w nim jeszcze adnych obiektw. Jej zadaniem jest tworzenie i uruchamianie obiektw
wymaganych przez program.
Listing 4.3 przedstawia prost wersj klasy Employee zawierajc pole statyczne nextId
i metod statyczn getNextId. Program ten wstawia do tablicy trzy obiekty typu Employee
i drukuje informacje o reprezentowanych przez nie pracownikach. Na kocu drukuje kolejny
numer identyfikacyjny, aby zademonstrowa dziaanie metody statycznej.
Listing 4.3. StaticTest/StaticTest.java
/**
* Ten program demonstruje uycie metod statycznych.
* @version 1.01 2004-02-19
* @author Cay Horstmann
*/
public class StaticTest
{
public static void main(String[] args)
{
// Wstawienie do tablicy staff trzech obiektw reprezentujcych pracownikw.
Employee[] staff = new Employee[3];
staff[0] = new Employee("Tomasz", 40000);
staff[1] = new Employee("Dariusz", 60000);
staff[2] = new Employee("Grzegorz", 65000);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

Kada klasa moe zawiera metod main, co jest bardzo przydatne przy przeprowadzaniu testw jednostkowych klas. Moemy na przykad wstawi metod main do
klasy Employee:
class Employee
{
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
. . .
public static void main(String[] args)
// test jednostkowy
{
Employee e = new Employee("Romeo", 50000, 2003, 3, 31);
e.raiseSalary(10);
System.out.println(e.getName() + " " + e.getSalary());
}
. . .
}

Aby przetestowa dziaanie klasy Employee w odosobnieniu, naley uy nastpujcego


polecenia:
java Employee

Jeli klasa Employee wchodzi w skad wikszej aplikacji, uruchomienie tego programu
za pomoc poniszego polecenia:
java Aplikacja

spowoduje, e metoda main klasy Employee nie zostanie wykonana.


// Drukowanie informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
{
e.setId();
System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary="
+ e.getSalary());
}
int n = Employee.getNextId();
// Wywoanie metody statycznej.
System.out.println("Nastpny dostpny identyfikator=" + n);
}
}
class Employee
{
private static int nextId = 1;
private String name;
private double salary;
private int id;
public Employee(String n, double s)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

163

164

Java. Podstawy
name = n;
salary = s;
id = 0;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public int getId()
{
return id;
}
public void setId()
{
id = nextId;
nextId++;
}
public static int getNextId()
{
return nextId;
}

// Ustawienie identyfikatora na kolejny dostpny numer.

// Zwrcenie pola statycznego.

public static void main(String[] args) // test jednostkowy


{
Employee e = new Employee("Grzegorz", 50000);
System.out.println(e.getName() + " " + e.getSalary());
}
}

Klasa Employee zawiera statyczn metod main przeznaczon do testw jednostkowych.


Aby uruchomi obie metody main, naley uy poniszych polece:
java Employee

i
java StaticTest

4.5. Parametry metod


Zaczniemy od przegldu terminw opisujcych sposoby przekazywania parametrw do metod
(lub funkcji) w rnych jzykach programowania. Termin wywoanie przez warto (ang.
call by value) oznacza, e metoda odbiera tylko warto dostarczon przez wywoujcego.
Natomiast wywoanie przez referencj (ang. call by reference) oznacza, e metoda odbiera

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

165

lokalizacj zmiennej dostarczonej przez wywoujcego. W zwizku z tym metoda moe zmodyfikowa warto zmiennej przekazanej przez referencj, ale nie moe tego zrobi ze zmienn
przekazan przez warto. Okrelenia wywoanie przez s standardowo uywane w terminologii programistycznej do opisu parametrw metod i dotycz nie tylko Javy; jest jeszcze jeden termin tego typu wywoanie przez nazw (ang. call by name), ale ma on ju
tylko znaczenie historyczne, poniewa by stosowany w jzyku Algol jednym z najstarszych jzykw programowania wysokiego poziomu.
W Javie zawsze stosowane s wywoania przez warto. Oznacza to, e metoda otrzymuje
kopi wartoci wszystkich parametrw, a wic nie moe zmodyfikowa wartoci przekazanych do niej zmiennych.
Przeanalizujmy na przykad ponisze wywoanie:
double percent = 10;
harry.raiseSalary(percent);

Bez wzgldu na to, jaka jest implementacja tej metody, wiadomo, e po jej wywoaniu warto
zmiennej percent nadal bdzie wynosia 10.
Przyjrzyjmy si tej sytuacji nieco uwaniej. Niech nasza metoda sprbuje potroi warto
swojego parametru:
public static void tripleValue(double x)
{
x = 3 * x;
}

// nie dziaa

Wywoajmy t metod:
double percent = 10;
tripleValue(percent);

To jednak nie dziaa. Po wywoaniu metody warto zmiennej percent nadal wynosi 10.
Oto opis zdarze:
1.

Zmienna x jest inicjowana kopi wartoci zmiennej percent (tzn. 10).

2. Warto zmiennej x jest potrojona teraz wynosi 30. Ale zmienna percent
ma nadal warto 10 (zobacz rysunek 4.6).
3. Metoda koczy dziaanie, a zmienna x nie jest ju uywana.

S jednak dwa rodzaje parametrw metod:

typy podstawowe (liczby i wartoci logiczne),

referencje do obiektw.

Wiemy ju, e metoda nie moe zmieni wartoci parametru typu podstawowego. Z parametrami obiektowymi jest inaczej. Mona z atwoci utworzy metod, ktra potraja pensj
pracownika:
public static void tripleSalary(Employee x)
{
x.raiseSalary(200);
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

// dziaa

166

Java. Podstawy

Rysunek 4.6.
Modyfikacja
parametru
liczbowego
nie ma staego
efektu

Po uruchomieniu poniszego kodu:


harry = new Employee(. . .);
tripleSalary(harry);

maj miejsce nastpujce zdarzenia:


1.

Zmienna x jest inicjowana kopi wartoci obiektu harry, to znaczy referencj


do obiektu.

2. Metoda raiseSalary jest wywoywana na rzecz tej referencji. Pensja obiektu


klasy Employee, do ktrego odwouje si zarwno zmienna x, jak i harry,

jest zwikszana o 200 procent.


3. Metoda koczy dziaanie i zmienna x nie jest dalej uywana. Oczywicie zmienna
obiektowa harry nadal odwouje si do obiektu, ktrego pensja zostaa potrojona

(zobacz rysunek 4.7).


Jak wida, implementacja metody, ktra zmienia stan parametru w postaci obiektu, jest atw
i czsto stosowan metod programowania. Prostota bierze si std, e metoda odbiera kopi
referencji do obiektu i zarwno orygina, jak i kopia odwouj si do tego samego obiektu.
W wielu jzykach programowania (zwaszcza w C++ i Pascalu) parametry mona przekazywa
do metod na dwa sposoby: za pomoc wywoania przez warto i przez referencj. Niektrzy
programici (i niestety niektrzy autorzy ksiek) uwaaj, e w Javie dla obiektw stosowane jest wywoanie przez referencj. To nieprawda. Poniewa to bdne przekonanie jest
bardzo powszechne, warto szczegowo przeanalizowa przeciwny do niego przykad.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

167

Rysunek 4.7. Modyfikacja parametru obiektowego ma stay efekt

Sprbujemy napisa metod zamieniajc dwa obiekty klasy Employee:


public static void swap(Employee x, Employee y)
{
Employee temp = x;
x = y;
y = temp;
}

// nie dziaa

Gdyby w Javie stosowane byy wywoania przez referencj dla obiektw, ta metoda dziaaaby:
Employee a = new Employee("Alicja", . . .);
Employee b = new Employee("Bartosz", . . .);
swap(a, b);
// Czy a odwouje si teraz do Bartosza, czy Alicji?

Jednak metoda ta nie zmienia referencji do obiektw przechowywanych w zmiennych a i b.


Parametry x i y metody swap s inicjowane kopiami tych referencji. Nastpnie metoda
przystpuje do zamiany tych kopii.
// x odwouje si do Alicji, a y do Bartosza.
Employee temp = x;
x = y;
y = temp;
// Teraz x odwouje si do Bartosza, a y do Alicji.

Wysiek ten idzie jednak na marne. Zmienne parametrowe x i y wychodz z uycia po zakoczeniu metody. Oryginalne zmienne a i b nadal odwouj si do tych samych obiektw, do
ktrych odwoyway si przed wywoaniem metody (zobacz rysunek 4.8).
Powyszy opis problemu stanowi dowd na to, e jzyk programowania Java nie uywa
wywoa przez referencj dla obiektw. W zamian referencje do obiektw s przekazywane przez warto.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

168

Java. Podstawy

Rysunek 4.8. Zamiana parametrw obiektowych nie ma trwaego rezultatu

Oto zestawienie zasad dotyczcych tego, co mona, a czego nie mona robi z parametrami
metod w Javie:

Metoda nie moe zmodyfikowa parametru typu podstawowego (czyli bdcego


liczb lub wartoci logiczn).

Metoda moe zmieni stan obiektu przekazanego jako parametr.

Metoda nie moe sprawi, aby parametr obiektowy zacz si odwoywa


do nowego obiektu.

Powysze twierdzenia prezentuje program z listingu 4.4. Najpierw prbuje potroi warto
parametru liczbowego, co koczy si niepowodzeniem:
Testowanie tripleValue:
Przed: percent=10.0
Koniec metody: x=30.0
Po: percent=10.0

Nastpnie udaje si potroi pensj pracownika:


Testowanie tripleSalary:
Przed: salary=50000.0
Koniec metody: salary=150000.0
Po: salary=150000.0

Po zakoczeniu dziaania metody stan obiektu, do ktrego odwouje si zmienna harry, jest
zmieniony. Jest to moliwe dziki temu, e metoda ta zmodyfikowaa stan obiektu poprzez
kopi referencji do niego.
Na zakoczenie program prezentuje niepowodzenie metody swap:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

Testowanie swap:
Przed: a=Alicja
Przed: b=Grzegorz
Koniec metody: x=Grzegorz
Koniec metody: y=Alicja
Po: a=Alicja
Po: b=Grzegorz

Jak wida, parametry x i y zostay zamienione, ale zmienne a i b pozostay bez zmian.
W C++ moliwe jest zarwno wywoanie przez warto, jak i referencj. Parametry
bdce referencjami oznaczane s symbolem &. Na przykad metody void triple
Value(double& x) czy void swap(Employee& x, Employee& y), ktre modyfikuj swoje
parametry, mona z atwoci zaimplementowa.
Listing 4.4. ParamTest/ParamTest.java
/**
* Ten program demonstruje przekazywanie parametrw w Javie.
* @version 1.00 2000-01-27
* @author Cay Horstmann
*/
public class ParamTest
{
public static void main(String[] args)
{
/*
* Test 1. Metody nie mog modyfikowa parametrw liczbowych.
*/
System.out.println("Testowanie tripleValue:");
double percent = 10;
System.out.println("Przed: percent=" + percent);
tripleValue(percent);
System.out.println("Po: percent=" + percent);
/*
* Test 2. Metody mog zmienia stan parametrw bdcych obiektami.
*/
System.out.println("\nTestowanie tripleSalary:");
Employee harry = new Employee("Grzegorz", 50000);
System.out.println("Przed: salary=" + harry.getSalary());
tripleSalary(harry);
System.out.println("Po: salary=" + harry.getSalary());
/*
* Test 3. Metody nie mog dodawa nowych obiektw do parametrw obiektowych.
*/
System.out.println("\nTestowanie swap:");
Employee a = new Employee("Alicja", 70000);
Employee b = new Employee("Grzegorz", 60000);
System.out.println("Przed: a=" + a.getName());
System.out.println("Przed: b=" + b.getName());
swap(a, b);
System.out.println("Po: a=" + a.getName());
System.out.println("Po: b=" + b.getName());

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

169

170

Java. Podstawy
}
public static void tripleValue(double x)
// nie dziaa
{
x = 3 * x;
System.out.println("Koniec metody: x=" + x);
}
public static void tripleSalary(Employee x)
// dziaa
{
x.raiseSalary(200);
System.out.println("Koniec metody: salary=" + x.getSalary());
}
public static void swap(Employee x, Employee y)
{
Employee temp = x;
x = y;
y = temp;
System.out.println("Koniec metody: x=" + x.getName());
System.out.println("Koniec metody: y=" + y.getName());
}
}
class Employee
// Uproszczona klasa Employee.
{
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

171

4.6. Konstruowanie obiektw


Umiemy ju pisa proste konstruktory definiujce pocztkowy stan obiektw. Poniewa jednak obiekty s niezwykle wanym elementem jzyka, ich konstrukcj wspiera kilka rnych
mechanizmw. Opisujemy je w poniszych podrozdziaach.

4.6.1. Przecianie
Przypomnijmy, e klasa GregorianCalendar miaa wicej ni jeden konstruktor. Do wyboru
byy dwa:
GregorianCalendar today = new GregorianCalendar();

lub
GregorianCalendar deadline = new GregorianCalendar(2099, Calendar.DECEMBER, 31);

Taka sytuacja nazywa si przecianiem. Przecianie to sytuacja, w ktrej kilka metod ma


tak sam nazw (w tym przypadku konstruktor GregorianCalendar), ale rne parametry.
Kompilator musi zdecydowa, ktr wersj wywoa. Decyzj podejmuje na podstawie dopasowania typw parametrw w nagwkach rnych metod do typw wartoci przekazanych
w konkretnym wywoaniu. Jeli niemoliwe jest dopasowanie parametrw lub istnieje wicej
ni jedno dopasowanie, wystpuje bd kompilacji (proces ten nazywa si rozstrzyganiem
przeciania ang. overloading resolution).
W Javie mona przeciy dowoln metod. W zwizku z tym peny opis metody
skada si z nazwy i typw argumentw. Informacje te nazywane s sygnatur
metody. Na przykad klasa String zawiera cztery metody publiczne o nazwie indexOf.
Oto ich sygnatury:
indexOf(int)
indexOf(int, int)
indexOf(String)
indexOf(String, int)

Okrelenie typu zwrotnego nie wchodzi w skad sygnatury metody. Oznacza to, e nie
mona utworzy dwch metod o takich samych nazwach i typach parametrw, ale rnych typach zwrotnych.

4.6.2. Inicjacja pl wartociami domylnymi


Jeli warto pola nie zostanie jawnie ustawiona w konstruktorze, pole to automatycznie przyjmie warto domyln pola typw liczbowych s ustawiane na 0, wartoci logicznych na
false, a referencji do obiektw na null. Taki styl programowania jest jednak uwaany za
niewaciwy. Z pewnoci kod taki jest trudniejszy do zrozumienia, jeli pola s inicjowane
niewidocznie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

172

Java. Podstawy

Na tym polega podstawowa rnica pomidzy polami i zmiennymi lokalnymi.


Zmienne lokalne musz by jawnie inicjowane w metodzie. Natomiast jeli pole
klasy nie zostanie zainicjowane, zostanie automatycznie ustawione na warto domyln
(0, false lub null).

Wemy jako przykad klas Employee. Wyobramy sobie, e nie okrelilimy w konstruktorze sposobu inicjacji niektrych jej pl. Domylnie pole salary miaoby warto 0, a pola
name i hireDay miayby wartoci null.
Nie jest to jednak dobre rozwizanie, poniewa w wyniku wywoania metody getName lub
getHireDay otrzymalibymy warto null, ktrej raczej nie oczekiwalibymy:
Date h = harry.getHireDay();
calendar.setTime(h);
// Jeli h ma warto null, zostanie zgoszony wyjtek.

4.6.3. Konstruktor bezargumentowy


Wiele klas zawiera konstruktor bezargumentowy tworzcy obiekty o okrelonym domylnym
stanie pocztkowym. Poniej znajduje si przykadowy konstruktor domylny klasy Employee:
public Employee()
{
name = "";
salary = 0;
hireDay = new Date();
}

Konstruktor domylny jest stosowany, w przypadku gdy programista nie utworzy adnego
konstruktora. Konstruktor ten ustawia wszystkie pola na wartoci domylne. W zwizku
z tym wszystkie dane liczbowe bdce skadowymi obiektu miayby warto 0, wartoci
logiczne byyby ustawione na false, a zmienne obiektowe na null.
Jeli klasa ma przynajmniej jeden konstruktor, ale nie ma konstruktora domylnego, nie mona
tworzy jej obiektw bez podania odpowiednich parametrw konstrukcyjnych. Na przykad
pierwsza wersja klasy Employee na listingu 4.2 zawieraa jeden konstruktor:
Employee(String name, double salary, int y, int m, int d)

W przypadku tej klasy utworzenie obiektu z wartociami domylnymi nie byoby moliwe.
To znaczy, e ponisze wywoanie spowodowaoby bd:
e = new Employee();

4.6.4. Jawna inicjacja pl


Dziki moliwoci przeciania konstruktorw pocztkowy stan obiektu klasy moe by
ustawiany na wiele sposobw. Bez wzgldu na wywoywany konstruktor nigdy nie zaszkodzi, jeli kada skadowa obiektu bdzie miaa jak sensown warto.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

173

Naley pamita, e konstruktor domylny jest dostpny tylko wtedy, gdy klasa
nie ma adnego innego konstruktora. Aby umoliwi tworzenie obiektw klasy,
ktra ma ju konstruktor, za pomoc widocznego poniej wywoania:
new NazwaKlasy()

trzeba dostarczy konstruktor domylny (bezparametrowy). Oczywicie, jeli wartoci


wszystkich pl mog by domylne, mona napisa nastpujcy konstruktor:
public NazwaKlasy()
{
}

Wystarczy przypisa kademu polu w definicji klasy jak warto. Na przykad:


class Employee
{
. . .
private String name = "";
}

To przypisanie nastpuje przed wywoaniem konstruktora. Skadnia ta jest szczeglnie przydatna, jeli wszystkie konstruktory klasy musz ustawi okrelon skadow na t sam warto.
Warto inicjujca nie musi by staa. W poniszym przykadzie pole jest inicjowane wywoaniem metody. Wemy pod uwag klas Employee, w ktrej kady pracownik ma swj identyfikator id. Pole to moe by inicjowane nastpujco:
class Employee
{
private static int nextId;
private int id = assignId();
. . .
private static int assignId()
{
int r = nextId;
nextId++;
return r;
}
. . .
}

W C++ nie mona bezporednio zainicjowa pl klasy. Wszystkie pola musz by


ustawione w konstruktorze. W jzyku tym jednak mona posugiwa si specjaln skadni w postaci listy inicjujcej:
Employee::Employee(String n, double s, int y, int m, int d)
: name(n),
salary(s),
hireDay(y, m, d)
{
}

// C++

W Javie skadnia taka nie jest potrzebna, poniewa obiekty nie maj podobiektw, tylko
wskaniki do innych obiektw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

174

Java. Podstawy

4.6.5. Nazywanie parametrw


Przy pisaniu bardzo prostych konstruktorw (a pisze si ich bardzo duo) problemem moe
si okaza wymylanie nazw dla parametrw.
My z reguy jestemy za stosowaniem nazw jednoliterowych:
public Employee(String n, double s)
{
name = n;
salary = s;
}

Wad tej metody jest to, e stosowane w niej nazwy nic nie mwi o przeznaczeniu parametrw.
Niektrzy programici przed nazw kadego parametru stawiaj przedrostek a:
public Employee(String aName, double aSalary)
{
name = aName;
salary = aSalary;
}

Jest to cakiem dobre rozwizanie. Ju na pierwszy rzut oka wiadomo, jakie jest przeznaczenie
kadego z parametrw.
Inna czsto stosowana sztuczka wykorzystuje fakt, e zmienne parametryczne przesaniaj
skadowe obiektw o tej samej nazwie. Jeli na przykad zostanie wywoany parametr o nazwie
salary, to salary bdzie si odnosi do parametru, a nie skadowej obiektu. Aby uzyska dostp
do tej skadowej, trzeba wtedy napisa this.salary. Przypomnijmy sobie, e this oznacza
parametr niejawny, to znaczy obiekt, ktry jest konstruowany. Poniej znajduje si przykad:
public Employee(String name, double salary)
{
this.name = name;
this.salary = salary;
}

W jzyku C++ czsto stosowan praktyk jest poprzedzanie nazw skadowych


obiektw znakiem podkrelenia lub ustalon liter (czsto wybr pada na litery
m i x). Na przykad pole salary mogoby mie nazw _salary, mSalary lub xSalary.
Technika ta jest mao rozpowszechniona wrd programistw Javy.

4.6.6. Wywoywanie innego konstruktora


Sowo kluczowe this odwouje si do parametru niejawnego metody. Ma ono jednak jeszcze jedno zastosowanie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

175

Jeli pierwsza instrukcja konstruktora ma posta this(), to konstruktor ten wywouje


inny konstruktor tej samej klasy. Oto typowy przykad takiej sytuacji:
public Employee(double s)
{
// Wywouje Employee(String, double)
this("Employee #" + nextId, s);
nextId++;
}

Kiedy wywoamy new Employee(6000), konstruktor Employee(double) wywoa konstruktor


Employee(String, double).
Sowo kluczowe uyte w takim przypadku jest bardzo przydatne. Wsplny kod konstruktorw
wystarczy napisa tylko jeden raz.
Referencja this w Javie jest identyczna ze wskanikiem this w C++. Jednak
w tym drugim jzyku jeden konstruktor nie moe wywoa innego konstruktora.
Aby wydzieli wsplny kod inicjujcy w C++, konieczne jest napisanie osobnej metody.

4.6.7. Bloki inicjujce


Znamy ju dwa sposoby inicjacji pl danych:

poprzez ustawienie wartoci w konstruktorze;

poprzez przypisanie wartoci w deklaracji.

Jest jeszcze trzeci sposb polegajcy na zastosowaniu bloku inicjujcego. W deklaracji


klasy mog si znajdowa dowolne bloki kodu. Zawarte w nich instrukcje s wykonywane
za kadym razem, gdy konstruowany jest obiekt danej klasy. Na przykad:
class Employee
{
private static int nextId;
private int id;
private String name;
private double salary;
// Blok inicjujcy obiektu.
{
id = nextId;
nextId++;
}
public Employee(String n, double s)
{
name = n;
salary = s;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

176

Java. Podstawy
public Employee()
{
name = "";
salary = 0;
}
. . .
}

Najpierw zostanie zainicjowane pole id w bloku inicjujcym, bez wzgldu na to, ktry
konstruktor zostanie wywoany. Blok inicjujcy jest wykonywany na pocztku, a po nim
instrukcje zawarte w konstruktorze.
Sposb ten nigdy nie jest niezbdny i nie jest zbyt powszechnie stosowany. Zazwyczaj prociej jest umieci kod inicjujcy wewntrz konstruktora.
W bloku inicjujcym mona ustawia wartoci pl, mimo e ich definicje znajduj
si dopiero w dalszej czci klasy. Aby jednak unikn cyklicznych definicji, nie
mona odczytywa wartoci pl, ktre s inicjowane pniej. Zasady te zostay szczegowo opisane w sekcji 8.3.2.3 specyfikacji jzyka Java (http://docs.oracle.com/
javase/specs). Poniewa reguy te s na tyle skomplikowane, e nawet programici
kompilatorw mieli z nimi problemy wczesne wersje Javy zawieray subtelne bdy
w ich implementacji zalecamy umieszczanie blokw inicjujcych za definicjami pl.

Zastosowanie wszystkich moliwych sposobw inicjacji pl danych moe ujemnie wpyn


na czytelno kodu. Kiedy wywoywany jest konstruktor, maj miejsce nastpujce zdarzenia:
1.

Wszystkie pola s inicjowane wartociami domylnymi (0, false lub null).

2. Wszystkie inicjatory i bloki inicjujce s wykonywane w takiej kolejnoci,

w jakiej znajduj si w klasie.


3. Jeli w pierwszym wierszu konstruktora znajduje si wywoanie innego konstruktora,

wykonywane s instrukcje innego konstruktora.


4. Wykonywane jest ciao konstruktora.

Oczywicie dobrze jest tak zorganizowa swj kod inicjujcy, aby inny programista mg
go bez wikszych problemw zrozumie. Na przykad klasa, w ktrej konstruktory s uzalenione od kolejnoci deklaracji pl danych, byaby nieco dziwna i podatna na bdy.
Pole statyczne mona zainicjowa, podajc warto pocztkow lub korzystajc ze statycznego bloku inicjujcego. Pierwszy z tych sposobw ju znamy:
private static int nextId = 1;

Jeli inicjacja pl statycznych klasy odbywa si za pomoc bardziej zoonego kodu, mona
si posuy statycznym blokiem inicjujcym.
Kod naley umieci w bloku opatrzonym etykiet static. Oto przykad: chcemy, aby numery
identyfikacyjne pracownikw zaczynay si od losowej liczby cakowitej mniejszej od 10 000.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

177

// statyczny blok inicjujcy


static
{
Random generator = new Random();
nextId = generator.nextInt(10000);
}

Inicjacja statyczna nastpuje w chwili pierwszego zaadowania klasy. Pola statyczne, podobnie
jak zmienne skadowe, przybieraj wartoci domylne 0, false lub null, jeli nie zostan im
nadane jawnie inne wartoci. Inicjatory pl statycznych i statyczne bloki inicjujce s wykonywane w takiej kolejnoci, w jakiej znajduj si w deklaracji klasy.
Oto czarodziejska sztuczka w jzyku Java, dziki ktrej zaimponujesz swoim
wsppracownikom. Mona napisa program Witaj, wiecie! bez metody main.
public class Hello
{
static
{
System.out.println("Witaj, wiecie");
}
}

W wyniku wywoania powyszej klasy za pomoc polecenia java Hello statyczny blok
inicjujcy wydrukuje napis Witaj, wiecie, a potem pojawi si paskudny komunikat
o bdzie informujcy o braku metody main. Mona unikn tego poajania, umieszczajc
na kocu bloku inicjujcego wywoanie System.exit(0).

Program na listingu 4.5 demonstruje w praktyce zagadnienia, ktre zostay omwione w tym
podrozdziale:

przecianie konstruktorw,

wywoanie innego konstruktora za pomoc sowa kluczowego this(),

konstruktor domylny,

zastosowanie bloku inicjujcego obiektw,

statyczny blok inicjujcy,

inicjacja zmiennych skadowych.

Listing 4.5. ConstructorTest/ConstructorTest.java


import java.util.*;
/**
* Ten program demonstruje techniki konstrukcji obiektw.
* @version 1.01 2004-02-19
* @author Cay Horstmann
*/
public class ConstructorTest
{
public static void main(String[] args)
{
// Wstawienie do tablic staff trzech obiektw klasy Employee.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

178

Java. Podstawy
Employee[] staff = new Employee[3];
staff[0] = new Employee("Hubert", 40000);
staff[1] = new Employee(60000);
staff[2] = new Employee();
// Wydruk informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",id=" + e.getId() + ",salary="
+ e.getSalary());
}
}
class Employee
{
private static int nextId;
private int id;
private String name = "";
private double salary;

// Inicjacja zmiennej skadowej obiektu.

// Statyczny blok inicjujcy.


static
{
Random generator = new Random();
// Ustawienie zmiennej nextId na losow liczb cakowit z przedziau 0 9999.
nextId = generator.nextInt(10000);
}
// Blok inicjujcy obiektw.
{
id = nextId;
nextId++;
}
// Trzy konstruktory przecione.
public Employee(String n, double s)
{
name = n;
salary = s;
}
public Employee(double s)
{
// Wywoanie konstruktora Employee(String, double).
this("Employee #" + nextId, s);
}
// Konstruktor domylny.
public Employee()
{
// Zmienna name zainicjowana wartoci "" patrz niej.
// Zmienna salary nie jest jawnie ustawiona inicjacja wartoci 0.
// Zmienna id jest inicjowana w bloku inicjujcym.
}
public String getName()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

179

return name;

public double getSalary()


{
return salary;
}

public int getId()


{
return id;
}

java.util.Random 1.0

Random()

Tworzy nowy generator liczb losowych.

int nextInt(int n) 1.2

Zwraca losow liczb z przedziau od 0 do n-1.

4.6.8. Niszczenie obiektw i metoda finalize


W niektrych jzykach programowania, zwaszcza w C++, dostpne s tak zwane destruktory. Metody te wykonuj pewne operacje porzdkowe, kiedy dany obiekt wyjdzie z uytku.
Ich najczstsz czynnoci jest przywracanie pamici przydzielonej obiektom. Poniewa
w Javie zastosowano mechanizm automatycznego usuwania nieuytkw, nie trzeba tego robi
rcznie. Dlatego w jzyku Java nie ma destruktorw.
Oczywicie niektre obiekty korzystaj z innych zasobw ni pami, jak np. pliki lub uchwyty
do innych obiektw, ktre wykorzystuj zasoby systemowe. W takiej sytuacji koniecznie
trzeba zwrci do ponownego uytku wykorzystywany zasb, kiedy przestanie by potrzebny.
Do kadej klasy mona doda metod finalize. Jest ona wywoywana przed usuniciem
obiektu przez system zbierania nieuytkw. Nie naley jednak polega na metodzie finalize
do przywracania zasobw, ktrych jest mao, poniewa nigdy nie wiadomo, kiedy nastpi jej
wywoanie.
Wywoanie metody System.runFinalizersOnExit(true) gwarantuje, e metody finalizujce zostan wywoane przed zakoczeniem programu. Metoda ta nie jest jednak bezpieczna i jej stosowanie nie jest zalecane. Alternatywne rozwizanie polega na
wykonaniu pewnych czynnoci w momencie zamknicia programu za pomoc metody
Runtime.addShutdownHook szczegowe informacje na ten temat mona znale
w dokumentacji API.

Jeli dany zasb musi by zamknity natychmiast po zakoczeniu jego uywania, trzeba
o to zadba we wasnym zakresie. Do tego celu suy metoda close, ktr programista

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

180

Java. Podstawy
wywouje w celu skasowania okrelonych zasobw i gdy skoczy prac z obiektem. W czci
11.2.4, Instrukcja try z zasobami, dowiesz si, jak sprawi, aby metoda ta bya wywoywana automatycznie.

4.7. Pakiety
Wygodnym sposobem na organizacj pracy i oddzielenie wasnych klas od pozostaych jest
umieszczanie klas w tak zwanych pakietach (ang. packages).
Standardowa biblioteka Javy skada si z wielu pakietw, do ktrych nale java.lang,
java.util, java.net itd. Standardowe pakiety Javy maj struktur hierarchiczn. Mona je
umieszcza jedne w drugich, podobnie jak w przypadku katalogw i podkatalogw. Wszystkie
standardowe pakiety Javy znajduj si w pakietach java i javax.
Gwnym powodem stosowania pakietw jest ch uniknicia kolizji nazw klas. Przypumy,
e dwch niezalenych programistw wpadnie na wietny pomys napisania klasy o nazwie
Employee. Dopki obie te klasy znajduj si w osobnych pakietach, nie ma adnego konfliktu.
Aby zachowa unikatowo nazw pakietw, firma Sun zaleca stosowanie w tych nazwach
odwrconych domen internetowych (ktre s unikatowe). W obrbie takiego pakietu mona
nastpnie tworzy kolejne podpakiety. Na przykad jeden z programistw zarejestrowa
domen horstmann.com. Po odwrceniu otrzymujemy com.horstmann. Pakiet ten mona nastpnie podzieli na podpakiety o nazwach typu com.horstmann.corejava.
Kompilator nie rozpoznaje adnych powiza pomidzy pakietami i podpakietami. Na przykad pakiety java.util i java.util.jar nie maj ze sob nic wsplnego. Kady z nich jest
niezalenym pakietem klas.

4.7.1. Importowanie klas


Kada klasa moe uywa wszystkich klas ze swojego pakietu i wszystkich klas publicznych z innych pakietw.
Dostp do klasy publicznej z innego pakietu mona uzyska na dwa sposoby. Pierwszy z nich
polega na dodaniu penej nazwy pakietu przed nazw kadej klasy. Na przykad:
java.util.Date today = new java.util.Date();

Ta metoda jest oczywicie bardzo pracochonna. Prostszy i czciej stosowany sposb polega
na uyciu instrukcji import. Instrukcja ta umoliwia stosowanie skrconego zapisu odwoa
do klas w pakiecie. Dziki jej zastosowaniu nie trzeba pisa penych nazw klas.
Mona zaimportowa cay pakiet lub tylko jedn klas, a instrukcje import powinny si
znajdowa na samym pocztku pliku rdowego (ale pod instrukcjami package). Na przykad ponisza instrukcja importuje wszystkie klasy znajdujce si w pakiecie java.util:
import java.util.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

181

Dziki temu w zapisie:


Date today = new Date();

nie jest potrzebny przedrostek okrelajcy pakiet. Mona take zaimportowa tylko okrelon
klas z pakietu:
import java.util.Date;

Zapis java.util.* jest prostszy i nie wywiera adnego wpywu na rozmiar kodu. Jednak
dziki importowi poszczeglnych klas oddzielnie osoba czytajca kod moe si atwiej
zorientowa, ktre klasy s w uyciu.
W edytorze Eclipse dostpna jest opcja Organize Imports, ktr mona znale
w menu Source. Po jej uyciu takie importy pakietw jak java.util.* s automatycznie zastpowane list importw konkretnych klas, np.:
import java.util.ArrayList;
import java.util.Date;

Funkcja ta jest niezwykle przydatnym narzdziem.

Naley jednak pamita, e symbolu * mona uy do importu tylko jednego pakietu. Zapis
java.* lub java.*.* do importu wszystkich pakietw z przedrostkiem java jest niedozwolony.
W wikszoci przypadkw programista nie interesuje si zbytnio importowanymi pakietami.
Jedyna sytuacja, w ktrej taka uwaga jest potrzebna, wystpuje wtedy, gdy dochodzi do konfliktu nazw. Na przykad zarwno pakiet java.util, jak i java.sql maj klas Date.
Wyobramy sobie, e napisalimy program importujcy oba te pakiety.
import java.util.*;
import java.sql.*;

Uycie klasy Date w takiej sytuacji spowoduje bd kompilacji:


Date today;

// Bd--java.util.Date czy java.sql.Date?

Kompilator nie wie, ktrej klasy o nazwie Date uy. Problem ten mona rozwiza, dodajc
instrukcj importu konkretnej klasy:
import java.util.*;
import java.sql.*;
import java.util.Date;

Co zrobi w sytuacji, w ktrej potrzebne s obie te klasy? Konieczne jest uywanie za kadym razem penej nazwy pakietu z nazw klasy.
java.util.Date deadline = new java.util.Date();
java.sql.Date today = new java.sql.Date(...);

Lokalizacja klas w pakietach naley do kompilatora. Kod bajtowy w plikach klas zawsze
zawiera pene nazwy pakietw w odwoaniach do innych klas.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

182

Java. Podstawy

Programici jzyka C++ czsto myl instrukcj import z dyrektyw #include. Nie
maj one ze sob nic wsplnego. W C++ konieczne jest uycie dyrektywy #include,
aby doczy zewntrzne deklaracje, poniewa kompilator C++ nie przeszukuje adnych
plikw z wyjtkiem tego, ktry kompiluje, i doczonych plikw nagwkowych. Kompilator
Java otworzy kady plik, jeli wskae mu si jego lokalizacj.
W Javie mona cakowicie pomin mechanizm import, ale to wymagaoby podawania
penych nazw klas, jak java.util.Date. W jzyku C++ dyrektyw #include nie da si
unikn.
Jedyna korzy, jaka pynie z uywania instrukcji import, to wygoda. Mona si odwoa
do klasy za pomoc nazwy, ktra jest krtsza ni pena nazwa pakietu. Na przykad po
dodaniu instrukcji import java.util.* (albo import.java.util.Date) do klasy java.
util.Date mona odwoywa si, piszc tylko Date.
Odpowiednikiem pakietw w jzyku C++ s przestrzenie nazw. Instrukcje Javy package
i import mona traktowa jako odpowiedniki dyrektyw namespace i using w C++.

4.7.2. Importy statyczne


W Java SE 5.0 wprowadzono moliwo importowania za pomoc instrukcji import metod
i pl statycznych, nie tylko klas.
Jeli na przykad na pocztku pliku rdowego zostanie wstawiona ponisza instrukcja:
import static java.lang.System.*;

metod i pl statycznych klasy System bdzie mona uywa bez przedrostka w postaci nazwy
tej klasy:
out.println("egnaj, wiecie!");
exit(0);

// tj. System.out
// tj. System.exit

Mona te zaimportowa konkretn metod lub pole:


import static java.lang.System.out;

Wtpliwe jest, aby programici chcieli skraca nazwy System.out i System.exit, poniewa
wtedy kod byby mniej przejrzysty. Z drugiej strony kod:
sqrt(pow(x, 2) + pow(y, 2))

wydaje si bardziej przejrzysty ni:


Math.sqrt(Math.pow(x, 2) + Math.pow(y, 2))

4.7.3. Dodawanie klasy do pakietu


Aby umieci klas w pakiecie, naley na pocztku pliku rdowego, przed kodem definiujcym klasy w tym pakiecie, umieci nazw wybranego pakietu. Na przykad pocztek
pliku Employee.java z listingu 4.7 wyglda nastpujco:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

183

package com.horstmann.corejava;
public class Employee
{
. . .
}

Jeli na pocztku pliku nie ma instrukcji package, klasy znajdujce si w tym pliku nale
do pakietu domylnego (ang. default package). Pakiet domylny nie ma nazwy. Wszystkie
prezentowane do tej pory klasy naleay do tego pakietu.
Pliki rdowe naley umieszcza w podkatalogu odpowiadajcym penej nazwie pakietu.
Na przykad wszystkie pliki pakietu com.horstmann.corejava powinny si znale w podkatalogu com/horstmann/corejava (w systemie Windows com\horstmann\corejava). Kompilator umieszcza pliki klas w takiej samej strukturze katalogw.
Program na listingach 4.6 i 4.7 jest rozoony na dwa pakiety: klasa PackageTest naley do
pakietu domylnego, a klasa Employee do pakietu com.horstmann.corejava. W zwizku z tym
plik Employee.java musi si znajdowa w podkatalogu com/horstmann/corejava. Struktura
katalogw jest nastpujca:
. (katalog bazowy)
PackageTest.java
PackageTest.class
com/
horstmann/
corejava/
Employee.java
Employee.class

Aby skompilowa ten program, naley przej do katalogu bazowego i wykona polecenie:
javac PackageTest.java

Kompilator automatycznie odnajdzie plik com/horstmann/corejava/Employee.java i skompiluje go.


Przeanalizujmy bardziej realistyczny przykad, w ktrym nie ma pakietu domylnego,
a klasy s rozoone na kilka rnych pakietw (com.horstmann.corejava i com.mycompany).
. (katalog bazowy)
com/
horstmann/
corejava/
Employee.java
Employee.class
mycompany/
PayrollApp.java
PayrollApp.class

W tym przypadku kompilacj i uruchomienie klas naley przeprowadzi w katalogu bazowym, czyli tym, ktry zawiera katalog com:
javac com/mycompany/PayrollApp.java
java com.mycompany.PayrollApp

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

184

Java. Podstawy
Zauwa, e kompilator dziaa na plikach (z rozszerzeniem .java), podczas gdy interpreter
Javy uruchamia klasy.

Listing 4.6. PackageTest/PackageTest.java


import com.horstmann.corejava.*;
// W powyszym pakiecie znajduje si definicja klasy Employee.
import static java.lang.System.*;
/**
* Ten program demonstruje uycie pakietw.
* @author cay
* @version 1.11 2004-02-19
* @author Cay Horstmann
*/
public class PackageTest
{
public static void main(String[] args)
{
// Dziki instrukcji import nie ma koniecznoci stosowania penej nazwy
// com.horstmann.corejava.Employee.
Employee harry = new Employee("Hubert Kowalski", 50000, 1989, 10, 1);
harry.raiseSalary(5);

// Dziki instrukcji import static nie ma koniecznoci pisa System.out.


out.println("name=" + harry.getName() + ",salary=" + harry.getSalary());

Listing 4.7. com.horstmann.corejava/Employee.java


package com.horstmann.corejava;
// Klasy znajdujce si w tym pliku nale do powyszego pakietu.
import java.util.*;
// Instrukcje import nastpuj po instrukcji package.
/**
* @version 1.10 1999-12-18
* @author Cay Horstmann
*/
public class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
// W klasie GregorianCalendar stycze ma numer 0.
hireDay = calendar.getTime();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

185

}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}

Od nastpnego rozdziau przykady kodu bdziemy umieszcza w pakietach. Dziki


temu bdzie mona utworzy projekt w IDE dla kadego rozdziau zamiast dla kadego podrozdziau.

Kompilator nie sprawdza struktury katalogw w czasie kompilacji plikw rdowych. Jeli mamy na przykad plik rdowy, na pocztku ktrego znajduje si ponisza dyrektywa:
package com.mycompany;

moemy go skompilowa nawet poza podkatalogiem com/mycompany. Plik ten zostanie


skompilowany bezbdnie, jeli nie jest uzaleniony od adnych innych pakietw. Tak
powstaego programu nie da si jednak uruchomi. Maszyna wirtualna nie odnajdzie
powstaych klas, kiedy sprbujemy uruchomi ten program.

4.7.4. Zasig pakietw


Wiemy ju, do czego su modyfikatory dostpu public i private. Obiekty oznaczone pierwszym z tych dwch modyfikatorw s dostpne we wszystkich klasach, a drugim tylko
w klasie, w ktrej s zdefiniowane. Jeli nie ma adnego modyfikatora dostpu, obiekt (tzn.
klasa, metoda lub zmienna) jest dostpny dla wszystkich metod w pakiecie.
Przypomnijmy sobie program z listingu 4.2. Klasa Employee nie jest tam zdefiniowana jako
publiczna. W zwizku z tym dostp do niej maj tylko inne klasy (np. EmployeeTest) znajdujce si w tym samym pakiecie (tu domylnym). W przypadku klas to domylne dziaanie jest
korzystne, ale jeli chodzi o zmienne, to wybr ten nie by trafny. Zmienne musz by jawnie

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

186

Java. Podstawy
oznaczone jako private, gdy w przeciwnym przypadku bd widoczne w caym pakiecie.
To oczywicie oznacza amanie zasad hermetyzacji. Niestety bardzo atwo mona zapomnie
o wpisaniu sowa kluczowego private. Poniszy przykad pochodzi z klasy Window dostpnej w pakiecie java.awt wchodzcym w skad JDK:
public class Window extends Container
{
String warningString;
. . .
}

Naley zauway, e zmienna warningString nie jest prywatna! Oznacza to, e metody wszystkich klas z pakietu java.awt maj do niej dostp i mog modyfikowa jej warto (np. ustawi
na acuch Zaufaj mi!). Ze zmiennej tej korzystaj jednak tylko metody nalece do klasy
Window, w zwizku z czym najlepszym rozwizaniem byoby oznaczy j sowem kluczowym
private. Prawdopodobnie programista pisa kod w popiechu i najzwyczajniej zapomnia
o modyfikatorze dostpu (nie podajemy nazwiska tego programisty kady moe sobie
sam zajrze do omawianego pliku rdowego).
Zadziwiajce jest to, e nigdy nie naprawiono tego bdu, mimo i pisalimy o nim
we wszystkich dziewiciu wydaniach tej ksiki. Najwidoczniej osoby zajmujce
si implementacj Javy nie czytaj naszych publikacji. Co wicej, w klasie przybyo z czasem sporo nowych pl, ale tylko okoo poowa z nich ma modyfikator dostpu private.

Czy jest to jaki problem? To zaley, poniewa pakiety nie s zamknitymi jednostkami.
Oznacza to, e kady moe doda do pakietu swoje klasy. Oczywicie zoliwi lub niedouczeni programici mog napisa kod, ktry bdzie modyfikowa zmienne o zasigu pakietowym. Na przykad w pierwszych wersjach Javy mona byo z atwoci przemyci dodatkow klas do pakietu java.awt. Wystarczyo na pocztku jej kodu napisa:
package java.awt;

Nastpnie plik z tak klas trzeba byo umieci w podkatalogu java.awt na ciece klas i ju
dostp do wntrznoci pakietu java.awt stawa otworem. Ta sztuczka umoliwiaa ustawienie acucha z ostrzeeniem (zobacz rysunek 4.9).
Rysunek 4.9.
Zmiana acucha
ostrzeenia
w oknie apletu

W JDK 1.2 wprowadzono zmiany w mechanizmie adujcym klasy (ang. class loader), aby
jawnie zabrania adowania klas uytkownika, ktrych pakiety maj nazwy zaczynajce si
od sowa java. Oczywicie klasy niestandardowe z tej ochrony nie skorzystaj. W zamian
udostpniono mechanizm piecztowania pakietw (ang. package sealing), ktrego zadaniem

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

187

jest rozwizanie problemw z rnymi sposobami dostpu do pakietw. Jeli pakiet jest zapiecztowany, nie mona do niego doda adnych nowych klas. W rozdziale 10. opisujemy techniki tworzenia plikw JAR zawierajcych pakiety zapiecztowane.

4.8. cieka klas


Jak wiadomo, klasy s przechowywane w podkatalogach systemu plikw. cieka do klasy
musi odpowiada nazwie jej pakietu.
Pliki klas mona take przechowywa w plikach JAR (ang. Java archive). Plik JAR zawiera
skompresowane pliki klas i katalogi oraz pozwala zaoszczdzi miejsce i polepszy wydajno. Wikszo niestandardowych bibliotek uywanych w programach ma posta co najmniej
jednego pliku JAR. W JDK take jest dostpna pewna liczba takich plikw, np. jre/lib/rt.jar,
ktry zawiera tysice klas bibliotecznych. Techniki tworzenia plikw JAR zostay opisane
w rozdziale 10.
Struktura plikw JAR jest zgodna z formatem ZIP. W zwizku z tym do pliku rt.jar
i kadego innego o takim rozszerzeniu mona zajrze przy uyciu dowolnego narzdzia ZIP.

Aby mc uywa okrelonych klas w rnych programach, naley wykona nastpujce


czynnoci:
1.

Umie pliki klas w wybranym katalogu, na przykad /home/user/classdir. Pamitaj,


e jest to katalog bazowy drzewa pakietu. Jeli dodasz klas com.horstmann.
corejava.Employee, plik klasy Employee.class musi si znale w podkatalogu
/home/user/classdir/com/horstmann/corejava.

2. Umie wszystkie pliki JAR w wybranym katalogu, na przykad /home/user/archives.


3. Ustaw ciek klas. cieka klas to zbir lokalizacji, ktre mog zawiera pliki klas.

W systemie UNIX poszczeglne elementy cieki klas s rozdzielane dwukropkiem:


/home/user/classdir:.:/home/user/archives/archive.jar

W systemie Windows separatorem jest rednik:


c:\classdir;.;c:\archives\archive.jar

W obu przypadkach kropka oznacza biecy katalog.


Niniejsza cieka zawiera nastpujce elementy:

katalog bazowy home/user/classdir lub c:\classdir;

biecy katalog (.);

plik JAR /home/user/archives/archive.jar lub c:\archives/archive.jar.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

188

Java. Podstawy
Od Java SE 6 do okrelenia katalogu z plikami JAR mona uy symbolu wieloznacznego:
/home/user/classdir:.:/home/user/archives/'*'

lub
c:\classdir;.;c:\archives\*

W systemie UNIX trzeba zastosowa symbol zastpczy dla *, aby unikn rozwinicia
powoki.
Wszystkie pliki JAR (ale nie .class) znajdujce si w katalogu archives s dodawane do
cieki klas.
Pliki biblioteczne wykonawcze (plik rt.jar i inne pliki JAR, ktre znajduj si w katalogach
jre/lib i jre/lib/ext) s zawsze przeszukiwane w celu znalezienia klas. Nie dodaje si ich
bezporednio do cieki klas.
Kompilator javac zawsze szuka plikw w biecym katalogu, natomiast program
uruchamiajcy Java Virtual Machine przeszukuje biecy katalog tylko wtedy, kiedy
w ciece klas znajduje si katalog .. Problemu nie ma, jeli cieka klas nie zostaa
ustawiona, poniewa wtedy zawiera tylko katalog .. Jeli cieka klas zostanie ustawiona bez katalogu ., programy bd kompiloway si bez problemw, ale nie bd
chciay dziaa.

cieka klas zawiera list wszystkich katalogw i plikw JAR, od ktrych naley zacz
szukanie klas. Przeanalizujmy nasz przykadow ciek klas:
/home/user/classdir:.:/home/user/archives/archive.jar

Przypumy, e maszyna wirtualna szuka pliku klasy com.horstmann.corejava.Employee.


Szukanie zaczyna od systemowych plikw klas, ktre znajduj si w archiwach w katalogach
jre/lib i jre/lib/ext. Tam wspomnianej klasy nie ma, wic kontynuuje szukanie na ciece
klas. Poszukiwane s nastpujce pliki:

/home/user/classdir/com/horstmann/corejava/Employee.class,

com/horstmann/corejava/Employee.class, zaczynajc od biecego katalogu,

com/horstmann/corejava/Employee.class wewntrz pliku /home/user/archives/


archive.jar.

Kompilator ma trudniejsze zadanie dotyczce wyszukiwania plikw ni maszyna wirtualna.


Jeli odwoamy si do klasy, nie podajc jej pakietu, kompilator musi najpierw znale pakiet,
ktry t klas zawiera. W tym celu sprawdza wszystkie dyrektywy import, ktre s potencjalnym rdem klas. Wyobramy sobie na przykad, e plik rdowy zawiera ponisze
dyrektywy:
import java.util.*;
import com.horstmann.corejava.*;

a w kodzie rdowym uyto klasy Employee. Najpierw kompilator prbuje kolejno znale
klasy java.lang.Employee (poniewa pakiet java.lang jest zawsze importowany domylnie), java.util.Employee, com.horstmann.Employee i Employee w biecym pakiecie. Szuka

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

189

wszystkich tych klas we wszystkich lokalizacjach wymienionych na ciece klas. Jeli kompilator znajdzie wicej ni jedn z tych klas, zgasza bd kompilacji (poniewa klasy musz
by unikatowe, kolejno instrukcji import nie ma znaczenia).
Kompilator idzie nawet o krok dalej i sprawdza, czy plik rdowy klasy nie jest nowszy ni
skompilowany plik tej klasy. Jeli plik rdowy jest nowszy, jest on automatycznie kompilowany jeszcze raz. Przypomnijmy, e z innych pakietw mona importowa tylko klasy
publiczne. Jeden plik rdowy moe zawiera tylko jedn klas publiczn, a nazwa tego pliku
i nazwa zawartej w nim klasy publicznej musz si ze sob zgadza. Dziki temu kompilator
nie ma problemw z lokalizacj plikw rdowych klas publicznych. Klasy niepubliczne
mona importowa z biecego pakietu. Ich definicje mog si znajdowa w plikach o innych
nazwach. Jeli zostanie zaimportowana klasa z biecego pakietu, kompilator przeszuka
wszystkie pliki rdowe znajdujce si w tym pakiecie w celu znalezienia definicji tej klasy.

4.8.1. Ustawianie cieki klas


ciek klas najlepiej ustawia za pomoc opcji -classpath (lub -cp):
java -classpath /home/user/classdir:.:/home/user/archives/archive.jar MyProg.java

lub
java -classpath c:\classdir;.;c:\archives\archive.jar MyProg.java

Cae polecenie musi si znajdowa w jednym wierszu. Dugie polecenia tego typu najlepiej
wstawia do skryptw powoki lub plikw wsadowych.
Ustawianie cieki za pomoc opcji -classpath jest metod preferowan, ale nie jedyn. Inny
sposb polega na ustawieniu zmiennej rodowiskowej CLASSPATH. Dokadna procedura zaley
od konkretnej powoki. W powoce Bourne Again (bash) naley uy nastpujcego polecenia:
export CLASSPATH=/home/user/classdir:.:/home/user/archives/archive.jar

W powoce C:
setenv CLASSPATH /home/user/classdir:.:/home/user/archives/archive.jar

W systemie Windows:
set CLASSPATH=c:\classdir;.;c:\archives\archive.jar

cieka klas jest dostpna do wyjcia z powoki.


S osoby, ktre zalecaj ustawienie zmiennej rodowiskowej CLASSPATH na stae.
Oglnie nie jest to dobry pomys. Ludzie czsto zapominaj o ustawieniach globalnych, a potem dziwi si, e maj problemy z adowaniem klas. Szczeglnie nagannym
przykadem jest w tym przypadku instalator dla systemu Windows programu QuickTime
firmy Apple. Ustawia on globalnie zmienn CLASSPATH na plik JAR, ktrego uywa, ale
nie dodaje biecego katalogu do cieki klas. Z tego powodu mnstwo programistw
stracio troch nerww, kiedy ich programy przechodziy kompilacj, ale nie mona byo
ich uruchomi.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

190

Java. Podstawy

Niektrzy zalecaj cakowite pominicie cieki klas poprzez umieszczenie wszystkich plikw JAR w katalogu jre/lib/ext. Jest to bardzo za rada, i to z dwch powodw. Archiwa rcznie adujce inne klasy nie dziaaj poprawnie, kiedy znajduj si
w katalogu rozszerze (wicej informacji na temat programw adujcych klasy znajduje
si w rozdziale 9. drugiego tomu). Ponadto programici czsto zapominaj o plikach, ktre
umiecili tam kilka miesicy wczeniej. Potem zachodz w gow, czemu loader klas
ignoruje ich wspaniae klasy, podczas gdy ten po prostu aduje dawno zapomniane klasy
z katalogu rozszerze.

4.9. Komentarze dokumentacyjne


JDK zawiera bardzo przydatne narzdzie o nazwie javadoc, ktre generuje dokumentacj
w formie plikw HTML z plikw rdowych. Dokumentacja API opisana w rozdziale 3.
powstaa w wyniku uruchomienia narzdzia javadoc na kodzie rdowym standardowej
biblioteki Javy.
Profesjonaln dokumentacj moe stworzy kady, kto doda do kodu rdowego komentarze
zaczynajce si od specjalnej sekwencji znakw /**. Jest to bardzo wygodne rozwizanie,
poniewa umoliwia przechowywanie kodu i dokumentacji do niego w jednym miejscu. Jeli
kod i dokumentacja znajduj si w osobnych plikach, z czasem mog si pojawi midzy
nimi rozbienoci. Jednak dziki temu, e komentarze dokumentacyjne znajduj si w tym
samym pliku co kod rdowy, aktualizacja obu jest znacznie uatwiona.

4.9.1. Wstawianie komentarzy


Narzdzie javadoc pobiera informacje dotyczce nastpujcych elementw:

pakietw,

klas i interfejsw publicznych,

publicznych i chronionych (protected) metod i konstruktorw,

pl publicznych i chronionych.

Znaczenie sowa kluczowego protected zostao opisane w rozdziale 5., a interfejsy w rozdziale 6.
Kada z wymienionych konstrukcji moe (i powinna) by opatrzona komentarzem. Komentarz
powinien si znajdowa bezporednio nad tym, czego dotyczy. Pocztek komentarza okrela
sekwencja znakw /**, a koniec */.
W komentarzu mona umieci dowolny tekst oraz specjalne znaczniki dokumentacyjne.
Znaczniki dokumentacyjne rozpoczynaj si od znaku @, np. @author czy @param.
Pierwsze zdanie komentarza powinno by streszczeniem. Narzdzie javadoc automatycznie
generuje strony streszcze zawierajce te zdania.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

191

W tekcie komentarza mona uywa znacznikw HTML, takich jak <em></em> (sucy
do emfazy), <code></code> (sucy do oznaczania fragmentw kodu), <strong></strong>
(dajcy silne wyrnienie) czy nawet <img /> (do wstawiania obrazw). Powinno si jednak unika nagwkw <h1> i poziomych kresek <hr>, poniewa mog wchodzi w interakcje z formatowaniem dokumentu.
Jeli w komentarzach znajduj si odnoniki do innych plikw, takich jak obrazy
(mog to by wykresy lub rysunki przedstawiajce elementy interfejsu uytkownika),
naley pliki te umieci w podkatalogu folderu zawierajcego plik rdowy o nazwie
doc-files. Narzdzie javadoc kopiuje katalogi doc-files i ich zawarto z katalogw rdowych do katalogw dokumentacji. Nazwa katalogu doc-files musi si znale w ciece
odnonika, np. <img src="doc-files/uml.png" alt="Diagram UML" />.

4.9.2. Komentarze do klas


Komentarz klasy musi si znajdowa za instrukcjami import, bezporednio przed definicj
klasy.
Przykad komentarza do klasy:
/**
* Obiekt <code>Card</code> reprezentuje kart do gry, np.
* dama kier. Karta ma kolor (karo, kier, trefl lub pik)
* i warto (1 = as, 2 . . . 10, 11 = walet,
* 12 = dama, 13 = krl)
*/
public class Card
{
. . .
}

Znak * nie musi si znajdowa na pocztku kadej linijki. Na przykad poniszy


komentarz jest tak samo poprawny:
/**
Obiekt <code>Card</code> reprezentuje kart do gry, np.
dama kier. Karta ma kolor (karo, kier, trefl lub pik)
i warto (1 = as, 2 . . . 10, 11 = walet,
12 = dama, 13 = krl)
*/

Jednak wikszo IDE automatycznie dodaje gwiazdki i zmienia ich ustawienie w odpowiedzi na zmiany w amaniu wierszy.

4.9.3. Komentarze do metod


Komentarz do metody musi si znajdowa bezporednio przed metod, ktrej dotyczy. Poza
znacznikami oglnego przeznaczenia mona stosowa dodatkowe znaczniki:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

192

Java. Podstawy

@param opis zmiennej

Dodaje pozycj do sekcji Parameters metody. Opis moe zajmowa kilka wierszy
i zawiera znaczniki HTML. Wszystkie znaczniki @param dotyczce jednej metody
powinny si znajdowa w jednym miejscu.

@return opis

Dodaje sekcj Returns. Opis moe zajmowa kilka wierszy i zawiera


znaczniki HTML.

@throws opis klasy

Dodaje informacj, e dana metoda moe spowodowa wyjtek. Wyjtki


s tematem rozdziau 11.
Przykad komentarza do metody:
/**
* Podnosi pensj pracownika.
* @param byPercent warto okrelajca, o ile procent podnie pensj (np. 10 = 10%).
* @return kwota podwyki
*/
public double raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
return raise;
}

4.9.4. Komentarze do pl
Komentarze s potrzebne tylko do pl publicznych, co na og oznacza zmienne statyczne.
Na przykad:
/**
* Kolor karo.
*/
public static final int HEARTS = 1;

4.9.5. Komentarze oglne


W komentarzach dokumentacji klas mona uywa poniszych znacznikw:

@author imi i nazwisko

Dodaje pozycj Author. Jeli jest kilku autorw, mona zastosowa kilka
znacznikw @author.

@version tekst

Dodaje pozycj Version. Tekst moe by opisem aktualnej wersji.


Nastpujcych znacznikw mona uywa we wszystkich komentarzach dokumentacyjnych:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

193

@since tekst

Dodaje pozycj Since. Tekst to opis wersji, w ktrej wprowadzono dan funkcj.
Na przykad @since version 1.7.1.

@deprecated tekst

Dodaje komentarz informujcy, e dana klasa, metoda lub zmienna nie powinny
by uywane. Tekst powinien zawiera informacj o zamienniku. Na przykad:
@deprecated W zamian naley uywa <code>setVisible(true)</code>

Do innych czci dokumentacji lub dokumentw zewntrznych mona si odwoywa za


pomoc hiperczy. Do tego celu su znaczniki @see i @link.

@see odwoanie

Dodaje hipercze w sekcji See also. Moe by uywany do klas i metod.


Odwoanie moe mie jedn z poniszych form:
pakiet.klasa#struktura etykieta
<a href="...">etykieta</a>
"tekst"

Najbardziej uyteczna jest pierwsza wersja. Narzdzie javadoc automatycznie


wstawia odnonik do dokumentacji z podanej nazwy klasy, metody lub zmiennej.
Na przykad:
@see com.horstmann.corejava.Employee#raiseSalary(double)

Powyszy znacznik tworzy odnonik do metody raiseSalary(double) w klasie


com.horstmann.corejava.Employee. Mona pomin nazw pakietu lub nazw
pakietu i klasy. W takiej sytuacji struktura bdzie zlokalizowana w biecym
pakiecie lub klasie.
Naley zauway, e znakiem rozdzielajcym klas i nazw metody lub zmiennej
jest znak #, a nie kropka. Kompilator Javy jest bardzo inteligentny i bez problemu
rozpoznaje rne zastosowania kropki jako separatora pakietw, podpakietw,
klas, klas wewntrznych, metod i zmiennych. Niestety narzdzie javadoc nie jest
tak inteligentne i trzeba mu pomc.
Jeli po znaczniku @see znajduje si znak <, oznacza to, e trzeba poda hipercze.
Moe ono prowadzi pod dowolny adres URL. Na przykad:
@see <a href="www.horstmann.com/corejava.html">Strona internetowa ksiki</a>

W kadym z tych przypadkw mona okreli opcjonaln etykiet, ktra bdzie


kotwic odnonika. W przypadku pominicia etykiety kotwic jest nazwa kodu
docelowego lub adres URL.
Jeli po znaczniku @see znajduje si znak ", tekst zostanie wywietlony w sekcji
See also. Na przykad:
@see "Core Java 2. Tom 2"

Znacznikw @see mona wstawi kilka, ale wszystkie musz by w jednym miejscu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

194

Java. Podstawy

Odnoniki do klas lub metod mona wstawia w dowolnych miejscach we wszystkich


komentarzach dokumentacyjnych. Suy do tego znacznik w specjalnej formie:
{@link pakiet.klasa#struktura etykieta}

Wszystkie zasady dotyczce znacznika @see maj zastosowanie take do tego


znacznika.

4.9.6. Komentarze do pakietw i oglne


Komentarze do klas, metod i zmiennych znajduj si bezporednio w plikach rdowych
Javy pomidzy znakami /** i */. Natomiast generowanie komentarzy do pakietw wymaga
dodania osobnego pliku do kadego katalogu pakietu. S dwie moliwoci:
1.

Utworzenie pliku HTML o nazwie package.html. Zostanie pobrane wszystko,


co znajduje si pomidzy znacznikami <body> i </body>.

2. Utworzenie pliku Java o nazwie package-info.java. Na pocztku tego pliku musz


si znajdowa komentarz /** */ i instrukcja package. Nie powinno w nim by

adnych dodatkowych komentarzy ani kodu.


Istnieje take moliwo utworzenia oglnego komentarza do wszystkich plikw rdowych.
Powinien si znajdowa w pliku o nazwie overview.html, zlokalizowanym w katalogu macierzystym wszystkich plikw rdowych. Zostanie pobrane wszystko, co znajduje si pomidzy
znacznikami <body> i </body>. Komentarz ten wywietla si, gdy uytkownik kliknie opcj
Overview na pasku nawigacyjnym.

4.9.7. Generowanie dokumentacji


Ponisze punkty opisuj procedur generowania dokumentacji, ktra w tym przypadku zostanie umieszczona w katalogu o nazwie docDirectory.
1.

Przejd do katalogu z plikami rdowymi, ktrych dokumentacj chcesz


wygenerowa. Jeli program zawiera zagniedone pakiety, jak com.horstmann.
corejava, naley otworzy katalog zawierajcy podkatalog com (w tym samym
katalogu powinien si znajdowa plik overview.html, jeli zosta utworzony).

2. Aby wygenerowa dokumentacj jednego pakietu, naley wyda nastpujce

polecenie:
javadoc -d docDirectory nazwaPakietu

W przypadku kilku pakietw polecenie wyglda tak:


javadoc -d docDirectory NazwaPakietu1 nazwaPakietu2

Jeli pliki znajduj si w pakiecie domylnym, powysze polecenie ma nastpujc


form:
javadoc -d docDirectory *.java

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

195

W przypadku braku opcji -d docDirectory pliki HTML zostan umieszczone w biecym


katalogu. Nie zalecamy jednak takiego rozwizania, gdy powoduje ono niemay baagan.
Dziaaniem programu javadoc mona sterowa za pomoc rozmaitych opcji wiersza polece.
Na przykad opcje -author i -version dodaj do dokumentacji to, co oznaczono znacznikami
@author i @version (przy domylnych ustawieniach znaczniki te s pomijane). Inna przydatna
opcja to -link, ktra dodaje cza do klas standardowych. Na przykad ponisze polecenie:
javadoc -link http://docs.oracle.com/javase/7/docs/api *.java

utworzy odnoniki do wszystkich standardowych klas bibliotecznych w dokumentacji na stronie internetowej firmy Oracle.
Opcja -linksource konwertuje wszystkie pliki rdowe na pliki HTML (brak kolorowania
skadni, ale s numery wierszy). Nazwa kadej klasy i metody jest zamieniana na odnonik
do rda.
Informacje na temat pozostaych opcji mona znale w internetowej dokumentacji narzdzia javadoc pod adresem http://docs.oracle.com/javase/1.5.0/docs/guide/javadoc.
Wicej moliwoci konfiguracyjnych daj tak zwane doclety (ang. doclets). Mona
napisa doclet umoliwiajcy generowanie dokumentacji w dowolnym formacie
innym ni HTML. Poniewa niewiele osb potrzebuje takiej moliwoci, po szczegowe
informacje na temat docletw odsyamy do dokumentacji internetowej, ktra znajduje
si pod adresem http://docs.oracle.com/javase/1.5.0/docs/guide/javadoc/doclet/
overview.html.

4.10. Porady dotyczce projektowania klas


Na zakoczenie tego rozdziau przedstawiamy kilka wskazwek, ktrych stosowanie pomaga
w tworzeniu lepszych klas z punktu widzenia dobrego stylu programowania obiektowego.
Niniejsza lista nie jest bynajmniej ostatecznym rdem wiedzy na ten temat.
1.

Dane powinny by prywatne.


To jest najwaniejsza ze wszystkich zasad niestosowanie jej powoduje naruszenie
zasad hermetyzacji. Niewykluczone, e z tego powodu bdzie konieczne napisanie
kilku mutatorw lub metod dostpowych, ale i tak lepiej, aby pola danych pozostay
prywatne. Przekonalimy si na wasnej skrze, e sposb reprezentacji danych
moe si zmieni, ale sposb ich uywania ulega zmianom znacznie rzadziej. Jeli
dane s prywatne, zmiany w ich reprezentacji nie maj wpywu na uytkownikw
klasy, a bdy s atwiejsze do wykrycia.

2. Dane powinny by zawsze zainicjowane.

Java nie inicjuje zmiennych lokalnych, ale zmienne skadowe obiektw. Nie naley
pozwala na inicjacj zmiennych wartociami domylnymi, tylko inicjowa
je jawnie, podajc warto domyln lub wartoci domylne we wszystkich
konstruktorach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

196

Java. Podstawy
3. Nie naley stosowa zbyt wielu rnych podstawowych typw danych

w jednej klasie.
Jeli klasa zawiera kilka powizanych ze sob zmiennych tego samego typu,
naley je zastpi now klas. Dziki temu kod klas jest bardziej zrozumiay
i atwiejszy w modyfikacji. Na przykad ponisze pola klasy Customer mona
zastpi now klas o nazwie Address:
private
private
private
private

String street;
String city;
String state;
int zip;

Dziki temu znacznie prociej jest wprowadza zmiany w adresach, jak na przykad
w przypadku koniecznoci dodania obsugi adresw midzynarodowych.
4. Nie wszystkie pola wymagaj wasnych metod dostpu i zmiany.

Po utworzeniu obiektu zmiany moe wymaga na przykad wysoko pensji


pracownika, ale z pewnoci nie data zatrudnienia. Ponadto obiekty czsto
zawieraj skadowe, do ktrych nikt spoza klasy nie powinien mie dostpu.
Moe to by na przykad tablica skrtw nazw wojewdztw w klasie Address.
5. Klasy o zbyt duej funkcjonalnoci powinny by dzielone.

Oczywicie ta wskazwka nie jest precyzyjna, poniewa kady ma inne zdanie


na temat tego, ile to jest za duo funkcji. Jeli jednak istnieje moliwo
podzielenia zoonej klasy na dwie prostsze, naley z tej moliwoci skorzysta
(z drugiej strony nie naley przesadza klasy zawierajce po jednej metodzie
to odchylenie w drug stron).
Ponisza klasa jest przykadem zego stylu projektowania:
public class CardDeck
// zy styl
{
private int[] value;
private int[] suit;
public
public
public
public
public

CardDeck() { . . . }
void shuffle() { . . . }
int getTopValue() { . . . }
int getTopSuit() { . . . }
void draw() { . . . }

Klasa ta implementuje dwie odrbne koncepcje: tali kart (CardDeck) i zwizane


z ni metody shuffle (tasuj) i draw (pobierz) oraz metody sprawdzajce warto
i kolor karty. Naleaoby utworzy oddzieln klas o nazwie Card reprezentujc
kart. W ten sposb powinny powsta dwie klasy, z ktrych kada ma wasny
zakres dziaa:
public class CardDeck
{
private Card[] cards;
public CardDeck() { . . . }
public void shuffle() { . . . }

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 4.

Obiekty i klasy

197

public Card getTop() { . . . }


public void draw() { . . . }
}
public class Card
{
private int value;
private int suit;
public Card(int aValue, int aSuit) { . . . }
public int getValue() { . . . }
public int getSuit() { . . . }
}

6. Nazwy metod i klas powinny odpowiada ich przeznaczeniu.

Podobnie jak zmiennym, klasom naley nadawa nazwy odzwierciedlajce ich


przeznaczenie (w bibliotece standardowej jest kilka klas, ktrych nazwy budz
wtpliwoci, np. klasa Date, ktra opisuje godzin).
Zgodnie z konwencj nazwa klasy powinna by rzeczownikiem (np. Zamwienie)
lub skada si z przymiotnika i rzeczownika (np. SzybkieZamwienie). Nazwy
akcesorw powinny si zaczyna od pisanego maymi literami sowa get
(np. getSalary), a mutatorw od sowa set (np. setSalary).
W tym rozdziale opisalimy podstawowe informacje dotyczce obiektw i klas, dziki ktrym Java jest jzykiem obiektowym. Aby jednak jzyk by w peni obiektowy, musi obsugiwa dziedziczenie i polimorfizm. Tym wasnociom Javy zosta powicony nastpny
rozdzia.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

198

Java. Podstawy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Dziedziczenie
W tym rozdziale:

Klasy, nadklasy i podklasy

Klasa bazowa Object

Klasa ArrayList

Obiekty osonowe i automatyczne opakowywanie typw

Metody ze zmienn liczb parametrw

Klasy wyliczeniowe

Refleksja

Porady projektowe dotyczce dziedziczenia

Rozdzia 4. wprowadzi pojcia klas i obiektw. Ten rozdzia wprowadza kolejne zagadnienie
o fundamentalnym znaczeniu dla programowania obiektowego dziedziczenie (ang. inheritance). Z zaoenia technika ta umoliwia tworzenie nowych klas na bazie klas ju istniejcych. Klasa, ktra dziedziczy po innej klasie, przejmuje jej metody i pola oraz dodaje
wasne metody i pola, ktre su przystosowaniu do nowych zada. Technika ta ma kluczowe
znaczenie dla programowania w Javie.
Dodatkowo rozdzia ten opisuje refleksj (ang. reflection), czyli technik inspekcji klas
w trakcie dziaania programu. Mimo e refleksja daje ogromne moliwoci, jest bez wtpienia technik skomplikowan. Poniewa ma ona wiksze znaczenie dla twrcw narzdzi
ni programistw aplikacji, t cz rozdziau mona przeczyta pobienie i wrci do niej
w razie potrzeby.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

200

Java. Podstawy

5.1. Klasy, nadklasy i podklasy


Wrmy do omawianej w poprzednim rozdziale klasy Employee. Wyobramy sobie, e (niestety) pracujemy w firmie, w ktrej kierownicy s traktowani inaczej ni pozostali pracownicy. Oczywicie istnieje midzy nimi te wiele podobiestw. Zarwno zwykli pracownicy,
jak i kierownictwo dostaj wypat. Jednak podczas gdy zwykli pracownicy, aby otrzyma pensj, musz ukoczy powierzone im zadania, kierownicy, jeli osign zamierzony cel, dostaj
dodatek do wypaty. Jest to typowa sytuacja, w ktrej naley wykorzysta dziedziczenie.
Dlaczego? Oczywicie mona utworzy cakiem now klas o nazwie Manager o odpowiednich waciwociach. Mona jednak wykorzysta cz kodu, ktry zosta napisany wczeniej w klasie Employee. Wszystkie pola oryginalnej klasy zostayby zachowane. Stosujc
bardziej abstrakcyjn terminologi, istnieje oczywisty zwizek jest pomidzy klasami
Manager i Employee. Kady kierownik (ang. manager) jest pracownikiem (ang. employee).
Relacja typu jest stanowi cech charakterystyczn dziedziczenia.
Do wyraania relacji dziedziczenia suy sowo kluczowe extends. W poniszym przykadowym kodzie klasa Manager dziedziczy po klasie Employee.
class Manager extends Employee
{
Dodatkowe metody i pola.
}

Dziedziczenie w Javie i C++ jest podobne, jednak w Javie wyraa si je za pomoc


sowa kluczowego extends, a w C++ za pomoc symbolu :. W Javie dziedziczenie moe by wycznie publiczne. Nie ma w tym jzyku odpowiednikw znanych z C++
dziedziczenia prywatnego i chronionego.

Sowo kluczowe extends oznacza, e tworzona jest nowa klasa na podstawie istniejcej klasy.
Klasa istniejca nazywana jest nadklas (ang. superclass), klas bazow (ang. base class)
lub klas macierzyst (ang. parent class). Nowo utworzona klasa nazywa si podklas
(ang. subclass), klas pochodn (ang. derived class) lub klas potomn (ang. child class).
Wikszo programistw Javy uywa terminw nadklasa i podklasa, ale niektrym
bardziej odpowiada analogia klasy macierzystej i potomnej, ktra bardzo dobrze pasuje do
koncepcji dziedziczenia.
Klasa Employee jest nadklas, ale nie ze wzgldu na to, e jest wysza rang albo ma wiksz
funkcjonalno. W rzeczywistoci jest odwrotnie: podklasa ma wiksz funkcjonalno ni
nadklasa. Bdzie mona si o tym przekona, kiedy przejdziemy do analizy klasy Manager,
ktra zawiera wicej danych i ma wiksz funkcjonalno ni jej nadklasa Employee.
Przedrostki nad- i pod- pochodz od sposobu opisu zbiorw w informatyce teoretycznej i matematyce. Zbir wszystkich pracownikw zawiera zbir wszystkich kierownikw, czyli zbir pracownikw jest nadzbiorem zbioru kierownikw. Albo w drug
stron zbir kierownikw jest podzbiorem zbioru pracownikw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

201

Klasa Manager zawiera dodatkowe pole do przechowywania dodatku do pensji i now metod
do ustawiania jego wysokoci:
class Manager extends Employee
{
private double bonus;

. . .
public void setBonus(double b)
{
bonus = b;
}

Powyszego pola i powyszej metody nie wyrnia nic szczeglnego. Po utworzeniu obiektu
klasy Manager mona na jego rzecz wywoa metod setBonus:
Manager boss = . . .;
boss.setBonus(5000);

Oczywicie na rzecz obiektu klasy Employee nie mona wywoa metody setBonus. Nie ma jej
wrd metod tej klasy.
Natomiast na rzecz obiektw klasy Manager mona wywoywa metody getName i getHireDay,
mimo e nie zostay one zdefiniowane bezporednio w tej klasie. S dostpne, poniewa
zostay odziedziczone po klasie Employee.
Podobnie zostay odziedziczone pola name, salary i hireDay. Kady obiekt klasy Manager
ma cztery pola: name, salary, hireDay i bonus.
W definicji klasy rozszerzajcej inn klas konieczne jest podanie tylko rnic pomidzy
tymi klasami. Metody oglnego przeznaczenia naley umieszcza w nadklasie, a metody
bardziej wyspecjalizowane w podklasie. Wydzielanie wsplnego kodu i umieszczanie go
w nadklasie jest czsto stosowan technik programowania obiektowego.
Niektre metody obecne w klasie nadrzdnej s niewaciwe dla klasy Manager. Jest tak w przypadku metody getSalary, ktra powinna zwraca sum podstawy wynagrodzenia i dodatku.
Konieczne jest napisanie nowej metody przesaniajcej (ang. override) metod z nadklasy:
class Manager extends Employee
{
. . .
public double getSalary()
{
. . .
}
. . .
}

Jak powinna wyglda implementacja tej metody? Na pierwszy rzut oka wydaje si to proste wystarczy zwrci sum pl salary i bonus:
public double getSalary()
{
return salary + bonus;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

// nie dziaa

202

Java. Podstawy
Tak si jednak nie da. Metoda getSalary klasy Manager nie ma bezporedniego dostpu do
prywatnych pl nadklasy Employee. Oznacza to, e metoda getSalary klasy Manager nie ma
bezporedniego dostpu do pola salary, mimo e kady obiekt tej klasy zawiera pole o nazwie
salary. Tylko metody klasy Employee maj dostp do tych prywatnych pl. Jeli metody
klasy Manager chc uzyska do nich dostp, musz zrobi to co wszystkie inne metody
uy interfejsu publicznego. W tym przypadku konieczne jest uycie metody getSalary klasy
Employee.
Sprbujmy jeszcze raz. Zamiast bezporednio odwoywa si do pola salary, uyjemy metody
getSalary:
public double getSalary()
{
double baseSalary = getSalary();
return baseSalary + bonus;
}

// nadal nie dziaa

Problem polega na tym, e metoda getSalary wywouje sama siebie, poniewa klasa Manager
zawiera metod o takiej nazwie (dokadniej mwic, jest to ta metoda, ktr prbujemy
zaimplementowa). W ten sposb powstaa nieskoczona seria wywoa jednej metody, ktra
prowadzi do zaamania programu.
Musimy zaznaczy, e odwoujemy si do metody getSalary klasy Employee, a nie klasy,
w ktrej si znajdujemy. Do tego celu suy sowo kluczowe super. Instrukcja:
super.getSalary()

wywoa metod getSalary klasy Employee. Poniej znajduje si poprawna wersja metody
getSalary w klasie Manager:
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}

Niektrzy programici uwaaj, e sowo kluczowe super jest odpowiednikiem referencji this. Nie jest to jednak precyzyjne porwnanie, poniewa sowo super nie jest
referencj do obiektu. Nie mona na przykad przypisa jego wartoci do innej zmiennej
obiektowej. super to sowo kluczowe, ktre nakazuje kompilatorowi wywoanie metody
z nadklasy.

Wiemy ju, e do podklasy mona dodawa nowe pola oraz dodawa lub przesania metody
z nadklasy. Dziedziczenie nie umoliwia natomiast pozbycia si adnych metod ani pl.
W Javie do wywoywania metod nadklasy suy sowo kluczowe super. W C++ w takiej
sytuacji naley uy nazwy nadklasy z operatorem ::. Na przykad metoda getSalary
klasy Manager wywoywaaby Employee::getSalary zamiast super.getSalary.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

203

Na koniec dodamy konstruktor.


public Manager(String n, double s, int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}

W tym miejscu sowo kluczowe super znaczy co innego. Instrukcja:


super(n, s, year, month, day);

mwi: wywoaj konstruktor nadklasy Employee z parametrami n, s, year, month i day.


Poniewa konstruktor Manager nie ma dostpu do pl prywatnych klasy Employee, musi je
zainicjowa poprzez inny konstruktor. Konstruktor jest wywoywany za pomoc specjalnej
skadni z uyciem sowa kluczowego super. Wywoanie z tym sowem kluczowym musi by
pierwsz instrukcj konstruktora podklasy.
Jeli konstruktor podklasy nie wywouje jawnie konstruktora nadklasy, wywoywany jest
konstruktor domylny (bezparametrowy) nadklasy. Jeli nadklasa nie ma konstruktora domylnego, a konstruktor podklasy nie wywouje jawnie adnego innego konstruktora nadklasy,
kompilator zgosi bd.
Przypomnijmy, e sowo this ma dwa zastosowania: okrela referencj do parametru niejawnego i wywouje inny konstruktor tej samej klasy. Podobnie dwa zastosowania ma sowo super: wywouje metody nadklasy i konstruktory nadklasy. W przypadku
wywoywania konstruktorw sowa te s blisko spokrewnione. Wywoanie konstruktora
moe nastpowa tylko w pierwszej instrukcji innego konstruktora. Parametry konstrukcyjne s przekazywane albo do innego konstruktora tej samej klasy (this), albo do konstruktora nadklasy (super).

W jzyku C++ nie uywa si w konstruktorze sowa kluczowego super, tylko skadni
listy inicjujcej nadklasy. Konstruktor Manager w C++ wygldaby nastpujco:
Manager::Manager(String n, double s, int year, int month, int day)
: Employee(n, s, year, month, day)
{
bonus = 0;
}

// C++

Po ponownym zdefiniowaniu metody getSalary dla obiektw Manager dodatki do pensji


kierownikw bd dodawane automatycznie.
Oto praktyczny przykad obrazujcy powysze rozwaania. Utworzymy nowy obiekt klasy
Manager i ustawimy dodatek dla kierownika:
Manager boss = new Manager("Karol Parol", 80000, 1987, 12, 15);
boss.setBonus(5000);

Tworzymy tablic trzech pracownikw:


Employee[] staff = new Employee[3];

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

204

Java. Podstawy
Wstawiamy do niej obiekty zwykych pracownikw i kierownikw:
staff[0] = boss;
staff[1] = new Employee("Henryk Kwiatek", 50000, 1989, 10, 1);
staff[2] = new Employee("Artur Kwiatkowski", 40000, 1990, 3, 15);

Wywietlamy pensj kadego z nich:


for (Employee e : staff)
System.out.println(e.getName() + " " + e.getSalary());

Powysza ptla zwraca nastpujce dane:


Karol Parol 85000.0
Henryk Kwiatek 50000.0
Artur Kwiatkowski 40000.0

Obiekty staff[1] i staff[2] drukuj podstawowe wynagrodzenie, poniewa s to obiekty klasy


Employee. Natomiast obiekt Staff[0] jest obiektem klasy Manager, a wic jego metoda getSalary
dolicza dodatek do podstawowego wynagrodzenia.
Na uwag zasuguje fakt, e wywoanie:
e.getSalary()

wybiera odpowiedni metod getSalary. Warto zauway, e zmienna e jest zadeklarowana jako typ Employee, ale rzeczywistym typem, do ktrego odwouje si ta zmienna, moe
by albo Employee, albo Manager.
Kiedy zmienna e odwouje si do obiektu klasy Employee, wywoanie e.getSalary() wywouje
metod getSalary klasy Employee. Jeli jednak e odwouje si do obiektu klasy Manager,
metoda getSalary jest wywoywana z klasy Manager. Maszyna wirtualna rozpoznaje, do jakiego
typu odwouje si zmienna e, dziki czemu moe wywoa odpowiedni metod.
Moliwo odwoywania si przez obiekty (jak zmienna e) do wielu rnych typw nosi
nazw polimorfizmu (ang. polymorphism). Automatyczny dobr odpowiednich metod w trakcie dziaania programu nazywa si wizaniem dynamicznym (ang. dynamic binding). Oba
te zagadnienia zostay szczegowo opisane w tym rozdziale.
W Javie nie ma potrzeby deklarowania metody jako wirtualnej. Wizanie dynamiczne
jest dziaaniem domylnym. Aby metoda nie bya wirtualna, naley uy sowa kluczowego final (opis sowa kluczowego final znajduje si dalej w tym rozdziale).

Listing 5.1 przedstawia program demonstrujcy rnic naliczania pensji dla obiektw klasy
Employee (listing 5.2) i Manager (listing 5.3).
Listing 5.1. inheritance/ManagerTest.java
package inheritance;
/**
* Ten program demonstruje dziedziczenie.
* @version 1.21 2004-02-21
* @author Cay Horstmann
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

public class ManagerTest


{
public static void main(String[] args)
{
// Tworzenie obiektu klasy Manager.
Manager boss = new Manager("Karol Parol", 80000, 1987, 12, 15);
boss.setBonus(5000);
Employee[] staff = new Employee[3];
// Wstawienie obiektw klas Manager i Employee do tablicy staff.
staff[0] = boss;
staff[1] = new Employee("Henryk Kwiatek", 50000, 1989, 10, 1);
staff[2] = new Employee("Artur Kwiatkowski", 40000, 1990, 3, 15);
// Wywietlanie informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
}
}

Listing 5.2. inheritance/Employee.java


package inheritance;
import java.util.Date;
import java.util.GregorianCalendar;
public class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

205

206

Java. Podstawy
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
}

Listing 5.3. inheritance/Manager.java


package inheritance;
class Manager extends Employee
{
private double bonus;
/**
* @param n imi i nazwisko pracownika
* @param s pensja
* @param year rok przyjcia do pracy
* @param month miesic przyjcia do pracy
* @param day dzie przyjcia do pracy
*/
public Manager(String n, double s, int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
}

5.1.1. Hierarchia dziedziczenia


Dziedziczenie nie dotyczy tylko jednego poziomu klas. Na przykad rozszerzeniem klasy
Manager moe by klasa Executive (dyrektor). Struktur klas i ich drogi dziedziczenia od
wsplnej klasy bazowej nazywa si hierarchi dziedziczenia (ang. inheritance hierarchy)
zobacz rysunek 5.1. cieka od danej klasy do jej przodkw w hierarchii dziedziczenia ma
nazw acucha dziedziczenia (ang. inheritance chain).
acuchw dziedziczenia moe by wiele. Na przykad klas Employee mog rozszerza klasy
Programmer i Secretary, ktre nie musz mie nic wsplnego z klas Manager (albo ze sob
nawzajem). Struktura ta moe mie dowoln dugo.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

207

Rysunek 5.1.
Hierarchia
dziedziczenia
klasy Employee

Java nie obsuguje dziedziczenia wielokrotnego. Sposoby naladowania techniki


dziedziczenia wielokrotnego s opisane w czci o interfejsach w podrozdziale 6.1.

5.1.2. Polimorfizm
W podjciu decyzji dotyczcej tego, czy w danym przypadku zastosowa dziedziczenie,
pomaga prosta zasada. Regua jest mwi, e kady obiekt podklasy jest obiektem nadklasy.
Na przykad kady kierownik jest pracownikiem. W zwizku z tym klasa Manager moe by
podklas klasy Employee. Oczywicie twierdzenia tego nie mona odwrci nie kady
pracownik jest kierownikiem.
Regu relacji jest mona take sformuowa jako zasad zamienialnoci (ang. substitution
principle). Zasada ta gosi, e wszdzie tam, gdzie mona uy obiektu nadklasy, mona uy
obiektu podklasy.
Mona na przykad przypisa obiekt podklasy do zmiennej nadklasy.
Employee e;
e = new Employee(. . .);
e = new Manager(. . .);

// Powinien by obiekt klasy Employee.


// OK, obiekt klasy Manager te moe by.

W Javie zmienne obiektowe s polimorficzne. Zmienna typu Employee moe odwoywa si


do dowolnego obiektu klasy Employee, jak rwnie do kadego obiektu podklasy klasy Employee
(jak Manager, Executive, Secretary itd.).
Skorzystalimy z tej zasady w programie z listingu 5.1:
Manager boss = new Manager(. . .);
Employee[] staff = new Employee[3];
staff[0] = boss;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

208

Java. Podstawy
W tym przypadku zmienne staff[0] i boss odwouj si do tego samego obiektu. Jednak
dla kompilatora staff[0] jest obiektem wycznie klasy Employee.
Oznacza to, e mona uy poniszego wywoania:
boss.setBonus(5000);

// OK

Ale ponisze spowoduje bd:


staff[0].setBonus(5000);

// bd

Typ zadeklarowany zmiennej staff[0] to Employee, a metoda setBonus nie jest obecna w tej
klasie.
Nie mona przypisa referencji nadklasy do zmiennej podklasy. Na przykad ponisze przypisanie jest niedozwolone:
Manager m = staff[i];

// bd

Powd jest jasny nie wszyscy pracownicy s kierownikami. Gdyby powysze przypisanie udao si i zmienna m byaby referencj do obiektu klasy Employee, ktry nie jest kierownikiem, to moliwe byoby te wywoanie m.setBonus(), a to z kolei spowodowaoby
bd czasu wykonania.
W Javie moliwa jest konwersja tablic referencji do obiektw podklasy na tablic
referencji do obiektw nadklasy bez rzutowania. Spjrzmy na ponisz tablic obiektw klasy Manager:
Manager[] managers = new Manager[10];

Mona j przekonwertowa na tablic Employee[]:


Employee[] staff = managers;

// OK

Mona pomyle, czemu nie. Przecie kady kierownik jest te pracownikiem. Jednak dzieje
si tu co zaskakujcego. Pamitajmy, e zmienne managers i staff s referencjami do
tej samej tablicy. Spjrzmy teraz na ponisz instrukcj:
staff[0] = new Employee("Henryk Kwiatek", ...);

Kompilator nie zgosi adnego sprzeciwu przy kompilacji tego przypisania, ale zmienne
staff[0] i manager[0] s t sam referencj, a wic wyglda na to, e awansowalimy
zwykego pracownika na stanowisko kierownicze. Byaby to bardzo za sytuacja. Wywoanie managers[0].setBonus(1000) usiowaoby uzyska dostp do nieistniejcego pola
i spowodowaoby uszkodzenie okolicznej pamici.
Aby unikn takiego zniszczenia, wszystkie tablice pamitaj, z jakim typem zostay
utworzone, i pilnuj, aby przechowywane w nich byy tylko odpowiednie referencje.
Na przykad tablica utworzona za pomoc instrukcji new Manager[10] pamita, e jest
tablic kierownikw. Prba zapisania w niej referencji do typu Employee spowoduje wyjtek ArrayStoreException.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

209

5.1.3. Wizanie dynamiczne


Wane jest, aby zrozumie, co si dzieje w momencie wywoania metody na rzecz obiektu.
Wyglda to tak:
1.

Kompilator sprawdza zadeklarowany typ obiektu i nazw metody. Powiedzmy,


e wywoujemy x.f(param), a niejawny parametr x jest zadeklarowany jako obiekt
klasy C. Pamitajmy, e metod o nazwie f moe by kilka, a rnica midzy nimi
polega na tym, e maj rne listy parametrw. Na przykad mog to by metody
f(int) i f(String). Kompilator tworzy list wszystkich metod o nazwie f dostpnych
w klasie C i wszystkich metod publicznych o tej nazwie w nadklasie klasy C (prywatne
metody nadklasy s niedostpne).
W ten sposb powstaje lista wszystkich potencjalnych metod do wywoania.

2. Nastpnie kompilator sprawdza typy parametrw podanych w wywoaniu metody.


Jeli wrd zebranych metod o nazwie f znajduje si taka, ktrej parametry pasuj

dokadnie do podanych parametrw, to zostaje ona wywoana. Proces ten nazywa


si rozstrzyganiem przeciania. Na przykad dla wywoania x.f("Witaj")
kompilator wybierze metod f(String), a nie f(int). Sytuacja moe
si skomplikowa ze wzgldu na konwersj typw (int na double, Manager
na Employee itd.). Jeli kompilator nie moe znale metody pasujcej do podanych
parametrw lub znajdzie kilka pasujcych metod, zgasza bd.
W ten sposb kompilator znajduje metod, ktr naley wywoa.
Przypomnijmy, e nazwa oraz lista typw parametrw metody s nazywane sygnatur metody. Na przykad metody f(int) i f(String) maj takie same nazwy, ale
rne sygnatury. Jeli w podklasie zostanie zdefiniowana metoda o takiej samej sygnaturze jak metoda w klasie nadrzdnej, dana metoda nadklasy zostanie przesonita.
Typ zwrotny nie jest czci sygnatury, ale musi si on w metodzie przesaniajcej zgadza z tym w metodzie przesonitej. Podklasa moe zmieni typ zwrotny na podtyp
oryginalnego typu. Wyobramy sobie na przykad, e klasa Employee zawiera metod
public Employee getKumpel() { ... }

Meneder nie chciaby si kolegowa ze zwykym pracownikiem. Dlatego w podklasie


metoda ta moe zosta przesonita:
public Manager getKolega() { ... }

// Mona zmieni typ zwrotny

Mwi si, e obie metody getKolega maj kowariantne typy zwrotne (ang. covariant
return types).
3. Jeli metoda jest prywatna, statyczna lub finalna albo jest konstruktorem, kompilator
wie, ktr dokadnie metod wywoa (modyfikator final jest opisany w kolejnym

podrozdziale). Nazywa si to wizaniem statycznym (ang. static binding).


W przeciwnym przypadku to, ktra metoda zostanie wywoana, zaley
od rzeczywistego typu parametru niejawnego; musi te by zastosowane wizanie
dynamiczne w trakcie dziaania programu. W naszym przykadzie kompilator
wygenerowaby instrukcj wywoujc metod f(String) za pomoc wizania
dynamicznego.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

210

Java. Podstawy
4. Kiedy program dziaa i wywouje metod za pomoc wizania dynamicznego,

maszyna wirtualna musi wywoa t wersj niniejszej metody, ktra odpowiada


rzeczywistemu typowi obiektu, do ktrego odwouje si zmienna x. Niech tym
typem bdzie D podklasa klasy C. Jeli klasa D zawiera definicj metody f(String),
wywoana zostaje wanie ta metoda. W przeciwnym przypadku metoda ta jest
szukana w nadklasie klasy D itd.
Gdyby to wyszukiwanie byo przeprowadzane przy kadym wywoaniu tej metody,
tracono by duo czasu. Dlatego maszyna wirtualna tworzy na pocztku tabel
metod zawierajc sygnatury i ciaa wszystkich metod, ktre mog by wywoane.
Kiedy dana metoda jest wywoywana, maszyna wirtualna odszukuje j w swojej
tabeli. W naszym przykadzie maszyna wirtualna przeszukuje tabel metod klasy D
i odnajduje metod f(String). Moe to by metoda D.f(String) albo X.f(String),
gdzie X to jedna z nadklas klasy D. Scenariusz ten moe si zmieni w jednej
sytuacji. Jeli wywoanie ma posta super.f(param), kompilator przeszukuje
tabel metod nadklasy parametru niejawnego.
Przeanalizujmy ten proces na przykadzie wywoania e.getSalary() z listingu 5.1. Obiekt e jest
typu Employee. Klasa Employee zawiera tylko jedn metod o nazwie getSalary, ktra nie ma parametrw. Dziki temu w tym przypadku nie ma problemu z rozstrzyganiem przeciania.
Poniewa metoda getSalary nie jest prywatna, statyczna ani finalna, jest wizana dynamicznie. Maszyna wirtualna tworzy tabele metod dla klas Employee i Manager. Tabela Employee
wskazuje, e wszystkie jej metody s zdefiniowane w samej klasie Employee:
Employee:
getName() -> Employee.getName()
getSalary() -> Employee.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(double)

To jednak nie wszystko. Jak si niebawem dowiemy, klasa Employee ma nadklas Object,
po ktrej dziedziczy kilka metod. Na razie ignorujemy te dodatkowe metody.
Tabela metod klasy Manager wyglda nieco inaczej. Trzy metody s odziedziczone, jedna
przedefiniowana, a jedna zostaa dodana.
Manager:
getName() -> Employee.getName()
getSalary() -> Manager.getSalary()
getHireDay() -> Employee.getHireDay()
raiseSalary(double) -> Employee.raiseSalary(double)
setBonus(double) -> Manager.setBonus(double)

Oto co si dzieje z wywoaniem e.getSalary() w trakcie dziaania programu:


1.

Maszyna wirtualna tworzy tabel metod dla rzeczywistego typu e. Moe to by


tabela klasy Employee lub jednej z jej podklas, np. Manager.

2. Nastpnie maszyna wirtualna szuka klasy zawierajcej definicj metody o sygnaturze


getSalary(). W ten sposb znajduje metod, ktr ma wywoa.
3. Ostatecznie maszyna wirtualna wywouje odpowiedni metod.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

211

Wizanie dynamiczne ma jedn bardzo wan cech: umoliwia rozszerzanie programw


bez modyfikacji istniejcego kodu. Przypumy, e zostaa utworzona klasa Executive
i istnieje moliwo, e zmienna e odwouje si do obiektu tej klasy. Kod zawierajcy wywoanie e.getSalary() nie musi by ponownie kompilowany. Metoda Executive.getSalary()
zostanie wywoana automatycznie, jeli zmienna e odwouje si do obiektu typu Executive.
Kiedy metoda jest przesaniana, jej odpowiednik w podklasie musi mie przynajmniej tak sam widoczno jak orygina. Jeli metoda w nadklasie jest publiczna,
w podklasie rwnie musi by publiczna. Pominicie specyfikatora public w metodzie
podklasy jest czstym bdem. W takim przypadku kompilator informuje, e usiowano
zastosowa niszy przywilej dostpu.

5.1.4. Wyczanie dziedziczenia klasy i metody finalne


Zdarzaj si sytuacje, kiedy chcemy, aby nie tworzono podklas jednej z klas. Klasy, ktrych
nie mona rozszerza, nazywaj si klasami finalnymi (ang. final), a do ich oznaczania suy
modyfikator final. Zamy na przykad, e nie chcemy, aby klasa Executive bya rozszerzana. W tym celu naley w jej definicji uy modyfikatora final:
final class Executive extends Manager
{
. . .
}

Finalna moe te by metoda w klasie. W takim przypadku nie mona jej przesoni w adnej z podklas (wszystkie metody w klasie finalnej s finalne). Na przykad:
class Employee
{
. . .
public final String getName()
{
return name;
}
. . .
}

Przypomnijmy, e pola rwnie mog by finalne. Warto takiego pola nie moe
by zmieniana po utworzeniu obiektu. Jeli klasa jest finalna, tylko jej metody s
automatycznie finalne, nie dotyczy to pl.

Jest tylko jeden powd, dla ktrego warto zdefiniowa klas lub metod jako finaln: aby
zapewni, e adna podklasa nie zmieni semantyki. Na przykad metody getTime i setTime
klasy Calendar s finalne. Oznacza to, e projektanci tej klasy wzili na siebie ciar odpowiedzialnoci za konwersj pomidzy klas Date a stanem kalendarza. adna podklasa nie powinna
mie moliwoci mieszania si w to. Klasa String te jest finalna. Oznacza to, e nie mona
utworzy jej podklasy. Innymi sowy, jeli mamy referencj do obiektu String, wiemy, e
jest to obiekt klasy String i nic innego.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

212

Java. Podstawy
Wedug niektrych programistw wszystkie metody powinny by finalne, chyba e istnieje
dobry powd, dla ktrego potrzebny jest w danym przypadku polimorfizm. W C++ i C#
metody nie s polimorficzne, dopki jawnie si tego nie zada. Moe to jest w pewnym
sensie skrajne podejcie, ale zgadzamy si, e przy projektowaniu hierarchii klas naley
powanie rozway moliwo zastosowania modyfikatora final dla klas i metod.
Na pocztku istnienia Javy niektrzy programici uywali sowa kluczowego final w nadziei,
e unikn narzutu powodowanego przez wizanie dynamiczne. Jeli metoda nie jest przesonita i jest krtka, kompilator moe zoptymalizowa jej wywoywanie poprzez proces
polegajcy na wstawieniu jej kodu w miejsce wywoania (ang. inlining). Na przykad wywoanie metody e.getName() zostaoby zastpione dostpem do pola e.name. Jest to godna uwagi
poprawa procesory nie przepadaj za rozgazianiem, poniewa stoi ono w sprzecznoci
z ich strategi pobierania zawczasu kolejnej funkcji podczas przetwarzania innej. Jeli jednak metoda getName moe by przesonita w innej klasie, kompilator nie moe zastosowa
wstawiania kodu, poniewa nie wie, jak dziaa ta przesonita wersja.
Na szczcie kompilator JIT w maszynie wirtualnej spisuje si lepiej ni zwyky kompilator.
Wie dokadnie, ktre klasy s rozszerzeniem danej klasy, i ma moliwo sprawdzenia, czy
dana metoda jest przesonita w ktrej z tych klas. Jeli metoda jest krtka, czsto wywoywana i nie jest przesonita, kompilator JIT moe zastosowa inlining. Co si stanie, jeli
maszyna wirtualna zaaduje inn podklas, ktra przesania nasz metod? Wtedy proces
inliningu musi zosta cofnity. Jest to operacja powolna, ale zdarza si bardzo rzadko.

5.1.5. Rzutowanie
Przypomnijmy z rozdziau 3., e proces wymuszania konwersji pomidzy dwoma typami
nazywa si rzutowaniem (ang. casting). W Javie dostpna jest specjalna notacja oznaczajca rzutowanie. Na przykad:
double x = 3.405;
int nx = (int) x;

W powyszym kodzie warto zmiennej x zostaa przekonwertowana na typ cakowitoliczbowy, co spowodowao utrat czci uamkowej.
Podobnie jak od czasu do czasu konieczna jest konwersja typu double na int, tak samo bywa,
e trzeba przekonwertowa referencj do obiektu jednej klasy na referencj do obiektu innej
klasy. Do rzutowania referencji do obiektw uywa si podobnej notacji jak do rzutowania
typw liczbowych. Nazw klasy docelowej naley umieci w nawiasach i wstawi przed
referencj, ktra ma by rzutowana. Na przykad:
Manager boss = (Manager) staff[0];

Tego typu rzutowanie moe by potrzebne tylko w jednego rodzaju sytuacji aby w peni
wykorzysta obiekt, ktrego typ zosta chwilowo zgubiony. Na przykad w klasie TestManager
tablica staff musiaa by typu Employee, poniewa niektre z przechowywanych w niej
obiektw reprezentoway zwykych pracownikw. Aby uzyska dostp do nowych skadowych obiektw klasy Manager, konieczne by byo ich przekonwertowanie z powrotem na

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

213

typ Manager (w zaprezentowanym wczeniej przykadowym kodzie specjalnie uniknlimy


rzutowania, inicjalizujc zmienn boss obiektem klasy Manager przed zapisaniem jej w tablicy
aby ustawi dodatek do wypaty, potrzebny jest odpowiedni typ obiektu).
Wiadomo, e kady obiekt w Javie ma typ. Typ ten okrela rodzaj obiektu, do ktrego odwouje si zmienna, i wskazuje, co obiekt ten moe robi. Na przykad zmienna staff[i] odwouje
si do obiektu klasy Employee (a wic moe take odwoywa si do obiektu klasy Manager).
Kompilator sprawdza, czy programista nie wymaga zbyt wiele, zapisujc warto w zmiennej.
Jeli przypisze referencj do obiektu podklasy do zmiennej obiektowej nadklasy, zmniejsza
swoje wymagania, wic kompilator bez problemu si na to zgodzi. Jeli jednak referencja do
obiektu nadklasy zostanie przypisana zmiennej obiektu podklasy, zwiksza swoje wymagania.
W takim przypadku konieczne jest zastosowanie rzutowania, ktre umoliwi sprawdzenie
tych wymaga w trakcie dziaania programu.
Co si stanie, jeli programista sprbuje wykona rzutowanie w d acucha dziedziczenia
i oszuka w kwestii zawartoci obiektu?
Manager boss = (Manager) staff[1];

// Bd

W trakcie dziaania programu systemy wykonawcze Javy wykryj niedorzeczne wymagania


i wygeneruj wyjtek ClassCastException. Jeli wyjtek nie zostanie przechwycony, program zostanie zamknity. W zwizku z tym do dobrych praktyk programistycznych naley
sprawdzenie, czy rzutowanie si powiedzie, przed jego zastosowaniem. Do tego celu suy
operator instanceof. Na przykad:
if (staff[1] instanceof Manager)
{
boss = (Manager) staff[1];
. . .
}

I wreszcie, kompilator nie pozwoli na rzutowanie, ktre nie ma szans powodzenia. Na przykad
rzutowanie:
Date c = (Date) staff[1];

spowoduje bd kompilacji, poniewa Date nie jest podklas klasy Employee.


Podsumowujc:

Rzutowanie jest moliwe wycznie w obrbie hierarchii dziedziczenia.

Przy rzutowaniu typu nadklasy na typ podklasy naley sprawdzi wykonalno


operacji za pomoc operatora instanceof.
Test:
x instanceof C

nie wygeneruje wyjtku, jeli zmienna x ma warto null, tylko zwrci warto false.
Sens tego zachowania polega na tym, e skoro null oznacza, i referencja nie wskazuje na aden obiekt, z pewnoci nie odwouje si do obiektu klasy C.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

214

Java. Podstawy
Faktem jest, e konwersja typu obiektu za pomoc rzutowania nie jest z reguy dobrym pomysem. W naszym przykadzie rzadko potrzebne jest rzutowanie obiektu klasy Employee na obiekt
klasy Manager. Metoda getSalary dziaa prawidowo na obiektach obu tych klas. Wizanie
dynamiczne, na ktrym opiera si polimorfizm, automatycznie lokalizuje odpowiedni metod.
Jedyna sytuacja, w ktrej potrzebne jest takie rzutowanie, ma miejsce wtedy, gdy chcemy uy
metody dostpnej tylko dla obiektw klasy Manager, czyli setBonus. Jeli wywoanie metody
setBonus na rzecz obiektw klasy Employee okae si konieczne, naley rozway moliwo, e nadklasa zostaa le zaprojektowana. Niewykluczone, e dobrym rozwizaniem okae
si dodanie do nadklasy metody setBonus. Pamitajmy, e do przerwania dziaania programu
wystarczy jeden nieprzechwycony wyjtek. Zasadniczo najlepiej jest wystrzega si rzutowania i operatora instanceof.
Skadnia rzutowania w Javie pochodzi ze starego i zego jzyka C, natomiast
dziaanie tego mechanizmu jest podobne do bezpiecznego rzutowania dynamic_
cast w C++. Na przykad:
Manager boss = (Manager) staff[1];

// Java

jest odpowiednikiem:
Manager* boss = dynamic_cast<Manager*>(staff[1]);

// C++

Jest tylko jedna rnica. Jeli rzutowanie nie powiedzie si, nie powstaje obiekt null,
ale wyjtek. W tym sensie przypomina to znane z C++ rzutowanie referencji. Jest to
jedna z bolczek. W C++ mona zadba o test typu i konwersj w jednej operacji.
Manager* boss = dynamic_cast<Manager*>(staff[1]);
if (boss != NULL) . . .

// C++

W Javie konieczne jest uycie operatora instanceof i zastosowanie rzutowania:


if (staff[1] instanceof Manager)
{
Manager boss = (Manager) staff[1];
. . .
}

5.1.6. Klasy abstrakcyjne


Im bliej wierzchoka hierarchii dziedziczenia, tym klasy s bardziej oglne i czsto bardziej
abstrakcyjne. W pewnym momencie klasa nadrzdna staje si tak abstrakcyjna, e zaczyna
by traktowana nie jako klasa do tworzenia obiektw o okrelonym przeznaczeniu, a jako
podstawa do tworzenia innych klas. Przeanalizujmy na przykad rozszerzenie hierarchii klasy
Employee. Pracownik (ang. employee), podobnie jak student, jest osob. Poszerzymy nasz
hierarchi klas o klasy Person (osoba) i Student. Rysunek 5.2 przedstawia relacje dziedziczenia zachodzce pomidzy tymi klasami.
Po co w ogle zaprzta sobie gow takim poziomem abstrakcji? Niektre cechy ma kada
osoba, np. nazwisko. Zarwno studenci, jak i pracownicy maj nazwiska, a wic wprowadzenie wsplnej nadklasy umoliwia wydzielenie metody getName i przeniesienie jej na wyszy
poziom hierarchii dziedziczenia.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

215

Rysunek 5.2.
Diagram
dziedziczenia
klasy Person
i jej podklas

Dodamy teraz now metod o nazwie getDescription, ktra zwraca krtki opis osoby, np.:
pracownik zarabiajcy 50 000,00 z
student specjalizacji informatyka

Implementacja tej metody dla klas Employee i Student jest atwa. Jakie natomiast informacje
mona poda w klasie Person? Klasa ta ma jedynie informacje na temat nazwiska osoby.
Oczywicie mona zaimplementowa metod Person.getDescription(), ktra zwraca pusty
acuch. Istnieje jednak lepsze rozwizanie. Dziki uyciu sowa kluczowego abstract
w ogle nie ma potrzeby implementowania tej metody.
public abstract String getDescription();
// nie jest potrzebna adna implementacja

Klasa zawierajca przynajmniej jedn metod abstrakcyjn sama musi by abstrakcyjna.


abstract class Person
{ . . .
public abstract String getDescription();
}

Poza metodami abstrakcyjnymi klasy abstrakcyjne mog zawiera pola i metody konkretne.
Na przykad klasa Person przechowuje nazwisko osoby i ma metod konkretn, ktra zwraca
te dane.
abstract class Person
{
private String name;
public Person(String n)
{
name = n;
}
public abstract String getDescription();
public String getName()
{
return name;
}
}

Niektrzy programici nie wiedz, e klasy abstrakcyjne mog zawiera metody


konkretne. Naley zawsze przenosi wsplne pola i metody (bez wzgldu na to,
czy s abstrakcyjne, czy nie) do nadklasy (abstrakcyjnej lub nie).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

216

Java. Podstawy
Metody abstrakcyjne peni rol symbolu zastpczego dla metod, ktre s implementowane
w podklasach. Przy rozszerzaniu klasy abstrakcyjnej programista ma do wyboru jedn z dwch
opcji: moe pozostawi niezdefiniowane niektre lub wszystkie metody nadklasy wtedy
podklasa rwnie musi by abstrakcyjna, albo zdefiniowa wszystkie metody i wtedy podklasa nie jest abstrakcyjna.
Jako przykad zdefiniujemy klas Student, ktra bdzie rozszerzaa abstrakcyjn klas Person
i implementowaa metod getDescription. Poniewa adna z metod klasy Student nie jest
abstrakcyjna, klasa ta rwnie nie musi by abstrakcyjna.
Klas mona zdefiniowa jako abstrakcyjn, nawet jeli nie zawiera adnych metod abstrakcyjnych.
Nie mona tworzy obiektw klas abstrakcyjnych. To znaczy, e jeli klasa ma w deklaracji
sowo abstract, nie moe mie obiektw. Na przykad ponisze wyraenie:
new Person("Wincenty Witos")

jest bdne. Mona natomiast tworzy obiekty podklas konkretnych.


Warto zauway, e mona tworzy zmienne obiektowe klas abstrakcyjnych, ale musz si
one odwoywa do obiektw nieabstrakcyjnych podklas. Na przykad:
Person p = new Student("Wincenty Witos", "Ekonomia");

W tym przypadku p jest zmienn abstrakcyjnego typu Person odwoujc si do egzemplarza


nieabstrakcyjnej podklasy Student.
W C++ metoda abstrakcyjna nazywa si funkcj czysto wirtualn i jest oznaczana
kocowymi znakami =0:
class Person
// C++
{
public:
virtual string getDescription() = 0;
. . .
};

W C++ klasa jest abstrakcyjna, jeli zawiera co najmniej jedn funkcj czysto wirtualn.
Nie ma w tym jzyku specjalnego sowa kluczowego okrelajcego klas abstrakcyjn.

Zdefiniujemy konkretn podklas Student, ktra rozszerza abstrakcyjn klas Person:


class Student extends Person
{
private String major;
public Student(String n, String m)
{
super(n);
major = m;
}
public String getDescription()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

217

return "student specjalizacji " + major;

Klasa Student zawiera definicj metody getDescription. W zwizku z tym wszystkie metody
tej klasy s konkretne, czyli nie jest ona abstrakcyjna.
Program przedstawiony na listingach 5.4, 5.5, 5.6 i 5.7 definiuje abstrakcyjn nadklas
o nazwie Person i dwie konkretne podklasy o nazwach Employee i Student. Do tablicy referencji typu Person wstawiane s obiekty klas Employee i Student:
Person[] people = new Person[2];
people[0] = new Employee(. . .);
people[1] = new Student(. . .);

Listing 5.4. abstractClasses/PersonTest.java


import java.util.*;
/**
* Ten program demonstruje klasy abstrakcyjne.
* @version 1.01 2004-02-21
* @author Cay Horstmann
*/
public class PersonTest
{
public static void main(String[] args)
{
Person[] people = new Person[2];
// Wstawienie do tablicy people obiektw Student i Employee.
people[0] = new Employee("Henryk Kwiatek", 50000, 1989, 10, 1);
people[1] = new Student("Maria Mrozowska", "informatyka");

// Drukowanie imion i nazwisk oraz opisw wszystkich obiektw klasy Person.


for (Person p : people)
System.out.println(p.getName() + ", " + p.getDescription());

Listing 5.5. abstractClasses/Person.java


package abstractClasses;
public abstract class Person
{
public abstract String getDescription();
private String name;
public Person(String n)
{
name = n;
}
public String getName()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

218

Java. Podstawy

return name;

Listing 5.6. abstractClasses/Employee.java


package abstractClasses;
import java.util.Date;
import java.util.GregorianCalendar;
public class Employee extends Person
{
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
super(n);
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public String getDescription()
{
return String.format("pracownik zarabiajcy
}

public void raiseSalary(double byPercent)


{
double raise = salary * byPercent / 100;
salary += raise;
}

Listing 5.7. abstractClasses/Student.java


package abstractClasses;
public class Student extends Person
{
private String major;
/**
* @param n imi i nazwisko studenta

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

%.2f z", salary);

Rozdzia 5.

Dziedziczenie

219

* @param m specjalizacja studenta


*/
public Student(String n, String m)
{
// Przekazanie n do konstruktora nadklasy.
super(n);
major = m;
}

public String getDescription()


{
return "student specjalizacji " + major;
}

Poniszy fragment kodu odpowiada za wydruk imion i nazwisk oraz opisw powyszych
obiektw:
for (Person p : people)
System.out.println(p.getName() + ", " + p.getDescription());

Wtpliwoci moe budzi ponisze wywoanie:


p.getDescription()

Czy nie jest to wywoanie niezdefiniowanej metody? Naley pamita, e zmienna p nie
odwouje si nigdy do obiektu Person, poniewa nie mona utworzy obiektu klasy abstrakcyjnej Person. Zmienna p zawsze odwouje si do obiektu konkretnej podklasy, jak Employee
lub Student. Dla tych obiektw metoda getDescription jest zdefiniowana.
Czy mona by byo pomin abstrakcyjn metod nadklasy abstrakcyjnej Person i zdefiniowa metody getDescription w podklasach Employee i Student? Gdybymy tak zrobili, niemoliwe byoby wywoanie metody getDescription na rzecz zmiennej p, poniewa kompilator zezwala na wywoywanie tylko tych metod, ktre s zdefiniowane w danej klasie.
Metody abstrakcyjne s bardzo wanym elementem jzyka programowania Java. Najczciej wystpuj w interfejsach. Wicej informacji na temat interfejsw zawiera rozdzia 6.

5.1.7. Ochrona dostpu


Wiemy ju, e najlepiej jest, kiedy pola metody s prywatne, a metody publiczne. Wszystko,
co jest oznaczone sowem kluczowym private, jest niewidoczne dla innych klas. Na pocztku
tego rozdziau dowiedzielimy si te, e powysza zasada dotyczy take podklas podklasa nie ma dostpu do pl prywatnych swojej nadklasy.
W niektrych sytuacjach konieczne jest ograniczenie widocznoci metody tylko do podklas
lub, rzadziej, zezwolenie metodom podklas na dostp do pl nadklasy. W takim przypadku
naley uy sowa kluczowego protected. Jeli na przykad klasa Employee zawiera pole
hireDay zadeklarowane jako protected, a nie private, metody klasy Manager maj do tego
pola bezporedni dostp.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

220

Java. Podstawy
Jednak metody klasy Manager maj dostp tylko do pola hireDay obiektw Manager, a nie
innych obiektw klasy Employee. Ograniczenie to ma zapobiec naduywaniu mechanizmu
ochrony w celu tworzenia podklas tylko po to, aby uzyska dostp do chronionych pl.
W praktyce z pl chronionych naley korzysta ostronie. Zamy, e nasza klasa, ktra
zawiera pola chronione, jest uywana przez innych programistw. Ci programici mog
tworzy bez naszej wiedzy klasy dziedziczce po naszej klasie i w ten sposb uzyska dostp
do chronionych pl naszej klasy. W takiej sytuacji zmiana implementacji owej nadklasy
pocigaaby za sob problemy u wspomnianych programistw. Takie dziaanie jest sprzeczne
z ide OOP, ktra opiera si na hermetyzacji danych.
Wicej sensu ma tworzenie chronionych metod. Metoda moe by chroniona, jeli jej uycie moe sprawia problemy. Oznacza to, e podklasy (ktre prawdopodobnie dobrze znaj
swoich przodkw) z pewnoci uyj danej metody prawidowo, podczas gdy metody innych
klas niekoniecznie.
Dobrym przykadem tego rodzaju metody jest metoda clone z klasy Object wicej
szczegw znajdziesz w rozdziale 6.
Skadowe chronione klasy w Javie s widoczne dla wszystkich jej podklas i innych
klas w pakiecie. Jest to nieco inne pojcie ochrony ni w C++ i powoduje ono, e
skadowe chronione w Javie s jeszcze mniej bezpieczne ni w C++.

Oto zestawienie wszystkich czterech modyfikatorw dostpu Javy sucych do kontroli


widocznoci:
1.

private widoczno w obrbie klasy;

2. public widoczno wszdzie;


3. protected widoczno w pakiecie i wszystkich podklasach;
4. widoczno w obrbie pakietu (niefortunne) zachowanie domylne, ktre

nie wymaga adnego modyfikatora.

5.2. Klasa bazowa Object


Klasa Object jest podstaw wszystkich pozostaych klas w Javie. Kada klasa w tym jzyku
rozszerza klas Object. Nigdy jednak nie trzeba pisa czego takiego:
class Employee extends Object

Jeli adna nadklasa nie jest jawnie podana, to automatycznie jest ni klasa Object. Poniewa kada klasa w Javie stanowi rozszerzenie klasy Object, trzeba si zapozna z usugami
wiadczonymi przez t klas. W tym rozdziale opisujemy tylko podstawowe zagadnienia,
a po szczegowe informacje odsyamy do kolejnych rozdziaw lub dokumentacji internetowej (niektre metody klasy Object mog by uywane tylko podczas pracy z wtkami
wicej o wtkach dowiesz si w rozdziale 14.).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

221

Za pomoc zmiennej typu Object mona si odwoywa do wszystkich typw obiektw:


Object obj = new Employee("Henryk Kwiatek", 35000);

Oczywicie zmienna typu Object jest jedynie generycznym kontenerem dla dowolnych wartoci. Aby zrobi z niej uytek, trzeba posiada wiedz na temat oryginalnego typu i wykona rzutowanie:
Employee e = (Employee) obj;

W Javie tylko typy podstawowe (liczby, znaki i wartoci logiczne) nie s obiektami.
Wszystkie typy tablicowe bez wzgldu na to, czy przechowuj obiekty, czy typy podstawowe s typami klasowymi rozszerzajcymi klas Object.
Employee[] staff = new Employee[10];
obj = staff;
// OK
obj = new int[10];
// OK

W jzyku C++ nie ma uniwersalnej klasy bazowej, jednak kady wskanik mona
przekonwertowa na wskanik void*.

5.2.1. Metoda equals


Dostpna w klasie Object metoda equals porwnuje dwa obiekty. Jej implementacja w klasie
Object sprawdza, czy dwie referencje do obiektw s identyczne. Jest to bardzo rozsdne dziaanie domylne jeli dwa obiekty s identyczne, powinny by sobie rwne. W przypadku
wielu klas nie potrzeba nic wicej. Na przykad porwnywanie dwch obiektw PrintStream
pod ktem rwnoci nie ma wikszego sensu, jednak czsto potrzebne jest porwnywanie
stanw, czyli sytuacji, w ktrej dwa obiekty s rwne, jeli maj ten sam stan.
Na przykad dwch pracownikw uznamy za rwnych, jeli maj identyczne imi i nazwisko,
pensj i zostali zatrudnieni w tym samym czasie (w prawdziwej bazie danych pracownikw
lepiej byoby porwna identyfikatory pracownikw; ten przykad ma na celu zobrazowanie
mechanizmw implementacyjnych metody equals).
class Employee
{
. . .
public boolean equals(Object otherObject)
{
// Szybkie sprawdzenie, czy obiekty s identyczne.
if (this == otherObject) return true;
// Musi zwrci false, jeli parametr jawny ma warto null.
if (otherObject == null) return false;
// Jeli klasy nie pasuj, nie mog by rwne.
if (getClass() != otherObject.getClass())
return false;
// Wiadomo, e otherObject nie jest obiektem null klasy Employee.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

222

Java. Podstawy
Employee other = (Employee) otherObject;
// Sprawdzenie, czy pola maj identyczne wartoci.
return name.equals(other.name)
&& salary == other.salary
&& hireDay.equals(other.hireDay);
}
}

Metoda getClass zwraca nazw klasy obiektu szczegowy opis tej metody znajduje si
w dalszej czci tego rozdziau. W naszym tecie dwa obiekty mog by rwne tylko wtedy,
kiedy nale do tej samej klasy.
Aby zabezpieczy si na wypadek, gdyby zmienne name lub hireDay byy null,
mona uy metody Objects.equals. Wywoanie Objects.equals(a, b) zwraca
true, jeli oba argumenty s null, false jeli jeden z argumentw jest null, a w pozostaych przypadkach wywouje a.equals(b). Przy uyciu tej metody ostatni instrukcj
w metodzie Employee.equals mona zmieni nastpujco:
return Objects.equals(name, other.name)
&& salary == other.salary
&& Object.equals(hireDay, other.hireDay);

Implementujc metod equals w podklasie, najpierw naley wywoa metod equals nalec do nadklasy. Jeli test zakoczy si niepowodzeniem, obiekty nie mog by rwne. Jeli
pola nadklasy s rwne, mona porwnywa skadowe obiektw podklasy.
class Manager extends Employee
{
. . .
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;
// Metoda super.equals sprawdzia, czy this i otherObject nale do tej samej klasy.
Manager other = (Manager) otherObject;
return bonus == other.bonus;
}
}

5.2.2. Porwnywanie a dziedziczenie


Jak powinna si zachowa metoda equals, jeli parametry jawny i niejawny nie nale do tej
samej klasy? Jest to do kontrowersyjna kwestia. W poprzednim przykadzie metoda equals
zwracaa warto false, jeli klasy nie byy identyczne. Jednak wielu programistw zamiast tej
metody uywa operatora instanceof:
if (!(otherObject instanceof Employee)) return false;

Dziki temu obiekt otherObject moe nalee do podklasy klasy Employee. Metoda ta moe
jednak sprawia problemy. Specyfikacja jzyka Java wymaga, aby metoda equals miaa nastpujce wasnoci:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.
1.

Dziedziczenie

223

Zwrotno: x.equals(x) powinno zwraca true, jeli x nie ma wartoci null.

2. Symetria: dla dowolnych referencji x i y, x.equals(y) powinno zwrci warto


true wtedy i tylko wtedy, gdy y.equals(x) zwrci warto true.
3. Przemienno: dla dowolnych referencji x, y i z, jeli x.equals(y) zwraca warto
true i y.equals(z) zwraca true, to x.equals(y) zwraca t sam warto.
4. Niezmienno: jeli obiekty, do ktrych odwouj si zmienne x i y, nie zmieniy
si, kolejne wywoania x.equals(y) zwracaj t sam warto.
5. Dla kadego x rnego od null, wywoanie x.equals(null) powinno zwrci
warto false.

Powysze reguy s oczywicie podyktowane zdrowym rozsdkiem. Programista biblioteki nie


powinien by zmuszany do tracenia czasu na podejmowanie decyzji, czy wywoa x.equals(y),
czy y.equals(x), aby zlokalizowa jaki element w strukturze danych.
Jednak w przypadku gdy parametry nale do rnych klas, regua symetrii moe pocign za
sob pewne konsekwencje. Przeanalizujmy ponisze wywoanie:
e.equals(m)

gdzie e jest obiektem klasy Employee, a m klasy Manager. Tak si skada, e kady z nich ma
takie samo imi i nazwisko, pensj i dat zatrudnienia. Jeli wywoanie Employee.equals
uyje operatora instanceof, zostanie zwrcona warto true. Oznacza to jednak, e wywoanie odwrotne:
m.equals(e)

take musi zwrci warto true regua symetrii nie zezwala na zwrcenie wartoci false
ani spowodowanie wyjtku.
To krpuje klas Manager. Jej metoda equals zmusz ja do porwnywania si z klas Employee
bez brania pod uwag informacji waciwych tylko kierownikom! Nagle operator instanceof przesta wydawa si tak atrakcyjny!
Niektrzy twierdz, e test getClass jest bdny, poniewa amie regu zamienialnoci. Czsto
przytaczany jest przykad metody equals w klasie AbstractSet, ktra sprawdza, czy dwa
zbiory maj te same elementy. Klasa AbstractSet ma dwie konkretne podklasy: TreeSet
i HashSet. Kada z nich lokalizuje elementy za pomoc innego algorytmu. Bez wzgldu na
implementacj potrzebna jest moliwo porwnywania zbiorw.
Jednak przykad zbioru dotyczy raczej wskiej specjalizacji. Mona by byo zdefiniowa metod
AbstractSet.equals jako finaln, poniewa nikt nie powinien zmienia semantyki rwnoci
zbiorw (obecnie metoda ta nie jest finalna, dziki czemu moliwe jest zaimplementowanie
w podklasie bardziej efektywnego algorytmu porwnujcego).
Z naszego punktu widzenia moliwe s dwa odrbne scenariusze:

Jeli podklasy maj wasny mechanizm porwnywania, regua symetrii zmusza


nas do uycia testu getClass.

Jeli mechanizm porwnujcy jest ustalony w nadklasie, mona uy operatora


instanceof, pozwalajc, aby obiekty rnych podklas byy rwne.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

224

Java. Podstawy
W przypadku klas Employee i Manager dwa obiekty uznajemy za rwne, jeli maj takie same
pola. Jeli dwa obiekty klasy Manager maj takie same imiona i nazwiska, pensje i daty zatrudnienia, ale rne dodatki do pensji, chcemy, aby byy uznane za rne. Dlatego uylimy testu
getClass.
Zamy jednak, e do porwnywania uylimy identyfikatorw pracownikw. Ten rodzaj
porwnania jest odpowiedni dla wszystkich podklas. W takim przypadku moglibymy zastosowa operator instanceof, a metod Employee.equals zadeklarowa jako finaln.
Standardowa biblioteka Javy zawiera ponad 150 implementacji metody equals.
Znajduj si wrd nich wersje z operatorem instanceof, wywoaniem getClass,
przechwytywaniem wyjtku ClassCastException i nierobice nic. W dokumentacji API
klasy java.sql.Timestamp mona znale notatk, w ktrej implementatorzy sami ze
wstydem przyznaj, e zapdzili si w kozi rg. Klasa java.sql.Timestamp dziedziczy po
klasie java.util.Date, ktrej metoda equals wykorzystuje operator instanceof. Nie da
si przesoni metody equals, aby bya dokadna i symetryczna.

Poniej znajduje si opis procedury pisania idealnej metody equals:


1.

Nadaj parametrowi jawnemu nazw otherObject pniej konieczne bdzie


rzutowanie go na inn zmienn, ktra powinna mie nazw other.

2. Sprawd, czy this i otherObject s identyczne:


if (this == otherObject) return true;

Ta instrukcja suy tylko do optymalizacji. Jest to do czsto spotykany przypadek.


atwiej sprawdzi tosamo obiektw, ni porwnywa ich pola.
3. Sprawd, czy obiekt otherObject jest rwny null, i zwr warto false, jeli jest.
if (otherObject == null) return false;

4. Porwnaj klasy obiektw this i otherObject. Jeli semantyka metody equals


moe zmieni si w podklasach, uyj testu getClass:
if (getClass() != otherObject.getClass()) return false;

Jeli wszystkie podklasy korzystaj z tej samej semantyki, mona uy operatora


instanceof:
if (!(otherObject instanceof ClassName)) return false;

5. Rzutuj obiekt otherObject na zmienn typu swojej klasy:


ClassName other = (ClassName) otherObject

6. Porwnaj pola zgodnie z wasnymi wymaganiami. Dla pl typw podstawowych


uyj operatora ==, a metody Objects.equals dla obiektw. Zwr warto true,
jeli wszystkie pola si zgadzaj, lub false w przeciwnym przypadku.
return field1 == other.field1
&& field2.equals(other.field2)
&& . . .;

Jeli przedefiniujesz metod equals w podklasie, uyj odwoania


super.equals(other).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

225

Do porwnania elementw dwch tablic mona uy statycznej metody Arrays.


equals.

Poniej znajduje si czsto spotykany bd w implementacji metody equals. Na


czym on polega?
public class Employee
{
public boolean equals(Employee other)
{
return Objects.equals(name, other.name)
&& salary == other.salary
&& Objects.equals(hireDay, other.hireDay);
}
...
}

Ta metoda deklaruje parametr jawny jako typ Employee. W wyniku tego nie przesania ona
metody equals z klasy Object, ale tworzy zupenie now metod.
Mona chroni si przed tego typu bdem, oznaczajc metody majce przesania
metody nadklasy znacznikiem @Override:
@Override public boolean equals(Object other)

Jeli zostanie popeniony bd i zdefiniowana nowa metoda, kompilator zgosi bd.


Przypumy na przykad, e dodano ponisz deklaracj do klasy Employee:
@Override public boolean equals(Employee other)

Zostanie zgoszony bd, poniewa ta metoda nie przesania adnej metody w nadklasie Object.
java.util.Arrays 1.2

static boolean equals(typ[] a, typ[] b) 5.0

Zwraca warto true, jeli tablice s rwne pod wzgldem liczby elementw
i elementy te s takie same na odpowiadajcych sobie pozycjach w obu tablicach.
Tablice te mog przechowywa elementy nastpujcych typw: Object, int, long,
short, char, byte, boolean, float, double.
java.util.Objects 7

static boolean equals(Object a, Object b)

Zwraca warto true, jeli a i b s null, false jeli a lub b jest null,
oraz a.equals(b) w pozostaych przypadkach.

5.2.3. Metoda hashCode


Kod mieszajcy (ang. hash code) to skrt do obiektu w postaci pochodzcej od niego liczby
cakowitej. Kody mieszajce powinny mie rne wartoci. To znaczy, e jeli x i y to dwa
rne obiekty, powinno istnie wysokie prawdopodobiestwo, e x.hashCode() i y.hashCode()

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

226

Java. Podstawy
to dwie rne liczby. Tabela 5.1 przedstawia trzy przykadowe kody mieszajce zwrcone
przez metod hashCode klasy String.

Tabela 5.1. Kody mieszajce zwrcone przez metod hashCode


acuch

Kod mieszajcy

Witaj

83588971

Henryk

-2137002381

Kwiatek

1350142454

Klasa String oblicza kody mieszajce za pomoc nastpujcego algorytmu:


int hash = 0;
for (int i = 0; i < length(); i++)
hash = 31 * hash + charAt(i);

Metoda hashCode znajduje si w klasie Object. Dlatego kady obiekt ma domylny kod mieszajcy, ktry jest derywowany od adresu obiektu w pamici. Przeanalizujmy poniszy kod:
String s = "OK";
StringBuilder sb = new StringBuilder(s);
System.out.println(s.hashCode() + " " + sb.hashCode());
String t = new String("OK");
StringBuilder tb = new StringBuilder(t);
System.out.println(t.hashCode() + " " + tb.hashCode());

Wynik przedstawia tabela 5.2.


Tabela 5.2. Kody mieszajce acuchw i obiektw klasy StringBuilder
Obiekt

Kod mieszajcy

2556

sb

20526976

2556

tb

205271144

Naley zauway, e acuchy s i t maj takie same kody, poniewa kody mieszajce acuchw s pochodnymi ich zawartoci. Obiekty sb i tb maj rne kody, poniewa dla klasy
StringBuilder nie zdefiniowano metody hashCode. W zwizku z tym domylna metoda hashCode
klasy Object utworzya ich kody mieszajce na podstawie adresw w pamici.
W przypadku przedefiniowania metody equals naley take przedefiniowa metod hashCode
dla obiektw, ktre uytkownicy mog wstawia do tablicy mieszajcej (ang. hash table)
tablice mieszajce zostay opisane w rozdziale 13.
Metoda hashCode powinna zwraca liczb cakowit (moe by ujemna). Aby kody mieszajce rnych obiektw byy rne, wystarczy uy kombinacji kodw mieszajcych pl tych
obiektw.
Poniej znajduje si przykadowa metoda hashCode klasy Employee:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

227

class Employee
{
public int hashCode()
{
return 7 * name.hashCode()
+ 11 * new Double(salary).hashCode()
+ 13 * hireDay.hashCode();
}
. . .
}

Od Java 7 mona tu dokona dwch udoskonale. Po pierwsze, lepiej jest uy zabezpieczonej przed null metody Objects.hashCode, ktra zwraca 0, jeli jej argument jest null, albo
wynik wywoania hashCode na tym argumencie w pozostaych przypadkach.
public int hashCode()
{
return 7 * Objects.hashCode(name)
+ 11 * new Double(salary).hashCode()
+ 13 * Objects.hashCode(hireDay);
}

Po drugie, gdy trzeba poczy kilka wartoci skrtu (ang. hash value), mona wywoa
metod Objects.hash, przekazujc jej wszystkie te wartoci. Spowoduje to wywoanie metody
Objects.hashCode dla kadego argumentu i poczenie wartoci. Wwczas metoda Employee.
hashCode moe by o wiele prostsza:
public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}

Definicje metod equals i hashCode musz si ze sob zgadza jeli x.equals(y) zwraca
warto true, to x.hashCode() musi mie tak sam warto jak y.hashCode(). Jeli na przykad
metoda Employee.equals porwnuje identyfikatory pracownikw, metoda hashCode musi
miesza identyfikatory, a nie imiona i nazwiska pracownikw lub adresy w pamici.
Jeli pola s typu tablicowego, mona uy metody Arrays.hashCode, ktra oblicza
kod mieszajcy zoony z kodw mieszajcych elementw tablicy.
java.lang.Object 1.0

int hashCode()

Zwraca kod mieszajcy obiektu. Kod ten moe by kad dodatni lub ujemn
liczb cakowit. Identyczne obiekty powinny mie takie same kody mieszajce.
java.lang.Objects 7

int hash(Object... objects)

Zwraca warto skrtu bdc kombinacj wartoci skrtu wszystkich podanych


obiektw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

228

Java. Podstawy

static int hashCode(Object a)

Zwraca 0, jeli a jest null, lub a.hashCode() w pozostaych przypadkach.


java.util.Arrays 1.2

static int hashCode(typ[] a) 5.0

Oblicza kod mieszajcy tablicy a, ktra moe przechowywa elementy nastpujcych


typw: Object, int, long, short, char, byte, boolean, float, double.

5.2.4. Metoda toString


Kolejn wan metod klasy Object jest metoda toString, ktra zwraca obiekt reprezentujcy
warto obiektu. Z typowym przykadem jej zastosowania mamy do czynienia, gdy metoda
toString klasy Point zwraca nastpujcy acuch:
java.awt.Point[x=10,y=20]

Wikszo metod toString (ale nie wszystkie) ma nastpujcy format: nazwa klasy plus
wartoci pl wymienione w nawiasach kwadratowych. Poniej znajduje si implementacja
metody toString w klasie Employee:
public String toString()
{
return "Employee[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}

Mona to jednak zrobi nieco lepiej. Zamiast na sztywno wpisywa nazw klasy w metodzie toString, nazw t mona pobra za pomoc wywoania getClass().getName().
public String toString()
{
return getClass().getName()
+ "[name=" + name
+ ",salary=" + salary
+ ",hireDay=" + hireDay
+ "]";
}

Dziki temu metoda ta bdzie dziaaa take w podklasach.


Oczywicie twrca podklasy powinien zdefiniowa wasn metod toString, uwzgldniajc pola tej klasy. Jeli w nadklasie uyto wywoania getClass().getName(), w podklasie
mona uy wywoania super.toString(). Poniej znajduje si przykadowa metoda toString
klasy Manager:
class Manager extends Employee
{
. . .
public String toString()

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

229

{
return super.toString()
+ "[bonus=" + bonus
+ "]";
}
}

Wydruk zawartoci obiektu Manager wygldaby nastpujco:


Manager[name=...,salary=...,hireDay=...][bonus=...]

Metoda toString jest bardzo czsto uywana z jednego wanego powodu: za kadym razem,
gdy obiekt jest czony z acuchem za pomoc operatora +, kompilator automatycznie
wywouje metod toString, aby utworzy acuchow reprezentacj obiektu. Na przykad:
Point p = new Point(10, 20);
String message = "Aktualne pooenie to " + p;
// Automatyczne wywoanie p.toString().

Zamiast x.toString() mona napisa "" + x. Ta instrukcja czy pusty acuch


z acuchow reprezentacj x, czyli robi dokadnie to samo co x.toString().
W przeciwiestwie do metody toString, ta instrukcja dziaa nawet wtedy, gdy x jest
typu podstawowego.

Jeli x jest dowolnego typu, w wywoaniu:


System.out.println(x);

metoda println wywouje x.toString() i drukuje powstay w ten sposb acuch.


Klasa Object zawiera metod toString, ktra drukuje nazw klasy i kod mieszajcy obiektu.
Na przykad wywoanie:
System.out.println(System.out)

zwrci wynik podobny do poniszego:


java.io.PrintStream@187aeca

Jest to spowodowane tym, e programista implementujcy klas PrintStream nie zada sobie
trudu, aby przesoni metod toString.
Metoda toString jest doskonaym narzdziem do rejestracji danych. Wiele klas biblioteki standardowej zawiera metod toString, ktra umoliwia uzyskanie informacji na temat stanu
obiektu. Jest to szczeglnie przydatne w przypadku komunikatw rejestracyjnych typu:
System.out.println("Aktualne pooenie = " + position);

Jak piszemy w rozdziale 11., jeszcze lepszym rozwizaniem jest uycie obiektu klasy Logger
i zastosowanie nastpujcego wywoania:
Logger.global.info("Aktualne pooenie = " + position);

Program na listingu 5.8 zawiera implementacje metod equals, hashCode oraz toString w klasach Employee (listing 5.9) i Manager (listing 5.10).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

230

Java. Podstawy

Niestety tablice dziedzicz metod toString po klasie Object, przez co typ tablicy
jest drukowany w przestarzaym formacie. Na przykad:
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
String s = "" + luckyNumbers;

Powyszy kod zwraca acuch typu [I@e48e1b (przedrostek [I oznacza tablic liczb
cakowitych). Mona temu zaradzi poprzez wywoanie metody Arrays.toString.
Poniszy kod:
String s = Arrays.toString(luckyNumbers);

zwrci acuch [2, 3, 5, 7, 11, 13].


Aby prawidowo wydrukowa zawarto tablicy wielowymiarowej (tzn. tablicy tablic), naley
uy metody Arrays.deepToString.

Zdecydowanie zalecamy dodawanie metody toString do kadej nowej klasy. Kady,


kto bdzie tych klas uywa, z pewnoci doceni uatwienia dotyczce rejestracji
danych.
Listing 5.8. equals/EqualsTest.java
package equals;
/**
* Jest to program demonstrujcy uycie metody equals.
* @version 1.12 2012-01-26
* @author Cay Horstmann
*/
public class EqualsTest
{
public static void main(String[] args)
{
Employee alice1 = new Employee("Alicja Adamczuk", 75000, 1987, 12, 15);
Employee alice2 = alice1;
Employee alice3 = new Employee("Alicja Adamczuk", 75000, 1987, 12, 15);
Employee bob = new Employee("Bartosz Borkowski", 50000, 1989, 10, 1);
System.out.println("alice1 == alice2: " + (alice1 == alice2));
System.out.println("alice1 == alice3: " + (alice1 == alice3));
System.out.println("alice1.equals(alice3): " + alice1.equals(alice3));
System.out.println("alice1.equals(bob): " + alice1.equals(bob));
System.out.println("bob.toString(): " + bob);
Manager carl = new Manager("Karol Krakowski", 80000, 1987, 12, 15);
Manager boss = new Manager("Karol Krakowski", 80000, 1987, 12, 15);
boss.setBonus(5000);
System.out.println("boss.toString(): " + boss);
System.out.println("carl.equals(boss): " + carl.equals(boss));
System.out.println("alice1.hashCode(): " + alice1.hashCode());
System.out.println("alice3.hashCode(): " + alice3.hashCode());

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

System.out.println("bob.hashCode(): " + bob.hashCode());


System.out.println("carl.hashCode(): " + carl.hashCode());
}
}

Listing 5.9. equals/Employee.java


package equals;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Objects;
public class Employee
{
private String name;
private double salary;
private Date hireDay;
public Employee(String n, double s, int year, int month, int day)
{
name = n;
salary = s;
GregorianCalendar calendar = new GregorianCalendar(year, month - 1, day);
hireDay = calendar.getTime();
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}
public Date getHireDay()
{
return hireDay;
}
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
public boolean equals(Object otherObject)
{
// Sprawdzenie, czy obiekty s identyczne
if (this == otherObject) return true;
// Musi zwrci false, jeli jawny parametr jest null
if (otherObject == null) return false;
// Jeli klasy nie zgadzaj si, nie mog by jednakowe

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

231

232

Java. Podstawy
if (getClass() != otherObject.getClass()) return false;
// Teraz wiadomo, e otherObject jest typu Employee i nie jest null
Employee other = (Employee) otherObject;
// Sprawdzenie, czy pola maj identyczne wartoci
return Objects.equals(name, other.name) && salary == other.salary &&
Objects.equals(hireDay, other.hireDay);
}
public int hashCode()
{
return Objects.hash(name, salary, hireDay);
}
public String toString()
{
return getClass().getName() + "[name=" + name + ",salary=" + salary +
",hireDay=" + hireDay
+ "]";
}
}

Listing 5.10. equals/Manager.java


package equals;
public class Manager extends Employee
{
private double bonus;
public Manager(String n, double s, int year, int month, int day)
{
super(n, s, year, month, day);
bonus = 0;
}
public double getSalary()
{
double baseSalary = super.getSalary();
return baseSalary + bonus;
}
public void setBonus(double b)
{
bonus = b;
}
public boolean equals(Object otherObject)
{
if (!super.equals(otherObject)) return false;
Manager other = (Manager) otherObject;
// Mtoda super.equals okrelia, e obiekty nale do tej samej klasy
return bonus == other.bonus;
}
public int hashCode()

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

233

{
return super.hashCode() + 17 * new Double(bonus).hashCode();
}
public String toString()
{
return super.toString() + "[bonus=" + bonus + "]";
}
}
java.lang.Object 1.0

Class getClass()

Zwraca obiekt Class zawierajcy informacje dotyczce klasy obiektu. W dalszej


czci tego rozdziau dowiemy si, e w Javie istnieje zamknita w klasie Class
reprezentacja czasu wykonywania klas.

boolean equals(Object otherObject)

Porwnuje dwa obiekty. Zwraca warto true, jeli oba obiekty wskazuj ten
sam obszar pamici, lub false w przeciwnym przypadku. We wasnych klasach
powinno si t metod przesania.

String toString()

Zwraca acuch reprezentujcy warto obiektu. We wasnych klasach powinno


si t metod przesania.
java.lang.Class 1.0

String getName()

Zwraca nazw klasy.

Class getSuperClass()

Zwraca nadklas danej klasy w postaci obiektu klasy Class.

5.3. Generyczne listy tablicowe


W wielu jzykach programowania, zwaszcza w C, rozmiary wszystkich tablic musz by
ustalone w czasie kompilacji. Nie podoba si to programistom, poniewa zmusza ich to do
niewygodnych kompromisw. Ilu pracownikw bdzie zatrudnia dany dzia? Z pewnoci
nie wicej ni 100. Co zrobi, jeli jeden dzia bdzie zatrudnia a 150 pracownikw? Czy
dla kadego dziau z tylko 10 pracownikami konieczne jest marnowanie 90 pozostaych
miejsc?
W Javie sytuacja ta przedstawia si znacznie lepiej. Rozmiar tablicy mona ustawi w trakcie
dziaania programu.
int actualSize = . . .;
Employee[] staff = new Employee[actualSize];

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

234

Java. Podstawy
Oczywicie powyszy fragment kodu nie rozwizuje w peni problemu dynamicznej zmiany
rozmiaru tablic w trakcie dziaania programu. Kiedy rozmiar tablicy jest ustalony, nie mona go
atwo zmieni. W Javie najprostszy sposb na poradzenie sobie z tak czsto spotykan
sytuacj jest uycie klasy o nazwie ArrayList. Obiekty tej klasy s podobne do tablic, z t
rnic, e automatycznie dostosowuj swoje rozmiary w wyniku dodawania i odejmowania
elementw. Nie wymaga to adnych modyfikacji kodu.
ArrayList jest klas generyczn z parametrem typu. Aby okreli typ obiektw prze-

chowywanych przez list tablicow, naley poda nazw klasy w nawiasach ostrych, np.
ArrayList<Employee>. Sposb definiowania klas generycznych zosta opisany w rozdziale 13.,
cho znajomo tych technik nie jest konieczna, aby mc uywa typu ArrayList.

Poniszy kod deklaruje i tworzy list tablicow przechowujc obiekty klasy Employee:
ArrayList<Employee> staff = new ArrayList<Employee>();

Wpisywanie parametru typu po obu stronach jest troch mudne. Dlatego w Java 7 parametr
ten mona po prawej stronie opuci.
ArrayList<Employee> staff = new ArrayList<>();

Jest to tzw. skadnia diamentowa (ang. diamond syntax), nazwana tak ze wzgldu na to, e
pusty nawias <> przypomina ksztatem diament. Naley j stosowa w poczeniu z operatorem new. Kompilator sprawdza, co si dzieje z now wartoci. Jeeli zostaje przypisana do
zmiennej, przekazana do metody lub zwrcona przez metod, to kompilator sprawdza oglny
typ tej zmiennej lub tego parametru albo tej metody. Nastpnie wstawia ten typ w nawias <>.
W przedstawionym przykadzie znajduje si przypisanie new ArrayList() do zmiennej typu
ArrayList<Employee>, wic typ oglny to Employee.
Przed wersj 5.0 w Javie nie byo klas generycznych. Zamiast tego bya sama
klasa ArrayList, ktra moga przechowywa elementy typu Object. Nadal mona
uywa klasy ArrayList bez <>. Jest to tak zwany typ surowy (ang. raw), w ktrym usunito parametr typu.

W jeszcze starszych wersjach Javy tablice dynamiczne tworzono za pomoc klasy


Vector. Klasa ArrayList jest jednak bardziej efektywna i nie ma powodu do uywania starej klasy Vector.

Nowe elementy do listy s dodawane za pomoc metody add. Poniszy fragment kodu
zapenia list tablicow pracownikami:
staff.add(new Employee("Henryk Kwiatek", . . .));
staff.add(new Employee("Waldemar Kowalski", . . .));

Lista tablicowa zawiera wewntrzn tablic referencji do obiektw. Kiedy skoczy si miejsce w tej tablicy, lista wykonuje swoje magiczne sztuczki. Jeli wewntrzna tablica jest pena
i zostanie wywoana metoda add, lista automatycznie utworzy wiksz tablic i skopiuje do
niej wszystkie obiekty.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

235

Jeli liczba elementw, ktre bd przechowywane w tablicy, jest znana, przynajmniej w przyblieniu, przed zapenieniem listy naley wywoa metod ensureCapacity.
staff.ensureCapacity(100);

Powysze wywoanie zarezerwuje miejsce dla wewntrznej tablicy mogcej przechowywa


100 elementw. Dziki temu 100 pierwszych wywoa metody add nie spowoduje czasochonnej realokacji.
Pocztkow pojemno listy mona take przekaza do konstruktora klasy ArrayList:
ArrayList<Employee> staff = new ArrayList<>(100);

Alokacja listy tablicowej:


new ArrayList<>(100)

// Pojemno 100

nie jest tym samym co alokacja nowej tablicy:


new Employee[100]

// Rozmiar 100

Pomidzy pojemnoci listy tablicowej a rozmiarem tablicy jest dua rnica. Tablica
o rozmiarze 100 zawiera 100 miejsc, w ktrych moe przechowywa dane. Lista tablicowa o pojemnoci 100 ma moliwo przechowywania 100 elementw (a nawet
wicej kosztem dodatkowej realokacji). Jednak na pocztku, nawet po jej utworzeniu,
lista tablicowa nie zawiera adnych elementw.

Metoda size sprawdza liczb elementw w licie tablicowej. Na przykad wywoanie:


staff.size()

zwrci biec liczb elementw w licie tablicowej staff. To wywoanie jest odpowiednikiem wywoania:
a.length

dla tablicy a.
Kiedy jest ju pewne, e rozmiar listy tablicowej si nie zmieni, mona wywoa metod trim
ToSize. Metoda ta dostosowuje rozmiar bloku pamici przechowujcego list dokadnie
do aktualnego rozmiaru listy. Nadmiar pamici zostanie wyczyszczony przez system zbierania nieuytkw.
Jeli po dopasowaniu rozmiaru listy zostan dodane do niej nowe elementy, blok ponownie
zostanie przeniesiony, co zabiera czas. Metody trimToSize naley uywa wycznie wtedy,
gdy jest pewne, e do listy tablicowej nie bd dodawane ju nowe elementy.
Klasa ArrayList przypomina dostpny w C++ szablon vector. Jedno i drugie jest
typem generycznym. Ale szablon vector przecia operator [], dziki czemu
dostp do elementw jest wygodniejszy. Poniewa w Javie nie mona przecia operatorw, trzeba uywa do tego celu metod. Ponadto wektory w C++ s kopiowane
przez warto. Jeli a i b s wektorami, przypisanie a = b tworzy nowy wektor a o takiej
samej dugoci jak b i kopiuje wszystkie elementy z b do a. Takie samo przypisanie w Javie
powoduje, e zarwno a, jak i b odwouj si do tej samej listy tablicowej.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

236

Java. Podstawy
java.util.ArrayList<T> 1.2

ArrayList<T>()

Tworzy pust list tablicow.

ArrayList<T>(int initialCapacity)

Tworzy pust list tablicow o podanej pojemnoci.


Parametry:

initialCapacity

Pocztkowa pojemno listy

boolean add(T obj)

Dodaje element na kocu listy. Zawsze zwraca warto true.


Parametry:

obj

Element do dodania

int size()

Zwraca liczb elementw aktualnie przechowywanych w licie (warto ta nie


moe by wiksza ni pojemno listy).

void ensureCapacity(int capacity)

Zapewnia, e lista bdzie miaa wystarczajc pojemno do przechowywania


danej liczby elementw, bez potrzeby realokacji wewntrznej tablicy.
Parametry:

capacity

Docelowa pojemno listy

void trimToSize()

Redukuje pojemno listy do jej aktualnego rozmiaru.

5.3.1. Dostp do elementw listy tablicowej


Niestety nie ma nic za darmo. Cen za wygod zwizan z automatycznym powikszaniem
si listy tablicowej jest bardziej skomplikowany dostp do jej elementw. Powd jest taki,
e klasa ArrayList nie naley do jzyka Java zostaa utworzona przez jakiego programist i dodana do standardowej biblioteki.
Zamiast skadni z operatorem [], aby uzyska dostp do obiektw w celu ich odczytania
lub modyfikacji, konieczne jest stosowanie metod get i set.
Aby na przykad ustawi warto i-tego elementu, naley napisa:
staff.set(i, harry);

Powyszy zapis jest odpowiednikiem poniszego:


a[i] = harry;

dla tablicy a (tak samo jak w tablicach, numerowanie indeksw zaczyna si od zera).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

237

Nie naley wywoywa list.set(i, x), jeli rozmiar tablicy nie jest wikszy od i.
Na przykad poniszy kod jest bdny:
ArrayList<Employee> list = new ArrayList<>(100);
list.set(0, x);

// Pojemno 100, rozmiar 0


// Nie ma jeszcze elementu 0

Do zapeniania tablicy uywaj metody add zamiast set, ktr naley stosowa tylko
w celu podmiany wczeniej dodanego elementu.

Aby pobra warto elementu listy tablicowej, naley napisa:


Employee e = staff.get(i);

Zapis ten jest odpowiednikiem poniszego:


Employee e = a[i];

Gdy nie byo generycznych klas, metoda get surowej klasy ArrayList nie miaa
innego wyjcia, jak zwraca obiekty klasy Object. Z tego powodu wywoujcy t
metod musia rzutowa zwrcon warto na odpowiedni typ:
Employee e = (Employee) staff.get(i);

Ponadto surowa klasa ArrayList nie jest w peni bezpieczna. Jej metody add i set
przyjmuj obiekty kadego typu. Ponisze wywoanie:
staff.set(i, new Date());

w czasie kompilacji spowoduje tylko ostrzeenie, a problemy zaczn si dopiero po


uzyskaniu obiektu i prbie rzutowania go. Przy uyciu ArrayList<Employee> kompilator
wykryje ten bd.

Czasami moliwe jest wzicie tego, co najlepsze, z tablic i list tablicowych elastycznoci
i wygodnego dostpu do elementw. Trzeba zastosowa nastpujc sztuczk. Naley utworzy list tablicow i wstawi do niej wszystkie potrzebne elementy:
ArrayList<X> list = new ArrayList<>();
while (. . .)
{
x = . . .;
list.add(x);
}

Nastpnie wszystkie elementy naley skopiowa do tablicy za pomoc metody toArray:


X[] a = new X[list.size()];
list.toArray(a);

Aby doda element w rodku listy, naley uy metody add z parametrem okrelajcym indeks:
int n = staff.size() / 2;
staff.add(n, e);

Elementy znajdujce si na pozycjach od n do gry s przesuwane, aby zrobi miejsce dla


nowego elementu. Jeli nowy rozmiar listy przekracza jej pojemno, nastpuje realokacja
wewntrznej tablicy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

238

Java. Podstawy
Podobnie ze rodka listy mona usun dowolny element:
Employee e = staff.remove(n);

Elementy znajdujce si nad nim zostan skopiowane w d, a rozmiar tablicy zostanie


zmniejszony o jeden.
Operacje wstawiania i usuwania elementw nie nale do najefektywniejszych. W przypadku
maych list nie ma raczej czym si przejmowa, ale jeli elementw jest duo i s one czsto wstawiane do rodka kolekcji i z niej usuwane, naley rozway uycie listy dwukierunkowej (ang. linked list). Zagadnienia zwizane z listami dwukierunkowymi zostay poruszone w rozdziale 13.
Od Java 5.0 zawarto listy tablicowej mona przemierza za pomoc ptli typu for each:
for (Employee e : staff)
dziaania zwizane z e

Ta ptla daje taki sam rezultat jak ponisza:


for (int i = 0; i < staff.size(); i++)
{
Employee e = staff.get(i);
dziaania zwizane z e
}

Listing 5.11 przedstawia zmodyfikowan wersj programu EmployeeTest z rozdziau 4.


Tablica Employee[] zostaa zastpiona list tablicow ArrayList<Employee>. Naley zwrci
uwag na nastpujce zmiany:

Nie ma koniecznoci okrelenia rozmiaru tablicy.

Za pomoc metody add mona doda dowoln liczb elementw.

Do sprawdzenia liczby elementw zostaa uyta metoda size() zamiast metody


length.

Dostp do elementu daje wywoanie a.get(i) zamiast a[i].

Listing 5.11. arrayList/ArrayListTest.java


package arrayList;
import java.util.*;
/**
* Ten program demonstruje uycie klasy ArrayList.
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class ArrayListTest
{
public static void main(String[] args)
{
// Wstawienie do listy staff trzech obiektw klasy Employee.
ArrayList<Employee> staff = new ArrayList<>();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

239

staff.add(new Employee("Karol Krakowski", 75000, 1987, 12, 15));


staff.add(new Employee("Henryk Kwiatek", 50000, 1989, 10, 1));
staff.add(new Employee("Waldemar Kowalski", 40000, 1990, 3, 15));
// Zwikszenie pensji wszystkich pracownikw o 5%.
for (Employee e : staff)
e.raiseSalary(5);
// Drukowanie informacji o wszystkich obiektach Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary()
+ ",hireDay="
+ e.getHireDay());
}
}
java.util.ArrayList<T> 1.2

void set(int index, T obj)

Wstawia warto do listy tablicowej w miejscu o okrelonym indeksie, nadpisuje


poprzedni zawarto.
Parametry:

index

Pozycja (warto od 0 do size() - 1)

obj

Nowa warto

T get(int index)

Pobiera warto zapisan w okrelonym indeksie.


Parametry:

index

Indeks elementu, ktry ma zosta pobrany


(warto od 0 do size() - 1)

void add(int index, T obj)

Dodaje element i przesuwa pozostae do gry.


Parametry:

index

Indeks wstawianego elementu (warto od 0


do size() - 1)

obj

Nowy element

T remove(int index)

Usuwa element i przesuwa w d wszystkie elementy, ktre znajduj si nad nim.


Usunity element jest zwracany.
Parametry:

index

Indeks elementu, ktry ma zosta usunity


(warto od 0 do size() - 1)

5.3.2. Zgodno pomidzy typowanymi a surowymi listami tablicowymi


Piszc wasny program, ze wzgldw bezpieczestwa naley zawsze uywa parametrw
typu. W tej czci dowiesz si, jak korzysta ze starego kodu, w ktrym parametry te nie
zostay uyte.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

240

Java. Podstawy
Majc ponisz star klas:
public class EmployeeDB
{
public void update(ArrayList list) { ... }
public ArrayList find(String query) { ... }
}

list tablicow z typem mona przekaza do metody update bez rzutowania.


ArrayList<Employee> staff = ...;
employeeDB.update(staff);

Do metody update przekazywany jest obiekt staff.


Mimo e kompilator nie zgasza adnego bdu ani ostrzeenia, to wywoanie nie
jest w peni bezpieczne. Metoda update moe doda do listy tablicowej elementy,
ktre nie s typu Employee. Kiedy te elementy s pobierane, powstaje wyjtek. Mimo
e brzmi to strasznie, dziaanie to jest dokadnie takie samo jak przed Java SE 5.0.
Integralno maszyny wirtualnej nigdy nie jest zagroona. W tej sytuacji nie zostaje
naruszone bezpieczestwo, ale nie ma te adnych korzyci z testw przeprowadzanych
w czasie kompilacji.

Jeli natomiast obiekt surowej klasy ArrayList zostanie przypisany do typu z parametrem,
zostanie wywietlone ostrzeenie.
ArrayList<Employee> result = employeeDB.find(query);

// Powoduje ostrzeenie

Aby ostrzeenie si pojawio, naley poda kompilatorowi opcj -Xlint:unchecked.

Zastosowanie rzutowania nie powoduje zniknicia ostrzeenia.


ArrayList<Employee> result = (ArrayList<Employee>)
employeeDB.find(query);
// Powoduje kolejne ostrzeenie

Zostanie wywietlone inne ostrzeenie informujce, e rzutowanie moe wprowadza w bd.


Jest to spowodowane niezbyt udanym ograniczeniem typw generycznych w Javie. Ze wzgldw zgodnoci kompilator konwertuje wszystkie listy tablicowe z typem na surowe obiekty
klasy ArrayList po uprzednim sprawdzeniu, e reguy dotyczce typw nie zostay zamane.
W dziaajcym programie wszystkie listy tablicowe s takie same w maszynie wirtualnej
nie ma parametrw okrelajcych typ. W zwizku z tym rzutowania (ArrayList) i (Array
List<Employee>) powoduj przeprowadzenie identycznych sprawdze w trakcie dziaania
programu.
Niewiele mona z tym zrobi. Pracujc ze starszym kodem, naley przyglda si ostrzeeniom
zgaszanym przez kompilator i pociesza si tym, e nie maj one wielkiego znaczenia.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

241

Gdy ju osigniesz zadowalajcy rezultat, moesz oznaczy rzutowan zmienn adnotacj


@SuppressWarnings("unchecked"):
@SuppressWarnings("unchecked") ArrayList<Employee> result =
(ArrayList<Employee>) employeeDB.find(query); // Powoduje zgoszenie kolejnego ostrzeenia

5.4. Osony obiektw i autoboxing


Od czasu do czasu konieczna jest konwersja typu podstawowego, jak int, na obiekt. Kady
typ podstawowy ma swj odpowiednik w postaci klasy. Na przykad typowi int odpowiada
klasa Integer. Tego typu klasy czsto s nazywane klasami osonowymi (ang. wrapper).
Nazwy klas osonowych s oczywiste: Integer, Long, Float, Double, Short, Byte, Character, Void i Boolean (sze pierwszych dziedziczy po wsplnej nadklasie Number). Klasy
osonowe s niezmienialne, tzn. nie mona zmieni opakowanej wartoci po utworzeniu
osony. Ponadto s finalne, co oznacza, e nie mona tworzy ich podklas.
Zamy, e potrzebujemy tablicy liczb cakowitych. Niestety parametr typu w nawiasach
ostrych nie moe okrela typu podstawowego. Nie mona utworzy listy ArrayList<int>.
W takiej sytuacji przydatna okazuje si klasa osonowa. List obiektw klasy Integer mona zadeklarowa bez problemu.
ArrayList<Integer> list = new ArrayList<>();

Lista ArrayList<Integer> jest znacznie mniej wydajna ni tablica int[], poniewa


kada warto jest osobno zapakowana wewntrz obiektu. Tego typu konstrukcji
naley uywa wycznie w przypadku maych kolekcji, kiedy wygoda programisty jest
waniejsza od wydajnoci.

Kolejna innowacja wprowadzona w Java SE 5.0 uatwia dodawanie i pobieranie elementw


z tablicy. Wywoanie:
list.add(3);

jest automatycznie konwertowane na:


list.add(new Integer(3));

Tego typu konwersja nazywa si automatycznym opakowywaniem (ang. autoboxing).


Mogoby si wydawa, e bardziej odpowiednim terminem byoby autowrapping,
ale czon boxing zosta przejty z jzyka C#.

Jeli natomiast obiekt klasy Integer zostanie przypisany do wartoci int, zostanie automatycznie odpakowany. To znaczy kompilator przekonwertuje:
int n = list.get(i);

na:
int n = list.get(i).intValue();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

242

Java. Podstawy
Automatyczne opakowywanie i odpakowywanie dziaa nawet w przypadku operacji arytmetycznych. Mona na przykad zastosowa do referencji do obiektu osonowego operator
inkrementacji:
Integer n = 3;
n++;

Kompilator automatycznie wstawi instrukcje odpakowujce obiekt, zwikszajce opakowan


w nim warto i opakowujce j z powrotem.
W wikszoci przypadkw wydaje si, e typy podstawowe i ich osony s jednym i tym
samym. Jest midzy nimi tylko jedna znaczca rnica: tosamo. Jak wiadomo, operator
== zastosowany do obiektw osonowych sprawdza tylko, czy obiekty te maj identyczne
lokalizacje w pamici. W zwizku z tym ponisze porwnanie prawdopodobnie zakoczyoby si niepowodzeniem:
Integer a = 1000;
Integer b = 1000;
if (a == b) ...

Jednak implementacja Javy moe, jeli tak zdecyduje, opakowa czsto pojawiajce si
wartoci w identyczne obiekty i wtedy takie porwnanie zakoczyoby si powodzeniem.
Taka dwuznaczno nie jest podana. Rozwizaniem problemu jest porwnywanie obiektw osonowych za pomoc metody equals.
Specyfikacja automatycznego opakowywania wymaga, aby typy boolean, char 127
oraz short i int w przedziale 128 do 127 byy opakowywane w ustalone obiekty.
Jeli na przykad w powyszym fragmencie kodu a i b zostayby zainicjowane wartoci 100,
porwnywanie musiaoby zakoczy si powodzeniem.

Naley rwnie zaznaczy, e opakowywanie i odpakowywanie zawdziczamy uprzejmoci


kompilatora, a nie maszyny wirtualnej. Kompilator wstawia odpowiednie wywoania, kiedy
generuje kod bajtowy klasy. Rola maszyny wirtualnej sprowadza si tylko do wykonywania
tego kodu.
Obiekty osonowe typw liczbowych s take czsto uywane do innego celu. Projektanci Javy
odkryli, e obiekty osonowe s dobrym miejscem na przechowywanie niektrych podstawowych metod, jak te, ktre su do konwersji acuchw cyfr na liczby.
Aby przekonwertowa acuch na liczb, naley uy nastpujcej instrukcji:
int x = Integer.parseInt(s);

Kod ten nie ma nic wsplnego z obiektami klasy Integer metoda parseInt jest statyczna.
Jednak klasa Integer bya dobrym miejscem na przechowywanie tej metody.
W opisie API znajduj si informacje o innych waniejszych metodach klasy Integer. Pozostae
klasy odpowiadajce typom liczbowym zawieraj podobne metody.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

243

Niektrzy programici uwaaj, e klas osonowych mona uywa do implementacji metod, ktre mog modyfikowa parametry liczbowe. S jednak w bdzie.
Pamitamy z rozdziau 4., e w Javie nie mona napisa metody zwikszajcej parametr liczbowy, poniewa parametry s zawsze przekazywane do metod przez warto.
public static void triple(int x)
{
x = 3 * x;
}

// nie zadziaa
// modyfikacja lokalnej zmiennej

Czy mona to omin, stosujc typ Integer zamiast int?


public static void triple(Integer x)
{
...
}

// nie zadziaa

Problem polega na tym, e obiekty klasy Integer s niezmienialne informacje zawarte


w obiekcie osonowym nie mog by zmieniane. Nie mona uy tych klas osonowych
do tworzenia metod modyfikujcych parametry liczbowe.
Aby napisa metod zmieniajc parametry liczbowe, naley uy jednego z typw Holder
zdefiniowanych w pakiecie org.omg.CORBA. Dostpne s typy IntHolder, BooleanHolder
itd. Kady taki typ ma publiczne (!) pole o nazwie value, poprzez ktre mona uzyska
dostp do wartoci.
public static void triple(IntHolder x)
{
x.value = 3 * x.value;
}
java.lang.Integer 1.0

int intValue()

Zwraca warto obiektu Integer jako liczb typu int (przesania metod intValue
z klasy Number).

static String toString(int i)

Zwraca nowy obiekt klasy String reprezentujcy liczb i w systemie dziesitnym.

static String toString(int i, int radix)

Zwraca reprezentacj liczby i w systemie okrelonym przez parametr radix.

Static int parseInt(String s)

Static int parseInt(String s, int radix)

Zwraca liczb cakowit utworzon z cyfr w acuchu s. acuch ten musi


reprezentowa liczb w systemie dziesitnym (w przypadku pierwszej metody)
lub w systemie okrelonym przez parametr radix (druga metoda).

static Integer valueOf(String s)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

244

Java. Podstawy

static Integer valueOf(String s, int radix)

Zwraca obiekt klasy Integer zainicjowany liczb cakowit reprezentowan przez


acuch s. acuch ten musi reprezentowa liczb w systemie dziesitnym
(w przypadku pierwszej metody) lub w systemie okrelonym przez parametr radix
(druga metoda).
java.text.NumberFormat 1.1

Number parse(String s)

Zwraca warto liczbow, jeli acuch s reprezentuje liczb.

5.5. Metody ze zmienn liczb parametrw


Przed wersj 5.0 Javy kada metoda miaa sta liczb parametrw. Obecnie jednak mona
tworzy metody, ktre da si wywoywa z rn liczb parametrw (mona spotka ich
angielsk nazw varargs).
Znamy ju metod printf. Na przykad wywoania:
System.out.printf("%d", n);

i
System.out.printf("%d %s", n, "widgets");

dotycz tej samej metody, mimo e pierwsze z nich ma dwa parametry, a drugie trzy.
Definicja metody printf wyglda nastpujco:
public class PrintStream
{
public PrintStream printf(String fmt, Object... args) { return format(fmt, args); }
}

W powyszym kodzie trzykropek () jest czci kodu Javy. Okrela on, e metoda moe
przyjmowa dowoln liczb obiektw (parametr fmt jest obowizkowy).
Metoda printf w rzeczywistoci przyjmuje dwa parametry acuch formatu i tablic
Object[], ktra zawiera wszystkie pozostae parametry (jeli zostanie podana warto typu
podstawowego, jak int, mechanizm automatycznego opakowywania zamieni j na obiekt). Musi
ona przeskanowa acuch fmt i dopasowa i-ty specyfikator formatu do wartoci args[i].
Innymi sowy, z punktu widzenia programisty implementujcego metod printf typ parametru Object jest dokadnie tym samym co Object[].
Kompilator musi przekonwertowa kade wywoanie metody printf, pakujc parametry do
tablicy i wykonujc w razie potrzeby automatyczne opakowywanie:
System.out.printf("%d %s", new Object[] { new Integer(n), "widgets" } );

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

245

Mona definiowa wasne metody ze zmienn liczb parametrw. Parametry te mog by kadego typu, take podstawowego. Poniej znajduje si prosty przykad takiej funkcji
zwraca najwiksz liczb w zbiorze o zmiennych rozmiarach:
public static double max(double... values)
{
double largest = Double.MIN_VALUE;
for (double v : values) if (v > largest) largest = v;
return largest;
}

Naley j wywoa w nastpujcy sposb:


double m = max(3.1, 40.4, -5);

Kompilator przekazuje tablic new double[] {3.1, 40.4, -5} do funkcji max.
Ostatnim parametrem metody o zmiennej liczbie parametrw moe by tablica.
Na przykad:
System.out.printf("%d %s", new Object[] { new Integer(1), "widgets" } );

W zwizku z tym mona przedefiniowa istniejc funkcj, ktrej ostatni parametr jest
tablic, na metod ze zmienn liczb parametrw, nie uszkadzajc istniejcego kodu.
Na przykad w ten sposb rozszerzono metod MessageFormat.format w Java SE 5.0.
Mona nawet zadeklarowa metod main w nastpujcy sposb:
public static void main(String... args)

5.6. Klasy wyliczeniowe


W rozdziale 3. nauczylimy si definiowa typy wyliczeniowe. Oto typowy przykad:
public enum Size {SMALL, MEDIUM, LARGE, EXTRA_LARGE}

Typ zdefiniowany przez powysz deklaracj jest w rzeczywistoci klas. Ma ona dokadnie
cztery egzemplarze nie mona tworzy jej nowych obiektw.
W zwizku z tym do porwnywania typw wyliczeniowych nie trzeba uywa metody equals.
Wystarczy operator ==.
Do typu wyliczeniowego mona doda konstruktory, metody i pola. Oczywicie konstruktory
s wywoywane tylko wwczas, gdy s konstruowane stae wyliczeniowe. Na przykad:
public enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private String abbreviation;
private Size(String abbreviation) { this.abbreviation = abbreviation; }

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

246

Java. Podstawy
public String getAbbreviation() { return abbreviation; }
}

Wszystkie typy wyliczeniowe s podklasami klasy Enum. Dziedzicz po niej kilka metod.
Do najbardziej przydatnych naley metoda toString, ktra zwraca nazw staej wyliczeniowej. Na przykad wywoanie Size.SMALL.ToString() zwraca acuch SMALL.
Przeciwiestwem metody toString jest statyczna metoda valueOf. Na przykad ponisza
instrukcja ustawia s na Size.SMALL.
Size s = (Size) Enum.valueOf(Size.class, "SMALL");

Kady typ wyliczeniowy ma statyczn metod values, ktra zwraca wszystkie wartoci wyliczenia. Na przykad wywoanie:
Size[] values = Size.values();

zwraca tablic zawierajc nastpujce elementy: Size.SMALL, Size.MEDIUM, Size.LARGE


oraz Size.EXTRA_LARGE.
Metoda ordinal zwraca pooenie staej wyliczeniowej w deklaracji enum, zaczynajc liczenie
od zera. Na przykad wywoanie Size.MEDIUM.ordinal() zwraca warto 1.
Krtki program przedstawiony na listingu 5.12 demonstruje zastosowanie typw wyliczeniowych.
Klasa Enum ma parametr typu, ktry dla uproszczenia pominlimy. Na przykad
typ wyliczeniowy Size w rzeczywistoci rozszerza Enum<Size>. Parametr typu jest
uywany przez metod compareTo (metod compareTo opisujemy w rozdziale 6., a parametry typu w rozdziale 12.).
Listing 5.12. enums/EnumTest.java
package enums;
import java.util.*;
/**
* Ten program demonstruje typy wyliczeniowe.
* @version 1.0 2004-05-24
* @author Cay Horstmann
*/
public class EnumTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj rozmiar: (SMALL, MEDIUM, LARGE, EXTRA_LARGE) ");
String input = in.next().toUpperCase();
Size size = Enum.valueOf(Size.class, input);
System.out.println("rozmiar=" + size);
System.out.println("skrt=" + size.getAbbreviation());
if (size == Size.EXTRA_LARGE)
System.out.println("Dobra robota -- nie pomine znaku podkrelenia _.");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

247

}
}
enum Size
{
SMALL("S"), MEDIUM("M"), LARGE("L"), EXTRA_LARGE("XL");
private Size(String abbreviation) { this.abbreviation = abbreviation; }
public String getAbbreviation() { return abbreviation; }
private String abbreviation;
}
java.lang.Enum<E> 5.0

static Enum valueOf(Class enumClass, String name)

Zwraca sta wyliczeniow danej klasy o podanej nazwie.

String toString()

Zwraca nazw staej wyliczeniowej.

int ordinal()

Zwraca pooenie w deklaracji enum (liczc od zera) staej wyliczeniowej.

int compareTo(E other)

Zwraca ujemn liczb cakowit, jeli staa wyliczeniowa wystpuje przed other,
zero jeli this == other, lub liczb dodatni w przeciwnym przypadku.
Kolejno staych jest okrelana przez deklaracj enum.

5.7. Refleksja
Biblioteka refleksyjna dostarcza bogaty i zaawansowany zestaw narzdzi do pisania programw, ktre dynamicznie zarzdzaj kodem Javy. Mechanizm ten jest czsto wykorzystywany
w JavaBeans skadniku architekturalnym Javy (wicej informacji na temat JavaBeans
znajduje si w drugim tomie). Dziki refleksji Java obsuguje narzdzia, do ktrych przyzwyczajeni s uytkownicy jzyka Visual Basic. Narzdzia suce do szybkiej budowy
aplikacji mog dynamicznie uzyskiwa informacje o funkcjonalnoci dodawanych klas,
zwaszcza kiedy w trakcie projektowania lub dziaania programu dodawane s nowe klasy.
Program, ktry moe analizowa funkcjonalno klas, nazywa si programem refleksyjnym.
Mechanizm refleksji ma bardzo due moliwoci. W kolejnych podrozdziaach opisujemy jego
nastpujce zastosowania:

Analiza waciwoci klasy w trakcie dziaania programu.

Inspekcja obiektw w czasie dziaania programu, na przykad do napisania jednej


metody toString, ktra dziaa we wszystkich klasach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

248

Java. Podstawy

Implementacja generycznego kodu manipulujcego tablicami.

Wykorzystanie obiektw Method, ktre dziaaj tak jak wskaniki do funkcji


w innych jzykach, np. C++.

Refleksja to wszechstronny i skomplikowany mechanizm. Jest interesujca przede wszystkim


dla twrcw narzdzi, mniej dla programistw aplikacji. Osoby, ktre s zainteresowane pisaniem aplikacji, a nie narzdzi dla innych programistw Javy, mog pomin reszt rozdziau
i wrci do niego kiedy indziej.

5.7.1. Klasa Class


Kiedy uruchomiony jest program, system wykonawczy Javy cay czas przechowuje informacje o typach wszystkich obiektw. Do informacji tych zaliczaj si nazwy klas, do
ktrych nale obiekty. Informacje o typach czasu wykonywania programu s wykorzystywane przez maszyn wirtualn do wyboru odpowiednich metod do wykonania.
Do informacji tych mona jednak uzyska dostp take dziki specjalnej klasie. Klasa przechowujca te informacje ma nazw Class. Metoda getClass() klasy Object zwraca egzemplarz klasy Class.
Employee e;
. . .
Class cl = e.getClass();

Podobnie jak obiekt klasy Employee opisuje cechy okrelonego pracownika, obiekt klasy Class
opisuje cechy okrelonej klasy. Chyba najczciej uywan metod klasy Class jest metoda
getName, ktra zwraca nazw klasy. Na przykad ponisza instrukcja:
System.out.println(e.getClass().getName() + " " + e.getName());

drukuje:
Employee Henryk Kwiatek

jeli e jest zwykym pracownikiem, lub


Manager Henryk Kwiatek

jeli e jest kierownikiem.


Jeli klasa naley do jakiego pakietu, nazwa tego pakietu stanowi cz nazwy tej klasy:
Date d = new Date();
Class cl = d.getClass();
String name = cl.getName();

// Zmienna name jest ustawiana na java.util.Date.

Obiekt klasy Class odpowiadajcy nazwie wybranej klasy mona utworzy za pomoc statycznej metody forName.
String className = "java.util.Date";
Class cl = Class.forName(className);

Metody tej naley uy, jeli nazwa klasy jest przechowywana w acuchu, ktry zmienia
si w czasie dziaania programu. Powyszy kod dziaa, jeli className jest nazw klasy lub

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

249

interfejsu. W przeciwnym przypadku metoda forName powoduje wyjtek kontrolowany


(ang. checked exception). Informacje na temat obsugi wyjtkw podczas uywania tej metody
znajduj si w podrozdziale 5.7.2, Podstawy przechwytywania wyjtkw.
Przy uruchamianiu programu najpierw adowana jest klasa zawierajca metod main.
aduje ona wszystkie klasy, ktrych potrzebuje. Kada z tych zaadowanych klas
aduje kolejne klasy, ktrych potrzebuje itd. W przypadku duej aplikacji proces ten moe
zajmowa duo czasu, co denerwowaoby uytkownika. Mona jednak zastosowa pewn
sztuczk, ktra da uytkownikowi wraenie, e program uruchamia si szybciej. Naley
sprawi, aby klasa zawierajca metod main nie odwoywaa si bezporednio do innych
klas. Najpierw naley wywietli ekran powitalny, a potem rcznie wymusi zaadowanie
pozostaych klas za pomoc wywoania Class.forName.

Trzecia metoda tworzenia obiektu typu Class jest wygodnym skrtem. Jeli T jest dowolnym
typem Javy, T.class jest odpowiadajcym mu obiektem klasy Class. Na przykad:
Class cl1 = Date.class;
// Naley zaimportowa java.util.*;.
Class cl2 = int.class;
Class cl3 = Double[].class;

Naley zauway, e obiekt klasy Class w rzeczywistoci oznacza typ, ktry moe, ale nie musi
by klas. Na przykad int nie jest klas, ale int.class jest z pewnoci obiektem typu Class.
Od Java SE 5.0 klasa Class jest sparametryzowana. Na przykad Employee.class
jest typu Class<Employee>. Nie bdziemy dry tego tematu, poniewa jeszcze
bardziej skomplikowalibymy i tak ju wystarczajco abstrakcyjn koncepcj. Dla praktycznych celw mona zignorowa parametr typu i pracowa na surowym typie Class.
Wicej informacji na ten temat znajduje si w rozdziale 13.

Z powodw historycznych metoda getName zwraca nieco dziwne nazwy typw


tablicowych:

Double[].class.getName() zwraca [Ljava.lang.Double;,

int[].class.getName() zwraca [I.

Maszyna wirtualna obsuguje unikatowy obiekt Class dla kadego typu. W zwizku z tym do
porwnywania obiektw class mona uywa operatora ==. Na przykad:
if (e.getClass() == Employee.class) . . .

Inna przydatna metoda umoliwia tworzenie w locie egzemplarzy klas. Nazywa si newIn
stance(). Na przykad:
e.getClass().newInstance();

Powysza instrukcja tworzy egzemplarz tego samego typu co e. Metoda newInstance wywouje
konstruktor domylny (ten, ktry nie przyjmuje adnych parametrw). Jeli klasa nie ma
konstruktora domylnego, powodowany jest wyjtek.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

250

Java. Podstawy
Przy uyciu metod forName i newInstance mona utworzy obiekt z nazwy klasy przechowywanej w acuchu.
String s = "java.util.Date";
Object m = Class.forName(s).newInstance();

Jeli konieczne jest podanie parametrw dla konstruktora klasy, ktrej obiekt jest
tworzony w ten sposb, nie mona uy powyszych instrukcji. W zamian trzeba uy
metody newInstance klasy Constructor.

Metoda newInstance jest odpowiednikiem konstruktora wirtualnego w C++. Jednak


konstruktory wirtualne w tym jzyku nie s waciwoci jzyka, a tylko idiomem,
ktry musi by obsugiwany przez specjaln bibliotek. Klasa Class jest podobna do
klasy type_info w C++, a metoda getClass jest odpowiednikiem operatora typeid. Klasa
Class Javy jest jednak nieco bardziej wszechstronna ni type_info. Ta druga potrafi
tylko zwrci acuch z nazw typu. Nie tworzy nowych obiektw tego typu.

5.7.2. Podstawy przechwytywania wyjtkw


Techniki przechwytywania wyjtkw zostay opisane w rozdziale 11., ale zanim do niego
dojdziemy, napotkamy po drodze kilka metod, ktre gro, e mog spowodowa wyjtek.
Kiedy w czasie dziaania programu wystpuje bd, program moe spowodowa wyjtek.
Mechanizm wyjtkw zapewnia wiksz elastyczno ni koczenie programu, poniewa
mona napisa procedur, ktra przechwyci taki wyjtek i go odpowiednio obsuy.
Jeli nie ma procedury obsugi wyjtku, program zostaje zakoczony, a w konsoli zostaje
wydrukowany komunikat informujcy o jego typie. Komunikat taki moe si pojawi
w wyniku przypadkowego uycia referencji null lub przekroczenia rozmiaru tablicy.
S dwa rodzaje wyjtkw: niekontrolowane (ang. unchecked) i kontrolowane (ang. checked).
W przypadku tych drugich kompilator sprawdza, czy napisano procedur do ich obsugi.
Jednak wiele czsto spotykanych wyjtkw, jak dostp do referencji null, jest niekontrolowanych. Kompilator nie sprawdza, czy zadbano o procedur obsugi dla tych bdw
czas naley powici na ich unikanie, a nie na pisanie procedur do ich obsugi.
Nie wszystkich bdw mona jednak unikn. Jeli wyjtek moe wystpi mimo najlepszych
stara programisty, kompilator nalega na napisanie procedury do jego obsugi. Przykadem
metody powodujcej kontrolowany wyjtek jest Class.forName. W rozdziale 11. opisano kilka
technik obsugi wyjtkw. W tym miejscu prezentujemy tylko najprostsz implementacj procedury obsugi wyjtku.
Instrukcje, ktre mog spowodowa wyjtek kontrolowany, naley umieci w bloku try.
W klauzuli catch naley wpisa kod obsugujcy wyjtek.
try
{
Instrukcje, ktre mog powodowa wyjtki

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

251

}
catch(Exception e)
{
Procedura obsugi wyjtku
}

Na przykad:
try
{
String name = . . .;
Class cl = Class.forName(name);
Dziaania zwizane z cl

// Pobranie nazwy klasy.


// Moe spowodowa wyjtek.

}
catch(Exception e)
{
e.printStackTrace();
}

Jeli klasa o podanej nazwie nie istnieje, reszta kodu w bloku try jest pomijana i nastpuje
przejcie do bloku catch (w tym miejscu drukujemy dane ze ledzenia stosu za pomoc metody
printStack klasy Throwable, ktra jest nadklas klasy Exception). Jeli adna z metod w bloku
try nie spowoduje wyjtku, kod w bloku catch zostaje pominity.
Konieczne jest dostarczanie procedur tylko dla wyjtkw kontrolowanych. Mona atwo
sprawdzi, ktre metody powoduj kontrolowane wyjtki. Kompilator zgasza problem za
kadym razem, kiedy wywoywana jest metoda mogca spowodowa wyjtek kontrolowany,
a nie napisane dla niej procedury obsugi wyjtkw.
java.lang.Class 1.0

static Class forName(string className)

Zwraca obiekt klasy Class reprezentujcy klas o nazwie className.

Object newInstance()

Zwraca nowy egzemplarz klasy.


java.lang.reflect.Constructor 1.1

Object newInstance(Object[] args)

Tworzy nowy egzemplarz klasy przy uyciu konstruktora.


Parametry:

args

Parametry konstruktora. Wicej informacji na temat


podawania parametrw znajduje si w podrozdziale
dotyczcym refleksji.

java.lang.Throwable 1.0

void printStackTrace()

Drukuje obiekt Throwable i dane ze ledzenia stosu do standardowego strumienia


bdw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

252

Java. Podstawy

5.7.3. Zastosowanie refleksji w analizie funkcjonalnoci klasy


Poniej znajduje si zwizy opis najwaniejszych funkcji mechanizmu refleksji, ktre
umoliwiaj analiz struktury klasy.
Trzy klasy Field, Method i Constructor dostpne w pakiecie java.lang.reflect opisuj
odpowiednio pola, metody i konstruktory klasy. Kada z nich ma metod o nazwie getName,
ktra zwraca nazw odpowiedniego elementu. Klasa Field ma metod getType, ktra zwraca
obiekt typu Class, zawierajcy informacje o typie pola. Klasy Method i Constructor maj
metody informujce o typach parametrw, a klasa Method informuje dodatkowo o typie
zwrotnym. Kada z trzech wymienionych klas ma metod getModifiers, ktra zwraca liczb
cakowit z wczonymi i wyczonymi rnymi bitami, okrelajc uyte modyfikatory, jak
public i static. Do analizy liczby zwrconej przez t metod mona uy metod statycznych klasy Modifier dostpnej w pakiecie java.lang.reflect. Aby sprawdzi, czy metoda
lub konstruktor mia modyfikator public, private lub final, naley uy metod isPublic,
isPrivate lub isFinal dostpnych w klasie Modifier. Jedyne, co jest potrzebne, to odpowiednia metoda w klasie Modifier dziaajca na liczbie zwrconej przez metod getModifiers.
Modyfikatory mona take drukowa za pomoc metody Modifier.toString.
Metody getFields, getMethods i getConstructors klasy Class zwracaj tablice publicznych
pl, metod i konstruktorw klasy. Do tego wliczaj si publiczne skadowe nadklasy. Metody
getDeclaredFields, getDeclaredMethods i getDeclaredConstructors klasy Class zwracaj
tablice zawierajce wszystkie pola, metody i konstruktory zadeklarowane w klasie. Wliczaj
si do nich skadowe prywatne i chronione, ale nie nadklasy.
Listing 5.13 prezentuje sposb wydrukowania wszystkich informacji o klasie. Ten program
monituje o podanie nazwy klasy, po czym drukuje sygnatury wszystkich metod i konstruktorw oraz nazwy wszystkich pl klasy. Jeli na przykad programowi zostanie podana klasa
java.lang.Double, wydrukuje on nastpujce dane:
public class java.lang.Double extends java.lang.Number
{
public java.lang.Double(java.lang.String);
public java.lang.Double(double);
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public
public

int hashCode();
int compareTo(java.lang.Object);
int compareTo(java.lang.Double);
boolean equals(java.lang.Object);
java.lang.String toString();
static java.lang.String toString(double);
static java.lang.Double valueOf(java.lang.String);
static boolean isNaN(double);
boolean isNaN();
static boolean isInfinite(double);
boolean isInfinite();
byte byteValue();
short shortValue();
int intValue();
long longValue();
float floatValue();
double doubleValue();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.
public
public
public
public

static
static
static
static

double
native
native
native

Dziedziczenie

253

parseDouble(java.lang.String);
long doubleToLongBits(double);
long doubleToRawLongBits(double);
double longBitsToDouble(long);

public static final double POSITIVE_INFINITY;


public static final double NEGATIVE_INFINITY;
public static final double NaN;
public static final double MAX_VALUE;
public static final double MIN_VALUE;
public static final java.lang.Class TYPE;
private double value;
private static final long serialVersionUID;
}

Naley zauway, e program ten potrafi przeanalizowa kad klas, ktr interpreter Javy
potrafi zaadowa, a nie tylko te klasy, ktre byy dostpne w czasie kompilacji. Ten program bdziemy wykorzystywa w kolejnym rozdziale, w ktrym bdziemy zaglda do wntrza
klas wewntrznych generowanych automatycznie przez kompilator.
Listing 5.13. reflection/ReflectionTest.java
package reflection;
import java.util.*;
import java.lang.reflect.*;
/**
* Ten program wykorzystuje technik refleksji do wydrukowania penych informacji o klasie.
* @version 1.1 2004-02-21
* @author Cay Horstmann
*/
public class ReflectionTest
{
public static void main(String[] args)
{
// Wczytanie nazwy klasy z argumentw wiersza polece lub danych od uytkownika.
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Podaj nazw klasy (np. java.util.Date): ");
name = in.next();
}
try
{
// Drukowanie nazwy klasy i nadklasy (jeli != Object).
Class cl = Class.forName(name);
Class supercl = cl.getSuperclass();
String modifiers = Modifier.toString(cl.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print("klasa " + name);
if (supercl != null && supercl != Object.class) System.out.print(" rozszerza
klas "
+ supercl.getName());

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

254

Java. Podstawy
System.out.print("\n{\n");
printConstructors(cl);
System.out.println();
printMethods(cl);
System.out.println();
printFields(cl);
System.out.println("}");
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
System.exit(0);
}
/**
* Drukowanie wszystkich konstruktorw klasy.
* @param cl klasa
*/
public static void printConstructors(Class cl)
{
Constructor[] constructors = cl.getDeclaredConstructors();
for (Constructor c : constructors)
{
String name = c.getName();
System.out.print("
");
String modifiers = Modifier.toString(c.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(name + "(");
// Drukowanie typw parametrw.
Class[] paramTypes = c.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}

/**
* Drukuje wszystkie metody klasy.
* @param cl klasa
*/
public static void printMethods(Class cl)
{
Method[] methods = cl.getDeclaredMethods();
for (Method m : methods)
{
Class retType = m.getReturnType();
String name = m.getName();
System.out.print("
");
// Drukowanie modyfikatorw, typu zwrotnego i nazwy metody.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

255

String modifiers = Modifier.toString(m.getModifiers());


if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.print(retType.getName() + " " + name + "(");
// Drukowanie typw parametrw.
Class[] paramTypes = m.getParameterTypes();
for (int j = 0; j < paramTypes.length; j++)
{
if (j > 0) System.out.print(", ");
System.out.print(paramTypes[j].getName());
}
System.out.println(");");
}
}
/**
* Drukowanie wszystkich pl klasy.
* @param cl klasa
*/
public static void printFields(Class cl)
{
Field[] fields = cl.getDeclaredFields();
for (Field f : fields)
{
Class type = f.getType();
String name = f.getName();
System.out.print("
");
String modifiers = Modifier.toString(f.getModifiers());
if (modifiers.length() > 0) System.out.print(modifiers + " ");
System.out.println(type.getName() + " " + name + ";");
}
}
}
java.lang.Class 1.0

Field[] getFields() 1.1

Field[] getDeclaredFields() 1.1

Metoda getFields zwraca tablic zawierajc obiekty Field reprezentujce pola


publiczne klasy lub nadklasy. Metoda getDeclaredFields zwraca tablic obiektw
Field reprezentujcych wszystkie pola klasy. Obie metody zwracaj tablic o zerowej
dugoci, jeli nie ma takich pl lub obiekt Class reprezentuje typ podstawowy
bd tablicowy.

Method[] getMethods 1.1

Method[] getDeclaredMethods() 1.1

Zwraca tablic obiektw Method. Metoda getMethods zwraca metody publiczne,


wliczajc metody odziedziczone. Metoda getDeclaredMethods zwraca wszystkie
metody klasy lub interfejsu, ale nie uwzgldnia metod odziedziczonych.

Constructor[] getConstructors() 1.1

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

256

Java. Podstawy

Constructor[] getDeclaredConstructors() 1.1

Zwraca tablic obiektw Constructor reprezentujcych wszystkie konstruktory


publiczne (getConstructors) lub wszystkie konstruktory w ogle
(getDeclaredConstructors) klasy reprezentowanej przez obiekt Class.
java.lang.reflect.Field 1.1
java.lang.reflect.Method 1.1
java.lang.reflect.Constructor 1.1

Class getDeclaringClass()

Zwraca obiekt klasy Class reprezentujcy klas, ktra definiuje dany konstruktor,
metod lub pole.

Class[] getExceptionTypes() (tylko klasy Constructor i Method)

Zwraca tablic obiektw Class, ktre reprezentuj typy wyjtkw powodowanych


przez metod.

int getModifiers()

Zwraca liczb cakowit opisujc modyfikatory konstruktora, metody lub pola.


Do analizy zwrconej wartoci su metody klasy Modifier.

String getName()

Zwraca w postaci acucha nazw konstruktora, metody lub pola.

Class[] getParameterTypes() (tylko klasy Constructor i Method)

Zwraca tablic obiektw klasy Class reprezentujcych typy parametrw.

Class getReturnType() (tylko w klasie Method)

Zwraca obiekt klasy Class reprezentujcy typ zwrotny.


java.lang.reflect.Modifier 1.1

static String toString(int modifiers)

Zwraca w postaci acucha modyfikatory odpowiadajce bitom ustawionym


przez metod modifiers.

static boolean isAbstract(int modifiers)

static boolean isFinal(int modifiers)

static boolean isInterface(int modifiers)

static boolean isNative(int modifiers)

static boolean isPrivate(int modifiers)

static boolean isProtected(int modifiers)

static boolean isPublic(int modifiers)

static boolean isStatic(int modifiers)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

static boolean isStrict(int modifiers)

static boolean isSynchronized(int modifiers)

static boolean isVolatile(int modifiers)

Dziedziczenie

257

Sprawdza bit w wartoci modifiers odpowiadajcy modyfikatorowi znajdujcemu


si w nazwie metody.

5.7.4. Refleksja w analizie obiektw w czasie dziaania programu


W poprzednim podrozdziale nauczylimy si sprawdza nazwy i typy pl danych obiektw:

Tworzymy odpowiedni obiekt klasy Class.

Wywoujemy na rzecz obiektu Class metod getDeclaredFields.

Teraz pjdziemy o krok dalej i dobierzemy si do zawartoci pl danych. Oczywicie zawarto okrelonego pola obiektu o znanych w trakcie pisania programu typie i nazwie mona
podejrze bez trudu. Jednak refleksja umoliwia uzyskanie informacji o polach obiektw,
ktre w czasie kompilacji nie byy jeszcze znane.
Kluczowe znaczenie ma w tym przypadku metoda get z klasy Field. Jeli f jest obiektem
typu Field (na przykad utworzonym za pomoc metody getDeclaredFields), a obj jest
obiektem klasy, ktrej polem jest f, wywoanie f.get(obj) zwraca obiekt, ktrego wartoci
jest aktualna warto pola obiektu obj. Przeanalizujmy to nieco skomplikowane zagadnienie na
przykadzie.
Employee harry = new Employee("Henryk Kwiatek", 35000, 10, 1, 1989);
Class cl = harry.getClass();
// Obiekt Class reprezentujcy pracownika.
Field f = cl.getDeclaredField("name");
// Pole name klasy Employee.
Object v = f.get(harry);
// Warto pola name obiektu harry
// tj. obiekt klasy String "Henryk Kwiatek".

Ten kod sprawia jednak jeden problem. Poniewa pole name jest prywatne, metoda get spowoduje wyjtek IllegalAccessException. Za pomoc tej metody mona sprawdzi tylko wartoci
dostpnych pl. Zabezpieczenia w Javie zezwalaj na sprawdzenie, jakie pola zawiera obiekt,
ale nie pozwalaj na sprawdzenie ich wartoci bez odpowiednich uprawnie dostpu.
Przy standardowych ustawieniach mechanizm refleksji honoruje mechanizmy ochronne Javy.
Jeli jednak program nie dziaa pod kontrol menedera zabezpiecze, mona omin ustawienia ochrony dostpu. W tym celu naley wywoa metod setAccessible na rzecz obiektu
klasy Field, Method lub Constructor. Na przykad:
f.setAccessible(true);

// Teraz mona wywoa f.get(harry);.

Metoda setAccessible naley do klasy AccessibleObject, ktra jest wspln nadklas klas
Field, Method i Constructor. Zostaa ona utworzona z myl o debugerach, schowkach i podobnych mechanizmach. Nieco dalej uywamy tej metody dla generycznej metody toString.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

258

Java. Podstawy
Metoda get sprawia jeszcze jeden problem, z ktrym musimy sobie poradzi. Pole name jest
typu String, a wic nie ma problemu, eby zwrci jego warto jako typ Object, ale pole
salary jest typu double, a w Javie typy liczbowe nie s obiektami. W tym przypadku mona
uy metody getDouble z klasy Field lub wywoa metod get. W tym drugim przypadku
mechanizm refleksji automatycznie opakuje warto pola w obiekt odpowiedniego typu, tutaj
Double.
Oczywicie wartoci, ktre mona sprawdzi, mona te ustawi. Wywoanie f.set(obj,
value) ustawia pole f obiektu obj na warto value.

Listing 5.14 przedstawia generyczn metod toString, ktra dziaa z kad klas. Wszystkie
pola danych s pobierane za pomoc metody getDeclaredFields. Nastpnie metoda setAcce
ssible umoliwia dostp do wszystkich tych pl. Pobierane s nazwa i warto kadego
pola. Program na listingu 5.14 rekursywnie wywouje metod toString, zamieniajc kad
warto na acuch.
class ObjectAnalyzer
{
public String toString(Object obj)
{
Class cl = obj.getClass();
. . .
String r = cl.getName();
// Przegld pl tej klasy i wszystkich jej nadklas.
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// Pobranie nazw i wartoci wszystkich pl.
for (Field f : fields)
{
if (!Modifier.isStatic(f.getModifiers()))
{
if (!r.endsWith("[")) r += ","
r += f.getName() + "=";
try
{
Object val = f.get(obj);
r += toString(val);
}
catch (Exception e) { e.printStackTrace(); }
}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl != null);
return r;
}
. . .
}

W penej wersji kodu na listingu 5.14 konieczne byo rozwizanie kilku skomplikowanych
problemw. Cykliczne odwoania mog spowodowa nieskoczon rekursj. Dlatego klasa

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

259

ObjectAnalyzer (listing 5.15) zapamituje obiekty, ktre byy ju odwiedzane. Aby zajrze

do tablic, potrzebne jest zastosowanie innej metody. Wicej szczegw na ten temat znajduje si w kolejnym podrozdziale.
Za pomoc metody toString mona zajrze do rodka kadego obiektu. Na przykad
wywoanie:
ArrayList<Integer> squares = new ArrayList<>();
for (int i = 1; i <= 5; i++) squares.add(i * i);
System.out.println(new ObjectAnalyzer().toString(squares));

zwraca:
java.util.ArrayList[elementData=class
java.lang.Object[]{java.lang.Integer[value=1][][],
java.lang.Integer[value=4][][],java.lang.Integer[value=9][][],java.lang.Integer
[value=16][][],
java.lang.Integer[value=25][][],null,null,null,null,null},size=5][modCount=5][][]

Przy uyciu generycznej metody toString mona zaimplementowa metody toString


w poszczeglnych klasach:
public String toString()
{
return new ObjectAnalyzer().toString(this);
}

Jest to bezproblemowa metoda na utworzenie metody toString, ktra moe si przyda


w wielu programach.
Listing 5.14. objectAnalyzerTest/ObjectAnalyzerTest.java
package objectAnalyzer;
import java.util.ArrayList;
/**
* Ten program analizuje obiekty za pomoc refleksji.
* @version 1.12 2012-01-26
* @author Cay Horstmann
*/
public class ObjectAnalyzerTest
{
public static void main(String[] args)
{
ArrayList<Integer> squares = new ArrayList<>();
for (int i = 1; i <= 5; i++)
squares.add(i * i);
System.out.println(new ObjectAnalyzer().toString(squares));
}
}

Listing 5.15. objectAnalyzer/ObjectAnalyzer.java


package objectAnalyzer;
import java.lang.reflect.AccessibleObject;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

260

Java. Podstawy
import
import
import
import

java.lang.reflect.Array;
java.lang.reflect.Field;
java.lang.reflect.Modifier;
java.util.ArrayList;

class ObjectAnalyzer
{
private ArrayList<Object> visited = new ArrayList<>();
/**
* Konwertuje obiekt na acuch zawierajcy list wszystkich pl.
* @param obj obiekt
* @return acuch zawierajcy nazw klasy obiektu oraz nazwy i wartoci wszystkich pl.
*/
public String toString(Object obj)
{
if (obj == null) return "null";
if (visited.contains(obj)) return "...";
visited.add(obj);
Class cl = obj.getClass();
if (cl == String.class) return (String) obj;
if (cl.isArray())
{
String r = cl.getComponentType() + "[]{";
for (int i = 0; i < Array.getLength(obj); i++)
{
if (i > 0) r += ",";
Object val = Array.get(obj, i);
if (cl.getComponentType().isPrimitive()) r += val;
else r += toString(val);
}
return r + "}";
}
String r = cl.getName();
// Inspekcja pl tej klasy i wszystkich nadklas.
do
{
r += "[";
Field[] fields = cl.getDeclaredFields();
AccessibleObject.setAccessible(fields, true);
// Pobranie nazw i wartoci wszystkich pl.
for (Field f : fields)
{
if (!Modifier.isStatic(f.getModifiers()))
{
if (!r.endsWith("[")) r += ",";
r += f.getName() + "=";
try
{
Class t = f.getType();
Object val = f.get(obj);
if (t.isPrimitive()) r += val;
else r += toString(val);
}
catch (Exception e)
{
e.printStackTrace();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

261

}
}
}
r += "]";
cl = cl.getSuperclass();
}
while (cl != null);
return r;
}
}
java.lang.reflect.AccessibleObject 1.2

void setAccessible(boolean flag)

Ustawia znacznik dostpnoci obiektu refleksyjnego. Warto true oznacza


wyczenie mechanizmu kontroli dostpu Javy, dziki czemu mona sprawdza
i ustawia prywatne pola obiektu.

boolean isAccessible()

Sprawdza znacznik dostpnoci obiektu refleksyjnego.

static void setAccessible(AccessibleObject[] array, boolean flag)

Ustawia znacznik dostpnoci obiektw tablicowych.


java.lang.Class 1.1

Field getField(String name)

Field[] getFields()

Zwraca pole publiczne o danej nazwie lub tablic wszystkich pl.

Field getDeclaredField(String name)

Field[] getDeclaredFields()

Zwraca pole o danej nazwie zadeklarowane w klasie lub tablic wszystkich pl.
java.lang.reflect.Field 1.1

Object get(Object obj)

Zwraca warto pola reprezentowanego przez obiekt Field w obiekcie obj.

void set(Object obj, Object newValue)

Ustawia pole reprezentowane przez obiekt Field w obiekcie obj na now warto.

5.7.5. Zastosowanie refleksji w generycznym kodzie tablicowym


Klasa Array dostpna w pakiecie java.lang.reflect umoliwia dynamiczne tworzenie tablic.
Moliwo ta jest wykorzystana na przykad w implementacji metody copyOf w klasie Array.
Przypomnijmy sobie, jak uy tej metody do powikszenia tablicy, ktra zostaa zapeniona.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

262

Java. Podstawy
Employee[] a = new Employee[100];
. . .
// Tablica jest pena.
a = Arrays.copyOf(a, 2 * a.length);

Jak napisa tak metod? Pomocny jest fakt, e tablic Employee[] mona przekonwertowa
na tablic Object[]. Brzmi obiecujco. Oto pierwsza prba.
public static Object[] badCopyOf(Object[] a, int newLength) // nieprzydatna
{
Object[] newArray = new Object[newLength];
System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength));
return newArray;
}

Niestety jest problem z uyciem powstaej tablicy. Ten kod zwraca tablic obiektw
(Object[]). Odpowiada za to poniszy wiersz:
new Object[newLength]

Tablicy obiektw nie mona rzutowa na tablic pracownikw (Employee[]) w czasie


dziaania programu zostaby spowodowany wyjtek ClassCastException. Problem polega na
tym, e jak nam wiadomo, tablice w Javie pamitaj typ swoich elementw, to znaczy typ
elementu, ktry by uyty w wyraeniu new podczas ich tworzenia. Mona tymczasowo
przekonwertowa tablic Employee[] na Object[], a pniej wrci do poprzedniego stanu.
Niemoliwe jednak jest rzutowanie tablicy, ktra od chwili powstania jest typu Object[],
na typ Employee[]. Do napisania tego rodzaju generycznego kodu tablicy konieczna jest
moliwo utworzenia nowej tablicy tego samego typu co oryginalna tablica. Do tego celu
potrzebne s metody dostpne w klasie Array z pakietu java.lang.reflect. Kluczowe znaczenie ma metoda newInstance klasy Array, ktra tworzy now tablic. Metoda ta przyjmuje
jako argumenty typ elementw i dany rozmiar tablicy.
Object newArray = Array.newInstance(componentType, newLength);

Aby tego dokona, trzeba zna typ elementw i rozmiar nowej tablicy.
Pierwsz warto mona uzyska za pomoc wywoania Array.getLength(a). Statyczna metoda
getLength klasy Array zwraca rozmiar tablicy. Aby sprawdzi typ elementw nowej tablicy:
1.

Utwrz obiekt klasowy a.

2. Sprawd, czy to na pewno jest tablica.


3. Znajd odpowiedni typ dla tablicy za pomoc metody getComponentType (ktra jest
zdefiniowana tylko dla obiektw klasowych reprezentujcych tablice) klasy Class.

Dlaczego metoda getLength naley do klasy Array, a getComponentType do klasy Class?


Nie wiadomo czasami wydaje si, e rozkad w klasach metod refleksyjnych jest nieco
przypadkowy.
Oto potrzebny kod:
public static Object goodCopyOf(Object a, int newLength)
{
Class cl = a.getClass();
if (!cl.isArray()) return null;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

263

Class componentType = cl.getComponentType();


int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
return newArray;

Naley zauway, e metoda copyOf moe powiksza tablice kadego typu, nie tylko przechowujce obiekty.
int[] a = { 1, 2, 3, 4 };
a = (int[]) goodCopyOf(a);

Aby to byo moliwe, parametr metody goodCopyOf jest typu Object, a nie tablic obiektw
(Object[]). Tablic typu int[] mona przekonwertowa na Object, ale nie na tablic
obiektw!
Listing 5.16 demonstruje obie metody powikszania tablicy. Zauwa, e rzutowanie typu
zwrotnego metody badCopyOf spowoduje wyjtek.
Listing 5.16. arrays/CopyOfTest.java
package arrays;
import java.lang.reflect.*;
import java.util.*;
/**
* Ten program demonstruje zastosowanie refleksji do manipulacji tablicami.
* @version 1.2 2012-05-04
* @author Cay Horstmann
*/
public class CopyOfTest
{
public static void main(String[] args)
{
int[] a = { 1, 2, 3 };
a = (int[]) goodCopyOf(a);
System.out.println(Arrays.toString(a));
String[] b = { "Tomek", "Daniel", "Henryk" };
b = (String[])goodCopyOf(b);
System.out.println(Arrays.toString(b));

System.out.println("Ponisze wywoanie spowoduje wyjtek.");


b = (String[]) badCopyOf(b, 10);

/**
* Ta metoda prbuje powikszy tablic, tworzc now tablic i kopiujc wszystkie elementy.
* @param a tablica, ktra ma by powikszona.
* @param newLength nowa dugo tablicy
* @return wiksza tablica zawierajca wszystkie elementy tablicy a. Zwrcona tablica jest
* typu Object[], a nie takiego samego jak a.
*/
public static Object[] badCopyOf(Object[] a, int newLength) // nieprzydatna
{
Object[] newArray = new Object[newLength];

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

264

Java. Podstawy
System.arraycopy(a, 0, newArray, 0, Math.min(a.length, newLength));
return newArray;
}
/**
* Ta metoda powiksza tablic, tworzc now tablic tego samego typu
* i kopiujc wszystkie elementy.
* @param a tablica, ktra ma by powikszona. Moe to by tablica obiektw lub
* elementw typu podstawowego.
* @return wiksza tablica zawierajca wszystkie elementy tablicy a.
*/
public static Object goodCopyOf(Object a, int newLength)
{
Class cl = a.getClass();
if (!cl.isArray()) return null;
Class componentType = cl.getComponentType();
int length = Array.getLength(a);
Object newArray = Array.newInstance(componentType, newLength);
System.arraycopy(a, 0, newArray, 0, Math.min(length, newLength));
return newArray;
}
}
java.lang.reflect.Array 1.1

static Object get(Object array, int index)

static xxx getXxx(Object array, int index)


xxx to jeden z typw podstawowych: boolean, byte, char, double, float, int, long,
short. Te metody zwracaj warto przechowywan w okrelonym indeksie tablicy.

static void set(Object array, int index, Object newValue)

static setXxx(Object array, int index, xxx newValue)


xxx to jeden z typw podstawowych: boolean, byte, char, double, float, int, long,
short. Te metody zapisuj now warto w danej tablicy w okrelonym indeksie.

static int getLength(Object array)

Zwraca dugo tablicy.

static Object newInstance(Class componentType, int length)

static Object newInstance(Class componentType, int[] lengths)

Zwraca now tablic danego typu o okrelonych rozmiarach.

5.7.6. Wywoywanie dowolnych metod


W jzykach C i C++ mona wywoa dowoln funkcj, posugujc si ustawionym na ni
wskanikiem. Na pierwszy rzut oka wydaje si, e w Javie nie ma wskanikw do metod
umoliwiaj one podanie lokalizacji metody innej metodzie, dziki czemu ta druga moe pniej wywoa t pierwsz. Projektanci jzyka Java stwierdzili nawet, e wskaniki do metod

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

265

nie s bezpieczne, mog bowiem stanowi rdo bdw, a lepszym od nich rozwizaniem
s interfejsy (opisane w kolejnym rozdziale). Jednak dziki refleksji take w Javie mona
wywoywa dowolne metody.
Wrd niestandardowych rozszerze dodanych przez firm Microsoft do pochodzcego od Javy jzyka J++ (i jego nastpcy C#) znajduje si jeszcze inny typ wskanikw do metod o nazwie delegacja (ang. delegate). Nie jest to to samo co klasa Method
opisywana w tym podrozdziale. Bardziej przydatne od delegacji s jednak klasy wewntrzne
(ktre wprowadzamy w kolejnym rozdziale).

Przypomnijmy sobie, e za pomoc metody get klasy Field mona sprawdzi dowolne pole
obiektu. Podobnie klasa Method zawiera metod invoke, ktra umoliwia wywoanie metody
zapakowanej w biecy obiekt klasy Method. Sygnatura metody invoke wyglda nastpujco:
Object invoke(Object obj, Object... args)

Pierwszy jest parametr niejawny, a pozostae obiekty to parametry jawne.


W przypadku metody statycznej pierwszy parametr jest ignorowany mona go ustawi
na null.
Jeli na przykad m1 reprezentuje metod getName klasy Employee, poniszy kod pokazuje, jak
mona j wywoa:
String n = (String) m1.invoke(harry);

Jeli typ zwrotny jest podstawowy, metoda invoke zwrci typ osony. Zamy na przykad,
e m2 reprezentuje metod getSalary klasy Employee. Zwrcony obiekt bdzie typu Double
i trzeba wykona odpowiednie rzutowanie. Mona do tego celu zastosowa automatyczne
odpakowywanie.
double s = (Double) m2.invoke(harry);

Jak uzyska obiekt klasy Method? Mona oczywicie wywoa metod getDeclaredMethods
i przeszuka zwrcon tablic w celu znalezienia danej metody. Mona rwnie wywoa
metod getMethod klasy Class. Jest ona podobna do metody getField, ktra przyjmuje nazw
pola w postaci acucha i zwraca obiekt typu Field. Jednak metod o takiej samej nazwie
moe by kilka, wic naley uwaa, aby si nie pomyli. Z tego powodu konieczne jest
podanie dodatkowo typw parametrw danej metody. Sygnatura metody getMethod jest
nastpujca:
Method getMethod(String name, Class... parameterTypes)

Poniszy kod przedstawia sposb uzyskania wskanikw do metod getName i raiseSalary


klasy Employee:
Method m1 = Employee.class.getMethod("getName");
Method m2 = Employee.class.getMethod("raiseSalary", double.class);

Skoro znamy ju zasady uywania obiektw klasy Method, wykorzystajmy je w praktyce.


Listing 5.17 przedstawia program drukujcy tabel wartoci funkcji matematycznych, jak
Math.sqrt lub Math.sin. Wydruk wyglda nastpujco:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

266

Java. Podstawy
public static native double java.lang.Math.sqrt(double)
1.0000 |
1.0000
2.0000 |
1.4142
3.0000 |
1.7321
4.0000 |
2.0000
5.0000 |
2.2361
6.0000 |
2.4495
7.0000 |
2.6458
8.0000 |
2.8284
9.0000 |
3.0000
10.0000 |
3.1623

Oczywicie kod drukujcy tabel jest niezaleny od samej funkcji, dla ktrej zastosowano
wcicia.
double dx = (to - from) / (n - 1);
for (double x = from; x <= to; x += dx)
{
double y = (Double) f.invoke(null, x);
System.out.printf("%10.4f | %10.4f%n", x, y);
}

W powyszym kodzie f jest obiektem typu Method. Pierwszy parametr metody invoke ma
warto null, poniewa wywoywana jest metoda statyczna.
Wyrwnanie funkcji Math.sqrt zostao uzyskane poprzez ustawienie f na:
Math.class.getMethod("sqrt", double.class)

czyli metod klasy Math o nazwie sqrt, z parametrem typu double.


Listing 5.17 przedstawia peny kod generycznego tabulatora i kilka przykadowych wywoa.
Listing 5.17. methods/MethodTableTest.java
package methods;
import java.lang.reflect.*;
/**
* Ten program demonstruje sposb wywoywania metod poprzez refleksj.
* @version 1.2 2012-05-04
* @author Cay Horstmann
*/
public class MethodTableTest
{
public static void main(String[] args) throws Exception
{
// Pobranie wskanikw do metod square i sqrt.
Method square = MethodTableTest.class.getMethod("square", double.class);
Method sqrt = Math.class.getMethod("sqrt", double.class);
// Drukowanie tabel wartoci x i y.

printTable(1, 10, 10, square);


printTable(1, 10, 10, sqrt);

/**

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

267

* Zwraca kwadrat liczby.


* @param x liczba
* @return x podniesione do kwadratu
*/
public static double square(double x)
{
return x * x;
}
/**
* Drukuje tablic wartoci x i y dla danej metody.
* @param od dolnej granicy wartoci x
* @param do grnej granicy wartoci x
* @param n liczba wierszy w tabeli
* @param f metoda z parametrem i typem zwrotnym typu double
*/
public static void printTable(double from, double to, int n, Method f)
{
// Drukowanie metody jako nagwka tabeli.
System.out.println(f);
double dx = (to - from) / (n - 1);

for (double x = from; x <= to; x += dx)


{
try
{
double y = (Double) f.invoke(null, x);
System.out.printf("%10.4f | %10.4f%n", x, y);
}
catch (Exception e)
{
e.printStackTrace();
}
}

Powyszy program pokazuje, e z obiektami klasy Method mona zrobi wszystko to co ze


wskanikami do funkcji w jzyku C (i delegacjami w C#). Podobnie jak w C, taki styl programowania jest niewygodny i zawsze podatny na bdy. Co si stanie, jeli metoda zostanie
wywoana przy uyciu nieprawidowych parametrw? Metoda invoke spowoduje wyjtek.
Dodatkowo parametry i typy zwrotne metody invoke musz by typu Object. Oznacza to
konieczno czstego rzutowania w obie strony. W ten sposb kompilator zostaje pozbawiony
moliwoci sprawdzenia kodu. Przez to bdy ujawniaj si tylko podczas testw, kiedy s
trudniejsze do naprawienia. Ponadto kod tworzcy wskanik do metody przy uyciu refleksji
jest znacznie wolniejszy ni kod, ktry wywouje metody bezporednio.
Z wymienionych powodw zalecamy uywa obiektw klasy Method wycznie wtedy, kiedy
jest to absolutnie niezbdne. Prawie zawsze lepiej jest uy interfejsu i klas wewntrznych
(ktre s tematem kolejnego rozdziau). Zgadzamy si zwaszcza ze stanowiskiem projektantw jzyka Java i nie polecamy uywania obiektw typu Method dla funkcji zwrotnych.
Dziki uyciu interfejsw dla metod zwrotnych (zobacz nastpny rozdzia) kod jest znacznie
szybszy i atwiejszy w utrzymaniu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

268

Java. Podstawy
java.lang.reflect.Method 1.1

public Object invoke(Object implicitParameter, Object[] explicitParameters)

Wywouje metod reprezentowan przez obiekt, przekazujc podane parametry,


oraz zwraca warto, ktr zwraca ta metoda. W przypadku metod statycznych
parametr niejawny powinien mie warto null. Wartoci typw podstawowych
naley przekazywa w obiektach osonowych. Wartoci zwrotne typw
podstawowych musz by odpakowywane.

5.8. Porady projektowe dotyczce dziedziczenia


Na zakoczenie tego rozdziau przedstawiamy kilka porad dotyczcych dziedziczenia.
1.

Wsplne metody i pola umieszczaj w nadklasie.


Z tego powodu pole name umieszczamy w klasie Person zamiast w klasach
Employee i Student.

2. Nie uywaj pl chronionych.

Niektrzy programici uwaaj, e zdefiniowanie wikszoci pl jako chronionych


jest dobrym pomysem, poniewa dziki temu podklasy maj w razie potrzeby
do nich dostp. Jednak mechanizm stojcy za sowem kluczowym protected
nie daje dobrej ochrony, i to z dwch powodw. Po pierwsze, liczba podklas jest
nieskoczona kady moe napisa podklas naszej klasy, a nastpnie napisa
kod uzyskujcy bezporedni dostp do chronionych pl egzemplarzy, co stanowi
zamanie zasad hermetyzacji. Po drugie, w Javie wszystkie klasy znajdujce
si w jednym pakiecie maj dostp do pl chronionych pozostaych klas,
bez wzgldu na to, czy s podklasami.
Uyteczne mog natomiast by metody chronione, ktre nie s gotowe do uytku
i powinny zosta ponownie zdefiniowane w podklasach.
3. Za pomoc dziedziczenia imituj relacj jest.

Dziedziczenie umoliwia zaoszczdzenie wielu wierszy kodu, ale niestety jest czsto
naduywane. Wyobramy sobie na przykad, e potrzebujemy klasy o nazwie
Contractor (pracownik kontraktowy). Pracownik kontraktowy ma imi i nazwisko
oraz dat zatrudnienia, ale nie ma staej pensji. Zamiast tego otrzymuje
wynagrodzenie zalene od liczby przepracowanych godzin i nie pracuje na tyle
dugo, aby dosta podwyk. Istnieje wic pokusa, aby utworzy podklas Contractor
klasy Employee i doda pole hourlyWage (stawka godzinowa).
class Contractor extends Employee
{
private double hourlyWage;
. . .
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 5.

Dziedziczenie

269

Nie jest to jednak dobre rozwizanie, poniewa kady obiekt pracownika


kontraktowego bdzie mia zarwno pole pensji, jak i stawki godzinowej. Stanie
si to rdem niekoczcych si problemw podczas implementacji metod
generujcych rachunki lub formularze podatkowe. Bdzie konieczne napisanie
wikszej iloci kodu, ni gdyby zrezygnowano na pocztku z dziedziczenia.
Relacja pracownik pracownik kontraktowy nie jest typu jest. Pracownik
kontraktowy nie jest specjalnym typem pracownika.
4. Nie uywaj dziedziczenia, jeli ktra z metod nie dziaa odpowiednio.

Wyobramy sobie, e chcemy napisa klas o nazwie Holiday. Jak wiadomo,


kade wito jest jakim dniem, a dni mona reprezentowa jako obiekty klasy
GregorianCalendar. W zwizku z tym moemy wykorzysta dziedziczenie.
class Holiday extends GregorianCalendar { . . . }

Niestety zbir dni wolnych nie jest zamknity dla wszystkich odziedziczonych
metod. Jedn z publicznych metod klasy GregorianCalendar jest add. Za jej pomoc
dni witeczne mona zamieni w dni niewiteczne:
Holiday christmas;
christmas.add(Calendar.DAY_OF_MONTH, 12);

Z tego powodu dziedziczenie nie jest waciw technik w tym przypadku.


5. Przesaniajc metod, nie zmieniaj jej spodziewanego dziaania.

Zasada zamienialnoci ma zastosowanie nie tylko do skadni, ale co waniejsze


do zachowania. Przesaniajc metod, nie naley bez powodu zmienia jej
zachowania. Kompilator w takiej sytuacji nie pomoe, poniewa nie moe sprawdzi,
czy nowa definicja metody jest waciwa. Na przykad problem z metod add
w klasie Holiday mona rozwiza, definiujc t metod ponownie w taki sposb,
aby nic nie robia lub powodowaa wyjtek czy te przechodzia do kolejnego wita.
Niestety ta poprawka amie zasad zamienialnoci. Ponisze instrukcje:
int d1 = x.get(Calendar.DAY_OF_MONTH);
x.add(Calendar.DAY_OF_MONTH, 1);
int d2 = x.get(Calendar.DAY_OF_MONTH);
System.out.println(d2 - d1);

powinny dziaa przewidywalnie, bez wzgldu na to, czy x jest typu


GregorianCalendar, czy Holiday.
Oczywicie na ten temat mona toczy zaarte spory dotyczce tego, co naley
uwaa za przewidywalne dziaanie. Na przykad niektrzy programici stwierdz,
e zasada zamienialnoci wymaga, aby metoda Manager.equals ignorowaa pole
bonus, poniewa robi to take metoda Employee.equals. Takie dyskusje s bezcelowe,
jeli prowadzi si je bez odpowiedniego kontekstu. Przede wszystkim naley
pamita, aby przesaniajc metody w podklasach, nie zaciera przeznaczenia
oryginalnego projektu.
6. Wykorzystuj polimorfizm zamiast informacji o typach.

Kiedy napotkasz kod typu:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

270

Java. Podstawy
if (x jest typu 1)
dziaanie1(x);
else if (x jest typu 2)
dziaanie2(x);

zawsze pomyl o polimorfizmie.


Czy dziaanie1 i dziaanie2 reprezentuj wspln koncepcj? Jeli tak, zamie
t koncepcj na metod nadklasy lub interfejsu wsplnego dla obu typw. Dziki
temu wystarczy wywoanie typu:
x.dziaanie();

Polimorficzny mechanizm dynamicznego przydzielania zada wywoa odpowiedni


metod.
Kod wykorzystujcy metody polimorficzne lub interfejsy jest zdecydowanie
atwiejszy do utrzymania i rozszerzania ni kod zawierajcy wiele sprawdze
typw.
7. Nie naduywaj refleksji.

Moliwo wykrywania pl i metod w czasie dziaania programu pozwala na pisanie


programw o zadziwiajcym stopniu uoglnienia. Ta funkcjonalno jest przydatna
w programowaniu systemw, ale nie sprawdza si w aplikacjach. Refleksja
jest wraliwym mechanizmem kompilator nie moe pomc w znajdowaniu
bdw. Wszystkie bdy s odkrywane w czasie dziaania programu i wtedy
powoduj wyjtki.
Znamy ju zasady dziaania fundamentw programowania obiektowego: klas, dziedziczenia i polimorfizmu. W kolejnym rozdziale opisujemy dwa zaawansowane zagadnienia, bardzo wane dla tych, ktrzy chc efektywnie wykorzystywa moliwoci Javy. S to interfejsy i klasy wewntrzne.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Interfejsy
i klasy wewntrzne
W tym rozdziale:

Interfejsy

Klonowanie obiektw

Interfejsy i metody zwrotne

Klasy wewntrzne

Klasy poredniczce

Znamy ju wszystkie podstawowe narzdzia programowania obiektowego. W tym rozdziale


poznamy kilka bardziej zaawansowanych, a powszechnie stosowanych technik. Wiedza ta,
mimo i mniej intuicyjna, stanowi uzupenienie poznanego dotychczas zestawu narzdzi
programistycznych.
Na pocztku zajmiemy si interfejsami (ang. interface), ktre opisuj, co klasy powinny
robi, ale nie okrelaj w jaki sposb. Klasa moe implementowa jeden lub wicej interfejsw. Obiektw klas implementujcych interfejsy mona uywa zawsze wtedy, kiedy
wymagana jest zgodno z okrelonym interfejsem. Nastpnie przejdziemy do klonowania
obiektw (czasami nazywanego kopiowaniem gbokim). Klon obiektu to nowy obiekt z takim
samym stanem jak pierwowzr. Modyfikacje klonu nie maj wpywu na oryginalny obiekt.
Kolejne zagadnienie to klasy wewntrzne (ang. inner class). Z technicznego punktu widzenia
s one nieco skomplikowane, poniewa ich definicje znajduj si wewntrz innych klas, a ich
metody maj dostp do pl klas je zawierajcych. Klasy wewntrzne znajduj zastosowanie
przy projektowaniu zbiorw wsppracujcych ze sob klas. W szczeglnoci umoliwiaj
pisanie zwizego i profesjonalnego kodu obsugujcego zdarzenia GUI.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

272

Java. Podstawy
Rozdzia zamyka opis obiektw porednich, implementujcych dowolne interfejsy. Obiekty
te to bardzo wyspecjalizowane narzdzia, ktrych uywaj programici narzdzi systemowych. Przy pierwszej lekturze tej ksiki mona ten podrozdzia pomin.

6.1. Interfejsy
W Javie interfejs nie jest klas, ale zestawem wymaga, ktre musz by spenione, aby klasa
zostaa uznana za zgodn z danym interfejsem.
Typowy dostawca usug mwi co takiego: Jeli twoja klasa spenia wymagania okrelonego
interfejsu, ja wykonam moj usug. Przeanalizujmy konkretny przykad. Metoda sort
z klasy Array sortuje tablice obiektw, ale pod jednym warunkiem: obiekty te musz nalee do klas, ktre implementuj interfejs Comparable.
Interfejs Comparable wyglda nastpujco:
public interface Comparable
{
int compareTo(Object other);
}

Oznacza to, e kada klasa implementujca powyszy interfejs musi mie metod compareTo,
ktra przyjmuje parametr typu Object i zwraca liczb cakowit.
Od Java SE 5.0 interfejs Comparable jest typem sparametryzowanym:
public interface Comparable<T>
{
int compareTo(T other);
// Parametr jest typu T.
}

Na przykad klasa implementujca interfejs Comparable<Employee> musi zawiera metod:


int compareTo(Employee other)

Nadal mona uywa surowego typu Comparable bez parametru typu, ale wtedy
konieczne jest wykonywanie na wasn rk rzutowania parametru metody compareTo
na odpowiedni typ.

Nie ma potrzeby stosowania sowa kluczowego public w deklaracjach metod w interfejsie,


poniewa wszystkie metody s automatycznie publiczne.
Oczywicie jest jeszcze jedno wymaganie, ktrego nie mona opisa w interfejsie. W wywoaniu x.compareTo(y) metoda compareTo musi porwna dwa podane obiekty i zwrci informacj na temat tego, ktry z nich jest wikszy. Liczba ujemna oznacza, e wikszy jest y,
zero, e obiekty s rwne, a liczba dodatnia, e wikszy jest x.
Ten interfejs ma tylko jedn metod, ale s interfejsy, ktre maj ich wicej. Pniej przekonamy si, e interfejsy mog nawet zawiera definicje staych. Waniejsze jest jednak to,
czego interfejs nie moe zawiera. Interfejsy nie mog zawiera pl obiektowych ani imple-

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

273

mentowa metod. Dostarczanie pl obiektowych i implementacja metod s czynnociami,


ktre nale do klas implementujcych interfejsy. Interfejs mona kojarzy z klas abstrakcyjn, ktra nie zawiera adnych pl obiektowych nieco dalej opisujemy rnice midzy
tymi dwoma konstrukcjami.
Przejdmy do metody sort klasy Array. Chcemy posortowa tablic obiektw klasy Employee.
W zwizku z tym klasa Employee musi implementowa interfejs Comparable.
Aby klasa implementowaa okrelony interfejs, naley wykona dwie czynnoci:
1.

Zadeklarowa, e klasa bdzie implementowaa dany interfejs.

2. Zdefiniowa wszystkie metody danego interfejsu.

Implementacja interfejsu jest wyraana za pomoc sowa kluczowego implements:


class Employee implements Comparable

Nastpnie naley zdefiniowa metod compareTo. Powiedzmy, e chcemy porwnywa pracownikw pod wzgldem wysokoci ich pensji. Poniej znajduje si odpowiednia implementacja metody compareTo:
public int compareTo(Object otherObject)
{
Employee other = (Employee) otherObject;
return Double.compare(salary, other.salary);
}

W przykadzie tym uylimy statycznej metody Double.compare, ktra zwraca warto ujemn,
gdy pierwszy argument jest mniejszy od drugiego, 0 gdy argumenty s rwne, oraz dodatni
warto w pozostaych przypadkach.
W deklaracji metody compareTo w interfejsie nie uyto sowa kluczowego public,
poniewa wszystkie metody w interfejsie s publiczne. Natomiast w implementacji interfejsu sowo to musi si pojawi w deklaracji metody. W przeciwnym przypadku
kompilator uzna, e metoda jest widoczna w obrbie pakietu co jest domylnym
zachowaniem dla klas. Nastpnie kompilator zgasza, e nadano mniejsze uprawnienia
dostpu.

Od Java SE 5.0 powysze zadanie mona wykona nieco lepiej. Zaimplementujemy interfejs
Comparable<Employee>.
class Employee implements Comparable<Employee>
{
public int compareTo(Employee other)
{
return Double.compare(salary, other.salary);
}
. . .
}

Warto zauway, e mao eleganckie rzutowanie parametru Object znikno.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

274

Java. Podstawy

Metoda compareTo interfejsu Comparable zwraca liczb cakowit. Jeli obiekty nie
s rwne, nie ma znaczenia, jaka liczba dodatnia lub ujemna zostanie zwrcona.
Ta elastyczno moe si okaza przydatna przy porwnywaniu pl przechowujcych liczby
cakowite. Niech na przykad kady pracownik ma unikatowy identyfikator w postaci liczby
cakowitej. Chcemy wykona sortowanie wedug identyfikatorw. W takim przypadku
wystarczy zwrci wynik dziaania id - inny.id. Warto ta bdzie ujemna, jeli pierwszy
identyfikator jest mniejszy od drugiego, bdzie wynosia 0, jeli s takie same, lub bdzie
dodatnia w przeciwnym przypadku. Istnieje jednak jedna puapka: porwnywane liczby
nie mog by zbyt due, poniewa mog spowodowa przekroczenie zakresu. Jeli wiadomo, e identyfikatory nie mog mie wartoci ujemnych lub e ich maksymalna warto bezwzgldna nie przekracza wartoci (Integer.MAX_VALUE-1)/2, nie ma problemu.
Oczywicie sztuczka ta nie nadaje si do stosowania z liczbami zmiennoprzecinkowymi.
Rnica salary - inna.salary moe zosta zaokrglona do 0, jeli porwnywane liczby
maj bardzo zblione, ale nie identyczne wartoci. Wywoanie Double.compare(x, y)
zwraca -1, gdy x < y, lub 1, gdy x > 0.

Wiemy ju, co klasa musi zrobi, aby mc skorzysta z usugi sortowania musi zaimplementowa metod compareTo. To nadzwyczaj rozsdne. Musi istnie jaki sposb na porwnywanie obiektw przez metod sort. Ale czemu klasa Employee nie moe definiowa metody
compareTo bez implementacji interfejsu Comparable?
Powodem wprowadzenia interfejsw w Javie byo to, e jest to jzyk ze cis kontrol
typw. Podczas tworzenia wywoania metody kompilator musi mie moliwo sprawdzenia,
czy ta metoda w ogle istnieje. W metodzie sort mona znale nastpujce instrukcje:
if (a[i].compareTo(a[j]) > 0)
{
// Zamiana miejscami obiektw a[i] i a[j].
. . .
}

Kompilator musi wiedzie, czy a[i] rzeczywicie udostpnia metod compareTo. Jeli a jest
tablic obiektw klasy implementujcej interfejs Comparable, wiadomo, e istnieje metoda
compareTo, poniewa kada klasa implementujca interfejs Comparable musi j definiowa.
Mona si spodziewa, e metoda sort klasy Arrays przyjmuje tablic Comparable[],
dziki czemu kompilator moe zgasza bdy, jeli metoda ta zostanie wywoana
przy uyciu tablicy zawierajcej obiekty nieimplementujce interfejsu Comparable. Niestety tak nie jest. W zamian metoda sort przyjmuje tablic Object[] i stosuje mao eleganckie rzutowanie:
// Kod z biblioteki standardowej niezalecany
if (((Comparable) a[i]).compareTo(a[j]) > 0)
{
// Zamiana miejscami obiektw a[i] i a[j]
. . .
}

Jeli obiekt a[i] nie naley do klasy implementujcej interfejs Comparable, maszyna
wirtualna zgasza wyjtek.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

275

Listing 6.1 przedstawia peny kod programu sortujcego tablic egzemplarzy klasy Employee,
ktra z kolei zostaa zaprezentowana na listingu 6.2.
Listing 6.1. interfaces/EmployeeSortTest.java
package interfaces;
import java.util.*;
/**
* Ten program demonstruje sposb uycia interfejsu Comparable.
* @version 1.30 2004-02-27
* @author Cay Horstmann
*/
public class EmployeeSortTest
{
public static void main(String[] args)
{
Employee[] staff = new Employee[3];
staff[0] = new Employee("Henryk Kwiatek", 35000);
staff[1] = new Employee("Karol Kowalski", 75000);
staff[2] = new Employee("Tadeusz Nowak", 38000);
Arrays.sort(staff);
// Drukowanie informacji o wszystkich obiektach klasy Employee.
for (Employee e : staff)
System.out.println("name=" + e.getName() + ",salary=" + e.getSalary());
}
}

Listing 6.2. interfaces/Employee.java


package interfaces;
public class Employee implements Comparable<Employee>
{
private String name;
private double salary;
public Employee(String n, double s)
{
name = n;
salary = s;
}
public String getName()
{
return name;
}
public double getSalary()
{
return salary;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

276

Java. Podstawy
public void raiseSalary(double byPercent)
{
double raise = salary * byPercent / 100;
salary += raise;
}
/**
* Porwnuje pracownikw wedug wysokoci pensji.
* @param other inny obiekt klasy Employee
* @return warto ujemna, jeli pracownik ma nisz pensj ni inny (other) pracownik,
* 0, jeli pensje s rwne, w przeciwnym razie liczba dodatnia
*/
public int compareTo(Employee other)
{
return Double.compare(salary, other.salary);
}
}
java.lang.Comparable<T> 1.0

int compareTo(T other)

Porwnuje obiekt z obiektem other i zwraca liczb ujemn, jeli pierwszy obiekt
jest mniejszy od drugiego, zero, jeli obiekty s rwne, lub liczb dodatni
w przeciwnym przypadku.
java.util.Arrays 1.2

static void sort(Object[] a)

Sortuje zawarto tablicy a przy uyciu zoptymalizowanego algorytmu mergesort.


Wszystkie elementy tablicy musz nalee do klas, ktre implementuj interfejs
Comparable, i musz da si porwna.
java.lang.Integer 7

static int compare(int x, int y)

Zwraca ujemn liczb cakowit, gdy x < y, zero, gdy x i y s rwne, oraz dodatni
liczb cakowit w pozostaych przypadkach.
java.lang.Double 7

static int compare(double x, double y)

Zwraca ujemn liczb cakowit, gdy x < y, zero, gdy x i y s rwne, oraz dodatni
liczb cakowit w pozostaych przypadkach.

6.1.1. Wasnoci interfejsw


Interfejsy nie s klasami. Nie mona utworzy egzemplarza interfejsu za pomoc operatora new:
x = new Comparable(. . .);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

// bd

Rozdzia 6.

Interfejsy i klasy wewntrzne

277

Zgodnie ze standardem jzyka implementator musi zapewni, e dla wszystkich x


i y sgn(x.compareTo(y)) = sgn(y.compareTo(x)) (oznacza to, e jeli wywoanie
x.compareTo(y) spowoduje wyjtek, to y.compareTo(x) rwnie musi spowodowa wyjtek). Sowo sgn oznacza znak liczby, sgn(n) ma warto -1, jeli n jest wartoci ujemn,
0 jeli n jest rwne 0, lub 1, jeli n jest wartoci dodatni. Innymi sowy, jeli parametry
metody compareTo zostan zamienione miejscami, znak (ale niekoniecznie sama warto) wyniku rwnie musi zmieni si na przeciwny.
Podobnie jak w przypadku metody equals, mog wystpi problemy z dziedziczeniem.
Poniewa klasa Manager rozszerza klas Employee, implementuje interfejs Comparable
<Employee>, a nie Comparable<Manager>. Jeli w klasie Manager zostanie przesonita
metoda compareTo, naley liczy si z porwnywaniem zwykych pracownikw z kierownikami. Nie mona zwyczajnie rzutowa typu zwykego pracownika na kierownika:
class Manager extends Employee
{
public int compareTo(Employee other)
{
Manager otherManager = (Manager) other;
...
}
...
}

// nie

To amie zasad antysymetrii. Jeli x jest typu Employee, a y typu Manager, wywoanie
x.compareTo(y) nie spowoduje wyjtku obiekty zostan porwnane jako zwykli pracownicy. Jednak odwrotne wywoanie y.compareTo(x) spowoduje wyjtek ClassCastException.
Jest to taka sama sytuacja jak w przypadku metody equals, ktr opisywalimy w rozdziale 5. Rozwizanie jest rwnie takie samo. Istniej dwa osobne scenariusze.
Jeli porwnywanie w podklasach opiera si na innych zasadach, naley zabroni porwnywania obiektw, ktre nale do innych klas. Kada metoda compareTo powinna si
zaczyna od nastpujcego testu:
if (getClass() != other.getClass()) throw new ClassCastException();

Jeli algorytmy porwnywania obu klas s takie same, naley utworzy tylko jedn metod
compareTo w nadklasie i zadeklarowa j jako finaln.
Przyjmijmy na przykad, e chcemy, aby kierownicy byli lepsi od zwykych pracownikw,
bez wzgldu na pensj. Co z pozostaymi klasami, jak Executive i Secretary? Aby ustali
porzdek dziobania, naley w klasie Employee zdefiniowa klas o nazwie np. rank.
Niech kada podklasa przesania metod rank i implementuje metod compareTo, ktra
bierze pod uwag wartoci rank.

Mimo e nie mona tworzy obiektw interfejsw, dopuszczalne jest deklarowanie ich
zmiennych.
Comparable x;

// OK

Zmienna interfejsowa musi si odwoywa do obiektu klasy implementujcej okrelony


interfejs:
x = new Employee(. . .);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

// W porzdku, pod warunkiem e klasa Employee implementuje


// interfejs comparable.

278

Java. Podstawy
Podobnie jak mona sprawdzi za pomoc operatora instanceof, czy obiekt naley do danej
klasy, mona przy uyciu niniejszego operatora sprawdzi, czy dany obiekt implementuje
okrelony interfejs:
if (anObject instanceof Comparable) { . . . }

Podobnie jak w przypadku klas, mona tworzy hierarchie interfejsw. W ten sposb mog
powstawa acuchy interfejsw przechodzce od najwyszego stopnia uoglnienia do najwyszego stopnia specjalizacji. Wyobramy sobie, e mamy interfejs o nazwie Moveable.
public interface Moveable
{
void move(double x, double y);
}

Wtedy nietrudno sobie wyobrazi sens istnienia interfejsu Powered, ktry by go rozszerza:
public interface Powered extends Moveable
{
double milesPerGallon();
}

Podczas gdy w interfejsie nie moe by pl obiektowych ani metod statycznych, mog by
stae. Na przykad:
public interface Powered extends Moveable
{
double milesPerGallon();
double SPEED_LIMIT = 95;
// Statyczna finalna staa publiczna.
}

Metody w interfejsie s zawsze publiczne, ale stae s statyczne, finalne i publiczne.


Metody w interfejsach mona oznacza sowem kluczowym public, a stae sowami public static final. Niektrzy programici robi to z przyzwyczajenia, a inni
z kolei z chci poprawienia czytelnoci kodu. Jednak specyfikacja jzyka Java nie zaleca
stosowania niepotrzebnych sw kluczowych. My stosujemy si do niniejszego zalecenia.

Niektre interfejsy zawieraj wycznie definicje staych. Na przykad interfejs SwingCon


stants z biblioteki standardowej definiuje stae NORTH, SOUTH, HORIZONTAL itd. Kada klasa
implementujca ten interfejs dziedziczy wszystkie te stae. W metodach takiej klasy mona
pisa odwoania typu NORTH zamiast SwingConstants.NORTH. Trzeba jednak przyzna, e taki
sposb uywania interfejsw wydaje si nieco zwyrodniay, wic nie zalecamy go.
Podczas gdy klasa moe mie tylko jedn nadklas, moe implementowa kilka interfejsw.
Dziki temu programista zyskuje pen dowolno, jeli chodzi o definiowanie zachowa
klasy. Na przykad w jzyku Java istnieje bardzo wany interfejs o nazwie Cloneable
(szczegowy opis tego interfejsu znajduje si w kolejnym podrozdziale). Jeli okrelona
klasa implementuje ten interfejs, bdzie mona za pomoc metody clone klasy Object wykona dokadn kopi obiektu tej klasy. Przypumy zatem, e chcemy, aby tworzona przez nas
klasa miaa zarwno funkcjonalno porwnywania, jak i klonowania. W takim przypadku
wystarczy zaimplementowa dwa opisane do tej pory interfejsy:
class Employee implements Cloneable, Comparable

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

279

Poszczeglne interfejsy naley oddzieli przecinkami.

6.1.2. Interfejsy a klasy abstrakcyjne


Kady, kto przeczyta sekcj o klasach abstrakcyjnych w rozdziale 5., moe si zastanawia,
czemu projektanci Javy zadawali sobie trud wprowadzania interfejsw. Czemu interfejs Compa
rable nie moe by zwyk klas abstrakcyjn?
abstract class Comparable
// czemu nie?
{
public abstract int compareTo(Object other);
}

W takiej sytuacji klasa Employee rozszerzaaby klas abstrakcyjn i definiowaa metod


compareTo:
class Employee extends Comparable
{
public int compareTo(Object other) { . . . }
}

// czemu nie?

Zasygnalizowany problem ma niestety zwizek z zastosowaniem klas abstrakcyjnych do


wyraania oglnych wasnoci. Kada klasa moe rozszerza tylko jedn klas. Przypumy,
e klasa Employee rozszerza ju inn klas, np. Person. W takiej sytuacji nie moe ju dziedziczy po innej klasie.
class Employee extends Person, Comparable

// bd

Jednak kada klasa moe implementowa dowoln liczb interfejsw:


class Employee extends Person implements Comparable

// OK

Inne jzyki, np. C++, zezwalaj na dziedziczenie po wicej ni jednej klasie. Nazywa si to
dziedziczeniem wielokrotnym (ang. multiple inheritance). Projektanci Javy postanowili
zrezygnowa z tej funkcji, poniewa komplikuje ona jzyk (jak w C++) lub odbija si na
wydajnoci (jak w jzyku Eiffel).
Interfejsy maj wikszo zalet dziedziczenia wielokrotnego, a s pozbawione wad zwizanych z komplikacj i efektywnoci jzyka.
Jzyk C++ obsuguje dziedziczenie wielokrotne z wszystkimi jego komplikacjami, jak
bazowe klasy wirtualne, zasady dominacji i poprzeczne rzutowanie wskanikw. Niewielu programistw jzyka C++ korzysta z dziedziczenia wielokrotnego, a niektrzy twierdz nawet, e nie powinno si go uywa w ogle. Inni programici zalecaj wykorzystywa dziedziczenie wielokrotne tylko w stylu mix-in. W takim przypadku gwna klasa
bazowa opisuje obiekt macierzysty, a dodatkowe klasy bazowe (tzw. mix-ins) mog
dostarcza dodatkowe cechy. Ten styl przypomina znan z Javy metod jednej klasy
bazowej i dodatkowych interfejsw. Jednak klasy dodatkowe w C++ mog dodawa
domylne zachowania, podczas gdy interfejsy w Javie nie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

280

Java. Podstawy

6.2. Klonowanie obiektw


Jeli skopiujemy zmienn, orygina i kopia s referencjami do tego samego obiektu (zobacz
rysunek 6.1). Oznacza to, e zmiana jednej z nich pociga za sob zmian w drugiej.
Employee original = new Employee("Jan Kowalski", 50000);
Employee copy = original;
copy.raiseSalary(10);
// To wywoanie take dotyczy oryginau.

Rysunek 6.1.
Kopiowanie
i klonowanie

Aby stworzona kopia bya cakiem nowym obiektem, ktrego stan pocztkowy jest taki sam
jak pierwowzoru, ale z czasem mogy pojawi si rnice, naley uy metody clone.
Employee copy = original.clone();
copy.raiseSalary(10);
// Oryginalny obiekt pozostaje bez zmian.

Operacja ta nie jest jednak wcale prosta. Metoda clone jest metod chronion klasy Object,
a to oznacza, e we wasnym kodzie nie mona do niej uzyska w prosty sposb dostpu.
Tylko klasa Employee moe klonowa obiekty typu Employee. Ograniczenie to nie zostao
wprowadzone bez powodu. Przeanalizujmy, jak moe wyglda implementacja metody clone
w klasie Object. Nie ma ona adnych informacji na temat obiektw do sklonowania, a wic
jedyne wyjcie to robienie kopii pole po polu. Jeli jednak klonowany obiekt zawiera refe-

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

281

rencje do podobiektw, skopiowanie takiego pola spowoduje powstanie nowej referencji do


tego samego podobiektu. W zwizku z tym obiekt oryginalny i jego kopia nadal dysponuj
pewnymi wsplnymi danymi.
Do zobrazowania tego zjawiska wykorzystamy klas Employee, ktra zostaa wprowadzona
w rozdziale 4. Rysunek 6.2 przedstawia, co si dzieje, kiedy obiekt tej klasy zostanie sklonowany przez metod clone klasy Object. Wida wyranie, e standardowa operacja klonowania jest pytka nie kopiuje obiektw, do ktrych odwouj si zmienne znajdujce
si w innych obiektach.

Rysunek 6.2. Kopiowanie pytkie

Czy takie pytkie kopiowanie w czym przeszkadza? To zaley. Jeli podobiekt wspdzielony przez inny obiekt i jego klon jest niezmienialny, wspdzielenie nie ma adnych
negatywnych skutkw. Jest tak na pewno wtedy, gdy podobiekt naley do niezmienialnej
klasy, np. String. Podobiekt moe te pozosta nietknity przez cay okres ycia obiektu,
jeli adna metoda ustawiajca nie zostanie wywoana na jego rzecz ani adna metoda nie
utworzy do niego referencji.
Czsto jednak zdarza si, e podobiekt jest zmienialny. W takim przypadku konieczne jest
przedefiniowanie metody clone w taki sposb, aby wykonywaa kopiowanie gbokie
(ang. deep copying), polegajce na sklonowaniu take podobiektw. W naszym przykadzie
pole hireDay jest obiektem klasy Date, ktra jest zmienialna.
W przypadku kadej klasy naley podj nastpujce decyzje:
1.

Czy standardowa metoda clone wystarczy.

2. Czy mona standardow metod clone uzupeni wywoaniami clone na rzecz

zmienialnych podobiektw.
3. Czy metoda clone nie powinna by wywoywana.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

282

Java. Podstawy
Trzecia z wymienionych opcji jest domylna. Aby mc wybra ktr z pozostaych, klasa musi:
1.

Implementowa interfejs Cloneable.

2. Przedefiniowa metod clone, dodajc do niej modyfikator public.


Metoda clone w klasie Object jest chroniona, przez co nie mona w kodzie po prostu umieci wywoania typu jakiObiekt.clone(). Ale czy metody chronione nie
s dostpne w podklasach i czy kada klasa nie jest podklas klasy Object? Na szczcie
zasady dotyczce dostpu chronionego s bardziej wyrafinowane (zobacz rozdzia 5.).
Podklasa moe wywoa chronion metod clone tylko w celu sklonowania swojego wasnego obiektu. Aby obiekty mogy by klonowane przez dowoln metod, metoda clone
musi by przedefiniowana jako publiczna.

W takim przypadku uycie interfejsu Cloneable nie ma nic wsplnego z normalnym zastosowaniem interfejsw. W szczeglnoci nie okrela on metody clone metoda ta jest
dziedziczona po klasie Object. Interfejs jest tu jedynie znacznikiem informujcym, e projektant klasy wie, na czym polega klonowanie. Obiekty s tak wraliwe na punkcie klonowania, e generuj kontrolowany wyjtek, jeli tylko obiekt dajcy sklonowania nie implementuje interfejsu Cloneable.
Interfejs Cloneable jest jednym z kilku interfejsw znacznikowych (ang. tagging
interface, marker interface) Javy. Przypomnijmy, e typowym zastosowaniem interfejsw, jak Comparable, jest zapewnienie, e okrelona klasa zawiera implementacj
okrelonej metody lub metod. Interfejs znacznikowy nie ma adnych metod, a jego jedynym celem jest zezwolenie na uycie operatora instanceof do sprawdzenia typu:
if (obj instanceof Cloneable) . . .

Nie zalecamy stosowania interfejsw znacznikowych.

Nawet jeli standardowa implementacja metody clone (kopiowanie pytkie) jest wystarczajca,
nadal konieczne jest zaimplementowanie interfejsu Cloneable, przedefiniowanie metody clone
na publiczn i wywoanie metody super.clone(). Spjrzmy na przykad:
class Employee implements Cloneable
{
// Zwikszenie widocznoci na public i zmiana typu zwrotnego.
public Employee clone() throws CloneNotSupportedException
{
return (Employee) super.clone();
}
. . .
}

Przed Java SE 5.0 metoda clone zawsze zwracaa typ Object. Obecnie kowariantne
typy zwrotne umoliwiaj okrelenie odpowiedniego typu dla metod clone.

Metoda clone, ktr ogldalimy przed chwil, nie rozszerza w aden sposb funkcjonalnoci
metody Object.clone. Jedyne jej zadanie to upublicznienie metody. Aby zrobi gbok kopi,
naley bardziej si postara, aby skopiowa zmienialne pola obiektowe.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

283

Ponisza metoda clone tworzy gbok kopi obiektu:


class Employee implements Cloneable
{
. . .
public Employee clone() throws CloneNotSupportedException
{
// Wywoanie metody Object.clone().
Employee cloned = (Employee) super.clone();
// Klonowanie pl zmienialnych.
cloned.hireDay = (Date) hireDay.clone()

return cloned;

Metoda clone klasy Object moe spowodowa wyjtek CloneNotSupportedException robi


to, kiedy jest wywoywana na rzecz obiektu, ktrego klasa nie implementuje interfejsu Clone
able. Oczywicie klasy Employee i Date implementuj ten interfejs, a wic wyjtku nie
bdzie. Kompilator o tym jednak nie wie. Dlatego zadeklarowalimy wyjtek:
public Employee clone() throws CloneNotSupportedException

Czy nie lepiej byoby przechwyci ten wyjtek?


public Employee clone()
{
try
{
return (Employee) super.clone();
}
catch (CloneNotSupportedException e) { return null; }
// To si nie stanie, poniewa interfejs Cloneable jest zaimplementowany.
}

Takie podejcie jest odpowiednie dla klas finalnych. W pozostaych sytuacjach lepiej zostawi
specyfikator throws tam, gdzie jego miejsce.
Naley uwaa na klonowanie podklas. Jeli na przykad metoda clone zostaa zdefiniowana
dla klasy Employee, mona jej uy do klonowania obiektw klasy Manager. Czy metoda clone
klasy Employee poradzi sobie w takiej sytuacji? To zaley od tego, jakie pola zawiera klasa
Manager. W tym przypadku nie ma problemu, poniewa pole bonus jest typu podstawowego.
Jednak klasa Manager mogaby mie pola wymagajce gbokiego kopiowania lub takie, ktre
nie s klonowalne. Nie ma adnej gwarancji, e programista podklasy wnis odpowiednie
poprawki do metody clone, aby odpowiednio wykonywaa swoje zadanie. Z tego powodu
metoda clone w klasie Object jest chroniona. Nie moemy jednak skorzysta z tej wygody,
jeli chcemy, aby uytkownicy naszej klasy wywoywali metod clone.
Czy naley implementowa metod clone we wasnych klasach? Jeli uytkownicy musz
tworzy gbokie kopie ich obiektw, to tak. Niektrzy programici uwaaj, e naley w ogle
unika metody clone i zamiast niej implementowa inn, przeznaczon do tego samego celu.
Zgadzamy si, e metoda clone nie jest idealna, ale nie pozbdziemy si problemw z ni

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

284

Java. Podstawy
zwizanych, przerzucajc si na inn metod. W kadym razie klonowanie jest znacznie
rzadziej wykonywan operacj, ni si wydaje. Implementacj metody clone zawiera mniej
ni 5 procent wszystkich klas w bibliotece standardowej.
Program na listingach 6.3 i 6.4 klonuje obiekt klasy Employee, a nastpnie wywouje dwie
metody zmieniajce. Metoda raiseSalary zmienia warto pola salary, podczas gdy setHire
Day zmienia stan pola hireDay. adna ze zmian nie ma wpywu na oryginalny obiekt, poniewa metoda clone wykonuje kopiowanie gbokie.
Wszystkie typy tablicowe maj publiczn (niechronion) metod clone. Mona za
jej pomoc tworzy nowe tablice zawierajce kopie wszystkich elementw. Na
przykad:
int[] luckyNumbers = { 2, 3, 5, 7, 11, 13 };
int[] cloned = (int[]) luckyNumbers.clone();
cloned[5] = 12;
// Nie zmienia elementu luckyNumbers[5].

Listing 6.3. clone/CloneTest.java


package clone;
/**
* Ten program demonstruje klonowanie.
* @version 1.10 2002-07-01
* @author Cay Horstmann
*/
public class CloneTest
{
public static void main(String[] args)
{
try
{
Employee original = new Employee("Jan W. Kowalski", 50000);
original.setHireDay(2000, 1, 1);
Employee copy = original.clone();
copy.raiseSalary(10);
copy.setHireDay(2002, 12, 31);
System.out.println("original=" + original);
System.out.println("copy=" + copy);
}
catch (CloneNotSupportedException e)
{
e.printStackTrace();
}
}
}

Listing 6.4. clone/Employee.java


package clone;
import java.util.Date;
import java.util.GregorianCalendar;
public class Employee implements Cloneable

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.
{

Interfejsy i klasy wewntrzne

285

private String name;


private double salary;
private Date hireDay;
public Employee(String n, double s)
{
name = n;
salary = s;
hireDay = new Date();
}
public Employee clone() throws CloneNotSupportedException
{
// Wywoanie metody Object.clone().
Employee cloned = (Employee) super.clone();
// Klonowanie pl zmienialnych.
cloned.hireDay = (Date) hireDay.clone();
}

return cloned;

/**
* Ustawia dat zatrudnienia na podany dzie.
* @param year rok zatrudnienia
* @param month miesic zatrudnienia
* @param day dzie zatrudnienia
*/
public void setHireDay(int year, int month, int day)
{
Date newHireDay = new GregorianCalendar(year, month - 1, day).getTime();

// Przykad zmiany pola obiektowego.


hireDay.setTime(newHireDay.getTime());

public void raiseSalary(double byPercent)


{
double raise = salary * byPercent / 100;
salary += raise;
}

public String toString()


{
return "Employee[name=" + name + ",salary=" + salary + ",hireDay=" + hireDay + "]";
}

Rozdzia 1. drugiego tomu przedstawia alternatywny sposb klonowania obiektw,


polegajcy na zastosowaniu serializacji. Niniejsza technika jest atwa w implementacji i bezpieczna, ale niestety mao wydajna.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

286

Java. Podstawy

6.3. Interfejsy a sprzenie zwrotne


W programowaniu czsto wykorzystywane jest zjawisko sprzenia zwrotnego (ang. callback). Ten styl programowania polega na okreleniu dziaa, ktre maj zosta wykonane
w odpowiedzi na okrelone zdarzenia. Moe to by na przykad wykonanie pewnych czynnoci w odpowiedzi na kliknicie przycisku lub wybr elementu w menu. Poniewa nie
potrafimy jeszcze implementowa interfejsw uytkownika, przeanalizujemy prostsz, ale
podobn sytuacj.
Pakiet javax.swing zawiera klas Timer, ktr mona wykorzysta do odmierzania czasu. Jeli
na przykad program zawiera zegar, niniejsza klasa moe wysya co sekund sygna, ktrego programista uyje do aktualizacji tarczy zegara.
Konstruujc zegar, mona ustawi przedzia czasu oraz zdefiniowa dowolne dziaania, ktre
maj zosta wykonane po upyniciu kadego kolejnego przedziau.
Jak poinformowa zegar, co ma robi? W wielu jzykach programowania w tym celu podaje
si nazw funkcji, ktra ma by wywoywana. Jednak klasy w bibliotece standardowej Javy
s obiektowe. Trzeba przekaza obiekt jednej z tych klas, a zegar wywoa jedn z metod
takiego obiektu. Przekazywany obiekt ma t przewag nad funkcj, e moe zawiera dodatkowe dane.
Oczywicie zegar musi wiedzie, ktr metod ma wywoa. Dodatkowo obiekt przekazywany do zegara musi nalee do klasy implementujcej interfejs ActionListener z pakietu
java.awt.event. Oto ten interfejs:
public interface ActionListener
{
void actionPerformed(ActionEvent event);
}

Zegar wywouje metod actionPerformed po upyniciu wyznaczonego czasu.


Jak dowiedzielimy si w rozdziale 5., w Javie istnieje odpowiednik wskanikw do
funkcji w postaci obiektw Method. S one jednak trudne w uyciu, wolniejsze i nie
mona ich sprawdza pod ktem bezpieczestwa w czasie kompilacji. W sytuacji, w ktrej
w C++ naley uy wskanika do funkcji, w Javie powinno si rozway uycie interfejsu.

Wyobramy sobie, e chcemy, aby co dziesi sekund drukowany by napis Kiedy usyszysz
dwik, bdzie godzina. Aby wykona to zadanie, mona zdefiniowa klas implementujc
interfejs ActionListener i w metodzie o nazwie actionPerformed umieci wszystkie instrukcje, ktre maj zosta wykonane.
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

287

Toolkit.getDefaultToolkit().beep();

Zatrzymajmy si na chwil przy parametrze ActionEvent metody actionPerformed. Dostarcza on informacji dotyczcych zdarzenia, jak np. obiekt rdowy, ktry je wygenerowa
wicej na ten temat zostao napisane w rozdziale 8. W tym przypadku jednak dane zdarzenia nie maj znaczenia, a wic mona bez obaw zignorowa wspomniany parametr.
Nastpnie utworzymy obiekt naszej klasy i uyjemy go w konstruktorze klasy Timer.
ActionListener listener = new TimePrinter();
Timer t = new Timer(10000, listener);

Pierwszy parametr konstruktora Timer okrela liczb milisekund, ktre maj dzieli kolejne
powiadomienia chcemy by powiadamiani co dziesi sekund. Drugi parametr to obiekt
nasuchujcy (ang. listener object).
Na koniec uruchamiamy zegar.
t.start();

Co dziesi sekund bdzie wywietlany komunikat typu:


Kiedy usyszysz dwik, bdzie godzina Fri Dec 14 10:26:40 CET 2007

po ktrym nastpi odtworzenie dwiku.


Listing 6.5 przedstawia opisywany program w caoci. Po uruchomieniu zegara wywietlane
jest okno dialogowe z przyciskiem OK zamykajcym program. W czasie oczekiwania na reakcj uytkownika co dziesi sekund wywietlane s aktualne data i godzina.
Listing 6.5. timer/TimerTest.java
package timer;
/**
@version 1.00 2000-04-13
@author Cay Horstmann
*/
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;
// Powyszy import ma na celu zapobiec konfliktowi z klas java.util.Timer.
public class TimerTest
{
public static void main(String[] args)
{
ActionListener listener = new TimePrinter();
// Konstrukcja zegara wywoujcego obiekt nasuchujcy
// co dziesi sekund.
Timer t = new Timer(10000, listener);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

288

Java. Podstawy
t.start();
JOptionPane.showMessageDialog(null, "Zamkn program?");
System.exit(0);
}
}
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
Toolkit.getDefaultToolkit().beep();
}
}

Po uruchomieniu tego programu trzeba cierpliwie odczeka dziesi sekund. Wprawdzie okno
dialogowe Zamkn program? pojawia si od razu, ale dwik jest odtwarzany po raz pierwszy
po upywie dziesiciu sekund.
Warto zauway, e klasa javax.swing.Timer zostaa w tym programie zaimportowana bezporednio. Ma to na celu zapobiec konfliktowi klas javax.swing.Timer i java.util.Timer,
ktre su do planowania zada wykonywanych w tle.
javax.swing.JOptionPane 1.2

static void showMessageDialog(Component parent, Object message)

Wywietla okno dialogowe z komunikatem i przyciskiem OK. Okno jest wywietlane


na rodku komponentu parent. Jeli parent ma warto null, okno wywietla
si na rodku ekranu.
javax.swing.Timer 1.2

Timer(int interval, ActionListener listener)

Tworzy zegar powiadamiajcy obiekt listener o upyniciu liczby milisekund


okrelonej przez parametr interval.

void start()

Uruchamia zegar, ktry wywouje metod actionPerformed na rzecz obiektw


nasuchujcych.

void stop()

Zatrzymuje zegar. Po zatrzymaniu zegar nie wywouje metody actionPerformed.


javax.awt.Toolkit 1.0

static Toolkit getDefaultToolkit()

Zwraca standardowy zestaw narzdzi, na ktry skadaj si dane dotyczce


rodowiska GUI.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

289

void beep()

Odtwarza krtki alarm dwikowy.

6.4. Klasy wewntrzne


Klasa wewntrzna (ang. inner class) charakteryzuje si tym, e jest zdefiniowana wewntrz
innej klasy. S trzy powody, dla ktrych warto definiowa tego typu klasy:

Metody klas wewntrznych maj dostp do danych w obszarze, w ktrym


s zdefiniowane wliczajc dane, ktre w innym przypadku byyby prywatne.

Klas wewntrzn mona ukry przed innymi klasami tego samego pakietu.

Anonimowe klasy wewntrzne s przydatne przy definiowaniu metod zwrotnych,


poniewa umoliwiaj uniknicie pisania duych iloci kodu.

Poniewa temat ten jest nieco skomplikowany, rozbijemy go na kilka kolejno omwionych
czci:

prezentacj przykadu prostej klasy wewntrznej uzyskujcej dostp do pola


obiektowego swojej klasy zewntrznej;

reguy skadniowe klas wewntrznych;

omwienie klas wewntrznych i przekonanie si, w jaki sposb odbywa si ich


konwersja na zwyke klasy; zbyt wraliwi czytelnicy mog pomin t sekcj;

opis lokalnych klas wewntrznych (ang. local inner class), ktre maj dostp
do zmiennych lokalnych otaczajcych je klas;

wprowadzenie do anonimowych klas wewntrznych (ang. anonymous inner class)


oraz pokazanie, jak si ich uywa do implementacji metod zwrotnych;

przedstawienie sposobu tworzenia wewntrznych klas pomocniczych przy uyciu


statycznych klas wewntrznych.

6.4.1. Dostp do stanu obiektu w klasie wewntrznej


Skadnia klas wewntrznych jest nieco skomplikowana. Dlatego zdecydowalimy si na
zaprezentowanie prostego, ale raczej sztucznego przykadu przedstawiajcego sposb ich
uycia. Refaktoryzowalimy klas TimerTest i wydzielilimy klas TalkingClock. Obiekt tej
klasy jest tworzony przy uyciu dwch parametrw: odstpu czasu pomidzy komunikatami
i znacznika wczajcego lub wyczajcego alarm dwikowy.
public class TalkingClock
{
private int interval;
private boolean beep;
public TalkingClock(int interval, boolean beep) { . . . }

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

290

Java. Podstawy

W C++ istniej klasy zagniedone. Klasa zagniedona znajduje si w obrbie


klasy zewntrznej. Oto typowy przykad: klasa listy dowiza definiuje klas przechowujc dowizania i klas definiujc pozycj iteratora.
class LinkedList
{
public:
class Iterator
// klasa zagniedona
{
public:
void insert(int x);
int erase();
. . .
};
. . .
private:
class Link
// klasa zagniedona
{
public:
Link* next;
int data;
};
. . .
};

Zagniedanie wyraa relacje midzy klasami, a nie obiektami. Obiekty klasy LinkedList
nie maj podobiektw typu Iterator lub Link.
Ma to dwie zalety dotyczce kontroli nazw i kontroli dostpu. Poniewa klasa Iterator
jest zagniedona w klasie LinkedList, na zewntrz nazywa si LinkedList::Iterator,
dziki czemu nie ma moliwoci wystpienia konfliktu z inn klas o nazwie Iterator.
W Javie ta zaleta nie ma wikszego znaczenia, poniewa podobnie dziaaj pakiety.
Zauwamy, e klasa Link nie jest prywatnym skadnikiem klasy LinkedList. Jest w peni
ukryta przed pozostaym kodem. Dziki temu mona bezpiecznie uywa w niej pl publicznych. Dostp do nich mog uzyska metody klasy LinkedList (ktra musi mie do nich
dostp), a dla pozostaej czci programu s one niewidoczne. Przed wprowadzeniem
klas wewntrznych w Javie tego rodzaju kontrola nie bya moliwa.
Jednak klasy wewntrzne w Javie maj dodatkow funkcjonalno, ktrej brak klasom
zagniedonym w C++. Obiekt klasy wewntrznej zawiera niejawn referencj do obiektu
klasy zewntrznej, ktry go utworzy. Za pomoc tego wskanika obiekt ma dostp do
stanu obiektu zewntrznego. Szczegy tego mechanizmu s opisane w dalszej czci
tego rozdziau.
W Javie statyczne klasy wewntrzne nie maj tego wskanika. S odpowiednikiem klas
zagniedonych w C++.
public void start() { . . . }
public class TimePrinter implements ActionListener
// klasa wewntrzna
{
. . .
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

291

Naley zauway, e klasa TimePrinter znajduje si wewntrz klasy TalkingClass. Nie


oznacza to jednak, e kady obiekt klasy TalkingClock ma pole TimePrinter. Jak si niebawem
przekonamy, obiekty klasy TimePrinter s tworzone przez metody klasy TalkingClass.
Poniej znajduje si kod klasy TimePrinter. Zauwamy, e metoda actionPerformed przed
odtworzeniem alarmu sprawdza stan znacznika beep.
private class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}

Dzieje si co zaskakujcego. Klasa TimePrinter nie ma pola, czyli zmiennej o nazwie beep.
W zamian beep odnosi si do pola obiektu TalkingClock, ktry utworzy obiekt TimePrinter.
Jest to innowacyjne podejcie. Normalnie metoda odwoywaaby si do pl obiektu, ktry
j wywoa. Metody klas wewntrznych maj dostp zarwno do wasnych pl, jak i pl
obiektu zewntrznego.
Aby to dziaao, obiekt klasy wewntrznej zawsze ma niejawn referencj do obiektu, ktry
go utworzy (zobacz rysunek 6.3).
Rysunek 6.3.
Obiekt klasy
wewntrznej
zawiera
referencj
do obiektu klasy
zewntrznej

Ta referencja jest niewidoczna w definicji klasy wewntrznej. Dla jasnoci nazwiemy referencj do obiektu zewntrznego outer. W takiej sytuacji metoda actionPerformed jest rwnowana z ponisz:
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (outer.beep) Toolkit.getDefaultToolkit().beep();
}

Referencja klasy zewntrznej jest ustawiana w konstruktorze. Kompilator modyfikuje konstruktory wszystkich klas wewntrznych, dodajc parametr referencji do obiektu klasy
zewntrznej. Poniewa klasa TimePrinter nie definiuje adnego konstruktora, kompilator
syntetyzuje konstruktor domylny, w wyniku czego powstaje poniszy kod:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

292

Java. Podstawy
public TimePrinter(TalkingClock clock)
{
outer = clock;
}

// Kod wygenerowany automatycznie.

Przypominamy jeszcze raz, e outer nie jest sowem kluczowym Javy, a suy nam tylko do
ilustracji mechanizmw rzdzcych klasami wewntrznymi.
Kiedy metoda start tworzy obiekt TimePrinter, kompilator przekazuje referencj this do
aktualnego obiektu TalkingClock konstruktorowi:
ActionListener listener = new TimePrinter(this);

// Automatycznie dodany parametr.

Listing 6.6 przedstawia kompletny program testujcy nasz klas wewntrzn. Jeszcze raz
wrmy do kontroli dostpu. Gdyby klasa TimePrinter bya zwyk klas, musiaaby uzyska
dostp do znacznika beep za pomoc metody publicznej klasy TalkingClock. Uycie klasy
wewntrznej zmienia sytuacj na lepsze. Nie ma koniecznoci tworzenia akcesorw, ktrych
potrzebuje tylko jedna klasa.
Klas TimePrinter mona byo zadeklarowa jako prywatn. W takim przypadku
obiekty tej klasy mogyby by tworzone wycznie przez metody klasy TalkingClock.
Tylko klasy wewntrzne mog by prywatne. Zwyke klasy zawsze maj zasig pakietowy lub publiczny.
Listing 6.6. innerClass/InnerClassTest.java
package innerClass;
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
javax.swing.Timer;

/**
* Ten program demonstruje sposb uycia klas wewntrznych.
* @version 1.10 2004-02-27
* @author Cay Horstmann
*/
public class InnerClassTest
{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock(1000, true);
clock.start();
// Niech program dziaa, dopki uytkownik nie wcinie przycisku OK.
JOptionPane.showMessageDialog(null, "Zamkn program?");
System.exit(0);
}
}
/**
* Zegar drukujcy informacje o czasie w rwnych odstpach czasu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

293

*/
class TalkingClock
{
private int interval;
private boolean beep;
/**
* Tworzy obiekt TalkingClock.
* @param interval odstp czasu pomidzy kolejnymi komunikatami (w milisekundach)
* @param beep warto true oznacza, e dwik ma by odtwarzany
*/
public TalkingClock(int interval, boolean beep)
{
this.interval = interval;
this.beep = beep;
}
/**
* Wczanie zegara.
*/
public void start()
{
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}
public class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
}

6.4.2. Specjalne reguy skadniowe dotyczce klas wewntrznych


W poprzednim podrozdziale referencj klasy wewntrznej do obiektu klasy zewntrznej
nazwalimy outer. Waciwa skadnia jest jednak nieco bardziej skomplikowana. Wyraenie:
OuterClass.this

okrela referencj do klasy zewntrznej. Na przykad metoda actionPerformed klasy wewntrznej TimePrinter moe wyglda nastpujco:
public void actionPerformed(ActionEvent event)
{
. . .
if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep();
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

294

Java. Podstawy
Z drugiej strony konstruktor klasy wewntrznej mona napisa przy uyciu nastpujcej
skadni:
outerObject.new InnerClass(parametry konstrukcyjne)

Na przykad:
ActionListener listener = this.new TimePrinter();

W tym przypadku referencja do klasy zewntrznej nowo utworzonego obiektu klasy Time
Printer zostaje ustawiona na referencj this metody, ktra tworzy obiekt klasy wewntrznej.
Jest to najczciej spotykany przypadek. Kwalifikator .this jak zwykle nie jest konieczny.
Mona te jednak ustawi referencj do klasy zewntrznej na inny obiekt, jeli poda si bezporednio jego nazw. Poniewa klasa TimePrinter jest wewntrzn klas publiczn, mona na
przykad utworzy obiekt TimePrinter dla dowolnego obiektu TalkingClock:
TalkingClock jabberer = new TalkingClock(1000, true);
TalkingClock.TimePrinter listener = jabberer.new TimePrinter();

Naley zauway, e odwoanie do klasy wewntrznej wyglda nastpujco:


OuterClass.InnerClass

jeli znajduje si poza klas zewntrzn.

6.4.3. Czy klasy wewntrzne s potrzebne i bezpieczne?


Wielu programistw dodanie klas wewntrznych do Java 1.1 uznao za powane odejcie od
filozofii tworzenia jzyka prostszego ni C++. Nie da si ukry, e skadnia klas wewntrznych jest skomplikowana (bdzie jeszcze bardziej skomplikowana, gdy zaczniemy zajmowa
si anonimowymi klasami wewntrznymi). Interakcje klas wewntrznych z innymi funkcjami jzyka, takimi jak kontrola dostpu i zabezpieczenia, nie s jasne.
Czy dodanie funkcjonalnoci, ktra jest bardziej elegancka i interesujca ni potrzebna, jest
dla Javy pocztkiem drogi donikd, ktr przeszo tak wiele jzykw?
Mimo e nie podejmiemy prby udzielenia jednoznacznej odpowiedzi na to pytanie, warto
odnotowa, e klasy wewntrzne s zjawiskiem znanym kompilatorowi, a nie maszynie
wirtualnej. S one konwertowane na zwyke pliki klas, ktrych nazwy skadaj si z nazw
klasy zewntrznej i wewntrznej rozdzielonych symbolem dolara. Maszyna wirtualna nie ma
adnych specjalnych danych na ich temat.
Na przykad klasa TimePrinter zdefiniowana w klasie TalkingClock jest zamieniana na plik
klasy o nazwie TalkingClock$TimePrinter.class. Aby to sprawdzi, mona przeprowadzi
nastpujcy eksperyment: uruchom program ReflectionTest z rozdziau 5. i podaj do zbadania klas TalkingClock$TimePrinter albo uyj narzdzia javap:
javap -private NazwaKlasy

Zostan zwrcone nastpujce dane:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

295

Przy podawaniu nazwy klasy w wierszu polece w systemie UNIX naley pamita,
aby symbol $ zastpi symbolem zastpczym. To znaczy program ReflectionTest
naley uruchomi w nastpujcy sposb:
java reflection.ReflectionTest klasaWewnetrzna.TalkingClock\$TimePrinter

a narzdzie javap nastpujco:


javap -private klasaWewnetrzna.TalkingClock\$TimePrinter
public class TalkingClock$TimePrinter
{
public TalkingClock$TimePrinter(TalkingClock);
public void actionPerformed(java.awt.event.ActionEvent);
final TalkingClock this$0;
}

Jak wida, kompilator wygenerowa dodatkowe pole obiektowe this$0 dla referencji do klasy
zewntrznej (nazwa this$0 jest utworzona przez kompilator, a wic nie mona uywa jej
w kodzie programu). Jest te parametr konstruktora TalkingClock.
Skoro kompilator moe wykona tak transformacj, czy nie mona by byo zrobi tego wasnorcznie? Sprbujmy. Klas TimePrinter zdefiniowalibymy jako zwyk klas, na zewntrz
klasy TalkingClock. Podczas konstrukcji obiektu TimePrinter przekazujemy mu referencj this
obiektu, ktry go tworzy.
class TalkingClock
{
. . .
public void start()
{
ActionListener listener = new TimePrinter(this);
Timer t = new Timer(interval, listener);
t.start();
}
}
class TimePrinter implements ActionListener
{
private TalkingClock outer;
. . .
public TimePrinter(TalkingClock clock)
{
outer = clock;
}
}

Przejdmy teraz do metody actionPerformed. Musi ona mie dostp do outer.beep.


if (outer.beep) . . .

// bd

W tym miejscu pojawia si problem. Klasa wewntrzna ma dostp do danych prywatnych


klasy zewntrznej, ale nasza zewntrzna klasa TimePrinter nie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

296

Java. Podstawy
Dowodzi to, e klasy wewntrzne rzeczywicie maj wiksze moliwoci ni zwyke klasy,
poniewa maj wiksze prawa dostpu.
Niektrych moe ciekawi, w jaki sposb klasy wewntrzne uzyskuj swoje zwikszone prawa
dostpu, skoro s konwertowane na zwyke klasy o dziwnych nazwach maszyna wirtualna nic o nich nie wie. Aby rozwiza t zagadk, zbadajmy jeszcze raz klas TalkingClock za
pomoc programu ReflectionTest:
class TalkingClock
{
private int interval;
private boolean beep;
public TalkingClock(int, boolean);
static boolean access$0(TalkingClock);
public void start();
}

Zwrmy uwag na statyczn metod access$0, ktr kompilator doda do klasy zewntrznej.
Zwraca ona pole beep obiektu, ktry jest przekazany jako parametr. (Nazwa metody moe
by troch inna, np. access$000 wszystko zaley od kompilatora).
Metod t wywouj metody klasy wewntrznej. Instrukcja:
if (beep)

w metodzie actionPerformed klasy TimePrinter wykonuje nastpujce wywoanie:


if (access$0(outer));

Czy nie stanowi to zagroenia bezpieczestwa? Niestety tak. Wywoanie metody access$0
w celu odczytania wartoci prywatnego pola beep nie jest trudne. Oczywicie nazwa access$0
nie jest dozwolon nazw dla metody w Javie, ale haker znajcy dobrze struktur plikw klas
moe z atwoci utworzy taki plik zawierajcy instrukcje maszyny wirtualnej wywoujce
t metod. Mona do tego celu uy na przykad edytora heksadecymalnego. Poniewa ukryte
metody dostpu s widoczne w obrbie pakietu, kod hakera musiaby zosta umieszczony
w tym samym pakiecie co atakowana klasa.
Podsumowujc, jeli klasa wewntrzna ma dostp do prywatnego pola danych, to take inne
klasy dodane do pakietu klasy zewntrznej bd miay do niego dostp. Zrobienie tego
wymaga jednak determinacji i wiedzy. Programista nie moe przypadkowo uzyska dostpu.
Musi w tym celu utworzy lub zmodyfikowa specjalny plik klasy.

6.4.4. Lokalne klasy wewntrzne


Analizujc uwanie kod klasy TalkingClock, mona spostrzec, e nazwa typu TimePrinter
jest potrzebna tylko jeden raz przy tworzeniu obiektu tego typu w metodzie start.
W takiej sytuacji mona zdefiniowa klas lokalnie w obrbie jednej metody.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

297

Syntetyzowane konstruktory i metody bywaj nieco zawie. Wyobramy sobie, e


klasa TimePrinter jest prywatn klas wewntrzn. W maszynie wirtualnej klasy
prywatne nie istniej, a wic kompilator radzc sobie najlepiej, jak moe tworzy
klas o zasigu pakietowym z prywatnym konstruktorem:
private TalkingClock$TimePrinter(TalkingClock);

Oczywicie tego konstruktora nie da si wywoa, dlatego istnieje jeszcze jeden o zasigu
pakietowym:
TalkingClock$TimePrinter(TalkingClock, TalkingClock$1)

Niniejszy konstruktor wywouje ten pierwszy z wymienionych.


Wywoanie tego konstruktora w metodzie start klasy TalkingClock jest konwertowane
przez kompilator na poniszy kod:
new TalkingClock$TimePrinter(this, null)
public void start()
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}

ActionListener listener = new TimePrinter();


Timer t = new Timer(interval, listener);
t.start();

W definicjach klas lokalnych nie stosuje si specyfikatorw dostpu (public lub private).
Nie ma do nich dostpu spoza bloku, w ktrym zostay zdefiniowane.
Wielk zalet klas lokalnych jest to, e pozostaj ukryte przed wiatem zewntrznym. Nie
ma do nich dostpu nawet kod otaczajcej klasy. W omawianym przypadku tylko metoda
start wie o istnieniu klasy TimePrinter.

6.4.5. Dostp do zmiennych finalnych z metod zewntrznych


Klasy lokalne maj jeszcze jedn cech, ktra wyrnia je na tle pozostaych klas wewntrznych. Maj one dostp nie tylko do pl klas je otaczajcych, ale rwnie do zmiennych lokalnych. Zmienne te musz by jednak finalne. Oto typowy przykad. Przeniesiemy parametry
interval i beep z konstruktora klasy TalkingClock do metody start.
public void start(int interval, final boolean beep)
{
class TimePrinter implements ActionListener
{
public void actionPerformed(ActionEvent event)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

298

Java. Podstawy
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
}
ActionListener listener = new TimePrinter();
Timer t = new Timer(interval, listener);
t.start();
}

Naley zauway, e klasa TalkingClock nie musi ju zawiera pola beep. Odwouje si do
zmiennej parametrycznej beep metody start.
Moe nie jest to zaskakujce. Wiersz:
if (beep) . . .

znajduje si w caoci w metodzie start, wic dlaczego nie powinien mie dostpu do wartoci
zmiennej beep?
Aby to sprawdzi, przeanalizujemy uwanie przepyw sterowania.
1.

Nastpuje wywoanie metody start.

2. W wyniku wywoania konstruktora klasy wewntrznej TimePrinter nastpuje


inicjacja zmiennej obiektowej listener.
3. Referencja listener zostaje przekazana do konstruktora Timer, nastpuje
uruchomienie zegara i zakoczenie metody start. W tym momencie parametr
beep metody start przestaje istnie.
4. Chwil pniej metoda actionPerformed wykonuje wiersz if(beep).

Aby metoda actionPerformed dziaaa, klasa TimePrinter musiaa skopiowa warto pola beep
jako zmienn lokaln metody start przed znikniciem wartoci parametru beep. Wanie to si
stao. W powyszym przykadzie kompilator tworzy nazw TalkingClock$1TimePrinter dla
lokalnej klasy wewntrznej. Analiza klasy TalkingClock$1TimePrinter za pomoc programu
ReflectionTest da nastpujcy wynik:
class TalkingClock$1TimePrinter
{
TalkingClock$1TimePrinter(TalkingClock, boolean);
public void actionPerformed(java.awt.event.ActionEvent);
final boolean val$beep;
final TalkingClock this$0;
}

Zwrmy uwag na parametr boolean w konstruktorze i zmienn obiektow val$beep. W trakcie tworzenia obiektu warto beep jest przekazywana do konstruktora i zapisywana w polu
o nazwie val$beep. Kompilator wykrywa obecno zmiennych lokalnych, tworzy odpowiadajce im pola obiektowe oraz kopiuje te zmienne do konstruktora, po czym wykorzystuje je
do inicjacji utworzonych pl.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

299

Z punktu widzenia programisty taki dostp do zmiennych lokalnych jest bardzo podanym
rozwizaniem. Umoliwia ono uproszczenie klas wewntrznych dziki redukcji liczby pl,
ktre trzeba zadeklarowa jawnie.
Wiemy ju, e metody klas lokalnych mog si odwoywa wycznie do finalnych zmiennych
lokalnych. Dlatego parametr beep w omawianym przykadzie zosta zadeklarowany jako final.
Zmienna lokalna ze sowem kluczowym final w deklaracji nie moe by modyfikowana po
tym, jak zostanie raz zainicjowana. Dziki temu mamy gwarancj, e zmienna lokalna i jej
kopia w klasie lokalnej maj zawsze t sam warto.
Wiemy ju, e zmienne finalne mog by uywane w charakterze staych:
public static final double SPEED_LIMIT = 90;

Sowo kluczowe final mona stosowa do zmiennych lokalnych, obiektowych i statycznych. Jego znaczenie jest zawsze takie samo: do danej zmiennej mona przypisa warto tylko jeden raz. Nie mona jej pniej zmieni.
Nie ma jednak koniecznoci inicjowania zmiennej finalnej po jej zdefiniowaniu. Na przykad finalna zmienna parametryczna beep jest inicjowana po wywoaniu metody start
(jeli metoda jest wywoywana kilka razy, kade wywoanie tworzy wasny parametr beep).
Zmienna obiektowa val$beep dostpna w klasie wewntrznej TalkingClock$1Time
Printer jest ustawiana jeden raz w konstruktorze klasy wewntrznej. Zmienna finalna,
ktra nie zostaa zainicjowana, nazywa si pust zmienn finaln (ang. blank final
variable).

Ograniczenia wprowadzane przez sowo final bywaj niewygodne. Wyobramy sobie, e


chcemy aktualizowa licznik w otaczajcym go zakresie. W poniszym kodzie chcemy
sprawdzi liczb wywoa metody compareTo w trakcie sortowania.
int counter = 0;
Date[] dates = new Date[100];
for (int i = 0; i < dates.length; i++)
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter++;
// bd
return super.compareTo(other);
}
};
Arrays.sort(dates);
System.out.println(counter + " porwna.");

Zmienna counter nie moe by zadeklarowana jako finalna, poniewa musi by aktualizowana. Nie mona jej zastpi typem Integer, poniewa obiekty tego typu s niezmienialne.
Rozwizanie polega na uyciu tablicy o rozmiarze 1:
final int[] counter = new int[1];
for (int i = 0; i < dates.length; i++)
dates[i] = new Date()
{
public int compareTo(Date other)
{
counter[0]++;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

300

Java. Podstawy
return super.compareTo(other);
}
};

Zmienna tablicowa jest zadeklarowana jako finalna, ale to jedynie oznacza, e nie moe si
odwoywa do innej tablicy. Elementy tablicy mona bez przeszkd zmienia.
Kiedy klasy wewntrzne zostay wynalezione, prototyp kompilatora automatycznie wykonywa transformacj wszystkich zmiennych lokalnych, ktre byy modyfikowane w klasie
wewntrznej. Jednak niektrzy programici nie byli zadowoleni z tego, e kompilator za
ich plecami tworzy obiekty na stercie. Dlatego zdecydowano si na wprowadzenie ograniczenia ze sowem kluczowym final. Niewykluczone, e w przyszych wersjach Javy decyzja ta zostanie zmieniona.

6.4.6. Anonimowe klasy wewntrzne


Uywajc lokalnych klas wewntrznych, mona pj o krok dalej. Jeli chcemy utworzy
tylko jeden obiekt jakiej klasy, to nie trzeba nawet nadawa jej nazwy. Taka klasa nazywa
si anonimow klas wewntrzn (ang. anonymous inner class).
public void start(int interval, final boolean beep)
{
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval, listener);
t.start();
}

Skadnia zastosowana powyej jest bez wtpienia bardzo enigmatyczna. Jej znaczenie jest
nastpujce: utworzenie nowego obiektu klasy implementujcej interfejs ActionListener,
ktrego wymagana metoda actionPerformed znajduje si w klamrach { }.
Oglna skadnia jest nastpujca:
new NadTyp(parametry konstrukcyjne)
{
Metody i dane klasy wewntrznej.
}

NadTyp w tym przypadku moe by interfejsem, np. ActionListener. Klasa wewntrzna implementuje wtedy ten interfejs. Jeli natomiast NadTyp jest klas, klasa wewntrzna j rozszerza.

Anonimowa klasa wewntrzna nie moe mie konstruktora, poniewa nazwa konstruktora
musi by taka sama jak nazwa klasy, a tak przecie nie jest. W zwizku z tym parametry

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

301

konstrukcyjne podaje si konstruktorowi nadklasy. Zwaszcza klasa wewntrzna nie moe


mie parametrw konstrukcyjnych, jeli implementuje interfejs. Niemniej konieczne jest
wpisanie nawiasw, jak poniej:
new TypInterfejsowy()
{
metody i dane
}

Aby zauway rnic pomidzy tworzeniem obiektu klasy a tworzeniem obiektu anonimowej klasy wewntrznej rozszerzajcej t klas, trzeba uwanie si przyjrze.
Person queen = new Person("Maria");
// Obiekt klasy Person.
Person count = new Person("Dracula") { . . .};
// Obiekt klasy wewntrznej rozszerzajcej klas Person.

Jeli po zamkniciu nawiasu zawierajcego parametry konstrukcyjne znajduje si klamra


otwierajca, oznacza to, e definiowana jest anonimowa klasa wewntrzna.
Czy anonimowe klasy wewntrzne s doskonaym pomysem, czy idealnym sposobem na
zaciemnienie kodu? Odpowied prawdopodobnie ley gdzie porodku. Jeli kod klasy
wewntrznej jest krtki i prosty, to moe pozwoli programicie zaoszczdzi nieco pisania.
Z drugiej strony wanie takie oszczdzajce czas cechy prowadz do zwycistwa w konkursie na najbardziej niejasny kod napisany w Javie.
Listing 6.7 przedstawia peny kod rdowy programu z zegarem, w ktrym uyto anonimowej
klasy wewntrznej. W porwnaniu z programem z listingu 6.6 ta wersja jest znacznie krtsza,
a przy odrobinie praktyki rwnie atwa do zrozumienia.
Listing 6.7. anonymousInnerClass/AnonymousInnerClassTest.java
package anonymousInnerClass;
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;
javax.swing.Timer;

/**
* Ten program demonstruje zastosowanie anonimowych klas wewntrznych.
* @version 1.10 2004-02-27
* @author Cay Horstmann
*/
public class AnonymousInnerClassTest
{
public static void main(String[] args)
{
TalkingClock clock = new TalkingClock();
clock.start(1000, true);
// Niech program dziaa, dopki uytkownik nie wcinie przycisku OK.
JOptionPane.showMessageDialog(null, "Zamkn program?");
System.exit(0);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

302

Java. Podstawy
}
}
/**
* Zegar drukujcy informacje o czasie w rwnych odstpach czasu.
*/
class TalkingClock
{
/**
* Tworzy obiekt TalkingClock.
* @param interval odstp czasu pomidzy kolejnymi komunikatami (w milisekundach)
* @param beep warto true oznacza, e dwik ma by odtwarzany
*/
public void start(int interval, final boolean beep)
{
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
Date now = new Date();
System.out.println("Kiedy usyszysz dwik, bdzie godzina " + now);
if (beep) Toolkit.getDefaultToolkit().beep();
}
};
Timer t = new Timer(interval, listener);
t.start();
}
}

Poniej przedstawiona jest sztuczka o nazwie inicjacja z podwjn klamr (ang. double brace initialization), w ktrej wykorzystuje si skadni klas wewntrznych.
Przypumy, e chcemy utworzy list tablicow i przekaza j do metody:
ArrayList<String> friends = new ArrayList<>();
favorites.add("Henryk");
favorites.add("Tomasz");
invite(friends);

Jeli lista nie bdzie ju wicej potrzebna, dobrze by byo zdefiniowa j jako anonimow. Jak wwczas jednak doda do niej elementy? Ano tak:
invite(new ArrayList<String>() {{ add("Henryk"); add("Tomasz"); }})

Zwr uwag na podwjne klamry. Zewntrzne su do utworzenia anonimowej podklasy klasy ArrayList, a wewntrzne to blok konstrukcyjny obiektu (patrz rozdzia 4.).

Czsto wygodnie jest utworzy anonimow podklas, ktra jest niemal identyczna
jak jej nadklasa. Trzeba jednak wwczas uwaa na metod equals. W rozdziale 5.
zalecamy, aby w metodzie tej uywa nastpujcego testu:
if (getClass() != other.getClass()) return false;

Anonimowa podklasa go nie przejdzie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

303

W danych dziennika albo debugera czsto zamieszcza si nazw biecej klasy, np.:
System.err.println("Co si stao w " + getClass());

Jednak w przypadku metody statycznej to si nie uda. Wywoanie getClass() jest tosame z this.getClass(), a statyczne metody nie maj this. Dlatego w zamian naley uy
poniszego wyraenia:
new Object(){}.getClass().getEnclosingClass() // pobiera klas metody statycznej

Instrukcja new Object() {} tworzy anonimowy obiekt anonimowej podklasy klasy Object,
a metoda getEnclosingClass pobiera jego klas, czyli klas zawierajc statyczn
metod.

6.4.7. Statyczne klasy wewntrzne


Od czasu do czasu chcemy ukry jedn klas wewntrz innej klasy, przy czym nie jest nam
potrzebna referencja w klasie wewntrznej do obiektw klasy zewntrznej. Aby wyczy
tworzenie tej referencji, klas wewntrzn naley zadeklarowa jako statyczn.
Przeanalizujemy typow sytuacj, w ktrej wspomniana funkcjonalno moe by potrzebna.
Mamy znale najmniejsz i najwiksz warto w tablicy. Oczywicie dla kadej z nich piszemy po jednej metodzie, w wyniku czego tablica jest przemierzana dwa razy. Kod byby bardziej efektywny, gdyby obie wartoci sprawdza rwnoczenie, przechodzc przez tablic
tylko jeden raz.
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}

Jednak metoda musi zwrci dwie liczby. Problem ten mona rozwiza, definiujc klas
o nazwie Pair przechowujc dwie wartoci:
class Pair
{
private double first;
private double second;
public Pair(double f, double s)
{
first = f;
second = s;
}
public double getFirst() { return first; }
public double getSecond() { return second; }
}

Dziki temu funkcja minmax moe zwrci obiekt typu Pair.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

304

Java. Podstawy
class ArrayAlg
{
public static Pair minmax(double[] values)
{
. . .
return new Pair(min, max);
}
}

Wywoujcy t funkcj sprawdza odpowiedzi za pomoc metod getFirst i getSecond:


Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());

Oczywicie sowo Pair jest bardzo czsto uywane, przez co w duym projekcie moe si
zdarzy, e jaki inny programista rwnie wpadnie na doskonay pomys utworzenia klasy
o nazwie Pair, tylko e przechowujcej na przykad dwa acuchy. Aby unikn potencjalnego konfliktu nazw, Pair mona uczyni publiczn klas wewntrzn w klasie ArrayAlg.
Wtedy klasa Pair na zewntrz bdzie znana pod nazw ArrayAlg.Pair:
ArrayAlg.Pair p = ArrayAlg.minmax(d);

W tym jednak przypadku, w przeciwiestwie do poprzednich sytuacji, nie chcemy, aby obiekt
Pair zawiera referencje do jakichkolwiek innych obiektw. Tworzenie referencji mona
wyczy, stosujc sowo kluczowe static w deklaracji klasy wewntrznej:
class ArrayAlg
{
public static class Pair
{
. . .
}
. . .
}

Oczywicie tylko klasy wewntrzne mog by statyczne. Statyczna klasa wewntrzna rni
si od zwykej klasy wewntrznej tylko tym, e obiekt takiej klasy nie zawiera referencji do
obiektu klasy zewntrznej, ktry go wygenerowa. W omawianym przykadzie uycie statycznej metody wewntrznej byo konieczne, poniewa obiekt tej klasy jest tworzony wewntrz
metody statycznej:
public static Pair minmax(double[] d)
{
. . .
return new Pair(min, max);
}

Gdyby klasa Pair nie bya statyczna, kompilator zgosiby bd polegajcy na braku niejawnego obiektu typu ArrayAlg do inicjacji obiektu klasy wewntrznej.
Wewntrznych klas statycznych naley uywa zawsze wtedy, kiedy klasa wewntrzna
nie wymaga dostpu do obiektu klasy zewntrznej. Niektrzy programici statyczne
klasy wewntrzne nazywaj klasami zagniedonymi.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

305

Klasy wewntrzne deklarowane w interfejsach s automatycznie statyczne i publiczne.

Listing 6.8 przedstawia kompletny kod rdowy klasy ArrayAlg i klasy zagniedonej Pair.
Listing 6.8. staticInnerClass/StaticInnerClassTest.java
package staticInnerClass;
/**
* Ten program demonstruje zastosowanie statycznych klas wewntrznych.
* @version 1.01 2004-02-27
* @author Cay Horstmann
*/
public class StaticInnerClassTest
{
public static void main(String[] args)
{
double[] d = new double[20];
for (int i = 0; i < d.length; i++)
d[i] = 100 * Math.random();
ArrayAlg.Pair p = ArrayAlg.minmax(d);
System.out.println("min = " + p.getFirst());
System.out.println("max = " + p.getSecond());
}
}
class ArrayAlg
{
/**
* Para liczb zmiennoprzecinkowych.
*/
public static class Pair
{
private double first;
private double second;
/**
* Tworzy par dwch liczb zmiennoprzecinkowych.
* @param f pierwsza liczba
* @param s druga liczba
*/
public Pair(double f, double s)
{
first = f;
second = s;
}
/**
* Zwraca pierwsz liczb z pary.
* @return pierwsza liczba
*/
public double getFirst()
{
return first;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

306

Java. Podstawy
/**
* Zwraca drug liczb z pary.
* @return druga liczba
*/
public double getSecond()
{
return second;
}
}
/**
* Znajduje najwiksz i najmniejsz warto w tablicy.
* @param values tablica liczb zmiennoprzecinkowych
* @return para liczb, w ktrej pierwsza liczba okrela warto najmniejsz, a druga
* najwiksz
*/
public static Pair minmax(double[] values)
{
double min = Double.MAX_VALUE;
double max = Double.MIN_VALUE;
for (double v : values)
{
if (min > v) min = v;
if (max < v) max = v;
}
return new Pair(min, max);
}
}

6.5. Klasy proxy


Ostatni cz tego rozdziau powicilimy klasom proxy, ktre zostay wprowadzone
w Java SE 1.3. Ich zastosowanie sprowadza si do tworzenia w trakcie dziaania programu
nowych klas implementujcych okrelone interfejsy. Klasy proxy s potrzebne tylko wtedy,
gdy w czasie kompilacji nie wiadomo jeszcze, ktre interfejsy dana klasa ma implementowa.
Sytuacje takie zdarzaj si rzadko, a wic osoby niezainteresowane zaawansowanymi technikami mog t cz ksiki pomin. Jednak w pewnego typu aplikacjach systemowych
swoboda oferowana przez klasy poredniczce moe si okaza niezwykle pomocna.
Wyobramy sobie, e chcemy utworzy obiekt klasy implementujcej jeden lub wicej interfejsw, ktrych dokadnej charakterystyki w czasie kompilacji jeszcze nie znamy. Jest to trudny
do rozwizania problem. Do utworzenia samej klasy mona uy metody newInstance lub
wykorzysta refleksj w celu znalezienia konstruktora. Nie mona jednak utworzy obiektu
interfejsu. Trzeba utworzy now klas w dziaajcym programie.
W niektrych programach rozwizanie tego problemu polega na wygenerowaniu kodu, zapisaniu go w pliku, wywoaniu kompilatora i zaadowaniu powstaego pliku klasy. Metoda ta jest
niestety powolna oraz wymaga wsppracy programu z kompilatorem. Lepszym rozwizaniem
jest technika klas proxy. Klasa proxy moe tworzy nowe klasy w czasie dziaania programu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

307

Implementuje okrelone przez programist interfejsy. Klasa proxy zawiera nastpujce


metody:

wszystkie metody wymagane przez okrelone interfejsy;

wszystkie metody zdefiniowane w klasie Object (toString, equals itd.).

Ale przecie nie mona w czasie dziaania programu napisa kodu dla nowych klas. W zamian
trzeba dostarczy obiekt obsugujcy wywoanie, czyli tzw. invocation handler. Jest to
obiekt dowolnej klasy, ktra implementuje interfejs InvocationHandler. Zawiera on tylko
jedn metod:
Object invoke(Object proxy, Method method, Object[] args)

Kiedy wywoywana jest jaka metoda na rzecz obiektu klasy proxy, nastpuje wywoanie
metody invoke obiektu obsugujcego wywoanie przy uyciu obiektu klasy Method i parametrw oryginalnego wywoania. Obiekt obsugujcy wywoanie musi si dowiedzie, jak
obsuy wywoanie.
Aby utworzy obiekt klasy proxy, naley uy metody newProxyInstance klasy Proxy.
Niniejsza metoda pobiera trzy parametry:

Mechanizm adowania klas (ang. class loader). Zgodnie z modelem zabezpiecze


Javy dozwolone jest uywanie rnych mechanizmw adowania klas systemowych,
klas pobranych z internetu itd. Wicej na temat tzw. loaderw klas piszemy
w rozdziale 9. w drugim tomie. Obecnie w miejsce tego parametru wstawiamy null,
aby uy domylnego loadera.

Tablica obiektw klasy Class po jednym dla kadego interfejsu, ktry ma by


zaimplementowany.

Obiekt obsugujcy wywoanie.

Dwa pytania pozostaj bez odpowiedzi. Jak zdefiniowa obiekt obsugujcy wywoanie?
Co mona zrobi z powstaym obiektem proxy? Odpowied na powysze pytania zaley
oczywicie od problemu, ktry chcemy rozwiza za pomoc mechanizmu klas proxy. Klasy
te mona wykorzysta do wielu celw, np.:

przesyanie wywoa metod do zdalnych serwerw;

czenie zdarze interfejsu uytkownika z akcjami w dziaajcym programie;

ledzenie wywoa metod w celu uatwienia lokalizacji bdw.

W przykadowym programie uyjemy klas proxy i obiektw obsugi wywoa do ledzenia


wywoa metod. Zdefiniujemy klas osonow TraceHandler przechowujc opakowany obiekt.
Jej metoda invoke drukuje nazw i parametry metody, ktra ma by wywoana, a nastpnie
wywouje t metod przy uyciu opakowanego obiektu jako parametru niejawnego.
class TraceHandler implements InvocationHandler
{
private Object target;
public TraceHandler(Object t)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

308

Java. Podstawy

target = t;

public Object invoke(Object proxy, Method m, Object[] args)


throws Throwable
{
// Wydruk nazwy i parametrw metody.
. . .
// Wywoanie metody.
return m.invoke(target, args);
}

Poniszy kod przedstawia sposb tworzenia obiektu proxy, ktry jest odpowiedzialny za
ledzenie metod:
Object value = . . .;
// Tworzenie osony.
InvocationHandler handler = new TraceHandler(value);
// Tworzenie obiektu proxy dla jednego lub wikszej liczby interfejsw.
Class[] interfaces = new Class[] { Comparable.class };
Object proxy = Proxy.newProxyInstance(null, interfaces, handler);

Dziki temu, jeli metoda ktrego z interfejsw zostanie wywoana na rzecz obiektu proxy,
zostanie wydrukowana jej nazwa i parametry oraz nastpi wywoanie tej metody na rzecz
obiektu value.
W programie na listingu 6.9 obiekty proxy s wykorzystywane do ledzenia wyszukiwania
binarnego. Zapeniamy tablic obiektami proxy liczb cakowitych od 1 do 1000. Nastpnie za
pomoc metody binarySearch klasy Arrays w tablicy tej wyszukujemy jedn losow liczb.
Na koniec wywietlamy pasujcy element.
Object[] elements = new Object[1000];
// Zapenienie tablicy obiektami proxy liczb cakowitych od 1 do 1000.
for (int i = 0; i < elements.length; i++)
{
Integer value = i + 1;
elements[i] = Proxy.newInstance(. . .);
// obiekt proxy wartoci
}
// Tworzenie losowej liczby cakowitej.
Integer key = new Random().nextInt(elements.length) + 1;
// Wyszukiwanie losowej liczby cakowitej.
int result = Arrays.binarySearch(elements, key);
// Wydruk pasujcej wartoci, jeli istnieje.
if (result >= 0) System.out.println(elements[result]);

Klasa Integer implementuje interfejs Comparable. Obiekty proxy nale do klasy, ktra jest
definiowana w czasie dziaania programu (ma nazw typu $Proxy0). Ta klasa rwnie implementuje interfejs Comparable, jednak jej metoda compareTo wywouje metod invoke handlera obiektu proxy.
Jak widzielimy wczeniej w tym rozdziale, klasa Integer implementuje interfejs
Comparable<Integer>. Jednak w czasie dziaania programu wszystkie typy parametryzowane s czyszczone i tworzony jest obiekt proxy przy uyciu obiektu class surowej klasy Comparable.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

309

Metoda binarySearch wykonuje wywoania podobne do poniszego:


if (elements[i].compareTo(key) < 0) . . .

Poniewa tablica zostaa zapeniona obiektami proxy, metoda compareTo wywouje metod
invoke klasy TraceHandler. Ta z kolei metoda drukuje nazw i parametry metody, a nastpnie
wywouje metod compareTo na rzecz opakowanego obiektu Integer.
Na kocu programu znajduje si nastpujcy wiersz kodu:
System.out.println(elements[result]);

Listing 6.9. proxy/ProxyTest.java


package proxy;
import java.lang.reflect.*;
import java.util.*;
/**
* Ten program demonstruje uycie klas proxy.
* @version 1.00 2000-04-13
* @author Cay Horstmann
*/
public class ProxyTest
{
public static void main(String[] args)
{
Object[] elements = new Object[1000];
// Wstawienie do tablicy obiektw proxy liczb cakowitych z przedziau 1 1000.
for (int i = 0; i < elements.length; i++)
{
Integer value = i + 1;
InvocationHandler handler = new TraceHandler(value);
Object proxy = Proxy.newProxyInstance(null, new Class[] { Comparable.class } ,
handler);
elements[i] = proxy;
}
// Tworzenie losowej liczby cakowitej.
Integer key = new Random().nextInt(elements.length) + 1;
// Szukanie liczby.
int result = Arrays.binarySearch(elements, key);

// Drukowanie dopasowanej wartoci, jeli zostanie znaleziona.


if (result >= 0) System.out.println(elements[result]);

/**
* Obiekt obsugujcy wywoanie, ktry drukuje nazw metody i parametry, a nastpnie
* wywouje oryginaln metod.
*/
class TraceHandler implements InvocationHandler
{
private Object target;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

310

Java. Podstawy
/**
* Tworzy obiekt TraceHandler.
* @param t parametr niejawny wywoania metody
*/
public TraceHandler(Object t)
{
target = t;
}
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable
{
// Drukowanie argumentu niejawnego.
System.out.print(target);
// Drukowanie nazwy metody.
System.out.print("." + m.getName() + "(");
// Drukowanie argumentw jawnych.
if (args != null)
{
for (int i = 0; i < args.length; i++)
{
System.out.print(args[i]);
if (i < args.length - 1) System.out.print(", ");
}
}
System.out.println(")");
// Wywoanie rzeczywistej metody.
return m.invoke(target, args);
}
}

Metoda println wywouje metod toString na rzecz obiektu proxy. Wywoanie to rwnie
jest przekierowywane do obiektu obsugujcego wywoanie.
Wynik dziaania programu jest nastpujcy:
500.compareTo(288)
250.compareTo(288)
375.compareTo(288)
312.compareTo(288)
281.compareTo(288)
296.compareTo(288)
288.compareTo(288)
288.toString()

Mona zaobserwowa, jak algorytm wyszukiwania binarnego namierza liczb, dzielc przeszukiwany obszar na dwie poowy na kadym etapie. Zauwamy, e metoda toString jest
w obiekcie proxy, mimo i nie naley do interfejsu Comparable w kolejnym podrozdziale
dowiemy si, e niektre metody klasy Object s zawsze w obiektach proxy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 6.

Interfejsy i klasy wewntrzne

311

6.5.1. Wasnoci klas proxy


Skoro wiemy ju, jak dziaaj klasy proxy, przeanalizujemy ich wasnoci. Nie zapominajmy,
e klasy te s tworzone w czasie dziaania programu, a po utworzeniu staj si zwykymi
klasami, tak jak wszystkie inne klasy dziaajce w maszynie wirtualnej.
Kada klasa proxy rozszerza klas Proxy. Klasa proxy ma tylko jedno pole obiektowe
obiekt obsugujcy wywoanie, ktre jest zdefiniowane w nadklasie Proxy. Wszystkie dodatkowe dane potrzebne do przeprowadzenia dziaa obiektu proxy musz by zapisane w obiekcie obsugi wywoania. Na przykad gdy obiekty Comparable uczynilimy obiektami proxy
w programie na listingu 6.9, obiekt TraceHandler opakowywa rzeczywiste obiekty.
Wszystkie klasy proxy przesaniaj metody toString, equals i hashCode klasy Object. Tak jak
wszystkie metody klas proxy, wywouj one tylko metod invoke obiektu obsugujcego wywoanie. Pozostae metody klasy Object (np. clone i getClass) nie s przedefiniowywane.
Nazwy klas proxy nie s zdefiniowane. Klasa Proxy w maszynie wirtualnej firmy Sun generuje nazwy zaczynajce si od przedrostka $Proxy.
Kady mechanizm adujcy klasy i uporzdkowany zestaw interfejsw ma tylko jedn klas
proxy. Jeli zatem wywoamy metod newProxyInstance dwa razy przy uyciu tego samego
mechanizmu adowania klas i tablicy interfejsw, otrzymamy dwa obiekty tej samej klasy.
Klas t mona take uzyska za pomoc metody getProxyClass:
Class proxyClass = Proxy.getProxyClass(null, interfaces);

Klasy proxy s zawsze publiczne i finalne. Jeli wszystkie interfejsy implementowane przez
klas proxy s publiczne, klasa taka nie naley do adnego pakietu. W przeciwnym przypadku wszystkie niepubliczne interfejsy musz nalee do tego samego pakietu. Wtedy klasa
proxy rwnie przynaley do tego pakietu.
Aby sprawdzi, czy okrelony obiekt Class reprezentuje klas proxy, naley wywoa metod
isProxyClass klasy Proxy.
java.lang.reflect.InvocationHandler 1.3

Object invoke(Object proxy, Method method, Object[] args)

W definicji tej metody naley umieci dziaania, ktre maj by wykonane


przy kadym wywoaniu metody na rzecz obiektu proxy.
java.lang.reflect.Proxy 1.3

static Class getProxyClass(ClassLoader loader, Class[] interfaces)

Zwraca klas proxy, ktra implementuje dane interfejsy.

static Object newProxyInstance(ClassLoader loader, Class[] interfaces,


InvocationHandler handler)

Tworzy nowy egzemplarz klasy proxy, ktra implementuje dane interfejsy.


Wszystkie metody wywouj metod invoke danego obiektu obsugi.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

312

Java. Podstawy

static boolean isProxyClass(Class c)

Zwraca warto true, jeli c jest klas proxy.


Na tym koczy si ostatni rozdzia dotyczcy podstaw jzyka Java. Z interfejsami i klasami
wewntrznymi bdziemy si spotyka bardzo czsto. Natomiast klasy proxy s zaawansowan
technik, ktra ley w sferze zainteresowa przede wszystkim twrcw narzdzi, a nie programistw aplikacji. W rozdziale 7. zaczynamy nauk programowania grafiki i interfejsw
uytkownika.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Grafika
W tym rozdziale:

Wprowadzenie do biblioteki Swing

Tworzenie ramek

Pozycjonowanie ramek

Wywietlanie informacji w komponencie

Figury 2D

Kolory

Kroje czcionek

Wywietlanie obrazw

Do tej pory tworzylimy tylko programy, ktre pobieray dane z klawiatury, przetwarzay je
i wywietlay wyniki w konsoli. Wikszo uytkownikw oczekuje jednak nieco wicej.
Ani nowoczesne programy, ani strony internetowe nie dziaaj w ten sposb. W tym rozdziale zaczynamy nauk pisania programw z graficznym interfejsem uytkownika (GUI).
Nauczymy si przede wszystkim ustawia rozmiar i pooenie okien na ekranie, wywietla
tekst pisany rnymi krojami czcionki, wywietla obrazy itd. Cay zdobyty tu warsztat
przyda nam si w kolejnych rozdziaach, w ktrych bdziemy tworzy ciekawe projekty.
Dwa nastpne rozdziay opisuj przetwarzanie zdarze, takie jak wcinicie klawisza na klawiaturze lub kliknicie przyciskiem myszy, oraz dodawanie do aplikacji takich elementw
interfejsu jak menu i przyciski. Po zapoznaniu si z tymi trzema rozdziaami bdziesz dysponowa wiedz, ktra jest niezbdna do pisania aplikacji graficznych. Bardziej zaawansowane techniki zwizane z programowaniem grafiki zostay opisane w drugim tomie.
Osoby zainteresowane wycznie programowaniem po stronie serwera, ktre nie maj
w planach pisania GUI, mog bezpiecznie pomin te rozdziay.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

314

Java. Podstawy

7.1. Wprowadzenie do pakietu Swing


W Java 1.0 udostpniono bibliotek klas o nazwie Abstract Window Toolkit (AWT), ktra
dostarczaa podstawowe narzdzia do programowania GUI. Podstawowa biblioteka AWT
przerzuca zadania dotyczce tworzenia elementw interfejsu oraz obsugi ich zachowa na
natywne narzdzia GUI danej platformy (Windows, Solaris, Mac OS X itd.). Jeli na przykad przy uyciu oryginalnej biblioteki AWT umieszczono w oknie pole tekstowe, dane
wprowadzane do niego byy w rzeczywistoci obsugiwane przez odpowiednik tego
pola w systemie. Dziki temu program napisany w Javie mg teoretycznie dziaa na dowolnej platformie, a jego styl by taki sam jak innych programw w danym systemie. Std wzi
si slogan firmy Sun: Napisz raz, uruchamiaj wszdzie.
Metoda programowania oparta na odpowiednikach sprawdzaa si w przypadku prostych
aplikacji. Natomiast szybko wyszo na jaw, e napisanie wysokiej jakoci przenonej biblioteki graficznej opartej na natywnych elementach interfejsu uytkownika jest niezwykle trudnym zadaniem. Elementy interfejsu, jak menu, paski przewijania i pola tekstowe, mog
si nieco rni na rnych platformach. Przez to trudno byo stworzy program dziaajcy
identycznie w rnych systemach. Ponadto niektre rodowiska graficzne (jak np. X11/Motif)
nie dysponuj tak szerok gam elementw interfejsu jak systemy Windows czy Mac OS X.
Ten fakt ogranicza zasobno przenonej biblioteki do tych elementw, ktre mona znale na wszystkich platformach. W wyniku tego aplikacje GUI budowane na bazie AWT
ustpoway wygldem i funkcjonalnoci natywnym aplikacjom takich systemw jak Windows czy Mac OS X. Na domiar zego na rnych platformach biblioteka AWT zawieraa rne
bdy. Programici narzekali, e musz testowa swoje aplikacje na wszystkich platformach,
co zoliwie nazywano napisz raz, testuj wszdzie.
W 1996 roku firma Netscape stworzya bibliotek GUI o nazwie IFC (ang. Internet Foundation
Classes), w ktrej zastosowano zupenie inne podejcie. Elementy interfejsu uytkownika,
jak przyciski, menu itd., byy rysowane w pustym oknie. Rola systemu polegaa tylko na
wywietlaniu okien i rysowaniu w nich. Dziki temu widgety firmy Netscape wyglday
i dziaay zawsze tak samo, bez wzgldu na platform. Firma Sun podja wspprac z
Netscape w celu udoskonalenia tej technologii, czego owocem bya biblioteka o nazwie kodowej Swing. Biblioteka ta zostaa udostpniona w postaci dodatku w Java 1.1, a do biblioteki
standardowej wcielono j w Java SE 1.2.
Zgodnie z myl Dukea Ellingtona, e Nic nie ma znaczenia, jeli jest pozbawione swingu,
Swing sta si oficjaln nazw zestawu narzdzi do tworzenia GUI, niewykorzystujcego
systemowych odpowiednikw elementw. Swing wchodzi w skad Java Foundation Classes
(JFC). Biblioteka JFC jest bardzo dua i zawiera wiele innych narzdzi poza Swingiem. Zaliczaj si do nich interfejsy API dostpnoci, grafiki dwuwymiarowej oraz obsugi operacji
przecigania i upuszczania.
Oczywicie elementy interfejsu oparte na Swingu pojawiaj si z pewnym opnieniem
w stosunku do komponentw uywanych przez AWT. Z naszego dowiadczenia wynika, e
rnica ta nie powinna stanowi problemu na adnym w miar niezbyt starym sprzcie. Z drugiej strony argumenty przemawiajce za uyciem Swinga s przytaczajce:

Swing udostpnia bogaty i wygodny w uyciu zestaw elementw interfejsu


uytkownika.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

315

Biblioteka Swing nie zastpia AWT, tylko zostaa zbudowana w oparciu o architektur swojej poprzedniczki. Komponenty Swing oferuj znacznie wiksze moliwoci.
Uywajc biblioteki Swing, zawsze korzysta si z podstawowej biblioteki AWT, zwaszcza
przy obsudze zdarze. Od tej pory piszc Swing, mamy na myli klasy rysujce interfejs
uytkownika, a piszc AWT, mylimy o podstawowych mechanizmach okien, takich jak
obsuga zdarze.

Swing w niewielkim stopniu zaley od platformy, dziki czemu jest mniej podatny
na bdy zwizane z danym systemem.

Sposb dziaania i wygld elementw Swinga jest taki sam na rnych platformach.

Niemniej trzecia z wymienionych zalet moe by uwaana za wad jeli elementy interfejsu uytkownika wygldaj tak samo na wszystkich platformach, to znaczy, e wygldaj
inaczej ni elementy natywne, co z kolei oznacza, e bd sabiej znane uytkownikom.
W pakiecie Swing problem ten zosta rozwizany w bardzo elegancki sposb. Programista
piszcy program przy uyciu klas Swing moe nada mu specyficzny charakter. Rysunki 7.1
i 7.2 przedstawiaj ten sam program w stylu systemu Windows i GTK.
Rysunek 7.1.
Styl systemu
Windows

Dodatkowo firma Sun opracowaa niezaleny od platformy styl o nazwie Metal, ktry pniej przez specjalistw od marketingu przechrzczono na Java look and feel. Jednak wikszo programistw nadal uywa okrelenia Metal i my rwnie trzymamy si tej konwencji
w tej ksice.
Ze wzgldu na krytyczne gosy kierowane pod adresem stylu Metal, ktry wedug niektrych wydawa si zbyt ciki, w Java SE 5.0 nieco go odwieono (zobacz rysunek 7.3).
Obecnie styl Metal obsuguje wiele motyww rnych wersji kolorystycznych i zestaww
czcionek. Motyw domylny nazywa si Ocean.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

316

Java. Podstawy

Rysunek 7.2.
Styl GTK

Rysunek 7.3.
Motyw Ocean
stylu Metal

W Java SE 6 poprawiono obsug natywnego stylu systemu Windows i GTK. Aktualnie aplikacje Swing obsuguj schematy kolorw i pulsujce przyciski oraz paski przewijania, ktre
stay si ostatnio modne.
W Java 7 dodano nowy styl o nazwie Nimbus (rysunek 7.4), ale nie jest on domylnie dostpny.
W stylu tym wykorzystywana jest grafika wektorowa zamiast bitmapowej, dziki czemu
interfejsy tworzone przy jego uyciu s niezalene od rozmiaru ekranu.
Niektrzy uytkownicy wol, aby aplikacje w Javie wyglday tak jak inne programy na danej
platformie, inni wol styl Metal, a jeszcze inni preferuj styl cakiem innego producenta. Jak
przekonamy si w rozdziale 8., umoliwienie uytkownikom wyboru dowolnego stylu jest
bardzo atwe.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

317

Rysunek 7.4.
Styl Nimbus

W Javie moliwe jest rozszerzenie istniejcego stylu, a nawet utworzenie cakiem


nowego. W tej ksice nie mamy wystarczajco duo miejsca, aby opisa ten
mudny proces polegajcy na okreleniu sposobu rysowania kadego komponentu. Jednak niektrzy programici pokusili si o to, zwaszcza ci, ktrzy przenosili programy w Javie
na nietypowe platformy, jak terminale sklepowe czy urzdzenia kieszonkowe. Zbir ciekawych stylw mona znale pod adresem http://www.javootoo.com/.
W Java SE 5.0 wprowadzono styl o nazwie Synth, ktry upraszcza cay ten proces.
W Synth styl mona definiowa poprzez dostarczenie obrazw i deskryptorw XML.
Nie trzeba nic programowa.

Styl Napkin (http://napkinlaf.sourceforge.net) nadaje elementom interfejsu uytkownika wygld przypominajcy odrczne rysowanie. Wysyajc do klienta utworzony
w nim prototyp, dajemy wyrany sygna, e nie jest to jeszcze ukoczony produkt.

Programowanie interfejsu uytkownika w Javie opiera si obecnie w wikszoci


na pakiecie Swing. Jest tylko jeden godny uwagi wyjtek. rodowisko zintegrowane
Eclipse uywa zestawu narzdzi graficznych o nazwie SWT, ktry podobnie jak AWT odwzorowuje natywne komponenty na rnych platformach. Artykuy na temat SWT mona znale na stronie http://www.eclipse.org/articles/.
Firma Oracle pracuje nad alternatywn technologi o nazwie JavaFX, ktra moe w przyszoci zastpi Swing. Jeli chcesz dowiedzie si o niej wicej, zajrzyj na stron
http://www.oracle.com/technetwork/java/javafx/overview.

Kady, kto pisa programy dla systemu Microsoft Windows w jzykach Visual Basic lub C#,
wie, jak duym uatwieniem s graficzne narzdzia do projektowania ukadu i edytory zasobw. Narzdzia te umoliwiaj zaprojektowanie caej wizualnej strony aplikacji oraz automatycznie generuj wikszo (czsto cao) kodu GUI. Dla Javy rwnie dostpne s narzdzia

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

318

Java. Podstawy
wspomagajce budowanie GUI, ale naszym zdaniem, aby si nimi sprawnie posugiwa,
trzeba najpierw nauczy si robi to samodzielnie. Pozostaa cz tego rozdziau zostaa
powicona opisowi technik wywietlania okien i rysowania w nich rnych elementw.

7.2. Tworzenie ramki


Okno najwyszego poziomu, tzn. takie, ktre nie jest zawarte w adnym innym oknie, nazywa
si w Javie ramk (ang. frame). W bibliotece AWT dla tego typu okien utworzono klas
o nazwie Frame. Wersja Swing tej klasy nosi nazw JFrame i j rozszerza. Ramka JFrame jest
jednym z niewielu komponentw Swinga, ktre nie s rysowane w obszarze roboczym.
W zwizku z tym elementy dodatkowe (przyciski, pasek tytuu, ikony itd.) s rysowane przez
system operacyjny, a nie klasy Swing.
Nazwy wikszoci klas komponentw Swing zaczynaj si od litery J, np. JButton,
JFrame itd. Istniej take klasy Button i Frame, ale s to komponenty AWT. Jeli
litera J zostanie przypadkowo pominita, program moe przej kompilacj bez problemu,
ale mieszanina komponentw AWT i Swing moe spowodowa nietypowe zachowania lub
wygld aplikacji.

W tym podrozdziale przejrzymy najpopularniejsze metody pracy z komponentem Swing


o nazwie JFrame. Listing 7.1 przedstawia prosty program wywietlajcy na ekranie pust
ramk widoczn na rysunku 7.5.
Listing 7.1. simpleFrame/SimpleFrameTest.java
package simpleFrame;
import java.awt.*;
import javax.swing.*;
/**
* @version 1.32 2007-06-12
* @author Cay Horstmann
*/
public class SimpleFrameTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
SimpleFrame frame = new SimpleFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

319

Rysunek 7.5.
Najprostsza
widoczna ramka

class SimpleFrame extends JFrame


{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;

public SimpleFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
}

Przeanalizujemy powyszy program wiersz po wierszu.


Klasy Swing znajduj si w pakiecie javax.swing. Nazwa pakietu javax oznacza, e jest to
pakiet rozszerzajcy Javy, a nie podstawowy. Swing jest uznawany za rozszerzenie ze wzgldw historycznych. Jest jednak dostpny w kadej wersji Java SE od 1.2.
Domylny rozmiar ramki 00 jest raczej mao atrakcyjny. Zdefiniowalimy podklas o nazwie
SimpleFrame, ktrej konstruktor ustawia rozmiar na 300200 pikseli. Jest to jedyna rnica
pomidzy klasami SimpleFrame i JFrame.
W metodzie main klasy SimpleFrameTest tworzony jest obiekt klasy SimpleFrame, ktry nastpnie zosta uwidoczniony.
W kadym programie opartym na Swingu trzeba poradzi sobie z dwoma zagadnieniami
technicznymi.
Po pierwsze, konfiguracja kadego komponentu Swing musi si odbywa w wtku dystrybucji zdarze (ang. event dispatch thread), ktry steruje wysyaniem zdarze, jak kliknicia
przyciskiem myszy lub wcinicie klawisza na klawiaturze, do elementw interfejsu uytkownika. Poniszy fragment programu wykonuje instrukcje w wtku dystrybucji zdarze:
EventQueue.invokeLater(new Runnable()
{
public void run()
{
instrukcje
}
});

Szczegowy opis tego zagadnienia znajduje si w rozdziale 14. Na razie potraktujmy to


jako magiczny fragment kodu potrzebny do uruchomienia programu Swing.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

320

Java. Podstawy

Wiele programw nie inicjuje interfejsu uytkownika w wtku dystrybucji zdarze.


Nie ma adnych przeszkd, aby operacj t przeprowadza w wtku gwnym.
Niestety ze wzgldu na to, e komponenty Swing staway si coraz bardziej zoone,
programici w firmie Sun nie mogli zagwarantowa bezpieczestwa tej metody. Prawdopodobiestwo wystpienia bdu jest niezwykle niskie, ale nikt nie chciaby by tym
pechowcem, ktremu si to przydarzy. Lepiej pisa waciwy kod, nawet jeli wyglda
nieco tajemniczo.

Po drugie, okrelamy, co ma si sta, kiedy uytkownik zamknie ramk aplikacji. W tym przypadku chcemy, aby program zosta zamknity. Odpowiedzialna jest za to ponisza instrukcja:
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

W programach skadajcych si z wielu ramek program nie powinien koczy dziaania


w wyniku zamknicia jednej z nich. Przy standardowych ustawieniach, jeli uytkownik
zamknie ramk, zostanie ona ukryta, a program nie zakoczy dziaania (dobrze by byo,
gdyby program by wyczany w chwili zamknicia jego ostatniej ramki, ale Swing dziaa
inaczej).
Samo utworzenie ramki nie oznacza, e zostanie ona wywietlona. Ramki na pocztku swojego
istnienia s niewidoczne. Dziki temu programista moe doda do nich wszystkie komponenty, zanim uka si po raz pierwszy. Aby wywietli ramk, metoda main wywouje na jej
rzecz metod setVisible.
Przed Java SE 5.0 mona byo uywa metody show dziedziczonej przez klas JFrame
po nadklasie Window. Nadklas klasy Window jest Component, ktra take zawiera
metod show. Stosowanie metody Component.show zaczto odradza w Java SE 1.2.
W zamian, aby wywietli komponent, naley uy metody setVisible(true). Natomiast
metoda Window.show nie bya odradzana a do Java SE 1.4. Moliwo uwidocznienia
okna i przeniesienia go na przd bya nawet przydatna. Niestety metoda show dla okien
rwnie jest odradzana od Java SE 5.0.

Po rozplanowaniu instrukcji inicjujcych metoda main koczy dziaanie. Zauwamy, e zakoczenie metody main nie oznacza zamknicia programu, a jedynie gwnego wtku. Wtek
dystrybucji zdarze podtrzymuje dziaanie programu a do jego zakoczenia poprzez zamknicie ramki lub wywoanie metody System.exit.
Uruchomiony program przedstawia rysunek 7.5 jest to zwyke szare okno najwyszego
poziomu. Jak wida, pasek tytuu i pozostae dodatki, jak zaokrglone rogi suce do zmiany
rozmiaru okna, zostay narysowane przez system operacyjny, a nie klasy Swing. Elementy te
bd wyglday inaczej, jeli uruchomimy ten program w systemach Windows, GTK czy
Mac OS X. Biblioteka Swing rysuje wszystko, co znajduje si wewntrz ramki. W tym przypadku jej zadanie sprowadza si jedynie do wypenienia domylnym kolorem ta.
Od Java SE 1.4 istnieje moliwo wyczenia wszelkich dodatkw za pomoc
wywoania metody frame.setUndecorated(true).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

321

7.3. Pozycjonowanie ramki


Klasa JFrame udostpnia tylko kilka metod zmieniajcych wygld ramek. Oczywicie wikszo metod sucych do zmiany wymiarw i pooenia ramki jest w klasie JFrame dziedziczona po rnych nadklasach. Poniej znajduje si lista najwaniejszych z tych metod:

setLocation i setBounds ustawiaj pooenie ramki.

setIconImage okrela ikon wywietlan w pasku tytuu, w zasobniku

systemowym itd.

setTitle ustawia tekst w pasku tytuu.

setResizable pobiera warto logiczn okrelajc, czy uytkownik moe


zmienia rozmiar ramki.

Rysunek 7.6 przedstawia hierarchi dziedziczenia klasy JFrame.


Rysunek 7.6.
Hierarchia
dziedziczenia
klas ramek
i komponentw
w pakietach
AWT i Swing

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

322

Java. Podstawy

W wycigach z API do tego podrozdziau przedstawiamy te metody, ktre naszym


zdaniem maj najwiksze znaczenie przy nadawaniu ramkom odpowiedniego stylu.
Niektre z nich s udostpnione w klasie JFrame. Inne z kolei pochodz od rnych nadklas klasy JFrame. Czasami konieczne moe by przeszukanie dokumentacji API w celu
znalezienia metod przeznaczonych do okrelonego celu. Niestety jest to do mudna
praca, jeli chodzi o metody dziedziczone. Na przykad metoda toFront ma zastosowanie
do obiektw typu JFrame, ale poniewa jest dziedziczona po klasie Window, w dokumentacji JFrame nie ma jej opisu. Jeli uwaasz, e powinna istnie metoda wykonujca okrelone dziaanie, ale nie ma jej w dokumentacji danej klasy, przeszukaj dokumentacj
metod nadklas tej klasy. Na grze kadej strony w dokumentacji API znajduj si odnoniki do nadklas, a lista metod dziedziczonych znajduje si pod zestawieniem nowych
i przesonitych metod.

Wedug dokumentacji API metody suce do zmieniania rozmiaru i ksztatu ramek znajduj
si w klasach Component (bdcej przodkiem wszystkich obiektw GUI) i Window (bdcej
nadklas klasy Frame). Na przykad do zmiany pooenia komponentu mona uy metody
setLocation z klasy Component. Wywoanie:
setLocation(x, y)

umieci lewy grny rg komponentu w odlegoci x pikseli od lewej krawdzi ekranu i y


pikseli od gry. Wartoci (0, 0) oznaczaj lewy grny rg ekranu. Podobnie metoda setBounds
w klasie Component umoliwia zmian rozmiaru i pooenia komponentu (zwaszcza ramki
JFrame) w jednym wywoaniu:
setBounds(x, y, width, height)

Istnieje te moliwo pozostawienia decyzji o pooeniu okna systemowi. Wywoanie:


setLocationByPlatform(true);

przed wywietleniem okna spowoduje, e system we wasnym zakresie okreli jego pooenie (ale nie rozmiar). Zazwyczaj nowe okno jest wywietlane z nieznacznym przesuniciem wzgldem poprzedniego.
W przypadku ramki wsprzdne metod setLocation i setBounds s wzgldne do
caego ekranu. W rozdziale 9. dowiemy si, e wsprzdne komponentw znajdujcych si w kontenerze odnosz si do tego kontenera.

7.3.1. Wasnoci ramek


Wiele metod klas komponentw wystpuje w parach get-set, jak ponisze metody klasy Frame:
public String getTitle()
public void setTitle(String title)

Taka para metod typu get-set nazywa si wasnoci (ang. property). Wasno ma nazw
i typ. Nazwa jest taka sama jak sowo uzyskane w wyniku opuszczenia czonu get i zamienienia pierwszej litery powstaego sowa na ma. Na przykad klasa Frame ma wasno o nazwie
title i typie String.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

323

Z zaoenia title jest wasnoci ramki. Ustawienie (set) niniejszej wasnoci powoduje
zmian tytuu na ekranie uytkownika. Pobranie jej (get) zwraca warto, ktra zostaa wczeniej ustawiona.
Nie wiemy (i nie obchodzi nas to), jak klasa Frame implementuje t wasno. Prawdopodobnie wykorzystuje swj odpowiednik ramki do przechowywania tytuu. Moliwe, e ma
nastpujce pole:
private String title;

// nie jest wymagane dla wasnoci

Jeli klasa zawiera pasujce pole, nie wiemy (lub nie obchodzi nas to), jak metody dostpowe i ustawiajce s zaimplementowane. Prawdopodobnie po prostu odczytuj i ustawiaj
dane pole. Moliwe, e robi jeszcze co, np. powiadamiaj system o kadej zmianie tytuu.
Jest tylko jeden wyjtek od konwencji get-set: metody wasnoci typu logicznego zaczynaj si od przedrostka is. Na przykad dwie przedstawione poniej metody definiuj wasno
locationByPlatform:
public boolean isLocationByPlatform()
public void setLocationByPlatform(boolean b)

Znacznie wicej na temat wasnoci piszemy w rozdziale 8. drugiego tomu.


Wiele jzykw programowania, zwaszcza Visual Basic i C#, standardowo obsuguje
wasnoci. Niewykluczone, e w przyszoci w Javie rwnie pojawi si podobna konstrukcja jzykowa.

7.3.2. Okrelanie rozmiaru ramki


Pamitajmy, e jeli nie ustawimy wasnego rozmiaru ramek, wszystkie bd miay wymiary
00 pikseli. Aby nie komplikowa naszych przykadowych programw, ustawiamy ramki na
rozmiar do przyjcia na wikszoci ekranw. Jednak w profesjonalnej aplikacji naley sprawdzi rozdzielczo ekranu uytkownika i napisa podprogram zmieniajcy rozmiar ramek
w zalenoci od potrzeby. Na przykad okno, ktre wyglda znakomicie na ekranie laptopa,
na ekranie o wysokiej rozdzielczoci skurczy si do rozmiarw znaczka pocztowego.
Aby sprawdzi rozmiar ekranu, naley wykona nastpujce dziaania: wywoaj statyczn
metod getDefaultToolkit klasy Toolkit w celu utworzenia obiektu typu Toolkit (klasa
Toolkit jest zbiornikiem rozmaitych metod, ktre wsppracuj z systemem). Nastpnie
wywoaj metod getScreenSize, ktra zwraca rozmiar ekranu w postaci obiektu Dimension.
Obiekt tego typu przechowuje wysoko i szeroko w zmiennych publicznych (!) o nazwach
width i height. Poniej przedstawiamy opisywany fragment programu:
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenWidth = screenSize.width;
int screenHeight = screenSize.height;

Rozmiar ramki ustawiamy na poow ekranu i pozwalamy systemowi na ustalenie pooenia ramki:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

324

Java. Podstawy
setSize(screenWidth / 2, screenHeight / 2);
setLocationByPlatform(true);

Dodatkowo dostarczymy ikon. Do wygodnego adowania obrazw mona wykorzysta klas


ImageIcon. Poniej przedstawiony jest sposb jej uycia:
Image img = new ImageIcon("icon.gif").getImage();
setIconImage(img);

Ikona moe si pojawi w rnych miejscach, w zalenoci od systemu operacyjnego.


W systemie Windows pojawi si na przykad w lewym grnym rogu okna oraz bdzie
widoczna wrd aktywnych zada pojawiajcych si w wyniku nacinicia kombinacji
klawiszy Alt+Tab.
Listing 7.2 przedstawia peny kod omawianego programu. Po jego uruchomieniu zwr
uwag na ikon Core Java.
Listing 7.2. sizedFrame/SizedFrameTest.java
package sizedFrame;
import java.awt.*;
import javax.swing.*;
/**
* @version 1.32 2007-04-14
* @author Cay Horstmann
*/
public class SizedFrameTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new SizedFrame();
frame.setTitle("SizedFrame");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true););
}
});
}
}
class SizedFrame extends JFrame
{
public SizedFrame()
{
// Sprawdzenie wymiarw ekranu.
Toolkit kit = Toolkit.getDefaultToolkit();
Dimension screenSize = kit.getScreenSize();
int screenHeight = screenSize.height;
int screenWidth = screenSize.width;
// Ustawienie szerokoci i wysokoci ramki oraz polecenie systemowi, aby ustali jej pooenie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

325

setSize(screenWidth / 2, screenHeight / 2);


setLocationByPlatform(true);
// Ustawienie ikony i tytuu.

Image img = new ImageIcon("icon.gif").getImage();


setIconImage(img);

Jeszcze kilka dodatkowych wskazwek dotyczcych obsugi ramek:

Jeli ramka zawiera tylko standardowe komponenty, jak przyciski i pola tekstowe,
jej rozmiar mona ustawi za pomoc metody pack. Rozmiar zostanie ustawiony
na najmniejszy, w ktrym zmieszcz si wszystkie komponenty. Czsto rozmiar
gwnej ramki jest ustawiany na najwiksz warto. Od Java SE 1.4 ramk mona
zmaksymalizowa za pomoc nastpujcego wywoania:
frame.setExtendedState(Frame.MAXIMIZED_BOTH);

Dobrym pomysem jest zapisanie pooenia i rozmiaru ramki ustawionych przez


uytkownika i zastosowanie tych ustawie przy ponownym uruchomieniu aplikacji.
Jak to zrobi, dowiemy si w rozdziale 10., przy okazji omawiania API preferencji.

Jeli aplikacja wykorzystuje kilka ekranw, ich rozmiary mona sprawdzi


za pomoc metod GraphicsEnvironment i GraphicsDevice.

Ponadto klasa GraphicsDevice umoliwia uruchomienie programu w trybie


penoekranowym.

java.awt.Component 1.0

boolean isVisible()

void setVisible(boolean b)

Sprawdza lub ustawia wasno widocznoci. Komponenty s pocztkowo widoczne


z wyjtkiem komponentw najwyszego poziomu, jak JFrame.

void setSize(int width, int height) 1.1

Ustawia szeroko i wysoko komponentu.

void setLocation(int x, int y) 1.1

Przenosi komponent w inne miejsce. Wsprzdne x i y s wzgldne do kontenera


lub ekranu, jeli komponent jest komponentem najwyszego poziomu (np. JFrame).

void setBounds(int x, int y, int width, int height) 1.1

Przesuwa komponent i zmienia jego rozmiar.

Dimension getSize() 1.1

void setSize(Dimension d) 1.1

Pobiera lub ustawia wasno size komponentu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

326

Java. Podstawy
java.awt.Window 1.0

void toFront()

Przenosi okno przed wszystkie pozostae okna.

void toBack()

Przenosi okno na sam d stosu okien i odpowiednio przestawia pozostae


widoczne okna.

boolean isLocationByPlatform() 5.0

void setLocationByPlatform(boolean b) 5.0

Pobiera lub ustawia wasno locationByPlatform. Jeli zostanie ona ustawiona


przed wywietleniem okna, platforma wybierze odpowiedni lokalizacj.
java.awt.Frame 1.0

boolean isResizable()

void setResizable(boolean b)

Pobiera lub ustawia wasno resizable. Jeli jest ona ustawiona, uytkownik
moe zmienia rozmiar ramki.

String getTitle()

void setTitle(String s)

Pobiera lub ustawia wasno title okrelajc tekst na pasku tytuu.

Image getIconImage()

void setIconImage(Image image)

Pobiera lub ustawia wasno iconImage, ktra okrela ikon ramki. System moe
wywietli ikon jako dodatek w ramce lub w innym miejscu.

boolean isUndecorated() 1.4

void setUndecorated(boolean b) 1.4

Pobiera lub ustawia wasno undecorated. Jeli ta wasno jest ustawiona, ramka
nie zawiera adnych dodatkw, jak pasek tytuu czy przycisk zamykajcy. Ta metoda
musi by wywoana przed wywietleniem ramki.

int getExtendedState() 1.4

void setExtendedState(int state) 1.4

Pobiera lub ustawia stan rozszerzonego okna. Moliwe stany to:


Frame.NORMAL
Frame.ICONIFIED
Frame.MAXIMIZED_HORIZ
Frame.MAXIMIZED_VERT
Frame.MAXIMIZED_BOTH

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

327

java.awt.Toolkit 1.0

static Toolkit getDefaultToolkit()

Zwraca standardowy zestaw narzdzi.

Dimension getScreenSize()

Pobiera rozmiar ekranu.


javax.swing.ImageIcon 1.2

ImageIcon(String filename)

Tworzy ikon, ktrej obraz jest przechowywany w pliku.

Image getImage()

Pobiera obraz ikony.

7.4. Wywietlanie informacji w komponencie


W tym podrozdziale nauczymy si wywietla informacje w ramce. Zamiast przykadowego
programu wywietlajcego tekst w konsoli, ktry widzielimy w rozdziale 3., zaprezentujemy program wywietlajcy tekst w ramce, jak na rysunku 7.7.
Rysunek 7.7.
Ramka
wywietlajca
tekst

acuch tekstowy mona wydrukowa bezporednio na ramce, ale jest to uznawane za zy


zwyczaj programistyczny. Ramki w Javie s z zaoenia kontenerami do przechowywania
komponentw, takich jak pasek menu i inne elementy interfejsu uytkownika. Z reguy rysowanie odbywa si na innym dodanym do ramki komponencie.
Struktura ramki JFrame jest zadziwiajco skomplikowana. Rysunek 7.8 przedstawia schemat
budowy takiej ramki. Jak wida, ramka JFrame skada si z czterech warstw. Trzy z nich
podstawowa (ang. root pane), warstwowa (ang. layered pane) i przezroczysta (ang. glass
pane) nie s dla nas interesujce. Ich przeznaczeniem jest organizacja paska menu i warstwy z treci oraz implementacja stylu. Cz interesujca programistw Swing to warstwa
treci (ang. content pane). Podczas projektowania ramki komponenty dodaje si do warstwy
treci, stosujc kod podobny do poniszego:
Container contentPane = frame.getContentPane();
Component c = . . .;
contentPane.add(c);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

328

Java. Podstawy
Do Java SE 1.4 metoda add klasy JFrame powodowaa wyjtek wywietlajcy komunikat
Do not use JFrame.add(). Use JFrame.getContentPane().add() instead. W Java SE 5.0
zrezygnowano z takiej edukacji programistw, czego wyrazem jest zezwolenie na wywoywanie metody JFrame.add na rzecz warstwy z treci.
Dziki temu od Java SE 5.0 mona stosowa krtki zapis:
frame.add(c);

Tym razem chcemy doda do ramki jeden komponent, na ktrym narysujemy nasz komunikat.
Aby mc rysowa na komponencie, naley zdefiniowa klas rozszerzajc klas JComponent
i przesoni w niej metod paintComponent klasy nadrzdnej.
Metoda paintComponent przyjmuje jeden parametr typu Graphics. Obiekt typu Graphics zawiera
ustawienia dotyczce rysowania obrazw i tekstu, jak czcionka czy aktualny kolor. Rysowanie w Javie zawsze odbywa si za porednictwem obiektu Graphics. Udostpnia on metody
rysujce wzory, obrazy i tekst.
Rysunek 7.8.
Wewntrzna
struktura
ramki JFrame

Parametr Graphics jest podobny do kontekstu urzdze (ang. device context)


w systemie Windows i kontekstu graficznego (ang. graphics context) w programowaniu dla systemu X11.

Poniszy fragment programu tworzy komponent, na ktrym mona rysowa:


class MyComponent extends JComponent
{
public void paintComponent(Graphics g)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

329

{
kod rysujcy
}
}

Za kadym razem, kiedy okno musi by ponownie narysowane, metoda obsugi zdarze
informuje o tym komponent. Powoduje to uruchomienie metod paintComponent wszystkich
komponentw.
Nigdy nie naley wywoywa metody paintComponent samodzielnie. Jest ona wywoywana
automatycznie, gdy trzeba ponownie narysowa jak cz aplikacji, i nie naley zaburza
tego automatycznego procesu.
Jakiego rodzaju czynnoci uruchamiaj t automatyczn reakcj? Rysowanie jest konieczne,
na przykad kiedy uytkownik zwikszy rozmiar okna albo je zminimalizuje, a nastpnie
zmaksymalizuje. Jeli zostanie otwarte nowe okno, ktre czciowo przykryje stare, to po
zamkniciu tego nowego okna przykryta cz starego zostanie zniszczona i trzeba j bdzie
ponownie narysowa (system graficzny nie zapisuje pikseli, ktre znajduj si pod spodem).
Oczywicie przy pierwszym uruchomieniu okna musi ono przetworzy kod okrelajcy sposb i miejsce rysowania pocztkowych elementw.
Aby wymusi ponowne rysowanie ekranu, naley uy metody repaint. Wywoa
ona metody paintComponent wszystkich komponentw, ktre maj prawidowo
skonfigurowany obiekt Graphics.

Z przedstawionego powyej fragmentu kodu wnioskujemy, e metoda paintComponent przyjmuje tylko jeden parametr typu Graphics. Jednostk miary obiektw Graphics wywietlanych na ekranie s piksele. Para wsprzdnych (0, 0) okrela lewy grny rg komponentu,
na ktrego powierzchni odbywa si rysowanie.
Wywietlanie tekstu jest specjalnym rodzajem rysowania. Klasa Graphics udostpnia metod
drawString o nastpujcej skadni:
g.drawString(text, x, y)

Chcemy narysowa acuch: To nie jest program Witaj, wiecie w oryginalnym oknie
w odlegoci okoo jednej czwartej szerokoci od lewej krawdzi i poowy wysokoci od krawdzi grnej. Mimo e nie potrafimy jeszcze mierzy dugoci acuchw, zaczniemy rysowanie w punkcie o wsprzdnych (75, 100). Oznacza to, e pierwsza litera acucha jest
przesunita w prawo o 75 pikseli i w d o 100 pikseli (w rzeczywistoci o 100 pikseli w d
przesunita jest podstawowa linia pisma wicej na ten temat w dalszej czci rozdziau).
Kod opisanej metody paintComponent znajduje si poniej:
class NotHelloWorldComponent extends JComponent
{
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
public void paintComponent(Graphics g)
{
g.drawString("To nie jest program Witaj, wiecie", MESSAGE_X, MESSAGE_Y);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

330

Java. Podstawy

}
. . .

W kocu komponent powinien informowa uytkownika o tym, jaki ma by jego rozmiar.


Przesonimy metod getPreferredSize, aby zwracaa obiekt klasy Dimension z preferowan
szerokoci i wysokoci:
class NotHelloWorldComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
. . .
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH,
DEFAULT_HEIGHT); }
}

Gdy umiecimy w ramce jakie komponenty i bdziemy chcieli uy ich preferowanych rozmiarw, to zamiast setSize wywoamy metod pack:
class NotHelloWorldFrame extends JFrame
{
public NotHelloWorldFrame()
{
add(new NotHelloWorldComponent());
pack();
}
}

Listing 7.3 zawiera peny kod programu.


Niektrzy programici wol rozszerza klas JPanel zamiast JComponent. Obiekt
JPanel jest z zaoenia kontenerem na inne komponenty, ale mona take na nim
rysowa. Jest jednak midzy nimi jedna rnica panel jest nieprzezroczysty, czyli
rysuje wszystkie piksele w swoim obrbie. Najprostszym sposobem na zrobienie tego jest
narysowanie panelu z kolorowym tem za pomoc wywoania metody super.paintCompo
nent w metodzie paintComponent kadej podklasy panelu:
class NotHelloWorldPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
}

procedury rysujce

Listing 7.3. notHelloWorld/NotHelloWorld.java


package notHelloWorld;
import javax.swing.*;
import java.awt.*;
/**
* @version 1.32 2007-06-12

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

* @author Cay Horstmann


*/
public class NotHelloWorld
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new NotHelloWorldFrame();
frame.setTitle("NotHelloWorld");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca panel z komunikatem.
*/
class NotHelloWorldFrame extends JFrame
{
public NotHelloWorldFrame()
{
add(new NotHelloWorldComponent());
pack();
}
}
/**
* Panel wywietlajcy komunikat.
*/
class NotHelloWorldPanel extends JComponent
{
public static final int MESSAGE_X = 75;
public static final int MESSAGE_Y = 100;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public void paintComponent(Graphics g)
{
g.drawString("To nie jest program Witaj, wiecie.", MESSAGE_X, MESSAGE_Y);
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH,
DEFAULT_HEIGHT); }
}
javax.swing.JFrame 1.2

Container getContentPane()

Zwraca obiekt ContentPane dla ramki JFrame.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

331

332

Java. Podstawy

Component add(Component c)

Dodaje i zwraca dany komponent do warstwy treci ramki (przed Java SE 5.0
ta metoda powodowaa wyjtek).
java.awt.Component 1.0

void repaint()

Powoduje ponowne jak najszybsze narysowanie komponentu.

Dimension getPreferredSize()

Metoda, ktr naley przesoni, aby zwracaa preferowany rozmiar komponentu.


javax.swing.JComponent 1.2

void paintComponent(Graphics g)

Metoda, ktr naley przesoni w celu zdefiniowania sposobu rysowania


okrelonego komponentu.
java.awt.Window 1.0

void pack()

Zmienia rozmiar okna, biorc pod uwag preferowane rozmiary znajdujcych


si w nim komponentw.

7.5. Figury 2D
Od Java 1.0 klasa Graphics udostpnia metody rysujce linie, prostokty, elipsy itd. Ich moliwoci s jednak bardzo ograniczone. Nie ma na przykad moliwoci ustawienia gruboci
linii ani obracania figur.
W Java 1.2 wprowadzono bibliotek Java2D udostpniajc szeroki wachlarz metod graficznych. W tym rozdziale opisujemy tylko podstawy tej biblioteki wicej bardziej zaawansowanych informacji na ten temat znajduje si w rozdziale 7. w drugim tomie.
Aby narysowa figur biblioteki Java2D, trzeba utworzy obiekt klasy Graphics2D. Klasa ta
jest podklas klasy Graphics. Od Java SE 2 metody takie jak paintComponent automatycznie
odbieraj obiekty klasy Graphics2D. Wystarczy zastosowa rzutowanie:
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
. . .
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

333

Figury geometryczne w bibliotece Java2D s obiektami. Istniej klasy reprezentujce linie,


prostokty i elipsy:
Line2D
Rectangle2D
Ellipse2D

Wszystkie te klasy implementuj interfejs Shape.


Biblioteka Java2D obsuguje take bardziej skomplikowane figury, jak uki, krzywe
drugiego i trzeciego stopnia oraz trajektorie. Wicej informacji na ten temat znajduje
si w rozdziale 7. drugiego tomu.

Aby narysowa figur, naley najpierw utworzy obiekt klasy implementujcej interfejs Shape,
a nastpnie wywoa metod draw klasy Graphics2D. Na przykad:
Rectangle2D rect = . . .;
g2.draw(rect);

Przed pojawieniem si biblioteki Java2D do rysowania figur uywano metod klasy


Graphics, np. drawRectangle. Na pierwszy rzut oka te stare wywoania wydaj si
prostsze, ale uywajc biblioteki Java2D, pozostawiamy sobie rne moliwoci do wyboru mona pniej ulepszy rysunki za pomoc rozmaitych narzdzi dostpnych
w bibliotece Java2D.

Biblioteka Java2D nieco komplikuje programowanie. W przeciwiestwie do metod rysujcych


z wersji 1.0, w ktrych wsprzdne byy liczbami cakowitymi, figury Java2D uywaj
wsprzdnych zmiennoprzecinkowych. W wielu przypadkach stanowi to due uatwienie
dla programisty, poniewa moe on okrela figury przy uyciu lepiej znanych mu jednostek (jak milimetry lub cale), ktre pniej s konwertowane na piksele. Biblioteka Java2D
wykonuje obliczenia o pojedynczej precyzji na liczbach typu float w wikszoci wykonywanych wewntrznie dziaa. Pojedyncza precyzja w zupenoci wystarcza celem oblicze geometrycznych jest przecie ustawienie pikseli na ekranie lub w drukarce. Dopki
bdy zaokrglania mieszcz si w zakresie jednego piksela, rezultat wizualny nie cierpi.
Ponadto obliczenia na liczbach typu float s na niektrych platformach szybsze, a dodatkowo wartoci tego typu zajmuj o poow mniej miejsca ni wartoci typu double.
Czasami jednak obliczenia na liczbach typu float bywaj niewygodne, poniewa Java niewzruszenie wymaga rzutowania, jeli niezbdna jest konwersja wartoci typu double na typ
float. Przeanalizujmy na przykad ponisz instrukcj:
float f = 1.2;

// bd

Ta instrukcja spowoduje bd kompilacji, poniewa staa 1.2 jest typu double i kompilator
obawia si utraty danych. Rozwizaniem jest dodanie przyrostka F do staej zmiennoprzecinkowej:
float f = 1.2F;

// OK

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

334

Java. Podstawy
Teraz przyjrzyjmy si poniszej instrukcji:
Rectangle2D r = . . .
float f = r.getWidth();

// bd

Instrukcja ta spowoduje bd kompilacji z tego samego powodu co poprzednia. Metoda


getWidth zwraca warto typu double. W tym przypadku rozwizaniem jest rzutowanie:
float f = (float) r.getWidth();

// OK

Poniewa stosowanie przyrostkw i rzutowania nie jest wygodne, projektanci biblioteki


Java2D postanowili utworzy dwie wersje kadej klasy reprezentujcej figur: jedn ze
wsprzdnymi typu float dla oszczdnych programistw i jedn ze wsprzdnymi typu
double dla leniwych (w tej ksice zaliczamy si do tych drugich, czyli stosujemy wsprzdne
typu double, gdzie tylko moemy).
Projektanci biblioteki zastosowali ciekaw i pocztkowo wprowadzajc w bd metod
pakowania obu wersji klas. Przyjrzyjmy si klasie Rectangle2D. Jest to abstrakcyjna klasa
majca dwie konkretne podklasy, ktre s dodatkowo statycznymi klasami wewntrznymi:
Rectangle2D.Float
Rectangle2D.Double

Rysunek 7.9 przedstawia diagram dziedziczenia.


Rysunek 7.9.
Klasy
prostoktw 2D

Najlepiej ignorowa fakt, e obie te konkretne klasy s statyczne i wewntrzne to tylko


taki chwyt, ktry pozwala unikn nazw FloatRectangle2D i DoubleRectangle2D (wicej informacji na temat statycznych klas wewntrznych znajduje si w rozdziale 6.).
Tworzc obiekt klasy Rectangle2D.Float, wartoci okrelajce wsprzdne naley podawa
jako typu float. W przypadku klasy Rectangle2D.Double wsprzdne musz by typu double.
Rectangle2D.Float floatRect = new Rectangle2D.Float(10.0F, 25.0F, 22.5F, 20.0F);
Rectangle2D.Double doubleRect = new Rectangle2D.Double(10.0, 25.0, 22.5, 20.0);

Poniewa zarwno klasa Rectangle2D.Float, jak i Rectangle2D.Double rozszerzaj wspln


klas Rectangle2D, a metody w tych podklasach przesaniaj metody nadklasy, zapamitywanie typu figury nie przynosi waciwie adnych korzyci. Referencje do prostoktw mona
przechowywa w zmiennych typu Rectangle2D.
Rectangle2D floatRect = new Rectangle2D.Float(10.0F, 25.0F, 22.5F, 20.0F);
Rectangle2D doubleRect = new Rectangle2D.Double(10.0, 25.0, 22.5, 20.0);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

335

Oznacza to, e uycie klas wewntrznych jest konieczne tylko przy tworzeniu obiektw figur.
Parametry konstruktora okrelaj lewy grny rg, szeroko i wysoko prostokta.
Klasa Rectangle2D.Float zawiera jedn metod, ktrej nie dziedziczy po klasie
Rectangle2D. Jest to metoda setRect(float x, float y, float h, float w).
Metody tej nie mona uy, jeli referencja do obiektu typu Rectangle2D.Float jest
przechowywana w zmiennej typu Rectangle2D. Nie jest to jednak dua strata klasa
Rectangle2D zawiera metod setRect z parametrami typu double.

Metody klasy Rectangle2D przyjmuj parametry i zwracaj wartoci typu double. Na przykad
metoda getWidth zwraca warto typu double, nawet jeli szeroko jest zapisana w postaci
liczby typu float w obiekcie typu Rectangle2D.Float.
Aby cakowicie pozby si wartoci typu float, naley uywa klas typu Double.
Jednak w programach tworzcych wiele tysicy figur warto rozway uycie klas
Float ze wzgldu na oszczdno pamici.

Wszystko, co napisalimy do tej pory na temat klas Rectangle2D, dotyczy rwnie pozostaych
klas reprezentujcych figury. Dodatkowo istnieje klasa o nazwie Point2D, ktrej podklasy to
Point2D.Float i Point2D.Double. Poniszy fragment programu tworzy obiekt takiej klasy:
Point2D p = new Point2D.Double(10, 20);

Klasa Point2D jest niezwykle przydatna zastosowanie obiektw Point2D jest


znacznie blisze idei programowania obiektowego ni uywanie oddzielnych wartoci x i y. Wiele konstruktorw przyjmuje parametry typu Point2D. Zalecamy stosowanie obiektw tej klasy, gdzie si da dziki nim obliczenia geometryczne s czsto duo prostsze.

Klasy Rectangle2D i Ellipse2D dziedzicz po wsplnej nadklasie RectangularShape. Wprawdzie elipsa nie jest prostoktna, ale mona na niej opisa prostokt (zobacz rysunek 7.10).
Rysunek 7.10.
Prostokt
opisany
na elipsie

Klasa RectangularShape definiuje ponad 20 metod wsplnych dla tych figur. Zaliczaj si
do nich metody getWidth, getHeight, getCenterX i getCenterY (niestety w czasie pisania tej
ksiki nie byo metody getCenter zwracajcej obiekt typu Point2D).
Dodatkowo do hierarchii klas reprezentujcych figury dodano kilka starszych klas z Java 1.0.
Klasy Rectangle i Point, ktre przechowuj prostokt i punkt przy uyciu wsprzdnych
cakowitych, rozszerzaj klasy Rectangle2D i Point2D.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

336

Java. Podstawy
Rysunek 7.11 przedstawia relacje pomidzy klasami figur. Klasy Double i Float zostay
pominite, a klasy spadkowe wyrniono szarym tem.

Rysunek 7.11. Relacje midzy klasami figur

Tworzenie obiektw typu Rectangle2D i Ellipse2D jest prostym zadaniem. Naley poda:

wsprzdne x i y lewego grnego rogu,

wysoko i szeroko.

W przypadku elipsy te wartoci dotycz opisanego na niej prostokta. Na przykad instrukcja:


Ellipse2D e = new Ellipse2D.Double(150, 200, 100, 50);

utworzy elips wpisan w prostokt, ktrego lewy grny rg znajduje si w punkcie o wsprzdnych (150, 200) o szerokoci 100 i wysokoci 50.
Czasami jednak wsprzdne lewego grnego rogu nie s od razu dostpne. Czsto zdarza si,
e dostpne s dwa punkty lece naprzeciw siebie, ale nie s to rogi grny lewy i prawy dolny.
Nie mona utworzy prostokta w poniszy sposb:
Rectangle2D rect = new Rectangle2D.Double(px, py, qx - px, qy - py);

// bd

Jeli p nie jest lewym grnym rogiem, jedna lub obie wsprzdne bd miay wartoci
ujemne i prostokt si nie pojawi. W takim przypadku naley najpierw utworzy pusty prostokt i uy metody setFrameFromDiagonal:
Rectangle2D rect = new Rectangle2D.Double();
rect.setFrameFromDiagonal(px, py, qx, qy);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

337

Jeszcze lepiej, jeli p i q s punktami rogw reprezentowanymi przez obiekty typu Point2D:
rect.setFrameFromDiagonal(p, q);

Przy tworzeniu elipsy zazwyczaj znane s rodek, szeroko i wysoko opisanego na niej
prostokta, a nie jego rogi (ktre nawet nie le na elipsie). Metoda setFrameFromCenter
przyjmuje punkt rodkowy, ale wymaga take jednego z czterech rogw. W zwizku z tym
elips zazwyczaj tworzy si nastpujco:
Ellipse2D ellipse = new Ellipse2D.Double(centerX - width / 2, centerY - height / 2,
width, height);

Aby utworzy lini, naley poda jej punkt pocztkowy i kocowy w postaci obiektw Point2D
lub par liczb:
Line2D line = new Line2D.Double(start, end);

lub
Line2D line = new Line2D.Double(startX, startY, endX, endY);

Program przedstawiony na listingu 7.4 rysuje prostokt, elips znajdujc si wewntrz tego
prostokta, przektn prostokta oraz koo o takim samym rodku jak prostokt. Rysunek 7.12
przedstawia wynik dziaania tego programu.
Rysunek 7.12.
Rysowanie figur
geometrycznych

Listing 7.4. draw/DrawTest.java


package draw;
import java.awt.*;
import java.awt.geom.*;
import javax.swing.*;
/**
* @version 1.32 2007-04-14
* @author Cay Horstmann
*/
public class DrawTest
{
public static void main(String[] args)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

338

Java. Podstawy
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new DrawFrame();
frame.setTitle("DrawTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca panel z rysunkami.
*/
class DrawFrame extends JFrame
{
public DrawFrame()
{
add(new DrawComponent());
pack();
}
}
/**
* Komponent wywietlajcy prostokty i elipsy.
*/
class DrawComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 400;
private static final int DEFAULT_HEIGHT = 400;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// Rysowanie prostokta.
double
double
double
double

leftX = 100;
topY = 100;
width = 200;
height = 150;

Rectangle2D rect = new Rectangle2D.Double(leftX, topY, width, height);


g2.draw(rect);
// Rysowanie elipsy.
Ellipse2D ellipse = new Ellipse2D.Double();
ellipse.setFrame(rect);
g2.draw(ellipse);
// Rysowanie przektnej.
g2.draw(new Line2D.Double(leftX, topY, leftX + width, topY + height));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

// Rysowanie koa z takim samym rodkiem.


double centerX = rect.getCenterX();
double centerY = rect.getCenterY();
double radius = 150;
Ellipse2D circle = new Ellipse2D.Double();
circle.setFrameFromCenter(centerX, centerY, centerX + radius, centerY + radius);
g2.draw(circle);
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH,
DEFAULT_HEIGHT); }
}
java.awt.geom.RectangularShape 1.2

double getCenterX()

double getCenterY()

double getMinX()

double getMinY()

double getMaxX()

double getMaxY()

Zwraca wsprzdn x lub y punktu rodkowego, punktu o najmniejszych


lub najwikszych wsprzdnych prostokta.

double getWidth()

double getHeight()

Zwraca szeroko lub wysoko prostokta.

double getX()

double getY()

Zwraca wsprzdn x lub y lewego grnego rogu prostokta.


java.awt.geom.Rectangle2D.Double 1.2

Rectangle2D.Double(double x, double y, double w, double h)

Tworzy prostokt z lewym grnym rogiem w podanym miejscu i o podanej


szerokoci i dugoci.
java.awt.geom.Rectangle2D.Float 1.2

Rectangle2D.Float(float x, float y, float w, float h)

Tworzy prostokt z lewym grnym rogiem w podanym miejscu i o podanej


szerokoci i dugoci.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

339

340

Java. Podstawy
java.awt.geom.Ellipse2D.Double 1.2

Ellipse2D.Double(double x, double y, double w, double h)

Rysuje elips wpisan w prostokt, ktrego lewy grny rg znajduje si w podanym


miejscu i ktry ma okrelone wysoko oraz szeroko.
java.awt.geom.Point2D.Double 1.2

Point2D.Double(double x, double y)

Rysuje punkt o podanych wsprzdnych.


java.awt.geom.Line2D.Double 1.2

Line2D.Double(Point2D start, Point2D end)

Line2D.Double(double startX, double startY, double endX, double endY)

Rysuje lini midzy dwoma podanymi punktami.

7.6. Kolory
Metoda setPaint z klasy Graphics2D ustawia kolor, ktry jest stosowany we wszystkich
kolejnych rysunkach graficznych. Na przykad:
g2.setPaint(Color.RED);
g2.drawString("Uwaga!", 100, 100);

Figury zamknite (np. prostokt czy elipsa) mona w takiej sytuacji wypeni za pomoc
metody fill (zamiast draw):
Rectangle2D rect = . . .;
g2.setPaint(Color.RED);
g2.fill(rect);
// Wypenienie prostokta rect kolorem czerwonym.

Aby zastosowa kilka kolorw, naley wybra kolor, zastosowa metod draw lub fill,
a nastpnie wybra inny kolor i ponownie zastosowa metod draw lub fill.
Metoda fill rysuje o jeden piksel mniej po prawej i na dole. Jeli na przykad narysujemy prostokt new Rectangle2D.Double(0, 0, 10, 20), to rysunek bdzie obejmowa piksele o wsprzdnych x = 10 i y = 20. Jeli wypenimy ten prostokt kolorem,
piksele te nie zostan pokolorowane.

Do definiowania kolorw suy klasa java.awt.Color. W klasie tej dostpnych jest 13 nastpujcych predefiniowanych staych reprezentujcych kolory:
BLACK, BLUE, CYAN, DARK_GRAY, GRAY, GREEN, LIGHT_GRAY, MAGENTA, ORANGE, PINK, RED,
WHITE, YELLOW

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

341

Przed Java SE 1.4 stae okrelajce kolory byy pisane maymi literami, np.
Color.red. Byo to sprzeczne z przyjt konwencj pisania staych wielkimi literami. Obecnie nazwy tych staych mona pisa wielkimi lub, ze wzgldu na zgodno
wsteczn, maymi literami.

Niestandardowy kolor mona zdefiniowa, tworzc obiekt klasy Color i podajc wartoci
trzech skadowych: czerwonego, zielonego i niebieskiego. Warto kadego ze skadnikw
(zajmujcych po jednym bajcie) musi nalee do zbioru 0 255. Poniszy kod przedstawia
sposb wywoania konstruktora klasy Color z parametrami okrelajcymi stopie czerwieni,
niebieskiego i zieleni:
Color(int redness, int greenness, int blueness)

Poniej znajduje si przykadowa procedura tworzca niestandardowy kolor:


g2.setPaint(new Color(0, 128, 128));
g2.drawString("Witaj!", 75, 125);

// niebieskozielony

Poza jednolitymi kolorami mona take stosowa bardziej skomplikowane ustawienia, jak rne odcienie czy obrazy. Wicej informacji na ten temat znajduje si
w drugim tomie w rozdziale o zaawansowanych technikach AWT. Jeli zamiast obiektu
typu Graphics2D zostanie uyty obiekt Graphics, kolory naley ustawia za pomoc metody
setColor.

Do ustawiania koloru ta suy metoda setBackground z klasy Component, bdcej nadklas


klasy JComponent.
MyComponent p = new MyComponent();
p.setBackground(Color.PINK);

Istnieje te metoda setForeground, ktra okrela kolor elementw rysowanych na komponencie.


Metody brighter() i darker() jak sama nazwa wskazuje sprawiaj, e aktualnie uywany kolor staje si janiejszy bd ciemniejszy. Ponadto metoda brighter
jest dobrym sposobem na wyrnienie wybranego elementu. W rzeczywistoci metoda ta
nieznacznie rozjania kolor. Aby kolor by duo janiejszy, mona t metod zastosowa trzy razy: c.brighter().brighter().brighter().

Znacznie wicej predefiniowanych nazw kolorw znajduje si w klasie SystemColor. Stae


tej klasy okrelaj kolory stosowane do rozmaitych elementw systemu uytkownika. Na
przykad instrukcja:
p.setBackground(SystemColor.window)

ustawia kolor ta komponentu na domylny dla wszystkich okien w systemie uytkownika


(to jest wstawiane przy kadym rysowaniu okna). Kolory zdefiniowane w klasie SystemColor
s szczeglnie przydatne, kiedy chcemy narysowa elementy interfejsu uytkownika nieodbiegajce kolorystyk od standardowych elementw w systemie. Tabela 7.1 zawiera nazwy
kolorw systemowych oraz ich opisy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

342

Java. Podstawy

Tabela 7.1. System kolorw


Nazwa

Zastosowanie

desktop

Kolor ta pulpitu

activeCaption

Kolor belki tytuowej aktywnego okna

activeCaptionText

Kolor tekstu na belce tytuowej

activeCaptionBorder

Kolor obramowania aktywnej belki

inactiveCaption

Kolor nieaktywnej belki

inactiveCaptionText

Kolor tekstu nieaktywnej belki

inactiveCaptionBorder

Kolor obramowania nieaktywnej belki

window

To okna

windowBorder

Kolor obramowania okna

windowText

Kolor tekstu w oknie

menu

To menu

menuText

Kolor tekstu w menu

text

Kolor ta tekstu

textText

Kolor tekstu

textInactiveText

Kolor tekstu nieaktywnych elementw sterujcych

textHighlight

Kolor ta wyrnionego tekstu

textHighlightText

Kolor wyrnionego tekstu

control

Kolor ta elementw sterujcych

controlText

Kolor tekstu w elementach sterujcych

controlLtHighlight

Sabe wyrnienie elementw sterujcych

controlHighlight

Silne wyrnienie elementw sterujcych

controlShadow

Kolor cienia elementw sterujcych

controlDkShadow

Ciemniejszy kolor cienia elementw sterujcych

scrollbar

Kolor ta dla suwakw

info

Kolor ta dla tekstu pomocy

infoText

Kolor tekstu pomocy

java.awt.Color 1.0

Color(int r, int g, int b)

Tworzy obiekt reprezentujcy kolor.


Parametry:

Warto barwy czerwonej

Warto barwy zielonej

Warto barwy niebieskiej

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

343

java.awt.Graphics 1.0

Color getColor()

void setColor(Color c)

Pobiera lub ustawia kolor. Wszystkie nastpne rysunki bd miay ten kolor.
Parametry:

Nowy kolor

java.awt.Graphics2D 1.2

Paint getPaint()

void setPaint(Paint p)

Pobiera lub ustawia wasno paint danego kontekstu graficznego. Klasa Color
implementuje interfejs Paint. W zwizku z tym za pomoc tej metody mona
ustawi atrybut paint na jednolity kolor.

void fill(shape s)

Wypenia figur aktualnym kolorem.


java.awt.Component 1.0

Color getBackground()

void setBackground(Color c)

Pobiera lub ustawia kolor ta.


Parametry:

Color getForeground()

void setForeground(Color c)

Nowy kolor ta

Pobiera lub ustawia kolor frontu.


Parametry:

Nowy kolor frontu

7.7. Czcionki
Program przedstawiony na pocztku tego rozdziau wywietla acuch tekstu pisany domyln
czcionk. Czsto jednak zdarza si, e tekst musi by napisany inn czcionk. Identyfikatorem
czcionki jest jej nazwa. Nazwa czcionki skada si z nazwy rodziny czcionek, np. Helvetica,
i opcjonalnego przyrostka, np. Bold. Na przykad nazwy Helvetica i Helvetica Bold nale
do rodziny czcionek o nazwie Helvetica.
Aby sprawdzi, jakie czcionki s dostpne w danym komputerze, naley wywoa metod
getAvailableFontFamilyNames z klasy GraphicsEnvironment. Ta metoda zwraca tablic nazw
wszystkich dostpnych czcionek w postaci acuchw. Egzemplarz klasy GraphicsEnvi
ronment reprezentujcy rodowisko graficzne systemu uytkownika mona utworzy za

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

344

Java. Podstawy
pomoc statycznej metody getLocalGraphicsEnvironment. Poniszy program drukuje nazwy
wszystkich czcionek znajdujcych si w systemie:
import java.awt.*;
public class ListFonts
{
public static void main(String[] args)
{
String[] fontNames = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getAvailableFontFamilyNames();
for (String fontName : fontNames)
System.out.println(fontName);
}
}

W jednym z systemw pocztek tej listy wyglda nastpujco:


Abadi MT Condensed Light
Arial
Arial Black
Arial Narrow
Arioso
Baskerville
Binner Gothic
. . .

Lista ta zawiera ponad 70 pozycji.


Nazwy czcionek mog by znakami towarowymi, a ich projekty mog w niektrych jurysdykcjach podlega prawom autorskim. W zwizku z tym dystrybucja czcionek czsto wie
si z uiszczaniem opat licencyjnych ich wacicielom. Oczywicie, podobnie jak s tanie
podrbki drogich perfum, istniej te podrbki czcionek imitujce oryginay. Na przykad
imitacja czcionki Helvetica w systemie Windows nosi nazw Arial.
Jako wsplny punkt odniesienia w bibliotece AWT zdefiniowano pi logicznych nazw
czcionek:
SansSerif
Serif
Monospaced
Dialog
DialogInput

Czcionki te s zawsze zamieniane na czcionki, ktre znajduj si w danym urzdzeniu. Na


przykad w systemie Windows czcionka SansSerif jest zastpowana czcionk Arial.
Dodatkowo pakiet SDK firmy Sun zawsze zawiera trzy rodziny czcionek: Lucida Sans,
Lucida Bright i Lucida Sans Typewriter.
Aby narysowa znak dan czcionk, najpierw trzeba utworzy obiekt klasy Font. Konieczne
jest podanie nazwy i stylu czcionki oraz rozmiaru w punktach drukarskich. Ponisza instrukcja
tworzy obiekt klasy Font:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

345

Trzeci argument okrela rozmiar w punktach. Jednostka ta jest powszechnie stosowana w typografii do okrelania rozmiaru czcionek. Jeden punkt jest rwny 1/72 cala, czyli okoo 0,35 mm.
W konstruktorze klasy Font mona uy logicznej nazwy czcionki zamiast nazwy fizycznej.
Styl (zwyky, pogrubiony, kursywa lub pogrubiona kursywa) okrela drugi argument konstruktora Font, ktry moe mie jedn z poniszych wartoci:
Font.PLAIN
Font.BOLD
Font.ITALIC
Font.BOLD + Font.ITALIC

Sposb odwzorowania logicznych nazw czcionek na fizyczne jest okrelony w pliku


fontconfig.properties w katalogu jre/lib znajdujcym si w folderze instalacji Javy.
Informacje na temat tego pliku znajduj si pod adresem http://docs.oracle.com/
javase/7/docs/technotes/guides/intl/fontconfig.html.

Pliki czcionek mona wczytywa w formatach TrueType lub PostScript type 1. Potrzebny jest
do tego strumie wejciowy dla danej czcionki zazwyczaj z pliku lub adresu URL (wicej
informacji na temat strumieni znajduje si w rozdziale 1. drugiego tomu). Nastpnie naley
wywoa statyczn metod Font.createFont:
URL url = new URL("http://www.fonts.com/Wingbats.ttf");
InputStream in = url.openStream();
Font f1 = Font.createFont(Font.TRUETYPE_FONT, in);

Zastosowana zostaa zwyka czcionka o rozmiarze 1 punktu. Do okrelenia danego rozmiaru czcionki naley uy metody deriveFont:
Font f = f1.deriveFont(14.0F);

Istniej dwie przecione wersje metody deriveFont. Jedna z nich (przyjmujca


parametr typu float) ustawia rozmiar czcionki, a druga (przyjmujca parametr typu
int) ustawia styl czcionki. W zwizku z tym instrukcja f1.deriveFont(14) ustawia styl,
a nie rozmiar czcionki! Styl czcionki bdzie w tym przypadku kursyw, poniewa binarna
reprezentacja liczby 14 ustawia bit ITALIC.

Fonty Javy zawieraj symbole i znaki ASCII. Na przykad znak \u2297 fontu Dialog to
znak .Dostpne s tylko te symbole, ktre zdefiniowano w zestawie znakw Unicode.
Poniszy fragment programu wywietla napis Witaj, wiecie! standardow czcionk bezszeryfow systemu z zastosowaniem pogrubienia i o rozmiarze 14 punktw:
Font sansbold14 = new Font("SansSerif", Font.BOLD, 14);
g2.setFont(sansbold14);
String message = "Witaj, wiecie!";
g2.drawString(message, 75, 100);

Teraz wyporodkujemy nasz napis w zawierajcym go komponencie. Do tego celu potrzebne


s informacje o szerokoci i wysokoci acucha w pikselach. O wymiarach tych decyduj
trzy czynniki:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

346

Java. Podstawy

czcionka (w tym przypadku jest to pogrubiona czcionka bezszeryfowa o rozmiarze


14 punktw),

acuch (w tym przypadku Witaj, wiecie!),

urzdzenie, na ktrym acuch bdzie wywietlany (w tym przypadku ekran monitora).

Obiekt reprezentujcy wasnoci czcionki urzdzenia z ekranem tworzymy za pomoc metody


getFontRenderContext z klasy Graphics2D. Zwraca ona obiekt klasy FontRenderContext.
Obiekt ten naley przekaza metodzie getStringBounds z klasy Font:
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);

Metoda getStringBounds zwraca prostokt, w ktrym mieci si acuch.


Do interpretacji wymiarw tego prostokta potrzebna jest znajomo podstawowych poj
z zakresu skadu tekstw (zobacz rysunek 7.13). Linia bazowa (ang. baseline) to teoretyczna
linia, na ktrej opiera si dolna cz litery, np. e. Wyduenie grne (ang. ascent) to odstp
dzielcy lini bazow i lini grn pisma (ang. ascender), ktra okrela grn granic liter,
takich jak b lub k czy te wielkich liter. Wyduenie dolne (ang. descent) to odlego pomidzy lini bazow a lini doln pisma (ang. descender), ktra stanowi granic dolnej czci
takich liter jak p lub g.

Rysunek 7.13. Pojcia z zakresu skadu tekstw

Interlinia (ang. leading) to odstp pomidzy wydueniem dolnym jednej linii a wydueniem
grnym nastpnej linii (termin pochodzi od paskw oowiu uywanych przez zecerw do
oddzielania linii). Wysoko (ang. height) czcionki to odlego pomidzy nastpujcymi po
sobie liniami bazowymi i jest rwna sumie wyduenia dolnego, leadingu i wyduenia grnego.
Szeroko prostokta zwracanego przez metod getStringBounds okrela szeroko tekstu.
Wysoko natomiast jest rwna sumie wyduenia dolnego, leadingu i wyduenia grnego.
Prostokt ma swj pocztek na linii bazowej acucha. Grna wsprzdna y prostokta ma
warto ujemn. W zwizku z tym szeroko, wysoko i wyduenie grne acucha mona
sprawdzi nastpujco:
double stringWidth = bounds.getWidth();
double stringHeight = bounds.getHeight();
double ascent = -bounds.getY();

Aby sprawdzi wyduenie dolne lub leading, naley uy metody getLineMetrics klasy Font.
Zwraca ona obiekt klasy LineMetrics, dysponujcy metodami do sprawdzania wyduenia
dolnego i leadingu:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

347

LineMetrics metrics = f.getLineMetrics(message, context);


float descent = metrics.getDescent();
float leading = metrics.getLeading();

W poniszym fragmencie programu wykorzystano wszystkie opisane powyej informacje do


umieszczenia acucha na rodku zawierajcego go komponentu:
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
// (x, y) = lewy grny rg tekstu
double x = (getWidth() - bounds.getWidth()) / 2;
double y = (getHeight() - bounds.getHeight()) / 2;
// Dodanie wyduenia grnego do y w celu signicia do linii bazowej.
double ascent = -bounds.getY();
double baseY = y + ascent;
g2.drawString(message, (int) x, (int) baseY);

Aby uatwi sobie zrozumienie techniki wyrodkowywania tekstu, warto sobie uzmysowi,
e metoda getWidth() zwraca szeroko komponentu. Pewna cz tej przestrzeni, bounds.
getWidth(), jest zajmowana przez tekst. Reszta powinna by podzielona na dwie rwne czci,
rozmieszczone po obu stronach tekstu. Ten sam sposb rozumowania dotyczy wysokoci.
Kiedy konieczne jest obliczenie wymiarw ukadu bez uycia metody paintCom
ponent, nie mona uzyska obiektu obrazowania czcionki typu Graphics2D.
W zamian naley wywoa metod getFontMetrics klasy JComponent, a nastpnie metod
getFontRenderContext.
FontRenderContext context = getFontMetrics(f).getFontRenderContext();

Przykadowy program przedstawiony poniej nie tylko drukuje napis, ale take lini bazow
i prostokt otaczajcy napis. Rysunek 7.14 przedstawia wynik dziaania tego programu.
Listing 7.5 zawiera jego kod.
Rysunek 7.14.
Linia bazowa
i prostokt
otaczajcy
acuch

Listing 7.5. font/FontTest.java


package font;
import
import
import
import

java.awt.*;
java.awt.font.*;
java.awt.geom.*;
javax.swing.*;

/**
* @version 1.33 2007-04-14

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

348

Java. Podstawy
* @author Cay Horstmann
*/
public class FontTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new FontFrame();
frame.setTitle("FontTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka z komponentem zawierajcym tekst.
*/
class FontFrame extends JFrame
{
public FontFrame()
{
add(new FontComponent());
pack();
}
}
/**
* Komponent z tekstem w ramce na rodku.
*/
class FontComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
String message = "Witaj, wiecie!";
Font f = new Font("Serif", Font.BOLD, 36);
g2.setFont(f);
// Sprawdzenie rozmiaru tekstu.
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D bounds = f.getStringBounds(message, context);
// set (x, y) = lewy grny rg tekstu
double x = (getWidth() - bounds.getWidth()) / 2;
double y = (getHeight() - bounds.getHeight()) / 2;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

349

// Dodanie wyduenia grnego do y w celu signicia do linii bazowej.


double ascent = -bounds.getY();
double baseY = y + ascent;
// Rysowanie komunikatu.
g2.drawString(message, (int) x, (int) baseY);
g2.setPaint(Color.LIGHT_GRAY);
// Rysowanie linii bazowej.
g2.draw(new Line2D.Double(x, baseY, x + bounds.getWidth(), baseY));
// Rysowanie otaczajcego tekst prostokta.
Rectangle2D rect = new Rectangle2D.Double(x, y, bounds.getWidth(),
bounds.getHeight());
g2.draw(rect);
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH,
DEFAULT_HEIGHT); }
}
java.awt.Font 1.0

Font(String name, int style, int size)

Tworzy obiekt reprezentujcy czcionk.


Parametry:

name

Nazwa czcionki moe by nazwa typu Helvetica


Bold lub logiczna nazwa typu Serif lub SansSerif

style

Styl: Font.PLAIN, Font.BOLD, Font.ITALIC lub


Font.BOLD + Font.ITALIC

size

Rozmiar w punktach (na przykad 12)

String getFontName()

Pobiera nazw czcionki (typu Helvetica Bold).

String getFamily()

Pobiera nazw rodziny czcionek (np. Helvetica).

String getName()

Pobiera nazw logiczn (np. SansSerif), jeli czcionka zostaa utworzona z nazwy
logicznej. W przeciwnym przypadku zwraca nazw czcionki.

Rectangle 2D getStringBounds(String s, FontRenderContext context) 1.2

Zwraca prostokt otaczajcy acuch. Prostokt ma swj pocztek na linii bazowej


acucha. Grna wsprzdna y prostokta ma warto rwn odwrotnoci
wyduenia grnego. Wysoko prostokta jest rwna sumie wyduenia grnego,
dolnego i leadingu. Szeroko jest rwna szerokoci tekstu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

350

Java. Podstawy

LineMetrics getLineMetrics(String s, FontRenderContext context) 1.2

Zwraca ona obiekt klasy LineMetrics dysponujcy metodami do sprawdzania


wyduenia dolnego i leadingu.

Font deriveFont(int style) 1.2

Font deriveFont(float size) 1.2

Font deriveFont(int style, float size) 1.2

Zwraca now czcionk rnic si od aktualnej tylko rozmiarem podanym


jako argument.
java.awt.font.LineMetrics 1.2

float getAscent()

Pobiera wyduenie grne czcionki odlego linii bazowej od wierzchokw


wielkich liter.

float getDescent()

Pobiera wyduenie dolne czcionki odlego linii bazowej od podstaw liter


sigajcych dolnej linii pisma.

float getLeading()

Pobiera leading czcionki odstp pomidzy spodem jednej linii tekstu


a wierzchokiem nastpnej.

float getHeight()

Pobiera cakowit wysoko czcionki odlego pomidzy dwiema liniami


bazowymi tekstu (wyduenie dolne + leading + wyduenie grne).
java.awt.Graphics 1.0

Font getFont()

void setFont(Font font)

Pobiera lub ustawia czcionk. Czcionka ta bdzie stosowana w kolejnych


operacjach rysowania tekstu.
Parametry:

font

Czcionka

void drawString(String str, int x, int y)

Rysuje acuch przy uyciu aktualnej czcionki i koloru.


Parametry:

str

acuch

Wsprzdna x pocztku acucha

Wsprzdna y linii bazowej acucha

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

351

java.awt.Graphics2D 1.2

FontRenderContext getFontRenderContext()

Pobiera kontekst wizualizacji czcionki, ktry okrela cechy czcionki w kontekcie


graficznym.

void drawString(String str, float x, float y)

Rysuje acuch przy zastosowaniu aktualnej czcionki i koloru.


Parametry:

str

acuch

Wsprzdna x pocztku acucha

Wsprzdna y linii bazowej acucha

javax.swing.JComponent 1.2

FontMetrics getFontMetrics(Font f) 5.0

Pobiera cechy czcionki. Klasa FontMetrics jest prekursorem klasy LineMetrics.


java.awt.FontMetrics 1.0

FontRenderContext getFontRenderContext() 1.2

Pobiera kontekst wizualizacji czcionki.

7.8. Wywietlanie obrazw


Poznalimy techniki tworzenia prostych rysunkw skadajcych si z linii i figur geometrycznych. Bardziej zoone obrazy, jak zdjcia, maj zazwyczaj inne pochodzenie, np.
przenosi si je do komputera za pomoc skanera lub wytwarza w wyspecjalizowanym do
tego celu oprogramowaniu. W drugim tomie nauczymy si tworzy obrazy zoone z pojedynczych pikseli zapisanych w tablicy technika ta jest czsto uywana na przykad podczas tworzenia obrazw fraktalnych.
Obrazy zapisane w postaci plikw na dysku lub w internecie mona wczyta do aplikacji
w Javie i wywietli na obiektach Graphics. Obrazy mona wczytywa na wiele sposobw.
W poniszym przykadzie uyta jest znana nam ju klasa ImageIcon:
Image image = new ImageIcon(filename).getImage();

Zmienna image zawiera referencj do obiektu opakowujcego obraz. Mona go wywietli za


pomoc metody drawImage z klasy Graphics:
public void paintComponent(Graphics g)
{
. . .
g.drawImage(image, x, y, null);
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

352

Java. Podstawy
Program z listingu 7.6 robi nawet wicej, poniewa wypenia cae okno wieloma obrazami.
Rezultat tego wida na rysunku 7.15. Za to kaskadowe wypenienie odpowiedzialna jest
metoda paintComponent. Najpierw rysujemy jeden obraz w lewym grnym rogu, a nastpnie
zapeniamy cae okno za pomoc metody copyArea:
for (int i = 0; i * imageWidth <= getWidth(); i++)
for (int j = 0; j * imageHeight <= getHeight(); j++)
if (i + j > 0)
g.copyArea(0, 0, imageWidth, imageHeight, i * imageWidth, j * imageHeight);

Rysunek 7.15.
Okno wypenione
kopiami jednego
obrazu

Listing 7.6 przedstawia peny kod rdowy opisywanego program.


Listing 7.6. image/ImageTest.java
package image;
import java.awt.*;
import javax.swing.*;
/**
* @version 1.33 2007-04-14
* @author Cay Horstmann
*/
public class ImageTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ImageFrame();
frame.setTitle("ImageTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca komponent obrazu.
*/
class ImageFrame extends JFrame
{
public ImageFrame()
{
add(new ImageComponent());

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 7.

Grafika

pack();
}
}
/**
* Komponent wywietlajcy powielony obraz.
*/
class ImageComponent extends JComponent
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private Image image;
public ImageComponent()
{
image = new ImageIcon("blue-ball.gif").getImage();
}
public void paintComponent(Graphics g)
{
if (image == null) return;
int imageWidth = image.getWidth(this);
int imageHeight = image.getHeight(this);
// Rysowanie obrazu w lewym grnym rogu.
g.drawImage(image, 0, 0, null);
// Powielenie obrazu w obrbie komponentu.
for (int i = 0; i * imageWidth <= getWidth(); i++)
for (int j = 0; j * imageHeight <= getHeight(); j++)
if (i + j > 0) g.copyArea(0, 0, imageWidth, imageHeight, i * imageWidth, j
* imageHeight);
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH,
DEFAULT_HEIGHT); }
}
java.awt.Graphics 1.0

boolean drawImage(Image img, int x, int y, ImageObserver observer)

Rysuje obraz w naturalnym rozmiarze. Uwaga: to wywoanie moe zwrci


warto przed narysowaniem obrazu.
Parametry:

img

Obraz do narysowania

Wsprzdna x lewego grnego rogu

Wsprzdna y lewego grnego rogu

observer

Obiekt powiadamiajcy o postpie procesu


wizualizacji (moe by warto null)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

353

354

Java. Podstawy

boolean drawImage(Image img, int x, int y, int width, int height,


ImageObserver observer)

Rysuje obraz o zmienionych wymiarach. System dopasowuje rozmiar obrazu


do obszaru o podanej szerokoci i wysokoci. Uwaga: to wywoanie moe zwrci
warto przed narysowaniem obrazu.
Parametry:

img

Obraz do narysowania

Wsprzdna x lewego grnego rogu

Wsprzdna y lewego grnego rogu

width

Szeroko obrazu

height

Wysoko obrazu

observer

Obiekt powiadamiajcy o postpie procesu


wizualizacji (moe by warto null)

void copyArea(int x, int y, int width, int height, int dx, int dy)

Kopiuje obszar ekranu.


Parametry:

Wsprzdna x lewego grnego rogu obszaru


rdowego

Wsprzdna y lewego grnego rogu obszaru


rdowego

width

Szeroko obszaru rdowego

height

Wysoko obszaru rdowego

dx

Odlego w poziomie od obszaru rdowego


do obszaru docelowego

dy

Odlego w pionie od obszaru rdowego


do obszaru docelowego

Na tym zakoczymy wprowadzenie do grafiki w Javie. Bardziej zaawansowane techniki, takie


jak grafika 2D i obrbka obrazw, zostay opisane w drugim tomie. W kolejnym rozdziale
dowiemy si, jak programy reaguj na dane wprowadzane przez uytkownika.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Obsuga zdarze
W tym rozdziale:

Podstawy obsugi zdarze

Akcje

Zdarzenia generowane przez mysz

Hierarchia zdarze AWT

Obsuga zdarze ma fundamentalne znaczenie w programach z graficznym interfejsem uytkownika. Kady, kto chce tworzy interfejsy graficzne w Javie, musi opanowa obsug zdarze. Ten rozdzia opisuje model obsugi zdarze biblioteki AWT. Do opisywanych zagadnie
nale przechwytywanie zdarze w komponentach interfejsu uytkownika i urzdzeniach
wejciowych, a take akcje (ang. actions), czyli bardziej strukturalna metoda przetwarzania
zdarze.

8.1. Podstawy obsugi zdarze


Kady system operacyjny posiadajcy graficzny interfejs uytkownika stale monitoruje
zachodzce w nim zdarzenia, jak naciskanie klawiszy na klawiaturze czy kliknicia przyciskiem myszy. Informacje o tych zdarzeniach s przesyane do uruchomionych programw.
Nastpnie kady program podejmuje samodzieln decyzj, w jaki sposb, jeli w ogle, zareagowa na te zdarzenia. W takich jzykach jak Visual Basic relacje pomidzy zdarzeniami
a kodem s oczywiste. Programista pisze kod obsugi kadego interesujcego go zdarzenia
i umieszcza go w tzw. procedurze obsugi zdarze (ang. event procedure). Na przykad
z przyciskiem o nazwie HelpButton w jzyku Visual Basic moe by skojarzona procedura
obsugi zdarze o nazwie HelpButton_Click. Kod tej procedury jest wykonywany w odpowiedzi na kade kliknicie tego przycisku. Kady komponent GUI w jzyku Visual Basic
reaguje na ustalony zestaw zdarze nie mona zmieni zdarze, na ktre reaguje dany
komponent.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

356

Java. Podstawy
Natomiast programici czystego jzyka C zajmujcy si zdarzeniami musz pisa procedury
nieprzerwanie monitorujce kolejk zdarze w celu sprawdzenia, jakie powiadomienia przesya system operacyjny (z reguy do tego celu stosuje si ptl zawierajc bardzo rozbudowan instrukcj switch!). Technika ta jest oczywicie bardzo mao elegancka i sprawia wiele
problemw podczas pisania kodu. Jej zalet jest natomiast to, e nie ma adnych ogranicze
dotyczcych zdarze, na ktre mona reagowa, w przeciwiestwie do innych jzykw,
np. Visual Basica, ktre wkadaj bardzo duo wysiku w ukrywanie kolejki zdarze przed
programist.
W rodowisku programistycznym Javy przyjto podejcie porednie pomidzy jzykami Visual
Basic a C, jeli chodzi o oferowane moliwoci, a co za tym idzie take zoono. Poruszajc si w zakresie zdarze, ktre obsuguje biblioteka AWT, programista ma pen kontrol nad sposobem przesyania zdarze ze rde zdarze (ang. event sources), np. przyciskw lub paskw przewijania, do suchaczy zdarze (ang. event listener). Na suchacza
zdarze mona desygnowa kady obiekt w praktyce wybiera si ten obiekt, ktry
z atwoci moe wykona odpowiednie dziaania w odpowiedzi na zdarzenie. Ten delegacyjny model zdarze daje znacznie wiksze moliwoci ni jzyk Visual Basic, w ktrym
suchacz jest ustalony z gry.
rda zdarze dysponuj metodami, w ktrych mona rejestrowa suchaczy zdarze. Kiedy
ma miejsce okrelone zdarzenie, rdo wysya powiadomienie o nim do wszystkich obiektw
nasuchujcych, ktre zostay dla niego zarejestrowane.
Jak mona si spodziewa, informacje o zdarzeniu w jzyku obiektowym, takim jak Java, s
pakowane w obiekcie zdarze (ang. event object). W Javie wszystkie obiekty zdarze nale do klasy java.util.EventObject. Oczywicie istniej te podklasy reprezentujce kady
typ zdarzenia, takie jak ActionEvent czy WindowEvent.
Rne rda zdarze mog dostarcza rnego rodzaju zdarze. Na przykad przycisk moe
wysya obiekty ActionEvent, podczas gdy okno wysya obiekty WindowEvent.
Podsumujmy, co ju wiemy na temat obsugi zdarze w bibliotece AWT:

Obiekt nasuchujcy jest egzemplarzem klasy implementujcej specjalny interfejs


nasuchu (ang. listener interface).

rdo zdarze to obiekt, ktry moe rejestrowa obiekty nasuchujce i wysya


do nich obiekty zdarze.

rdo zdarze wysya obiekty zdarze do wszystkich zarejestrowanych suchaczy


w chwili wystpienia zdarzenia.

Informacje zawarte w obiekcie zdarze s wykorzystywane przez obiekty nasuchujce


przy podejmowaniu decyzji dotyczcej reakcji na zdarzenie.

Rysunek 8.1 przedstawia relacje pomidzy klasami obsugi zdarze a interfejsami.


Poniej znajduje si przykadowa definicja suchacza:
ActionListener listener = . . .;
JButton button = new JButton("OK");
button.addActionListener(listener);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

357

Rysunek 8.1.
Relacje pomidzy
rdami zdarze
a suchaczami

Od tej pory obiekt listener bdzie powiadamiany o kadym zdarzeniu akcji w przycisku.
Jak si mona domyli, zdarzenie akcji w przypadku przycisku to jego kliknicie.
Klasa implementujca interfejs ActionListener musi definiowa metod o nazwie action
Performed, ktra jako parametr przyjmuje obiekt typu ActionEvent:
class MyListener implements ActionListener
{
. . .
public void actionPerformed(ActionEvent event)
{
// Instrukcje wykonywane w odpowiedzi na kliknicie przycisku.
. . .
}
}

Kiedy uytkownik kliknie przycisk, obiekt typu JButton tworzy obiekt typu ActionEvent
i wywouje metod listener.actionPerformed(event), przekazujc do niej ten obiekt zdarzenia. rdo zdarze, takie jak przycisk, moe mie kilka suchaczy. W takim przypadku
kliknicie przycisku przez uytkownika powoduje wywoanie metod actionPerformed wszystkich suchaczy.
Rysunek 8.2 przedstawia relacje pomidzy rdem zdarze, suchaczem zdarze i obiektem zdarze.

8.1.1. Przykad obsuga kliknicia przycisku


Aby nabra biegoci w posugiwaniu si modelem delegacji zdarze, przeanalizujemy szczegowo prosty program reagujcy na kliknicie przycisku. Utworzymy panel zawierajcy trzy
przyciski, ktrych zdarze bd nasuchiwa trzy obiekty nasuchujce.
W tym przypadku za kadym razem, gdy uytkownik kliknie jeden z przyciskw na panelu,
skojarzony z tym przyciskiem obiekt odbierze obiekt typu ActionEvent oznaczajcy kliknicie
przycisku. W odpowiedzi obiekt nasuchujcy zmieni kolor ta panelu.
Przed przejciem do programu, ktry nasuchuje klikni przyciskw, musimy najpierw zapozna si z technik tworzenia i dodawania przyciskw do panelu (wicej informacji na temat
elementw GUI znajduje si w rozdziale 9.).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

358

Java. Podstawy

Rysunek 8.2.
Powiadamianie
o zdarzeniach

Tworzenie przycisku polega na podaniu jego konstruktorowi acucha okrelajcego etykiet


przycisku, ikony lub jednego i drugiego. Poniej znajduj si przykady tworzenia dwch
przyciskw:
JButton yellowButton = new JButton("ty");
JButton blueButton = new JButton(new ImageIcon("blue-ball.gif"));

Przyciski do panelu dodaje si za pomoc metody add:


JButton yellowButton = new JButton("ty");
JButton blueButton = new JButton("Niebieski");
JButton redButton = new JButton("Czerwony");
buttonPanel.add(yellowButton);
buttonPanel.add(blueButton);
buttonPanel.add(redButton);

Wynik powyszych dziaa przedstawia rysunek 8.3.


Nastpnie konieczne jest dodanie procedur nasuchujcych tych przyciskw. Do tego potrzebne
s klasy implementujce interfejs ActionListener, ktry jak ju wspominalimy zawiera
tylko jedn metod: actionPerformed. Sygnatura tej metody jest nastpujca:
public void actionPerformed(ActionEvent event)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

359

Rysunek 8.3.
Panel
z przyciskami

Interfejs ActionListener nie ogranicza si tylko do klikni przyciskw. Znajduje


on zastosowanie w wielu innych sytuacjach, takich jak:

wybr elementu z pola listy za pomoc dwukrotnego kliknicia,

wybr elementu menu,

kliknicie klawisza Enter w polu tekstowym,

upyw okrelonej iloci czasu dla komponentu Timer.

Wicej szczegw na ten temat znajduje si w tym i kolejnym rozdziale.


Sposb uycia interfejsu ActionListener jest taki sam we wszystkich sytuacjach: metoda
actionPerformed (jedyna w interfejsie ActionListener) przyjmuje obiekt typu Action
Event jako parametr. Ten obiekt zdarzenia dostarcza informacji o zdarzeniu, ktre miao
miejsce.

Reakcj na kliknicie przycisku ma by zmiana koloru ta panelu. dany kolor bdziemy


przechowywa w klasie nasuchujcej:
class ColorAction implements ActionListener
{
private Color backgroundColor;
public ColorAction(Color c)
{
backgroundColor = c;
}
public void actionPerformed(ActionEvent event)
{
// Ustawienie koloru ta panelu.
. . .
}
}

Nastpnie dla kadego koloru tworzymy osobny obiekt i kady z nich rejestrujemy jako suchacza przycisku.
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

360

Java. Podstawy
Jeli uytkownik kliknie na przykad przycisk z napisem ty, zostanie wywoana metoda
actionPerformed obiektu yellowAction. Pole backgroundColor tego obiektu ma warto color.
YELLOW.
Zosta jeszcze tylko jeden problem do rozwizania. Obiekt typu ColorAction nie ma dostpu
do zmiennej buttonPanel. Mona to rozwiza na jeden z dwch sposobw. Mona zapisa
panel w obiekcie ColorAction i skonstruowa go w konstruktorze ColorAction. Wygodniej
jednak byoby, gdyby ColorAction bya klas wewntrzn klasy ButtonFrame. Dziki temu
jej metody miayby automatycznie dostp do zewntrznego panelu (wicej informacji na
temat klas wewntrznych znajduje si w rozdziale 6.).
Zastosujemy drug z opisanych metod. Poniej przedstawiamy klas ColorAction wewntrz
klasy ButtonFrame:
class ButtonPanel extends JFrame
{
private JPanel buttonPanel;
. . .
private class ColorAction implements ActionListener
{
private Color backgroundColor;
. . .
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);
}
}
}

Przypatrzmy si uwaniej metodzie actionPerformed. Klasa ColorAction nie posiada pola


buttonPanel. Ma go natomiast zewntrzna klasa ButtonFrame.
Jest to bardzo czsto spotykana sytuacja. Obiekty nasuchu zdarze czsto musz wykonywa
dziaania, ktre maj wpyw na inne obiekty. Klas nasuchujc czsto mona umieci
w strategicznym miejscu wewntrz klasy, ktrej obiekt ma mie zmieniony stan.
Listing 8.1 przedstawia kompletny program. Kliknicie jednego z przyciskw powoduje
zmian koloru ta panelu przez odpowiedniego suchacza akcji.
Listing 8.1. button/ButtonFrame.java
package button;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z panelem zawierajcym przyciski
*/
public class ButtonFrame extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = 300;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.
private static final int DEFAULT_HEIGHT = 200;
public ButtonFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Tworzenie przyciskw
JButton yellowButton = new JButton("ty");
JButton blueButton = new JButton("Niebieski");
JButton redButton = new JButton("Czerwony");
buttonPanel = new JPanel();
// Dodanie przyciskw do panelu
buttonPanel.add(yellowButton);
buttonPanel.add(blueButton);
buttonPanel.add(redButton);
// Dodanie panelu do ramki
add(buttonPanel);
// Utworzenie akcji przyciskw
ColorAction yellowAction = new ColorAction(Color.YELLOW);
ColorAction blueAction = new ColorAction(Color.BLUE);
ColorAction redAction = new ColorAction(Color.RED);
// Powizanie akcji z przyciskami
yellowButton.addActionListener(yellowAction);
blueButton.addActionListener(blueAction);
redButton.addActionListener(redAction);
}
/**
* Suchacz akcji ustawiajcy kolor ta panelu.
*/
private class ColorAction implements ActionListener
{
private Color backgroundColor;
public ColorAction(Color c)
{
backgroundColor = c;
}
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);
}
}
}
javax.swing.JButton 1.2

JButton(String label)

JButton(Icon icon)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Obsuga zdarze

361

362

Java. Podstawy

JButton(String label, Icon icon)

Tworzy przycisk. acuch etykiety moe zawiera sam tekst lub (od Java SE 1.3)
kod HTML, np. "<html><b>Ok</b></html>".
java.awt.Container 1.0

Component add(Component c)

Dodaje komponent c do kontenera.

8.1.2. Nabywanie biegoci w posugiwaniu si klasami wewntrznymi


Niektrzy programici nie przepadaj za klasami wewntrznymi, poniewa uwaaj, e klasy
i obiekty o duych rozmiarach spowalniaj dziaanie programu. Przyjrzyjmy si temu twierdzeniu. Nie potrzebujemy nowej klasy dla kadego elementu interfejsu uytkownika. W naszym
programie wszystkie trzy przyciski wspdziel jedn klas nasuchujc. Oczywicie kady
z nich posiada osobny obiekt nasuchujcy. Ale obiekty te nie s due. Kady z nich zawiera
warto okrelajc kolor i referencj do panelu. A tradycyjne rozwizanie z zastosowaniem
instrukcji if-else rwnie odwouje si do tych samych obiektw kolorw przechowywanych przez suchaczy akcji, tylko e jako zmienne lokalne, a nie pola obiektw.
Poniej przedstawiamy dobry przykad tego, jak anonimowe klasy wewntrzne mog uproci kod programu. W programie na listingu 8.1 z kadym przyciskiem zwizane s takie
same dziaania:
1.

Utworzenie przycisku z etykiet.

2. Dodanie przycisku do panelu.


3. Utworzenie suchacza akcji z odpowiednim kolorem.
4. Dodanie suchacza akcji.

Napiszemy metod pomocnicz, ktra bdzie upraszczaa te czynnoci:


public void makeButton(String name, Color backgroundColor)
{
JButton button = new JButton(name);
buttonPanel.add(button);
ColorAction action = new ColorAction(backgroundColor);
button.addActionListener(action);
}

Teraz wystarcz tylko nastpujce wywoania:


makeButton("ty", Color.YELLOW);
makeButton("Niebieski", Color.BLUE);
makeButton("Czerwony", Color.RED);

Moliwe s dalsze uproszczenia. Zauwamy, e klasa ColorAction jest potrzebna tylko jeden
raz w metodzie makeButton. A zatem mona j przerobi na klas anonimow:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

363

public void makeButton(String name, final Color backgroundColor)


{
JButton button = new JButton(name);
buttonPanel.add(button);
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);
}
});
}

Kod suchacza akcji sta si znacznie prostszy. Metoda actionPerformed odwouje si do zmiennej parametrycznej backgroundColor (podobnie jak w przypadku wszystkich zmiennych lokalnych wykorzystywanych w klasie wewntrznej, parametr ten musi by finalny).
Nie jest potrzebny aden jawny konstruktor. Jak widzielimy w rozdziale 6., mechanizm klas
wewntrznych automatycznie generuje konstruktor zapisujcy wszystkie finalne zmienne
lokalne, ktre s uywane w jednej z metod klasy wewntrznej.
Anonimowe klasy wewntrzne potrafi zmyli niejednego programist. Mona jednak
przyzwyczai si do ich rozszyfrowywania, wyrabiajc sobie umiejtno pomijania wzrokiem kodu procedury:
button.addActionListener(new ActionListener()

{
public void actionPerformed(ActionEvent event)
{
buttonPanel.setBackground(backgroundColor);

}
});
Akcja przycisku ustawia kolor ta. Dopki procedura obsugi zdarze skada si z tylko
kilku instrukcji, wydaje si, e z odczytem nie powinno by problemw, zwaszcza jeli
w sferze naszych zainteresowa nie le mechanizmy klas wewntrznych.
java.util.EventObject 1.1

Object setSource()

Zwraca referencj do obiektu, w ktrym wystpio zdarzenie.


java.awt.event.ActionEvent 1.1

String getActionCommand()

Zwraca acuch polecenia skojarzonego z danym zdarzeniem akcji. Jeli zdarzenie


pochodzi od przycisku, acuch polecenia jest taki sam jak etykieta przycisku,
chyba e zosta zmieniony za pomoc metody setActionCommand.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

364

Java. Podstawy

Suchaczem przycisku moe by obiekt kadej klasy, ktra implementuje interfejs


ActionListener. My wolimy uywa obiektw nowej klasy, ktra zostaa utworzona
specjalnie z myl o wykonywaniu akcji przycisku. Jednak niektrzy programici nie czuj
si pewnie w stosowaniu klas wewntrznych i wybieraj inne podejcie. Implementuj
interfejs ActionListener w kontenerze rde zdarze. Nastpnie kontener ten ustawia
sam siebie jako suchacza w nastpujcy sposb:
yellowButton.addActionListener(this);
blueButton.addActionListener(this);
redButton.addActionListener(this);

W tej sytuacji aden z trzech przyciskw nie ma osobnego suchacza. Dysponuj one
wsplnym obiektem, ktrym jest ramka przycisku. W zwizku z tym metoda actionPer
formed musi sprawdzi, ktry przycisk zosta kliknity.
class ButtonFrame extends JFrame implements ActionListener
{
. . .
public void actionPerformed(ActionEvent event)
{
Object source = event.getSource();
if (source == yellowButton) . . .
else if (source == blueButton) . . .
else if (source == redButton ) . . .
else . . .
}
}

Jak wida, metoda ta jest nieco zagmatwana, przez co nie zalecamy jej stosowania.

8.1.3. Tworzenie suchaczy zawierajcych jedno wywoanie metody


Prostych suchaczy zdarze mona tworzy bez uycia klas wewntrznych. Wyobramy
sobie na przykad, e mamy przycisk z etykiet Load, ktrego procedura obsugi zdarze
zawiera tylko jedn metod:
frame.loadData();

Oczywicie mona uy anonimowej klasy wewntrznej:


loadButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
frame.loadData();
}
});

Jednak klasa EventHandler moe utworzy takiego suchacza automatycznie za pomoc nastpujcego wywoania:
EventHandler.create(ActionListener.class, frame, "loadData")

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

365

Oczywicie nadal konieczne jest zainstalowanie procedury obsugi:


loadButton.addActionListener(
EventHandler.create(ActionListener.class, frame, "loadData"));

Jeli suchacz wywouje metod z jednym parametrem, ktry mona uzyska z parametru
zdarzenia, mona uy innego rodzaju metody create. Na przykad wywoanie:
EventHandler.create(ActionListener.class, frame, "loadData", "source.text")

jest rwnoznaczne z:
new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
frame.loadData(((JTextField) event.getSource()).getText());
}
}

Nazwy wasnoci source i text zamieniaj si w wywoania metod getSource i getText.


Drugi argument w wywoaniu EventHandler.create musi nalee do klasy publicznej. W przeciwnym razie mechanizm refleksji nie bdzie w stanie znale i wywoa docelowej metody.
java.beans.EventHandler 1.4

static Object create(Class listenerInterface, Object target, String action)

static Object create(Class listenerInterface, Object target, String action,


String eventProperty)

static Object create(Class listenerInterface, Object target, String action,


String eventProperty, String listenerMethod)

Tworzy obiekt klasy poredniczcej implementujcej dany interfejs. Albo podana


metoda, albo wszystkie metody interfejsu wykonuj dane akcje na rzecz obiektu
docelowego.
Akcj moe by metoda lub wasno obiektu docelowego. Jeli jest to wasno,
wykonywana jest jej metoda ustawiajca. Na przykad akcja text jest zamieniana
na wywoanie metody setText.
Wasno zdarzenia skada si z jednej lub wikszej liczby nazw wasnoci
oddzielonych kropkami. Pierwsza wasno jest wczytywana z parametru metody
nasuchujcej. Druga wasno pochodzi od powstaego obiektu itd. Wynik kocowy
staje si parametrem akcji. Na przykad wasno source.text jest zamieniana
na wywoania metod getSource i getText.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

366

Java. Podstawy

8.1.4. Przykad zmiana stylu


Domylnym stylem programw pisanych przy uyciu Swinga jest Metal. Istniej dwa sposoby na zmian stylu. Pierwszy z nich polega na utworzeniu pliku swing.properties w katalogu
jre/lib w miejscu instalacji Javy. W pliku tym naley ustawi wasno swing.defaultlaf na
nazw klasy stylu, ktry chcemy zastosowa. Na przykad:
swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel

Zauwamy, e styl Metal jest zlokalizowany w pakiecie javax.swing. Pozostae style znajduj si w pakiecie com.sun.java i nie musz by obecne w kadej implementacji Javy.
Obecnie ze wzgldu na prawa autorskie pakiety stylw systemw Windows i Mac OS X s
dostpne wycznie z wersjami rodowiska uruchomieniowego Javy przeznaczonymi dla
tych systemw.
Poniewa w plikach wasnoci linie zaczynajce si od znaku # s ignorowane,
mona w takim pliku umieci kilka stylw i wybiera je wedle upodobania, odpowiednio zmieniajc pooenie znaku #:
#swing.defaultlaf=javax.swing.plaf.metal.MetalLookAndFeel
swing.defaultlaf=com.sun.java.swing.plaf.motif.MotifLookAndFeel
#swing.defaultlaf=com.sun.java.swing.plaf.windows.WindowsLookAndFeel

Aby zmieni styl w ten sposb, konieczne jest ponowne uruchomienie programu. Programy
Swing wczytuj plik swing.properties tylko jeden raz przy uruchamianiu.

Drugi sposb polega na dynamicznej zmianie stylu. Naley wywoa statyczn metod
UIManager.setLookAndFeel oraz przekaza do niej nazw klasy wybranego stylu. Nastpnie
wywoujemy statyczn metod SwingUtilities.updateComponentTreeUI w celu odwieenia
caego zbioru komponentw. Metodzie tej wystarczy przekaza tylko jeden komponent,
a pozostae znajdzie ona samodzielnie. Metoda UIManager.setLookAndFeel moe spowodowa kilka wyjtkw, jeli nie znajdzie danego stylu lub jeli wystpi bd podczas adowania
stylu. Jak zwykle nie zgbiamy kodu obsugujcego wyjtki, poniewa szczegowo zajmiemy
si tym w rozdziale 11.
Poniszy przykadowy fragment programu przedstawia sposb przeczenia na styl Motif:
String plaf = "com.sun.java.swing.plaf.motif.MotifLookAndFeel";
try
{
UIManager.setLookAndFeel(plaf);
SwingUtilities.updateComponentTreeUI(panel);
}
catch(Exception e) { e.printStackTrace(); }

Aby odnale wszystkie zainstalowane style, naley uy wywoania:


UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();

W takiej sytuacji nazw kadego stylu i jego klasy mona uzyska nastpujco:
String name = infos[i].getName();
String className = infos[i].getClassName();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

367

Listing 8.2 przedstawia peny kod programu demonstrujcego przeczanie stylw (zobacz
rysunek 8.4). Program ten jest podobny do programu z listingu 8.1. Idc za rad z poprzedniej sekcji, akcj przycisku, polegajc na zmianie stylu, okrelilimy za pomoc metody
pomocniczej makeButton i anonimowej klasy wewntrznej.
Listing 8.2. plaf/PlafFrame.java
package plaf;
import java.awt.event.*;
import javax.swing.*;

/**
* Ramka z panelem zawierajcym przyciski zmieniajce styl.
*/
public class PlafFrame extends JFrame
{
private JPanel buttonPanel;
public PlafFrame()
{
buttonPanel = new JPanel();
UIManager.LookAndFeelInfo[] infos = UIManager.getInstalledLookAndFeels();
for (UIManager.LookAndFeelInfo info : infos)
makeButton(info.getName(), info.getClassName());
add(buttonPanel);
pack();
}
/**
* Tworzy przycisk zmieniajcy styl.
* @param name nazwa przycisku
* @param plafName nazwa klasy stylu
*/
void makeButton(String name, final String plafName)
{
// Dodanie przycisku do panelu.
JButton button = new JButton(name);
buttonPanel.add(button);
// Ustawienie akcji przycisku.
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Akcja przycisku przeczenie na nowy styl.
try
{
UIManager.setLookAndFeel(plafName);
SwingUtilities.updateComponentTreeUI(PlafFrame.this);
pack();
}
catch (Exception e)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

368

Java. Podstawy

Rysunek 8.4.
Zmienianie stylu

e.printStackTrace();
}
}
});
}
}

Program ten ma jeden interesujcy fragment. Metoda actionPerformed wewntrznej klasy


nasuchowej musi przekaza referencj this zewntrznej klasy PlafFrame do metody update
ComponentTreeUI. Przypomnijmy z rozdziau 6., e wskanik this zewntrznego obiektu
naley oznaczy przedrostkiem w postaci nazwy klasy zewntrznej:
SwingUtilities.updateComponentTreeUI(PlafPanel.this);
javax.swing.UIManager 1.2

static UIManager.LookAndFeelInfo[] getInstalledLookAndFeels()

Tworzy tablic obiektw reprezentujcych zainstalowane style.

static setLookAndFeel(String className)

Ustawia aktualny styl, wykorzystujc do tego podan nazw klasy


(np. javax.swing.plaf.metal.MetalLookAndFeel).
javax.swing.UIManager.LookAndFeelInfo 1.2

String getName()

Zwraca nazw stylu.

String getClassName()

Zwraca nazw klasy implementujcej dany styl.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

369

8.1.5. Klasy adaptacyjne


Nie wszystkie zdarzenia s tak atwe w obsudze jak kliknicie przycisku. W profesjonalnym
programie naley stale sprawdza, czy uytkownik nie zamyka gwnej ramki, aby zapobiec
ewentualnej utracie jego danych. Gdy uytkownik zamyka ramk, powinno wywietla si okno
dialogowe monitujce o potwierdzenie tego zamiaru.
Kiedy uytkownik zamyka okno, obiekt klasy JFrame jest rdem zdarzenia WindowEvent.
Aby przechwyci to zdarzenie, konieczny jest odpowiedni obiekt nasuchujcy, ktry naley
doda do listy suchaczy okna ramki.
WindowListener listener = . . .;
frame.addWindowListener(listener);

Obiekt nasuchujcy okna musi nalee do klasy implementujcej interfejs WindowListener.


Interfejs ten zawiera siedem metod. Ramka wywouje jedn z nich w odpowiedzi na jedno
z siedmiu zdarze, ktre mog mie miejsce w przypadku okna. Nazwy tych metod mwi
same za siebie. Naley tylko wyjani, e iconified w systemie Windows oznacza to samo
co minimized. Poniej wida cay interfejs WindowListener:
public interface WindowListener
{
void windowOpened(WindowEvent e);
void windowClosing(WindowEvent e);
void windowClosed(WindowEvent e);
void windowIconified(WindowEvent e);
void windowDeiconified(WindowEvent e);
void windowActivated(WindowEvent e);
void windowDeactivated(WindowEvent e);
}

Aby sprawdzi, czy okno zostao zmaksymalizowane, naley zainstalowa obiekt


WindowStateListener zobacz wycig z API na kocu tej sekcji.

W Javie klasa, ktra implementuje dany interfejs, musi definiowa wszystkie jego metody.
W tym przypadku oznacza to implementacj siedmiu metod. Przypomnijmy jednak, e interesuje nas tylko jedna z nich, o nazwie windowClosing.
Oczywicie nic nie stoi na przeszkodzie, aby zaimplementowa ten interfejs, wstawi wywoanie System.exit(0) do metody windowClosing i napisa sze nicnierobicych funkcji dla
pozostaych metod:
class Terminator implements WindowListener
{
public void windowClosing(WindowEvent e)
{
if (uytkownik potwierdza)
System.exit(0);
}
public void windowOpened(WindowEvent e) {}
public void windowClosed(WindowEvent e) {}
public void windowIconified(WindowEvent e) {}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

370

Java. Podstawy
public void windowDeiconified(WindowEvent e) {}
public void windowActivated(WindowEvent e) {}
public void windowDeactivated(WindowEvent e) {}
}

Pisanie szeciu metod, ktre nic nie robi, jest tym rodzajem pracy, ktrej nikt nie lubi. Zadanie to uatwiaj klasy adaptacyjne (ang. adapter class) dostpne z kadym interfejsem nasuchujcym w bibliotece AWT, ktry ma wicej ni jedn metod. Klasy te implementuj
wszystkie metody interfejsw, ktrym odpowiadaj, ale metody te nic nie robi. Na przykad klasa WindowAdapter zawiera definicje siedmiu nicnierobicych metod. Oznacza to, e
klasa adaptacyjna automatycznie spenia wymagania techniczne stawiane przez Jav, a dotyczce implementacji odpowiadajcego jej interfejsu nasuchujcego. Klas adaptacyjn mona
rozszerzy, definiujc w podklasie metody odpowiadajce niektrym, ale nie wszystkim typom
zdarze interfejsu (interfejsy, ktre maj tylko jedn metod, np. ActionListener, nie potrzebuj
metod adaptacyjnych).
Rozszerzymy klas WindowAdapter. Odziedziczymy po niej sze nicnierobicych metod,
a metod windowClosing przesonimy:
class Terminator extends WindowAdapter
{
public void windowClosing(WindowEvent e)
{
if (uytkownik potwierdza)
System.exit(0);
}
}

Teraz moemy zarejestrowa obiekt typu Terminator jako suchacza zdarze:


WindowListener listener = new Terminator();
frame.addWindowListener(listener);

Kade zdarzenie okna wygenerowane przez ramk jest przekazywane do obiektu listener za
pomoc wywoania jednej z jego siedmiu metod (zobacz rysunek 8.5). Sze z nich nie robi nic,
a metoda windowClosing wywouje metod System.exit(0), zamykajc tym samym aplikacj.
Jeli w nazwie metody rozszerzanej klasy adaptacyjnej znajdzie si bd, kompilator go nie wykryje. Jeli na przykad w klasie rozszerzajcej WindowAdapter zostanie
zdefiniowana metoda windowIsClosing, nowa klasa bdzie zawieraa osiem metod, a metoda windowClosing nie bdzie nic robia.

Utworzenie klasy rozszerzajcej klas adaptacyjn WindowAdapter jest krokiem naprzd, ale
mona posun si jeszcze dalej. Nie ma potrzeby nadawa obiektowi listener nazwy.
Wystarczy napisa:
frame.addWindowListener(new Terminator());

Ale czemu poprzestawa na tym? Klasa nasuchujca moe by anonimow klas wewntrzn
ramki.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

371

Rysunek 8.5.
Obiekt
nasuchujcy
zdarze
dotyczcych okna

frame.addWindowListener(new
WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
if (uytkownik potwierdza)
System.exit(0);
}
});

Powyszy fragment programu ma nastpujce dziaanie:

Definiuje klas bez nazwy, rozszerzajc klas WindowAdapter.

Do utworzonej anonimowej klasy dodaje metod windowClosing


(podobnie jak wczeniej, metoda ta zamyka program).

Dziedziczy sze pozostaych nicnierobicych metod po klasie WindowAdapter.

Tworzy obiekt tej nowej klasy obiekt rwnie nie ma nazwy.

Przekazuje ten obiekt do metody addWindowListener.

Powtarzamy jeszcze raz, e do skadni wewntrznych klas anonimowych trzeba si przyzwyczai. Dziki nim mona pisa tak zwizy kod, jak to tylko moliwe.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

372

Java. Podstawy
java.awt.event.WindowListener 1.1

void windowOpened(WindowEvent e)

Jest wywoywana po otwarciu okna.

void windowClosing(WindowEvent e)

Jest wywoywana, kiedy uytkownik wyda polecenie menedera okien,


aby zamkn okno. Okno zostanie zamknite tylko wtedy, gdy zostanie
wywoana jego metoda hide lub dispose.

void windowClosed(WindowEvent e)

Jest wywoywana po zamkniciu okna.

void windowIconified(WindowEvent e)

Jest wywoywana po zminimalizowaniu okna.

void windowDeiconified(WindowEvent e)

Jest wywoywana po przywrceniu okna.

void windowActivated(WindowEvent e)

Jest wywoywana po uaktywnieniu okna. Aktywna moe by tylko ramka


lub okno dialogowe. Z reguy meneder okien zaznacza w jaki sposb aktywne
okno, np. podwietlajc pasek tytuu.

void WindowDeactivated(WindowEvent e)

Jest wywoywana po dezaktywowaniu okna.


java.awt.event.WindowStateListener 1.4

void windowStateChanged(WindowEvent event)

Jest wywoywana po zmaksymalizowaniu, zminimalizowaniu lub przywrceniu


okna do normalnego rozmiaru.
java.awt.event.WindowEvent 1.1

int getNewState() 1.4

int getOldState() 1.4

Zwraca nowy i stary stan okna w zdarzeniu zmiany stanu okna. Zwracana liczba
cakowita jest jedn z nastpujcych wartoci:
Frame.NORMAL
Frame.ICONIFIED
Frame.MAXIMIZED_HORIZ
Frame.MAXIMIZED_VERT
Frame.MAXIMIZED_BOTH

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

373

8.2. Akcje
Czsto jedn opcj mona wybra na kilka rnych sposobw. Uytkownik moe wybra
odpowiedni funkcj w menu, nacisn okrelony klawisz lub przycisk na pasku narzdzi.
Zaprogramowanie takiej funkcjonalnoci w modelu zdarze AWT jest proste naley
wszystkie zdarzenia zwiza z tym samym obiektem nasuchujcym. Wyobramy sobie, e
blueAction jest obiektem nasuchujcym akcji, ktrego metoda actionPerformed zmienia
kolor ta na niebieski. Jeden obiekt mona zwiza jako suchacza z kilkoma rdami zdarze:

przyciskiem paska narzdzi z etykiet Niebieski;

elementem menu z etykiet Niebieski;

skrtem klawiszowym Ctrl+N.

Dziki temu zmiana koloru bdzie wykonywana zawsze w taki sam sposb, bez znaczenia,
czy wywoa j kliknicie przycisku, wybr elementu menu, czy nacinicie klawisza.
W pakiecie Swing dostpna jest niezwykle przydatna struktura opakowujca polecenia i wica je z rnymi rdami zdarze interfejs Action. Akcja to obiekt, ktry opakowuje:

opis polecenia (acuch tekstowy i opcjonalna ikona),

parametry niezbdne do wykonania polecenia (w naszym przypadku


wymagany kolor).

Interfejs Action zawiera nastpujce metody:


void actionPerformed(ActionEvent event)
void setEnabled(boolean b)
boolean isEnabled()
void putValue(String key, Object value)
Object getValue(String key)
void addPropertyChangeListener(PropertyChangeListener listener)
void removePropertyChangeListener(PropertyChangeListener listener)

Pierwsza z tych metod jest ju nam znana z interfejsu ActionListener. Naley doda, e interfejs Action rozszerza interfejs ActionListener. W zwizku z tym wszdzie, gdzie powinien
si znale obiekt ActionListener, mona uy obiektu Action.
Dwie kolejne metody wczaj i wyczaj akcj oraz sprawdzaj, czy akcja jest aktualnie
wczona. Kiedy akcja jest zwizana z menu lub paskiem narzdzi i jest wyczona, odpowiadajca jej opcja ma kolor szary.
Metody putValue i getValue zapisuj i pobieraj pary nazwa warto z obiektu akcji. Nazwy
akcji i ikony s zapisywane w obiektach akcji za pomoc dwch predefiniowanych acuchw: Action.NAME i Action.SMALL_ICON:
action.putValue(Action.NAME, "Niebieski");
action.putValue(Action.SMALL_ICON, new ImageIcon("blue-ball.gif"));

Tabela 8.1 przedstawia zestawienie wszystkich predefiniowanych nazw tablicowych akcji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

374

Java. Podstawy

Tabela 8.1. Predefiniowane stae interfejsu Action


Nazwa

Warto

NAME

Nazwa akcji wywietlana na przyciskach i elementach menu

SMALL_ICON

Maa ikona moe by wywietlana na przyciskach, pasku narzdzi


lub elementach menu

SHORT_DESCRIPTION

Krtki opis ikony wywietlany w etykiecie narzdzia

LONG_DESCRIPTION

Dugi opis ikony do uytku w pomocy internetowej; aden komponent Swinga


nie uywa tej wartoci

MNEMONIC_KEY

Skrt akcji wywietlany na elementach menu (zobacz rozdzia 9.)

ACCELERATOR_KEY

Skrt klawiaturowy; aden komponent Swinga nie uywa tej wartoci

ACTION_COMMAND_KEY

Uywana w przestarzaej ju metodzie registerKeyboardAction

DEFAULT

Wasno pasujca do wszystkiego; aden komponent Swinga nie uywa tej wartoci

Jeli obiekt akcji jest dodawany do menu lub paska narzdzi, jego nazwa i ikona s automatycznie pobierane i wywietlane w menu lub na pasku narzdzi. Warto wasnoci SHORT_DES
CRIPTION zamienia si w dymek opisujcy narzdzie.
Pozostae dwie metody interfejsu Action umoliwiaj powiadamianie innych obiektw, zwaszcza menu i paskw narzdzi, ktre s rdem akcji, o zmianach wasnoci obiektu akcji.
Jeli na przykad menu jest dodawane jako obiekt nasuchujcy zmian wasnoci obiektu
akcji i obiekt ten zostanie nastpnie wyczony, menu zostanie wywoane, a nazwa akcji bdzie
szara. Obiekty nasuchu zmian wasnoci s ogln konstrukcj stanowic cz modelu
komponentw JavaBean. Wicej informacji na temat Beanw i ich wasnoci znajduje si
w drugim tomie.
Nie naley zapomina, e Action to interfejs, a nie klasa. Kada klasa implementujca go
musi definiowa wszystkie siedem metod, ktre opisalimy. Na szczcie jaki dobry czowiek napisa klas o nazwie AbstractAction, ktra implementuje wszystkie te metody z wyjtkiem actionPerformed. Klasa ta zajmuje si zapisywaniem par nazwa warto i zarzdzaniem obiektami nasuchujcymi zmian wasnoci. Wystarczy rozszerzy klas AbstractAction
i zdefiniowa metod actionPerformed.
Utworzymy obiekt wykonujcy polecenia zmiany koloru. Zapiszemy nazw polecenia, ikon
i dany kolor. Kolor zapiszemy w tablicy par nazwa warto dostarczanej przez klas
AbstractAction. Poniej znajduje si kod rdowy klasy ColorAction. Konstruktor ustawia
pary nazwa warto, a metoda actionPerformed wykonuje akcj zmiany koloru.
public class ColorAction extends AbstractAction
{
public ColorAction(String name, Icon icon, Color c)
{
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);
putValue("color", c);
putValue(Action.SHORT_DESCRIPTION, "Ustaw kolor panelu na " + name.toLowerCase());
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

375

public void actionPerformed(ActionEvent event)


{
Color c = (Color) getValue("color");
buttonPanel.setBackground(c);
}
}

Nasz przykadowy program tworzy trzy obiekty tej klasy, np.:


Action blueAction = new ColorAction("Niebieski", new ImageIcon("blue-ball.gif"),
Color.BLUE);

Teraz konieczne jest zwizanie akcji z przyciskiem. Jest to atwe, poniewa moemy uy
konstruktora JButton, ktry przyjmuje obiekt typu Action.
JButton blueButton = new JButton(blueAction);

Konstruktor odczytuje nazw i ikon z akcji, ustawia krtki opis jako etykiet oraz ustawia
akcj jako suchacza. Ikony i etykiet przedstawia rysunek 8.6.
Rysunek 8.6.
Przyciski
zawieraj ikony
z obiektw akcji

W kolejnym rozdziale wykaemy, e rwnie atwe jest dodawanie tej samej akcji do menu.
Na koniec przypiszemy obiekty akcji do klawiszy, dziki czemu akcje te bd wykonywane,
kiedy uytkownik wpisze polecenia z klawiatury. Kojarzenie akcji z klawiszami naley zacz
od wygenerowania obiektu klasy KeyStroke. Klasa ta opakowuje opis klawisza. Do utworzenia obiektu typu KeyStroke nie uywa si konstruktora, ale statycznej metody getKey
Stroke klasy KeyStroke.
KeyStroke ctrlBKey = KeyStroke.getKeyStroke("ctrl N");

Do zrozumienia nastpnego etapu potrzebna jest znajomo pojcia aktywnoci komponentu (ang. keyboard focus). Interfejs uytkownika moe si skada z wielu przyciskw,
menu, paskw przewijania i innych komponentw. Kiedy zostanie nacinity klawisz, zdarzenie to zostaje wysane do aktywnego komponentu. Komponent ten jest z reguy (cho
nie zawsze) w jaki sposb wizualnie wyrniony. Na przykad w stylu Javy tekst na aktywnym przycisku ma cienk obwdk. Fokus (aktywno komponentu) mona przenosi na
rne komponenty za pomoc klawisza Tab. Nacinicie klawisza spacji powoduje kliknicie
aktywnego przycisku. Inne klawisze wywouj inne dziaania. Na przykad klawisze strzaek mog sterowa paskiem przewijania.
Jednak my nie chcemy wysya zdarzenia nacinicia klawisza do aktywnego komponentu.
W przeciwnym razie kady przycisk musiaby zna procedur obsugi kombinacji klawiszy
Ctrl+Y, Ctrl+B i Ctrl+R.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

376

Java. Podstawy
Jest to bardzo powszechny problem. Jednak projektanci biblioteki Swing znaleli dla niego
proste rozwizanie. Kady JComponent posiada trzy mapy wejcia (ang. input maps), z ktrych kada odwzorowuje obiekty KeyStroke na zwizane z nimi akcje. Mapy te odpowiadaj trzem rnym sytuacjom (zobacz tabela 8.2).

Tabela 8.2. Mapy klawiaturowe


Znacznik

Wywouje dziaanie, gdy

WHEN_FOCUSED

komponent jest aktywny

WHEN_ANCESTOR_OF_FOCUSED_COMPONENT

komponent zawiera komponent aktywny

WHEN_IN_FOCUSED_WINDOW

komponent znajduje si w tym samym oknie co komponent aktywny

Powysze mapy s sprawdzane w nastpujcej kolejnoci w wyniku nacinicia klawisza:


1.

Sprawdzenie mapy WHEN_FOCUSED aktywnego komponentu. Jeli dany skrt


klawiaturowy istnieje, nastpuje wykonanie powizanego z nim dziaania.
Jeli dziaanie zostaje wykonane, nastpuje zatrzymanie sprawdzania warunkw.

2. Nastpuje sprawdzenie map WHEN_ANCESTOR_OF_FOCUSED_COMPONENT aktywnego

komponentu, a nastpnie jego komponentw nadrzdnych. Gdy zostanie


znaleziona mapa z danym skrtem klawiaturowym, nastpuje wykonanie
dziaania. Jeli dziaanie zostaje wykonane, nastpuje zatrzymanie sprawdzania
warunkw.
3. Odszukanie wszystkich widocznych i wczonych komponentw w aktywnym
oknie, w ktrych mapie WHEN_IN_FOCUSED_WINDOW znajduje si dany skrt

klawiaturowy. Umoliwienie tym komponentom (w kolejnoci zgodnej


z rejestracj zdarze nacinicia klawisza) wykonania odpowiednich dziaa.
Po wykonaniu pierwszego dziaania nastpuje zatrzymanie przetwarzania. Ta cz
procesu moe by rdem problemw, jeli dany skrt klawiaturowy pojawia si
w wicej ni jednej mapie WHEN_IN_FOCUSED_WINDOW.
Map wejcia komponentu tworzy si za pomoc metody getInputMap. Na przykad:
InputMap imap = panel.getInputMap(JComponent.WHEN_FOCUSED);

Warunek WHEN_FOCUSED powoduje, e ta mapa bdzie sprawdzana, gdy komponent jest aktywny.
Nam potrzebna jest inna mapa. Aktywny jest jeden z przyciskw, nie panel. Do wstawienia
skrtw klawiszy zmieniajcych kolor nadaje si jedna z pozostaych dwch map. W naszym
przykadowym programie uyjemy mapy WHEN_ANCESTOR_OF_FOCUSED_COMPONENT.
Klasa InputMap nie odwzorowuje bezporednio obiektw KeyStroke w postaci obiektw Action.
W zamian odwzorowuje w postaci dowolnych obiektw, a druga mapa, zaimplementowana
w klasie ActionMap, mapuje obiekty na akcje. Dziki temu atwiej jest wspdzieli te same
akcje przez skrty klawiaturowe pochodzce z rnych map wejcia.
A zatem kady komponent posiada trzy mapy wejcia i jedn map akcji (ang. action map).
Aby je powiza, trzeba wymyli nazwy dla akcji. Klawisz mona powiza z akcj w nastpujcy sposb:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

377

imap.put(KeyStroke.getKeyStroke("ctrl Z"), "panel.yellow");


ActionMap amap = panel.getActionMap();
amap.put("panel.yellow", yellowAction);

W przypadku akcji niewykonujcej adnych dziaa zwyczajowo stosuje si acuch none.


W ten sposb mona atwo dezaktywowa klawisz:
imap.put(KeyStroke.getKeyStroke("ctrl C"), "none");

Dokumentacja JDK zaleca stosowanie jako klucza akcji jej nazwy. Naszym zdaniem
nie jest to dobre rozwizanie. Nazwa akcji jest wywietlana na przyciskach i elementach menu, w zwizku z czym moe si zmienia w zalenoci od kaprysu projektanta interfejsu oraz moe by przetumaczona na wiele jzykw. Takie niestae acuchy
nie s dobrym wyborem w przypadku klawiszy wyszukiwania. Zalecamy wymylenie nazw
akcji niezalenych od wywietlanych nazw.

Poniej znajduje si zestawienie dziaa, ktre trzeba wykona, aby wywoa to samo dziaanie
w odpowiedzi na zdarzenie nacinicia przycisku, wyboru elementu z menu lub nacinicia
klawisza:
1.

Utwrz podklas klasy AbstractAction. Mona uy tej samej klasy dla wielu
spokrewnionych akcji.

2. Utwrz obiekt powyszej klasy akcji.


3. Utwrz przycisk lub element menu z obiektu powyszej klasy akcji. Konstruktor

odczyta etykiet i ikon z tego obiektu.


4. W przypadku akcji uruchamianych przez nacinicie klawisza konieczne jest

wykonanie dodatkowych czynnoci. Najpierw naley zlokalizowa komponent


najwyszego poziomu w oknie, np. panel zawierajcy wszystkie pozostae elementy.
5. Pobierz map WHEN_ANCESTOR_OF_FOCUSED_COMPONENT komponentu najwyszego
poziomu. Utwrz obiekt klasy KeyStroke reprezentujcy odpowiedni skrt

klawiaturowy. Utwrz obiekt bdcy kluczem dziaania, np. acuch opisujcy


akcj. Wstaw t par danych (klawisz, klucz dziaania) do mapy wejcia.
6. Pobierz map akcji komponentu najwyszego poziomu. Dodaj par klucz

akcji obiekt akcji do tej mapy.


Listing 8.3 przedstawia kompletny kod programu mapujcego przyciski i klawisze na obiekty
akcji. Mona go wyprbowa kliknicie jednego z przyciskw lub nacinicie kombinacji
klawiszy Ctrl+Z, Ctrl+N lub Ctrl+C spowoduje zmian koloru panelu.
Listing 8.3. action/ActionFrame.java
package action;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z panelem, ktry demonstruje akcje zmiany koloru.
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

378

Java. Podstawy
public class ActionFrame extends JFrame
{
private JPanel buttonPanel;
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public ActionFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
buttonPanel = new JPanel();
// Definicje akcji
Action yellowAction = new ColorAction("ty",
new ImageIcon("yellow-ball.gif"),
Color.YELLOW);
Action blueAction = new ColorAction("Niebieski",
new ImageIcon("blue-ball.gif"), Color.BLUE);
Action redAction = new ColorAction("Czerwony",
new ImageIcon("red-ball.gif"), Color.RED);
// Dodanie przyciskw dla akcji
buttonPanel.add(new JButton(yellowAction));
buttonPanel.add(new JButton(blueAction));
buttonPanel.add(new JButton(redAction));
// Dodanie panelu do ramki
add(buttonPanel);
// Powizanie klawiszy Z, N i C z nazwami
InputMap imap = buttonPanel.getInputMap(JComponent.WHEN_ANCESTOR_OF_
FOCUSED_COMPONENT);
imap.put(KeyStroke.getKeyStroke("ctrl Z"), "panel.yellow");
imap.put(KeyStroke.getKeyStroke("ctrl N"), "panel.blue");
imap.put(KeyStroke.getKeyStroke("ctrl C"), "panel.red");
// Powizanie nazw z akcjami
ActionMap amap = buttonPanel.getActionMap();
amap.put("panel.yellow", yellowAction);
amap.put("panel.blue", blueAction);
amap.put("panel.red", redAction);
}
public class ColorAction extends AbstractAction
{
/**
* Tworzy akcj zmiany koloru.
* @param name nazwa, ktra pojawi si na przycisku
* @param icon ikona, ktra pojawi si na przycisku
* @param c kolor ta
*/
public ColorAction(String name, Icon icon, Color c)
{
putValue(Action.NAME, name);
putValue(Action.SMALL_ICON, icon);
putValue(Action.SHORT_DESCRIPTION, "Ustaw kolor panelu na " +
name.toLowerCase());

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

putValue("color", c);
}
public void actionPerformed(ActionEvent event)
{
Color c = (Color) getValue("color");
buttonPanel.setBackground(c);
}
}
}
javax.swing.Action 1.2

boolean isEnabled()

void setEnabled(boolean b)

Pobiera lub ustawia wasno enabled akcji.

void putValue(String key, Object value)

Wstawia par nazwa warto do obiektu akcji.


Parametry:

key

Nazwa wasnoci, ktra ma zosta zapisana


z obiektem akcji. Moe to by dowolny acuch,
ale jest kilka nazw o z gry zdefiniowanym
znaczeniu zobacz tabel 8.1.

value

Obiekt powizany z nazw.

Object getValue(String key)

Zwraca warto z zapisanej pary nazwa warto.


javax.swing.KeyStroke 1.2

static KeyStroke getKeyStroke(String description)

Tworzy skrt klawiaturowy z czytelnego dla czowieka opisu (cigu acuchw


rozdzielonych spacjami). Opis zaczyna si od zera lub wikszej liczby
modyfikatorw shift control ctrl meta alt altGraph, a koczy si acuchem
typed i acuchem skadajcym si z jednego znaku (na przykad typed a)
lub opcjonalnym specyfikatorem zdarzenia (pressed domylny, lub released)
i kodem klawisza. Kod klawisza, jeli ma przedrostek VK_, powinien odpowiada
staej KeyEvent, na przykad INSERT odpowiada KeyEvent.VK_INSERT.
javax.swing.JComponent 1.2

ActionMap getActionMap() 1.3

Zwraca map wic klucze mapy akcji (ktre mog by dowolnymi obiektami)
z obiektami klasy Action.

InputMap getInputMap(int flag) 1.3

Pobiera map wejcia, ktra odwzorowuje klawisze w postaci kluczy mapy akcji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

379

380

Java. Podstawy
Parametry:

flag

Warunek okrelajcy, kiedy element aktywny


ma wywoa akcj jedna z wartoci z tabeli 8.2.

8.3. Zdarzenia generowane przez mysz


Takie zdarzenia jak kliknicie przycisku lub elementu w menu za pomoc myszy nie wymagaj pisania procedur obsugi. Te zdarzenia s obsugiwane automatycznie przez rne elementy interfejsu uytkownika. Aby jednak umoliwi rysowanie za pomoc myszy, konieczne
jest przechwycenie zdarze ruchu, kliknicia i przecigania myszy.
W tym podrozdziale prezentujemy prosty edytor grafiki pozwalajcy umieszcza, przesuwa i usuwa kwadraty z obszaru roboczego (zobacz rysunek 8.7).
Rysunek 8.7.
Program
obsugujcy
zdarzenia myszy

Kiedy uytkownik nacinie przycisk myszy, wywoywane s trzy metody nasuchujce: mouse
Pressed po naciniciu przycisku, mouseReleased po zwolnieniu przycisku myszy i mouse
Clicked. Jeli w sferze zainteresowa le wycznie pene kliknicia, pierwsze dwie z wymienionych metod mona pomin. Wywoujc metody getX i getY na rzecz obiektu klasy
MouseEvent, mona sprawdzi wsprzdne x i y wskanika myszy w chwili kliknicia.
Do rozrnienia pojedynczych, podwjnych i potrjnych (!) klikni suy metoda getClic
kCount.
Niektrzy projektanci interfejsw tworz kombinacje klawiszy poczone z klikniciami
myszk, np. Ctrl+Shift+kliknicie. Naszym zdaniem jest to postpowanie niegodne naladowania. Osoby, ktre nie zgadzaj si z nasz opini, moe przekona fakt, e sprawdzanie przyciskw myszy i klawiszy specjalnych jest niezwykle zagmatwanym zadaniem
niebawem si o tym przekonamy.
Aby sprawdzi, ktre modyfikatory zostay ustawione, naley uy maski bitowej. W oryginalnym API dwie maski przyciskw s rwnowane z maskami klawiszy specjalnych,
mianowicie:
BUTTON2_MASK == ALT_MASK
BUTTON3_MASK == META_MASK

Zrobiono tak, aby uytkownicy posiadajcy myszk z jednym przyciskiem mogli naladowa
pozostae przyciski za pomoc klawiszy specjalnych (ang. modifier keys). Od Java SE 1.4
zaproponowano jednak inn metod. Od tej pory istniej nastpujce maski:
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

381

SHIFT_DOWN_MASK
CTRL_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
META_DOWN_MASK

Metoda getModifiersEx zwraca dokadne informacje o przyciskach myszy i klawiszach specjalnych uytych w zdarzeniu myszy.
Pamitajmy, e maska BUTTON3_DOWN_MASK w systemie Windows sprawdza prawy (nie gwny)
przycisk myszy. Na przykad poniszy fragment programu sprawdza, czy prawy przycisk
myszy jest wcinity:
if ((event.getModifiersEx() & InputEvent.BUTTON3_DOWN_MASK) != 0)
. . . // procedury obsugi zdarzenia kliknicia prawym przyciskiem myszy

W przykadowym programie definiujemy zarwno metod mousePressed, jak i mouseClicked.


Jeli uytkownik kliknie piksel nieznajdujcy si w obrbie adnego z narysowanych kwadratw, zostanie dodany nowy kwadrat. Dziaanie to zostao zaimplementowane w metodzie mousePressed, a wic kwadrat pojawia si natychmiast po klikniciu, przed zwolnieniem
przycisku myszy. Dwukrotne kliknicie przyciskiem myszy w obrbie narysowanego kwadratu powoduje jego usunicie. Implementacja tej funkcji zostaa umieszczona w metodzie
mouseClicked, poniewa konieczne jest sprawdzenie liczby klikni:
public void mousePressed(MouseEvent event)
{
current = find(event.getPoint());
if (current == null)
// nie w obrbie kwadratu
add(event.getPoint());
}
public void mouseClicked(MouseEvent event)
{
current = find(event.getPoint());
if (current != null && event.getClickCount() >= 2)
remove(current);
}

Kiedy kursor myszy przesuwa si nad oknem, odbiera ono stay strumie zdarze ruchu
myszy. Zauwamy, e s osobne interfejsy MouseListener i MouseMotionListener. Wyrniono je z chci zwikszenia efektywnoci. Kiedy uytkownik przesuwa mysz, powstaje caa
masa zdarze dotyczcych tej czynnoci. Obiekt nasuchujcy, ktry oczekuje na kliknicia,
nie jest zajmowany przez nieinteresujce go zdarzenia ruchu.
Nasz testowy program przechwytuje zdarzenia ruchu i w odpowiedzi na nie zmienia wygld
kursora (na krzyyk). Odpowiedzialna jest za to metoda getPredefinedCursor z klasy Cursor.
Tabela 8.3 przedstawia stae podawane jako argument wspomnianej funkcji oraz reprezentowane przez nie kursory w systemie Windows.
Poniej znajduje si kod rdowy metody mouseMoved z klasy MouseMotionListener zdefiniowanej w naszym przykadowym programie:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

382

Java. Podstawy

Tabela 8.3. Przykadowe kursory


Ikona

Staa

Ikona

Staa

DEFAULT_CURSOR

NE_RESIZE_CURSOR

CROSSHAIR_CURSOR

E_RESIZE_CURSOR

HAND_CURSOR

SE_RESIZE_CURSOR

MOVE_CURSOR

S_RESIZE_CURSOR

TEXT_CURSOR

SW_RESIZE_CURSOR

WAIT_CURSOR

W_RESIZE_CURSOR

N_RESIZE_CURSOR

NW_RESIZE_CURSOR

public void mouseMoved(MouseEvent event)


{
if (find(event.getPoint()) == null)
setCursor(Cursor.getDefaultCursor());
else
setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}

Mona zdefiniowa wasne typy kursorw. Suy do tego metoda createCustomCursor z klasy Toolkit:
Toolkit tk = Toolkit.getDefaultToolkit();
Image img = tk.getImage("dynamite.gif");
Cursor dynamiteCursor = tk.createCustomCursor(img, new Point(10, 10), "dynamite
stick");

Pierwszy argument tej metody okrela plik graficzny przedstawiajcy kursor. Drugi wyznacza przesunicie punktu aktywnego kursora. Trzeci jest acuchem opisujcym kursor.
acuch ten moe suy zwikszeniu dostpnoci. Na przykad program czytajcy z ekranu
uywany przez osob niedowidzc moe przeczyta opis takiego kursora.

Jeli w czasie przesuwania myszy uytkownik kliknie jej przycisk, generowane s wywoania metody mouseDragged zamiast mouseMoved. Nasz przykadowy program zezwala na przeciganie kwadratw pod kursorem. Efekt ten uzyskalimy, aktualizujc pooenie przeciganego kwadratu, tak aby jego rodek znajdowa si w tym samym miejscu co punkt
centralny myszki. Nastpnie ponownie rysujemy obszar roboczy, aby ukaza nowe pooenie kursora myszy.
public void mouseDragged(MouseEvent event)
{
if (current != null)
{
int x = event.getX();
int y = event.getY();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

383

current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH, SIDELENGTH);


repaint();
}

Metoda mouseMoved jest wywoywana tylko wtedy, gdy kursor znajduje si w obrbie
komponentu. Natomiast metoda mouseDragged jest wywoywana nawet wtedy, gdy
kursor opuci komponent.

Istniej jeszcze dwie inne metody obsugujce zdarzenia myszy: mouseEntered i mouseExited.
S one wywoywane, gdy kursor myszy wchodzi do komponentu lub go opuszcza.
Na zakoczenie wyjanimy sposb nasuchiwania zdarze generowanych przez mysz. Kliknicia przyciskiem myszy s raportowane przez metod mouseClicked nalec do interfejsu
MouseListener. Poniewa wiele aplikacji korzysta wycznie z klikni myszk i wystpuj
one bardzo czsto, zdarzenia ruchu myszy i przecigania zostay zdefiniowane w osobnym
interfejsie o nazwie MouseMotionListener.
W naszym programie interesuj nas oba rodzaje zdarze generowanych przez mysz. Zdefiniowalimy dwie klasy wewntrzne o nazwach MouseHandler i MouseMotionHandler. Pierwsza
z nich jest podklas klasy MouseAdapter, poniewa definiuje tylko dwie z piciu metod interfejsu
MouseListener. Klasa MouseMotionHandler implementuje interfejs MouseMotionListener, co
znaczy, e zawiera definicje obu jego metod. Listingi 8.4 i 8.5 przedstawiaj kod rdowy
omawianego programu.
Listing 8.4. mouse/MouseFrame.java
package mouse;
import javax.swing.*;
/**
* Ramka zawierajca okienko do testowania myszy
*/
public class MouseFrame extends JFrame
{
public MouseFrame()
{
add(new MouseComponent());
pack();
}
}

Listing 8.5. mouse/MouseComponent.java


package mouse;
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.awt.geom.*;
java.util.*;
javax.swing.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

384

Java. Podstawy
/**
* Komponent z dziaaniami myszy, do ktrego mona dodawa (lub z ktrego mona usuwa) kwadraty.
*/
public class MouseComponent extends JComponent
{
private static final int SIDELENGTH = 10;
private ArrayList<Rectangle2D> squares;
private Rectangle2D current;
public MouseComponent()
{
squares = new ArrayList<>();
current = null;
addMouseListener(new MouseHandler());
addMouseMotionListener(new MouseMotionHandler());
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// Rysowanie wszystkich kwadratw
for (Rectangle2D r : squares)
g2.draw(r);
}
/**
* Znajduje pierwszy kwadrat zawierajcy punkt.
* @param p punkt
* @return pierwszy kwadrat zawierajcy punkt p
*/
public Rectangle2D find(Point2D p)
{
for (Rectangle2D r : squares)
{
if (r.contains(p)) return r;
}
return null;
}
/**
* Dodaje kwadrat do zbioru.
* @param p rodek kwadratu
*/
public void add(Point2D p)
{
double x = p.getX();
double y = p.getY();
current = new Rectangle2D.Double(x - SIDELENGTH / 2, y - SIDELENGTH /
2, SIDELENGTH,
SIDELENGTH);
squares.add(current);
repaint();
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

/**
* Usuwa kwadrat ze zbioru.
* @param s kwadrat, ktry ma by usunity
*/
public void remove(Rectangle2D s)
{
if (s == null) return;
if (s == current) current = null;
squares.remove(s);
repaint();
}
// Kwadrat zawierajcy kursor
private class MouseHandler extends MouseAdapter
{
public void mousePressed(MouseEvent event)
{
// Dodanie nowego kwadratu, jeli kursor nie jest wewntrz innego kwadratu
current = find(event.getPoint());
if (current == null) add(event.getPoint());
}
public void mouseClicked(MouseEvent event)
{
// Usunicie kwadratu w wyniku jego dwukrotnego kliknicia
current = find(event.getPoint());
if (current != null && event.getClickCount() >= 2) remove(current);
}
}
private class MouseMotionHandler implements MouseMotionListener
{
public void mouseMoved(MouseEvent event)
{
// Ustawienie kursora na krzyyk, jeli znajduje si wewntrz
// kwadratu
if (find(event.getPoint()) == null) setCursor(Cursor.getDefaultCursor());
else setCursor(Cursor.getPredefinedCursor(Cursor.CROSSHAIR_CURSOR));
}
public void mouseDragged(MouseEvent event)
{
if (current != null)
{
int x = event.getX();
int y = event.getY();
// Przecignicie aktualnego kwadratu w celu wyrodkowania go w punkcie (x, y)
current.setFrame(x - SIDELENGTH / 2, y - SIDELENGTH / 2, SIDELENGTH,
SIDELENGTH);
repaint();
}
}
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

385

386

Java. Podstawy
java.awt.event.MouseEvent 1.1

int getX()

int getY()

Point getPoint()

Zwraca wsprzdne x (pozioma) i y (pionowa) lub punkt, w ktrym miao miejsce


zdarzenie, mierzc od lewego grnego rogu komponentu bdcego rdem
zdarzenia.

int getClickCount()

Zwraca liczb kolejnych klikni przyciskiem myszy zwizanych z danym


zdarzeniem (odstp czasu oddzielajcy zdarzenia okrelane jako kolejne zaley
od systemu).
java.awt.event.InputEvent 1.1

int getModifiersEx() 1.4

Zwraca rozszerzone modyfikatory zdarzenia. Do sprawdzania zwrconych


wartoci su nastpujce maski:
BUTTON1_DOWN_MASK
BUTTON2_DOWN_MASK
BUTTON3_DOWN_MASK
SHIFT_DOWN_MASK
CTRL_DOWN_MASK
ALT_DOWN_MASK
ALT_GRAPH_DOWN_MASK
META_DOWN_MASK

static String getModifiersExText(int modifiers) 1.4

Zwraca acuch typu Shift+Button1, opisujcy rozszerzone modyfikatory


w danym zbiorze znacznikw.
java.awt.Toolkit 1.0

public Cursor createCustomCursor(Image image, Point hotSpot, String name) 1.2

Tworzy nowy obiekt niestandardowego kursora.


Parametry:

image

Obraz reprezentujcy kursor

hotSpot

Punkt centralny kursora (na przykad kocwka


strzaki lub rodek krzyyka)

name

Opis kursora wspomagajcy dostpno


w specjalnych rodowiskach

java.awt.Component 1.0

public void setCursor(Cursor cursor) 1.1

Ustawia obraz kursora na okrelony kursor.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

387

8.4. Hierarchia zdarze w bibliotece AWT


Majc ju pewne rozeznanie w temacie obsugi zdarze, na zakoczenie tego rozdziau zrobimy
krtki przegld architektury obsugi zdarze biblioteki AWT.
Jak wspominalimy wczeniej, zdarzenia w Javie s obsugiwane w metodologii obiektowej,
a wszystkie zdarzenia pochodz od klasy EventObject z pakietu java.util (nazw wsplnej
nadklasy nie jest Event, poniewa tak nazw nosi klasa zdarze w starym modelu zdarze
mimo e model ten jest obecnie odradzany, jego klasy nadal wchodz w skad biblioteki Javy).
Klasa EventObject posiada podklas AWTEvent bdc nadklas wszystkich klas zdarzeniowych AWT. Rysunek 8.8 przedstawia diagram dziedziczenia zdarze AWT.

Rysunek 8.8. Diagram dziedziczenia klas zdarzeniowych AWT

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

388

Java. Podstawy
Niektre komponenty Swing generuj obiekty zdarzeniowe jeszcze innych typw zdarze.
Rozszerzaj one bezporednio klas EventObject, a nie AWTEvent.
Obiekty zdarzeniowe zawieraj informacje o zdarzeniach przesyanych przez rdo zdarze
do swoich suchaczy. W razie potrzeby mona przeanalizowa obiekty zdarzeniowe, ktre
zostay przekazane do obiektw nasuchujcych, co zrobilimy w przykadzie z przyciskiem za pomoc metod getSource i getActionCommand.
Niektre klasy zdarzeniowe AWT s dla programisty Javy bezuyteczne. Na przykad biblioteka AWT wstawia do kolejki zdarze obiekty PaintEvent, ale obiekty te nie s dostarczane
do suchaczy. Programici Javy nie nasuchuj zdarze rysowania. Przesaniaj metod paint
Component, aby mc kontrolowa ponowne rysowanie. Ponadto AWT generuje pewne
zdarzenia, ktre s potrzebne tylko programistom systemowym. Nie opisujemy tych specjalnych typw zdarze.

8.4.1. Zdarzenia semantyczne i niskiego poziomu


Biblioteka AWT rozrnia zdarzenia niskiego poziomu i zdarzenia semantyczne. Zdarzenie
semantyczne jest dzieem uytkownika (jest to np. kliknicie przycisku). Dlatego zdarzenie
ActionEvent jest zdarzeniem semantycznym. Zdarzenia niskiego poziomu to takie zdarzenia, ktre umoliwiaj zaistnienie zdarze semantycznych. W przypadku kliknicia przycisku jest to jego nacinicie, szereg ruchw mysz i zwolnienie (ale tylko jeli zwolnienie
nastpi w obrbie przycisku). Moe to by nacinicie klawisza majce miejsce po wybraniu przycisku przez uytkownika za pomoc klawisza Tab i nacinicia go za pomoc spacji.
Podobnie semantycznym zdarzeniem jest przesunicie paska przewijania, a ruch mysz jest
zdarzeniem niskiego poziomu.
Poniej znajduje si lista najczciej uywanych klas zdarze semantycznych pakietu
java.awt.event:

ActionEvent kliknicie przycisku, wybr elementu z menu, wybr elementu listy,


nacinicie klawisza Enter w polu tekstowym.

AdjustmentEvent przesunicie paska przewijania.

ItemEvent wybr jednego z pl do wyboru lub elementw listy.

Do najczciej uywanych klas zdarze niskiego poziomu zaliczaj si:

KeyEvent nacinicie lub zwolnienie klawisza.

MouseEvent nacinicie lub zwolnienie przycisku myszy, poruszenie


lub przecignicie mysz.

MouseWheelEvent pokrcenie kkiem myszy.

FocusEvent uaktywnienie lub dezaktywacja elementu.

WindowEvent zmiana stanu okna.

Tych zdarze nasuchuj nastpujce interfejsy:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 8.

Obsuga zdarze

389

ActionListener
AdjustmentListener
FocusListener
ItemListener
KeyListener
MouseListener
MouseMotionListener
MouseWheelListener
WindowListener
WindowFocusListener
WindowStateListener

Niektre interfejsy nasuchujce AWT, te zawierajce wicej ni jedn metod, posiadaj


odpowiadajce im klasy adaptacyjne, ktre implementuj wszystkie ich metody (pozostae
interfejsy maj tylko jedn metod, a wic utworzenie dla nich klas adaptacyjnych nie daoby
adnych korzyci). Ponisze klasy adaptacyjne s czsto uywane:
FocusAdapter
KeyAdapter
MouseAdapter
MouseMotionAdapter
WindowAdapter

Tabela 8.4 przedstawia najwaniejsze interfejsy nasuchowe, zdarzenia i rda zdarze biblioteki AWT.
Tabela 8.4. Obsuga zdarze
Interfejs

Metody

Parametry/
metody dostpu

Zdarzenia
generowane przez

ActionListener

actionPerformed

ActionEvent

AbstractButton
JComboBox
JTextField
Timer

getActionCommand
getModifiers

AdjustmentListener

adjustmentValueChanged

AdjustmentEvent

JScrollbar

getAdjustable
getAdjustmentType
getValue

ItemListener

itemStateChanged

ItemEvent
getItem

AbstractButton
JComboBox

getItemSelectable
getStateChange

FocusListener

focusGained
focusLost

FocusEvent

KeyListener

keyPressed
keyReleased
keyTyped

KeyEvent

isTemporary
getKeyChar
getKeyCode
getKeyModifiersText
getKeyText
isActionText

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Component
Component

390

Java. Podstawy

Tabela 8.4. Obsuga zdarze cig dalszy


Parametry/
metody dostpu

Zdarzenia
generowane przez

mousePressed
mouseReleased
mouseEntered
mouseExited
mouseClicked

MouseEvent

Component

mouseDragged

MouseEvent

Component

MouseWheelEvent

Component

Interfejs

Metody

MouseListener

getClickCount
getX
getY
getPoint
translatePoint

MouseMotionListener

mouseMoved
MouseWheelListener

MouseWheelMoved

getWheelRotation
getScrollAmount

WindowListener

WindowFocusListener
WindowStateListener

windowClosing
windowOpened
windowIconified
windowDeiconified
windowClosed
windowActivated
windowDeactivated

WindowEvent

windowGainedFocus
windowLostFocus

WindowEvent

windowStateChanged

WindowEvent

Window

getWindow

Window

getOppositeWindow

Window

getOldState
getNewState

Pakiet javax.swing.event zawiera dodatkowe zdarzenia specyficzne dla komponentw Swinga.


Niektre z nich opisujemy w nastpnym rozdziale.
Na tym zakoczymy opis technik obsugi zdarze AWT. W nastpnym rozdziale nauczymy
si wykorzystywa najpopularniejsze komponenty Swinga oraz szczegowo przeanalizujemy
generowane przez nie zdarzenia.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Komponenty Swing
interfejsu uytkownika
W tym rozdziale:

Swing a wzorzec projektowy Model-View-Controller

Wprowadzenie do zarzdzania rozkadem

Wprowadzanie tekstu

Komponenty wyboru

Menu

Zaawansowane techniki zarzdzania rozkadem

Okna dialogowe

Gwnym celem poprzedniego rozdziau byo przedstawienie technik wykorzystania modelu


zdarze w Javie. W midzyczasie postawilimy pierwsze kroki w tworzeniu graficznego
interfejsu uytkownika. Ten rozdzia opisuje najwaniejsze narzdzia potrzebne do budowy
w peni funkcjonalnego GUI.
Zaczniemy od przegldu architektury, na ktrej opiera si Swing. Znajomo podstawowych
mechanizmw dziaania uatwia nauk efektywnego wykorzystania niektrych bardziej
zaawansowanych komponentw. W nastpnej kolejnoci omwimy najczciej uywane
komponenty Swing, czyli pola tekstowe, przeczniki (ang. radio button) i menu. Nastpnie
przejdziemy do rozmieszczania tych komponentw w oknie niezalenie od wybranego stylu
interfejsu za pomoc narzdzi zarzdcy ukadu (ang. layout manager). Na zakoczenie rozdziau nauczymy si tworzy okna dialogowe Swing.
Ten rozdzia opisuje podstawowe komponenty, takie jak komponenty tekstowe, przyciski
i suwaki. S to najczciej uywane komponenty, niezbdne w wikszoci interfejsw. Bardziej zaawansowane komponenty zostay opisane w drugim tomie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

392

Java. Podstawy

9.1. Swing a wzorzec projektowy


Model-View-Controller
Zgodnie z zapowiedzi zaczniemy od opisu architektury komponentw biblioteki Swing.
Najpierw zapoznamy si z oglnym pojciem wzorca projektowego (ang. design pattern),
a pniej przejdziemy do wzorca model-widok-kontroler (ang. Model-View-Controller
MVC), ktry mia niemay wpyw na projekt biblioteki Swing.

9.1.1. Wzorce projektowe


Przy rozwizywaniu problemu z reguy nie dochodzi si do rozwizania, zaczynajc od zera.
Zazwyczaj korzysta si z dowiadczenia innych programistw, np. zasigajc ich rady w interesujcych nas kwestiach. Wzorce projektowe umoliwiaj przedstawienie tej wiedzy w poukadany sposb.
Niedawno inynierowie oprogramowania zaczli gromadzi katalogi takich wzorcw. Inspiracj prekursorw w tej dziedzinie byy wzorce projektowe architekta Christophera Alexandra. W swojej ksice pod tytuem The Timeless Way of Building (Oxford University Press,
1979) zawar on zbir wzorcw projektowych do wykorzystania w pomieszczeniach publicznych i mieszkalnych. Oto przykadowy wzorzec z tej ksiki:
Umiejscowienie okna
Kady lubi aweczki w oknach, okna wykuszowe i due okna z niskimi parapetami
oraz przystawionymi do nich wygodnymi krzesami W pokoju pozbawionym
takiego miejsca rzadko potrafimy si zrelaksowa
Jeli w pokoju nie ma takiego miejsca, osoba w nim przebywajca jest rozdzierana
przez dwie siy z jednej strony chce wygodnie usi, a z drugiej cignie
j do wiata.
Oczywicie, jeli wygodne miejsca te, w ktrych planujemy spdza najwicej
czasu znajduj si z dala od okien, nie ma sposobu na przezwycienie tego
konfliktu
Wniosek: w kadym pokoju, w ktrym spdzasz cho troch czasu w cigu dnia,
wygospodaruj przynajmniej jedno wygodne miejsce przy oknie (rysunek 9.1).
Rysunek 9.1.
Miejsce
przy oknie

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

393

Kady wzorzec w katalogu Alexandra, podobnie jak wzorce projektowe oprogramowania,


jest zbudowany wedug okrelonego schematu. Najpierw opisywany jest kontekst, czyli
sytuacja, ktra powoduje powstanie problemu. Pniej nastpuje opis problemu z reguy
ma on posta zbioru kilku przeciwstawnych sobie si. Ostateczne rozwizanie jest zotym
rodkiem pomidzy tymi siami.
We wzorcu miejsca przy oknie kontekstem jest pokj, w ktrym spdzamy jak cz
dnia. Przeciwstawne siy to ch usadowienia si w wygodnym miejscu i przyciganie do
wiata. Rozwizanie polega na wygospodarowaniu miejsca przy oknie.
We wzorcu MVC, opisanym w kolejnym podrozdziale, kontekstem jest system interfejsu
uytkownika, ktry przedstawia informacje i odbiera dane od uytkownika. Jest kilka si.
Moe by wiele rnych reprezentacji tych samych danych, ktre musz by aktualizowane
wsplnie. Reprezentacja wizualna moe si zmienia, np. w zwizku z rnymi stylami.
Mechanizmy interakcji mog si zmienia, na przykad w zwizku z obsug polece gosowych. Rozwizanie polega na rozdziale obowizkw na trzy osobne komponenty: model,
widok i kontroler.
Wzorzec MVC nie jest jedynym wzorcem, ktrego uyto przy projektowaniu bibliotek
AWT i Swing. Oto kilka innych przykadw:

Kontenery i komponenty s przykadami wzorca Composite.

Panel przewijany (ScrollPane) to przykad wzorca Decorator.

Zarzdcy ukadu reprezentuj wzorzec Strategy.

Wan cech wzorcw projektowych jest to, e przenikaj one do kultury. Programici na
caym wiecie wiedz, o co nam chodzi, kiedy mwimy o wzorcu MVC lub Decorator.
Dziki temu wzorce stay si doskonaym narzdziem do opisu problemw zwizanych
z projektowaniem.
Formalny opis wielu wzorcw programistycznych znajduje si w nowatorskiej ksice powiconej tej tematyce pod tytuem Wzorce projektowe (WNT, Warszawa 2005), ktrej
autorem jest Erich Gamma i wsppracownicy (tytu oryginau Design patterns Elements
of Reusable Object-Oriented Software). Gorco polecamy take lektur doskonaej ksiki
pod tytuem A System of Patterns (John Wiley & Sons, 1996), ktrej autorem jest Frank
Buschmann i wsppracownicy. Naszym zdaniem ta pozycja jest mniej nowatorska od poprzedniej i bardziej przystpna.

9.1.2. Wzorzec Model-View-Controller


Zatrzymajmy si na chwil nad skadnikami kadego komponentu interfejsu uytkownika,
takimi jak przyciski, pola wyboru, pola tekstowe czy skomplikowany widok drzewa. Kady
komponent ma trzy cechy:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

394

Java. Podstawy

Tre np. stan przycisku (wcinity lub nie) lub tekst w polu tekstowym.

Wygld kolor, rozmiar itd.

Zachowanie reakcje na zdarzenia.

Nawet na pierwszy rzut oka taki prosty komponent jak przycisk wykazuje w miar zoone
interakcje pomidzy tymi cechami. Oczywicie wygld przycisku zaley od stylu. Przycisk
w stylu Metal wyglda inaczej ni Windows lub Motif. Dodatkowo na jego wygld ma
wpyw jego stan. Wcinicie przycisku oznacza konieczno ponownego narysowania go
ze zmienionym wygldem. Stan zaley od zdarze odbieranych przez przycisk. Kiedy
uytkownik nacinie przycisk myszy po uprzednim umieszczeniu kursora w obrbie przycisku na ekranie, przycisk ten zostanie wcinity.
Oczywicie uywajc przycisku w programie, nikt nie rozwaa dogbnie jego wewntrznych mechanizmw i cech. To jest przecie zadanie programisty, ktry ten przycisk implementowa. Natomiast programici implementujcy przyciski musz bardziej si nad nimi
skupi. Ich zadanie polega przecie na takim zaimplementowaniu przyciskw i innych
komponentw, aby dziaay bez zarzutw w kadym stylu.
W zwizku z tym projektanci biblioteki Swing postanowili skorzysta z dobrze znanego
wzorca o nazwie Model-View-Controller (MVC). Wzorzec ten, podobnie jak wiele innych
wzorcw projektowych, odwouje si do jednej z zasad projektowania zorientowanego
obiektowo, ktr opisywalimy w rozdziale 5., a ktra brzmi: nie obciaj jednego obiektu
zbyt du liczb dziaa. Nie twrz jednej klasy, ktra robi wszystko. Styl jednego komponentu zwi z jednym obiektem, a tre przechowuj w innym obiekcie. Wzorzec projektowy MVC podpowiada, jak to zrobi. Naley napisa trzy osobne klasy:

Model przechowuje tre.

Widok (ang. view) wywietla tre.

Kontroler (ang. controller) obsuguje dane wejciowe od uytkownika.

Wzorzec precyzyjnie okrela interakcje pomidzy tymi trzema obiektami. Model przechowuje tre i nie posiada interfejsu uytkownika. W przypadku przycisku nie ma tej treci
duo. Stanowi j tylko niewielki zestaw znacznikw okrelajcych, czy przycisk jest wcinity, czy nie, czy jest aktywny, czy nie itd. Bardziej interesujca jest tre w przypadku
pola tekstowego. Jest to obiekt acuchowy przechowujcy aktualny tekst. Nie jest to jednak to samo co widok treci jeli treci jest wicej, ni moe pomieci pole tekstowe,
uytkownik zobaczy tylko cz tekstu (rysunek 9.2).
Rysunek 9.2.
Model i widok
pola tekstowego

Model musi zawiera metody zmieniajce i sprawdzajce tre. Na przykad model tekstowy posiada metody dodajce lub usuwajce znaki z aktualnego tekstu i zwracajce ten tekst
w postaci acucha. Nie zapomnijmy, e model nie ma charakteru wizualnego. Rysowanie
danych przechowywanych w modelu naley do obowizkw widoku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

395

Termin model nie jest najlepszym okreleniem, poniewa zazwyczaj oznacza on


co abstrakcyjnego. Na przykad projektanci samochodw i samolotw buduj
modele, bdce imitacjami prawdziwych maszyn. Analogia ta w przypadku wzorca model-widok-kontroler prowadzi jednak na manowce. W tym wzorcu model przechowuje ca
tre, a widok dostarcza (pen lub niepen) wizualn reprezentacj tej treci. Lepsz
analogi byby model pozujcy malarzowi. Zadaniem artysty jest przyjrzenie si temu
modelowi i stworzenie jego widoku. W zalenoci od artysty widok ten moe by zwykym portretem, malowidem impresjonistycznym albo rysunkiem kubistycznym przedstawiajcym koczyny w powykrcanych proporcjach.

Jedn z zalet wzorca MVC jest to, e model mona przedstawia na rne sposoby, za kadym razem pokazujc inn cz caoci. Na przykad edytor HTML moe oferowa dwa
rwnoczesne widoki treci: widok strony, jakby bya wywietlona w przegldarce (tryb
WYSIWYG), oraz widok rda (rysunek 9.3). Kiedy model jest aktualizowany za porednictwem kontrolera jednego z widokw, oba widoki s informowane o tej zmianie. W momencie odebrania powiadomienia widoki aktualizuj si automatycznie. Oczywicie dla takich
prostych komponentw jak przycisk nie tworzy si wielu widokw tego samego modelu.
Rysunek 9.3.
Dwa oddzielne
widoki tego
samego modelu

Kontroler obsuguje zdarzenia zwizane z wprowadzaniem danych przez uytkownika, jak


kliknicie przyciskiem myszy czy nacinicie klawisza na klawiaturze. Jeli na przykad
uytkownik nacinie klawisz litery w polu tekstowym, kontroler wywouje polecenie modelu
dotyczce wstawiania znakw. Nastpnie model rozkazuje widokowi, aby si zaktualizowa.
Widok nie otrzymuje adnych informacji, dlaczego tekst si zmieni. Jeli natomiast uytkownik nacinie jeden z klawiszy strzaek, kontroler moe wyda widokowi polecenie, aby
si przewin. Przewijanie widoku nie wywiera adnego wpywu na tekst, a wic model nie
wie, e to zdarzenie miao w ogle miejsce.
Rysunek 9.4 przedstawia relacje pomidzy modelem, widokiem i kontrolerem.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

396

Java. Podstawy

Rysunek 9.4.
Relacje pomidzy
modelem,
widokiem
i kontrolerem

Programista Swing nie musi z reguy pamita o architekturze model-widok-kontroler. Kady


komponent interfejsu uytkownika posiada klas osonow (np. JButton czy JTextField),
ktra przechowuje model i widok. Kiedy programista wysya pytanie dotyczce treci (np.
tekstu w polu tekstowym), klasa osonowa odpytuje model i zwraca odpowied programicie.
danie zmiany widoku (np. przeniesienia karetki w polu tekstowym) jest przesyane przez
klas osonow do widoku. Czasami jednak klasa osonowa nie wywizuje si w peni ze
swojego zadania polegajcego na przesyaniu polece. W takim przypadku konieczne jest
odszukanie za jej pomoc modelu i praca bezporednio na nim (nie trzeba pracowa bezporednio nad widokiem to zadanie naley do procedur odpowiedzialnych za styl).
Poza byciem waciwym narzdziem do wykonania danego zadania wzorzec MVC by atrakcyjny dla projektantw biblioteki Swing z jeszcze jednego powodu pozwala na implementacj obieralnego wygldu, czyli stylu (ang. pluggable look and feel). Model przycisku czy
pola tekstowego jest niezaleny od stylu, ale oczywicie reprezentacja wizualna jest cakowicie
zalena od projektu interfejsu uytkownika w konkretnym stylu. Kontroler moe zachowywa si rnie. Na przykad w urzdzeniu sterowanym gosem musi obsugiwa cakiem inne
zdarzenia ni na standardowym komputerze z klawiatur i mysz. Dziki oddzieleniu podstawowego modelu od interfejsu uytkownika projektanci biblioteki Swing mog wielokrotnie
wykorzystywa kod w modelach, a nawet przecza styl w trakcie dziaania programu.
Oczywicie wzorce to tylko zestawy wskazwek, a nie cisy zbir zasad. adnego wzorca
nie mona zastosowa we wszystkich sytuacjach. Na przykad wzorzec dotyczcy miejsca przy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

397

oknie moe by trudny do zastosowania w niektrych pomieszczeniach. Podobnie projektanci


biblioteki Swing zetknli si z brutaln rzeczywistoci, dochodzc do wniosku, e implementacja obieralnego wygldu nie zawsze pozwala na dobr realizacj wzorca model-widok-kontroler. Modele atwo mona oddziela, a kady komponent interfejsu uytkownika posiada klas modelow. Natomiast zakres dziaa widoku i kontrolera nie zawsze daj
si rozdzieli i s one rozproszone w kilku rnych klasach. Oczywicie uytkownika tych
klas to zagadnienie nie dotyczy. W rzeczywistoci, jak pisalimy ju wczeniej, programista nie musi te pamita o modelach moe zwyczajnie uywa klas opakowujcych
komponenty.

9.1.3. Analiza MVC przyciskw Swing


W poprzednim rozdziale nauczylimy si uywa przyciskw, nic nie wiedzc o ich modelu,
widoku i kontrolerze. Poniewa jednak przyciski s jednym z najprostszych elementw interfejsu uytkownika, stanowi dobry punkt zaczepienia przy nabywaniu biegoci w posugiwaniu si wzorcem model-widok-kontroler. Podobne klasy i interfejsy mona spotka take
w bardziej zaawansowanych komponentach Swing.
Klasy modelowe wikszoci komponentw implementuj interfejs, ktrego nazwa koczy si
sowem Model, np. ButtonModel. Klasy implementujce ten interfejs mog definiowa stan
rnego rodzaju przyciskw. Przyciski nie s zbyt skomplikowane i biblioteka Swing zawiera
tylko jedn klas o nazwie DefaultButtonModel, ktra implementuje wspomniany interfejs.
Pogld na temat tego, jakiego rodzaju dane s przechowywane przez model przycisku, daje
przedstawiona poniej tabela 9.1, zawierajca wykaz i opis wasnoci interfejsu ButtonModel.
Tabela 9.1. Wasnoci interfejsu ButtonModel
Nazwa wasnoci

Warto

actionCommand

acuch polecenia dziaania zwizany z przyciskiem

mnemonic

Skrt klawiaturowy dla przycisku

armed

true, jeli przycisk zosta nacinity i kursor znajduje si nad nim

enabled

true, jeli przycisk moe by uywany

pressed

true, jeli przycisk zosta nacinity, a przycisk myszy nie zosta jeszcze zwolniony

rollover

true, jeli kursor znajduje si nad przyciskiem.

selected

true, jeli przycisk zosta wczony (uywana w przypadku pl wyboru i przecznikw)

Kady obiekt typu JButton przechowuje obiekt modelu przycisku, ktry mona z niego
wydoby.
JButton button = new JButton("Niebieski");
ButtonModel model = button.getModel();

W praktyce programist to niewiele obchodzi szczegy dotyczce stanu przycisku maj


znaczenie tylko dla widoku, ktry go rysuje. Wane informacje, jak to, czy przycisk jest wczony, s dostpne w klasie JButton (oczywicie klasa JButton pobiera te informacje z modelu).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

398

Java. Podstawy
Przyjrzyjmy si jeszcze raz interfejsowi ButtonModel, aby sprawdzi, czego w nim nie ma.
Model ten nie przechowuje etykiety ani ikony przycisku. Nie ma moliwoci sprawdzenia,
co znajduje si na froncie przycisku, patrzc tylko na jego model (w podrozdziale 9.4.2 o przecznikach przekonamy si, e czysto projektu jest rdem problemw dla programisty).
Warto doda, e ten sam model (czyli DefaultButtonModel) jest uywany dla przyciskw,
przecznikw, pl tekstowych, a nawet elementw menu. Oczywicie kady z tych typw
przyciskw posiada inny widok i kontroler. W stylu Metal przycisk JButton uywa klasy
o nazwie BasicButtonUI do reprezentacji widoku i klasy ButtonUIListener jako kontrolera.
Oglnie z kadym komponentem Swing zwizany jest obiekt widoku, ktrego nazwa koczy
si skrtem UI. Jednak nie kady komponent Swing posiada dedykowany obiekt kontrolera.
Po przeczytaniu tego wprowadzenia do mechanizmw dziaania przyciskw JButton moe
nasun si jedno pytanie: czym w rzeczywistoci jest JButton? Jest to po prostu klasa
osonowa dziedziczca po klasie JComponent, ktra przechowuje obiekt DefaultButtonModel,
dane widoku (takie jak etykieta i ikona przycisku) oraz obiekt BasicButtonUI odpowiedzialny
za widok przycisku.

9.2. Wprowadzenie do zarzdzania rozkadem


Zanim przejdziemy do opisu poszczeglnych komponentw Swing, takich jak pola tekstowe
i przeczniki, krtko opiszemy techniki rozmieszczania ich w obrbie ramki. JDK w przeciwiestwie do Visual Basica nie posiada projektanta formy. Pozycjonowanie komponentw
interfejsu uytkownika odbywa si za pomoc odpowiednio napisanych procedur.
Oczywicie wiele rodowisk programistycznych obsugujcych Jav udostpnia narzdzia
suce do automatyzacji wymienionych zada. Niemniej bardzo wana jest dokadna znajomo mechanizmw wewntrznych, poniewa nawet najlepsze narzdzia zazwyczaj wymagaj rcznego dostrojenia.
Zaczniemy od programu z rozdziau 8., ktry zmienia kolor ta w odpowiedzi na nacinicie
przycisku (rysunek 9.5).
Rysunek 9.5.
Panel z trzema
przyciskami

Przyciski te znajduj si na panelu JPanel i podlegaj zarzdcy rozkadu cigego (ang. flow
layout manager), czyli domylnemu zarzdcy rozkadu panelu. Rysunek 9.6 pokazuje, co si
dzieje, kiedy do panelu dodamy wicej przyciskw. Jak wida, kiedy nie ma ju miejsca,
nastpuje przejcie do nowego wiersza.
Ponadto przyciski zostaj na rodku, nawet jeli rozmiar okna si zmienia (rysunek 9.7).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

399

Rysunek 9.6.
Panel
z szecioma
przyciskami
zarzdzanymi
przez zarzdc
rozkadu
Rysunek 9.7.
Zmiana rozmiaru
panelu powoduje
automatyczne
przegrupowanie
przyciskw

Oglnie komponenty znajduj si w kontenerze, a zarzdca rozkadu okrela pooenie


i rozmiar komponentw w kontenerze.
Przyciski, pola tekstowe i inne elementy interfejsu uytkownika rozszerzaj klas Component.
Komponenty mog si znajdowa w takich kontenerach jak panele. Poniewa panele same
mog by umieszczane w innych kontenerach, klasa Container dziedziczy po klasie Component.
Rysunek 9.8 przedstawia hierarchi dziedziczenia klasy Component.
Niestety powysza hierarchia dziedziczenia jest w dwch miejscach niejasna. Po
pierwsze, okna najwyszego rzdu, jak JFrame, s podklasami klasy Container,
a wic take klasy Component, ale nie mog by umieszczane wewntrz innych kontenerw. Po drugie, klasa JComponent jest podklas klasy Container, a nie Component, przez
co do JButton mona dodawa inne komponenty (cho nie zostayby one wywietlone).

Kady kontener posiada domylnego zarzdc rozkadu, ale mona utworzy te wasny.
Na przykad ponisza instrukcja rozmieszcza komponenty w panelu za pomoc klasy Grid
Layout:
panel.setLayout(new GridLayout(4, 4));

Programista dodaje komponenty do kontenera. Metoda add tego kontenera przekazuje komponent i dane dotyczce jego umiejscowienia do zarzdcy rozkadu.
java.awt.Container 1.0

void setLayout(LayoutManager m)

Ustawia zarzdc rozkadu dla kontenera.

Component add(Component c)

Component add(Component c, Object constraints) 1.1

Dodaje komponent do kontenera i zwraca referencj do tego komponentu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

400

Java. Podstawy

Rysunek 9.8. Hierarchia dziedziczenia klasy Component

Parametry:

Komponent, ktry ma zosta dodany.

constraints Identyfikator zrozumiay dla zarzdcy rozkadu.


java.awt.FlowLayout 1.0

FlowLayout()

FlowLayout(int align)

FlowLayout(int align, int hgap, int vgap)

Parametry:

align

Jedna z trzech wartoci: LEFT, CENTER, RIGHT.

hgap

Przerwa w poziomie w pikselach (wartoci ujemne


powoduj nachodzenie na siebie elementw).

vgap

Przerwa w pionie w pikselach (wartoci ujemne


powoduj nachodzenie na siebie elementw).

9.2.1. Rozkad brzegowy


Zarzdca rozkadu brzegowego (ang. border layout manager) jest domylny dla panelu
z treci komponentw JFrame. W przeciwiestwie do zarzdcy rozkadu cigego, ktry w peni
kontroluje pooenie kadego komponentu, zarzdca rozkadu brzegowego pozwala wybra
miejsce dla kadego komponentu. Do okrelania pooenia wykorzystywane s kierunki wiata:
pnoc, poudnie, wschd i zachd, oraz rodek (rysunek 9.9).
Na przykad:
frame.add(component, BorderLayout.SOUTH);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

401

Rysunek 9.9.
Rozkad brzegowy

Najpierw ustawiane s komponenty pooone przy brzegach, a reszt powierzchni zajmuje


rodek. Kiedy zmienia si rozmiar komponentu, zmienia si tylko rozmiar rodka, za rozmiar elementw brzegowych pozostaje niezmieniony. Dodawanie komponentw polega na
zastosowaniu staych CENTER, NORTH, SOUTH, EAST i WEST klasy BorderLayout. Nie wszystkie
miejsca musz by zajte. W przypadku braku wartoci przyjmowana jest warto CENTER.
Stae klasy BorderLayout s zdefiniowane jako acuchy. Na przykad staa Border
Layout.SOUTH jest zdefiniowana jako South. Wielu programistw woli uywa acuchw, poniewa s krtsze, np. frame.add(component, "South"). Jeli jednak w acuchu znajdzie si bd, kompilator go nie przechwyci.

W przeciwiestwie do rozkadu cigego, rozkad brzegowy rozciga komponenty na ca


dostpn przestrze (rozkad cigy pozostawia preferowany rozmiar komponentu). W przypadku przyciskw moe to by problemem:
frame.add(yellowButton, BorderLayout.SOUTH);

// nie

Rysunek 9.10 przedstawia wynik zastosowania powyszego fragmentu programu. Przycisk


zosta rozcignity na ca szeroko obszaru poudniowego ramki. Gdyby zosta dodany
kolejny przycisk, zastpiby on swojego poprzednika.
Rysunek 9.10.
Jeden przycisk
w rozkadzie
brzegowym

Ten problem mona rozwiza za pomoc dodatkowych paneli. Spjrzmy na przykad na


rysunek 9.11. Trzy przyciski umieszczone na samym dole ekranu znajduj si na panelu.
Panel zosta ustawiony w poudniowej czci panelu z treci.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

402

Java. Podstawy

Rysunek 9.11.
Panel
umieszczony
w poudniowej
czci ramki

Aby osign tak konfiguracj, najpierw naley utworzy obiekt JPanel, a nastpnie doda
do niego wszystkie przyciski. Domylnym zarzdc rozkadu w panelu jest FlowLayout, ktry
w tym przypadku stanowi dobry wybr. Poszczeglne przyciski naley dodawa do panelu
za pomoc omawianej ju metody add. Pooenie i rozmiar przyciskw s kontrolowane
przez zarzdc rozkadu FlowLayout. Dziki temu przyciski bd si znajdoway na rodku
panelu i nie bd si rozciga na cay dostpny obszar. Na kocu naley doda panel do
panelu z treci ramki.
JPanel panel = new JPanel();
panel.add(yellowButton);
panel.add(blueButton);
panel.add(redButton);
frame.add(panel, BorderLayout.SOUTH);

Rozkad brzegowy rozciga panel na cay obszar poudniowy.


java.awt.BorderLayout 1.0

BorderLayout()

BorderLayout(int hgap, int vgap)

Tworzy nowy BorderLayout.


Parametry:

hgap

Przerwa w poziomie w pikselach (wartoci ujemne


powoduj nachodzenie na siebie elementw).

vgap

Przerwa w pionie w pikselach (wartoci ujemne


powoduj nachodzenie na siebie elementw).

9.2.2. Rozkad siatkowy


Rozkad siatkowy polega na rozmieszczeniu komponentw w wierszach i kolumnach tworzcych siatk. Wszystkie komponenty maj ten sam rozmiar. Przyciski kalkulatora przedstawionego na rysunku 9.12 zostay rozmieszczone za pomoc rozkadu siatkowego. W miar
zwikszania i zmniejszania okna przyciski rosn lub kurcz si, ale wszystkie maj takie same
rozmiary.
W konstruktorze obiektu rozkadu siatkowego naley okreli, ile wierszy i kolumn ma zosta
utworzonych.
panel.setLayout(new GridLayout(4, 4));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

403

Rysunek 9.12.
Kalkulator

Komponenty s dodawane najpierw do pierwszego wiersza, potem do drugiego itd.


panel.add(new JButton("1"));
panel.add(new JButton("2"));

Listing 9.1 przedstawia kod rdowy tego kalkulatora. Jest to zwyky kalkulator, a nie wersja
z odwrcon notacj polsk, ktra nie wiedzie czemu zyskaa sobie tak du popularno w publikacjach na temat Javy. W tym programie po dodaniu komponentu do ramki
nastpuje wywoanie metody pack. Metoda ta oblicza wysoko i szeroko ramki, wykorzystujc preferowane rozmiary wszystkich komponentw.
Listing 9.1. calculator/CalculatorPanel.java
package calculator;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Panel z przyciskami kalkulatora i wywietlaczem wyniku.
*/
public class CalculatorPanel extends JPanel
{
private JButton display;
private JPanel panel;
private double result;
private String lastCommand;
private boolean start;
public CalculatorPanel()
{
setLayout(new BorderLayout());
result = 0;
lastCommand = "=";
start = true;
// Dodanie wywietlacza
display = new JButton("0");
display.setEnabled(false);
add(display, BorderLayout.NORTH);
ActionListener insert = new InsertAction();
ActionListener command = new CommandAction();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

404

Java. Podstawy
// Wstawienie przyciskw na siatk 44
panel = new JPanel();
panel.setLayout(new GridLayout(4, 4));

addButton("7",
addButton("8",
addButton("9",
addButton("/",

insert);
insert);
insert);
command);

addButton("4",
addButton("5",
addButton("6",
addButton("*",

insert);
insert);
insert);
command);

addButton("1",
addButton("2",
addButton("3",
addButton("-",

insert);
insert);
insert);
command);

addButton("0",
addButton(".",
addButton("=",
addButton("+",

insert);
insert);
command);
command);

add(panel, BorderLayout.CENTER);

/**
* Dodaje przycisk do panelu centralnego.
* @param label etykieta przycisku
* @param listener suchacz przyciskw
*/
private void addButton(String label, ActionListener listener)
{
JButton button = new JButton(label);
button.addActionListener(listener);
panel.add(button);
}
/**
* Ta akcja wstawia acuch akcji przycisku na kocu tekstu do wywietlenia.
*/
private class InsertAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String input = event.getActionCommand();
if (start)
{
display.setText("");
start = false;
}
display.setText(display.getText() + input);
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

405

/**
* Ta akcja wykonuje polecenia okrelone przez akcj przycisku.
*/
private class CommandAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String command = event.getActionCommand();

if (start)
{
if (command.equals("-"))
{
display.setText(command);
start = false;
}
else lastCommand = command;
}
else
{
calculate(Double.parseDouble(display.getText()));
lastCommand = command;
start = true;
}

/**
* Wykonuje oczekujce dziaania.
* @param x warto, ktra ma by poczona z poprzednim wynikiem.
*/

public void calculate(double x)


{
if (lastCommand.equals("+")) result += x;
else if (lastCommand.equals("-")) result -= x;
else if (lastCommand.equals("*")) result *= x;
else if (lastCommand.equals("/")) result /= x;
else if (lastCommand.equals("=")) result = x;
display.setText("" + result);
}

Oczywicie niewiele programw ma tak regularny interfejs jak kalkulator. W praktyce wykorzystywane s niewielkie siatki (zazwyczaj skadajce si z jednego wiersza lub jednej
kolumny), za pomoc ktrych ustawia si niektre obszary okna. Na przykad panel z rozkadem siatkowym mona wykorzysta do utworzenia szeregu przyciskw o takich samych
rozmiarach.
java.awt.GridLayout 1.0

GridLayout(int rows, int columns)

GridLayout(int rows, int columns, int hgap, int vgap)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

406

Java. Podstawy
Tworzy nowy obiekt GridLayout. Parametr rows lub columns (ale nie oba naraz)
moe mie warto zero, co oznacza dowoln liczb komponentw w wierszu
lub kolumnie.
Parametry:

rows

Liczba wierszy siatki.

columns

Liczba kolumn siatki.

hgap

Przerwa w poziomie w pikselach (wartoci ujemne


powoduj nachodzenie na siebie elementw).

vgap

Przerwa w pionie w pikselach (wartoci ujemne


powoduj nachodzenie na siebie elementw).

9.3. Wprowadzanie tekstu


Jestemy ju gotowi na wprowadzenie elementw Swing interfejsu uytkownika. Zaczniemy
od komponentw pozwalajcych na wprowadzanie i edycj tekstu. Dane tekstowe mona
odbiera za pomoc komponentw JTextField i JTextArea. Pole tekstowe (ang. text field)
przyjmuje tylko jeden wiersz tekstu, a obszar tekstowy (ang. text area) wiele wierszy. Pole
hasa JPasswordField przyjmuje jeden wiersz tekstu, nie pokazujc jego treci.
Wszystkie trzy wymienione klasy dziedzicz po klasie JTextComponent. Obiektu samej tej
klasy nie mona utworzy, poniewa jest to klasa abstrakcyjna. Z drugiej strony, jak to czsto bywa w Javie, podczas przeszukiwania API poszukiwane metody mog si znajdowa
w nadklasie JTextComponent, a nie w jednej z jej podklas. Na przykad metody pobierajce
i ustawiajce tekst w polu tekstowym i obszarze tekstowym nale do klasy JTextComponent.
javax.swing.text.JTextComponent 1.2

String getText()

void setText(String text)

Pobiera lub ustawia tekst komponentu.

boolean isEditable()

void setEditable(boolean b)

Pobiera lub ustawia wasno editable, ktra okrela, czy uytkownik moe
edytowa zawarto komponentu tekstowego.

9.3.1. Pola tekstowe


Najczciej pola tekstowe s dodawane do okien za porednictwem panelu lub innego kontenera podobnie jak przyciski:
JPanel panel = new JPanel();
JTextField textField = new JTextField("acuch testowy", 20);
panel.add(textField);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

407

Powyszy fragment programu dodaje pole tekstowe i inicjuje je, wstawiajc do niego acuch
acuch testowy. Drugi parametr tego konstruktora ustawia szeroko pola. W tym przypadku jest to 20 kolumn. Niestety kolumna nie naley do precyzyjnych jednostek miary.
Jedna kolumna ma szeroko jednego znaku fontu uytego do napisania tekstu. Dziki temu,
jeli spodziewanych jest n znakw tekstu lub mniej, szeroko kolumny mona ustawi na n.
Metoda ta nie sprawdza si jednak dobrze w praktyce. Dla pewnoci naley zawsze doda
1 lub 2 do maksymalnej dugoci danych wejciowych. Ponadto naley pamita, e liczba
kolumn jest tylko wskazwk dla AWT, ktra okrela preferowany rozmiar. Jeli zarzdca
rozkadu jest zmuszony zwikszy lub zmniejszy pole tekstowe, moe odpowiednio dostosowa jego rozmiar. Szeroko kolumny ustawiona w konstruktorze JTextField nie stanowi
grnego limitu znakw, ktre moe wprowadzi uytkownik. Moliwe jest wpisanie duszego acucha, ktry po przekroczeniu szerokoci pola bdzie mona przewija. Uytkownicy nie lubi przewijanych pl tekstowych, a wic nie naley skpi dla nich miejsca. Liczb
kolumn mona ustawi ponownie w czasie dziaania programu za pomoc metody setCo
lumns.
Po zmianie rozmiaru pola tekstowego za pomoc metody setColumns naley wywoa metod revalidate zawierajcego je kontenera.
textField.setColumns(10);
panel.revalidate();

Metoda revalidate ponownie ustala rozmiar i rozkad wszystkich komponentw znajdujcych si w kontenerze. Po uyciu metody revalidate zarzdca rozkadu zmienia
rozmiar kontenera, dziki czemu moe by widoczne pole tekstowe o zmienionym
rozmiarze.
Metoda revalidate naley do klasy JComponent. Nie zmienia ona od razu rozmiaru
komponentu, ale zaznacza go jako kandydata do takiej zmiany. Podejcie to pozwala
unikn powtarzania oblicze, w przypadku gdy konieczna jest zmiana rozmiaru wielu
komponentw. Aby jednak obliczy ponownie rozmiar wszystkich komponentw w ramce
JFrame, naley wywoa metod validate klasa JFrame nie dziedziczy po klasie
JComponent.

Z reguy pole tekstowe ma na celu umoliwienie uytkownikowi wprowadzenia (lub edycji


istniejcego) tekstu. Bardzo czsto na pocztku pola te s puste. Aby tak byo, naley pomin parametr acuchowy w konstruktorze JTextField:
JTextField textField = new JTextField(20);

Tekst w polu tekstowym mona zmieni w dowolnej chwili za pomoc metody setText z klasy
JTextComponent, o ktrej bya mowa wczeniej. Na przykad:
textField.setText("Dzie dobry!");

Do sprawdzania, co wpisa uytkownik w polu tekstowym, suy metoda getText. Zwraca


ona acuch tekstu, ktry zosta wprowadzony przez uytkownika. Aby usun wiodce
i kocowe biae znaki z tekstu znajdujcego si w polu tekstowym, naley wywoa metod
trim na rzecz danych zwracanych przez metod getText:
String text = textField.getText().trim();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

408

Java. Podstawy
Do ustawiania kroju czcionki tekstu wprowadzanego przez uytkownika suy metoda setFont.
javax.swing.JTextField 1.2

JTextField(int cols)

Tworzy puste pole JTextField o szerokoci okrelonej przez podan liczb kolumn.

JTextField(String text, int cols)

Tworzy pole tekstowe zawierajce okrelony acuch znakw i majce okrelon


szeroko w kolumnach.

int getColumns()

void setColumns(int cols)

Pobiera lub ustawia liczb kolumn pola tekstowego.


javax.swing.JComponent 1.2

void revalidate()

Wymusza ponowne ustalenie pooenia i rozmiaru komponentu.

void setFont(Font f)

Ustawia krj czcionki dla komponentu.


java.awt.Component 1.0

void validate()

Ponownie ustala pooenie i rozmiar komponentu. Jeli komponent jest kontenerem,


ponownie ustalane s pooenie i rozmiar zawartych w nim komponentw.

Font getFont()

Pobiera nazw kroju czcionki komponentu.

9.3.2. Etykiety komponentw


Etykiety s komponentami przechowujcymi tekst. Nie posiadaj adnych ozdobnikw (na
przykad obramowania). Nie reaguj na dane wprowadzane przez uytkownika. Etykieta
moe by identyfikatorem komponentu. Na przykad pola tekstowe, w przeciwiestwie do
przyciskw, nie posiadaj identyfikujcych je etykiet. Aby nada etykiet komponentowi,
ktry standardowo nie ma identyfikatora, naley:
1.

Utworzy komponent JLabel z odpowiednim tekstem.

2. Umieci ten komponent w odpowiedniej odlegoci od komponentu, ktry ma by

identyfikowany, dziki czemu uytkownik odniesie wraenie, e etykieta dotyczy


waciwego komponentu.
Konstruktor klasy JLabel pozwala okreli pocztkowy tekst lub ikon oraz opcjonalnie
wyrwnanie treci. Do wyrwnania naley uywa staych z interfejsu SwingConstants.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

409

Interfejs ten definiuje kilka bardzo przydatnych staych, jak LEFT, RIGHT, CENTER, NORTH,
EAST itd. Klasa JLabel jest jedn z wielu klas Swing, ktre implementuj ten interfejs.
W zwizku z tym etykiet z wyrwnaniem do prawej mona utworzy na dwa sposoby:
JLabel label = new JLabel("Nazwa uytkownika: ", SwingConstants.RIGHT);

lub
JLabel label = new JLabel("Nazwa uytkownika: ", JLabel.RIGHT);

Metody setText i setIcon umoliwiaj ustawienie tekstu i ikony etykiety w czasie dziaania
programu.
W przyciskach, etykietach i elementach menu poza zwykym tekstem mona uywa
jzyka HTML. Nie zalecamy jednak tego przy przyciskach, poniewa zakcony
zostaje oryginalny styl. Natomiast w etykietach HTML pozwala uzyska ciekawe efekty.
Jedyne, co trzeba zrobi, to otoczy acuch etykiety znacznikami <html></html>:
label = new JLabel("<html><b>Wymagany</b> tekst:</html>");

Uwaga: wywietlenie pierwszego komponentu z etykiet HTML zabiera sporo czasu ze


wzgldu na konieczno zaadowania do skomplikowanych procedur obsugujcych
HTML.

Etykiety mona pozycjonowa w kontenerze tak samo jak inne komponenty. Oznacza to, e
aby umieci etykiet w podanym miejscu, mona zastosowa opisane wczeniej techniki.
javax.swing.JLabel 1.2

JLabel(String text)

JLabel(Icon icon)

JLabel(String text, int align)

JLabel(String text, Icon icon, int align)

Tworzy etykiet.
Parametry:

text

Tekst etykiety

icon

Ikona etykiety

align

Jedna ze staych interfejsu SwingConstants: LEFT


(domylna), CENTER lub RIGHT

String getText()

void setText(String text)

Pobiera lub ustawia tekst etykiety.

Icon getIcon()

void setIcon(Icon icon)

Pobiera lub ustawia ikon etykiety.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

410

Java. Podstawy

9.3.3. Pola hase


Pole hasa to specjalny rodzaj pola tekstowego. Znaki wpisywane w takie pole s niewidoczne
dla wcibskich osb stojcych w pobliu. Zamiast wpisywanych znakw pojawiaj si tak
zwane znaki echa zazwyczaj gwiazdki. W bibliotece Swing dostpna jest klasa JPas
swordField, ktra umoliwia tworzenie tego typu pl.
Pole hasa stanowi jeszcze jeden dowd na potwierdzenie bardzo duych moliwoci wzorca
MVC. Pole hasa wykorzystuje do przechowywania danych te same mechanizmy co zwyke
pole tekstowe, ale jego widok zosta zmieniony w taki sposb, e zamiast wpisywanych znakw pojawiaj si znaki zastpcze.
javax.swing.JPasswordField 1.2

JPasswordField(String text, int columns)

Tworzy pole hasa.

void setEchoChar(char echo)

Ustawia znak echa dla pola hasa. Jest to warto doradcza okrelony styl
moe wymusi stosowanie wasnego znaku. Warto 0 przywraca domylny znak.

char[] getPassword()

Zwraca tekst zawarty w polu hasa. Aby zwikszy bezpieczestwo, naley nadpisa
zawarto zwrconej tablicy, kiedy nie jest ju potrzebna (haso nie jest zwracane
jako acuch, poniewa acuchy pozostaj w maszynie wirtualnej, a zostan
usunite przez system zbierania nieuytkw).

9.3.4. Obszary tekstowe


Czasami konieczne jest odebranie od uytkownika tekstu o dugoci przekraczajcej jeden
wiersz. Do tego celu suy komponent JTextArea. W obszarze tekstowym uytkownik moe
wpisa dowoln liczb wierszy, do rozdzielenia ktrych suy klawisz Enter. Kady wiersz
koczy si symbolem \n. Rysunek 9.13 przedstawia obszar tekstowy w dziaaniu.
W konstruktorze komponentu JTextArea okrela si liczb wierszy i kolumn zajmowanych
przez tworzony obszar. Na przykad:
textArea = new JTextArea(8, 40);

// 8 wierszy po 40 kolumn.

Parametr columns ma takie samo przeznaczenie jak poprzednio. Nadal trzeba pamita o dodaniu kilku dodatkowych kolumn. Podobnie jak wczeniej, liczba wierszy i kolumn nie ogranicza moliwoci uytkownika. Jeli tekst jest zbyt dugi, pojawi si paski przewijania. Do
zmiany liczby wierszy i kolumn su, podobnie jak wczeniej, metody setRows i setColumns.
Liczby te okrelaj tylko preferowany rozmiar zarzdca rozkadu moe zmniejszy lub
zwikszy rozmiar pola tekstowego.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

411

Rysunek 9.13.
Komponenty
tekstowe

Jeli tekst nie mieci si w obszarze tekstowym, cz tekstu zostaje obcita. Aby tego unikn,
mona wczy zawijanie wierszy:
textArea.setLineWrap(true);

// Wczono zawijanie wierszy.

Zawijanie wierszy jest wycznie efektem wizualnym. Nie powoduje ono wstawiania znakw \n.

9.3.5. Panele przewijane


W Swingu obszar tekstowy nie posiada paskw przewijania. Aby si pojawiy, naley obszar
ten umieci wewntrz panelu przewijanego (ang. scroll pane).
textArea = new JTextArea(8, 40);
JScrollPane scrollPane = new JScrollPane(textArea);

Po zastosowaniu powyszego fragmentu kodu widokiem obszaru tekstowego zarzdza panel


przewijany. Paski przewijania pojawiaj si automatycznie, jeli tekst nie mieci si w obszarze
tekstowym, oraz same znikaj, kiedy tekstu zrobi si mniej. Przewijanie jest obsugiwane
wewntrz panelu przewijanego pisany przez programist program nie musi przetwarza
zdarze przewijania.
Ten oglny mechanizm dziaa we wszystkich komponentach, nie tylko obszarach tekstowych.
Aby doda do komponentu paski przewijania, naley umieci go w panelu przewijanym.
Listing 9.2 przedstawia rne komponenty tekstowe. Program wywietla pole tekstowe, pole
hasa oraz obszar tekstowy z paskami przewijania. Pole tekstowe i hasa maj etykiety. Kliknicie przycisku Wstaw powoduje wstawienie zawartoci pl tekstowych do obszaru tekstowego.
Listing 9.2. text/TextComponentFrame.java
package text;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z przykadowymi komponentami tekstowymi.
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

412

Java. Podstawy
public class TextComponentFrame extends JFrame
{
public static final int TEXTAREA_ROWS = 8;
public static final int TEXTAREA_COLUMNS = 20;
public TextComponentFrame()
{
final JTextField textField = new JTextField();
final JPasswordField passwordField = new JPasswordField();
JPanel northPanel = new JPanel();
northPanel.setLayout(new GridLayout(2, 2));
northPanel.add(new JLabel("Nazwa uytkownika: ", SwingConstants.RIGHT));
northPanel.add(textField);
northPanel.add(new JLabel("Haso: ", SwingConstants.RIGHT));
northPanel.add(passwordField);
add(northPanel, BorderLayout.NORTH);
final JTextArea textArea = new JTextArea(TEXTAREA_ROWS, TEXTAREA_COLUMNS);
JScrollPane scrollPane = new JScrollPane(textArea);
add(scrollPane, BorderLayout.CENTER);
// Dodanie przycisku wstawiajcego tekst do obszaru tekstowego
JPanel southPanel = new JPanel();
JButton insertButton = new JButton("Wstaw");
southPanel.add(insertButton);
insertButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
textArea.append("Nazwa uytkownika: " + textField.getText() + "
Haso: "
+ new String(passwordField.getPassword()) + "\n");
}
});
add(southPanel, BorderLayout.SOUTH);
pack();
}
}

Komponent JTextArea wywietla wycznie czysty tekst, bez specjalnych krojw


czcionek i formatowania. Aby wywietla sformatowany tekst (np. HTML), mona
uy klasy JEditorPane, ktra zostaa opisana w drugim tomie.
javax.swing.JTextArea 1.2

JTextArea()

JTextArea(int rows, int cols)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

413

JTextArea(String text, int rows, int cols)

Tworzy obszar tekstowy.

void setColumns(int cols)

Ustawia preferowan liczb kolumn szerokoci obszaru tekstowego.

void setRows(int rows)

Ustawia preferowan liczb wierszy wysokoci obszaru tekstowego.

void append(String newText)

Wstawia podany tekst na kocu tekstu znajdujcego si w obszarze tekstowym.

void setLineWrap(boolean wrap)

Wcza lub wycza zawijanie wierszy.

void setWrapStyleWord(boolean word)

Warto true oznacza zawijanie wierszy z uwzgldnieniem caych wyrazw.


Warto false oznacza zawijanie wierszy bez uwzgldnienia caych wyrazw.

void setTabSize(int c)

Ustawia tabulacj na c kolumn. Naley zauway, e tabulatory nie s zamieniane


na spacje, ale powoduj wyrwnanie z kolejnym tabulatorem.
javax.swing.JScrollPane 1.2

JScrollPane(Component c)

Tworzy panel przewijany wywietlajcy zawarto okrelonego komponentu.


Paski przewijania pojawiaj si, kiedy komponent jest wikszy ni widok.

9.4. Komponenty umoliwiajce wybr opcji


Umiemy ju odbiera dane od uytkownikw, ale w wielu sytuacjach lepszym rozwizaniem
jest podanie kilku opcji do wyboru ni pozwolenie uytkownikom na samodzielne wpisanie
informacji. Opcje do wyboru moe reprezentowa zestaw przyciskw lub lista elementw
(metoda ta nie wymaga sprawdzania bdw). W tym podrozdziale omawiamy tworzenie
pl wyboru (ang. checkbox), przecznikw (ang. radio button), list opcji do wyboru oraz
suwakw (ang. slider).

9.4.1. Pola wyboru


Do odbierania danych typu tak lub nie najlepiej nadaj si pola wyboru. Pola te maj
automatycznie nadawane etykiety. Zaznaczenie opcji polega na klikniciu wybranego pola,
a usunicie zaznaczenia na klikniciu pola, ktre jest zaznaczone. Do wczania lub wyczania opcji mona te uy klawisza spacji, kiedy pole wyboru jest aktywne.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

414

Java. Podstawy
Rysunek 9.14 przedstawia prosty program zawierajcy dwa pola wyboru. Jedno z nich wcza
lub wycza atrybut kursywy czcionki, a drugie robi to samo z atrybutem pogrubienia.
Zauwamy, e pole po prawej stronie jest aktywne, na co wskazuje prostoktna obwdka.
Kiedy uytkownik kliknie jedno z pl wyboru, nastpuje odwieenie ekranu i zastosowanie
nowych atrybutw czcionki.

Rysunek 9.14.
Pola wyboru

Obok pola wyboru musi si znajdowa etykieta identyfikacyjna. Jej tekst ustala si w konstruktorze.
bold = new JCheckBox("Pogrubienie");

Do wczania i wyczania opcji suy metoda setSelected. Na przykad:


bold.setSelected(true);

Metoda setSelected sprawdza aktualny stan kadego pola wyboru. Jest to false, jeli pole
wyboru nie jest zaznaczone, a true, jeli jest zaznaczone.
Kliknicie pola wyboru przez uytkownika uruchamia akcj. Z polem wyboru jak zawsze wiemy suchacza akcji. W omawianym programie oba pola wspdziel jednego
suchacza.
ActionListener listener = . . .
bold.addActionListener(listener);
italic.addActionListener(listener);

Metoda actionPerformed sprawdza stan pl Kursywa i Pogrubienie oraz ustawia czcionk


panelu na zwyk, pogrubion, kursyw lub pogrubion kursyw.
public void actionPerformed(ActionEvent event)
{
int mode = 0;
if (bold.isSelected()) mode += Font.BOLD;
if (italic.isSelected()) mode += Font.ITALIC;
label.setFont(new Font("Serif", mode, FONTSIZE));
}

Listing 9.3 przedstawia peny kod rdowy tego programu.


Listing 9.3. checkBox/CheckBoxTest.java
package checkBox;
import java.awt.*;
import javax.swing.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

415

/**
* @version 1.33 2007-06-12
* @author Cay Horstmann
*/
public class CheckBoxTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new CheckBoxFrame();
frame.setTitle("CheckBoxTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
javax.swing.JCheckBox 1.2

JCheckBox(String label)

JCheckBox(String label, Icon icon)

Tworzy pole wyboru, ktre nie jest pocztkowo zaznaczone.

JCheckBox(String label, boolean state)

Tworzy pole wyboru z podan etykiet o okrelonym stanie pocztkowym.

boolean isSelected ()

void setSelected(boolean state)

Pobiera lub ustawia stan pola wyboru.

9.4.2. Przeczniki
W poprzednim programie mona byo zaznaczy jedno z pl, oba lub nie zaznaczy adnego.
Istnieje wiele sytuacji, w ktrych chcemy, aby uytkownik mg wybra tylko jedn z kilku
opcji. Zaznaczenie jednego pola powoduje automatyczne usunicie zaznaczenia innego.
Grupy tego typu pl s te czsto nazywane grupami przyciskw radiowych, poniewa
dziaaniem przypominaj przyciski wyboru w starym radiu. Wcinicie jednego przycisku
powoduje, e wczeniej wcinity przycisk wyskakuje. Rysunek 9.15 przedstawia typowy
przykad ich zastosowania. Uytkownik ma do wyboru cztery rozmiary czcionki: Maa,
rednia, Dua i Bardzo dua oczywicie moliwy jest wybr tylko jednej opcji naraz.
Implementacja grup przyciskw radiowych w Swingu jest atwa. Dla kadej grupy naley
utworzy obiekt typu ButtonGroup. Nastpnie do tego obiektu dodaje si obiekty typu JRadio
Button. Zadaniem obiektu grupy przyciskw jest wyczanie wczeniej wczonego przycisku w odpowiedzi na wczenie innego.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

416

Java. Podstawy

Rysunek 9.15.
Grupa przyciskw
radiowych
(przecznikw)

ButtonGroup group = new ButtonGroup();


JRadioButton smallButton = new JRadioButton("Maa", false);
group.add(smallButton);
JRadioButton mediumButton = new JRadioButton("rednia", true);
group.add(mediumButton);
. . .

Drugi argument konstruktora powinien mie warto true w przeczniku, ktry ma by


wczony na pocztku, oraz false w pozostaych przecznikach. Naley pamita, e obiekt
grupy przyciskw kontroluje tylko ich zachowanie. Do grupowania przyciskw w celu odpowiedniej ich aranacji naley uy jakiego kontenera, jak np. JPanel.
Wracajc do rysunkw 9.14 i 9.15, mona zauway, e przyciski radiowe wygldaj inaczej ni pola wyboru. Te drugie s prostoktne i po zaznaczeniu pokazuje si w nich haczyk.
Przyciski radiowe s okrge, a kiedy si je kliknie, pojawia si w nich kropka.
Mechanizm powiadamiania o zdarzeniach w przecznikach jest taki sam jak we wszystkich
innych przyciskach. Kiedy uytkownik klika przecznik, generuje on akcj. Omawiany program zawiera definicj suchacza akcji, ktry ustawia odpowiedni rozmiar czcionki:
ActionListener listener = new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Parametr size odwouje si do ostatniego parametru metody addRadioButton.
label.setFont(new Font("Serif", Font.PLAIN, size));
}
};

Porwnajmy powyszego suchacza ze suchaczem z programu z polami wyboru. Kady


przecznik posiada osobny obiekt suchacza. Kady z tych obiektw ma jasno okrelone
zadanie ustawienie rozmiaru czcionki na okrelon warto. W przypadku pl wyboru
zastosowalimy nieco inn metod. Oba pola maj tego samego suchacza akcji. Wywouje
on metod, ktra sprawdza aktualny stan obu pl.
Czy mona by byo zastosowa t metod w przypadku przecznikw? Mona by byo
utworzy jednego suchacza, ktry obliczaby rozmiar w nastpujcy sposb:
if (smallButton.isSelected()) size = 8;
else if (mediumButton.isSelected()) size = 12;
. . .

Wolelimy jednak zastosowa osobne obiekty nasuchujce akcji, poniewa cilej wi


wartoci z przyciskami.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

417

Wiadomo, e w grupie przecznikw tylko jeden z nich jest wczony. Przydaaby


si moliwo sprawdzenia, ktry jest wczony, bez potrzeby sprawdzania wszystkich przyciskw w grupie. Poniewa obiekt ButtonGroup kontroluje wszystkie przyciski,
dobrze by byo, gdyby udostpnia referencj do wcinitego przycisku. Klasa Button
Group zawiera metod getSelection, ale nie zwraca ona przycisku, ktry jest wcinity.
Zwraca natomiast referencj ButtonModel do modelu zwizanego z tym przyciskiem.
Niestety adna z metod klasy ButtonModel nie jest zbyt pomocna. Interfejs ButtonModel
dziedziczy metod getSelectedObjects po interfejsie ItemSelectable, ktra jest bezuyteczna, zwracajc warto null. Obiecujca jest metoda getActionCommand, poniewa
polecenie akcji przecznika jest jego etykiet tekstow. Jednak polecenie akcji jego
modelu ma warto null. Wartoci polece akcji modeli s ustawiane tylko wtedy, gdy
zostan bezporednio ustawione polecenia akcji wszystkich przecznikw za pomoc
metody setActionCommand. Dziki temu mona sprawdzi polecenie akcji aktualnie
zaznaczonego przycisku za pomoc wywoania buttonGroup.getSelection().getAction
Command().

Listing 9.4 przedstawia kompletny kod programu ustawiajcego rozmiar czcionki za pomoc
przecznikw.
Listing 9.4. radioButton/RadioButtonFrame.java
package radioButton;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z przykadow etykiet tekstow i przecznikami sucymi do wyboru rozmiaru czcionki
*/
public class RadioButtonFrame extends JFrame
{
private JPanel buttonPanel;
private ButtonGroup group;
private JLabel label;
private static final int DEFAULT_SIZE = 36;
public RadioButtonFrame()
{
// Dodanie przykadowej etykiety tekstowej
label = new JLabel("Ko i w grali w koci z pikn m u rda.");
label.setFont(new Font("Serif", Font.PLAIN, DEFAULT_SIZE));
add(label, BorderLayout.CENTER);
// Dodanie przecznikw
buttonPanel = new JPanel();
group = new ButtonGroup();
addRadioButton("Maa", 8);
addRadioButton("rednia", 12);
addRadioButton("Dua", 18);
addRadioButton("Bardzo dua", 36);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

418

Java. Podstawy
add(buttonPanel, BorderLayout.SOUTH);
pack();
}
/**
* Tworzy przecznik ustawiajcy rozmiar czcionki przykadowego tekstu.
* @param name acuch identyfikujcy przecznik
* @param size rozmiar czcionki ustawiany przez ten przecznik
*/
public void addRadioButton(String name, final int size)
{
boolean selected = size == DEFAULT_SIZE;
JRadioButton button = new JRadioButton(name, selected);
group.add(button);
buttonPanel.add(button);
// Ten suchacz ustawia rozmiar czcionki etykiety
ActionListener listener = new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Parametr size odwouje si do ostatniego parametru metody addRadioButton
label.setFont(new Font("Serif", Font.PLAIN, size));
}
};
button.addActionListener(listener);
}
}
javax.swing.JRadioButton 1.2

JRadioButton(String label, Icon icon)

Tworzy pocztkowo niezaznaczony przecznik.

JRadioButton(String label, boolean state)

Tworzy przecznik o okrelonej etykiecie i stanie pocztkowym.


javax.swing.ButtonGroup 1.2

void add(AbstractButton b)

Dodaje przycisk do grupy.

ButtonModel getSelection()

Zwraca model przycisku.


javax.swing.ButtonModel 1.2

String getActionCommand()

Zwraca polecenie akcji modelu przycisku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

419

javax.swing.AbstractButton 1.2

void setActionCommand(String s)

Ustawia polecenie akcji dla przycisku i jego modelu.

9.4.3. Obramowanie
Jeli jedno okno zawiera kilka grup przecznikw, naley w jaki sposb te grupy oznaczy.
Do tego celu mona uy obramowania Swing. Obramowanie mona zastosowa do kadego
komponentu, ktry rozszerza klas JComponent. Najczciej obramowanie stosuje si wok
panelu, ktry zawiera elementy interfejsu uytkownika, jak przeczniki.
Do wyboru jest kilka rodzajw obramowa, ale sposb ich uycia jest taki sam dla wszystkich.
1.

Wywoaj statyczn metod klasy BorderFactory, ktra tworzy obramowanie.


Do wyboru s nastpujce style (zobacz rysunek 9.16):

LoweredBevel (ukos dolny),

RaisedBevel (ukos grny),

Etched (wgbienie),

Line (linia),

Matte (linia) umoliwia okrelenie gruboci poszczeglnych krawdzi,

Empty (pusta) tworzy obramowanie, ktre w ogle nie zajmuje miejsca.

2. Aby doda tytu do obramowania, naley je przekaza do metody


BorderFactory.createTitledBorder.
3. Mona pj na cao i poczy kilka obramowa: BorderFactory.
createCompoundBorder.
4. Utworzone obramowanie dodaje si do komponentu za pomoc metody setBorder
z klasy JComponent.
Rysunek 9.16.
Rodzaje
obramowa

Poniszy fragment programu tworzy obramowanie w stylu Etched z tytuem dla panelu:
Border etched = BorderFactory.createEtchedBorder()
Border titled = BorderFactory.createTitledBorder(etched, "Tytu");
panel.setBorder(titled);

Aby sprawdzi, jak wygldaj poszczeglne style obramowa, uruchom program z listingu 9.5.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

420

Java. Podstawy

Listing 9.5. border/BorderFrame.java


package border;
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.border.*;

/**
* Ramka z przecznikami sucymi do wyboru stylu obramowania.
*/
public class BorderFrame extends JFrame
{
private JPanel demoPanel;
private JPanel buttonPanel;
private ButtonGroup group;
public BorderFrame()
{
demoPanel = new JPanel();
buttonPanel = new JPanel();
group = new ButtonGroup();
addRadioButton("Lowered bevel", BorderFactory.createLoweredBevelBorder());
addRadioButton("Raised bevel", BorderFactory.createRaisedBevelBorder());
addRadioButton("Etched", BorderFactory.createEtchedBorder());
addRadioButton("Line", BorderFactory.createLineBorder(Color.BLUE));
addRadioButton("Matte", BorderFactory.createMatteBorder(10, 10, 10, 10,
Color.BLUE));
addRadioButton("Empty", BorderFactory.createEmptyBorder());
Border etched = BorderFactory.createEtchedBorder();
Border titled = BorderFactory.createTitledBorder(etched, "Typy obramowania");
buttonPanel.setBorder(titled);
setLayout(new GridLayout(2, 1));
add(buttonPanel);
add(demoPanel);
pack();
}
public void addRadioButton(String buttonName, final Border b)
{
JRadioButton button = new JRadioButton(buttonName);
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
demoPanel.setBorder(b);
}
});
group.add(button);
buttonPanel.add(button);
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

421

Rne obramowania maj rne opcje suce do ustawiania szerokoci i koloru. Szczegowe informacje na ten temat znajduj si w wycigach z API. Wielbicieli obramowa ucieszy
fakt, e istnieje klasa SoftBevelBorder, suca do tworzenia obramowa o mniej ostrych
rogach, oraz e obramowanie LineBorder moe mie take zaokrglone rogi. Wymienione
obramowania mona tworzy wycznie za pomoc konstruktorw klas nie istnieje dla
nich metoda BorderFactory.
javax.swing.BorderFactory 1.2

static Border createLineBorder(Color color)

static Border createLineBorder(Color color, int thickness)

Tworzy obramowanie w postaci zwykej linii.

static MatteBorder createMatteBorder(int top, int left, int bottom,


int right, Color color)

static MatteBorder createMatteBorder(int top, int left, int bottom,


int right, Icon tileIcon)

Tworzy obramowanie o okrelonej gruboci i wypenione okrelonym kolorem


lub powtarzajcym si obrazem.

static Border createEmptyBorder()

static Border createEmptyBorder(int top, int left, int bottom, int right)

Tworzy puste obramowanie.

static Border createEtchedBorder()

static Border createEtchedBorder(Color highlight, Color shadow)

static Border createEtchedBorder(int type)

static Border createEtchedBorder(int type, Color highlight, Color shadow)

Tworzy obramowanie w postaci linii z efektem trjwymiarowym.


Parametry:

highlight, shadow

Kolory dla efektu trjwymiarowego

type

Warto EtchedBorder.RAISED
lub EtchedBorder.LOWERED

static Border createBevelBorder(int type)

static Border createBevelBorder(int type, Color highlight, Color shadow)

static Border createLoweredBevelBorder()

static Border createRaisedBevelBorder()

Tworzy obramowanie wygldajce jak wznoszca si lub opadajca skona


powierzchnia.
Parametry:

highlight, shadow

Kolory dla efektu trjwymiarowego

type

Warto EtchedBorder.RAISED
lub EtchedBorder.LOWERED

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

422

Java. Podstawy

static TitledBorder createTitledBorder(String title)

static TitledBorder createTitledBorder(Border border)

static TitledBorder createTitledBorder(Border border, String title)

static TitledBorder createTitledBorder(Border border, String title,


int justification, int position)

static TitledBorder createTitledBorder(Border border, String title,


int justification, int position, Font font)

static TitledBorder createTitledBorder(Border border, String title,


int justification, int position, Font font, Color color)

Tworzy obramowanie z tytuem o okrelonych cechach.


Parametry:

title

Tytu

border

Obramowanie, do ktrego ma by
dodany tytu

justification

Jedna ze staych TitledBorder: LEFT,


CENTER, RIGHT, LEADING, TRAILING,
DEFAULT_JUSTIFICATION (do lewej)

position

Jedna ze staych TitledBorder: ABOVE_TOP,


TOP, BELOW_TOP, ABOVE_BOTTOM, BOTTOM,
BELOW_BOTTOM, DEFAULT_POSITION (gra)

font

Krj czcionki w tytule

color

Kolor tytuu

static CompoundBorder createCompoundBorder(Border outsideBorder,


Border insideBorder)

Tworzy obramowanie z dwch rodzajw obramowa.


javax.swing.border.SoftBevelBorder 1.2

SoftBevelBorder(int type)

SoftBevelBorder(int type, Color highlight, Color shadow)

Tworzy obramowanie ukone o mniej ostrych rogach.


Parametry:

highlight, shadow

Kolory dla efektu trjwymiarowego

type

Warto EtchedBorder.RAISED
lub EtchedBorder.LOWERED

javax.swing.border.LineBorder 1.2

public LineBorder(Color color, int thickness, boolean roundedCorners)

Tworzy obramowanie o okrelonym kolorze i gruboci. Jeli parametr


roundedCorners ma warto true, rogi s zaokrglone.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

423

javax.swing.JComponent 1.2

void setBorder(Border border)

Ustawia obramowanie komponentu.

9.4.4. Listy rozwijalne


Jeli opcji do wyboru jest duo, przeczniki nie zdaj egzaminu, poniewa zajmuj zbyt
duo miejsca. W takim przypadku lepiej uy listy rozwijalnej (ang. combo box). Kliknicie
takiego komponentu powoduje rozwinicie listy opcji, z ktrych uytkownik moe wybra
tylko jedn (zobacz rysunek 9.17).
Rysunek 9.17.
Lista rozwijalna

Jeli pole listy rozwijalnej jest edytowalne (editable), aktualnie wybran opcj mona edytowa, tak jakby bya polem tekstowym. Dlatego komponent ten jest te nazywany polem
typu kombi czy w sobie elastyczno pl tekstowych z zestawem ustalonych z gry
opcji. Do tworzenia tego typu pl suy klasa JComboBox.
Od Java 7 klasa JComboBox jest generyczna. Przykadowo JComboBox<String> przechowuje
obiekty typu String, a JComboBox<Integer> liczby cakowite.
Aby lista rozwijalna bya edytowalna, naley uy metody setEditable. Naley pamita,
e edytowanie ma wpyw wycznie na wybrany element. Nie powoduje to zmiany listy opcji
do wyboru.
Biecy wybr, ktry mg zosta zmodyfikowany, jeli pole jest edytowalne, mona pobra
za pomoc metody getSelectedItem. Jednak w edytowalnej licie element ten moe by
kadego typu, w zalenoci od tego, jaki edytor odbiera dane edytowane przez uytkownika
i zamienia wyniki na obiekty (opis edytorw znajduje si w rozdziale 6. drugiego tomu).
Jeli pole nie jest edytowalne, lepiej zastosowa wywoanie:
combo.getItemAt(combo.getSelectedIndex())

Instrukcja ta zwraca wybrany element z poprawnym typem.


W przedstawionym programie uytkownik moe wybra z listy jeden rodzaj czcionki (Serif,
SansSerif, Monospaced itd.). Moe take wpisa nazw innego fontu.
Poszczeglne opcje wstawia si za pomoc metody addItem. W tym programie metoda ta
jest wywoywana tylko w konstruktorze, ale mona j wywoywa w dowolnym miejscu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

424

Java. Podstawy
JComboBox<String> faceCombo = new JComboBox<>();
faceCombo.addItem("Serif");
faceCombo.addItem("SansSerif");
. . .

Ta metoda dodaje acuch na kocu listy. Elementy mona take dodawa w dowolnym
miejscu listy za pomoc metody InsertItemAt:
faceCombo.insertItemAt("Monospaced", 0);

// Dodanie opcji na pocztku listy.

Dodawane elementy mog by dowolnego typu lista rozwijalna wywouje metod toString
przed wywietleniem kadego elementu.
Do usuwania elementw z listy w czasie dziaania programu su metody removeItem
i removeItemAt. Pierwszej naley poda tre elementu, ktry ma by usunity, a drugiej numer
pozycji elementu do usunicia.
faceCombo.removeItem("Monospaced");
faceCombo.removeItemAt(0);

// usunicie pierwszego elementu

Metoda removeAllItems usuwa wszystkie elementy naraz.


Metoda addItem nie dziaa wydajnie przy dodawaniu duej liczby elementw do
listy rozwijalnej. Zamiast niej lepiej utworzy obiekt DefaultComboBoxModel, zapeni
go za pomoc metody addElement, a nastpnie wywoa metod setModel z klasy
JComboBox.

Kiedy uytkownik wybiera element z listy, generowana jest akcja. Aby sprawdzi, ktry
element zosta wybrany, naley wywoa metod getSource na rzecz parametru zdarzenia
w celu uzyskania referencji do listy rozwijalnej, ktra wysaa to zdarzenie. Nastpnie naley
wywoa metod getSelectedItem sprawdzajc, ktry element jest aktualnie wybrany. Zwrcon warto trzeba rzutowa na odpowiedni typ, zazwyczaj String.
public void actionPerformed(ActionEvent event)
{
label.setFont(new Font(
faceCombo.getItemAt(faceCombo.setSelectedIndex()),
Font.PLAIN,
DEFAULT_SIZE));
}

Listing 9.6 przedstawia kompletny kod programu.


Listing 9.6. comboBox/ComboBoxFrame.java
package comboBox;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z przykadow etykiet tekstow i list rozwijaln umoliwiajc wybr kroju czcionki.
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

425

public class ComboBoxFrame extends JFrame


{
private JComboBox<String> faceCombo;
private JLabel label;
private static final int DEFAULT_SIZE = 24;
public ComboBoxFrame()
{
// Dodanie tekstu etykiety
label = new JLabel("Ko i pies grali w koci z pikn m u rda.");
label.setFont(new Font("Serif", Font.PLAIN, DEFAULT_SIZE));
add(label, BorderLayout.CENTER);
// Tworzenie listy rozwijalnej i dodawanie nazw czcionek
faceCombo = new JComboBox<>();
faceCombo.addItem("Serif");
faceCombo.addItem("SansSerif");
faceCombo.addItem("Monospaced");
faceCombo.addItem("Dialog");
faceCombo.addItem("DialogInput");
// Suchacz listy rozwijalnej zmienia krj pisma etykiety na czcionk wybran przez uytkownika
faceCombo.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
label.setFont(new
Font(faceCombo.getItemAt(faceCombo.getSelectedIndex()), Font.PLAIN,
DEFAULT_SIZE));
}
});
// Dodanie listy rozwijalnej do panelu znajdujcego si przy poudniowej krawdzi ramki
JPanel comboPanel = new JPanel();
comboPanel.add(faceCombo);
add(comboPanel, BorderLayout.SOUTH);
pack();
}
}

Do tworzenia list opcji widocznych cay czas suy komponent JList. Zosta on
opisany w rozdziale 6. drugiego tomu.
javax.swing.JComboBox 1.2

boolean isEditable()

void setEditable(boolean b)

Pobiera lub ustawia wasno editable listy rozwijalnej.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

426

Java. Podstawy

void addItem(Object item)

Dodaje element do listy.

void insertItemAt(Object item, int index)

Wstawia element do listy na okrelonej pozycji.

void removeItem(Object item)

Usuwa element z listy.

void removeItemAt(int index)

Usuwa element z listy znajdujcy si na okrelonej pozycji.

void removeAllItems()

Usuwa wszystkie elementy z listy.

Object getSelectedItem()

Zwraca aktualnie wybrany element.

9.4.5. Suwaki
Listy rozwijalne pozwalaj na wybr jednej z kilku opcji. Suwaki natomiast umoliwiaj
wybr opcji z szerszego spektrum wartoci, na przykad jednej ze stu.
Najczciej stosowana metoda tworzenia suwakw jest nastpujca:
JSlider slider = new JSlider(min, max, initialValue);

Jeli parametry okrelajce warto minimaln, maksymaln i pocztkow nie zostan podane,
bd miay wartoci odpowiednio 0, 100 i 50.
Aby utworzy pionowy suwak, naley zastosowa nastpujc metod:
JSlider slider = new JSlider(SwingConstants.VERTICAL, min, max, initialValue);

Przedstawione konstruktory tworz zwyke suwaki, jak pierwszy na rysunku 9.18. Niebawem nauczymy si ozdabia suwaki rozmaitymi dodatkami.
W miar przesuwania przez uytkownika gaki suwaka przyjmuje on kolejne wartoci od
minimalnej do maksymalnej. Kiedy zmienia si warto, do wszystkich suchaczy zmian
wysyane jest zdarzenie typu ChangeEvent. Aby mc odbiera powiadomienia o zmianach,
naley wywoa metod addChangeListener i zainstalowa obiekt implementujcy interfejs
ChangeListener. Interfejs ten zawiera jedn metod o nazwie stateChanged. W metodzie tej
naley sprawdzi warto suwaka:
public void stateChanged(ChangeEvent event)
{
JSlider slider = (JSlider) event.getSource();
int value = slider.getValue();
. . .
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

427

Rysunek 9.18.
Suwaki

Suwak mona przyozdobi podziak (ang. ticks). W omawianym programie dla drugiego
suwaka zastosowano nastpujce ustawienia:
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);

Duga kreska pojawia si co 20 jednostek, a krtsza co pi. Jednostki odnosz si do wartoci suwaka, nie do pikseli.
Te instrukcje su tylko do ustawienia liczby jednostek, co ile maj si pojawia znaczniki
w postaci kresek. Aby kreski te zostay uwidocznione, potrzebne jest nastpujce wywoanie:
slider.setPaintTicks(true);

Dugie i krtkie kreski s wzajemnie niezalene. Mona na przykad ustawi dug kresk
co 20 jednostek i krtk co siedem, ale taka skala nie byaby zbyt klarowna.
Gak suwaka mona zmusi, aby przyklejaa si do kresek podziaki. Kiedy uytkownik
przecignie gak suwaka i j puci, zostanie ona natychmiast dosunita do najbliszej kreski.
Za aktywowanie tego trybu odpowiada ponisza procedura:
slider.setSnapToTicks(true);

Funkcja dosuwania nie dziaa tak dobrze, jak mona by byo sobie tego yczy.
Dopki gaka suwaka rzeczywicie nie zostanie dosunita, obiekt nasuchujcy
zmian raportuje wartoci, ktre nie odpowiadaj kreskom podziaki. Ponadto kliknicie
obok gaki takiego suwaka (czynno ta w innych suwakach powoduje przesunicie gaki
w stron kliknicia) nie powoduje jej przesunicia do kolejnej kreski.

Dugie kreski podziaki mona oznaczy etykietami:


slider.setPaintLabels(true);

Na przykad w przypadku suwaka o zakresie 0 100 i odstpie dugich kresek co 20 jednostek etykiety bd nastpujce: 0, 20, 40, 60, 80, 100.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

428

Java. Podstawy
Istnieje te moliwo zastosowania innych znacznikw, takich jak acuchy lub ikony
(rysunek 9.18). Czynnoci z tym zwizane s nieco skomplikowane. Trzeba zapeni tablic
mieszajc (ang. hash table) kluczami typu Integer i wartociami typu Component. Nastpnie wywouje si metod setLabelTable. Komponenty zostan umieszczone pod kreskami.
Zazwyczaj w takim przypadku uywane s obiekty typu JLabel. Poniszy fragment programu
ustawia etykiety A, B, C, D, E, F:
Hashtable<Integer, Component> labelTable = new Hashtable<Integer, Component>();
labelTable.put(0, new JLabel("A"));
labelTable.put(20, new JLabel("B"));
. . .
labelTable.put(100, new JLabel("F"));
slider.setLabelTable(labelTable);

Wicej informacji na temat tablic mieszajcych znajduje si w rozdziale 13.


Program przedstawiony na listingu 9.7 zawiera take suwaki z ikonami jako etykietami kresek podziaki.
Jeli nie wida etykiet lub kresek podziaki, naley si upewni, czy zostay wywoane metody setPaintTicks(true) i setPaintLabels(true).

Czwarty suwak na rysunku 9.18 jest pozbawiony prowadnicy. Za jej usunicie odpowiedzialna jest ponisza procedura:
slider.setPaintTrack(false);

Kierunek pitego suwaka zosta odwrcony za pomoc poniszej metody:


slider.setInverted(true);

Poniszy przykadowy program demonstruje wszystkie opisane rodzaje suwakw. Kady


suwak posiada obiekt nasuchujcy typu ChangeListener, ktry wstawia biec warto
suwaka do pola tekstowego umiejscowionego na samym dole ramki.
Listing 9.7. slider/SliderFrame.java
package slider;
import
import
import
import

java.awt.*;
java.util.*;
javax.swing.*;
javax.swing.event.*;

/**
* Ramka zawierajca kilka suwakw oraz pole tekstowe pokazujce wartoci ustawiane za ich pomoc
*/
public class SliderFrame extends JFrame
{
private JPanel sliderPanel;
private JTextField textField;
private ChangeListener listener;
public SliderFrame()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

sliderPanel = new JPanel();


sliderPanel.setLayout(new GridBagLayout());
// Wsplny suchacz wszystkich suwakw
listener = new ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
// Aktualizacja pola tekstowego w odpowiedzi na zmian wartoci suwaka
JSlider source = (JSlider) event.getSource();
textField.setText("" + source.getValue());
}
};
// Zwyky suwak
JSlider slider = new JSlider();
addSlider(slider, "Zwyky");
// Suwak z podziak
slider = new JSlider();
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Podziaka");
// Suwak z dosuwaniem gaki do najbliszej kreski
slider = new JSlider();
slider.setPaintTicks(true);
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Dosuwany");
// Suwak bez prowadnicy
slider = new JSlider();
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setPaintTrack(false);
addSlider(slider, "Bez prowadnicy");
// Suwak o odwrconym dziaaniu
slider = new JSlider();
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
slider.setInverted(true);
addSlider(slider, "Odwrcony");
// Suwak z etykietami liczbowymi
slider = new JSlider();
slider.setPaintTicks(true);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

429

430

Java. Podstawy
slider.setPaintLabels(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
addSlider(slider, "Etykiety");
// Suwak z etykietami literowymi
slider = new JSlider();
slider.setPaintLabels(true);
slider.setPaintTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(5);
Dictionary<Integer, Component> labelTable = new Hashtable<>();
labelTable.put(0, new JLabel("A"));
labelTable.put(20, new JLabel("B"));
labelTable.put(40, new JLabel("C"));
labelTable.put(60, new JLabel("D"));
labelTable.put(80, new JLabel("E"));
labelTable.put(100, new JLabel("F"));
slider.setLabelTable(labelTable);
addSlider(slider, "Niestandardowe etykiety");
// Suwak z etykietami ikonowymi
slider = new JSlider();
slider.setPaintTicks(true);
slider.setPaintLabels(true);
slider.setSnapToTicks(true);
slider.setMajorTickSpacing(20);
slider.setMinorTickSpacing(20);
labelTable = new Hashtable<Integer, Component>();
// Dodawanie obrazw kart
labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
labelTable.put(20, new JLabel(new ImageIcon("ten.gif")));
labelTable.put(40, new JLabel(new ImageIcon("jack.gif")));
labelTable.put(60, new JLabel(new ImageIcon("queen.gif")));
labelTable.put(80, new JLabel(new ImageIcon("king.gif")));
labelTable.put(100, new JLabel(new ImageIcon("ace.gif")));
slider.setLabelTable(labelTable);
addSlider(slider, "Ikony");
// Dodawanie pola tekstowego, ktre wywietla warto ustawion na suwaku
textField = new JTextField();
add(sliderPanel, BorderLayout.CENTER);
add(textField, BorderLayout.SOUTH);
pack();
}
/**
* Dodaje suwak do panelu suwakw i wie suchacza.
* @param s suwak

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

* @param description opis suwaka


*/
public void addSlider(JSlider s, String description)
{
s.addChangeListener(listener);
JPanel panel = new JPanel();
panel.add(s);
panel.add(new JLabel(description));
panel.setAlignmentX(Component.LEFT_ALIGNMENT);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridy = sliderPanel.getComponentCount();
gbc.anchor = GridBagConstraints.WEST;
sliderPanel.add(panel, gbc);
}
}
javax.swing.JSlider 1.2

JSlider()

JSlider(int direction)

JSlider(int min, int max)

JSlider(int min, int max, int initialValue)

JSlider(int direction, int min, int max, int initialValue)

Tworzy poziomy suwak o okrelonym kierunku oraz wartociach minimalnej,


maksymalnej i pocztkowej.
Parametry:

direction

SwingConstants.HORIZONTAL

lub SwingConstants.VERTICAL domylna


jest pierwsza z wymienionych wartoci.

min, max

Najmniejsza i najwiksza warto suwaka.


Domylne wartoci to 0 i 100.

initialValue

Warto pocztkowa suwaka domylnie 50.

void setPaintTicks(boolean b)

Jeli parametr b ma warto true, wywietla podziak.

void setMajorTickSpacing(int units)

void setMinorTickSpacing(int units)

Wstawia dugie i krtkie kreski podziaki co okrelon liczb jednostek.

void setPaintLabels(boolean b)

Jeli parametr b ma warto true, wywietla etykiety kresek podziaki.

void setLabelTable(Dictionary table)

Ustawia komponenty stanowice etykiety kresek. Kada para klucz warto


w tabeli ma posta new Integer(warto)/komponent.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

431

432

Java. Podstawy

void setSnapToTicks(boolean b)

Jeli parametr b ma warto true, gaka suwaka dosuwa si do najbliszej kreski


podziaki.

void setPaintTrack(boolean b)

Jeli parametr b ma warto true, wywietlana jest prowadnica, po ktrej przesuwa


si gaka suwaka.

9.5. Menu
Ten rozdzia zaczlimy od opisu najczciej uywanych komponentw, takich jak przyciski,
pola tekstowe i listy rozwijalne. W Swingu mona te tworzy inny rodzaj elementw interfejsu uytkownika znane z aplikacji posiadajcych graficzny interfejs menu rozwijalne.
Pasek menu znajdujcy si na grze okna zawiera nazwy rozwijalnych menu. Kliknicie jednej
z tych nazw powoduje otwarcie odpowiadajcego jej menu, ktre zawiera rne elementy
menu oraz podmenu. Kiedy uytkownik klika element menu, wszystkie menu zostaj zamknite, a do programu wysyany jest komunikat. Rysunek 9.19 przedstawia typowe menu
z podmenu.
Rysunek 9.19.
Menu z podmenu

9.5.1. Tworzenie menu


Proces budowy menu jest bardzo prosty. Najpierw naley utworzy pasek menu:
JMenuBar menuBar = new JMenuBar();

Pasek menu jest zwykym komponentem, ktry mona wstawi w dowolnym miejscu. Zazwyczaj jest on umieszczany na samej grze ramki za pomoc metody setJMenuBar:
frame.setJMenuBar(menuBar);

Dla kadego menu naley utworzy osobny obiekt:


JMenu editMenu = new JMenu("Edycja");

Menu najwyszego rzdu dodaje si do paska menu:


menuBar.add(editMenu);

Elementy menu, separatory i podmenu dodaje si do obiektu menu:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

433

JMenuItem pasteItem = new JMenuItem("Wklej");


editMenu.add(pasteItem);
editMenu.addSeparator();
JMenu optionsMenu = . . .;
// podmenu
editMenu.add(optionsMenu);

Na rysunku 9.19 separatory znajduj si pod elementami menu Wklej oraz Tylko do odczytu.
Kiedy uytkownik klika menu, uruchamia akcj. Kady element menu musi posiada obiekt
nasuchujcy akcji:
ActionListener listener = . . .;
pasteItem.addActionListener(listener);

Metoda JMenu.add(String s) dodaje element na kocu menu. Na przykad:


editMenu.add("Wklej");

Metoda add zwraca utworzony element menu, ktry mona przej w celu dodania dla niego
suchacza:
JMenuItem pasteItem = editMenu.add("Wklej");
pasteItem.addActionListener(listener);

Polecenia wykonywane w odpowiedzi na kliknicie elementu menu czsto mog by aktywowane take przez inne elementy interfejsu, jak przyciski na pasku narzdzi. W rozdziale 8.
nauczylimy si okrela polecenia za porednictwem obiektw Action. Polega to na zdefiniowaniu klasy implementujcej interfejs Action, zazwyczaj dla wygody rozszerzajcej klas
AbstractAction. Etykiet elementu menu okrela si w konstruktorze obiektu typu Abstract
Action. Ponadto naley przedefiniowa metod actionPerformed na procedur obsugi akcji.
Na przykad:
Action exitAction = new AbstractAction("Zakocz")
{
public void actionPerformed(ActionEvent event)
{
procedury obsugi akcji
System.exit(0);
}
};

// etykieta elementu menu

Nastpnie mona doda akcj do menu:


JMenuItem exitItem = fileMenu.add(exitAction);

Ta procedura dodaje element do menu, wykorzystujc do tego celu nazw akcji. Obiekt akcji
staje si jej suchaczem. Jest to skrcona forma zapisu poniszego fragmentu programu:
JMenuItem exitItem = new JMenuItem(exitAction);
fileMenu.add(exitItem);
javax.swing.JMenu 1.2

JMenu(String label)

Tworzy menu z okrelon etykiet.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

434

Java. Podstawy

JMenuItem add(JMenuItem item)

Dodaje element menu (lub menu).

JMenuItem add(String label)

Dodaje element menu z okrelon etykiet i zwraca ten element.

JMenuItem add(Action a)

Dodaje element menu z okrelon akcj i zwraca ten element.

void addSeparator()

Dodaje separator do menu.

JMenuItem insert(JMenuItem menu, int index)

Dodaje nowy element menu (lub podmenu) do menu w miejscu okrelonym


przez indeks.

JMenuItem insert(Action a, int index)

Dodaje element menu z okrelon akcj w miejscu okrelonym przez indeks.

void insertSeparator(int index)

Dodaje separator do menu.


Parametr:

index

void remove(int index)

void remove(JMenuItem item)

Miejsce wstawienia separatora

Tworzy element menu dla okrelonej akcji.


javax.swing.JMenuItem 1.2

JMenuItem(String label)

Tworzy element menu z dan etykiet.

JMenuItem(Action a) 1.3

Tworzy element menu dla danej akcji.


javax.swing.AbstractButton 1.2

void setAction(Action a) 1.3

Ustawia akcj dla przycisku lub elementu menu.


javax.swing.JFrame 1.2

void setJMenuBar(JMenuBar menubar)

Ustawia pasek menu w ramce.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

435

9.5.2. Ikony w elementach menu


Elementy menu s bardzo podobne do przyciskw. Klasa JMenuItem jest nawet rozszerzeniem
klasy AbstractButton. Menu, podobnie jak przyciski, mog mie etykiet tekstow, ikon lub
jedno i drugie. Ikon mona okreli za pomoc konstruktorw JMenuItem(String, Icon)
bd JMenuItem(Icon) lub metody setIcon, ktr klasa JMenuItem dziedziczy po klasie
AbstractButton. Na przykad:
JMenuItem cutItem = new JMenuItem("Wytnij", new ImageIcon("cut.gif"));

Na rysunku 9.19 ikony znajduj si obok kilku elementw menu. Przy standardowych ustawieniach tekst jest umieszczany po prawej stronie ikony elementu menu. Aby tekst pojawi
si po lewej stronie ikony, naley uy metody setHorizontalTextPosition, ktr klasa
JMenuItem dziedziczy po klasie AbstractButton. Na przykad ponisza instrukcja ustawia
tekst etykiety elementu menu po lewej stronie ikony:
cutItem.setHorizontalTextPosition(SwingConstants.LEFT);

Mona take doda ikon do akcji:


cutAction.putValue(Action.SMALL_ICON, new ImageIcon("cut.gif"));

Podczas konstruowania elementu menu z akcji warto Action.NAME staje si etykiet tego
elementu, a warto Action.SMALL_ICON ikon.
Inna metoda ustawiania ikony polega na uyciu konstruktora AbstractAction:
cutAction = new
AbstractAction("Wytnij", new ImageIcon("cut.gif"))
{
public void actionPerformed(ActionEvent event)
{
kod akcji
}
};
javax.swing.JMenuItem 1.2

JMenuItem(String label, Icon icon)

Tworzy element menu z okrelon ikon i etykiet.


javax.swing.AbstractButton 1.2

void setHorizontalTextPosition(int pos)

Okrela pooenie tekstu w poziomie wzgldem ikony.


Parametr:

pos

SwingConstants.RIGHT (wyrwnanie tekstu do prawej),


SwingConstants.LEFT (wyrwnanie tekstu do lewej)

javax.swing.AbstractAction 1.2

AbstractAction(string name, Icon smallIcon)

Tworzy akcj abstrakcyjn o okrelonej nazwie i z okrelon ikon.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

436

Java. Podstawy

9.5.3. Pola wyboru i przeczniki jako elementy menu


Pola wyboru i przeczniki jako elementy menu pojawiaj si obok nazwy elementu (zobacz
rysunek 9.19). Kiedy uytkownik kliknie taki element menu, zostanie on automatycznie zaznaczony lub te zaznaczenie zostanie usunite.
Elementy te traktuje si tak samo jak wszystkie inne elementy menu. Poniszy fragment kodu
tworzy element menu w postaci pola wyboru:
JCheckBoxMenuItem readonlyItem = new JCheckBoxMenuItem("Tylko do odczytu");
optionsMenu.add(readonlyItem);

Przeczniki w elementach menu dziaaj tak samo jak zwyke przeczniki. Musz nalee
do grupy przyciskw, w ktrej zaznaczenie jednego elementu powoduje usunicie zaznaczenia uprzednio wybranego.
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem = new JRadioButtonMenuItem("Wstawianie");
insertItem.setSelected(true);
JRadioButtonMenuItem overtypeItem = new JRadioButtonMenuItem("Nadpisywanie");
group.add(insertItem);
group.add(overtypeItem);
optionsMenu.add(insertItem);
optionsMenu.add(overtypeItem);

W przypadku tych elementw programista nie musi koniecznie wiedzie, kiedy dokadnie
nastpi wybr elementu. Moe natomiast sprawdzi jego stan za pomoc metody isSelected
(to oczywicie oznacza konieczno przechowywania referencji do tego elementu menu w polu
obiektowym). Do ustawiania stanu suy metoda setSelected.
javax.swing.JCheckBoxMenuItem 1.2

JCheckBoxMenuItem(String label)

Tworzy element menu w postaci pola wyboru o okrelonej etykiecie.

JCheckBoxMenuItem(String label, boolean state)

Tworzy element menu w postaci pola wyboru o okrelonej etykiecie i okrelonym


stanie pocztkowym (true oznacza zaznaczony).
javax.swing.JRadioButtonMenuItem 1.2

JRadioButtonMenuItem(String label)

Tworzy element menu w postaci przecznika o okrelonej etykiecie.

JRadioButtonMenuItem(String label, boolean state)

Tworzy element menu w postaci przecznika o okrelonej etykiecie i okrelonym


stanie pocztkowym (true oznacza zaznaczony).
javax.swing.AbstractButton 1.2

boolean isSelected()

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

437

void setSelected(boolean state)

Pobiera lub ustawia stan elementu (true oznacza zaznaczony).

9.5.4. Menu podrczne


Menu podrczne (ang. pop-up menu) nie jest zwizane z paskiem menu, tylko pojawia si
w rnych miejscach okna (rysunek 9.20).
Rysunek 9.20.
Menu podrczne

Proces tworzenia menu podrcznego wyglda podobnie jak w przypadku zwykego menu, z tym
wyjtkiem, e nie nadaje mu si tytuu.
JPopupMenu popup = new JPopupMenu();

Elementy do takiego menu dodaje si w typowy sposb:


JMenuItem item = new JMenuItem("Wytnij");
item.addActionListener(listener);
popup.add(item);

W przeciwiestwie do paska menu, ktry zawsze znajduje si na samej grze ramki, menu
podrczne musi by wywietlane za pomoc metody show. Naley w niej okreli komponent nadrzdny menu oraz jego lokalizacj za pomoc systemu wsprzdnych komponentu
nadrzdnego. Na przykad:
popup.show(panel, x, y);

Zazwyczaj menu podrczne s tak zaprogramowane, aby pokazyway si w odpowiedzi na


kliknicie przez uytkownika okrelonym przyciskiem myszy (tzw. pop-up trigger).
W systemach Windows i Linux funkcj t zazwyczaj peni prawy przycisk myszy. Za pojawienie si menu kontekstowego w odpowiedzi na kliknicie przez uytkownika przycisku
wyzwalajcego menu odpowiada ponisza instrukcja:
component.setComponentPopupMenu(popup);

Czasami do komponentu posiadajcego menu kontekstowe moe zosta wstawiony inny komponent, ktry rwnie posiada takie menu. Komponent podrzdny moe odziedziczy menu
kontekstowe elementu nadrzdnego dziki poniszej instrukcji:
child.setInheritsPopupMenu(true);
javax.swing.JPopupMenu 1.2

void show(Component c, int x, int y)

Wywietla menu kontekstowe.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

438

Java. Podstawy
Parametry:

Komponent, w ktrym ma si pojawi menu.

x, y

Wsprzdne (w przestrzeni komponentu c) grnego


lewego rogu menu kontekstowego.

boolean isPopupTrigger(MouseEvent event) 1.3

Zwraca warto true, jeli zdarzenie myszy powoduje pojawienie si menu


kontekstowego.
java.awt.event.MouseEvent 1.1

boolean isPopupTrigger()

Zwraca warto true, jeli zdarzenie myszy powoduje pojawienie si menu


kontekstowego.
javax.swing.JComponent 1.2

JPopupMenu getComponentPopupMenu() 5.0

void setComponentPopupMenu(JPopupMenu popup) 5.0

Pobiera lub ustawia menu kontekstowe dla komponentu.

boolean getInheritsPopupMenu() 5.0

void setInheritsPopupMenu(boolean b) 5.0

Pobiera lub ustawia wasno inheritsPopupMenu. Jeli wasno ta jest ustawiona,


a menu tego komponentu jest null, wykorzystuje menu kontekstowe jego
komponentu nadrzdnego.

9.5.5. Mnemoniki i akceleratory


Dla zaawansowanego uytkownika programu bardzo wanym usprawnieniem pracy jest
moliwo otwierania menu za pomoc mnemonikw. Mnemoniki do elementw menu okrela si poprzez okrelenie wybranej litery w konstruktorach tych elementw:
JMenuItem aboutItem = new JMenuItem("O programie", 'O');

Mnemonik jest wywietlany automatycznie w menu, a litera mnemoniku jest podkrelona


(rysunek 9.21). Na przykad etykieta elementu zdefiniowanego powyej bdzie wygldaa
nastpujco: O programie podkrelona litera O. Po rozwiniciu menu wystarczy nacisn klawisz O, aby wybra ten element (jeli litera mnemoniku nie wystpuje w acuchu
menu, jej nacinicie spowoduje wybr tego elementu, ale mnemonik nie bdzie wywietlany w menu oczywicie przydatno takich niewidocznych mnemonikw stoi pod znakiem zapytania).
Czasami programista nie chce, aby podkrelona zostaa pierwsza litera pasujca do mnemoniku. Jeli mamy na przykad mnemonik A dla elementu menu Zapisz jako, moemy sprawi,
aby zostaa podkrelona litera a w drugim wyrazie (Zapisz jako). W Java SE 1.4 wprowadzono
moliwo okrelania, ktra litera ma zosta podkrelona. Suy do tego metoda setDisplayed
MnemonicIndex.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

439

Rysunek 9.21.
Mnemoniki

Majc obiekt Action, mnemonik mona doda jako warto klucza Action.MNEMONIC_KEY:
cutAction.putValue(Action.MNEMONIC_KEY, new Integer('O'));

Liter mnemoniku mona poda tylko w konstruktorze elementu menu, w konstruktorze


samego menu nie. Aby doda mnemonik dla caego menu, naley uy metody setMnemonic:
JMenu helpMenu = new JMenu("Pomoc");
helpMenu.setMnemonic('P');

Aby przej do menu najwyszego poziomu na pasku menu, naley nacisn klawisz Alt
i liter mnemoniku. Aby na przykad przej do menu Pomoc, naley nacisn kombinacj
klawiszy Alt+P.
Za pomoc mnemonikw mona aktywowa element aktualnie otwartego menu lub jego podmenu. Natomiast akceleratory (ang. accelerators) to skrty klawiszowe, ktre daj dostp do
elementw menu bez jego otwierania. Na przykad w wielu programach akceleratory Ctrl+O
i Ctrl+S odpowiadaj elementom Otwrz i Zapisz w menu Plik. Do wizania elementw menu
z klawiszami skrtu (akceleratorami) suy metoda setAccelerator. Przyjmuje ona obiekt typu
Keystroke. Na przykad ponisza instrukcja wie skrt klawiszowy Ctrl+O z elementem
menu openItem:
openItem.setAccelerator(KeyStroke.getKeyStroke("ctrl O"));

Nacinicie kombinacji klawiszy akceleratora powoduje wybr odpowiedniej opcji z menu


i uruchomienie akcji, tak jakby uytkownik wybra dan opcj wprost z menu.
Akceleratory mona wiza wycznie z elementami menu, nie z samymi menu. Akceleratory
nie otwieraj adnego menu bezporednio uruchamiaj akcj zwizan z danym menu.
Teoretycznie tworzenie akceleratora dla elementu menu jest technik podobn do dodawania
akceleratora do komponentu Swing (technik t opisalimy w rozdziale 8.). Jednak kombinacja klawiszy akceleratora zwizanego z elementem menu jest automatycznie widoczna
w menu (rysunek 9.22).
Rysunek 9.22.
Akceleratory

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

440

Java. Podstawy

W systemie Windows kombinacja klawiszy Ctrl+F4 zamyka okno. Akcelerator ten


nie zosta jednak zaprogramowany w Javie. Jest to skrt systemu operacyjnego.
Wspomniana kombinacja klawiszy zawsze uruchamia zdarzenie WindowClosing dla aktywnego okna, bez wzgldu na to, czy w menu znajduje si element Zakocz.
javax.swing.JMenuItem 1.2

JMenuItem(String label, int mnemonic)

Tworzy element menu z okrelon etykiet i mnemonikiem.


Parametry:

label

Etykieta

mnemonic

Znak mnemoniczny, ktry zostanie podkrelony


w etykiecie.

void setAccelerator(KeyStroke k)

Ustawia klawisz k jako akcelerator do elementu menu. Klawisz skrtu jest


widoczny obok etykiety w menu.
javax.swing.AbstractButton 1.2

void setMnemonic(int mnemonic)

Ustawia mnemonik dla przycisku. Znak ten bdzie podkrelony w etykiecie.

void setDisplayedMnemonicIndex(int index) 1.4

Podkrela znak znajdujcy si na pozycji okrelonej przez parametr index. Metody


tej naley uywa, aby unikn podkrelenia pierwszej litery odpowiadajcej
mnemonikowi.

9.5.6. Aktywowanie i dezaktywowanie elementw menu


W okrelonych sytuacjach niektre elementy menu nie powinny by dostpne. Jeli na przykad jaki dokument zostanie otwarty w trybie tylko do odczytu, element menu Zapisz nie
powinien by dostpny. Jedno wyjcie polega na usuniciu go za pomoc metody JMenu.
remove, ale uytkownicy nie przepadaj za menu, ktrych zawarto ulega zmianom. Lepiej
jest zatem dezaktywowa te elementy menu, ktre w danej sytuacji nie s potrzebne. Element
taki ma kolor szary i nie dziaa (rysunek 9.23).
Rysunek 9.23.
Dezaktywowane
elementy menu

Do aktywowania i dezaktywowania elementw menu suy metoda setEnabled:


saveItem.setEnabled(false);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

441

Istniej dwie strategie aktywowania i dezaktywowania elementw menu. Przy kadej zmianie
sytuacji mona wywoywa metod setEnabled na rzecz odpowiednich elementw menu lub
akcji. Na przykad w odpowiedzi na przejcie w tryb tylko do odczytu mona zlokalizowa
elementy menu Zapisz i Zapisz jako w celu ich dezaktywacji. Inna metoda polega na wyczaniu elementw menu chwil przed wywietleniem tego menu. W takim przypadku
konieczna jest rejestracja suchacza zdarzenia wybrania menu. Pakiet javax.swing.event
zawiera definicj interfejsu MenuListener z trzema metodami:
void menuSelected(MenuEvent event)
void menuDeselected(MenuEvent event)
void menuCanceled(MenuEvent event)

Metoda menuSelected jest wywoywana przed wywietleniem menu, a zatem mona jej uywa do aktywacji i dezaktywacji elementw menu. Poniszy fragment programu dezaktywuje
polecenia Zapisz i Zapisz jako w odpowiedzi na zaznaczenie pola wyboru o nazwie Tylko do
odczytu:
public void menuSelected(MenuEvent event)
{
saveAction.setEnabled(!readonlyItem.isSelected());
saveAsAction.setEnabled(!readonlyItem.isSelected());
}

Dezaktywacja elementw menu bezporednio przed wywietleniem menu jest sprytnym rozwizaniem, ale nie sprawdza si w przypadku elementw posiadajcych
skrty klawiszowe. Poniewa wcinicie kombinacji klawiszy skrtu nie powoduje otwarcia
menu, akcja nie jest dezaktywowana, a wic mona j wyzwoli za pomoc akceleratora.
javax.swing.JMenuItem 1.2

void setEnabled(boolean b)

Aktywuje lub dezaktywuje element menu.


javax.swing.event.MenuListener 1.2

void menuSelected(MenuEvent e)

Wywoywana po wybraniu przez uytkownika menu, ale przed jego otwarciem.

void menuDeselected(MenuEvent e)

Wywoywana po dezaktywacji menu, ale przed jego zamkniciem.

void menuCanceled(MenuEvent e)

Wywoywana po anulowaniu wyboru menu, np. spowodowanym klikniciem


poza jego obrbem.
Listing 9.8 przedstawia program demonstrujcy wszystkie omawiane w tym podrozdziale
menu: menu zagniedone, dezaktywowane elementy menu, elementy menu w postaci pl
wyboru i przecznikw, menu kontekstowe oraz mnemoniki i akceleratory.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

442

Java. Podstawy

Listing 9.8. menu/MenuFrame.java


package menu;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z paskiem menu
*/
public class MenuFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private Action saveAction;
private Action saveAsAction;
private JCheckBoxMenuItem readonlyItem;
private JPopupMenu popup;
/**
* Przykadowa akcja, ktra drukuje nazw akcji do wyjcia System.out
*/
class TestAction extends AbstractAction
{
public TestAction(String name)
{
super(name);
}
public void actionPerformed(ActionEvent event)
{
System.out.println("Wybrano " + getValue(Action.NAME));
}
}
public MenuFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JMenu fileMenu = new JMenu("Plik");
fileMenu.add(new TestAction("Nowy"));
// Akceleratory
JMenuItem openItem = fileMenu.add(new TestAction("Otwrz"));
openItem.setAccelerator(KeyStroke.getKeyStroke("ctrl O"));
fileMenu.addSeparator();
saveAction = new TestAction("Zapisz");
JMenuItem saveItem = fileMenu.add(saveAction);
saveItem.setAccelerator(KeyStroke.getKeyStroke("ctrl S"));
saveAsAction = new TestAction("Zapisz jako");
fileMenu.add(saveAsAction);
fileMenu.addSeparator();
fileMenu.add(new AbstractAction("Zakocz")

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
// Menu z polem wyboru i przecznikami
readonlyItem = new JCheckBoxMenuItem("Tylko do odczytu");
readonlyItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
boolean saveOk = !readonlyItem.isSelected();
saveAction.setEnabled(saveOk);
saveAsAction.setEnabled(saveOk);
}
});
ButtonGroup group = new ButtonGroup();
JRadioButtonMenuItem insertItem = new JRadioButtonMenuItem("Wstawianie");
insertItem.setSelected(true);
JRadioButtonMenuItem overtypeItem = new JRadioButtonMenuItem("Nadpisywanie");
group.add(insertItem);
group.add(overtypeItem);
// Ikony
Action cutAction = new TestAction("Wytnij");
cutAction.putValue(Action.SMALL_ICON, new ImageIcon("cut.gif"));
Action copyAction = new TestAction("Kopiuj");
copyAction.putValue(Action.SMALL_ICON, new ImageIcon("copy.gif"));
Action pasteAction = new TestAction("Wklej");
pasteAction.putValue(Action.SMALL_ICON, new ImageIcon("paste.gif"));
JMenu editMenu = new JMenu("Edycja");
editMenu.add(cutAction);
editMenu.add(copyAction);
editMenu.add(pasteAction);
// Zagniedone menu
JMenu optionMenu = new JMenu("Opcje");
optionMenu.add(readonlyItem);
optionMenu.addSeparator();
optionMenu.add(insertItem);
optionMenu.add(overtypeItem);
editMenu.addSeparator();
editMenu.add(optionMenu);
// Mnemoniki
JMenu helpMenu = new JMenu("Pomoc");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

443

444

Java. Podstawy
helpMenu.setMnemonic('P');
JMenuItem indexItem = new JMenuItem("Indeks");
indexItem.setMnemonic('I');
helpMenu.add(indexItem);
// Mnemoniki mona take dodawa do akcji
Action aboutAction = new TestAction("O programie");
aboutAction.putValue(Action.MNEMONIC_KEY, new Integer('O'));
helpMenu.add(aboutAction);
// Dodanie wszystkich menu najwyszego rzdu do paska menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
menuBar.add(fileMenu);
menuBar.add(editMenu);
menuBar.add(helpMenu);
// Menu kontekstowe
popup = new JPopupMenu();
popup.add(cutAction);
popup.add(copyAction);
popup.add(pasteAction);
JPanel panel = new JPanel();
panel.setComponentPopupMenu(popup);
add(panel);
// Poniszy wiersz stanowi obejcie bdu 4966109
panel.addMouseListener(new MouseAdapter() {});
}
}

9.5.7. Paski narzdzi


Pasek narzdzi (ang. toolbar) zapewnia szybki dostp do najczciej uywanych polece programu (rysunek 9.24).
Rysunek 9.24.
Pasek narzdzi

Cech wyrniajc paski narzdzi jest ich zdolno do przenoszenia si w rne miejsca.
Mona za pomoc przecigania umieszcza je przy jednej z czterech krawdzi ramki (rysunek 9.25). Po zwolnieniu przycisku myszy pasek narzdzi pozostaje w nowej lokalizacji
(rysunek 9.26).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

445

Rysunek 9.25.
Przeciganie
paska narzdzi

Rysunek 9.26.
Pasek narzdzi
w nowej lokalizacji

Przeciganie paska narzdzi jest moliwe w kontenerach z ukadem krawdziowym lub dowolnym zarzdc rozkadu, ktry obsuguje ograniczenia North, East,
South i West.

Pasek narzdzi mona nawet cakiem oddzieli od ramki. Wtedy znajduje si on we wasnej
ramce (rysunek 9.27). Kiedy ramka zawierajca odczony pasek narzdzi zostanie zamknita,
pasek ten wraca do swojej pierwotnej ramki.
Rysunek 9.27.
Odczony
pasek narzdzi

Programowanie paskw narzdzi jest atwym zadaniem. Poniej do paska dodawany jest
element:
JToolBar bar = new JToolBar();
bar.add(blueButton);

Klasa JToolBar posiada take metod suc do dodawania obiektw Action. Wstawianie
obiektw typu Action do paska narzdzi wyglda nastpujco:
bar.add(blueAction);

Na pasku pokae si niewielka ikona akcji.


Do oddzielania grup przyciskw suy separator:
bar.addSeparator();

Na przykad na rysunku 9.24 separator znajduje si pomidzy trzecim a czwartym przyciskiem.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

446

Java. Podstawy
Nastpnie pasek narzdzi trzeba wstawi do ramki.
add(bar, BorderLayout.NORTH);

Mona take okreli tytu paska narzdzi, ktry bdzie widoczny po jego odczeniu:
bar = new JToolBar(titleString);

Domylnie paski narzdzi s uoone poziomo. Aby pasek narzdzi mia pionowe pooenie pocztkowe, naley zastosowa jedn z poniszych metod:
bar = new JToolBar(SwingConstants.VERTICAL)

lub
bar = new JToolBar(titleString, SwingConstants.VERTICAL)

Mimo e na paskach narzdzi najczciej spotyka si przyciski, mog si tam znale wszystkie inne komponenty na przykad lista rozwijalna.

9.5.8. Dymki
Wad paskw narzdzi jest to, e mae ikony niewiele mwi uytkownikowi o swoim
przeznaczeniu. Rozwizaniem tego problemu s dymki (ang. tooltips). Dymek pojawia si,
kiedy kursor myszy zatrzyma si na chwil nad przyciskiem. Tekst dymka jest wywietlany
w prostokcie z wypenieniem w jakim kolorze. Kiedy kursor myszy zostanie zabrany
znad przycisku, dymek znika (rysunek 9.28).
Rysunek 9.28.
Dymek

W bibliotece Swing dymek mona doda do kadego komponentu JComponent za pomoc


metody setToolTip:
exitButton.setToolTipText("Zamknij");

W przypadku obiektw typu Action dymki wie si z kluczami SHORT_DESCRIPTION:


exitAction.putValue(Action.SHORT_DESCRIPTION, "Zamknij");

Listing 9.9 demonstruje wstawianie tych samych obiektw typu Action do menu i paska
narzdzi. Naley zauway, e nazwy akcji pokazuj si jako nazwy elementw w menu
oraz jako krtkie opisy w chmurkach przyciskw na pasku narzdzi.
Listing 9.9. toolBar/ToolBarTest.java
package toolBar;
import java.awt.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

import javax.swing.*;
/**
* @version 1.13 2007-06-12
* @author Cay Horstmann
*/
public class ToolBarTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
ToolBarFrame frame = new ToolBarFrame();
frame.setTitle("ToolBarTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
javax.swing.JToolBar 1.2

JToolBar()

JToolBar(String titleString)

JToolBar(int orientation)

JToolBar(String titleString, int orientation)

Tworzy pasek narzdzi z okrelonym tytuem i pooeniem. Pooenie moe by


poziome SwingConstants.HORIZONTAL (domylne) lub pionowe
SwingConstants.VERTICAL.

JButton add(Action a)

Tworzy przycisk w pasku narzdzi z nazw, ikon, krtkim opisem i wywoaniem


zwrotnym akcji oraz dodaje ten przycisk na kocu paska.

void addSeparator()

Wstawia separator na kocu paska narzdzi.


javax.swing.JComponent 1.2

void setToolTipText(String text)

Ustawia tekst, ktry bdzie wywietlany w dymku po najechaniu kursorem


na dany komponent.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

447

448

Java. Podstawy

9.6. Zaawansowane techniki


zarzdzania rozkadem
Do tej pory komponenty interfejsu uytkownika rozmieszczalimy, stosujc rozkad brzegowy
(ang. border layout), cigy (ang. flow layout) oraz siatkowy (ang. grid layout). Techniki te
mog si okaza niewystarczajce w przypadku bardziej zaawansowanych zada. Ten podrozdzia zosta powicony zaawansowanym technikom zarzdzania rozkadem komponentw.
Programici Windowsa mog si dziwi, e w Javie tak duo uwagi powicono zarzdcom
rozkadu. W systemie Windows to nic wielkiego najpierw w edytorze okien dialogowych
przeciga si i upuszcza wybrane komponenty okna, a nastpnie za pomoc odpowiednich
narzdzi ustawia si je zgodnie z wymaganiami. Programici pracujcy w duych zespoach
czsto w ogle nie zajmuj si ukadem komponentw, poniewa robi to za nich wykwalifikowani projektanci interfejsw uytkownika.
Wad takiego podejcia jest to, e powstay ukad trzeba rcznie modyfikowa, jeli zmieni
si rozmiar komponentw. Ale dlaczego komponenty miayby zmienia rozmiar? Moe to
mie miejsce z dwch powodw. Po pierwsze, uytkownik moe uy wikszej czcionki na
przyciskach i dla tekstu w oknach dialogowych. Nietrudno si przekona, e wiele aplikacji
w systemie Windows bardzo le znosi takie modyfikacje. Przyciski nie powikszaj si,
przez co tekst jest upychany na takiej samej powierzchni jak wczeniej. Po drugie, podobny
problem moe wystpi przy lokalizacji programu. Na przykad polecenie Anuluj po
niemiecku brzmi Abbrechen. Jeli w projekcie przycisku przewidziano tylko tyle miejsca,
ile potrzeba dla sowa Anuluj, jego niemiecki odpowiednik bdzie czciowo obcity.
Dlaczego przyciski w systemie Windows nie powikszaj si, aby pomieci etykiety? Poniewa projektant interfejsu uytkownika nie da adnych instrukcji, w ktrym kierunku powinny
one rosn. Po zakoczeniu przecigania, upuszczania i ustawiania edytor okien dialogowych
pamita tylko pooenie i rozmiar kadego komponentu. Nie dysponuje informacjami, dlaczego te komponenty zostay ustawione w taki sposb.
Zarzdcy rozkadu w Javie oferuj znacznie lepsze podejcie do zagadnienia rozkadu komponentw. Dziki nim w rozkadzie dostpne s informacje dotyczce powiza midzy komponentami. Miao to szczeglne znaczenie w pierwotnej bibliotece AWT, ktra korzystaa
z rodzimych elementw interfejsu. Rozmiar przycisku moe znacznie rni si w stylu
Motif, w systemach Windows i Mac OS X, a program czy aplet nie wie z gry, gdzie bdzie
uruchamiany. Zrnicowanie w pewnym stopniu znikno wraz z pojawieniem si biblioteki
Swing. Jeli aplikacja wymusza okrelony styl, np. Metal, to wyglda identycznie na wszystkich platformach. Jeli jednak programista zezwoli uytkownikom na wybr odpowiadajcego
im stylu, musi przy aranacji komponentw polega na elastycznoci zarzdcw rozkadu.
Od Java 1.0 biblioteka AWT udostpnia rozkad GridBagLayout, ktry ukada komponenty
w wierszach i kolumnach. Rozmiary kolumn i wierszy mog si zmienia, a komponenty mog
zajmowa po kilka z nich. Ten rozkad jest bardzo elastyczny, ale nie mniej skomplikowany. Sam dwik sw GridBagLayout jest znany z tego, e przepenia lkiem serca programistw Javy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

449

Nieudan prb uwolnienia programistw od tyranii rozkadu GridBagLayout by projekt


w ramach biblioteki Swing rozkadu o nazwie BoxLayout. Cytujc za dokumentacj JDK
klasy BoxLayout: Zagniedanie wielu paneli z rnymi kombinacjami poziomych i pionowych paneli (sic!) daje efekt podobny do rozkadu GridBagLayout przy jednoczesnym unikniciu nadmiernej komplikacji. Poniewa jednak kady blok jest ustawiany osobno, nie mona
za pomoc rozkadu BoxLayout ustawia ssiadujcych komponentw zarwno w poziomie,
jak i w pionie.
W Java SE 1.4 podjto jeszcze jedn prb zastpienia rozkadu GridBagLayout, ktrej owocem jest SpringLayout. Komponenty w kontenerze cz umowne spryny (ang. springs),
ktre rozcigaj si lub kurcz w odpowiedzi na zmiany rozmiaru kontenera, dostosowujc
pooenie komponentw. Brzmi to niezbyt zachcajco i rzeczywicie rozkad sprynowy
(ang. spring layout) szybko poszed w zapomnienie.
W 2005 roku zesp pracujcy nad projektem NetBeans opracowa technologi Matisse,
ktra stanowi poczenie narzdzia do opracowywania rozkadu i zarzdcy rozkadu. Projektant interfejsu uytkownika upuszcza komponenty w kontenerze i okrela, ktre z nich
powinny si znajdowa w jednej linii. Narzdzie konwertuje zamierzenia projektanta
na instrukcje dla zarzdcy rozkadu grupowego (ang. group layout manager). Jest to o wiele
wygodniejsze podejcie ni wasnorczne pisanie caego kodu zarzdcy rozkadu. Zarzdca
rozkadu grupowego wchodzi obecnie w skad Java SE 6. Zalecamy uywanie narzdzi do
budowy GUI rodowiska NetBeans nawet tym, ktrzy na co dzie korzystaj z innego IDE.
Mona zaprojektowa GUI w NetBeans, a wygenerowany kod przenie do dowolnego
wybranego IDE.
W nastpnym podrozdziale opisujemy rozkad GridBagLayout, poniewa jest on w powszechnym uyciu i nadal jest najatwiejsz technik tworzenia kodu rozkadu w starszych wersjach Javy. Opisujemy strategie, dziki ktrym w wikszoci sytuacji rozkad GridBagLayout
mona w duym stopniu ujarzmi.
W dalszej kolejnoci przechodzimy do opisu narzdzia Matisse i zarzdcy rozkadu grupowego. Wiedza dotyczca dziaania zarzdcy rozkadu grupowego jest potrzebna do sprawdzenia, czy Matisse wygenerowa prawidowe instrukcje podczas wizualnego pozycjonowania
komponentw.
Tematyk zarzdcw rozkadu koczymy prezentacj sposobu cakowitego pominicia zarzdzania rozkadem i rcznego ustawiania komponentw oraz pisaniem wasnego zarzdcy
rozkadu.

9.6.1. Rozkad GridBagLayout


Rozkad GridBagLayout jest przodkiem wszystkich zarzdcw rozkadu. Mona go sobie
wyobrazi jako rozkad siatkowy pozbawiony ogranicze. Wiersze i kolumny mog mie
rne rozmiary. Ssiadujce ze sob komrki mona czy w celu zrobienia miejsca dla
duych komponentw (zarwno wiele procesorw tekstu, jak i jzyk HTML oferuj takie
same moliwoci edycji tabel najpierw tworzy si zwyk siatk, a nastpnie scala przylegajce komrki w razie potrzeby). Komponent nie musi zajmowa caej powierzchni komrki,
a jego pooenie w komrce mona kontrolowa.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

450

Java. Podstawy
Przyjrzyjmy si opcjom dotyczcym wyboru wasnoci czcionki na rysunku 9.29. Opieraj
si one na nastpujcych komponentach:

dwie listy rozwijalne suce do wyboru kroju i rozmiaru czcionki,

etykiety list rozwijalnych,

dwa pola wyboru suce do pogrubienia i pochylenia czcionki,

obszar tekstowy zawierajcy przykadowy tekst.

Rysunek 9.29.
Opcje czcionki

Podzielmy teraz cay kontener na siatk komrek, jak na rysunku 9.30 (wiersze i kolumny nie
musz mie takich samych rozmiarw). Kade pole wyboru zajmuje dwie kolumny, a obszar
tekstowy zajmuje cztery wiersze.
Rysunek 9.30.
Siatka uyta
do zaprojektowania
okna dialogowego

Utworzenie powyszej siatki za pomoc zarzdcy GridBagLayout wymaga nastpujcych


czynnoci:
1.

Utwrz obiekt typu GridBagLayout. Nie trzeba podawa liczby wierszy i kolumn,
z ktrych ma si skada siatka. Zarzdca sam sprbuje te informacje zdoby
na podstawie danych dostarczonych pniej.

2. Ustaw utworzony obiekt typu GridBagLayout jako zarzdc rozkadu komponentu.


3. Dla kadego komponentu utwrz obiekt typu GridBagConstraints. Okrel ukad

komponentw w siatce poprzez odpowiednie ustawienie wartoci pl tego obiektu.


4. Dodaj kady komponent z jego ograniczeniami (ang. constraints) za pomoc

poniszego wywoania:
add(component, constraints);

Poniej znajduje si przykadowy kod (ograniczenia opisujemy szczegowo nieco dalej).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

451

GridBagLayout layout = new GridBagLayout();


panel.setLayout(layout);
GridBagConstraints constraints = new GridBagConstraints();
constraints.weightx = 100;
constraints.weighty = 100;
constraints.gridx = 0;
constraints.gridy = 2;
constraints.gridwidth = 2;
constraints.gridheight = 1;
panel.add(component, constraints);

Sztuka polega na umiejtnym ustawieniu stanu obiektu GridBagConstraints. Najwaniejsze


parametry tego typu obiektw opisane zostay w kolejnych podrozdziaach.

9.6.1.1. Parametry gridx, gridy, gridwidth i gridheight


Ograniczenia gridx, gridy, gridwidth i gridheight su do okrelania lokalizacji komponentu na siatce. Parametry gridx i gridy okrelaj wiersz i kolumn, w ktrych ma si znajdowa lewy grny rg dodawanego komponentu. Wartoci gridwidth i gridheight okrelaj
liczb kolumn i wierszy zajmowanych przez komponent.
Wartoci wsprzdnych siatki zaczynaj si od 0, tzn. punkt gridx = 0 i gridy = 0 jest lewym
grnym rogiem. Na przykad wsprzdne obszaru tekstowego na rysunku to gridx = 2
i gridy = 0, poniewa zaczyna si on w kolumnie numer 2 (czyli trzeciej) wiersza o numerze 0. Szeroko tego obszaru wynosi gridwidth = 1 i gridheight = 4, czyli rwna si jednej
kolumnie i czterem wierszom.

9.6.1.2. Pola weight


Kady komponent w rozkadzie GridBagLayout musi mie ustawione pola weight (weightx
i weighty). Warto 0 powoduje, e komponent nie bdzie si rozszerza ani kurczy w stosunku do rozmiaru pocztkowego wzgldem okrelonej osi. W rozkadzie na rysunku 9.29
ustawilimy pole weightx etykiet na 0. Dziki temu bd one miay zawsze taki sam rozmiar,
bez wzgldu na rozmiar okna. Z drugiej strony, jeli pola weight wszystkich elementw zostan
ustawione na 0, kontener zamiast rozciga si na ca dostpn przestrze, bdzie pywa cinity na jej rodku.
Problemy zwizane z parametrami weight polegaj na tym, e s one wasnociami wierszy
i kolumn, a nie poszczeglnych komrek. Trzeba jednak okrela je w kategoriach komrek,
poniewa rozkad GridBagLayout nie eksponuje wierszy i kolumn. Wartoci parametrw weight
s obliczane jako maksimum wartoci weight w kadym wierszu lub kolumnie. Aby zatem
wiersz lub kolumna miay stay rozmiar, naley parametr weight wszystkich zawartych w nich
komponentw ustawi na 0.
Naley pamita, e wartoci weight nie okrelaj wzgldnych rozmiarw kolumn. Okrelaj
tylko, jaka cz wolnej przestrzeni ma by przydzielona kademu obszarowi, jeli kontener
przekroczy preferowany rozmiar. Takie dziaanie trudno opanowa intuicyjnie. Zalecamy
ustawienie wszystkich parametrw weight na 100. Nastpnie trzeba uruchomi program, aby
sprawdzi, jak wyglda. Zmniejszajc i zwikszajc okno, mona sprawdzi, jak dostosowuj

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

452

Java. Podstawy
si poszczeglne wiersze i kolumny. Jeli wyjdzie, e ktry wiersz lub ktra kolumna nie
powinna si powiksza, naley ustawi parametry weight wszystkich znajdujcych si w niej
komponentw na 0. Mona wyprbowa take inne wartoci weight, ale zazwyczaj nie przynosi to dobrego rezultatu.

9.6.1.3. Parametry fill i anchor


Aby komponent nie rozciga si i nie zapenia caej dostpnej przestrzeni, naley odpowiednio
ustawi ograniczenie fill. Parametr ten moe przyjmowa jedn z czterech wartoci, ktrych
poprawna posta jest nastpujca: GridBagConstraints.NONE, GridBagConstraints.HORIZONTAL,
GridBagConstraints.VERTICAL oraz GridBagConstraints.BOTH.
Jeli komponent nie zajmuje caego dostpnego miejsca, mona okreli jego pooenie
w tym obszarze za pomoc pola anchor. Dostpne wartoci to: GridBagConstraints.CENTER
(domylna), GridBagConstraints.NORTH, GridBagConstraints.NORTHEAST oraz GridBag
Constraints.EAST.

9.6.1.4. Dopenienie
Komponent mona otoczy dodatkow pust przestrzeni, odpowiednio ustawiajc pole insets
obiektu GridBagConstraints. W tym celu naley odpowiednio ustawi wartoci left, top,
right i bottom obiektu typu Insets. Jest to tak zwane dopenienie zewntrzne (ang. external padding).
Wartoci ipadx i ipady okrelaj dopenienie wewntrzne (ang. internal padding). Wartoci
te s dodawane do minimalnej szerokoci i wysokoci komponentu. Stanowi to zabezpieczenie przed skurczeniem si komponentu do minimalnych rozmiarw.

9.6.1.5. Inny sposb ustawiania wartoci parametrw gridx, gridy, gridwidth


i gridheight
Dokumentacja biblioteki AWT zaleca, aby zamiast bezwzgldnych ustawie wartoci gridx
i gridy stosowa sta GridBagConstants.RELATIVE. Komponenty natomiast naley dodawa
w okrelonej kolejnoci od lewej do prawej w pierwszym wierszu, potem drugim itd.
Liczb wierszy i kolumn w tym przypadku rwnie okrela si za pomoc odpowiednich
ustawie parametrw gridheight i gridwidth. Wyjtek stanowi sytuacja, w ktrej komponent
siga do ostatniego wiersza lub kolumny. Wtedy nie naley podawa konkretnej liczby, tylko
uy staej GridBagConstraints.REMAINDER. Stanowi to informacj dla zarzdcy rozkadu, e
dany komponent jest ostatni w wierszu.
Wydaje si, e opisywana metoda daje dobre rezultaty. Niezbyt rozsdne wydaje si jednak
ukrywanie rzeczywistych informacji o pooeniu przed zarzdc ukadu w nadziei, e zdoa
on pniej je odzyska.
Wszystko to wydaje si nieco skomplikowane, ale ponisza strategia dziaania znacznie uatwia
opanowanie rozkadu GridBagLayout:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.
1.

Komponenty Swing interfejsu uytkownika

453

Narysuj szkic rozkadu komponentw na kartce.

2. Opracuj tak siatk, w ktrej wszystkie mae komponenty mieszcz si

w pojedynczych komrkach, a wiksze komponenty zajmuj po kilka komrek.


3. Oznacz wiersze i kolumny swojej siatki numerami 0, 1, 2, 3 itd. To uatwi
sprawdzenie wartoci parametrw gridx, gridy, gridwidth i gridheight.
4. Dla kadego komponentu okrel, czy ma on wypenia swoj komrk w pionie,

czy poziomie. Jeli nie ma wypenia, zdecyduj o sposobie jego wyrwnania.


Su do tego parametry fill i anchor.
5. Ustaw wszystkie parametry weight na 100. Aby dany wiersz lub kolumna zachoway
swj domylny rozmiar na stae, ustaw warto parametru weightx lub weighty
wszystkich komponentw nalecych do tego wiersza lub kolumny na 0.
6. Napisz kod. Dokadnie sprawd ustawienia GridBagConstraints. Jedna niepoprawna

warto moe zniszczy cay ukad.


7. Skompiluj program, uruchom go i delektuj si.

Niektre rodowiska do budowy GUI udostpniaj nawet wizualne narzdzia suce do okrelania ogranicze. Rysunek 9.31 przedstawia okno dialogowe konfiguracji w NetBeans.
Rysunek 9.31.
Okrelanie
ogranicze
rozkadu
GridBagLayout
w rodowisku
NetBeans

9.6.1.6. Klasa pomocnicza uatwiajca prac z ograniczeniami GridBagLayout


Najbardziej mudn czynnoci zwizan z projektowaniem rozkadu GridBagLayout jest
pisanie kodu ustawiajcego ograniczenia. Wikszo programistw uatwia sobie ycie, piszc
funkcje lub mae klasy pomocnicze. Tak przykadow klas prezentujemy pod listingiem
omawianego do tej pory programu. Ta klasa ma nastpujce wasnoci:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

454

Java. Podstawy

Krtka nazwa GBC zamiast GridBagConstraints.

Dziedziczy po klasie GridBagConstraints, dziki czemu mona uywa krtszych


nazw staych, np. GBC.EAST.

Dodawanie komponentu odbywa si przy uyciu obiektu GBC, np.:


add(component, new GBC(1, 2));

Posiada dwa konstruktory ustawiajce najczciej uywane parametry: gridx


i gridy lub gridx, gridy, gridheight i gridwidth.
add(component, new GBC(1, 2, 1, 4));

Istniej wygodne metody typu set dla pl wystpujcych w parach x-y:


add(component, new GBC(1, 2).setWeight(100, 100));

Metody set zwracaj this, dziki czemu mona je stosowa acuchowo:


add(component, new GBC(1, 2).setAnchor(GBC.EAST).setWeight(100, 100));

Metody setInsets tworz obiekty typu Insets. Ponisza instrukcja tworzy


wstawki jednopikselowe:
add(component, new GBC(1, 2).setAnchor(GBC.EAST).setInsets(1));

Listing 9.10 przedstawia kompletny kod programu do zmiany wasnoci czcionek. Klasa GBC
znajduje si na listingu 9.11. Poniszy kod dodaje komponenty do siatki:
add(faceLabel, new GBC(0, 0).setAnchor(GBC.EAST));
add(face, new GBC(1, 0).setFill(GBC.HORIZONTAL).setWeight(100, 0).setInsets(1));
add(sizeLabel, new GBC(0, 1).setAnchor(GBC.EAST));
add(size, new GBC(1, 1).setFill(GBC.HORIZONTAL).setWeight(100, 0).setInsets(1));
add(bold, new GBC(0, 2, 2, 1).setAnchor(GBC.CENTER).setWeight(100, 100));
add(italic, new GBC(0, 3, 2, 1).setAnchor(GBC.CENTER).setWeight(100, 100));
add(sample, new GBC(2, 0, 1, 4).setFill(GBC.BOTH).setWeight(100, 100));

Dla osb, ktre opanoway ograniczenia siatki, kod tego typu jest atwy do odczytania i debugowania.
W kursie na stronie http://docs.oracle.com/javase/tutorial/uiswing/layout/
gridbag.html znajduje si zalecenie, aby uywa tego samego obiektu GridBagConstraints dla wszystkich komponentw. W naszym odczuciu powstay w ten sposb
kod jest trudny do odczytania i podatny na bdy. Spjrzmy na przykad na demonstracyjny
program dostpny na stronie http://docs.oracle.com/javase/tutorial/uiswing/events/
containerlistener.html. Czy przyciski z zaoenia miay si rozciga, czy moe programista zapomnia wyczy ograniczenie fill?
Listing 9.10. gridbag/FontFrame.java
package gridbag;
import
import
import
import

java.awt.*;
java.awt.event.*;
java.beans.*;
javax.swing.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

/**
* Ramka zawierajca komponenty ustawiajce wasnoci czcionki w rozkadzie GridBagLayout
*/
public class FontFrame extends JFrame
{
public static final int TEXT_ROWS = 10;
public static final int TEXT_COLUMNS = 20;
private
private
private
private
private

JComboBox<String> face;
JComboBox<Integer> size;
JCheckBox bold;
JCheckBox italic;
JTextArea sample;

public FontFrame()
{
GridBagLayout layout = new GridBagLayout();
setLayout(layout);
ActionListener listener = EventHandler.create(ActionListener.class, this,
"updateSample");
// Tworzenie komponentw
JLabel faceLabel = new JLabel("Krj: ");
face = new JComboBox<>(new String[] { "Serif", "SansSerif", "Monospaced",
"Dialog",
"DialogInput" });
face.addActionListener(listener);
JLabel sizeLabel = new JLabel("Rozmiar: ");
size = new JComboBox<>(new Integer[] { 8, 10, 12, 15, 18, 24, 36, 48 });
size.addActionListener(listener);
bold = new JCheckBox("Bold");
bold.addActionListener(listener);
italic = new JCheckBox("Italic");
italic.addActionListener(listener);
sample = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
sample.setText("Ko i pies grali w koci z pikn m u rda.");
sample.setEditable(false);
sample.setLineWrap(true);
sample.setBorder(BorderFactory.createEtchedBorder());
// Dodawanie komponentw do siatki przy uyciu klasy pomocniczej GBC
add(faceLabel, new GBC(0, 0).setAnchor(GBC.EAST));
add(face, new GBC(1, 0).setFill(GBC.HORIZONTAL).setWeight(100,
0).setInsets(1));
add(sizeLabel, new GBC(0, 1).setAnchor(GBC.EAST));
add(size, new GBC(1, 1).setFill(GBC.HORIZONTAL).setWeight(100,
0).setInsets(1));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

455

456

Java. Podstawy
add(bold, new GBC(0, 2, 2, 1).setAnchor(GBC.CENTER).setWeight(100, 100));
add(italic, new GBC(0, 3, 2, 1).setAnchor(GBC.CENTER).setWeight(100, 100));
add(sample, new GBC(2, 0, 1, 4).setFill(GBC.BOTH).setWeight(100, 100));
pack();
updateSample();
}
public void updateSample()
{
String fontFace = (String) face.getSelectedItem();
int fontStyle = (bold.isSelected() ? Font.BOLD : 0)
+ (italic.isSelected() ? Font.ITALIC : 0);
int fontSize = size.getItemAt(size.getSelectedIndex());
Font font = new Font(fontFace, fontStyle, fontSize);
sample.setFont(font);
sample.repaint();
}
}

Listing 9.11. gridbag/GBC.java


package gridbag;
import java.awt.*;
/**
* Ta klasa upraszcza korzystanie z klasy GridBagConstraints.
* @version 1.01 2004-05-06
* @author Cay Horstmann
*/
public class GBC extends GridBagConstraints
{
/**
* Tworzy obiekt typu GBC z podanymi wartociami gridx i gridy oraz wszystkimi pozostaymi
* parametrami ustawionymi na wartoci domylne.
* @param gridx wsprzdna gridx
* @param gridy wsprzdna gridy
*/
public GBC(int gridx, int gridy)
{
this.gridx = gridx;
this.gridy = gridy;
}
/**
* Tworzy obiekt typu GBC z podanymi wartociami gridx, gridy, gridwidth i gridheight oraz
* wszystkimi pozostaymi parametrami ustawionymi na wartoci domylne.
* @param gridx wsprzdna gridx
* @param gridy wsprzdna gridy
* @param gridwidth liczba zajmowanych komrek w poziomie
* @param gridheight liczba zajmowanych komrek w pionie
*/
public GBC(int gridx, int gridy, int gridwidth, int gridheight)
{
this.gridx = gridx;
this.gridy = gridy;
this.gridwidth = gridwidth;
this.gridheight = gridheight;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

}
/**
* Ustawia parametr anchor.
* @param anchor warto parametru anchor
* @return this obiekt do dalszej modyfikacji
*/
public GBC setAnchor(int anchor)
{
this.anchor = anchor;
return this;
}
/**
* Ustawia kierunek zapeniania.
* @param fill kierunek zapeniania
* @return this obiekt do dalszej modyfikacji
*/
public GBC setFill(int fill)
{
this.fill = fill;
return this;
}
/**
* Ustawia parametry weight komrek.
* @param weightx parametr weight w poziomie
* @param weighty parametr weight w pionie
* @return this obiekt do dalszej modyfikacji
*/
public GBC setWeight(double weightx, double weighty)
{
this.weightx = weightx;
this.weighty = weighty;
return this;
}
/**
* Ustawia dodatkow pust przestrze w komrce.
* @param distance dopenienie we wszystkich kierunkach
* @return this obiekt do dalszej modyfikacji
*/
public GBC setInsets(int distance)
{
this.insets = new Insets(distance, distance, distance, distance);
return this;
}
/**
* Ustawia dopenienia w komrce.
* @param top odstp od grnej krawdzi
* @param left odstp od lewej krawdzi
* @param bottom odstp od dolnej krawdzi
* @param right odstp od prawej krawdzi
* @return obiekt do dalszej modyfikacji
*/
public GBC setInsets(int top, int left, int bottom, int right)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

457

458

Java. Podstawy
this.insets = new Insets(top, left, bottom, right);
return this;
}
/**
* Ustawia dopenienie wewntrzne.
* @param ipadx dopenienie wewntrzne poziome
* @param ipady dopenienie wewntrzne pionowe
* @return obiekt do dalszej modyfikacji
*/
public GBC setIpad(int ipadx, int ipady)
{
this.ipadx = ipadx;
this.ipady = ipady;
return this;
}
}
java.awt.GridBagConstraints 1.0

int gridx, gridy

Ustawia pierwsz kolumn i pierwszy wiersz komrki. Domylna warto to 0.

int gridwidth, gridheight

Okrela liczb kolumn i wierszy zajmowanych przez komrk. Domylna


warto to 0.

double weightx, weighty

Okrela moliwoci komrki do powikszania si. Domylna warto to 0.

int anchor

Okrela wyrwnanie komponentu wewntrz komrki. Dostpne s wartoci


bezwzgldne:
NORTHWEST

NORTH

NORTHEAST

WEST

CENTER

EAST

SOUTHWEST

SOUTH

SOUTHEAST

oraz ich odpowiedniki niezalene od orientacji:


FIRST_LINE_START

LINE_START

FIRST_LINE_END

PAGE_START

CENTER

PAGE_END

LAST_LINE_START

LINE_END

LAST_LINE_END

Tych drugich naley uywa, jeli program ma by lokalizowany w jzykach,


w ktrych kierunek pisma biegnie od lewej do prawej lub od dou do gry.
Warto domylna to CENTER.

int fill

Okrela sposb wypeniania komrki przez komponent. Dostpne wartoci


to NONE, BOTH, HORIZONTAL i VERTICAL. Warto domylna to NONE.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

459

int ipadx, ipady

Okrela wewntrzne dopenienie wok komponentu. Warto domylna to 0.

Insets insets

Okrela zewntrzne dopenienie wzdu krawdzi komrki. Domylnie brak


dopenienia.

GridBagConstraints(int gridx, int gridy, int gridwidth, int gridheight,


double weightx, double weighty, int anchor, int fill, Insets insets,
int ipadx, int ipady) 1.2

Tworzy obiekt typu GridBagConstraints z wartociami wszystkich pl ustawionymi


na wartoci podane w argumentach. Firma Sun zaleca, aby konstruktora tego
uyway wycznie automatyczne generatory kodu, poniewa kod ten jest bardzo
nieprzyjazny dla czowieka.

9.6.2. Rozkad grupowy


Przed rozpoczciem opisu API klasy GroupLayout rzucimy okiem na narzdzie do budowy GUI
w NetBeans, ktre kiedy nazywao si Matisse. Nie bdzie to jednak peny kurs obsugi tego
narzdzia. Wicej informacji o nim mona znale na stronie https://netbeans.org/features/
java/swing.html.
Czynnoci konieczne do utworzenia ukadu grnej czci okna dialogowego widocznego na
rysunku 9.13 s nastpujce: utwrz nowy projekt i dodaj form JFrame. Przecignij etykiet, a pojawi si dwie linie pomocnicze oddzielajce etykiet od krawdzi kontenera.

Umie inn etykiet pod pierwszym wierszem.

Przecignij pole tekstowe, aby jego linia bazowa wyrwnaa si z lini bazow pierwszej
etykiety. Ponownie zwr uwag na linie pomocnicze.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

460

Java. Podstawy

Na zakoczenie ustaw pole hasa w jednej linii z doln etykiet i kolumn z polem znajdujcym si na grze.

Matisse wygeneruje nastpujcy kod:


layout.setHorizontalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jTextField1))
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jPasswordField1)))
.addContainerGap(222, Short.MAX_VALUE)));
layout.setVerticalGroup(
layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(jTextField1))
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(jPasswordField1))
.addContainerGap(244, Short.MAX_VALUE)));

Wyglda to do strasznie, ale na szczcie nie trzeba pisa tego kodu wasnorcznie. Znajomo podstaw dotyczcych akcji rozkadu jest jednak przydatna, poniewa umoliwia znajdywanie bdw. Przeanalizujemy podstawow struktur tego kodu. W wycigach z API
znajdujcych si na kocu tego podrozdziau zostao wyjanione przeznaczenie wszystkich
uytych tu klas i metod.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

461

Komponenty s umieszczane w obiektach typu GroupLayout.SequentialGroup lub Group


Layout.ParallelGroup. S to podklasy klasy GroupLayout.Group. Grupy mog zawiera
komponenty, luki pustego miejsca i zagniedone grupy. Rne metody add klas grup zwracaj obiekty grup, dziki czemu mona czy je w acuchy, np.:
group.addComponent(...).addPreferredGap(...).addComponent(...);

Jak wida w przykadowym kodzie, rozkad grupowy oddziela obliczenia zwizane z uoeniem w pionie i poziomie.
Uoenie w poziomie mona sobie wyobrazi jako komponenty o wysokoci rwnej 0, jak na
poniszym rysunku.

S dwie rwnolege sekwencje komponentw odpowiadajce (nieco uproszczonemu) poniszemu kodowi:


.addContainerGap()
.addGroup(layout.createParallelGroup()
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jTextField1))
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jPasswordField1)))

Ale to przecie nie moe dziaa prawidowo. Skoro etykiety maj rne dugoci, pole tekstowe i pole hasa nie mog by wyrwnane w jednej linii.
Musimy poinformowa program Matisse, e pola maj by wyrwnane. Zaznacz oba pola,
kliknij prawym przyciskiem myszy i wybierz opcj Align/Left to Column. Wyrwnaj te
etykiety (rysunek 9.32).
Czynnoci te powoduj due zmiany w kodzie:
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jLabel1, GroupLayout.Alignment.TRAILING)
.addComponent(jLabel2, GroupLayout.Alignment.TRAILING))
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(jTextField1)
.addComponent(jPasswordField1))

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

462

Java. Podstawy

Rysunek 9.32.
Wyrwnywanie
etykiet i pl
tekstowych
w Matisse

Teraz etykiety i pola znajduj si w rwnolegych grupach. Pierwsza grupa ma wyrwnanie


TRAILING (czyli wyrwnanie do prawej przy kierunku tekstu w prawo):

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

463

Zdolno Matisse do zamieniania instrukcji projektanta na zagniedone grupy wydaje si


magi, ale jak powiedzia Arthur C. Clarke kad wystarczajco zaawansowan technologi mona odrni od czarw.
Aby wszystko byo jasne, przyjrzymy si take pionowym obliczeniom. Tym razem komponenty naley traktowa tak, jakby nie miay szerokoci. Jest jedna grupa sekwencyjna zawierajca dwie rwnolege grupy, rozdzielone pustymi przestrzeniami.

Odpowiadajcy im kod jest nastpujcy:


layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(jTextField1))
.addPreferredGap(LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(jLabel2)
.addComponent(jPasswordField1))

Jak wida w kodzie, komponenty zostay wyrwnane wzgldem linii bazowych (linia bazowa
to linia, na ktrej opiera si tekst komponentu).
Mona wymusi, aby kilka komponentw miao taki sam rozmiar. Na przykad mona
sprawi, aby pole tekstowe i pole hasa miay dokadnie takie same szerokoci. W tym celu
w Matisse naley klikn prawym przyciskiem myszy i wybra opcj Same Size/Same
Width (rysunek 9.33).
Matisse doda nastpujc instrukcj do kodu rozkadu:
layout.linkSize(SwingConstants.HORIZONTAL, new Component[] {jPasswordField1, jTextField1});

Kod na listingu 9.12 przedstawia rozkad programu z poprzedniego podrozdziau przy uyciu
klasy GroupLayout zamiast GridBagLayout. Kod moe nie wydawa si ani troch prostszy ni
przedstawiony na listingu 9.10, ale tego nie musielimy pisa. Komponenty rozmiecilimy
za pomoc Matisse, a pniej nieco oczycilimy wygenerowany kod.
Listing 9.12. groupLayout/FontFrame.java
package groupLayout;
import java.awt.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

464

Java. Podstawy

Rysunek 9.33.
Wymuszanie
tej samej
szerokoci
dla dwch
komponentw

import java.awt.event.*;
import java.beans.*;
import javax.swing.*;
/**
* Ramka, ktrej komponenty zostay uoone za pomoc zarzdcy GroupLayout
*/
public class FontFrame extends JFrame
{
public static final int TEXT_ROWS = 10;
public static final int TEXT_COLUMNS = 20;
private
private
private
private
private
private

JComboBox<String> face;
JComboBox<Integer> size;
JCheckBox bold;
JCheckBox italic;
JScrollPane pane;
JTextArea sample;

public FontFrame()
{
ActionListener listener = EventHandler.create(ActionListener.class, this,
"updateSample");
// Tworzenie komponentw
JLabel faceLabel = new JLabel("Krj: ");
face = new JComboBox<>(new String[] { "Serif", "SansSerif", "Monospaced",
"Dialog",
"DialogInput" });
face.addActionListener(listener);
JLabel sizeLabel = new JLabel("Rozmiar: ");
size = new JComboBox<>(new Integer[] { 8, 10, 12, 15, 18, 24, 36, 48 });
size.addActionListener(listener);
bold = new JCheckBox("Bold");
bold.addActionListener(listener);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

italic = new JCheckBox("Italic");


italic.addActionListener(listener);
sample = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
sample.setText("Ko i pies grali w koci z pikn m u rda.");
sample.setEditable(false);
sample.setLineWrap(true);
sample.setBorder(BorderFactory.createEtchedBorder());
pane = new JScrollPane(sample);
GroupLayout layout = new GroupLayout(getContentPane());
setLayout(layout);
layout.setHorizontalGroup(layout.
createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup().addContainerGap().addGroup(
layout.createParallelGroup(GroupLayout.
Alignment.LEADING).addGroup(
GroupLayout.Alignment.TRAILING,
layout.createSequentialGroup().addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.TRAILING)
.addComponent(faceLabel).
addComponent(sizeLabel))
.addPreferredGap(LayoutStyle.
ComponentPlacement.RELATED)
.addGroup(
layout.createParallelGroup(
GroupLayout.Alignment.LEADING,
false)
.addComponent(size).
addComponent(face)))
.addComponent(italic).
addComponent(bold)).addPreferredGap(
LayoutStyle.ComponentPlacement.RELATED).addComponent(pane)
.addContainerGap()));
layout.linkSize(SwingConstants.HORIZONTAL, new java.awt.Component[] { face,
size });
layout.setVerticalGroup(layout.
createParallelGroup(GroupLayout.Alignment.LEADING)
.addGroup(
layout.createSequentialGroup().addContainerGap().addGroup(
layout.createParallelGroup(GroupLayout.
Alignment.LEADING).addComponent(
pane, GroupLayout.Alignment.TRAILING).addGroup(
layout.createSequentialGroup().addGroup(
layout.createParallelGroup(GroupLayout.
Alignment.BASELINE)
.addComponent(face).
addComponent(faceLabel))
.addPreferredGap(LayoutStyle.
ComponentPlacement.RELATED)
.addGroup(
layout.createParallelGroup(

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

465

466

Java. Podstawy
GroupLayout.Alignment.
BASELINE).addComponent(size)
.addComponent(sizeLabel)).
addPreferredGap(
LayoutStyle.ComponentPlacement.
RELATED).addComponent(
italic, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(LayoutStyle.
ComponentPlacement.RELATED)
.addComponent(bold, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE, Short.
MAX_VALUE)))
.addContainerGap()));
pack();
}
public void updateSample()
{
String fontFace = (String) face.getSelectedItem();
int fontStyle = (bold.isSelected() ? Font.BOLD : 0)
+ (italic.isSelected() ? Font.ITALIC : 0);
int fontSize = size.getItemAt(size.getSelectedIndex());
Font font = new Font(fontFace, fontStyle, fontSize);
sample.setFont(font);
sample.repaint();
}
}
javax.swing.GroupLayout 6

GroupLayout(Container host)

Tworzy obiekt GroupLayout sucy do rozkadu komponentw w kontenerze


host. Uwaga: konieczne jest wywoanie metody setLayout na rzecz obiektu
kontenera.

void setHorizontalGroup(GroupLayout.Group g)

void setVerticalGroup(GroupLayout.Group g)

Ustawia grup odpowiedzialn za rozkad w poziomie lub pionie.

void linkSize(Component... components)

void linkSize(int axis, Component... component)

Wymusza taki sam rozmiar komponentw lub taki sam rozmiar wzgldem tylko
jednej z osi (SwingConstants.HORIZONTAL lub SwingConstants.VERTICAL).

GroupLayout.SequentialGroup createSequentialGroup()

Tworzy grup, ktra ukada swoich potomkw sekwencyjnie.

GroupLayout.ParallelGroup createParallelGroup()

GroupLayout.ParallelGroup createParallelGroup(GroupLayout.Alignment align)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

GroupLayout.ParallelGroup createParallelGroup(GroupLayout.Alignment align,


boolean resizable)

Tworzy grup, ktra ukada swoich potomkw rwnolegle.


Parametry:

align

Warto BASELINE, LEADING (domylna), TRAILING


lub CENTER.

resizable

Warto true, jeli grupa moe zmienia rozmiar,


false, jeli preferowany rozmiar jest jednoczenie
rozmiarem minimalnym i maksymalnym.

boolean getHonorsVisibility()

void setHonorsVisibility(boolean b)

Pobiera lub ustawia wasno honorsVisibility. Warto true oznacza,


e komponenty niewidoczne nie bd brane pod uwag w rozkadzie. Warto
false oznacza traktowanie ich jak elementy widoczne. Opcje te pozwalaj
chwilowo ukry niektre komponenty bez zmiany ukadu.

boolean getAutoCreateGaps()

void setAutoCreateGaps(boolean b)

boolean getAutoCreateContainerGaps()

void setAutoCreateContainerGaps(boolean b)

Pobiera i ustawia wasnoci autoCreateGaps i autoCreateContainerGaps. Warto


true oznacza automatyczne dodawanie przerw pomidzy komponentami
lub pomidzy krawdziami kontenera a komponentami do nich przylegajcymi.
Warto domylna to false. Warto true jest przydatna podczas rcznego
tworzenia rozkadu GroupLayout.
javax.swing.GroupLayout.Group

GroupLayout.Group addComponent(Component c)

GroupLayout.Group addComponent(Component c, int minimumSize,


int preferredSize, int maximumSize)

Dodaje komponent do grupy. Wartoci parametrw okrelajcych rozmiar mog by


nieujemne lub staymi GroupLayout.DEFAULT_SIZE albo GroupLayout.PREFERRED_SIZE.
Uycie staej DEFAULT_SIZE powoduje wywoanie metody komponentu
getMinimumSize, getPreferredSize lub getMaximumSize. Staa PREFERRED_SIZE
powoduje wywoanie metody getPreferredSize.

GroupLayout.Group addGap(int size)

GroupLayout.Group addGap(int minimumSize, int preferredSize,


int maximumSize)

Tworzy przerw o podanym staym lub elastycznym rozmiarze.

GroupLayout.Group addGroup(GroupLayout.Group g)

Dodaje okrelon grup do grupy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

467

468

Java. Podstawy
javax.swing.GroupLayout.ParallelGroup

GroupLayout.ParallelGroup addComponent(Component c, GroupLayout.


Alignment align)

GroupLayout.ParallelGroup addComponent(Component c, GroupLayout.


Alignment align, int minimumSize, int preferredSize, int maximumSize)

GroupLayout.ParallelGroup addGroup(GroupLayout.Group g, GroupLayout.


Alignment align)

Dodaje komponent lub grup do grupy, stosujc okrelony sposb wyrwnania


BASELINE, LEADING, TRAILING lub CENTER.
javax.swing.GroupLayout.SequentialGroup

GroupLayout.SequentialGroup addContainerGap()

GroupLayout.SequentialGroup addContainerGap(int preferredSize,


int maximumSize)

Tworzy luk oddzielajc komponent od krawdzi kontenera.

GroupLayout.SequentialGroup addPreferredGap(LayoutStyle.
ComponentPlacement type)

Tworzy luk rozdzielajc komponenty. Parametr type moe przyj warto


LayoutStyle.ComponentPlacement.RELATED lub LayoutStyle.ComponentPlacement.
UNRELATED.

9.6.3. Nieuywanie adnego zarzdcy rozkadu


Zdarzaj si sytuacje, w ktrych programista nie chce zaprzta sobie gowy adnym zarzdc
rozkadu, poniewa chce jedynie umieci jaki komponent w okrelonym miejscu (czasami
nazywa si to pozycjonowaniem bezwzgldnym ang. absolute positioning). Technika
ta nie jest dobrym rozwizaniem w aplikacjach niezalenych od platformy, ale doskonale
nadaje si do szybkiego utworzenia prototypu.
Aby umieci komponent na stae w okrelonym miejscu, naley:
1.

Ustawi zarzdc rozkadu na null.

2. Wstawi wybrany komponent do kontenera.


3. Okreli pooenie i rozmiar:
frame.setLayout(null);
JButton ok = new JButton("OK");
frame.add(ok);
ok.setBounds(10, 10, 30, 15);
java.awt.Component 1.0

void setBounds(intx, int y, int width, int height)

Ustawia pooenie i rozmiar komponentu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.
Parametry:

Komponenty Swing interfejsu uytkownika

x, y

Nowy lewy grny rg komponentu

width, height

Nowy rozmiar komponentu

469

9.6.4. Niestandardowi zarzdcy rozkadu


Istnieje moliwo utworzenia wasnej klasy zarzdzajcej rozkadem komponentw w specjalny sposb. W ramach przykadu prezentujemy zarzdc rozmieszczajcego komponenty
na krawdzi koa (rysunek 9.34).
Rysunek 9.34.
Rozkad koowy

Niestandardowy zarzdca rozkadu musi implementowa interfejs LayoutManager. Konieczne


jest przesonicie nastpujcych metod:
void addLayoutComponent(String s, Component c);
void removeLayoutComponent(Component c);
Dimension preferredLayoutSize(Container parent);
Dimension minimumLayoutSize(Container parent);
void layoutContainer(Container parent);

Pierwsze dwie s wywoywane przy dodawaniu i usuwaniu komponentw. Jeli nie ma


wymogu przechowywania adnych dodatkowych informacji o komponentach, mona te metody
zdefiniowa w taki sposb, aby nic nie robiy. Kolejne dwie metody obliczaj przestrze
wymagan przez minimalny i preferowany rozkad komponentw. Zazwyczaj wartoci te
s sobie rwne. Pita metoda wykonuje rzeczywist prac i wywouje metod setBounds na
przecz wszystkich komponentw.
Biblioteka AWT udostpnia jeszcze interfejs o nazwie LayoutManager2 z dziesicioma metodami. Jego gwnym przeznaczeniem jest umoliwienie programicie
korzystania z metody add z ograniczeniami. Interfejs ten implementuj na przykad klasy
BorderLayout i GridBagLayout.

Listing 9.13 przedstawia kod bezuytecznego zarzdcy CircleLayout, ktry ukada komponenty na krawdzi koa. Klasa ramowa tego programu jest przedstawiona na listingu 9.14.
Listing 9.13. circleLayout/CircleLayout.java
package circleLayout;
import java.awt.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

470

Java. Podstawy
/**
* Ramka zawierajca komponenty uoone w kko
*/
public class CircleLayout implements LayoutManager
{
private int minWidth = 0;
private int minHeight = 0;
private int preferredWidth = 0;
private int preferredHeight = 0;
private boolean sizesSet = false;
private int maxComponentWidth = 0;
private int maxComponentHeight = 0;
public void addLayoutComponent(String name, Component comp)
{
}
public void removeLayoutComponent(Component comp)
{
}
public void setSizes(Container parent)
{
if (sizesSet) return;
int n = parent.getComponentCount();
preferredWidth = 0;
preferredHeight = 0;
minWidth = 0;
minHeight = 0;
maxComponentWidth = 0;
maxComponentHeight = 0;
// Obliczanie maksymalnych szerokoci i wysokoci komponentw
// oraz ustawianie preferowanego rozmiaru na sum rozmiarw komponentw
for (int i = 0; i < n; i++)
{
Component c = parent.getComponent(i);
if (c.isVisible())
{
Dimension d = c.getPreferredSize();
maxComponentWidth = Math.max(maxComponentWidth, d.width);
maxComponentHeight = Math.max(maxComponentHeight, d.height);
preferredWidth += d.width;
preferredHeight += d.height;
}
}
minWidth = preferredWidth / 2;
minHeight = preferredHeight / 2;
sizesSet = true;
}
public Dimension preferredLayoutSize(Container parent)
{
setSizes(parent);
Insets insets = parent.getInsets();
int width = preferredWidth + insets.left + insets.right;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

int height = preferredHeight + insets.top + insets.bottom;


return new Dimension(width, height);
}
public Dimension minimumLayoutSize(Container parent)
{
setSizes(parent);
Insets insets = parent.getInsets();
int width = minWidth + insets.left + insets.right;
int height = minHeight + insets.top + insets.bottom;
return new Dimension(width, height);
}
public void layoutContainer(Container parent)
{
setSizes(parent);
// Obliczenie rodka okrgu
Insets insets = parent.getInsets();
int containerWidth = parent.getSize().width - insets.left - insets.right;
int containerHeight = parent.getSize().height - insets.top - insets.bottom;
int xcenter = insets.left + containerWidth / 2;
int ycenter = insets.top + containerHeight / 2;
// Obliczenie promienia okrgu
int xradius = (containerWidth - maxComponentWidth) / 2;
int yradius = (containerHeight - maxComponentHeight) / 2;
int radius = Math.min(xradius, yradius);
// Ukadanie komponentw na okrgu
int n = parent.getComponentCount();
for (int i = 0; i < n; i++)
{
Component c = parent.getComponent(i);
if (c.isVisible())
{
double angle = 2 * Math.PI * i / n;
// rodek komponentu
int x = xcenter + (int) (Math.cos(angle) * radius);
int y = ycenter + (int) (Math.sin(angle) * radius);
// Przesunicie komponentu, aby jego rodek znajdowa si w punkcie (x, y),
// a jego rozmiar by rozmiarem preferowanym
Dimension d = c.getPreferredSize();
c.setBounds(x - d.width / 2, y - d.height / 2, d.width, d.height);
}
}
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

471

472

Java. Podstawy

Listing 9.14. circleLayout/CircleLayoutFrame.java


package circleLayout;
import javax.swing.*;
/**
* Ramka zawierajca przyciski uoone na obwodzie okrgu
*/
public class CircleLayoutFrame extends JFrame
{
public CircleLayoutFrame()
{
setLayout(new CircleLayout());
add(new JButton("ty"));
add(new JButton("Niebieski"));
add(new JButton("Czerwony"));
add(new JButton("Zielony"));
add(new JButton("Pomaraczowy"));
add(new JButton("Fuksja"));
add(new JButton("Bkit"));
pack();
}
}
java.awt.LayoutManager 1.0

void addLayoutComponent(String name, Component comp)

Dodaje komponent do rozkadu.


Parametry:

name

Identyfikator pooenia komponentu

comp

Komponent, ktry ma by wstawiony

void removeLayoutComponent(Component comp)

Usuwa komponent.

Dimension preferredLayoutSize(Container cont)

Zwraca preferowane wymiary kontenera.

Dimension minimumLayoutSize(Container cont)

Zwraca minimalne wymiary kontenera.

void layoutContainer(Container cont)

Ukada komponenty w kontenerze.

9.6.5. Kolejka dostpu


Dodajc kilka komponentw do okna, naley przemyle kolejk dostpu (ang. traversal
order) do nich. W chwili pierwszego pojawienia si okna aktywny jest komponent bdcy na
pierwszym miejscu w kolejce dostpu. Nacinicie klawisza Tab powoduje aktywowanie

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

473

kolejnego komponentu (przypomnijmy, e komponentami posiadajcymi fokus klawiaturowy


mona sterowa za pomoc klawiatury, np. mona klikn przycisk za pomoc spacji). Jak
wiadomo, wiele osb uywa klawisza Tab do nawigacji po elementach sterujcych interfejsu.
Nale do nich midzy innymi osoby nielubice uywa myszki oraz ci, ktrzy nie mog jej
uywa ze wzgldu na upoledzenia ruchowe bd te osoby poruszajce si po interfejsie
za pomoc polece gosowych. S to wystarczajce powody do poznania sposobu obsugi
kolejki dostpu przez Swinga.
Kolejno dostpu do komponentw jest bardzo prosta i odbywa si od lewej do prawej i od
gry do dou. Na przykad kolejno dostpu do komponentw w programie zmieniajcym
wasnoci czcionki jest nastpujca (rysunek 9.35):
Lista rozwijalna Krj.
Obszar tekstowy z przykadowym tekstem (aby przej do nastpnego pola, naley
nacisn kombinacj klawiszy Ctrl+Tab; znak tabulatora jest uznawany za tekst).
Lista rozwijalna Rozmiar.
Pole wyboru Pogrubienie.
Pole wyboru Kursywa.
Rysunek 9.35.
Kolejno
dostpu

Sytuacja komplikuje si, jeli kontener zawiera inne kontenery. Kiedy aktywowany jest inny
kontener, aktywny staje si komponent znajdujcy si w jego lewym grnym rogu, a nastpnie
aktywowane s kolejne komponenty w tym kontenerze. W kocu aktywowany jest komponent znajdujcy si za wspomnianym kontenerem.
Cech t mona obrci na swoj korzy, grupujc powizane elementy w dodatkowym kontenerze, np. panelu.
Elementy z kolejki dostpu usuwa si za pomoc instrukcji podobnej do poniszej:
component.setFocusable(false);

Technika ta jest przydatna w przypadku rysowanych komponentw, ktre nie pobieraj


danych z klawiatury.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

474

Java. Podstawy

9.7. Okna dialogowe


Wszystkie tworzone do tej pory komponenty byy wywietlane w ramach okna tworzonego
w aplikacji. Tego rodzaju sytuacje najczciej spotyka si w apletach, ktre dziaaj w oknie
przegldarki. Natomiast w samodzielnych aplikacjach czsto stosowane s oddzielne okna
dialogowe suce do podawania informacji uytkownikowi lub pobierania ich od uytkownika.
Podobnie jak wikszo systemw okienkowych, w bibliotece AWT wyrnia si okna
dialogowe modalne (ang. modal dialog box) i okna dialogowe niemodalne (ang. modeless
dialog box). Modalne okno dialogowe blokuje dostp do pozostaych okien aplikacji, dziki
czemu znajduje zastosowanie w sytuacjach, kiedy przed kontynuacj dziaania program
potrzebuje informacji od uytkownika. Na przykad modalne okno dialogowe pojawia si,
kiedy uytkownik chce wczyta plik. Przed rozpoczciem operacji konieczne jest podanie
nazwy pliku do wczytania. Aplikacja moe kontynuowa dziaanie dopiero po zamkniciu
(modalnego) okna dialogowego.
Niemodalne okna dialogowe nie blokuj dostpu do reszty aplikacji w trakcie podawania
informacji przez uytkownika. Oknem tego typu jest pasek narzdzi. Pasek ten pozostaje
widoczny tyle czasu, ile potrzeba, i nie przeszkadza to uytkownikowi w korzystaniu w tym
czasie z innych okien aplikacji.
Zaczniemy od najprostszego rodzaju okien dialogowych, czyli okien modalnych wywietlajcych jeden komunikat. W bibliotece Swing dostpna jest klasa JOptionPane, ktra umoliwia tworzenie prostych okien dialogowych bez koniecznoci pisania specjalnego kodu.
Nastpnie zajmiemy si tworzeniem bardziej zoonych okien dialogowych opartych na
wasnych oknach. Na koniec nauczymy si przenosi dane z aplikacji do okna dialogowego
i z powrotem.
Na zakoczenie tego podrozdziau prezentujemy dwa standardowe okna dialogowe wyboru
pliku i koloru. Okna dialogowe wyboru pliku s skomplikowane i do ich tworzenia potrzebna
jest znajomo klasy Swing JFileChooser napisanie wasnej takiej klasy byoby nie lada
wyzwaniem. Okno dialogowe JColorChooser suy do wybierania kolorw.

9.7.1. Okna dialogowe opcji


W bibliotece Swing dostpny jest zestaw gotowych do uycia okien dialogowych, ktre
z powodzeniem mona wykorzysta do pobrania pojedynczej prostej informacji od uytkownika. Klasa JOptionPane udostpnia cztery statyczne metody wywietlajce te proste okna:

showMessageDialog wywietla komunikat i czeka, a uytkownik kliknie

przycisk OK.

showConfirmDialog wywietla komunikat i odbiera potwierdzenie

(typu OK/Cancel).

showOptionDialog wywietla komunikat i odbiera opcj uytkownika


z okrelonego zestawu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

475

showInputDialog wywietla komunikat i odbiera jeden wiersz danych

od uytkownika.
Rysunek 9.36 przedstawia typowe okno dialogowe. Skada si ono z nastpujcych komponentw:

ikona,

komunikat,

dwa przyciski opcji.

Rysunek 9.36.
Okno dialogowe
opcji

Okno dialogowe przyjmujce dane wejciowe (ang. input dialog) zawiera dodatkowy komponent sucy do odbierania danych od uytkownika. Moe to by pole tekstowe, w ktrym
uytkownik wpisuje dowolny acuch tekstowy, albo lista rozwijalna z kilkoma opcjami do
wyboru.
Szczegy wygldu tych okien dialogowych oraz dobr ikon dla standardowych typw
komunikatw zale od stylu.
Ikona po lewej stronie zaley od typu komunikatu, ktrych jest pi:
ERROR_MESSAGE
INFORMATION_MESSAGE
WARNING_MESSAGE
QUESTION_MESSAGE
PLAIN_MESSAGE

Typ PLAIN_MESSAGE nie ma adnej ikony. Kady rodzaj okna dialogowego posiada take metod,
za pomoc ktrej mona wstawi wasn ikon.
Kady typ okna dialogowego pozwala na podanie komunikatu. Moe to by acuch tekstu,
ikona, komponent interfejsu uytkownika lub dowolny inny obiekt. Obiekt komunikatu jest
wywietlany nastpujco:
String

Rysuje acuch.

Icon

Wywietla ikon.

Component

Wywietla komponent.

Object[]

Wywietla wszystkie obiekty w tablicy jeden nad drugim.

Dowolny inny obiekt Stosuje metod toString i wywietla uzyskany acuch.


Opcje te mona obejrze, uruchamiajc program z listingu 9.15.
Oczywicie zdecydowanie najczciej wykorzystywana jest opcja podawania acucha komunikatu. Dostarczenie obiektu Component daje du elastyczno, poniewa mona sprawi,
aby metoda paintComponent narysowaa wszystko, co mona zechcie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

476

Java. Podstawy
Przyciski na dole zale od typu okna dialogowego i typu opcji. Metody showMessageDialog
i showInputDialog dostarczaj tylko standardowe przyciski (odpowiednio OK i OK/Cancel).
Metoda showConfirmDialog przyjmuje jeden z czterech typw opcji:
DEFAULT_OPTION
YES_NO_OPTION
YES_NO_CANCEL_OPTION
OK_CANCEL_OPTION

Metoda showOptionDialog pozwala na utworzenie dowolnego zestawu opcji. Opcje podawane


s w tablicy obiektw. Kady element tablicy jest wizualizowany w nastpujcy sposb:
String

Tworzy przycisk, ktrego etykiet jest acuch.

Icon

Tworzy przycisk, ktrego etykiet jest ikona.

Component

Wywietla komponent.

Dowolny inny obiekt Stosuje metod toString i tworzy przycisk, ktrego


etykiet jest uzyskany acuch.
Wartoci zwracane przez powysze funkcje s nastpujce:
showMessageDialog

Brak

showConfirmDialog

Liczba cakowita reprezentujca wybran opcj

showOptionDialog

Liczba cakowita reprezentujca wybran opcj

showInputDialog

acuch wprowadzony lub wybrany przez uytkownika

Metody showConfirmDialog i showOptionDialog zwracaj liczby cakowite reprezentujce kliknity przez uytkownika przycisk. W przypadku okna dialogowego jest to zwyky indeks
wybranej opcji lub warto CLOSED_OPTION, jeli uytkownik zamkn okno, nie wybierajc
adnej opcji. W oknie dialogowym potwierdzenia (ang. confirmation dialog) dostpne s
nastpujce wartoci zwrotne:
OK_OPTION
CANCEL_OPTION
YES_OPTION
NO_OPTION
CLOSED_OPTION

Na pierwszy rzut oka wydaje si, e opcji jest bardzo duo, ale w praktyce opanowanie ich
jest bardzo proste. Naley postpowa zgodnie z poniszymi wskazwkami:
1.

Wybierz rodzaj okna dialogowego (komunikat, potwierdzenie, opcje, dane


wejciowe).

2. Wybierz ikon (bd, informacja, ostrzeenie, pytanie, brak lub wasna).


3. Wybierz rodzaj komunikatu (acuch, ikona, wasny komponent lub stos

komponentw).
4. W przypadku okna potwierdzenia wybierz typ opcji (Yes/No, Yes/No/Cancel
lub OK/Cancel).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

477

5. W przypadku okna dialogowego opcji wybierz opcje (acuchy, ikony lub wasne

komponenty) i opcj domyln.


6. W przypadku okna dialogowego danych wejciowych zdecyduj, czy wybra pole

tekstowe, czy list rozwijaln.


7. Zlokalizuj odpowiedni metod do wywoania w API JOptionPane.

Wyobramy sobie na przykad, e chcemy utworzy okno dialogowe widoczne na rysunku 9.36. Okno to wywietla komunikat i prosi uytkownika o zatwierdzenie lub anulowanie. Jest to wic okno potwierdzenia. Jako ikona wywietli si znak zapytania. Typ opcji
to OK_CANCEL_OPTION. Oto przykadowy kod tworzcy takie okno:
int selection = JOptionPane.showConfirmDialog(parent,
"Message", "Tytu",
JOptionPane.OK_CANCEL_OPTION,
JOptionPane.QUESTION_MESSAGE);
if (selection == JOptionPane.OK_OPTION) . . .

acuch komunikatu moe zawiera znaki nowego wiersza (\n), ktre powoduj,
e acuch zostanie podzielony na kilka wierszy.

Program, ktrego klasa ramowa jest przedstawiona na listingu 9.15, wywietla sze sekcji
z przecznikami (rysunek 9.37). Klasa tworzca te komponenty znajduje si na listingu 9.16.
Nacinicie przycisku Poka powoduje wywietlenie odpowiedniego okna dialogowego.
Rysunek 9.37.
Program
OptionDialogTest

Listing 9.15. optionDialog/OptionDialogFrame.java


package optionDialog;
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

478

Java. Podstawy
import java.util.*;
import javax.swing.*;
/**
* Ramka zawierajca ustawienia dotyczce wyboru rnych okien dialogowych opcji
*/
public class OptionDialogFrame extends JFrame
{
private ButtonPanel typePanel;
private ButtonPanel messagePanel;
private ButtonPanel messageTypePanel;
private ButtonPanel optionTypePanel;
private ButtonPanel optionsPanel;
private ButtonPanel inputPanel;
private String messageString = "Komunikat";
private Icon messageIcon = new ImageIcon("blue-ball.gif");
private Object messageObject = new Date();
private Component messageComponent = new SampleComponent();
public OptionDialogFrame()
{
JPanel gridPanel = new JPanel();
gridPanel.setLayout(new GridLayout(2, 3));
typePanel = new ButtonPanel("Typ", "Komunikat", "Potwierdzenie", "Opcja",
"Dane wejciowe");
messageTypePanel = new ButtonPanel("Typ komunikatu", "ERROR_MESSAGE",
"INFORMATION_MESSAGE",
"WARNING_MESSAGE", "QUESTION_MESSAGE", "PLAIN_MESSAGE");
messagePanel = new ButtonPanel("Komunikat", "acuch", "Ikona", "Komponent",
"Inny", "Object[]");
optionTypePanel = new ButtonPanel("Potwierdzenie", "DEFAULT_OPTION",
"YES_NO_OPTION",
"YES_NO_CANCEL_OPTION", "OK_CANCEL_OPTION");
optionsPanel = new ButtonPanel("Opcja", "String[]", "Icon[]", "Object[]");
inputPanel = new ButtonPanel("Dane wejciowe", "Pole tekstowe", "Pole kombi");
gridPanel.add(typePanel);
gridPanel.add(messageTypePanel);
gridPanel.add(messagePanel);
gridPanel.add(optionTypePanel);
gridPanel.add(optionsPanel);
gridPanel.add(inputPanel);
// Dodanie panelu z przyciskiem Poka
JPanel showPanel = new JPanel();
JButton showButton = new JButton("Poka");
showButton.addActionListener(new ShowAction());
showPanel.add(showButton);
add(gridPanel, BorderLayout.CENTER);
add(showPanel, BorderLayout.SOUTH);
pack();
}
/**
* Pobiera aktualnie wybrany komunikat.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

479

* @return acuch, ikona, komponent lub tablica obiektw, w zalenoci od wyboru w panelu Komunikat
*/
public Object getMessage()
{
String s = messagePanel.getSelection();
if (s.equals("acuch")) return messageString;
else if (s.equals("Ikona")) return messageIcon;
else if (s.equals("Komponent")) return messageComponent;
else if (s.equals("Object[]")) return new Object[] { messageString,
messageIcon,
messageComponent, messageObject };
else if (s.equals("Inny")) return messageObject;
else return null;
}
/**
* Pobiera aktualnie wybrane opcje.
* @return tablica acuchw, ikon lub obiektw, w zalenoci od wyboru w panelu Opcja
*/
public Object[] getOptions()
{
String s = optionsPanel.getSelection();
if (s.equals("String[]")) return new String[] { "ty", "Niebieski",
"Czerwony" };
else if (s.equals("Icon[]")) return new Icon[] { new ImageIcon("yellowball.gif"),
new ImageIcon("blue-ball.gif"), new ImageIcon("red-ball.gif") };
else if (s.equals("Object[]")) return new Object[] { messageString,
messageIcon,
messageComponent, messageObject };
else return null;
}
/**
* Pobiera wybrany komunikat lub typ opcji.
* @param panel Typ komunikatu lub panel Potwierdzenie
* @return wybrana staa XXX_MESSAGE lub XXX_OPTION z klasy JOptionPane
*/
public int getType(ButtonPanel panel)
{
String s = panel.getSelection();
try
{
return JOptionPane.class.getField(s).getInt(null);
}
catch (Exception e)
{
return -1;
}
}
/**
* Suchacz akcji przycisku Poka wywietla okno dialogowe potwierdzenia, danych wejciowych,
* komunikatu lub opcji w zalenoci od wyboru typu panelu.
*/
private class ShowAction implements ActionListener
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

480

Java. Podstawy
public void actionPerformed(ActionEvent event)
{
if (typePanel.getSelection().equals("Potwierdzenie"))
JOptionPane.showConfirmDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(optionTypePanel),
getType(messageTypePanel));
else if (typePanel.getSelection().equals("Dane wejciowe"))
{
if (inputPanel.getSelection().equals("Pole tekstowe"))
JOptionPane.showInputDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(messageTypePanel));
else JOptionPane.showInputDialog(OptionDialogFrame.this, getMessage(),
"Tytu",
getType(messageTypePanel), null, new String[] { "ty",
"Niebieski", "Czerwony" },
"Niebieski");
}
else if (typePanel.getSelection().equals("Komunikat"))
JOptionPane.showMessageDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(messageTypePanel));
else if (typePanel.getSelection().equals("Opcja"))
JOptionPane.showOptionDialog(
OptionDialogFrame.this, getMessage(), "Tytu",
getType(optionTypePanel),
getType(messageTypePanel), null, getOptions(), getOptions()[0]);
}
}
}
/**
* Komponent z pomalowan powierzchni
*/
class SampleComponent extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
Rectangle2D rect = new Rectangle2D.Double(0, 0, getWidth() - 1,
getHeight() - 1);
g2.setPaint(Color.YELLOW);
g2.fill(rect);
g2.setPaint(Color.BLUE);
g2.draw(rect);
}
public Dimension getPreferredSize()
{
return new Dimension(10, 10);
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

Listing 9.16. optionDialog/ButtonPanel.java


package optionDialog;
import javax.swing.*;
/**
* Panel z przecznikami w ramce z tytuem
*/
public class ButtonPanel extends JPanel
{
private ButtonGroup group;
/**
* Tworzy panel przyciskw
* @param title Tytu wywietlany w obramowaniu
* @param options Tablica etykiet przecznikw
*/
public ButtonPanel(String title, String... options)
{
setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(),
title));
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
group = new ButtonGroup();
// Utworzenie po jednym przeczniku dla kadej opcji
for (String option : options)
{
JRadioButton b = new JRadioButton(option);
b.setActionCommand(option);
add(b);
group.add(b);
b.setSelected(option == options[0]);
}
}
/**
* Pobiera aktualnie wybran opcj
* @return Zwraca etykiet aktualnie wybranego przecznika
*/
public String getSelection()
{
return group.getSelection().getActionCommand();
}
}
javax.swing.JOptionPane 1.2

static void showMessageDialog(Component parent, Object message,


String title, int messageType, Icon icon)

static void showMessageDialog(Component parent, Object message,


String title, int messageType)

static void showMessageDialog(Component parent, Object message)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

481

482

Java. Podstawy

static void showInternalMessageDialog(Component parent, Object message,


String title, int messageType, Icon icon)

static void showInternalMessageDialog(Component parent, Object message,


String title, int messageType)

static void showInternalMessageDialog(Component parent, Object message)

Wywietla okno dialogowe z komunikatem lub wewntrzne okno dialogowe


(wewntrzne okno dialogowe w caoci zawiera si w swoim oknie nadrzdnym).
Parametry:

parent

Komponent nadrzdny (moe by null).

message

Komunikat, ktry ma si pojawi w oknie


dialogowym (moe by acuch, ikona,
komponent lub tablica tych elementw).

title

acuch widoczny na pasku tytuu.

messageType

Jedna z nastpujcych wartoci: ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

icon

Ikona, ktra ma si pojawi zamiast


standardowej ikony.

static int showConfirmDialog(Component parent, Object message,


String title, int optionType, int messageType, Icon icon)

static int showConfirmDialog(Component parent, Object message,


String title, int optionType, int messageType)

static int showConfirmDialog(Component parent, Object message,


String title, int optionType)

static int showConfirmDialog(Component parent, Object message)

static int showInternalConfirmDialog(Component parent, Object message,


String title, int optionType, int messageType, Icon icon)

static int showInternalConfirmDialog(Component parent, Object message,


String title, int optionType, int messageType)

static int showInternalConfirmDialog(Component parent, Object message,


String title, int optionType)

static int showInternalConfirmDialog(Component parent, Object message)

Wywietla okno dialogowe potwierdzenia lub wewntrzne okno dialogowe


potwierdzenia (wewntrzne okno dialogowe w caoci zawiera si w swoim oknie
nadrzdnym). Zwraca wybran przez uytkownika opcj (OK_OPTION, CANCEL_OPTION,
YES_OPTION, NO_OPTION) lub CLOSED_OPTION, jeli uytkownik zamknie okno.
Parametry:

parent

Komponent nadrzdny (moe by null).

message

Komunikat, ktry ma si pojawi w oknie


dialogowym (moe by acuch, ikona,
komponent lub tablica tych elementw).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

title

acuch widoczny na pasku tytuu.

messageType

Jedna z nastpujcych wartoci: ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

optionType

Jedna z nastpujcych wartoci: DEFAULT_OPTION,


YES_NO_OPTION, YES_NO_CANCEL_OPTION,
OK_CANCEL_OPTION.

icon

Ikona, ktra ma si pojawi zamiast


standardowej ikony.

483

static int showOptionDialog(Component parent, Object message, String title,


int optionType, int messageType, Icon icon, Object[] options, Object default)

static int showInternalOptionDialog(Component parent, Object message,


String title, int optionType, int messageType, Icon icon, Object[] options,
Object default)

Wywietla okno dialogowe opcji lub wewntrzne okno dialogowe opcji (wewntrzne
okno dialogowe w caoci zawiera si w swoim oknie nadrzdnym). Zwraca indeks
wybranej przez uytkownika opcji lub warto CLOSED_OPTION, jeli uytkownik
anulowa okno.
Parametry:

parent

Komponent nadrzdny (moe by null).

message

Komunikat, ktry ma si pojawi w oknie


dialogowym (moe by acuch, ikona,
komponent lub tablica tych elementw).

title

acuch widoczny na pasku tytuu.

messageType

Jedna z nastpujcych wartoci: ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

optionType

Jedna z nastpujcych wartoci: DEFAULT_OPTION,


YES_NO_OPTION, YES_NO_CANCEL_OPTION,
OK_CANCEL_OPTION.

icon

Ikona, ktra ma si pojawi zamiast


standardowej ikony.

options

Tablica opcji (moe zawiera acuchy,


ikony lub komponenty).

default

Domylna opcja, ktra jest prezentowana


uytkownikowi.

static Object showInputDialog(Component parent, Object message, String title,


int message-Type, Icon icon, Object[] values, Object default)

static String showInputDialog(Component parent, Object message, String title,


int message-Type)

static String showInputDialog(Component parent, Object message)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

484

Java. Podstawy

static String showInputDialog(Object message)

static String showInputDialog(Component parent, Object message,


Object default) 1.4

static String showInputDialog(Object message, Object default) 1.4

static Object showInternalInputDialog(Component parent, Object message,


String title, int messageType, Icon icon, Object[] values, Object default)

static String showInternalInputDialog(Component parent, Object message,


String title, int messageType)

static String showInternalInputDialog(Component parent, Object message)

Wywietla okno dialogowe przyjmowania danych lub wewntrzne okno dialogowe


przyjmowania danych (wewntrzne okno dialogowe w caoci zawiera si w swoim
oknie nadrzdnym). Zwraca acuch znakw wprowadzony przez uytkownika
lub warto null, jeli uytkownik anulowa okno.
Parametry:

parent

Komponent nadrzdny (moe by null).

message

Komunikat, ktry ma si pojawi w oknie


dialogowym (moe by acuch, ikona,
komponent lub tablica tych elementw).

title

acuch widoczny na pasku tytuu.

messageType

Jedna z nastpujcych wartoci: ERROR_MESSAGE,


INFORMATION_MESSAGE, WARNING_MESSAGE,
QUESTION_MESSAGE, PLAIN_MESSAGE.

icon

Ikona, ktra ma si pojawi zamiast


standardowej ikony.

values

Tablica wartoci, ktre maj zosta


uwzgldnione w licie rozwijalnej.

default

Warto domylnie prezentowana


uytkownikowi.

9.7.2. Tworzenie okien dialogowych


W poprzednim podrozdziale zostaa zaprezentowana technika tworzenia prostych okien dialogowych za pomoc klasy JOptionPane. W tym podrozdziale nauczymy si tworzy takie
okna na wasn rk.
Rysunek 9.38 przedstawia typowe modalne okno dialogowe zawierajce informacje
o programie wywietlane w odpowiedzi na kliknicie opcji O programie.
Implementacja takiego okna polega na rozszerzeniu klasy JDialog. Proces ten przebiega
w zasadzie tak samo jak w przypadku rozszerzania klasy JFrame przy tworzeniu okna gwnego aplikacji. Mianowicie:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

485

Rysunek 9.38.
Okno dialogowe
typu O programie

1.

W konstruktorze swojego okna dialogowego wywoaj konstruktor nadklasy JDialog.

2. Utwrz elementy interfejsu uytkownika okna dialogowego.


3. Utwrz procedury obsugi zdarze.
4. Ustaw rozmiar okna.

W konstruktorze nadklasy trzeba poda ramk nadrzdn, tytu okna dialogowego oraz
okreli modalno.
Ramka nadrzdna odpowiada za miejsce wywietlenia okna dialogowego. Warto null
powoduje, e okno naley do ukrytej ramki.
Modalno okrela, ktre z pozostaych okien aplikacji bd zablokowane, kiedy wywietli
si to okno. Okno niemodalne nie blokuje innych okien, natomiast okno modalne blokuje
wszystkie pozostae okna (poza swoimi potomkami). Niemodalne okna dialogowe znajduj
zastosowanie jako zawsze dostpne zestawy narzdzi. Modalne okno dialogowe mona zastosowa, w przypadku gdy do kontynuacji dziaania programu konieczne jest dostarczenie informacji przez uytkownika.
W Java SE 6 dostpne s dwa rodzaje modalnoci. Okno dialogowe z modalnoci
dokumentu (ang. document-modal dialog) blokuje wszystkie okna nalece do tego
samego dokumentu. Jako dokument w tym przypadku rozumie si hierarchi okien
majcych wsplnego przodka, ktry nie ma waciciela w postaci okna dialogowego.
W ten sposb rozwizano problem z systemami pomocy. Wczeniej uytkownik nie mg
korzysta z pomocy, jeli byo wywietlone okno modalne. Okno dialogowe z modalnoci zestawu narzdzi (ang. toolkit-modal dialog) blokuje wszystkie okna nalece do
tego samego zestawu narzdzi. Zestaw narzdzi to program w Javie, ktry uruchamia
kilka aplikacji, np. silnik apletw w przegldarce. Wicej informacji na ten temat mona
znale na stronie www.oracle.com/technetwork/articles/javase/modality-137604.html.

Poniej znajduje si kod rdowy tego okna dialogowego:


public AboutDialog extends JDialog
{
public AboutDialog(JFrame owner)
{
super(owner, "Test okna O programie", true);
add(new JLabel(
"<html><h1><i>Java. Podstawy</i></h1><hr>Cay Horstmann, Gary Cornell</html>"),
BorderLayout.CENTER);
JPanel panel = new JPanel();
JButton ok = new JButton("OK");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

486

Java. Podstawy
ok.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});
panel.add(ok);
add(panel, BorderLayout.SOUTH);
setSize(250, 150);
}
}

Jak wida, konstruktor tworzy elementy interfejsu uytkownika (w tym przypadku etykiety
i przycisk) oraz zawiera procedur obsugi przycisku i ustawia rozmiar okna dialogowego.
Aby wywietli okno dialogowe, naley utworzy nowy obiekt okna dialogowego, a nastpnie
go uwidoczni:
JDialog dialog = new AboutDialog(this);
dialog.setVisible(true);

We fragmencie kodu zaprezentowanym poniej okno dialogowe tworzone jest tylko jeden raz,
po czym mona go uywa za kadym razem, gdy uytkownik kliknie pozycj O programie.
if (dialog == null)
// pierwszy raz
dialog = new AboutDialog(this);
dialog.setVisible(true);

Kliknicie przycisku OK powinno zamyka okno. Dziaanie to zostao zdefiniowane w procedurze obsugi przycisku OK:
ok.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});

Okno zostanie ukryte take w wyniku kliknicia przycisku Zamknij. Podobnie jak w przypadku
ramki JFrame, dziaanie to mona zmieni za pomoc metody setDefaultCloseOperation.
Listing 9.17 przedstawia kod programu testujcego omawiane okno dialogowe, a listing 9.18
klas AboutDialog.
Listing 9.17. dialog/DialogFrame.java
package dialog;
import java.awt.event.*;
import javax.swing.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

/**
* Ramka z menu, ktrego akcja Plik/O programie wywietla okno dialogowe
*/
public class DialogFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private AboutDialog dialog;
public DialogFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Tworzenie menu Plik
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu fileMenu = new JMenu("Plik");
menuBar.add(fileMenu);
// Tworzenie elementw O programie i Zamknij
// Element O programie wywietla okno dialogowe O programie
JMenuItem aboutItem = new JMenuItem("O programie");
aboutItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (dialog == null) // pierwszy raz
dialog = new AboutDialog(DialogFrame.this);
dialog.setVisible(true); // wyskakujce okno dialogowe
}
});
fileMenu.add(aboutItem);
// Element Zamknij powoduje zamknicie programu
JMenuItem exitItem = new JMenuItem("Zamknij");
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
fileMenu.add(exitItem);
}
}

Listing 9.18. dialog/AboutDialog.java


package dialog;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

487

488

Java. Podstawy

/**
* Przykadowe modalne okno dialogowe wywietlajce komunikat i oczekujce na kliknicie przycisku Ok
*/
public class AboutDialog extends JDialog
{
public AboutDialog(JFrame owner)
{
super(owner, "Test okna O programie", true);
// Dodanie etykiety HTML
add(
new JLabel(
"<html><h1><i>Core Java</i></h1><hr> By Cay Horstmann and Gary
Cornell </html>"),
BorderLayout.CENTER);
// Przycisk Ok zamyka okno
JButton ok = new JButton("Ok");
ok.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
setVisible(false);
}
});
// Dodanie przycisku Ok przy krawdzi poudniowej
JPanel panel = new JPanel();
panel.add(ok);
add(panel, BorderLayout.SOUTH);
pack();
}
}
javax.swing.JDialog 1.2

public JDialog(Frame parent, String title, boolean modal)

Tworzy okno dialogowe. Okno nie jest widoczne, dopki nie zostanie celowo
uwidocznione.
Parametry:

parent

Ramka zawierajca okno

title

Tytu okna

modal

Warto true, jeli okno ma by modalne


(okno modalne blokuje dostp do pozostaych okien)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

489

9.7.3. Wymiana danych


Najczstszym powodem tworzenia okien dialogowych jest ch uzyskania danych od uytkownika. Wiemy ju, e utworzenie obiektu okna dialogowego jest bardzo atwe (naley poda
jego dane pocztkowe i uwidoczni je za pomoc metody setVisible(true)). Teraz zajmiemy
si przesyaniem danych z i do okna.
Przyjrzyjmy si oknu widocznemu na rysunku 9.39, ktre moe suy do pobierania nazwy
i hasa uytkownika pragncego poczy si z jak usug internetow.
Rysunek 9.39.
Okno dialogowe
z polem hasa

Okno dialogowe powinno posiada metody ustawiajce dane pocztkowe. Na przykad klasa
PasswordChooser omawianego programu zawiera metod o nazwie setUser, ktra wstawia
domylne wartoci do pl tekstowych:
public void setUser(User u)
{
username.setText(u.getName());
}

Po ustawieniu wartoci domylnych (jeli jest to konieczne) naley wywoa metod setVi
sible(true) w celu uwidocznienia okna.
Uytkownik moe poda wymagane informacje i klikn przycisk Ok lub Anuluj. Procedury
obsugujce zdarzenia kadego z tych przyciskw wywouj metod setVisible(false),
ktra koczy wywoanie metody setVisible(true). Uytkownik moe te zamkn okno.
Jeli nie zdefiniowano adnego suchacza zdarze okna, zastosowana zostanie standardowa
procedura zamykajca polegajca na ukryciu okna, w wyniku ktrego nastpuje przerwanie
dziaania metody setVisible(true).
Wane jest to, e blokada tworzona przez metod setVisible(true) dziaa do chwili zamknicia okna. To uatwia tworzenie modalnych okien dialogowych.
Musimy sprawdzi, czy uytkownik klikn przycisk Ok czy Anuluj. W kodzie znacznik ok
zosta ustawiony na warto false przed pokazaniem okna. Warto t na true moe zmieni
tylko procedura obsugi przycisku Ok. W przypadku jego nacinicia mona pobra dane
wpisane w oknie przez uytkownika.
Prezentowany przykadowy program posiada dodatkowe usprawnienie. Konstruujc obiekt typu
JDialog, trzeba okreli ramk nadrzdn. Czsto jednak zdarza si, e jedno okno dialogowe

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

490

Java. Podstawy

Przenoszenie danych z niemodalnego okna dialogowego nie jest takie proste.


Wywietlenie takiego okna nie powoduje zaoenia blokady przez metod setVi
sible(true) i program kontynuuje dziaanie. Kiedy uytkownik kliknie przycisk Ok
w oknie niemodalnym, musi ono wysa zdarzenie do jakiego obiektu nasuchujcego
w programie.

moe by wywietlane w kilku rnych ramkach. Lepiej jest wybra ramk nadrzdn,
kiedy okno dialogowe jest gotowe do wywietlenia ni po utworzeniu obiektu typu Pas
swordChooser.
Sztuka polega na tym, aby sprawi, e klasa PasswordChooser bdzie rozszerzaa klas JPanel,
a nie JDialog. W tym celu obiekt JDialog naley utworzy w locie, w metodzie showDialog:
public boolean showDialog(Frame owner, String title)
{
ok = false;
if (dialog == null || dialog.getOwner() != owner)
{
dialog = new JDialog(owner, true);
dialog.add(this);
dialog.pack();
}
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}

Warto zauway, e bezpiecznym ustawieniem dla parametru owner jest warto null.
Mona to zrobi jeszcze lepiej. Czasami ramka nadrzdna nie jest od razu gotowa. Mona j
z atwoci uzyska z dowolnego komponentu parent, np.:
Frame owner;
if (parent instanceof Frame)
owner = (Frame) parent;
else
owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);

Usprawnienie to zastosowalimy w naszym przykadowym programie. Mechanizm ten wykorzystuje take klasa JOptionPane.
Wiele okien dialogowych posiada przycisk domylny (ang. default button), ktry jest automatycznie naciskany, kiedy uytkownik nacinie klawisz wyzwolenia (ang. trigger key)
w wikszoci stylw jest to klawisz Enter. Przycisk domylny jest w jaki sposb wyrniony, zazwyczaj grubym obramowaniem.
Przycisk domylny ustawia si w panelu gwnym okna dialogowego (ang. root pane):
dialog.getRootPane().setDefaultButton(okButton);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

491

Stosujc si do naszych zalece dotyczcych umieszczenia okna dialogowego w panelu,


naley pamita, e przycisk domylny musi by ustawiony po zapakowaniu panelu w oknie.
Sam panel nie posiada panelu gwnego.
Listing 9.19 przedstawia kod klasy ramowej programu ilustrujcego przepyw danych
w oknie dialogowym. Klasa tego okna jest pokazana na listingu 9.20.
Listing 9.19. dataExchange/DataExchangeFrame.java
package dataExchange;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Ramka z menu, ktrego akcja Plik/Pocz wywietla okno dialogowe z polem hasa
*/
public class DataExchangeFrame extends JFrame
{
public static final int TEXT_ROWS = 20;
public static final int TEXT_COLUMNS = 40;
private PasswordChooser dialog = null;
private JTextArea textArea;
public DataExchangeFrame()
{
// Tworzenie menu Plik
JMenuBar mbar = new JMenuBar();
setJMenuBar(mbar);
JMenu fileMenu = new JMenu("Plik");
mbar.add(fileMenu);
// Tworzenie elementw menu Pocz i Zamknij
JMenuItem connectItem = new JMenuItem("Pocz");
connectItem.addActionListener(new ConnectAction());
fileMenu.add(connectItem);
// Opcja Zamknij zamyka program
JMenuItem exitItem = new JMenuItem("Zamknij");
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
fileMenu.add(exitItem);
textArea = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
add(new JScrollPane(textArea), BorderLayout.CENTER);
pack();
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

492

Java. Podstawy
/**
* Akcja Connect wywietla okno dialogowe z polem hasa
*/
private class ConnectAction implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
// Jeli jest to pierwszy raz, tworzy okno dialogowe
if (dialog == null) dialog = new PasswordChooser();
// Ustawianie wartoci domylnych
dialog.setUser(new User("Twoja nazwa", null));
// Wywietlenie okna dialogowego
if (dialog.showDialog(DataExchangeFrame.this, "Pocz"))
{
// Pobranie danych uytkownika w przypadku zatwierdzenia
User u = dialog.getUser();
textArea.append("nazwa uytkownika = " + u.getName() + ", haso = "
+ (new String(u.getPassword())) + "\n");
}
}
}
}

Listing 9.20. dataExchange/PasswordChooser.java


package dataExchange;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Elementy suce do podania hasa, ktre wida w oknie dialogowym
*/
public class PasswordChooser extends JPanel
{
private JTextField username;
private JPasswordField password;
private JButton okButton;
private boolean ok;
private JDialog dialog;
public PasswordChooser()
{
setLayout(new BorderLayout());
// Utworzenie panelu z polami nazwy uytkownika i hasa
JPanel panel = new JPanel();
panel.setLayout(new GridLayout(2, 2));
panel.add(new JLabel("Nazwa uytkownika:"));
panel.add(username = new JTextField(""));
panel.add(new JLabel("Haso:"));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

panel.add(password = new JPasswordField(""));


add(panel, BorderLayout.CENTER);
// Utworzenie przyciskw OK i Anuluj, ktre zamykaj okno dialogowe
okButton = new JButton("Ok");
okButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
ok = true;
dialog.setVisible(false);
}
});
JButton cancelButton = new JButton("Anuluj");
cancelButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
dialog.setVisible(false);
}
});
// Dodawanie przyciskw w pobliu poudniowej krawdzi
JPanel buttonPanel = new JPanel();
buttonPanel.add(okButton);
buttonPanel.add(cancelButton);
add(buttonPanel, BorderLayout.SOUTH);
}
/**
* Ustawia wartoci domylne okna dialogowego
* @param u domylne informacje uytkownika
*/
public void setUser(User u)
{
username.setText(u.getName());
}
/**
* Pobiera dane podane w oknie dialogowym
* @return a obiekt typu User, ktrego stan reprezentuje dane wprowadzone w oknie dialogowym
*/
public User getUser()
{
return new User(username.getText(), password.getPassword());
}
/**
* Wywietla panel z elementami przyjmujcymi dane od uytkownika w oknie dialogowym
* @param parent komponent w ramce nadrzdnej lub warto null
* @param title tytu okna dialogowego
*/
public boolean showDialog(Component parent, String title)
{
ok = false;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

493

494

Java. Podstawy
// Lokalizacja ramki nadrzdnej
Frame owner = null;
if (parent instanceof Frame) owner = (Frame) parent;
else owner = (Frame) SwingUtilities.getAncestorOfClass(Frame.class, parent);
// Jeli jest to pierwszy raz lub zmieni si uytkownik, utworzenie nowego okna dialogowego
if (dialog == null || dialog.getOwner() != owner)
{
dialog = new JDialog(owner, true);
dialog.add(this);
dialog.getRootPane().setDefaultButton(okButton);
dialog.pack();
}
// Ustawienie tytuu i wywietlenie okna dialogowego
dialog.setTitle(title);
dialog.setVisible(true);
return ok;
}
}
javax.swing.SwingUtilities 1.2

Container getAncestorOfClass(Class c, Component comp)

Zwraca najgbiej zagniedony kontener nadrzdny danego komponentu,


ktry naley do danej klasy lub jednej z jej podklas.
javax.swing.JComponent 1.2

JRootPane getRootPane()

Pobiera panel gwny (ang. root pane) zawierajcy dany komponent lub warto
null, jeli komponent ten nie posiada przodka z panelem gwnym.
javax.swing.JRootPane 1.2

void setDefaultButton(JButton button)

Ustawia domylny przycisk dla panelu gwnego. Aby dezaktywowa ten przycisk,
naley wywoa t metod z wartoci null.
javax.swing.JButton 1.2

boolean isDefaultButton()

Zwraca warto true, jeli dany przycisk jest domylny w swoim panelu gwnym.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

495

9.7.4. Okna dialogowe wyboru plikw


Wiele aplikacji musi posiada opcje otwierania i zapisywania plikw. Napisanie dobrego okna
dialogowego wywietlajcego pliki i katalogi oraz umoliwiajcego nawigacj po systemie
plikw nie jest atwe. Nie chcielibymy te ponownie wynajdywa koa. Na szczcie
w pakiecie Swing dostpna jest klasa JFileChooser pozwalajca na tworzenie okien dialogowych wygldajcych podobnie do tych, ktre mona spotka w wikszoci aplikacji rodzimych. Okna dialogowe JFileChooser s zawsze modalne. Naley zauway, e klasa JFile
Chooser nie jest podklas klasy JDialog. Zamiast metody setVisible(true) do wywietlania
okien dialogowych wyboru pliku suy metoda showOpenDialog, a do okien zapisu pliku
metoda showSaveDialog. Przycisk automatycznie zyskuje etykiet Open lub Save. Korzystajc
z metody showDialog, mona okreli wasn etykiet. Rysunek 9.40 przedstawia przykadowe
okno dialogowe wyboru pliku.
Rysunek 9.40.
Okno dialogowe
wyboru pliku

Ponisze punkty opisuj procedur tworzenia okna dialogowego wyboru pliku i odzyskiwania
tego, co uytkownik wybra w polu wyboru.
1.

Utwrz obiekt typu JFileChooser. W przeciwiestwie do konstruktora klasy JDialog


w tym przypadku nie trzeba podawa komponentu nadrzdnego. Dziki temu okno
wyboru pliku mona wykorzysta w kilku ramkach.
Na przykad:
JFileChooser chooser = new JFileChooser();

Wielokrotne uycie jednego obiektu JFileChooser jest dobrym pomysem, poniewa


konstruktor JFileChooser moe dziaa powoli, zwaszcza w systemie Windows, jeli
uytkownik posiada wiele zmapowanych dyskw sieciowych.
2. Ustaw katalog za pomoc metody setCurrentDirectory.

Na przykad ponisza procedura robi uytek z aktualnego katalogu roboczego:


chooser.setCurrentDirectory(new File("."));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

496

Java. Podstawy
W takim przypadku konieczne jest dostarczenie obiektu typu File. Obiekty tego
typu zostay szczegowo omwione w rozdziale 12. Na razie wystarczy nam wiedza,
e konstruktor File(String filename) zamienia plik lub katalog o okrelonej
nazwie na obiekt typu File.
3. Jeli istnieje due prawdopodobiestwo, e uytkownik wybierze plik o okrelonej
nazwie, nazw t naley poda za pomoc metody setSelectedFile:
chooser.setSelectedFile(new File(filename));

4. Aby umoliwi wybr kilku plikw w oknie dialogowym, naley uy metody


setMultiSelectionEnabled. Jest to oczywicie niezbyt czsto stosowana opcja.
chooser.setMultiSelectionEnabled(true);

5. Aby w oknie dialogowym widoczne byy wycznie pliki okrelonego typu

(na przykad z rozszerzeniem .gif), naley utworzy filtr plikw. Filtry plikw
zostay opisane w dalszej czci tego rozdziau.
6. Przy standardowych ustawieniach uytkownik moe wybiera tylko pliki.
Aby umoliwi wybr katalogw, naley uy metody setFileSelectionMode.
Jej parametrem powinna by jedna z nastpujcych wartoci JFileChooser.FILES_ONLY
(domylna), JFileChooser.DIRECTORIES_ONLY lub JFileChooser.FILES_AND_
DIRECTORIES.
7. Do uwidocznienia okna su metody showOpenDialog i showSaveDialog.

W ich wywoaniach konieczne jest podanie komponentu nadrzdnego:


int result = chooser.showOpenDialog(parent);

lub
int result = chooser.showSaveDialog(parent);

Jedyna rnica pomidzy tymi metodami dotyczy przycisku zatwierdzajcego,


ktry uytkownik naciska w celu zakoczenia operacji. Istnieje te metoda
o nazwie showDialog, przyjmujca tekst, ktry ma by wywietlony na przycisku:
int result = chooser.showDialog(parent, "Zaznacz");

Wywoania tego typu zwracaj warto tylko wwczas, gdy uytkownik zatwierdzi,
anuluje lub zamknie okno dialogowe. Moliwe wartoci zwrotne to:
JFileChooser.APPROVE_OPTION, JFileChooser.CANCEL_OPTION
i JFileChooser.ERROR_OPTION.
8. Wybrany plik lub pliki pobiera si za pomoc metod getSelectedFile()
lub getSelectedFiles. Zwracaj one jeden obiekt typu File lub tablic takich

obiektw. Do sprawdzenia nazwy obiektu plikowego mona uy jego metody


getPath. Na przykad:
String filename = chooser.getSelectedFile().getPath();

Wikszo powyszych czynnoci jest atwa. Najwicej problemw z oknem dialogowym


wyboru pliku mona mie przy okrelaniu podzbioru plikw, z ktrych uytkownik ma
wybiera. Zamy na przykad, e wybierane mog by obrazy typu GIF. W takiej sytuacji
w oknie wyboru powinny by wywietlane tylko pliki z rozszerzeniem .gif. Dodatkowo

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

497

powinna by dostpna jaka informacja, e wywietlone pliki nale do okrelonej kategorii,


np. Pliki GIF. Sytuacja moe by jednak bardziej skomplikowana. Jeli uytkownik moe
wybiera pliki JPEG, mona spotka dwa rne rozszerzenia .jpg lub .jpeg. Zamiast
opracowywa technik kodowania takich zoonych ogranicze, projektanci okna wyboru
pliku zdecydowali si na utworzenie bardziej eleganckiego mechanizmu. Pliki do wywietlenia s selekcjonowane za pomoc obiektu rozszerzajcego abstrakcyjn klas javax.swing.
filechooser.FileFilter. Przesyany jest kady plik, ale wywietlone zostaj tylko te, ktre
akceptuje filtr.
W chwili pisania tej ksiki dostpne byy dwie takie podklasy: domylny filtr akceptujcy
wszystkie pliki i filtr akceptujcy tylko pliki z okrelonym rozszerzeniem. Ponadto z atwoci mona napisa wasny filtr. Wystarczy zaimplementowa w nim dwie abstrakcyjne
metody nadklasy FileFilter:
public boolean accept(File f);
public String getDescription();

Pierwsza z tych metod sprawdza, czy plik powinien zosta zaakceptowany. Druga zwraca
opis typu pliku, ktry moe by wywietlony w oknie wyboru plikw.
W pakiecie java.io znajduje si niezwizany z omawian klas interfejs FileFilter, ktry udostpnia jedn metod boolean accept(File f). Jest ona
uywana w metodzie listFiles klasy File do tworzenia listy plikw znajdujcych si
w katalogu. Nie wiadomo, dlaczego projektanci biblioteki Swing nie rozszerzyli tego interfejsu moliwe, e biblioteka klas Javy staa si tak obszerna, e nawet sami
programici z Sun nie potrafi ogarn wszystkich standardowych klas i interfejsw.
W przypadku importu pakietw java.io i javax.swing.filechooser jednoczenie konieczne jest rozwizanie konfliktu nazw. Najprostsze rozwizanie polega na zaimportowaniu samej klasy javax.swing.filechooser.FileFilter zamiast wszystkich klas
tego pakietu javax.swing.filechooser.*.

Po utworzeniu obiektu filtru plikw naley go zainstalowa w obiekcie okna wyboru pliku
(ang. file chooser):
chooser.setFileFilter(new FileNameExtensionFilter("Pliki obrazw", "gif", "jpg");

W oknie wyboru plikw mona zainstalowa kilka filtrw. Su do tego ponisze instrukcje:
chooser.addChoosableFileFilter(filter1);
chooser.addChoosableFileFilter(filter2);
. . .

Uytkownik wybiera filtr z listy rozwijalnej znajdujcej si na dole okna. Domylnie filtr
All files jest zawsze dostpny. Jest to bardzo dobre rozwizanie, na wypadek gdyby uytkownik chcia wybra plik o niestandardowym rozszerzeniu. Aby usun filtr All files, naley
uy poniszej instrukcji:
chooser.setAcceptAllFileFilterUsed(false)

Okno wyboru plikw mona ozdobi specjalnymi ikonami i opisami plikw. W tym celu naley
dostarczy obiekt klasy rozszerzajcej klas FileView z pakietu javax.swing.filechooser.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

498

Java. Podstawy

W przypadku wielokrotnego uycia jednego okna wyboru plikw do adowania


i zapisywania plikw rnego rodzaju naley zastosowa ponisz instrukcj
chooser.resetChoosableFilters()

Czyci ona wszystkie stare filtry przed dodaniem nowych.

Jest to z pewnoci zaawansowana technika. W typowych sytuacjach nie ma potrzeby tworzenia widoku plikw, poniewa odpowiada za niego styl. Aby jednak specjalne typy plikw
miay rne ikony, mona utworzy wasny widok plikw. Wymaga to rozszerzenia klasy
FileView i implementacji piciu metod:
Icon getIcon(File f);
String getName(File f);
String getDescription(File f);
String getTypeDescription(File f);
Boolean isTraversable(File f);

Nastpnie do instalacji widoku plikw w oknie wyboru plikw uywamy metody setFileView.
Okno wyboru plikw wywouje zaimplementowane metody dla kadego pliku lub katalogu,
ktry chce wywietli. Jeli metoda zwrci warto null dla ikony, nazwy lub opisu, okno
wyboru plikw odwouje si do widoku domylnego w zastosowanym stylu. Zalet takiego
zachowania jest to, e programista musi si zaj tylko tymi typami plikw, ktre go interesuj.
Okno wyboru plikw podejmuje decyzj, czy otworzy katalog kliknity przez uytkownika
za pomoc metody isTraversable. Pamitajmy, e metoda ta zwraca obiekt typu Boolean,
a nie warto typu boolean (logiczn)! Wydaje si to dziwne, ale jest bardzo wygodne jeli
nie chcemy zmieni domylnego widoku, wystarczy zwrci warto null. Wtedy okno wyboru
plikw zastosuje domylny widok. Innymi sowy, ta metoda zwraca obiekt typu Boolean,
ktry umoliwia wybr jednej z trzech opcji: prawda (Boolean.TRUE), fasz (Boolean.FALSE)
i bez rnicy (null).
Poniszy przykadowy program zawiera prost klas widoku plikw. Wywietla ona okrelon ikon, kiedy jaki plik pasuje do filtru. W tym przypadku wywietlana jest ikona palety
dla wszystkich plikw obrazw.
class FileIconView extends FileView
{
public FileIconView(FileFilter aFilter, Icon anIcon)
{
filter = aFilter;
icon = anIcon;
}
public Icon getIcon(File f)
{
if (!f.isDirectory() && filter.accept(f))
return icon;
else return null;
}
private FileFilter filter;
private Icon icon;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

499

Ten widok plikw instalujemy w oknie wyboru plikw za pomoc metody setFileView:
chooser.setFileView(new FileIconView(filter,
new ImageIcon("palette.gif")));

Dziki temu obok wszystkich plikw zaakceptowanych przez filtr bdzie widoczna ikona
palety, a pozostae bd wywietlane w standardowy sposb. Oczywicie uywamy tego
samego filtru, ktry utworzylimy w oknie wyboru plikw.
Bardziej uyteczna przykadowa klasa o nazwie ExampleFileView znajduje si
w katalogu JDK demo/jfc/FileChooserDemo. Pozwala ona na wizanie ikon i opisw
z dowolnymi rozszerzeniami.

W kocu okno dialogowe mona wyposay w dodatkowe akcesorium (ang. accessory component). Na przykad rysunek 9.41 przedstawia akcesorium podgldu umieszczone obok listy
plikw. Wywietla ono miniatur wybranego pliku.
Rysunek 9.41.
Okno dialogowe
wyboru pliku
z akcesorium
podgldu

Akcesorium moe by kady komponent Swing. W tym przypadku rozszerzylimy klas


JLabel i ustawilimy jej ikon na przeskalowan kopi obrazu graficznego:
class ImagePreviewer extends JLabel
{
public ImagePreviewer(JFileChooser chooser)
{
setPreferredSize(new Dimension(100, 100));
setBorder(BorderFactory.createEtchedBorder());
}

public void loadImage(File f)


{
ImageIcon icon = new ImageIcon(f.getPath());
if(icon.getIconWidth() > getWidth())
icon = new ImageIcon(icon.getImage().getScaledInstance(
getWidth(), -1, Image.SCALE_DEFAULT));
setIcon(icon);
repaint();
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

500

Java. Podstawy
Jest tylko jedna trudno. Kiedy uytkownik kliknie inny plik, podgld powinien zosta
zaktualizowany. Okno wyboru plikw wykorzystuje mechanizm JavaBeans do powiadamiania
zainteresowanych suchaczy o zmianach swoich wasnoci. Zaznaczony plik jest wasnoci,
ktr mona monitorowa za pomoc obiektu PropertyChangeListener. Bardziej szczegowo mechanizm ten opisujemy w rozdziale 8. drugiego tomu. Poniej znajduje si kod
przechwytujcy omawiane powiadomienia:
chooser.addPropertyChangeListener(new
PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent event)
{
if (event.getPropertyName() == JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)
{
File newFile = (File) event.getNewValue()
// Aktualizacja akcesorium
. . .
}
}
});

W przykadowym programie kod znajduje si w konstruktorze ImagePreviewer.


Na listingach 9.21 9.23 zostaa przedstawiona zmodyfikowana wersja programu ImageViewer
z rozdziau 2. Dodano w nim niestandardowy widok plikw i akcesorium podgldu.
Listing 9.21. fileChooser/ImageViewerFrame.java
package fileChooser;
import
import
import
import

java.awt.event.*;
java.io.*;
javax.swing.*;
javax.swing.filechooser.*;

/**
* Ramka z menu zawierajcym opcj Otwrz i obszarem do prezentacji otwartych obrazw
*/
public class ImageViewerFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
private JLabel label;
private JFileChooser chooser;
public ImageViewerFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Pasek menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

JMenuItem openItem = new JMenuItem("Otwrz");


menu.add(openItem);
openItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
chooser.setCurrentDirectory(new File("."));
// Okno wyboru plikw
int result = chooser.showOpenDialog(ImageViewerFrame.this);
// Jeli plik obrazu zostanie zaakceptowany, ustaw go jako ikon etykiety
if (result == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
label.setIcon(new ImageIcon(name));
pack();
}
}
});
JMenuItem exitItem = new JMenuItem("Zamknij");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
// Etykieta do wywietlania obrazw
label = new JLabel();
add(label);
// Utworzenie akcesorium wyboru plikw
chooser = new JFileChooser();
// Akceptuje wszystkie pliki obrazw z rozszerzeniem .jpg, .jpeg, .gif
/*
final ExtensionFileFilter filter = new ExtensionFileFilter();
filter.addExtension("jpg");
filter.addExtension("jpeg");
filter.addExtension("gif");
filter.setDescription("Pliki obrazw");
*/
FileNameExtensionFilter filter = new FileNameExtensionFilter("Pliki obrazw",
"jpg", "jpeg", "gif");
chooser.setFileFilter(filter);
chooser.setAccessory(new ImagePreviewer(chooser));
}

chooser.setFileView(new FileIconView(filter, new ImageIcon("palette.gif")));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

501

502

Java. Podstawy

Listing 9.22. fileChooser/ImagePreviewer.java


package fileChooser;
import
import
import
import

java.awt.*;
java.beans.*;
java.io.*;
javax.swing.*;

/**
* Akcesorium wywietlajce podgld obrazw
*/
public class ImagePreviewer extends JLabel
{
/**
* Tworzy obiekt ImagePreviewer
* @param chooser okno wyboru plikw, ktrego wasno zmienia si, powoduje zmian obrazu
* w tym podgldzie
*/
public ImagePreviewer(JFileChooser chooser)
{
setPreferredSize(new Dimension(100, 100));
setBorder(BorderFactory.createEtchedBorder());
chooser.addPropertyChangeListener(new PropertyChangeListener()
{
public void propertyChange(PropertyChangeEvent event)
{
if (event.getPropertyName() ==
JFileChooser.SELECTED_FILE_CHANGED_PROPERTY)
{
// Uytkownik wybra inny plik
File f = (File) event.getNewValue();
if (f == null)
{
setIcon(null);
return;
}
// Wczytanie obrazu jako ikony
ImageIcon icon = new ImageIcon(f.getPath());
// Skalowanie obrazu, jeli jest zbyt duy na ikon
if (icon.getIconWidth() > getWidth()) icon = new
ImageIcon(icon.getImage()
.getScaledInstance(getWidth(), -1, Image.SCALE_DEFAULT));
setIcon(icon);
}
}
});
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

Listing 9.23. fileChooser/FileIconView.java


package fileChooser;
import
import
import
import

java.io.*;
javax.swing.*;
javax.swing.filechooser.*;
javax.swing.filechooser.FileFilter;

/**
* Widok plikw wywietlajcy ikon obok wszystkich plikw zaakceptowanych przez filtr
*/
public class FileIconView extends FileView
{
private FileFilter filter;
private Icon icon;
/**
* Tworzy obiekt FileIconView
* @param aFilter filtr plikw wszystkie pliki zaakceptowane przez ten filtr bd miay ikon
* @param anIcon ikona wywietlana obok wszystkich zaakceptowanych plikw
*/
public FileIconView(FileFilter aFilter, Icon anIcon)
{
filter = aFilter;
icon = anIcon;
}
public Icon getIcon(File f)
{
if (!f.isDirectory() && filter.accept(f)) return icon;
else return null;
}
}
javax.swing.JFileChooser 1.2

JFileChooser()

Tworzy okno dialogowe wyboru plikw, ktrego mona uywa w wielu ramkach.

void setCurrentDirectory(File dir)

Ustawia pocztkowy katalog wywietlany w oknie dialogowym wyboru plikw.

void setSelectedFile(File file)

void setSelectedFiles(File[] file)

Ustawia domylny plik w oknie dialogowym wyboru plikw.

void setMultiSelectionEnabled(boolean b)

Ustawia lub wycza tryb wyboru wielu plikw.

void setFileSelectionMode(int mode)

Pozwala na wybr tylko plikw (domylnie), tylko katalogw lub jednych i drugich.
Parametr mode moe mie jedn z nastpujcych wartoci

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

503

504

Java. Podstawy
JFileChooser.FILES_ONLY, JFileChooser.DIRECTORIES_ONLY
lub FileChooser.FILES_AND_DIRECTORIES.

int showOpenDialog(Component parent)

int showSaveDialog(Component parent)

int showDialog(Component parent, String approveButtonText)

Wizualizuje okno dialogowe, w ktrym przycisk zatwierdzajcy ma etykiet


Open bd Save lub w postaci acucha approveButtonText. Zwraca warto
APPROVE_OPTION, CANCEL_OPTION (jeli uytkownik klikn przycisk anulowania
lub zamkn okno) lub ERROR_OPTION (jeli wystpi bd).

File getSelectedFile()

File[] getSelectedFiles()

Pobiera plik lub pliki wybrane przez uytkownika (lub zwraca warto null,
jeli uytkownik nie wybra adnego pliku).

void setFileFilter(FileFilter filter)

Ustawia mask pliku dla okna dialogowego wyboru plikw. Wywietlone zostan
wszystkie pliki, dla ktrych filter.accept zwrci warto true. Ponadto dodaje
filtr do listy dostpnych filtrw.

void addChoosableFileFilter(FileFilter filter)

Dodaje filtr plikw do listy dostpnych filtrw.

void setAcceptAllFileFilterUsed(boolean b)

Dodaje lub wycza filtr All files w licie rozwijalnej.

void resetChoosableFileFilters()

Czyci list dostpnych filtrw plikw. Pozostaje tylko filtr All files, jeli nie zostanie
jawnie wyczony.

void setFileView(FileView view)

Ustawia widok plikw dostarczajcy informacji o plikach wywietlanych przez okno


wyboru.

void setAccessory(JComponent component)

Ustawia komponent akcesorium.


javax.swing.filechooser.FileFilter 1.2

boolean accept(File f)

Zwraca warto true, jeli plik ma by wywietlany.

String getDescription()

Zwraca opis filtru plikw, na przykad Pliki obrazw (*.gif, *.jpeg).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

505

javax.swing.filechooser.FileNameExtensionFilter 6

FileNameExtensionFilter(String description, String ... extensions)

Tworzy filtr plikw z podanymi opisami, akceptujcy wszystkie katalogi i pliki,


ktrych nazwy kocz si kropk i podanymi acuchami okrelajcymi
rozszerzenie.
javax.swing.filechooser.FileView 1.2

String getName(File f)

Zwraca nazw pliku f lub warto null. W normalnych warunkach metoda


ta zazwyczaj zwraca f.getName().

String getDescription(File f)

Zwraca moliwy do odczytu opis pliku f lub warto null. Jeli na przykad
f jest dokumentem HTML, metoda ta moe zwrci jego tytu.

String getTypeDescription(File f)

Zwraca moliwy do odczytu opis typu pliku f lub warto null. Jeli na przykad
f jest dokumentem HTML, metoda ta moe zwrci acuch Hypertext document.

Icon getIcon(File f)

Zwraca ikon pliku f lub warto null. Jeli na przykad f jest plikiem JPEG,
metoda ta moe zwrci miniatur.

Boolean isTraversable(File f)

Zwraca warto Boolean.TRUE, jeli uytkownik moe otworzy katalog f. Metoda


ta moe zwrci warto false, jeli katalog jest z zaoenia dokumentem zoonym.
Podobnie jak wszystkie metody klasy FileView, take ta metoda moe zwrci
warto null, tym samym odsyajc okno wyboru plikw do widoku domylnego.

9.7.5. Okna dialogowe wyboru kolorw


Jak przekonalimy si w poprzednim podrozdziale, wysokiej jakoci okno dialogowe wyboru
plikw jest skomplikowanym komponentem interfejsu uytkownika, ktrego zdecydowanie
lepiej nie implementowa wasnorcznie. Wiele zestaww narzdzi interfejsu uytkownika
udostpnia inne czsto spotykane rodzaje okien dialogowych: wyboru daty i godziny, wartoci walutowych, czcionek, kolorw itd. Korzyci s podwjne. Programista moe wykorzysta gotow wysokiej jakoci implementacj, zamiast opracowywa wasn, a uytkownicy
zyskuj jednolity styl aplikacji.
W Swing dostpny jest jeszcze tylko jeden dodatkowy komponent wyboru o nazwie JColor
Chooser (rysunki 9.42 do 9.44). Umoliwia on uytkownikowi wybr koloru. Podobnie jak
klasa JFileChooser, klasa JColorChooser jest komponentem, a nie oknem dialogowym. Zawiera
natomiast metody suce do tworzenia okien dialogowych z komponentem wyboru koloru.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

506

Java. Podstawy

Rysunek 9.42.
Karta Swatches
(prbki)

Rysunek 9.43.
Karta HSB

Rysunek 9.44.
Karta RGB

Ponisza instrukcja powoduje wywietlenie modalnego okna dialogowego z komponentem


wyboru koloru:
Color selectedColor = JColorChooser.showDialog(parent,title, initialColor);

Mona te wywietli niemodalne okno dialogowe z komponentem wyboru koloru. W takim


przypadku naley poda nastpujce informacje:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

komponent nadrzdny,

tytu okna dialogowego,

znacznik ustawiajcy modalno okna,

komponent wyboru koloru,

suchacze przyciskw OK i Cancel (lub null, jeli ma nie by suchaczy).

507

Poniszy fragment programu tworzy niemodalne okno dialogowe zmieniajce kolor ta


w odpowiedzi na kliknicie przycisku OK:
chooser = new JColorChooser();
dialog = JColorChooser.createDialog(
parent,
"Kolor ta",
false /* niemodalne */,
chooser,
new ActionListener()
// Suchacz przycisku OK.
{
public void actionPerformed(ActionEvent event)
{
setBackground(chooser.getColor());
}
},
null /* Brak suchacza przycisku Cancel. */);

Mona nawet zrobi to lepiej i doda natychmiastowy podgld wybranego koloru. Aby
monitorowa wybierane kolory, naley utworzy model wyboru komponentu wyboru i doda
suchacza zmian:
chooser.getSelectionModel().addChangeListener(new
ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
Procedury zwizane z chooser.getColor();
}
});

W tym przypadku nie ma adnych korzyci z przyciskw OK i Cancel dostarczanych przez


okno wyboru koloru. Komponent wyboru koloru mona doda bezporednio do niemodalnego okna dialogowego:
dialog = new JDialog(parent, false /* niemodalne */);
dialog.add(chooser);
dialog.pack();

Program przedstawiony na listingu 9.24 demonstruje wszystkie trzy wymienione typy okien
dialogowych. Kliknicie przycisku Modalne pociga za sob konieczno wyboru koloru,
zanim mona zrobi cokolwiek innego. Kliknicie przycisku Niemodalne daje niemodalne
okno dialogowe, ale zmiana koloru nastpuje dopiero po naciniciu przycisku OK. Przycisk
Bezporednie wywietla niemodalne okno dialogowe bez przyciskw. Kolor ta panelu jest
aktualizowany bezporednio po wybraniu koloru w oknie dialogowym.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

508

Java. Podstawy

Listing 9.24. colorChooser/ColorChooserPanel.java


package colorChooser;
import
import
import
import

java.awt.*;
java.awt.event.*;
javax.swing.*;
javax.swing.event.*;

/**
* Panel z przyciskami uruchamiajcymi trzy typy okien
*/
public class ColorChooserPanel extends JPanel
{
public ColorChooserPanel()
{
JButton modalButton = new JButton("Modalne");
modalButton.addActionListener(new ModalListener());
add(modalButton);
JButton modelessButton = new JButton("Niemodalne");
modelessButton.addActionListener(new ModelessListener());
add(modelessButton);
JButton immediateButton = new JButton("Bezporednie");
immediateButton.addActionListener(new ImmediateListener());
add(immediateButton);
}
/**
* Ten suchacz wywietla okno modalne.
*/
private class ModalListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
Color defaultColor = getBackground();
Color selected = JColorChooser.showDialog(ColorChooserPanel.this, "Ustaw
kolor ta",
defaultColor);
if (selected != null) setBackground(selected);
}
}
/**
* Ten suchacz wywietla okno niemodalne. Kolor ta panelu zmienia si po
* klikniciu przycisku OK.
*/
private class ModelessListener implements ActionListener
{
private JDialog dialog;
private JColorChooser chooser;
public ModelessListener()
{
chooser = new JColorChooser();
dialog = JColorChooser.createDialog(ColorChooserPanel.this, "Kolor ta",

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 9.

Komponenty Swing interfejsu uytkownika

false /* niemodalne */, chooser, new ActionListener()

// Suchacz
// przycisku OK

{
public void actionPerformed(ActionEvent event)
{
setBackground(chooser.getColor());
}
}, null /* Brak suchacza dla przycisku Cancel. */);
}
public void actionPerformed(ActionEvent event)
{
chooser.setColor(getBackground());
dialog.setVisible(true);
}
}
/**
* Ten suchacz wywietla okno niemodalne. Kolor ta panelu zmienia si bezporednio
* po wybraniu przez uytkownika koloru.
*/
private class ImmediateListener implements ActionListener
{
private JDialog dialog;
private JColorChooser chooser;
public ImmediateListener()
{
chooser = new JColorChooser();
chooser.getSelectionModel().addChangeListener(new ChangeListener()
{
public void stateChanged(ChangeEvent event)
{
setBackground(chooser.getColor());
}
});
dialog = new JDialog((Frame) null, false /* niemodalne */);
dialog.add(chooser);
dialog.pack();
}
public void actionPerformed(ActionEvent event)
{
chooser.setColor(getBackground());
dialog.setVisible(true);
}
}
}
javax.swing.JColorChooser 1.2

JColorChooser()

Tworzy komponent wyboru koloru z pocztkowym kolorem biaym.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

509

510

Java. Podstawy

Color getColor()

void setColor(Color c)

Pobiera i ustawia aktualny kolor.

static Color showDialog(Component parent, String title, Color initialColor)

Wywietla modalne okno dialogowe zawierajce komponent wyboru koloru.


Parametry:

parent

Komponent, nad ktrym ma si pojawi okno.

title

Tytu okna dialogowego.

initialColor

Pocztkowy kolor w oknie wyboru koloru.

static JDialog createDialog(Component parent, String title, boolean modal,


JcolorChooser chooser, ActionListener okListener, ActionListener
cancelListener)

Tworzy okno dialogowe zawierajce komponent wyboru koloru.


Parametry:

parent

Komponent, nad ktrym ma si pojawi okno.

title

Tytu okna dialogowego.

modal

Warto true, jeli wszystkie okna s


zablokowane do momentu zamknicia tego
okna.

chooser

Komponent wyboru koloru, ktry ma by


dodany do okna dialogowego.

okListener,
cancelListener

Suchacze przyciskw OK i Cancel.

Na tym zakoczymy opis elementw interfejsu uytkownika. Informacje zawarte w rozdziaach 7., 8. i 9. pozwalaj na tworzenie prostych GUI w Swingu. Bardziej zaawansowane komponenty Swing i techniki graficzne zostay opisane w drugim tomie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

10

Przygotowywanie apletw
i aplikacji do uytku
W tym rozdziale:

Pliki JAR

Java Web Start

Aplety

Zapisywanie ustawie aplikacji

W tej chwili powinnimy swobodnie posugiwa si wikszoci funkcji jzyka Java. Mamy
te solidne podstawy programowania interfejsw graficznych. Skoro potrafimy tworzy
aplikacje uytkowe, musimy pozna techniki przygotowywania ich do uytku na komputerze
uytkownika. W kwestii tej wybr czsto pada na aplety (ang. applet), ktre byy powodem
ogromnego zainteresowania Jav na pocztku jej istnienia. Aplet to specjalny rodzaj programu w Javie, ktry moe zosta pobrany przez przegldark z internetu i uruchomiony.
Mia on uwolni uytkownikw od problemw zwizanych z instalacj oprogramowania,
ktre byoby pobierane na dowolne urzdzenie lub komputer obsugujcy Jav i podczony
do internetu.
Aplety nie speniy pokadanych w nich oczekiwa z wielu powodw. Dlatego rozdzia ten
zaczynamy od technik pakowania aplikacji. Nastpnie przechodzimy do mechanizmu Java
Web Start, bdcego alternatyw dla dostarczania aplikacji za pomoc internetu, ktry naprawia niektre z wad apletw. Na kocu opisujemy aplety, pokazujc sytuacje, w ktrych mog
znale zastosowanie.
Piszemy take o sposobach zapisywania danych konfiguracyjnych i preferencji uytkownika
w programach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

512

Java. Podstawy

10.1. Pliki JAR


Pakowanie aplikacji ma na celu utworzenie jednego pliku do wykorzystania przez uytkownika zamiast caej struktury katalogw penych plikw klas. Do tego celu su poddawane kompresji ZIP pliki Java Archive (JAR). Mog one zawiera nie tylko pliki klas, ale
rwnie obrazy i pliki dwikowe.
W Javie dostpny jest te alternatywny algorytm kompresji o nazwie pack200, ktry
zosta zoptymalizowany pod ktem bardziej efektywnego zmniejszania rozmiarw
plikw klas w porwnaniu do zwykego algorytmu ZIP. Wedug zapewnie firmy Oracle
wspczynnik kompresji plikw klas siga a 90%. Wicej informacji na ten temat znajduje
si pod adresem http://docs.oracle.com/javase/1.5.0/docs/guide/deployment/
deployment-guide/pack200.html.

Do tworzenia plikw JAR suy narzdzie o nazwie jar (w standardowej instalacji JDK
znajduje si w katalogu jdk/bin). Najczciej stosowane polecenie tworzce plik JAR ma
nastpujc skadni:
jar cvf JARNazwaPliku Plik1 Plik2 . . .

Na przykad:
jar cvf CalculatorClasses.jar *.class icon.gif

Oglny format polecenia jar jest nastpujcy:


jar opcje Plik1 Plik2 . . .

Tabela 10.1 przedstawia wszystkie opcje narzdzia jar. S one podobne do opcji polecenia
tar w systemie Unix.
W plikach JAR mona pakowa aplikacje, komponenty programw (tak zwane beany, o ktrych mowa w rozdziale 8. drugiego tomu) i biblioteki kodu. Na przykad biblioteka wykonawcza JDK jest zawarta w bardzo duym pliku o nazwie rt.jar.

10.1.1. Manifest
Poza klasami, obrazami i innymi plikami rdowymi kady plik JAR zawiera plik manifestu,
ktry okrela specjalne wasnoci archiwum.
Wspomniany plik manifestu ma nazw MANIFEST.MF, a jego lokalizacja to specjalny podkatalog META-INF w pliku JAR. Minimalna zawarto takiego pliku nie jest zbyt interesujca:
Manifest-Version: 1.0

Zoone pliki tego typu mog zawiera znacznie wicej wpisw pogrupowanych w sekcjach. Pierwsza sekcja nosi nazw sekcji gwnej (ang. main section) i ma zastosowanie do

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

513

Tabela 10.1. Opcje narzdzia jar


Opcja

Opis

Tworzy puste archiwum i dodaje do niego pliki. Katalogi s przetwarzane rekursywnie.

Zmienia tymczasowo lokalizacj. Na przykad polecenie cvf JARFileName.jar -C classes


*.class przechodzi do katalogu classes w celu dodania klas.

Tworzy punkt startowy w manifecie (zobacz podrozdzia 10.1.2, Wykonywalne pliki JAR).

Okrela plik JAR o danej nazwie jako drugi argument wiersza polece. Jeli tego parametru
brakuje, jar wyle wynik do standardowego wyjcia (przy tworzeniu pliku JAR) lub wczyta
go ze standardowego wejcia (przy rozpakowywaniu lub tabulacji pliku JAR).

Tworzy plik indeksowy (przyspieszajcy wyszukiwanie w duych archiwach).

Dodaje manifest do pliku JAR. Manifest jest opisem zawartoci i pochodzenia pliku archiwum.
Kade archiwum ma domylny manifest, ale mona utworzy wasny, ktry uwierzytelnia
zawarto archiwum.

Blokuje tworzenie domylnego pliku manifestu.

Wywietla spis treci.

Aktualizuje istniejcy plik JAR.

Generuje obszerne dane wyjciowe.

Wypakowuje pliki. Jeli podanych zostanie kilka nazw plikw, zostan wypakowane tylko one.
W przeciwnym przypadku program wypakuje wszystkie pliki.

Wycza kompresj ZIP.

caego pliku JAR. Kolejne sekcje okrelaj wasnoci rnych elementw majcych nazwy,
jak konkretne pliki, pakiety czy adresy URL. Kada z nich musi si zaczyna od sowa Name.
Sekcje s rozdzielane pust lini. Na przykad:
Manifest-Version: 1.0
opis caego archiwum
Name: Woozle.class
opis jednego pliku
Name: com/mycompany/mypkg/
opis pakietu

Aby zmieni zawarto pliku manifestu, naley dokona niezbdnych zmian i wyda ponisze polecenie:
jar cfm NazwaPlikuJAR NazwaPlikuManifest . . .

Na przykad ponisze polecenie tworzy nowy plik JAR z plikiem manifestu:


jar cfm MyArchive.jar manifest.mf com/mycompany/mypkg/*.class

Aby zaktualizowa plik manifestu istniejcego pliku JAR, naley umieci w pliku tekstowym wpisy, ktre maj by dodane, i wyda nastpujce polecenie:
jar ufm MyArchive.jar manifest-additions.mf

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

514

Java. Podstawy

Wicej informacji na temat plikw JAR i manifestu mona znale na stronie


http://docs.oracle.com/javase/7/docs/technotes/guides/jar.

10.1.2. Wykonywalne pliki JAR


Istnieje moliwo okrelenia punktu startowego programu, czyli klasy, od ktrej zaczyna
si dziaanie programu, za pomoc opcji e narzdzia jar:
jar cvfe MyProgram.jar com.mycompany.mypkg.MainAppClass pliki do dodania

Klas gwn programu mona te okreli w manifecie. W tym celu naley do niego doda
instrukcj o nastpujcej postaci:
Main-Class: com.mycompany.mypkg.MainAppClass

Nie dodawaj rozszerzenia .class do nazwy klasy gwnej.


Na kocu ostatniego wiersza w pliku manifestu musi si znajdowa znak nowego
wiersza. W przeciwnym przypadku plik zostanie odczytany nieprawidowo. Bd polegajcy na utworzeniu pliku tekstowego zawierajcego tylko wiersz Main-Class bez znaku
koca wiersza jest czsto spotykany.

W obu wymienionych przypadkach program mona uruchomi przy uyciu nastpujcego


polecenia:
java -jar MyProgram.jar

W zalenoci od konfiguracji systemu operacyjnego moe by moliwe uruchomienie aplikacji za pomoc dwukrotnego kliknicia pliku JAR. Poniej znajduje si opis zachowania
rnych systemw w takiej sytuacji:

W systemie Windows instalator aplikacji Java tworzy dowizanie dla plikw


o rozszerzeniu .jar, ktre uruchamia te pliki za pomoc polecenia javaw -jar
(polecenie javaw, w przeciwiestwie do java, nie otwiera okna wiersza polece).

System Solaris rozpoznaje magiczn liczb pliku JAR i uruchamia j za pomoc


polecenia java -jar.

System Mac OS X rozpoznaje rozszerzenie .jar i uruchamia programy w Javie


w wyniku dwukrotnego kliknicia pliku JAR.

Jednak programy Javy w plikach JAR to nie to samo co aplikacje rodzime. W systemie
Windows mona skorzysta z narzdzi innych producentw sucych do zamieniania plikw
JAR na pliki wykonywalne tego systemu. Plik JAR jest opakowywany w plik o rozszerzeniu
.exe, ktry lokalizuje i uruchamia maszyn wirtualn Javy (JVM) lub informuje uytkownika,
co powinien zrobi, jeli JVM nie ma. Istnieje kilka komercyjnych i darmowych narzdzi tego
typu, np.: JSmooth (http://jsmooth.sourceforge.net) i Launch4J (http://launch4j.sourceforge.net).
Generator instalatorw IzPack (http://izpack.org) zawiera take rodzimy program uruchamiajcy. Wicej informacji na ten temat mona znale pod adresem http://www.javalobby.
org/articles/java2exe.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

515

W komputerach z systemem Mac OS X sytuacja wyglda nieco lepiej. W skad rodowiska


programistycznego XCode wchodzi narzdzie o nazwie Jar Bundler suce do zamieniania
plikw JAR na aplikacje Mac.

10.1.3. Zasoby
Klasy uywane zarwno w apletach, jak i aplikacjach czsto wykorzystuj pliki danych tego
samego typu:

pliki obrazw i zawierajce dwik;

pliki tekstowe zawierajce acuchy komunikatw i etykiety przyciskw;

pliki z danymi binarnymi, na przykad opisujcymi rozkad mapy.

W Javie takie pliki nazywane s zasobami (ang. resources).


W systemie Windows termin zasb (ang. resource) ma wsze znaczenie. Zasoby
w tym systemie take mog by plikami obrazw, etykietami przyciskw itd., ale
s zwizane z plikami wykonywalnymi z dostpem za porednictwem standardowego
interfejsu programistycznego. Natomiast pliki zasobw Javy s przechowywane osobno,
a nie jako czci plikw klas. Dostp do zasobw i ich interpretacja zaley od programu.

Wemy na przykad klas o nazwie AboutPanel, ktra wywietla komunikat widoczny na


rysunku 10.1.
Rysunek 10.1.
Wywietlanie
zasobu
z pliku JAR

Wiadomo, e tytu i rok wydania zostan zmienione w kolejnym wydaniu ksiki. Aby
uatwi zmian, ten tekst naley umieci w pliku tekstowym, a nie bezporednio w kodzie
programu.
Powstaje jednak pytanie, gdzie umieci taki plik jak about.txt. Oczywicie najlepiej byoby,
aby znajdowa si on razem z pozostaymi plikami programu w pliku JAR.
Program adujcy klasy potrafi znale pliki klas, jeli znajduj si gdzie na ciece klas,
w archiwum lub na serwerze sieciowym. Mechanizm zasobw oferuje podobn funkcjonalno
dla plikw, ktre nie s klasami. Poniej znajduje si spis wymaganych czynnoci:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

516

Java. Podstawy
1.

Utwrz obiekt Class klasy, ktra ma zasb, na przykad AboutPanel.class.

2. Jeli zasobem jest obraz lub plik audio, wywoaj metod getResource(filename)

w celu uzyskania lokalizacji zasobu w postaci adresu URL. Nastpnie odczytaj


go za pomoc metody getImage lub getAudioClip.
3. W przypadku innych zasobw ni obrazy i pliki audio dane z pliku naley wczytywa
za pomoc metody getResourceAsStream.

Chodzi o to, aby program adujcy klasy potrafi znale klas i odszuka zwizane z ni
zasoby w tej samej lokalizacji.
Na przykad poniszy fragment kodu tworzy ikon z pliku about.gif:
URL url = ResourceTest.class.getResource("about.gif");
Image img = new ImageIcon(url).getImage();

Powyszy kod mona odczyta nastpujco: znajd plik about.gif w tej samej lokalizacji,
w ktrej znajduje si klasa ResourceTest.
Ponisze instrukcje wczytuj plik about.txt:
InputStream stream = ResourceTest.class.getResourceAsStream("about.txt");
Scanner in = new Scanner(stream);

Plik zasobu nie musi si znajdowa w tym samym katalogu co klasa moe by w jakim
podkatalogu. Mona zastosowa hierarchiczn nazw zasobu, jak ponisza:
data/text/about.txt

Jest to wzgldna nazwa zasobu. Jest ona interpretowana wzgldem pakietu klasy, ktra aduje
dany zasb. Naley pamita, e zawsze trzeba uywa separatora /, bez wzgldu na separator katalogw stosowany w systemie, w ktrym s przechowywane pliki zasobw. Na przykad w systemie plikw systemu Windows separatory / s automatycznie zamieniane na \.
Nazwa zasobu zaczynajca si od znaku / jest bezwzgldn nazw zasobu. Jest ona lokalizowana w taki sam sposb jak klasa wewntrz pakietu. Na przykad zasb:
/corejava/title.txt

znajduje si w katalogu corejava (ktry moe by podkatalogiem cieki klas wewntrz


pliku JAR lub, w przypadku apletw, na serwerze sieciowym).
Jedynym przeznaczeniem funkcji adowania zasobw jest adowanie plikw. Nie istniej
adne standardowe metody interpretujce zawarto pliku zasobw. Kady program musi
interpretowa zawarto swoich plikw zasobw na swj wasny sposb.
Innym czsto spotykanym zastosowaniem zasobw jest midzynarodowa lokalizacja programw. W plikach zasobw przechowuje si acuchy, ktre zmieniaj si w zalenoci
od jzyka, czyli komunikaty i etykiety interfejsu uytkownika. Dla kadego jzyka tworzony
jest osobny plik. API internacjonalizacji, ktre zostao opisane w rozdziale 5. drugiego tomu,
udostpnia standardow metod suc do organizacji i dostpu do plikw lokalizacyjnych.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

517

Listing 10.1 przedstawia kod programu demonstrujcego adowanie zasobw. Ponisze polecenia kompiluj go, tworz plik JAR i uruchamiaj go:
javac ResourceTest.java
jar cvfm ResourceTest.jar ResourceTest.mf *.class *.gif *.txt
java -jar ResourceTest.jar

Aby przekona si, e program pobiera pliki zasobw z archiwum JAR, a nie biecego
katalogu, mona przenie ten program do innego folderu.
Listing 10.1. resource/ResourceTest.java
package resource;
import
import
import
import
import

java.awt.*;
java.io.*;
java.net.*;
java.util.*;
javax.swing.*;

/**
* @version 1.4 2007-04-30
* @author Cay Horstmann
*/
public class ResourceTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new ResourceTestFrame();
frame.setTitle("ResourceTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka adujca zasoby graficzne i tekstowe
*/
class ResourceTestFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 300;
public ResourceTestFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
URL aboutURL = getClass().getResource("about.gif");
Image img = new ImageIcon(aboutURL).getImage();
setIconImage(img);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

518

Java. Podstawy
JTextArea textArea = new JTextArea();
InputStream stream = getClass().getResourceAsStream("about.txt");
Scanner in = new Scanner(stream);
while (in.hasNext())
textArea.append(in.nextLine() + "\n");
add(textArea);
}
}
java.lang.Class 1.0

URL getResource(String name) 1.1

InputStream getResourceAsStream(String name) 1.1

Znajduje zasb w tym samym katalogu, w ktrym jest umieszczona klasa, i zwraca
adres URL lub strumie wejciowy, za pomoc ktrego mona ten zasb zaadowa.
Zwraca warto null, jeli zasb nie istnieje, dziki czemu nie powoduje wyjtku
dla bdu wejcia-wyjcia.

10.1.4. Piecztowanie pakietw


W rozdziale 4. wspomnielimy o moliwoci piecztowania (ang. seal) pakietw Javy w celu
uniemoliwienia dodawania do nich kolejnych klas. Moe by to konieczne w przypadku
uywania klas, metod i pl o zasigu pakietowym. Gdyby nie piecztowanie, inne klasy
mogyby by umieszczane w tym samym pakiecie i dziki temu uzyskiwa dostp do elementw pakietowych.
Jeli na przykad pakiet com.mycompany.util zostanie zapiecztowany, adna klasa spoza tego
zapiecztowanego archiwum nie moe by zdefiniowana za pomoc poniszej instrukcji:
package com.mycompany.util;

W tym celu wszystkie klasy pakietu naley umieci w pliku JAR. Domylnie pakiety
w pliku JAR nie s zapiecztowane. Mona zmieni to domylne globalne ustawienie, wstawiajc wiersz
Sealed: true

w gwnej sekcji pliku manifestu. Aby zapiecztowa tylko wybrane pakiety, naley do pliku
manifestu w pliku JAR wstawi dodatkowe sekcje:
Name: com/mycompany/util/
Sealed: true
Name: com/mycompany/misc/
Sealed: false

Aby zapiecztowa pakiet, naley utworzy plik tekstowy z instrukcjami manifestu. Nastpnie naley uruchomi narzdzie jar w zwyky sposb:
jar cvfm MyArchive.jar manifest.mf pliki do dodania

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

519

10.2. Java Web Start


Java Web Start jest technologi uruchamiania aplikacji bezporednio z internetu. Aplikacje
Java Web Start maj nastpujce cechy:

S zazwyczaj dostarczane za porednictwem przegldarki. Po pobraniu aplikacja


Java Web Start moe by uruchamiana bez uycia przegldarki.

Nie rezyduj w oknie przegldarki. Aplikacja dziaa we wasnym oknie ramowym,


poza przegldark.

Nie wykorzystuj implementacji Javy przegldarki. Przegldarka uruchamia tylko


zewntrzn aplikacj, podobnie jak w przypadku innych programw, takich jak
Adobe Acrobat lub Real Audio.

Aplikacjom podpisanym cyfrowo mona nadawa dowolne prawa dostpu.


Niepodpisane aplikacje dziaaj w piaskownicy (ang. sandbox), ktra nie zezwala
na potencjalnie niebezpieczne operacje.

Przygotowywanie aplikacji do dostarczania za porednictwem mechanizmu Java Web Start


polega na spakowaniu jej do jednego lub wikszej liczby plikw JAR. Nastpnie naley utworzy plik deskryptora w formacie JNLP (ang. Java Network Launch Protocol). Pliki umieszczamy na serwerze.
Dodatkowo serwer sieciowy musi raportowa typ MIME application/x-java-jnlp-file dla
plikw z rozszerzeniem .jnlp (typ MIME umoliwia przegldarkom podjcie decyzji, ktry
program pomocniczy uruchomi). Szczegowych informacji na ten temat naley szuka
w dokumentacji serwera.
Aby wyprbowa mechanizm Java Web Start, zainstaluj Tomcat dostpny na stronie
http://tomcat.apache.org/. Jest to kontener na serwlety i strony JSP, ale serwuje
te strony internetowe. Jest wstpnie skonfigurowany do serwowania poprawnego typu
MIME dla plikw JNLP.

Wyprbujemy mechanizm Java Web Start na kalkulatorze z rozdziau 9. Wykonaj nastpujce czynnoci:
1.

Skompiluj program.
javac -classpath .:/path/to/javaws.jar webstart/*.java

2. Utwrz plik JAR za pomoc poniszego polecenia:


jar cvfe Calculator.jar webstart.Calculator webstart/*.class

3. Przygotuj plik rozruchowy Calculator.jnlp o nastpujcej treci:


<?xml version="1.0" encoding="utf-8"?>
<jnlp spec="1.0+" codebase="http://localhost:8080/calculator/"
href="Calculator.jnlp">
<information>
<title>Przykadowy kalkulator</title>
<vendor>Cay S. Horstmann</vendor>

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

520

Java. Podstawy
<description>Kalkulator</description>
<offline-allowed/>
</information>
<resources>
<java version="1.6.0+"/>
<jar href="Calculator.jar"/>
</resources>
<application-desc/>
</jnlp>

(Naley pamita, e numer wersji to 1.6.0, a nie 6.0.)


Format pliku JNLP jest bardzo prosty. Jego pena specyfikacja znajduje si na stronie
http://www.oracle.com/technetwork/java/javase/javawebstart/index.html.
4. W przypadku wykorzystania serwera Tomcat naley utworzy katalog tomcat/

webapps/calculator, gdzie tomcat to katalog gwny instalacji Tomcata. Utwrz


podkatalog tomcat/webapps/calculator/WEB-INF i umie w nim poniszy plik
web.xml:
<?xml version="1.0" encoding="utf-8"?>
<web-app version="2.5" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd">
</web-app>

5. Umie pliki JAR i JNLP na serwerze sieciowym, aby adres URL zgadza
si z wpisem codebase w pliku JNLP. W przypadku serwera Tomcat pliki naley

umieci w katalogu tomcat/webapps/calculator.


6. Upewnij si, e masz przegldark skonfigurowan pod ktem Java Web Start,
sprawdzajc, czy typ MIME application/x-java-jnlp-file jest skojarzony

z aplikacj javaws. Jeli zainstalowano pakiet JDK, konfiguracja ta powinna


zosta wykonana automatycznie.
7. Uruchom serwer Tomcat.
8. Wpisz w przegldarce adres pliku JNLP. Na przykad w przypadku uycia serwera

Tomcat naley wpisa adres http://localhost:8080/calculator/Calculator.jnlp.


9. Powinno si pojawi okno uruchamiania Java Web Start (rysunek 10.2).
Rysunek 10.2.
Uruchamianie
aplikacji Java
Web Start

10. Chwil pniej powinien si pojawi kalkulator z informacj, e jest to aplikacja

Javy (rysunek 10.3).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

521

Rysunek 10.3.
Kalkulator otwarty
za porednictwem
Java Web Start

11. Kiedy nastpnym razem sprbujemy uzyska dostp do pliku JNLP, program bdzie

pobierany z pamici podrcznej. Zawarto tej pamici mona obejrze za pomoc


panelu kontrolnego w postaci wtyczki Javy (rysunek 10.4). W systemie Windows
wtyczki tej naley szuka w Panelu sterowania. W systemie Linux naley wykona
polecenie jdk/jre/bin/ControlPanel.
Aby nie uruchamia serwera podczas testowania konfiguracji JNLP, mona tymczasowo nadpisa adres URL codebase w pliku JNLP za pomoc poniszego polecenia:
javaws -codebase file://katalogProgramu plikJNL

Na przykad w systemie Unix polecenie to mona wyda w katalogu zawierajcym


plik JNLP:
javaws -codebase file://`pwd` Calculator.jnlp

Rysunek 10.4.
Aplikacje
w pamici
podrcznej

Oczywicie nie chcemy nakania uytkownikw do uruchamiania przegldarki pamici


podrcznej za kadym razem, kiedy chc uruchomi nasz aplikacj. Mona sprawi, aby
instalator proponowa utworzenie skrtw w menu Start i na pulpicie. W tym celu naley
doda poniszy kod do pliku JNLP:
<shortcut>
<desktop/>
<menu submenu="Akcesoria"/>
</shortcut>

Podczas pierwszego pobierania aplikacji zostanie wywietlone ostrzeenie o dodawaniu skrtw


(rysunek 10.5).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

522

Java. Podstawy

Rysunek 10.5.
Ostrzeenie
o integracji

Dodatkowo powinno si doda ikon dla skrtu i ekranu uruchomieniowego. Firma Sun zaleca
stosowanie ikon o rozmiarach 3232 i 6464 piksele. Pliki ikon naley umieci na serwerze
razem z plikami JAR i JNLP. Poniszy kod naley doda do sekcji information pliku JNLP:
<icon href="calc_icon32.png" width="32" height="32" />
<icon href="calc_icon64.png" width="64" height="64" />

Naley pamita, e ikony te nie s zwizane z ikon aplikacji. Aby wstawi ikon dla aplikacji, naley doda odrbny plik ikony do pliku JAR i wywoa metod IconImage na rzecz
klasy ramowej (zobacz listing 10.1).

10.2.1. Piaskownica
Kiedy kod uruchamiany na komputerze jest pobierany ze zdalnego miejsca, spraw pierwszorzdn staje si zawsze bezpieczestwo. Aplikacj Java Web Start moe uruchomi jedno
kliknicie. Wejcie na stron powoduje automatyczne uruchomienie wszystkich znajdujcych
si na niej apletw. Gdyby kliknicie odnonika lub wejcie na stron internetow pozwalao
na uruchomienie dowolnego kodu na komputerze uytkownika, przestpcy przeywaliby zoty
wiek wykradania poufnych informacji, danych finansowych i przejmowania komputerw uytkownikw w celu rozsyania spamu.
Technologia Java dysponuje zaawansowanym modelem ochrony, ktry zapobiega wykorzystywaniu jej do nikczemnych postpkw. Model ten zosta szczegowo opisany w tomie
drugim. Meneder zabezpiecze (ang. security manager) kontroluje dostp do wszystkich
zasobw systemowych. Przy standardowych ustawieniach zezwala tylko na nieszkodliwe
operacje. Aby zezwoli na dodatkowe operacje, kod musi by podpisany cyfrowo, a uytkownik musi zatwierdzi podpis certyfikatu.
Co pobierany zdalnie kod moe robi na wszystkich platformach? Zawsze mona wywietla
obrazy, odtwarza dwiki, odpowiada na nacinicia przez uytkownika klawiszy i przyciskw myszki oraz wysya dane wprowadzone przez uytkownika do hosta, z ktrego zosta
zaadowany kod. Taka funkcjonalno wystarcza do zaprezentowania faktw i liczb oraz
pobrania danych od uytkownika skadajcego zamwienie. Ograniczone rodowisko wykonawcze jest czsto nazywane piaskownic (ang. sandbox). Kod dziaajcy w piaskownicy
nie moe nic zmienia w systemie uytkownika ani go szpiegowa.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

523

Programy dziaajce w piaskownicy maj nastpujce ograniczenia:

Nie mog uruchamia adnych lokalnych programw.

Nie mog czyta ani zapisywa plikw w lokalnym systemie plikw.

Nie maj dostpu do adnych informacji o komputerze lokalnym, z wyjtkiem


wersji Javy i kilku mao wanych danych na temat systemu operacyjnego. Kod
dziaajcy w piaskownicy w szczeglnoci nie ma dostpu do nazwy uytkownika,
adresw e-mail itd.

Programy adowane z serwera zdalnego nie mog si komunikowa z adnym


hostem poza tym, z ktrego pochodz serwer taki nosi nazw serwera
pochodzenia (ang. originating host). Dziki temu uytkownik jest chroniony
przed programami, ktre mog wykrada wewntrzne zasoby.

Wszystkie wyskakujce okna zawieraj komunikat ostrzegawczy. Ma on na celu


zabezpieczenie przed pomyleniem ich z oknem aplikacji lokalnej. Istniej obawy,
e nic niepodejrzewajcy uytkownik wejdzie na stron internetow, podstpem
zostanie zmuszony do uruchomienia zdalnego kodu, a nastpnie wpisze haso
lub numer karty kredytowej, ktre zostan przesane z powrotem do serwera.
We wczesnych wersjach JDK komunikat ten brzmia bardzo zowieszczo: Untrusted
Java Applet Window. W kadej kolejnej wersji brzmienie to byo nieco agodzone:
Unauthenticated Java Applet Window czy Warning: Java Applet Window. Obecnie
jest to Java Applet Window lub Java Application Window.

10.2.2. Podpisywanie kodu


Ograniczenia piaskownicy s czsto zbyt rygorystyczne. Na przykad w wewntrznej sieci
firmowej nietrudno spotka aplikacj lub aplet Web Start wymagajcy dostpu do lokalnych
plikw. Istnieje moliwo bardzo szczegowego okrelenia, jakie prawa dostpu ma kada
aplikacja. Technik t opisujemy w rozdziale 9. drugiego tomu. Oczywicie aplikacja Web
Start moe po prostu zada wszystkich praw, ktre ma aplikacja lokalna. Grupa takich
aplikacji jest cakiem pokana. Aby nada wszystkie prawa, naley umieci poniszy fragment kodu w pliku JNLP:
<security>
<all-permissions/>
</security>

Aby mc dziaa poza piaskownic, pliki JAR aplikacji Java Web Start musz by podpisane cyfrowo. Podpisany plik JAR ma certyfikat okrelajcy tosamo tego, kto go podpisa.
Techniki kryptograficzne daj pewno, e certyfikat taki nie jest sfaszowany, a wszelkie
prby zmiany jego zawartoci s natychmiast wykrywane.
Wyobramy sobie, e pobieramy aplikacj utworzon i podpisan cyfrowo przez firm yWorks
GmbH z certyfikatem wydanym przez orodek certyfikacji Thawte (rysunek 10.6). Odbierajc aplikacj, mamy pewno, e:
1.

Kod aplikacji nie zosta zmieniony w aden sposb od chwili jej podpisania.

2. Podpis rzeczywicie pochodzi od firmy yWorks.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

524

Java. Podstawy

Rysunek 10.6.
Certyfikat
bezpieczestwa

3. Certyfikat naprawd zosta wydany przez orodek Thawte (mechanizm Java Web

Start potrafi sprawdza certyfikaty wydane przez Thawte i kilka innych instytucji).
Niestety nic wicej nie wiemy. Nie wiemy, czy kod jest na pewno bezpieczny. W rzeczywistoci, jeli klikniemy odnonik More Information, dowiemy si, e aplikacja zostanie uruchomiona bez zwyczajowych ogranicze. To, czy zainstalowa t aplikacj, czy nie, w duej mierze
zaley od tego, czy ufamy firmie yWorks.
Koszty uzyskania certyfikatu od jednego z obsugiwanych dostawcw wynosz kilkaset
dolarw rocznie. Wielu deweloperw generuje wasne certyfikaty i nimi podpisuje kod. Oczywicie Java Web Start nie ma moliwoci sprawdzenia jakoci tych certyfikatw. Odbierajc
tak aplikacj, wiadomo, e:
1.

Kod wyglda dokadnie tak samo jak wtedy, kiedy zosta podpisany.
Nikt niepowoany przy nim nie majstrowa.

2. Kto podpisa kod, ale Java Web Start nie moe sprawdzi kto.

W zwizku z tym mechanizm ten jest kompletnie bezuyteczny. Kady mg zmodyfikowa kod, a nastpnie go podpisa, twierdzc, e jest jego autorem. Niemniej Java Web Start
z najwiksz przyjemnoci wywietli certyfikat do zatwierdzenia (zobacz rysunek 10.7).
Teoretycznie certyfikat mona zweryfikowa w jeszcze inny sposb, ale niewielu uytkownikw ma wystarczajce umiejtnoci.
Oczywicie kadego dnia mnstwo ludzi pobiera z internetu i uruchamia aplikacje. Jeli
stwierdzisz, e uytkownicy ufaj Twojej aplikacji i infrastrukturze sieciowej, uywaj osobicie
podpisywanego certyfikatu (zobacz http://docs.oracle.com/javase/6/docs/technotes/guides/
javaws/developersguide/development.html). W przeciwnym przypadku naley zapewni uyt-

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

525

Rysunek 10.7.
Niebezpieczny
certyfikat

kownikom poczucie bezpieczestwa i pozosta w piaskownicy. Dziki API JNLP (o ktrym


mowa w kolejnym podrozdziale) po uzyskaniu zgody uytkownika mona da programowi
dostp tylko do wybranych zasobw.

10.2.3. API JNLP


API JNLP umoliwia aplikacjom dziaajcym w piaskownicy uzyskiwa dostp do zasobw lokalnych przy zachowaniu odpowiedniego poziomu bezpieczestwa. Istniej na przykad
usugi polegajce na wysyaniu i zapisywaniu plikw. Aplikacja nie ma dostpu do systemu
plikw i nie moe okrela nazw plikw. W zamian wywietlane jest okno dialogowe wyboru
plikw, w ktrym uytkownik programu wybiera plik. Przed wywietleniem tego okna pojawia
si ostrzeenie i uytkownik musi wyrazi zgod na kontynuowanie (rysunek 10.8). Ponadto
opisywane API nie daje w rzeczywistoci programowi dostpu do obiektu typu File.
Rysunek 10.8.
Ostrzeenie
Java Web Start

W szczeglnoci aplikacja nie moe sprawdzi lokalizacji pliku. W ten sposb programista
zyskuje narzdzia do implementacji akcji otwierania i zapisywania plikw, ale tak duo informacji systemowych, jak to tylko moliwe, jest ukrytych przed niezaufanymi aplikacjami.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

526

Java. Podstawy
To API udostpnia nastpujce usugi:

wysyanie i zapisywanie plikw,

dostp do schowka,

drukowanie,

pobieranie plikw,

wywietlanie dokumentw w domylnej przegldarce,

zapisywanie i odczytywanie staych danych konfiguracyjnych,

pilnowanie, aby by uruchomiony tylko jeden egzemplarz programu (od Java SE 5.0).

Dostp do usugi uzyskuje si za pomoc metody ServiceManager:


FileSaveService service = (FileSaveService)
ServiceManager.lookup("javax.jnlp.FileSaveService");

Powysza instrukcja spowoduje wyjtek UnavailableServiceException, jeli usuga jest niedostpna.


Aby mc kompilowa programy wykorzystujce API JNLP, naley umieci plik
javaws.jar na ciece klas. Plik ten znajduje si w podkatalogu jre/lib w katalogu JDK.

Omwimy najbardziej przydatne usugi JNLP. Aby zapisa plik, trzeba poda ciek startow
i rozszerzenia plikw wywietlanych w oknie dialogowym, dane do zapisania oraz sugerowan
nazw pliku. Na przykad:
service.saveFileDialog(".", new String[] { "txt" }, data, "calc.txt");

Dane musz by dostarczone w strumieniu InputStream, co nie zawsze jest atwe. W programie z listingu 10.2 przyjto nastpujc strategi dziaania:
1.

Utworzenie strumienia ByteArrayOutputStream przechowujcego bajty, ktre maj


by zapisane.

2. Utworzenie strumienia PrintStream wysyajcego swoje dane do strumienia


ByteArrayOutputStream.
3. Wydrukowanie informacji, ktre maj by zapisane, do strumienia PrintStream.
4. Utworzenie strumienia ByteArrayInputStream odczytujcego zapisane bajty.
5. Przekazanie strumienia do metody saveFileDialog.

Wicej na temat strumieni piszemy w rozdziale 1. drugiego tomu. Teraz wystarczy tylko
pobienie przejrze przykadowy program.
Do odczytu danych z pliku suy klasa FileOpenService. Jej metoda openFileDialog odbiera
sugerowan ciek pocztkow i rozszerzenia plikw wywietlanych w oknie dialogowym
i zwraca obiekt typu FileContents. Nastpnie mona za pomoc metod getInputStream
i getOutputStream odczyta i zapisa dane do pliku. Jeli uytkownik nie wybra pliku, metoda
openFileDialog zwraca warto null.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

527

FileOpenService service = (FileOpenService) ServiceManager.lookup("javax.jnlp.


FileOpenService");
FileContents contents = service.openFileDialog(".", new String[] { "txt" });
if (contents != null)
{
InputStream in = contents.getInputStream();
. . .
}

Pamitajmy, e aplikacja nie zna nazwy ani lokalizacji pliku. Aby otworzy okrelony plik,
mona uy klasy ExtendedService:
ExtendedService service = (ExtendedService) ServiceManager.lookup("javax.jnlp.
ExtendedService");
FileContents contents = service.openFile(new File("c:\\autoexec.bat"));
if (contents != null)
{
OutputStream out = contents.getOutputStream();
. . .
}

Uytkownik programu musi si zgodzi na dostp do tego pliku (rysunek 10.9).


Rysunek 10.9.
Ostrzeenie
o dostpie
do pliku

Do wywietlenia dokumentu w domylnej przegldarce naley uy interfejsu BasicService.


Zauwamy, e niektre systemy mog nie mie domylnej przegldarki.
BasicService service = (BasicService) ServiceManager.lookup("javax.jnlp.BasicService");
if (service.isWebBrowserSupported())
service.showDocument(url);
else . . .

Podstawowy interfejs PersistenceService pozwala aplikacji zapisywa niewielkie iloci danych


konfiguracyjnych i odczytywa je po jej ponownym uruchomieniu. Mechanizm ten przypomina nieco pliki cookie HTTP. Ten stay magazyn jako klucze wykorzystuje adresy URL.
Adres URL nie musi prowadzi do istniejcego zasobu sieciowego. S one w tym przypadku
wykorzystywane jako wygodny sposb nazewnictwa hierarchicznego. Dla kadego klucza URL
aplikacja moe zapisa dowolne dane binarne (rozmiar jednego bloku danych w magazynie
moe by ograniczony).
Odseparowanie od siebie poszczeglnych aplikacji jest moliwe dziki temu, e kady program moe uywa tylko takich kluczy, ktre zaczynaj si od okrelonego podstawowego
acucha (zgodnie z informacjami w pliku JNLP). Jeli na przykad aplikacja zostanie pobrana
ze strony http://myserver.com/apps, moe uywa tylko kluczy w formacie http://myserver.
com/apps/subkey1/subkey2/. Prby dostpu do innych kluczy zakocz si niepowodzeniem.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

528

Java. Podstawy
Aplikacja moe sprawdzi podstaw swojego klucza URL za pomoc metody getCodeBase
z klasy BasicService.
Do tworzenia kluczy suy metoda create z interfejsu PersistenceService.
URL url = new URL(codeBase, "mykey");
service.create(url, maxSize);

Aby uzyska informacje zwizane z okrelonym kluczem, naley wywoa metod get. Zwraca
ona obiekt typu FileContents, za porednictwem ktrego mona odczytywa i zapisywa dane
kluczy. Na przykad:
FileContents contents = service.get(url);
InputStream in = contents.getInputStream();
OutputStream out = contents.getOutputStream(true);

// true = nadpisz

Niestety nie ma dobrego sposobu na sprawdzenie, czy okrelony klucz ju istnieje, czy trzeba
go utworzy. Mona wywoa metod get. Jeli klucz nie istnieje, zostanie spowodowany
wyjtek FileNotFoundException.
Aplikacje Java Web Start i aplety mog drukowa, wykorzystujc normalne API drukowania. Pojawia si tylko okienko, w ktrym uytkownik jest proszony o zezwolenie na dostp do drukarki. Wicej informacji na temat API drukowania znajduje si w rozdziale 7. drugiego tomu.

Program na listingu 10.2 jest prostym rozszerzeniem wczeniej utworzonego kalkulatora.


Ma on wirtualn tam, ktra rejestruje wszystkie obliczenia. Mona zapisywa i adowa
histori oblicze. Aplikacja pozwala na ustawienie tytuu ramki, co suy demonstracji trwaego magazynu. Przy ponownym uruchomieniu aplikacja pobierze wybrany przez uytkownika
tytu z trwaego magazynu (rysunek 10.10).
Rysunek 10.10.
Aplikacja
WebStartCalculator

Listing 10.2. webstart/CalculatorFrame.java


package webstart;
import
import
import
import

java.awt.event.*;
java.beans.*;
java.io.*;
java.net.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

import javax.jnlp.*;
import javax.swing.*;
/**
* Ramka z kalkulatorem i menu do adowania oraz zapisywania historii oblicze
*/
public class CalculatorFrame extends JFrame
{
private CalculatorPanel panel;
public CalculatorFrame()
{
setTitle();
panel = new CalculatorPanel();
add(panel);
JMenu fileMenu = new JMenu("Plik");
JMenuBar menuBar = new JMenuBar();
menuBar.add(fileMenu);
setJMenuBar(menuBar);
JMenuItem openItem = fileMenu.add("Otwrz");
openItem.addActionListener(EventHandler.create(ActionListener.class, this,
"open"));
JMenuItem saveItem = fileMenu.add("Zapisz");
saveItem.addActionListener(EventHandler.create(ActionListener.class, this,
"save"));
}

pack();

/**
* Pobiera tytu z magazynu trwaego lub prosi uytkownika o podanie tytuu, jeli
* nie ma wczeniejszego wpisu.
*/
public void setTitle()
{
try
{
String title = null;
BasicService basic = (BasicService)
ServiceManager.lookup("javax.jnlp.BasicService");
URL codeBase = basic.getCodeBase();
PersistenceService service = (PersistenceService) ServiceManager
.lookup("javax.jnlp.PersistenceService");
URL key = new URL(codeBase, "title");
try
{

FileContents contents = service.get(key);


InputStream in = contents.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
title = reader.readLine();

}
catch (FileNotFoundException e)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

529

530

Java. Podstawy
title = JOptionPane.showInputDialog("Podaj tytu ramki:");
if (title == null) return;
service.create(key, 100);
FileContents contents = service.get(key);
OutputStream out = contents.getOutputStream(true);
PrintStream printOut = new PrintStream(out);
printOut.print(title);

}
setTitle(title);

}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (MalformedURLException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}

/**
* Otwiera plik historii i aktualizuje zawarto wywietlacza.
*/
public void open()
{
try
{
FileOpenService service = (FileOpenService) ServiceManager
.lookup("javax.jnlp.FileOpenService");
FileContents contents = service.openFileDialog(".", new String[] { "txt" });
JOptionPane.showMessageDialog(this, contents.getName());
if (contents != null)
{
InputStream in = contents.getInputStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String line;
while ((line = reader.readLine()) != null)
{
panel.append(line);
panel.append("\n");
}
}

}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

/**
* Zapisuje histori kalkulatora w pliku.
*/
public void save()
{
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream();
PrintStream printOut = new PrintStream(out);
printOut.print(panel.getText());
InputStream data = new ByteArrayInputStream(out.toByteArray());
FileSaveService service = (FileSaveService) ServiceManager
.lookup("javax.jnlp.FileSaveService");
service.saveFileDialog(".", new String[] { "txt" }, data, "calc.txt");
}
catch (UnavailableServiceException e)
{
JOptionPane.showMessageDialog(this, e);
}
catch (IOException e)
{
JOptionPane.showMessageDialog(this, e);
}
}

javax.jnlp.ServiceManager

static String[] getServiceNames()

Zwraca nazwy wszystkich dostpnych usug.

static Object lookup(string name)

Zwraca usug o podanej nazwie.


javax.jnlp.BasicService

URL getCodeBase()

Zwraca katalog zawierajcy kod aplikacji.

boolean isWebBrowserSupported()

Zwraca warto true, jeli rodowisko Web Start moe uruchomi przegldark.

boolean showDocument(URL url)

Podejmuje prb pokazania danego adresu URL w przegldarce. Zwraca warto


true, jeli danie koczy si powodzeniem.
javax.jnlp.FileContents

InputStream getInputStream()

Zwraca strumie wejciowy do odczytu zawartoci pliku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

531

532

Java. Podstawy

OutputStream getOutputStream(boolean overwrite)

Zwraca strumie wyjciowy do zapisu do pliku. Jeli parametr overwrite


ma warto true, aktualna tre pliku jest nadpisywana.

String getName()

Zwraca nazw pliku (nie pen ciek katalogow).

boolean canRead()

boolean canWrite()

Zwraca warto true, jeli dany plik nadaje si do odczytu lub zapisu.
javax.jnlp.FileOpenService

FileContents openFileDialog(String pathHint, String[] extensions)

FileContents[] openMultiFileDialog(String pathHint, String[] extensions)

Wywietla ostrzeenie dla uytkownika i okno wyboru pliku. Zwraca deskryptory


treci pliku lub plikw wybranych przez uytkownika bd warto null,
jeli uytkownik nie wybra adnego pliku.
javax.jnlp.FileSaveService

FileContents saveFileDialog(String pathHint, String[] extensions,


InputStream data,String nameHint)

FileContents saveAsFileDialog(String pathHint, String[] extensions,


FileContents data)

Wywietla ostrzeenie dla uytkownika i okno wyboru pliku. Zapisuje dane


i zwraca deskryptory treci pliku lub plikw wybranych przez uytkownika
bd warto null, jeli uytkownik nie wybra adnego pliku.
javax.jnlp.PersistenceService

long create(URL key, long maxsize)

Zapisuje dany klucz w pamici trwaej. Zwraca maksymalny rozmiar przyznawany


przez pami trwa.

void delete(URL key)

Usuwa dany klucz.

String[] getNames(URL url)

Zwraca wzgldne nazwy wszystkich kluczy, ktre zaczynaj si od danego


adresu URL.

FileContents get(URL key)

Tworzy deskryptor treci, za porednictwem ktrego mona modyfikowa dane


zwizane z danym kluczem. Jeli dla danego klucza nie istnieje aden wpis,
zgaszany jest wyjtek FileNotFoundException.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

533

10.3. Aplety
Aplety to programy w jzyku Java doczane do stron HTML. Strona HTML musi poinformowa przegldark, ktre aplety ma zaadowa oraz gdzie maj one by rozmieszczone. Jak
nietrudno si domyli, znacznik sucy do wstawiania apletw musi dostarcza informacje
dotyczce lokalizacji plikw klas oraz samego apletu (jego rozmiaru, lokalizacji itd.). Przegldarka pobiera pliki klas z internetu (lub katalogu na urzdzeniu uytkownika) i automatycznie
uruchamia aplet.
Na pocztku istnienia apletw jedyn przegldark, ktra je obsugiwaa, bya HotJava firmy
Sun. Oczywicie znalazo si niewiele osb, ktre byy skonne uywa oddzielnej przegldarki dla jednej dodatkowej funkcji. Aplety zyskay prawdziw popularno z chwil doczenia przez firm Netscape maszyny wirtualnej Javy do przegldarki Navigator. Niedugo
pniej to samo zrobia firma Microsoft w przegldarce Internet Explorer. Niestety Microsoft
podci skrzyda Netscape, opornie dodajc obsug starych wersji Javy w Internet Explorerze,
a w kocu cakiem tego zaniecha.
Problem ten rozwizuje narzdzi Java Plug-in. Integruje si ono z przegldarkami jako rozszerzenie, co umoliwia uruchamianie apletw przy wykorzystaniu zewntrznego rodowiska
uruchomieniowego Javy.
Aby mc uruchamia aplety opisywane w tym rozdziale, naley zainstalowa najnowsz wersj narzdzia Java Plug-in i upewni si, e przegldarka jest poczona
z wtyczk. Informacje na temat konfiguracji i pliki do pobrania mona znale na stronie http://java.com.

10.3.1. Prosty aplet


Aby tradycji stao si zado, przerobimy program NotHelloWorld na aplet. Aplet to zwyka klasa Javy rozszerzajca klas java.applet.Applet. Do implementacji apletw uyjemy
Swinga. Wszystkie nasze aplety bd rozszerzay klas JApplet, ktra jest nadklas apletw
Swing. Jak wida na rysunku 10.11, klasa JApplet jest bezporedni podklas klasy Applet.
Jeli aplet zawiera skadniki Swing, naley rozszerzy klas JApplet. Komponenty
Swing w zwykym kontenerze Applet nie s poprawnie rysowane.

Na listingu 10.3 przedstawiona jest apletowa wersja programu nie powitalnego.


Zwr uwag na due podobiestwo do wersji z rozdziau 7. Poniewa aplet dziaa w oknie
przegldarki, nie trzeba byo definiowa metody zamykajcej.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

534

Java. Podstawy

Rysunek 10.11.
Diagram
dziedziczenia
klasy Applet

Listing 10.3. applet/NotHelloWorld.java


package applet;
import java.awt.*;
import javax.swing.*;
/**
* @version 1.23 2012-05-14
* @author Cay Horstmann
*/
public class NotHelloWorld extends JApplet
{
public void init()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

535

EventQueue.invokeLater(new Runnable()
{
public void run()
{
JLabel label = new JLabel("To nie jest aplet Witaj, wiecie",
SwingConstants.CENTER);
add(label);
}
});

Uruchomienie powyszego apletu wymaga dwch czynnoci:


1.

Kompilacji plikw rdowych na pliki klas.

2. Utworzenia pliku HTML zawierajcego informacje o lokalizacji plikw klas oraz

rozmiarze apletu.
Poniej znajduje si jego zawarto:
<applet code="applet/NotHelloWorld.class" width="300" height="300">
</applet>

Dobrym pomysem jest przetestowanie apletu we wchodzcej w skad pakietu JDK przegldarce apletw (ang. applet viewer) przed otwarciem go w przegldarce internetowej. Ponisze
polecenie wiersza polece otwiera nasz aplet we wspomnianej przegldarce:
appletviewer NotHelloWorldApplet.html

Argumentem narzdzia appletviewer jest nazwa pliku HTML, nie pliku klasy. Rysunek 10.12
przedstawia nasz aplet w przegldarce apletw.
Rysunek 10.12.
Aplet
w przegldarce
apletw

Aplety mona take uruchamia w rodowisku programistycznym. W Eclipse naley


klikn polecenie Run/Run As/Java Applet.

Przegldarka apletw dobrze sprawdza si jako pierwszy etap testowania. Trzeba jednak
w kocu uruchomi aplet w przegldarce, aby sprawdzi, jak bdzie si prezentowa uytkownikowi. Przegldarka apletw pokazuje sam aplet bez otaczajcego go kodu HTML.
Jeli strona HTML zawiera kilka znacznikw applet, przegldarka otworzy kilka okien.
Aby obejrze swj aplet w przegldarce, wystarczy zaadowa w niej odpowiedni stron
HTML (rysunek 10.13). Jeli aplet nie pojawia si, naley zainstalowa narzdzie Java
Plug-in.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

536

Java. Podstawy

Rysunek 10.13.
Aplet
w przegldarce

Jeli w aplecie zostan wprowadzone jakie zmiany i zostanie on raz jeszcze poddany kompilacji, konieczne jest ponowne uruchomienie przegldarki, aby zaadowaa
nowe pliki klas. Samo odwieenie strony nie spowoduje zaadowania nowej wersji apletu.
Bywa to kopotliwe przy szukaniu bdw. Mona unikn ponownego uruchamiania przegldarki dziki uyciu konsoli Javy. Naley uruchomi t konsol i wyda polecenie x,
ktre czyci pami programu adujcego klasy. Wtedy po odwieeniu strony zostanie
zaadowana nowa wersja apletu. W systemie Windows naley otworzy panel kontrolny
Java Plug-in znajdujcy si w Panelu sterowania. W systemie Linux naley uy polecenia
jcontrol i zada wywietlenia panelu kontrolnego Javy. Konsola bdzie si pojawia
za kadym razem, kiedy adowany jest aplet.

10.3.1.1. Konwersja programw na aplety


Graficzne aplikacje w Javie mona z atwoci przekonwertowa na aplety. Cay kod dotyczcy interfejsu uytkownika pozostaje bez zmian. Oto lista niezbdnych czynnoci:
1.

Utwrz stron HTML z odpowiednim znacznikiem wstawiajcym aplet.

2. Utwrz podklas klasy JApplet.


3. Usu z aplikacji metod main. Nie twrz ramki dla aplikacji, poniewa bdzie

ona wywietlana w oknie przegldarki.


4. Przenie kod inicjujcy z konstruktora ramki do metody init apletu. Nie trzeba

jawnie konstruowa obiektu apletu przegldarka robi to automatycznie


i wywouje metod init.
5. Usu wywoanie metody setSize. W apletach za rozmiary odpowiadaj parametry
HTML width i height.
6. Usu wywoanie metody setDefaultCloseOperation. Apletu nie mona zamkn

jego dziaanie koczy si w chwili zamknicia przegldarki.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

537

7. Jeli w programie znajduje si wywoanie metody setTitle, naley je usun. Aplety

nie maj paskw tytuu (mona oczywicie nada tytu samej stronie HTML
za pomoc znacznika title).
8. Nie wywouj metody setVisible(true). Aplet jest wywietlany automatycznie.
java.applet.Applet 1.0

void init()

Jest wywoywana przy pierwszym adowaniu apletu. Metod t naley przesoni


i umieci w niej cay kod inicjujcy.

void start()

Naley przesoni t metod i umieci w niej kod, ktry ma by wykonywany


za kadym razem, gdy uytkownik odwiedza stron zawierajc ten aplet.
Do typowych dziaa naley tu reaktywacja wtku.

void stop()

Naley przesoni t metod i umieci w niej kod, ktry ma by wykonywany


za kadym razem, gdy uytkownik opuszcza stron zawierajc ten aplet.
Do typowych dziaa naley tu dezaktywacja wtku.

void destroy()

Metod t naley przedefiniowa, wstawiajc do niej kod wykonywany


w momencie zamknicia przegldarki.

void resize(int width, int height)

Wymusza zmian rozmiaru apletu. Byaby to doskonaa metoda, gdyby dziaaa


na stronach internetowych. Niestety obecnie nie dziaa w przegldarkach, poniewa
zakca ich mechanizm rozkadu elementw na stronie.

10.3.2. Znacznik applet i jego atrybuty


Znacznik applet w najprostszej postaci moe wyglda nastpujco:
<applet code="NotHelloWorld.class" width="300" height="100">

Wartoci atrybutu code jest nazwa pliku klasy, koniecznie z rozszerzeniem .class. Atrybuty
width i height okrelaj rozmiar okna apletu w pikselach. Koniec znacznika wyznacza znacznik zamykajcy </applet>. Tekst znajdujcy si pomidzy znacznikami <applet> i </applet>
jest wywietlany tylko wtedy, gdy przegldarka nie moe wywietli apletu. Atrybuty code,
width i height s wymagane. Przy braku ktregokolwiek z nich przegldarka nie moe wywietli apletu.
Wszystkie te informacje powinny si znajdowa w kodzie strony internetowej, ktrej minimalna tre moe wyglda nastpujco:
<html>
<head>
<title>NotHelloWorldApplet</title>

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

538

Java. Podstawy
</head>
<body>
<p>Poniszy wiersz tekstu jest wywietlany pod patronatem Javy:</p>
<applet code="NotHelloWorld.class" width="100" height="100">
Gdyby Twoja przegldarka obsugiwaa Jav, w tym miejscu znajdowaby si aplet.
</applet>
</body>
</html>

Znacznik applet ma nastpujce atrybuty:

width, height

Atrybuty te s wymagane i okrelaj szeroko i wysoko apletu w pikselach.


W przegldarce apletw wymiary te s traktowane jako pocztkowe, ale rozmiar
kadego okna tej przegldarki mona zmieni. W przegldarce internetowej
nie ma moliwoci zmiany rozmiaru apletu. Optymalny rozmiar apletu,
tak aby wyglda dobrze u kadego uytkownika, naley okreli metod prb
i bdw.

align

Okrela wyrwnanie apletu. Wartoci tego atrybutu s takie same jak atrybutu
align znacznika img.

vspace, hspace

Okrelaj liczb pikseli nad i pod apletem (vspace) oraz po jego obu stronach
(hspace).

code

Okrela nazw pliku klasy apletu. Nazwa ta jest traktowana wzgldem katalogu
podstawowego (patrz niej) lub aktualnej strony, jeli katalog podstawowy nie
jest okrelony.
cieka musi si zgadza z pakietem, do ktrego naley klasa. Jeli na przykad
klasa apletu naley do pakietu com.mycompany, atrybut wyglda nastpujco
code="com/mycompany/MyApplet.class". Mona te stosowa alternatywny zapis
w postaci code="com.mycompany.MyApplet.class", ale w takim przypadku nie mona
stosowa cieek bezwzgldnych. Jeli plik klasy znajduje si gdzie indziej, naley
uy atrybutu codebase.
Atrybut code okrela tylko nazw klasy apletu. Oczywicie sam aplet moe zawiera
take inne pliki klas. Kiedy przegldarka zaaduje klas zawierajc aplet, zorientuje
si, e potrzebne s dodatkowe klasy, i je rwnie zaaduje.
Wymagany jest atrybut code lub object (zobacz poniej).

codebase

Okrela adres URL, pod ktrym naley szuka plikw klas. Adres ten moe
by bezwzgldny i prowadzi nawet do innego serwera. Najczciej jednak stosuje
si adresy wzgldne do podkatalogw na serwerze. Jeli na przykad struktura plikw
i katalogw jest nastpujca:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

539

aDirectory/
MyPage.html
myApplets/
MyApplet.class

Strona MyPage.html powinna zawiera nastpujcy znacznik:


<applet code="MyApplet.class" codebase="myApplets" width="100" height="150">

archive

Okrela list plikw JAR zawierajcych klasy i inne zasoby apletu, ktre s pobierane
z serwera przed jego zaadowaniem. To znacznie przyspiesza proces adowania,
poniewa pobranie jednego pliku JAR zawierajcego kilka mniejszych innych
plikw wymaga tylko jednego dania HTTP. Pliki JAR na licie s rozdzielane
przecinkami:
<applet code="MyApplet.class"
archive="MyClasses.jar,corejava/CoreJavaClasses.jar"
width="100" height="150">

object

Okrela nazw pliku zawierajcego serializowalny obiekt apletu (serializacja


obiektu polega na zapisie jego wszystkich pl w pliku zagadnienie to opisujemy
w rozdziale 1. drugiego tomu). Przed wywietleniem apletu jego obiekt jest
poddawany deserializacji z powrotem do pierwotnego stanu. Jeli jest uywany
ten atrybut, nie jest wywoywana metoda init apletu, a metoda start.
Przed serializacj obiektu apletu naley wywoa jego metod stop. W ten sposb
mona utworzy trwa przegldark, ktra automatycznie przeadowuje aplety
i przywraca je do takiego samego stanu, w ktrym byy w chwili jej zamykania.
Jest to zaawansowana technika, rzadko uywana przez projektantw stron
internetowych.
W kadym znaczniku applet musi si znajdowa atrybut code lub object.
Na przykad:
<applet object="MyApplet.ser" width="100" height="150">

name

Twrcy skryptw wykorzystuj ten atrybut do odwoywania si do apletu w swoich


skryptach. Przegldarki Netscape i Internet Explorer zezwalaj na wywoywanie
metod apletw na stronie za porednictwem JavaScriptu.
Aby uzyska dostp do apletu z poziomu JavaScriptu, najpierw naley nada mu nazw:
<applet code="MyApplet.class" width="100" height="150" name="mine">
</applet>

Dziki temu mona si do niego odwoywa za pomoc zapisu document.applets.nazwa


apletu. Na przykad:
var myApplet = document.applets.mine;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

540

Java. Podstawy
Dziki integracji Javy i JavaScriptu w przegldarkach Netscape i Internet Explorer mona
wywoywa metody apletu:
myApplet.init();

Atrybut name ma take kluczowe znaczenie w sytuacjach, kiedy dwa aplety znajdujce si
na tej samej stronie maj si ze sob bezporednio komunikowa. Naley nada nazw kademu apletowi. acuch ten naley przekaza do metody getApplet z klasy AppletContext.
Mechanizm ten, o nazwie komunikacja midzy apletami (ang. inter-applet communication),
zosta opisany nieco dalej w tym rozdziale.
Na stronie http://www.javaworld.com/javatips/jw-javatip80.html Francis Lu wykorzystuje mechanizm komunikacji Javy z JavaScriptem do rozwizania odwiecznego
problemu zmiany rozmiaru apletu, na stae ustawionego przez atrybuty width i height.
Jest to dobry przykad integracji tych dwch jzykw.

alt

Obsug Javy w przegldarce mona wyczy. Jeli jaki przewraliwiony


administrator to zrobi, nieszczni uytkownicy w miejscu apletu zobacz tekst
zawarty w atrybucie alt.
<applet code="MyApplet.class" width="100" height="150"
alt="Wcz Jav, a zobaczysz tutaj mj aplet.">

Jeli przegldarka w ogle nie rozpoznaje apletw, czyli pochodzi z czasw


prehistorycznych, ignoruje znaczniki applet i param. W takiej sytuacji zostanie
wywietlony tekst znajdujcy si pomidzy znacznikami <applet> i </applet>.
Natomiast przegldarki obsugujce aplety nie wywietlaj tego tekstu.
Na przykad:
<applet code="MyApplet.class" width="100" height="150">
Gdyby Twoja przegldarka obsugiwaa Jav, w tym miejscu byby widoczny
mj aplet.
</applet>

10.3.3. Znacznik object


Znacznik object wchodzi w skad standardu HTML 4.0 i jest zalecany przez organizacj
W3C jako zastpnik znacznika applet. Ma on 35 atrybutw, z ktrych wikszo zwizana jest
z dynamicznym HTML-em (np. onkeydown). Atrybuty pozycjonujce, jak align i height, dziaaj dokadnie tak samo jak w znaczniku applet. Kluczowym atrybutem znacznika object
dla apletw jest classid. Okrela on lokalizacj obiektu. Oczywicie znacznik object moe
adowa rnego rodzaju obiekty, takie jak aplety Javy, komponenty ActiveX czy nawet Java
Plug-in. Typ obiektu okrela warto atrybutu codetype. Na przykad dla apletw Javy warto ta to application/java. Poniej znajduje si znacznik object adujcy aplet:
<object
codetype="application/java"
classid="java:MyApplet.class"
width="100" height="150">

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

541

Zauwamy, e za atrybutem classid moe si znajdowa atrybut codebase, ktry dziaa


dokadnie tak samo jak w znaczniku applet.

10.3.4. Parametry przekazujce informacje do apletw


Podobnie jak aplikacje wykorzystuj informacje podawane za porednictwem wiersza polece,
aplety uywaj parametrw podawanych w plikach HTML. Suy do tego znacznik param
i jego atrybuty. Wyobramy sobie, e chcemy, aby na stronie internetowej mona byo ustawi krj czcionki uywanej w aplecie. Mona do tego uy nastpujcego kodu HTML:
<applet code="FontParamApplet.class" width="200" height="200">
<param name="font" value="Helvetica"/>
</applet>

Nastpnie warto parametru pobieramy za pomoc metody getParameter z klasy Applet:


public class FontParamApplet extends JApplet
{
public void init()
{
String fontName = getParameter("font");
. . .
}
. . .
}

Metod getParameter mona wywoywa tylko w metodzie init apletu, nie w konstruktorze, poniewa w chwili jego wykonywania parametry nie s jeszcze gotowe.
Poniewa ukad wikszoci bardziej rozbudowanych apletw jest zdeterminowany przez
parametry, zalecamy zaniechanie uywania konstruktorw w apletach. Cay kod inicjujcy
mona umieci w metodzie init.

Parametry s zawsze zwracane jako acuchy. Jeli wymagana jest liczba, acuch trzeba
przekonwertowa na typ liczbowy. Su do tego standardowe metody, takie jak parseInt
z klasy Integer.
Na przykad kod HTML zawierajcy parametr size, ktry okrela rozmiar czcionki, mgby
wyglda nastpujco:
<applet code="FontParamApplet.class" width="200" height="200">
<param name="font" value="Helvetica"/>
<param name="size" value="24"/>
</applet>

Poniszy fragment kodu demonstruje sposb odczytu parametru liczbowego:


public class FontParamApplet extends JApplet
{
public void init()
{
String fontName = getParameter("font");
int fontSize = Integer.parseInt(getParameter("size"));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

542

Java. Podstawy
. . .
}
}

Przy porwnywaniu wartoci atrybutu name ze znacznika param z argumentem metody


getParameter nie jest rozpoznawana wielko liter.

Poza upewnieniem si, e parametry w kodzie pasuj, naley sprawdzi, czy parametr size
zosta ustawiony, czy nie. Suy do tego prosty test na obecno wartoci null. Na przykad:
int fontsize;
String sizeString = getParameter("size");
if (sizeString == null) fontSize = 12;
else fontSize = Integer.parseInt(sizeString);

Rysunek 10.14 przedstawia aplet rysujcy wykres supkowy, ktry w duym stopniu wykorzystuje parametry.
Rysunek 10.14.
Aplet
wywietlajcy
wykres

Aplet ten pobiera etykiety i wysokoci supkw z wartoci atrybutw znacznika param. Poniej znajduje si kod HTML strony widocznej na rysunku 10.14.
<applet code="Chart.class" width="400" height="300">
<param name="title" value="rednice planet"/>
<param name="values" value="9"/>
<param name="name.1" value="Merkury"/>
<param name="name.2" value="Wenus"/>
<param name="name.3" value="Ziemia"/>
<param name="name.4" value="Mars"/>
<param name="name.5" value="Jowisz"/>
<param name="name.6" value="Saturn"/>
<param name="name.7" value="Uran"/>
<param name="name.8" value="Neptun"/>
<param name="name.9" value="Pluton"/>
<param name="value.1" value="3100"/>
<param name="value.2" value="7500"/>
<param name="value.3" value="8000"/>

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.
<param name="value.4"
<param name="value.5"
<param name="value.6"
<param name="value.7"
<param name="value.8"
<param name="value.9"
</applet>

Przygotowywanie apletw i aplikacji do uytku

543

value="4200"/>
value="88000"/>
value="71000"/>
value="32000"/>
value="30600"/>
value="1430"/>

Mona byo utworzy w aplecie tablic acuchw i tablic liczb, ale wykorzystanie mechanizmu parametrw ma dwie zalety. Po pierwsze, na jednej stronie mona wywietli kilka kopii
tego samego apletu, pokazujcych rne wykresy trzeba zastosowa kilka znacznikw
applet z rnymi zestawami parametrw. Po drugie, mona zmienia dane przedstawione na
wykresie. Wprawdzie rednice planet jeszcze przez jaki czas si nie zmieni, ale wyobramy
sobie, e na stronie przedstawiamy tygodniowy wykres sprzeday. Stron internetow atwo
si aktualizuje, poniewa jest to czysty tekst. Edytowanie i kompilowanie plikw Javy wymaga
znacznie wicej wysiku.
Istniej nawet komercyjne komponenty JavaBean (tak zwane beany), ktre tworz o wiele
atrakcyjniejsze wykresy. Podajc parametry takiemu zakupionemu komponentowi, nie trzeba
nawet wiedzie nic na temat tworzenia wykresw.
Listing 10.4 przedstawia kod rdowy omawianego apletu rysujcego wykres. Naley zauway, e metoda init pobiera parametry, a metoda paintComponent rysuje wykres.
Listing 10.4. chart/Chart.java
package chart;
import
import
import
import

java.awt.*;
java.awt.font.*;
java.awt.geom.*;
javax.swing.*;

/**
* @version 1.33 2007-06-12
* @author Cay Horstmann
*/
public class Chart extends JApplet
{
public void init()
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
String v = getParameter("values");
if (v == null) return;
int n = Integer.parseInt(v);
double[] values = new double[n];
String[] names = new String[n];
for (int i = 0; i < n; i++)
{
values[i] = Double.parseDouble(getParameter("value." + (i + 1)));
names[i] = getParameter("name." + (i + 1));
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

544

Java. Podstawy

});

add(new ChartComponent(values, names, getParameter("title")));

/**
* Komponent rysujcy wykres supkowy.
*/
class ChartComponent extends JComponent
{
private double[] values;
private String[] names;
private String title;
/**
* Tworzy obiekt typu ChartComponent.
* @param v tablica wartoci wykresu
* @param n tablica nazw wartoci
* @param t tytu wykresu
*/
public ChartComponent(double[] v, String[] n, String t)
{
values = v;
names = n;
title = t;
}
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// Obliczanie wartoci minimalnej i maksymalnej.
if (values == null) return;
double minValue = 0;
double maxValue = 0;
for (double v : values)
{
if (minValue > v) minValue = v;
if (maxValue < v) maxValue = v;
}
if (maxValue == minValue) return;
int panelWidth = getWidth();
int panelHeight = getHeight();
Font titleFont = new Font("SansSerif", Font.BOLD, 20);
Font labelFont = new Font("SansSerif", Font.PLAIN, 10);
// Obliczanie szerokoci tytuu.
FontRenderContext context = g2.getFontRenderContext();
Rectangle2D titleBounds = titleFont.getStringBounds(title, context);
double titleWidth = titleBounds.getWidth();
double top = titleBounds.getHeight();
// Rysowanie tytuu.
double y = -titleBounds.getY();
// wysoko
double x = (panelWidth - titleWidth) / 2;
g2.setFont(titleFont);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

g2.drawString(title, (float) x, (float) y);


// Obliczanie szerokoci etykiet supkw.
LineMetrics labelMetrics = labelFont.getLineMetrics("", context);
double bottom = labelMetrics.getHeight();
y = panelHeight - labelMetrics.getDescent();
g2.setFont(labelFont);
// Obliczanie wspczynnika skali i szerokoci supkw.
double scale = (panelHeight - top - bottom) / (maxValue - minValue);
int barWidth = panelWidth / values.length;
// Rysowanie supkw.
for (int i = 0; i < values.length; i++)
{
// Uzyskanie wsprzdnych prostokta tworzcego supek.
double x1 = i * barWidth + 1;
double y1 = top;
double height = values[i] * scale;
if (values[i] >= 0) y1 += (maxValue - values[i]) * scale;
else
{
y1 += maxValue * scale;
height = -height;
}
// Wypenienie supka i rysowanie jego obrysu.
Rectangle2D rect = new Rectangle2D.Double(x1, y1, barWidth - 2, height);
g2.setPaint(Color.RED);
g2.fill(rect);
g2.setPaint(Color.BLACK);
g2.draw(rect);
// Rysowanie etykiety na rodku pod supkiem.
Rectangle2D labelBounds = labelFont.getStringBounds(names[i], context);

double labelWidth = labelBounds.getWidth();


x = x1 + (barWidth - labelWidth) / 2;
g2.drawString(names[i], (float) x, (float) y);

java.applet.Applet 1.0

public String getParameter(String name)

Pobiera warto parametru zdefiniowanego w znaczniku param na stronie


internetowej adujcej aplet. W acuchu name rozpoznawane s mae i wielkie
litery.

public String getAppletInfo()

T metod wielu programistw przedefiniowuje, aby zwracaa acuch zawierajcy


informacje o autorze, wersji i prawach autorskich do apletu. Informacje te naley
podawa poprzez przesonicie tej metody w klasie apletu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

545

546

Java. Podstawy

public String[][] getParameterInfo()

T metod mona przedefiniowa, aby zwracaa tablic opcji znacznika param,


obsugiwanych przez aplet. Kady wiersz zawiera trzy pozycje: nazw, typ i opis
parametru. Na przykad:
"fps", "1-10", "ramek na sekund"
"repeat", "boolean", "powtrzy ptl obrazu?"
"images", "url", "katalog zawierajcy obrazy"

10.3.5. Dostp do obrazw i plikw audio


W apletach mona uywa obrazw i plikw audio. W chwili pisania tego tekstu obsugiwane
byy nastpujce formaty plikw graficznych: GIF, PNG i JPEG, oraz plikw audio: AU, AIFF,
WAV i MIDI. Animowane gify s take poprawnie obsugiwane.
Lokalizacj obrazw i plikw audio okrela si za pomoc wzgldnych adresw URL. Bazowy
adres URL zazwyczaj sprawdza si za pomoc metody getDocumentBase lub getCodeBase.
Pierwsza z wymienionych metod pobiera adres URL strony HTML zawierajcej aplet, a druga
adres URL katalogu bazowego kodu.
We wczeniejszych wersjach Javy metody te byy rdem wielu nieporozumie
zobacz bd #4456393 na stronie http://bugs.sun.com/bugdatabase/index.jsp.

Bazowy adres URL i lokalizacj pliku naley przekaza do metody getImage lub getAudioClip.
Na przykad:
Image cat = getImage(getCodeBase(), "images/cat.gif");
AudioClip meow = getAudioClip(getCodeBase(), "audio/meow.au");

Wywietlania obrazw nauczylimy si w rozdziale 7. Jeli chodzi o pliki audio, wystarczy


wywoa metod play. Metod play z klasy Applet mona nawet wywoa bez uprzedniego
zaadowania pliku audio.
play(getCodeBase(), "audio/meow.au");
java.applet.Applet 1.0

URL getDocumentBase()

Pobiera adres URL strony zawierajcej aplet.

URL getCodeBase()

Pobiera adres URL katalogu bazowego z kodem, z ktrego adowany jest aplet.
Jest to albo bezwzgldny adres URL katalogu wskazywanego przez atrybut codebase,
albo adres pliku HTML, jeli atrybut codebase nie istnieje.

void play(URL url)

void play(URL url, String name)

Pierwsza wersja odtwarza plik dwikowy znajdujcy si pod podanym adresem


URL. Druga tworzy ciek wzgldn wobec podanego adresu URL z podanego
acucha. Jeli pliku audio nie ma, nic si nie dzieje.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

AudioClip getAudioClip(URL url)

AudioClip getAudioClip(URL url, String name)

547

Pierwsza wersja odtwarza plik dwikowy znajdujcy si pod podanym adresem


URL. Druga tworzy ciek wzgldn wobec podanego adresu URL z podanego
acucha. Jeli plik audio nie istnieje, metody te zwracaj warto null.

Image getImage(URL url)

Image getImage(URL url, String name)

Zwraca obiekt typu Image zawierajcy obraz wskazywany przez adres URL. Jeli
obraz nie istnieje, zwracana jest natychmiast warto null. W przeciwnym przypadku
zostaje uruchomiony osobny wtek adujcy obraz.

10.3.6. rodowisko dziaania apletu


Aplety rezyduj w przegldarce internetowej lub przegldarce apletw. Mog one wysya
do przegldarek dania dotyczce wykonania okrelonych dziaa, typu pobranie pliku audio,
wywietlenie komunikatu w pasku stanu lub otwarcie nowej strony. Przegldarka moe wykona danie lub je zignorowa. Jeli na przykad aplet dziaajcy w przegldarce apletw zgosi
danie otwarcia nowej strony, zostanie ono zignorowane.
Do komunikacji z przegldark aplet wykorzystuje metod getAppletContext. Zwraca ona
obiekt implementujcy interfejs typu AppletContext. Konkretn implementacj tego interfejsu mona traktowa jako ciek komunikacyjn pomidzy apletem a przegldark. Poza
metodami getAudioClip i getImage interfejs AppletContext zawiera take inne przydatne metody,
ktre opisujemy w kilku kolejnych podrozdziaach.

10.3.6.1. Komunikacja pomidzy apletami


Na jednej stronie internetowej moe si znajdowa kilka apletw. Jeli wszystkie one pochodz
z jednej bazy kodowej, mog si ze sob komunikowa. Jest to jednak zaawansowana technika,
ktrej nie uywa si zbyt czsto.
Jeli kady aplet w pliku HTML ma okrelon nazw w atrybucie name, za pomoc metody
getApplet z interfejsu AppletContext mona utworzy odwoanie do tego apletu. Jeli na
przykad plik HTML zawiera poniszy znacznik:
<applet code="Chart.class" width="100" height="100" name="Chart1">

odwoanie do apletu daje ponisza instrukcja:


Applet chart1 = getAppletContext().getApplet("Chart1");

Do czego mona wykorzysta takie odwoanie? Jeli klasa Chart zawiera metod przyjmujc nowe dane i ponownie rysujc wykres, metod t mona wywoa, wykonujc odpowiednie rzutowanie.
((Chart) chart1).setData(3, "Ziemia", 9000);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

548

Java. Podstawy
List wszystkich apletw znajdujcych si na stronie mona wywietli bez wzgldu na to,
czy maj one atrybut name, czy nie. Metoda getApplets zwraca obiekt typu wyliczeniowego
(wicej informacji na temat obiektw wyliczeniowych znajduje si w rozdziale 13.). Ponisza
ptla drukuje nazwy klas wszystkich apletw znajdujcych si na stronie:
Enumeration<Applet> e = getAppletContext().getApplets();
while (e.hasMoreElements())
{
Applet a = e.nextElement();
System.out.println(a.getClass().getName());
}

Aplety z rnych stron nie mog si ze sob komunikowa.

10.3.6.2. Wywietlanie elementw w przegldarce


Aplety maj dostp do dwch miejsc w oknie przegldarki: paska stanu i obszaru, na ktrym
wywietlane s strony. W obu przypadkach uywa si metod z klasy AppletContext.
Aby wywietli tekst w pasku stanu, naley uy metody showStatus. Na przykad:
showStatus("adowanie danych . . . prosz czeka");

Z naszego dowiadczenia wynika, e zastosowanie metody showStatus jest ograniczone. Przegldarka rwnie uywa paska stanu i w wikszoci przypadkw zastpuje dostarczony przez programist tekst informacj typu Applet running. W zwizku
z tym w pasku stanu mona wywietla komunikaty typu adowanie danych, ale nie
takie, ktre s dla uytkownika wane.

Aby zmusi przegldark do otwarcia nowej strony, naley uy metody showDocument, ktr
mona wywoa na kilka sposobw. Najprostsze wywoanie polega na podaniu jako argumentu
adresu URL strony, ktra ma zosta otwarta:
URL u = new URL("http://java.sun.com/index.html");
getAppletContext().showDocument(u);

Jednak takie wywoanie moe by problematyczne, poniewa nowa strona zastpuje aktualn, co powoduje zniknicie apletu. Aby wrci do apletu, konieczne jest nacinicie przycisku
Wstecz w przegldarce.
Aby nowa strona zostaa otwarta w nowym oknie, naley metodzie showDocument poda dodatkowy parametr (zobacz tabela 10.2). acuch _blank wymusza otwarcie dokumentu w nowym
oknie. Co ciekawsze, korzystajc z ramek HTML, mona podzieli okno na kilka czci o unikatowych nazwach, w ktrych bd wywietlane dokumenty dane przez aplet rwnie znajdujcy si w jednej z ramek. Przykadowy kod prezentujemy w kolejnym podrozdziale.
Przegldarka apletw nie wywietla stron internetowych. Metoda showDocument
jest przez ni ignorowana.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

549

Tabela 10.2. Metoda showDocument


Parametr okrelajcy cel

Lokalizacja

_self lub brak

Bieca ramka

_parent

Ramka nadrzdna

_top

Ramka najwyszego rzdu

_blank

Nowe okno najwyszego rzdu, bez nazwy

Dowolny inny acuch

Ramka o podanej nazwie; jeli nie ma ramki o takiej nazwie, otwierane


jest nowe okno o takiej nazwie

java.applet.Applet 1.2

public AppletContext getAppletContext()

Tworzy dostp do rodowiska przegldarki, w ktrej dziaa aplet. Przy uyciu


tych informacji mona kontrolowa wikszo przegldarek.

void showStatus(String msg)

Pokazuje podany acuch na pasku stanu przegldarki internetowej.


java.applet.AppletContext 1.0

Enumeration<Applet> getApplets()

Zwraca wyliczenie (zobacz rozdzia 13.) wszystkich apletw znajdujcych


si w tym samym rodowisku, czyli na jednej stronie internetowej.

Applet getApplet(String name)

Zwraca aplet o podanej nazwie znajdujcy si w biecym kontekcie. Zwraca


warto null, jeli aplet nie istnieje. Przeszukiwana jest tylko bieca strona
internetowa.

void showDocument(URL url)

void showDocument(URL url, String target)

Otwiera now stron internetow w przegldarce. Pierwsza wersja zastpuje star


stron now. Druga otwiera now stron w miejscu okrelonym przez parametr
target (tabela 10.2).

10.4. Zapisywanie preferencji uytkownika


Uytkownicy oczekuj, e wszystkie dokonane przez nich ustawienia zostan zapisane i zastosowane przy ponownym uruchamianiu aplikacji. Najpierw zajmiemy si prost technik opart
na zapisywaniu informacji konfiguracyjnych w plikach wasnoci, ktre byy kiedy stosowane w Javie. Nastpnie przejdziemy do opisu niezwykle funkcjonalnego API zarzdzania preferencjami.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

550

Java. Podstawy

10.4.1. Mapy wasnoci


Mapy wasnoci (ang. property map) to struktury danych przechowujce pary klucz warto,
ktre czsto znajduj zastosowanie jako przechowalnie danych konfiguracyjnych aplikacji.
Kada taka mapa ma trzy cechy:

Klucze i wartoci s acuchami.

Mona j atwo zapisa w pliku i zaadowa z niego.

Istnieje druga tabela przechowujca wartoci domylne.

Klasa odpowiedzialna za implementacj map wasnoci nosi nazw Properties.


Jak wiadomo, mapy wasnoci znajduj zastosowanie w okrelaniu opcji konfiguracyjnych
programw. Na przykad:
Properties settings = new Properties();
settings.put("width", "200");
settings.put("title", "Witaj, wiecie!");

Do zapisania takiej listy wasnoci w pliku naley uy metody store. My zapiszemy nasz
map w pliku o nazwie program.properties. Drugi argument metody store to komentarz, ktry
jest dodawany do pliku.
FileOutputStream out = new FileOutputStream("program.properties");
settings.store(out, "Ustawienia programu");

W pliku zostan zapisane nastpujce dane:


#Ustawienia programu
#Mon Apr 30 07:22:52 2007
width=200
title=Witaj, wiecie!

Do adowania plikw ustawie su nastpujce instrukcje:


FileInputStream in = new FileInputStream("program.properties");
settings.load(in);

Istnieje zwyczaj przechowywania ustawie programu w podkatalogu gwnego katalogu


uytkownika. Nazwa tego katalogu zazwyczaj zaczyna si od kropki w systemie Unix
konwencja taka oznacza, e katalog jest katalogiem systemowym ukrytym przed uytkownikiem. W naszym przykadowym programie stosujemy si do tej konwencji.
Do sprawdzenia katalogu gwnego uytkownika mona wykorzysta metod System.get
Properties, ktra tak si skada wykorzystuje obiekt typu Properties do zapisu danych
systemowych. Katalog gwny ma klucz user.home. Istnieje take metoda pozwalajca odczyta pojedynczy klucz:
String userDir = System.getProperty("user.home");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

551

Dobrze jest na wszelki wypadek dostarczy zestaw ustawie domylnych dla programu.
Klasa Properties dysponuje dwoma mechanizmami pozwalajcymi okreli ustawienia
domylne. Po pierwsze, mona utworzy acuch, ktry bdzie stosowany domylnie za
kadym razem, kiedy dany klucz nie zostanie znaleziony.
String title = settings.getProperty("title", "Domylny tytu");

Jeli w mapie wasnoci znajduje si wasno title, parametr title zostanie ustawiony na
jej acuch. W przeciwnym przypadku parametr ten przyjmie warto Domylny tytu.
Po drugie, jeli wpisywanie wartoci domylnej w kadym wywoaniu metody getProperty
okae si zbyt mudne, wszystkie ustawienia domylne mona umieci w drugorzdnej
mapie wasnoci dostarczanej nastpnie w konstruktorze mapy gwnej.
Properties defaultSettings = new Properties();
defaultSettings.put("width", "300");
defaultSettings.put("height", "200");
defaultSettings.put("title", "Domylny tytu");
. . .
Properties settings = new Properties(defaultSettings);

Mona nawet dostarczy domylne ustawienia dla ustawie domylnych. Wystarczy tylko
utworzy kolejn map wasnoci i przekaza j do konstruktora defaultSettings. Nie jest
to jednak czsto spotykane rozwizanie.
Listing 10.5 przedstawia program zapisujcy i adujcy ustawienia programu. Zapamitywane
s pooenie, rozmiar i tytu ramki. Wygld programu mona dostosowa wedug wasnego
uznania, edytujc plik o nazwie .corejava/program.properties znajdujcy si w katalogu
gwnym.
Listing 10.5. properties/PropertiesTest.java
package properties;
import
import
import
import

java.awt.EventQueue;
java.awt.event.*;
java.io.*;
java.util.Properties;

import javax.swing.*;
/**
* Program testujcy mechanizm wasnoci. Ten program zapamituje pooenie, rozmiar i tytu ramki.
* @version 1.00 2007-04-29
* @author Cay Horstmann
*/
public class PropertiesTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

552

Java. Podstawy

});

PropertiesFrame frame = new PropertiesFrame();


frame.setVisible(true);

/**
* Ramka pobierajca dane dotyczce pooenia i rozmiaru z pliku wasnoci oraz aktualizujca ten plik
* w momencie zamykania programu
*/
class PropertiesFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
private File propertiesFile;
private Properties settings;
public PropertiesFrame()
{
// Pobranie informacji o pooeniu, rozmiarze i tytule z pliku wasnoci
String userDir = System.getProperty("user.home");
File propertiesDir = new File(userDir, ".corejava");
if (!propertiesDir.exists()) propertiesDir.mkdir();
propertiesFile = new File(propertiesDir, "program.properties");
Properties defaultSettings = new Properties();
defaultSettings.put("left", "0");
defaultSettings.put("top", "0");
defaultSettings.put("width", "" + DEFAULT_WIDTH);
defaultSettings.put("height", "" + DEFAULT_HEIGHT);
defaultSettings.put("title", "");
settings = new Properties(defaultSettings);
if (propertiesFile.exists()) try
{
FileInputStream in = new FileInputStream(propertiesFile);
settings.load(in);
}
catch (IOException ex)
{
ex.printStackTrace();
}
int left = Integer.parseInt(settings.getProperty("left"));
int top = Integer.parseInt(settings.getProperty("top"));
int width = Integer.parseInt(settings.getProperty("width"));
int height = Integer.parseInt(settings.getProperty("height"));
setBounds(left, top, width, height);
// Jeli nie ma tytuu, uytkownik zostanie poproszony o jego podanie
String title = settings.getProperty("title");
if (title.equals("")) title = JOptionPane.showInputDialog("Wpisz tytu
ramki:");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

553

if (title == null) title = "";


setTitle(title);

addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent event)
{
settings.put("left", "" + getX());
settings.put("top", "" + getY());
settings.put("width", "" + getWidth());
settings.put("height", "" + getHeight());
settings.put("title", getTitle());
try
{
FileOutputStream out = new FileOutputStream(propertiesFile);
settings.store(out, "Ustawienia programu");
}
catch (IOException ex)
{
ex.printStackTrace();
}
System.exit(0);
}
});

Obiekty typu Properties s zwykymi tablicami pozbawionymi struktury hierarchicznej.


Programici czsto tworz namiastk hierarchii, odpowiednio nazywajc klucze, np.
window.main.color, window.main.title itd. Jednak klasa Properties nie zawiera adnych metod wspomagajcych organizacj takich hierarchii. Do przechowywania skomplikowanych informacji konfiguracyjnych lepiej uywa klasy Preferences, ktra zostaa
opisana w kolejnym podrozdziale.
java.util.Properties 1.0

Properties()

Tworzy pust map wasnoci.

Properties(Properties defaults)

Tworzy map wasnoci z zestawem ustawie domylnych.


Parametr:

defaults

Wartoci domylne

String getProperty(String key)

Pobiera map wasnoci. Zwraca acuch skojarzony z kluczem key lub acuch
skojarzony z kluczem key w mapie domylnej, jeli nie ma go w aktualnej mapie,
albo warto null, jeli nie zostanie on znaleziony take w tej drugiej mapie.
Parametr:

key

Klucz, ktrego warto ma zosta pobrana.

String getProperty(String key, String defaultValue)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

554

Java. Podstawy
Pobiera wasno z domyln wartoci, jeli klucz key nie zostanie znaleziony.
Zwraca acuch skojarzony z kluczem key lub acuch domylny, jeli nie ma go
w tablicy.
Parametry:

key

Klucz, ktrego warto ma zosta pobrana.

defaultValue

Warto zwracana, jeli dany klucz nie istnieje.

void load(InputStream in) throws IOException

aduje map wasnoci ze strumienia wejciowego.


Parametr:

in

Strumie wejciowy

void store(OutputStream out, String header) 1.2

Zapisuje map wasnoci w strumieniu wyjciowym.


Parametry:

out

Strumie wyjciowy

header

Nagwek umieszczany w pierwszym wierszu


zapisywanego pliku

java.lang.System 1.0

Properties getProperties()

Pobiera wszystkie waciwoci systemowe. Jeli aplikacja nie ma uprawnie


do pobierania wszystkich waciwoci systemowych, generowany jest wyjtek
zabezpiecze.

String getProperty(String key)

Pobiera waciwo systemow opatrzon podanym kluczem. Jeli aplikacja


nie ma uprawnie do pobierania tej waciwoci systemowej, generowany
jest wyjtek zabezpiecze. Ponisze waciwoci mona zawsze pobiera:
java.version
java.vendor
java.vendor.url
java.class.version
os.name
os.version
os.arch
file.separator
path.separator
line.separator
java.specification.version
java.vm.specification.version
java.vm.specification.vendor
java.vm.specification.name
java.vm.version
java.vm.vendor
java.vm.name

Nazwy wolno dostpnych waciwoci systemowych mona znale w pliku security/


java.policy znajdujcym si w katalogu rodowiska uruchomieniowego Javy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

555

10.4.2. API Preferences


Klasa Properties, mimo i umoliwia zapisywanie i odczytywanie danych konfiguracyjnych
w prosty sposb, ma kilka wad:

Nie zawsze mamy moliwo zapisania plikw konfiguracyjnych w katalogu


gwnym uytkownika, poniewa niektre systemy operacyjne nie znaj
koncepcji katalogu gwnego.

Nie istnieje standardowa konwencja nazywania plikw konfiguracyjnych,


co wie si z ryzykiem wystpienia konfliktw nazw, jeli uytkownik
zainstaluje kilka aplikacji Java.

Niektre systemy operacyjne maj centralne repozytorium, w ktrym przechowuj dane konfiguracyjne. Najlepszym przykadem takiego repozytorium jest rejestr w systemie Microsoft
Windows. Klasa Preferences pozwala utworzy takie repozytorium niezalenie od platformy.
W systemie Windows klasa ta wykorzystuje rejestr. W systemie Linux informacje te s zapisywane w lokalnym systemie plikw. Oczywicie implementacja repozytorium nie ma tajemnic dla programisty uywajcego klasy Preferences.
Repozytorium Preferences ma struktur drzewiast z nazwami cieek do wzw typu
/com/mycompany/myapp. Podobnie jak w przypadku pakietw, konfliktw nazw cieek unika
si, stosujc odwrcone nazwy domen. Projektanci tego API zalecaj nawet, aby ciekom do
wzw konfiguracyjnych nadawa takie same nazwy jak pakietom uywanym w programie.
Kady wze w repozytorium ma oddzieln tablic par klucz warto, w ktrej mona
przechowywa acuchy, liczby i tablice bajtw. Nie ma moliwoci zapisywania obiektw
serializowanych, poniewa projektanci uznali, e format ten jest zbyt ulotny do dugoterminowego przechowywania. Oczywicie ci, ktrzy si z tym nie zgadzaj, mog zapisywa serializowane obiekty w tablicach bajtw.
Wiksz elastyczno zapewniaj dodatkowe rwnolege drzewa. Kady uytkownik programu ma wasne drzewo i dodatkowe drzewo systemowe, ktre przechowuje ustawienia wsplne
wszystkich uytkownikw. Odpowiednie drzewo ustawie jest pobierane przez klas Prefe
rences przy uyciu pojcia biecego uytkownika, rozumianego zgodnie z systemem operacyjnym.
Aby uzyska dostp do wza drzewa, naley zacz od uytkownika root lub katalogu
systemowego root:
Preferences root = Preferences.userRoot();

lub
Preferences root = Preferences.systemRoot();

Nastpnie uzyskujemy dostp do wza. Mona ograniczy si do podania jego cieki:


Preferences node = root.node("/com/mycompany/myapp");

Za pomoc wygodnego skrtu mona pobra wze, ktrego cieka jest taka sama jak nazwa
pakietu klasy. Wystarczy pobra obiekt tej klasy i zastosowa ponisze wywoanie:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

556

Java. Podstawy
Preferences node = Preferences.userNodeForPackage(obj.getClass());

lub
Preferences node = Preferences.systemNodeForPackage(obj.getClass());

Zazwyczaj obj jest referencj this.


Majc wze, mona uzyska dostp do jego tablicy par klucz warto za pomoc nastpujcych metod:
String get(String key, String defval)
int getInt(String key, int defval)
long getLong(String key, long defval)
float getFloat(String key, float defval)
double getDouble(String key, double defval)
boolean getBoolean(String key, boolean defval)
byte[] getByteArray(String key, byte[] defval)

Naley pamita, e przy odczytywaniu informacji trzeba dostarczy warto domyln, na


wypadek gdyby w repozytorium brakowao jakich danych. Wartoci domylne s wymagane
z kilku powodw. Danych moe brakowa, poniewa uytkownik nigdy nie ustawi danej opcji.
Niektre ograniczone platformy mog nie mie repozytorium, a urzdzenia przenone mog
by tymczasowo odczone od repozytorium.
Do zapisu danych w repozytorium su metody put:
put(String key, String value)
putInt(String key, int value)

i tak dalej.
List wszystkich kluczy zapisanych w wle mona uzyska za pomoc metody String[] keys.
Nie ma jednak obecnie sposobu na sprawdzenie typu wartoci okrelonego klucza.
Centralne repozytoria, takie jak rejestr w systemie Windows, zazwyczaj cierpi z dwch
powodw:

Z czasem zamieniaj si w mietnisko pene przestarzaych informacji.

Dane konfiguracyjne plcz si w repozytorium, przez co trudno jest je przenie


na inn platform.

Klasa Preferences ma rozwizanie drugiego problemu. Mona wyeksportowa ustawienia


poddrzewa (lub rzadziej jednego wza) za pomoc poniszych metod:
void exportSubtree(OutputStream out)
void exportNode(OutputStream out)

Dane s zapisywane w formacie XML. Mona je zaimportowa do innego repozytorium za


pomoc nastpujcego wywoania:
void importPreferences(InputStream in)

Poniej znajduje si zawarto przykadowego pliku:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

557

<?xml version="1.0" encoding="UTF-8"?>


<!DOCTYPE preferences SYSTEM "http://java.sun.com/dtd/preferences.dtd">
<preferences EXTERNAL_XML_VERSION="1.0">
<root type="user">
<map/>
<node name="com">
<map/>
<node name="horstmann">
<map/>
<node name="corejava">
<map>
<entry key="left" value="11"/>
<entry key="top" value="9"/>
<entry key="width" value="453"/>
<entry key="height" value="365"/>
<entry key="title" value="Witaj, wiecie!"/>
</map>
</node>
</node>
</node>
</root>
</preferences>

Jeli program wykorzystuje klas Preferences, naley umoliwi uytkownikowi import i eksport ustawie, co uatwia przenoszenie ustawie na inny komputer. Program na listingu 10.6
demonstruje omwion technik. Zapisuje on pooenie, rozmiar i tytu gwnego okna. Po
zamkniciu i ponownym uruchomieniu programu okno bdzie wygldao dokadnie tak samo
jak przed zamkniciem programu.
Listing 10.6. preferences/PreferencesTest.java
package preferences;
import
import
import
import
import

java.awt.EventQueue;
java.awt.event.*;
java.io.*;
java.util.prefs.*;
javax.swing.*;

/**
* Program testujcy ustawianie preferencji. Zapamituje pooenie, rozmiar i tytu ramki.
* @version 1.02 2007-06-12
* @author Cay Horstmann
*/
public class PreferencesTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
PreferencesFrame frame = new PreferencesFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

558

Java. Podstawy
}
}
/**
* Ramka pobierajca dane dotyczce pooenia i rozmiaru z preferencji uytkownika oraz aktualizujca
* preferencje w momencie zamykania programu
*/
class PreferencesFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public PreferencesFrame()
{
// Sprawdzanie pooenia, rozmiaru i tytuu w preferencjach
Preferences root = Preferences.userRoot();
final Preferences node = root.node("/com/horstmann/corejava");
int left = node.getInt("left", 0);
int top = node.getInt("top", 0);
int width = node.getInt("width", DEFAULT_WIDTH);
int height = node.getInt("height", DEFAULT_HEIGHT);
setBounds(left, top, width, height);
// Jeli nie ma tytuu, uytkownik zostanie poproszony o jego podanie
String title = node.get("title", "");
if (title.equals("")) title = JOptionPane.showInputDialog("Wpisz tytu
ramki:");
if (title == null) title = "";
setTitle(title);
// Utworzenie okna wyboru plikw wywietlajcego pliki XML
final JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// Akceptacja wszystkich plikw z rozszerzeniem .xml
chooser.setFileFilter(new javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".xml") || f.isDirectory();
}
public String getDescription()
{
return "XML files";
}
});
// menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
JMenuItem exportItem = new JMenuItem("Eksport ustawie");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

menu.add(exportItem);
exportItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (chooser.showSaveDialog(PreferencesFrame.this) ==
JFileChooser.APPROVE_OPTION)
{
try
{
OutputStream out = new
FileOutputStream(chooser.getSelectedFile());
node.exportSubtree(out);
out.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
});
JMenuItem importItem = new JMenuItem("Import ustawie");
menu.add(importItem);
importItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
if (chooser.showOpenDialog(PreferencesFrame.this) ==
JFileChooser.APPROVE_OPTION)
{
try
{
InputStream in = new
FileInputStream(chooser.getSelectedFile());
Preferences.importPreferences(in);
in.close();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
});
JMenuItem exitItem = new JMenuItem("Zamknij");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
node.putInt("left", getX());
node.putInt("top", getY());
node.putInt("width", getWidth());
node.putInt("height", getHeight());

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

559

560

Java. Podstawy
node.put("title", getTitle());
System.exit(0);
}
});
}
}
java.util.prefs.Preferences 1.4

Preferences userRoot()

Zwraca wze preferencji root uytkownika programu.

Preferences systemRoot()

Zwraca wze preferencji root systemu.

Preferences node(String path)

Zwraca wze, do ktrego mona uzyska dostp z biecego wza


za porednictwem podanej cieki path. Jeli cieka jest bezwzgldna (czyli zaczyna
si od znaku /), szukanie wza zaczyna si od korzenia drzewa zawierajcego ten
wze preferencji. Jeli wze w podanej ciece nie istnieje, zostanie utworzony.

Preferences userNodeForPackage(Class cl)

Preferences systemNodeForPackage(Class cl)

Zwraca wze w biecym drzewie uytkownika lub drzewo systemowe, ktrego


cieka bezwzgldna wza odpowiada nazwie pakietu klasy cl.

String[] keys()

Zwraca wszystkie klucze nalece do wza.

String get(String key, String defval)

int getInt(String key, int defval)

long getLong(String key, long defval)

float getFloat(String key, float defval)

double getDouble(String key, double defval)

boolean getBoolean(String key, boolean defval)

byte[] getByteArray(String key, byte[] defval)

Zwraca warto skojarzon z danym kluczem lub podan warto domyln,


jeli z kluczem nie jest skojarzona adna warto lub skojarzona warto
jest nieprawidowego typu, lub magazyn preferencji nie jest dostpny.

void put(String key, String value)

void putInt(String key, int value)

void putLong(String key, long value)

void putFloat(String key, float value)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 10.

Przygotowywanie apletw i aplikacji do uytku

void putDouble(String key, double value)

void putBoolean(String key, boolean value)

void putByteArray(String key, byte[] value)

561

Zapisuje par klucz warto w wle.

void exportSubtree(OutputStream out)

Zapisuje preferencje wza i jego potomkw w okrelonym strumieniu.

void exportNode(OutputStream out)

Zapisuje preferencje wza (ale nie jego potomkw) w okrelonym strumieniu.

void importPreferences(InputStream in)

Importuje preferencje zawarte w okrelonym strumieniu.


Na tym zakoczymy opis technik przygotowywania aplikacji w Javie do uytku. W nastpnym rozdziale nauczymy si wykorzystywa wyjtki do okrelania zachowania programu
w sytuacjach awaryjnych. Dodatkowo opisujemy techniki testowania i znajdowania bdw,
ktre pozwalaj pozby si wielu usterek jeszcze przed uruchomieniem programu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

562

Java. Podstawy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

11

Wyjtki, dzienniki,
asercje i debugowanie
W tym rozdziale:

Obsuga bdw

Obsuga wyjtkw

Wskazwki dotyczce stosowania wyjtkw

Asercje

Dzienniki

Wskazwki dotyczce debugowania

Wskazwki dotyczce debugowania programw z GUI

Praca z debugerem

Gdybymy yli w idealnym wiecie, uytkownicy zawsze wprowadzaliby prawidowe dane,


wybierane pliki zawsze by istniay, a kod byby pozbawiony wszelkich bdw. Programy
prezentowane do tej pory s zbudowane tak, jakbymy yli w takim wanie wyimaginowanym
miejscu. Nadesza jednak pora na poznanie technik programistycznych sucych do pracy
w wiecie rzeczywistym, penym niepoprawnych danych i bdnego kodu.
Jeli podczas pracy z programem wystpi bd, ktry zniweczy jak cz pracy uytkownika, moe on ju nigdy nie wrci do tego programu. Te nieprzyjemne sytuacje mog by
spowodowane niedopatrzeniem programisty lub rozmaitymi czynnikami zewntrznymi.
W takiej sytuacji program musi przynajmniej:

poinformowa uytkownika o bdzie,

zapisa dotychczasow prac,

pozwoli uytkownikowi na eleganckie zakoczenie pracy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

564

Java. Podstawy
Sytuacje wyjtkowe, ktre mog wywoa awari programu (jak na przykad podanie nieprawidowych danych na wejciu), s w Javie rozwizywane za pomoc techniki obsugi
wyjtkw. Obsuga wyjtkw w Javie wyglda podobnie jak w jzykach C++ i Delphi.
Pierwsza cz tego rozdziau opisuje wyjtki w Javie.
Testowanie polega na przeprowadzaniu wielu rnych testw, majcych na celu sprawdzenie,
czy program dziaa zgodnie z przewidywaniami. Testy te mog jednak zabiera duo czasu,
a po zakoczeniu testowania zazwyczaj s niepotrzebne. Mona je wtedy usun i w razie
potrzeby wklei z powrotem, kiedy znw bd potrzebne. Jest to jednak mudne zajcie. Druga
cz rozdziau zostaa powicona selektywnemu uruchamianiu testw za pomoc asercji.
Komunikacja z uytkownikiem w wyjtkowej sytuacji nie zawsze jest moliwa. Czsto te
nie mona normalnie zamkn programu. Wtedy dobrym pomysem jest zapisanie danych
zdarzenia do pniejszej analizy. Trzecia cz tego rozdziau zostaa powicona technikom
zapisu danych do dziennika.
Na zakoczenie dajemy kilka wskazwek na temat wydobywania poytecznych informacji
z dziaajcego programu oraz uywania debugera w zintegrowanym rodowisku programistycznym.

11.1. Obsuga bdw


Wyobramy sobie, e w trakcie dziaania programu wystpi bd. Mg zosta spowodowany przez nieprawidowe dane w pliku, wadliwe poczenie sieciowe lub (musimy to powiedzie) uycie nieprawidowego indeksu tablicy bd referencji, ktra nie zostaa jeszcze
przypisana do adnego obiektu. Uytkownicy oczekuj, e w razie wystpienia bdu ich programy bd si zachowywa racjonalnie. Jeli program nie moe ukoczy zadania z powodu
bdu, powinien wykona jedn z dwch czynnoci:

powrci do bezpiecznego stanu i pozwoli uytkownikowi wyda inne polecenia;

pozwoli uytkownikowi zapisa ca prac i zamkn program.

To moe by trudne, poniewa procedury wykrywajce (lub powodujce) bdy zazwyczaj


znajduj si z dala od kodu, ktry mgby przywrci dane do bezpiecznego stanu, lub procedur mogcych zapisa prac i zamkn program. Obsuga wyjtkw polega na przekazywaniu
kontroli z miejsca wystpienia bdu do procedur, ktre mog rozwiza ten problem. Aby
obsuy sytuacje wyjtkowe w programie, naley przewidzie, jakiego rodzaju bdy i problemy mog wystpi. Co trzeba rozway?

Bdy danych wejciowych. Poza robieniem literwek uytkownicy czasami


nie trzymaj si oglnych zalece i chodz wasnymi ciekami. Zamy na przykad,
e uytkownik chce przej pod adres URL o niepoprawnej skadni. Program
powinien to sprawdzi, a jeli tego nie zrobi, wystpi problemy z warstw sieciow.

Bdy urzdze. Urzdzenia nie zawsze dziaaj tak, jak powinny. Drukarka moe
by wyczona, a strona internetowa, z ktrej drukujemy, moe by chwilowo
niedostpna. Urzdzenia czsto zawodz w trakcie pracy. Na przykad w drukarce
w czasie drukowania moe si skoczy papier.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

Ograniczenia fizyczne. Moe si skoczy miejsce na dysku.

Bdy w kodzie. Jedna z metod moe nie dziaa prawidowo. Moe na przykad
zwraca niepoprawne wyniki albo nieprawidowo uywa innych metod. Inne
przykady bdw spowodowanych przez kod to nieprawidowe obliczenie indeksu
w tablicy i prba dostpu do nieistniejcego elementu tablicy mieszajcej czy prba
pobrania elementu z pustego stosu.

565

Typow reakcj na bd w metodzie jest zwrot specjalnego kodu bdu, ktry nastpnie jest
przekazywany do analizy metodzie wywoujcej. Na przykad metody odczytujce dane
z plikw czsto zwracaj warto -1 zamiast standardowego znaku koca pliku. Jest to doskonay sposb na poradzenie sobie z wieloma sytuacjami wyjtkowymi. Inna czsto spotykana
warto zwrotna oznaczajca bd to referencja null.
Niestety nie zawsze da si zwrci kod bdu. Czasami odrnienie poprawnych danych od
niepoprawnych moe by trudne. Metoda zwracajca liczby cakowite nie moe zwrci
wartoci -1 jako bd, poniewa warto ta moe by poprawna.
W zwizku z tym, jeli metoda nie moe normalnie ukoczy swojego dziaania, moe wybra
alternatywny sposb wybrnicia z sytuacji (pisalimy o tym w rozdziale 5.). Wtedy nie zwraca
adnej wartoci, tylko wyrzuca obiekt zawierajcy informacje o bdzie. Zauwamy, e metoda
jest koczona natychmiast nie zwraca adnej wartoci. Ponadto dziaanie programu nie jest
wznawiane od miejsca, w ktrym metoda zostaa wywoana. Zamiast tego mechanizm obsugi
wyjtkw zaczyna szuka procedury obsugi bdw, ktra potrafi rozwiza dany problem.
Wyjtki maj wasn skadni oraz wchodz w skad specjalnej hierarchii dziedziczenia.
Najpierw zajmiemy si t skadni, a nastpnie udzielimy kilku wskazwek dotyczcych
efektywnego wykorzystania tej waciwoci jzyka.

11.1.1. Klasyfikacja wyjtkw


Typ obiektu wyjtku w Javie jest zawsze pochodn klasy Throwable. Jak si niebawem przekonamy, mona pisa wasne klasy wyjtkw, jeli te dostpne standardowo nie wystarczaj.
Rysunek 11.1 przedstawia uproszczony diagram hierarchii wyjtkw.
Naley zauway, e wszystkie wyjtki s potomkami klasy Throwable, chocia hierarchia
jej potomkw dzieli si na dwie gazie: Error i Exception.
Klasy bdce potomkami klasy Error odpowiadaj bdom wewntrznym i wyczerpaniu
zasobw w rodowisku uruchomieniowym. Nie naley wyrzuca obiektw tego typu, poniewa jeli wystpi bd wewntrzny, niewiele mona zrobi poza powiadomieniem uytkownika i zamkniciem programu. Tego typu sytuacje wystpuj niezbyt czsto.
Programici Javy o wiele wicej czasu powicaj klasie Exception. Ona take dzieli si na
dwie gazie: wyjtki zwizane z wykonywaniem, tak zwane wyjtki wykonawcze (Runtime
Exception), i pozostae. Oglna zasada gosi: wyjtki typu RuntimeException s powodowane przez bdy programisty. Wszystkie pozostae maj zwizek z innymi niepodanymi
zdarzeniami, takimi jak bdy wejcia-wyjcia, ktre zaszy w poza tym dobrym programie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

566

Java. Podstawy

Rysunek 11.1. Hierarchia wyjtkw w Javie

Do wyjtkw dziedziczcych po klasie RuntimeException nale:

niepoprawne rzutowanie,

dostp do nieistniejcego elementu tablicy,

dostp do pustego wskanika.

Do wyjtkw, ktre nie dziedzicz po RuntimeException, nale:

prba odczytu za kocem pliku,

prba otwarcia niepoprawnego adresu URL,

prba znalezienia obiektu typu Class dla acucha, ktry nie zgadza si z adn
z istniejcych klas.

Zasada jeli wystpi wyjtek RuntimeException, znaczy, e popenie bd sprawdza si


doskonale. Wyjtku ArrayIndexOutOfBoundsException mona unikn, porwnujc dany indeks
tablicy z jej rozmiarem. Wyjtek NullPointerException nie miaby miejsca, gdyby przed uyciem zmiennej sprawdzono, czy nie ma wartoci null.
Jak wyglda sprawa z adresami URL? Czy nie mona sprawdzi ich skadni przed uyciem?
Problem w tym, e rne przegldarki obsuguj adresy rnego rodzaju. Na przykad przegldarka Firefox poradzi sobie z adresem typu mailto:, podczas gdy przegldarka apletw
nie. W zwizku z tym pojcie niepoprawnej skadni moe mie rne znaczenie w rnych
rodowiskach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

567

W specyfikacji Javy wyjtki typu Error lub RuntimeException nazywane s wyjtkami niekontrolowanymi (ang. unchecked exception). Wszystkie pozostae to wyjtki kontrolowane
(ang. checked exception). My rwnie stosujemy t terminologi. Kompilator sprawdza, czy
dostarczono procedury obsugi dla wszystkich wyjtkw kontrolowanych.
Nazwa wyjtek wykonawczy (RuntimeException) jest nieco nieprecyzyjna, poniewa wszystkie bdy, ktre opisujemy, wystpuj w czasie dziaania programu.

Osoby zaznajomione ze znacznie ubosz hierarchi wyjtkw w standardowej


bibliotece C++ mog by zdziwione. Jzyk ten dysponuje dwiema podstawowymi
klasami wyjtkw o nazwie runtime_error i logic_error. Ta druga jest odpowiednikiem
klasy RuntimeException w Javie, czyli take okrela bdy logiczne. Klasa runtime_error
jest podstaw wyjtkw powodowanych przez nieprzewidywalne zdarzenia. Odpowiada
wyjtkom, ktre w Javie nie s typu RuntimeException.

11.1.2. Deklarowanie wyjtkw kontrolowanych


Metoda w Javie moe spowodowa wyjtek, jeli napotka sytuacj, z ktr nie potrafi sobie
poradzi. Zasada jest prosta: metoda nie tylko informuje kompilator, jakie wartoci moe
zwrci, lecz rwnie co moe si nie uda. Na przykad instrukcja odczytujca dane z pliku
wie, e pliku tego moe nie by lub moe by pusty. W zwizku z tym procedura przetwarzajca informacje z pliku musi powiadomi kompilator, e moe spowodowa wyjtek wejcia-wyjcia.
Informacj, e metoda moe spowodowa wyjtek, naley umieci w jej nagwku. Tre
nagwka moe si zmienia w zalenoci od tego, jakie wyjtki kontrolowane metoda moe
spowodowa. Poniej znajduje si na przykad deklaracja jednego z konstruktorw klasy
FileInputStream z biblioteki standardowej (wicej na temat strumieni piszemy w rozdziale 12.).
public FileInputStream(String name) throws FileNotFoundException

Ta deklaracja informuje, e konstruktor ten tworzy obiekt typu FileInputStream z parametru


typu String, ale moe take spowodowa wyjtek FileNotFoundException. Jeli taka przykra
sytuacja bdzie miaa miejsce, konstruktor nie utworzy nowego obiektu typu FileInputStream,
tylko wyrzuci obiekt typu FileNotFoundException. Wtedy system wykonawczy rozpocznie
poszukiwanie procedury obsugi wyjtkw, ktra potrafi obsuy obiekt typu FileNotFound
Exception.
Piszc metod, nie trzeba informowa o kadym moliwym wyjtku, ktry moe przez ni
zosta zgoszony. Aby zrozumie, co i kiedy naley uwzgldni w klauzuli throws, trzeba
pamita, e wyjtki s zgaszane w nastpujcych sytuacjach:

Wywoanie metody, ktra zgasza wyjtek kontrolowany, na przykad konstruktor


FileInputStream.

Wykrycie bdu i zgoszenie wyjtku kontrolowanego za pomoc instrukcji throw


(instrukcj throw opisujemy w kolejnym podrozdziale).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

568

Java. Podstawy

Bd programisty, typu a[-1] = 0, ktry powoduje wyjtek niekontrolowany,


np. ArrayIndexOutOfBoundsException.

Wystpienie bdu wewntrznego w maszynie wirtualnej lub bibliotece wykonawczej.

Jeli ma miejsce ktry z dwch pierwszych scenariuszy, trzeba poinformowa programistw,


ktrzy bd uywa naszej metody, o moliwoci wystpienia wyjtku. Dlaczego? Kada
metoda, ktra moe zgosi wyjtek, jest potencjaln puapk. Jeli adna procedura obsugi
nie przechwyci tego wyjtku, biecy wtek wykonywania zostanie zamknity.
Deklarujc metod, ktra moe spowodowa wyjtek, naley podobnie jak w metodach
nalecych do standardowych klas Javy w nagwku umieci specyfikacj wyjtku informujc, e metoda ta moe zgosi wyjtek.
class MyAnimation
{
. . .
public Image loadImage(String s) throws IOException
{
. . .
}
}

Jeli metoda moe zgosi wyjtki kontrolowane rnych typw, w jej nagwku musi si
znale ich lista rozdzielona przecinkami:
class MyAnimation
{
. . .
public Image loadImage(String s) throws FileNotFoundException, EOFException
{
. . .
}
}

Nie trzeba natomiast informowa o wyjtkach wewntrznych Javy, czyli tych, ktre dziedzicz po klasie Error. Ich rdem moe by kady fragment kodu, przez co nie ma moliwoci sprawowania nad nimi kontroli.
Podobnie nie naley informowa o wyjtkach dziedziczcych po klasie RuntimeException:
class MyAnimation
{
. . .
void drawImage(int i) throws ArrayIndexOutOfBoundsException
{
. . .
}
}

// zy styl

Wyjtki tego typu mog by w peni kontrolowane przez programist. Jeli istnieje ryzyko
wystpienia bdw zwizanych z indeksami w tablicy, naley powici chwil czasu na ich
napraw, zamiast informowa, e mog wystpi.
Podsumowujc, kada metoda musi deklarowa wszystkie wyjtki kontrolowane (ang.
checked), ktre moe zgosi. Wyjtki niekontrolowane s albo poza zasigiem programisty

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

569

(Error), albo powstaj w takich sytuacjach, do ktrych programista nie powinien dopuci
(RuntimeException). Jeli metoda nie deklaruje wszystkich wyjtkw kontrolowanych, kompilator wywietli komunikat o bdzie.
Oczywicie, jak przekonalimy si wczeniej, zamiast deklarowa wyjtek, mona go przechwyci. Wtedy nie zostanie on wyrzucony z metody, a wic nie jest potrzebna specyfikacja
throws. Dalej nauczymy si podejmowa decyzj, czy przechwyci wyjtek, czy pozwoli
zrobi to komu innemu.
Przesaniajc metod, naley pamita, e wyjtki kontrolowane deklarowane przez
metod w podklasie nie mog by bardziej oglne ni w metodzie nadklasy (metoda podklasy moe zgasza mniej oglne wyjtki lub nie zgasza adnych). Jeli metoda w nadklasie nie zgasza adnych wyjtkw kontrolowanych, metoda w podklasie
rwnie nie moe tego robi. Jeli na przykad przesonimy metod JComponent.paint
Component, metoda paintComponent w podklasie nie moe zgasza adnych wyjtkw kontrolowanych, poniewa nie robi tego metoda z nadklasy.

Jeli metoda deklaruje zgaszanie wyjtkw nalecych do okrelonej klasy, moe zgasza
wyjtki tej klasy lub dowolnej z jej podklas. Na przykad konstruktor FileInputStream moe
deklarowa zgaszanie wyjtkw typu IOException. W takim przypadku nie wiadomo, o jaki
typ wyjtku IOException konkretnie moe chodzi. Moe to by czysty typ IOException lub
jeden z jego podtypw, na przykad FileNotFoundException.
Specyfikator throws w Javie ma prawie identyczne zastosowanie jak throw w C++.
Jedyna rnica polega na tym, e w C++ specyfikatory throw s stosowane w czasie
dziaania programu, a nie kompilacji. Oznacza to, e kompilator C++ nie zwraca uwagi
na specyfikacje wyjtkw. Jeli jednak wyjtek wystpi w funkcji nieznajdujcej si na
licie throw, wywoywana jest funkcja unexpected i program zostaje zamknity.
Ponadto w C++, jeli funkcja nie posiada adnej specyfikacji throw, moe zgosi kady
rodzaj wyjtku. W Javie metoda bez specyfikatora throws nie moe w ogle zgasza
wyjtkw kontrolowanych.

11.1.3. Zgaszanie wyjtkw


Wyobramy sobie, e w naszym kodzie miao miejsce straszne zdarzenie. Mamy metod
o nazwie readData, ktra wczytuje plik z nastpujcym nagwkiem:
Content-length: 1024

Znak koca pliku pojawia si jednak po 733 znakach. Decydujemy, e sytuacja ta jest na tyle
nienormalna, e trzeba zgosi wyjtek.
Naley podj decyzj, jakiego typu wyjtek to ma by. Dobrym wyborem wydaje si jaki
rodzaj wyjtku IOException. W dokumentacji API mona znale typ EOFException, ktrego
opis brzmi Sygnalizuje niespodziewane napotkanie koca pliku w czasie wczytywania
danych. Doskonale. Zgaszamy go nastpujco:
throw new EOFException();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

570

Java. Podstawy
lub
EOFException e = new EOFException();
throw e;

Caa metoda wyglda tak:


String readData(Scanner in) throws EOFException
{
. . .
while (. . .)
{
if (!in.hasNext())
// napotkano koniec pliku
{
if (n < len)
throw new EOFException();
}
. . .
}
return s;
}

Klasa EOFException posiada jeszcze jeden konstruktor, ktry przyjmuje argument w postaci
acucha. Mona z niego zrobi dobry uytek, dostarczajc bardziej szczegowy opis sytuacji
wyjtkowej.
String gripe = "Content-length: " + len + ", Received: " + n;
throw new EOFException(gripe);

Jak wida, zgaszanie wyjtkw jest proste, jeli istnieje moliwo wykorzystania jednej
z istniejcych klas. W takim przypadku naley:
1.

Znale odpowiedni klas wyjtkw.

2. Utworzy obiekt tej klasy.


3. Zgosi go.

Kiedy metoda zgosi wyjtek, nie zwraca wartoci do wywoujcego. Oznacza to, e nie ma
koniecznoci zajmowania si domyln wartoci zwrotn lub kodem bdu.
Zgaszanie wyjtkw w C++ i Javie wyglda prawie tak samo. Jedyna rnica polega na tym, e w Javie mona generowa wycznie obiekty nalece do podklas
klasy Throwable. W C++ mona zgasza wartoci dowolnego typu.

11.1.4. Tworzenie klas wyjtkw


Czasami w programie moe wystpi problem, do ktrego nie pasuje adna ze standardowych
klas wyjtkw. W takim przypadku mona utworzy wasn klas, ktra powinna dziedziczy po klasie Exception lub jednej z jej podklas, na przykad IOException. Istnieje zwyczaj
polegajcy na dostarczeniu zarwno konstruktora domylnego, jak i konstruktora pobierajcego szczegowe dane (metoda toString w nadklasie Throwable drukuje szczegowe dane,
ktre mog by przydatne w czasie debugowania).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

571

class FileFormatException extends IOException


{
public FileFormatException() {}
public FileFormatException(String gripe)
{
super(gripe);
}
}

Teraz mona generowa wasne typy wyjtkw:


String readData(BufferedReader in) throws FileFormatException
{
. . .
while (. . .)
{
if (ch == -1)
// napotkano koniec pliku
{
if (n < len)
throw new FileFormatException();
}
. . .
}
return s;
}
java.lang.Throwable 1.0

Throwable()

Tworzy nowy obiekt typu Throwable, niezawierajcy adnych szczegowych


informacji.

Throwable(String message)

Tworzy obiekt typu Throwable z opisem okrelonym przez parametr message.


Zgodnie z konwencj wszystkie pochodne klasy wyjtkw posiadaj zarwno
konstruktor domylny, jak i konstruktor ze szczegowym komunikatem.

String getMessage()

Zwraca szczegowy opis wyjtku.

11.2. Przechwytywanie wyjtkw


Potrafimy ju zgasza wyjtki. To proste zadanie polega na wygenerowaniu wyjtku potem
mona o nim zapomnie. Oczywicie niektre partie kodu musz przechwytywa wyjtki,
a to wymaga duszego planowania.
Jeli wygenerowany wyjtek nie zostanie przechwycony, program zostanie zamknity, a w konsoli pojawi si komunikat informujcy o typie wyjtku i dane ze ledzenia stosu. Programy
z graficznym interfejsem (aplety i aplikacje) przechwytuj wyjtki, drukuj komunikaty ze

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

572

Java. Podstawy
ledzenia stosu wywoa i wracaj do ptli przetwarzajcej interfejs (dobrym rozwizaniem
podczas usuwania bdw z programu z graficznym interfejsem uytkownika jest ustawienie
konsoli w widocznym miejscu).
Do przechwytywania wyjtkw suy blok try-catch. Najprostsza forma tego bloku wyglda
nastpujco:
try
{
kod
wicej kodu
i jeszcze troch kodu
}
catch (TypWyjtku e)
{
procedura obsugi okrelonego typu wyjtkw
}

Jeli ktrykolwiek z fragmentw kodu w bloku try zgosi wyjtek typu okrelonego w klauzuli catch, to:
1.

Program pominie reszt kodu w bloku try.

2. Program wykona procedur obsugi znajdujc si w klauzuli catch.

Jeli aden z fragmentw kodu w bloku try nie zgosi wyjtku, klauzula catch zostaje
pominita.
Jeli w ktrejkolwiek z metod zostanie wygenerowany wyjtek innego typu ni okrelony
w klauzuli catch, program natychmiast z tej metody wychodzi (moe gdzie wyej w stosie
wywoa znajduje si klauzula catch przeznaczona dla tego typu wyjtku).
Powysze informacje zilustrujemy typowym przykadem procedury odczytujcej dane:
public void read(String filename)
{
try
{
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read()) != -1)
{
przetwarzanie danych wejciowych
}
}
catch (IOException exception)
{
exception.printStackTrace();
}
}

Jak wida, wikszo kodu w klauzuli try jest prosta odczytuje i przetwarza bajty a do
napotkania koca pliku. Rzut oka do API Javy pozwala si zorientowa, e metoda read moe
zgosi wyjtek IOException. W takim przypadku wychodzimy z ptli while, wchodzimy do

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

573

klauzuli catch i generujemy dane ze ledzenia stosu. W przypadku niewielkiego programu


wydaje si to rozsdnym sposobem obsugi tego wyjtku. Jakie s jednak inne wyjcia z tej
sytuacji?
Czsto najlepiej jest nic nie robi, tylko przekaza wyjtek do wywoujcego. Jeli bd
powstanie w metodzie read, najlepiej, by zaja si nim procedura, ktra wywoaa t metod!
Takie podejcie do problemu wymaga dodania informacji, e metoda moe zgosi wyjtek
IOException.
public void read(String filename) throws IOException
{
InputStream in = new FileInputStream(filename);
int b;
while ((b = in.read()) != -1)
{
przetwarzanie danych wejciowych
}
}

Naley pamita, e kompilator cile trzyma si specyfikatorw throws. Jeli wywoujemy


metod, ktra generuje wyjtek kontrolowany, musi on zosta obsuony lub przekazany dalej.
Ktre z tych dwch rozwiza jest lepsze? Oglna zasada nakazuje przechwytywa te wyjtki,
ktre mona obsuy, i odsya te, ktrych nie potrafimy obsuy.
Odsyajc wyjtek, trzeba doda specyfikator throws, aby ostrzec wywoujcego, e moe
zosta wygenerowany wyjtek.
Informacje na temat typw wyjtkw zgaszanych przez konkretne metody mona znale
w dokumentacji API. Dysponujc tymi informacjami, mona zdecydowa, czy je obsuy,
czy doda do listy throws. Wybr tej drugiej moliwoci nie stanowi dla programisty adnej
ujmy. Lepiej przekaza wyjtek do fachowej obsugi, ni obsuy go le.
Jest jeden wyjtek od tej reguy, o ktrym ju wspominalimy. Piszc metod przesaniajc
metod z nadklasy, ktra nie zgasza adnych wyjtkw (np. paintComponent z klasy JCompo
nent), kady kontrolowany wyjtek musimy przechwyci w kodzie tej metody. Nie mona
doda wicej specyfikatorw throws do metody w podklasie, ni jest w metodzie w nadklasie.
Przechwytywanie wyjtkw w Javie wyglda prawie tak samo jak w C++. Kod:
catch (Exception e)

// Java

jest analogiczny do kodu:


catch (Exception& e)

// C++

Nie istnieje instrukcja analogiczna do catch(...). Nie jest ona potrzebna w Javie,
poniewa wszystkie wyjtki pochodz od wsplnej nadklasy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

574

Java. Podstawy

11.2.1. Przechwytywanie wielu typw wyjtkw


W jednym bloku try mona przechwyci kilka typw wyjtkw i kady z nich obsuy
w inny sposb. Dla kadego typu naley napisa oddzieln klauzul catch, np.:
try
{
kod, ktry moe generowa wyjtki
}
catch (FileNotFoundException e)
{
dziaania dotyczce nieprawidowego adresu URL
}
catch (UnknownHostException e)
{
dziaania dotyczce nieznanych hostw
}
catch (IOException e)
{
dziaania dotyczce wszystkich pozostaych bdw wejcia-wyjcia
}

Obiekt wyjtku moe zawiera informacje o naturze wyjtku. Aby dowiedzie si wicej
o danym obiekcie, naley uy nastpujcego wywoania:
e.getMessage()

Ponisza instrukcja zwraca szczegowe dane na temat bdu (jeli istniej) lub rzeczywisty
typ obiektu wyjtku:
e.getClass().getName()

Od Java SE 7 mona przechwytywa rne typy wyjtkw w jednej klauzuli catch. Przypumy na przykad, e dziaanie w przypadku braku pliku i nieznanego hosta jest takie
samo. Wwczas mona poczy klauzule catch w nastpujcy sposb:
try
{
kod, ktry moe spowodowa wyjtki
}
catch (FileNotFoundException | UnknownHostException e)
{
dziaania awaryjne w przypadku braku plikw lub nieznanego hosta
}
catch (IOException e)
{
dziaania awaryjne dotyczce pozostaych problemw wejcia i wyjcia
}

Jest to potrzebne tylko wtedy, gdy typ jednego z przechwytywanych wyjtkw nie jest podklas
innego.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

575

Gdy przechwytywanych jest kilka wyjtkw, zmienn wyjtku jest final. Przykadowo zmiennej e w klauzuli nie mona przypisa innej wartoci:
catch (FileNotFoundException | UnknownHostException e) { ... }

Przechwytywanie po kilka wyjtkw naraz nie tylko sprawia, e kod jest bardziej
przejrzysty, ale rwnie powoduje jego szybsze dziaanie. W wygenerowanym kodzie
bajtowym znajduje si tylko jeden blok dla wsplnej klauzuli catch.

11.2.2. Powtrne generowanie wyjtkw


i budowanie acuchw wyjtkw
Wyjtek mona wygenerowa take w klauzuli catch. Zazwyczaj robi si to w celu zmiany
jego typu. W podsystemie, z ktrego korzystaj inni programici, warto utworzy taki typ
wyjtku, ktry wskazuje na awari tego podsystemu. Przykadem takiego typu wyjtku jest
ServletException. Kod wykonujcy serwlet moe nie potrzebowa wszystkich szczegw
na temat tego, co zawiodo. Zdecydowanie natomiast musi wiedzie, e serwer mia awari.
Poniszy fragment kodu przedstawia przechwycenie wyjtku i powtrne jego wygenerowanie:
try
{
dostp do bazy danych
}
catch (SQLException e)
{
throw new ServletException("Bd bazy danych: " + e.getMessage());
}

W tym przypadku do obiektu ServletException zosta dodany komunikat.


Mona jednak zrobi co lepszego, czyli ustawi pierwotny wyjtek jako powd (ang. cause)
nowego wyjtku:
try
{
dostp do bazy danych
}
catch (SQLException e)
{
Throwable se = new ServletException("database error");
se.initCause(e);
throw se;
}

Po przechwyceniu tego wyjtku mona odzyska dane dotyczce pierwotnego wyjtku:


Throwable e = se.getCause();

Ta technika opakowywania jest szczeglnie polecana. Pozwala ona na zgaszanie wyjtkw


wysokiego poziomu w podsystemach bez utraty szczegw dotyczcych pierwotnej awarii.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

576

Java. Podstawy

Technika opakowywania jest przydatna rwnie wtedy, gdy wyjtek kontrolowany


wystpuje w metodzie, ktra nie moe zgasza wyjtkw kontrolowanych. Mona
go przechwyci i opakowa w obiekt wyjtku wykonawczego.

Czasami trzeba tylko zarejestrowa informacj o wyjtku i zgosi go ponownie bez zmieniania:
try
{
dostp do bazy danych
}
catch (Exception e)
{
logger.log(level, message, e);
throw e;
}

Przed pojawieniem si Java SE 7 metoda ta bya problematyczna. Wyobramy sobie, e


przedstawiony kod znajduje si w metodzie:
public void updateRecord() throws SQLException

Kompilator najpierw znajdowa instrukcj throw w bloku catch, nastpnie sprawdza typ e
i narzeka, e metoda ta moe zgasza dowolny typ wyjtku, nie tylko SQLException. Zostao
to ju naprawione. Kompilator bierze pod uwag, e e pochodzi z bloku try. Biorc pod uwag,
e w bloku tym jedynymi wyjtkami kontrolowanymi s egzemplarze klasy SQLException, oraz
uwzgldniajc fakt, e e nie zmienia si w bloku catch, mona powiedzie, e otaczajca
metoda zgasza wyjtki typu SQLException.

11.2.3. Klauzula finally


Kiedy zostaje zgoszony wyjtek, nastpuje zatrzymanie wykonywania pozostaego kodu
w metodzie i wyjcie z niej. Moe to powodowa problem, jeli metoda ta pobraa jakie
zasoby, o ktrych wie tylko ona, a ktre musz zosta wyczyszczone. Rozwizaniem tego
problemu moe by przechwycenie i ponowne wygenerowanie wszystkich wyjtkw. Jest
to jednak mao wydajna metoda, poniewa konieczne jest czyszczenie zasobw w dwch miejscach w normalnym kodzie i w kodzie wyjtku.
W Javie dostpne jest lepsze rozwizanie polegajce na uyciu klauzuli finally. Poniej
przedstawiamy, jak prawidowo pozby si obiektu Graphics. Tych samych technik naley
uywa do zamykania pocze z baz danych. W rozdziale 4. drugiego tomu wyjaniamy,
dlaczego zamknicie wszystkich pocze z baz danych jest bardzo wane, nawet kiedy
wystpi wyjtki.
Kod w klauzuli finally jest wykonywany bez wzgldu na to, czy wyjtek zostanie przechwycony, czy nie. W poniszym przykadzie kontekst graficzny zostanie usunity bez
wzgldu na okolicznoci.
InputStream in = new FileInputStream(...);
try
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

577

// 1
potencjalne rdo wyjtkw
// 2
}
catch (IOException e)
{
// 3
wywietlanie okna dialogowego bdu
// 4
}
finally
{
// 5
in.close();
}
// 6

Przeanalizujmy trzy moliwe sytuacje, w ktrych program wykona zawarto klauzuli finally:
1.

Kod nie generuje adnego wyjtku. W tym przypadku najpierw wykonywany jest
kod w klauzuli try. Nastpnie wykonywana jest klauzula finally. Dalej sterowanie
przekazywane jest do pierwszej instrukcji za klauzul finally. Innymi sowy,
przepyw sterowania jest nastpujcy: 1, 2, 5 i 6.

2. Kod generuje wyjtek, w tym przypadku IOException, przechwytywany w klauzuli


catch. W tym przypadku kod w bloku try jest wykonywany do punktu, w ktrym

zosta wygenerowany wyjtek. Reszta kodu w tym bloku zostaje pominita. Nastpnie
wykonywany jest kod z pasujcej klauzuli catch i kod z klauzuli finally.
Jeli kod nie wygeneruje wyjtku, wykonany zostanie pierwszy wiersz kodu
za klauzul finally. Tym razem przepyw sterowania jest taki: 1, 3, 4, 5, 6.
Jeli klauzula catch wygeneruje wyjtek, zostaje on zwrcony do metody,
ktra wywoaa t metod, i przepyw sterowania przechodzi przez punkty 1, 3 i 5.
3. Kod zgasza wyjtek, ktrego nie przechwytuje adna klauzula catch. Wykonywany
jest kod w bloku try do momentu wygenerowania wyjtku. Nastpnie wykonywany
jest kod w klauzuli finally i wyjtek jest zwracany do metody, ktra wywoaa

t metod. Sterowanie przechodzi tylko przez punkty 1 i 5.


Klauzuli finally mona uy bez klauzuli catch. Przyjrzyjmy si poniszej instrukcji try:
InputStream in = ...;
try
{
potencjalne rdo wyjtkw
}
finally
{
in.close();
}

Instrukcja in.close() w klauzuli finally zostanie wykonana bez wzgldu na to, czy w bloku
try zostanie zgoszony wyjtek. Oczywicie, jeli wyjtek zostanie wygenerowany, jest on
zgaszany ponownie i musi zosta przechwycony w innej klauzuli catch.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

578

Java. Podstawy
W poniszej wskazwce wyjaniamy, dlaczego naszym zdaniem dobrym pomysem jest uycie
w ten sposb klauzuli finally do zamykania zasobw.
Zdecydowanie zalecamy oddzielenie od siebie blokw try-catch i try-finally.
Dziki temu kod jest znacznie bardziej przejrzysty. Na przykad:
InputStream in = ...;
try
{
try
{
potencjalne rdo wyjtkw
}
finally
{
in.close();
}
}
catch (IOException e)
{
wywietlenie informacji o bdzie
}

Wewntrzny blok try ma tylko jedno zadanie: pilnuje, czy strumie wejciowy zosta
zamknity. Zewntrzny blok try ma rwnie tylko jedno zadanie: pilnuje, aby bdy byy
wyciszane. Rozwizanie to jest nie tylko bardziej przejrzyste, ale te bardziej funkcjonalne bdy w klauzuli finally s zgaszane.

Klauzula finally moe generowa nieprawidowe wyniki, jeli zawiera instrukcj


return. Zamy, e wychodzimy ze rodka bloku try za pomoc instrukcji return.
Przed zwrceniem przez metod wartoci wykonywana jest zawarto bloku finally.
Jeli blok ten rwnie zawiera instrukcj return, przysania oryginaln warto zwrotn.
Przestudiujmy poniszy sztuczny przykad:
public static int f(int n)
{
try
{
int r = n * n;
return r;
}
finally
{
if (n == 2) return 0;
}
}

Jeli wywoamy metod f(2), blok try wyliczy warto r = 4 i wykona instrukcj return.
Jednak przed zwrceniem tej wartoci zostanie wykonana klauzula finally, ktra
spowoduje zwrot wartoci 0 zamiast spodziewanej 4.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

579

Czasami klauzula finally powoduje powane problemy, zwaszcza kiedy metoda sprztajca
take moe zgosi wyjtek. Wyobramy sobie, e chcemy, aby w chwili wystpienia wyjtku
w kodzie przetwarzajcym strumie zosta on zamknity.
InputStream in = ...;
try
{
potencjalne rdo wyjtkw
}
finally
{
in.close();
}

Nastpnie wyobramy sobie, e kod w bloku try zgasza wyjtek innego typu ni IOException,
ktry ley w sferze zainteresowa wywoujcego ten kod. Zostaje wykonana klauzula finally
i nastpuje wywoanie metody close. Ta metoda sama moe wygenerowa wyjtek IOExcep
tion! Jeli tak si stanie, oryginalny wyjtek zostaje utracony i zamiast niego zgaszany jest
wyjtek IOException.
Stanowi to problem, poniewa pierwszy wyjtek moe by bardziej interesujcy. Jeli chcesz
dziaa zgodnie ze sztuk i ponownie zgosi pierwotny wyjtek, to musisz bardzo skomplikowa kod. Oto przykad:
InputStream in = ...;
Exception ex = null;
try
{
try
{
potencjalne rdo wyjtkw
}
catch (Exception e)
{
ex = e;
throw e;
}
}
finally
{
try
{
in.close();
}
catch (Exception e)
{
if (ex == null) throw e;
}
}

Na szczcie w Java SE 7 znacznie uatwiono zamykanie zasobw, o czym przekonasz si


w nastpnym podrozdziale.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

580

Java. Podstawy

11.2.4. Instrukcja try z zasobami


W Java SE 7 moliwe jest zastosowanie wygodnego skrtu dla konstrukcji typu:
otwarcie zasobu
try
{
praca z zasobem
}
finally
{
zamknicie zasobu
}

Zasb musi jedynie nalee do klasy implementujcej interfejs AutoCloseable. Interfejs ten
ma tylko jedn metod:
void close() throws Exception

Istnieje te interfejs podrzdny AutoCloseable o nazwie Closeable, ktry rwnie


zawiera tylko metod close. Metoda ta jednak zgasza wyjtki typu IOException.

W najprostszej postaci instrukcja try z zasobami wyglda tak:


try (Resource res = ...)
{
praca z zasobem
}

Jeli blok try istnieje, metoda res.close() jest wywoywana automatycznie. Oto typowy
przykad odczyt wszystkich sw z pliku:
try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")))
{
while (in.hasNext())
System.out.println(in.next());
}

Gdy dziaanie bloku zakoczy si normalnie lub wystpi wyjtek, nastpi wywoanie metody
in.close(), jak gdyby zdefiniowana bya klauzula finally.
Mona te okreli kilka zasobw, np.:
try (Scanner in = new Scanner(new FileInputStream("/usr/share/dict/words")),
PrintWriter out = new PrintWriter("out.txt"))
{
while (in.hasNext())
out.println(in.next().toUpperCase());
}

Niezalenie od sposobu zakoczenia wykonywania bloku zasoby in i out zostan zamknite.


Gdybymy mieli to zaprogramowa rcznie, musielibymy uy dwch zagniedonych konstrukcji try-finally.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

581

Jak widziae w poprzednim podrozdziale, problemy powstaj w momencie, gdy zarwno blok
try, jak i metoda close zgaszaj wyjtek. Dziki instrukcji try z zasobami mona to elegancko
rozwiza. Oryginalny wyjtek jest zgaszany ponownie, a wszystkie wyjtki zgoszone przez
metody close s tumione. Zostaj automatycznie przechwycone i dodane do oryginalnego
wyjtku przy uyciu metody addSuppressed. Jeli Ci interesuj, moesz uzyska tablic wyjtkw metody close, wywoujc metod getSuppressed.
Nie musisz si mczy. Jeli musisz zamkn jaki zasb, zawsze uywaj instrukcji try
z zasobami.
Instrukcja try z zasobami te moe zawiera klauzule catch i finally, ktre s
wykonywane po zamkniciu zasobw. W praktyce jednak lepiej jest nie adowa
tak duo do jednej instrukcji try.

11.2.5. Analiza danych ze ledzenia stosu


Dane ze ledzenia stosu (ang. stack trace) przedstawiaj list wszystkich oczekujcych
wywoa metod w okrelonym momencie wykonywania programu. Prawie kady widzia tak
list jest wywietlana zawsze, gdy program w Javie zostaje zamknity z powodu nieprzechwyconego wyjtku.
Opis stosu mona uzyska za pomoc metody printStackTrace z klasy Throwable:
Throwable t = new Throwable();
ByteArrayOutputStream out = new ByteArrayOutputStream();
t.printStackTrace(out);
String description = out.toString();

Bardziej elastyczna w dziaaniu jest metoda getStackTrace, zwracajca tablic obiektw


klasy StackTraceElement, ktre mona przeanalizowa w programie, np.:
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement frame : frames)
analiza ramek

Klasa StackTraceElement posiada metody suce do sprawdzania nazwy pliku i numeru,


a take nazwy klasy i metody wykonywanego wiersza kodu. Metoda toString tworzy sformatowany acuch zawierajcy wszystkie te informacje.
Metoda Thread.getAllStackTraces generuje dane ze ledzenia stosu dla wszystkich wtkw.
Poniej znajduje si przykad jej uycia:
Map<Thread, StackTraceElement[]> map = Thread.getAllStackTraces();
for (Thread t : map.keySet())
{
StackTraceElement[] frames = map.get(t);
analiza ramek
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

582

Java. Podstawy
Wicej informacji na temat interfejsu Map i wtkw znajduje si w rozdziaach 13. i 14.
Listing 11.1 drukuje stos wywoa rekursywnej funkcji obliczajcej silni. Na przykad wywoanie factorial(3) zwrci nastpujcy wynik:
factorial(3):
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.main(StackTraceTest.java:34)
factorial(2):
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.factorial(StackTraceTest.java:24)
StackTraceTest.main(StackTraceTest.java:34)
factorial(1):
StackTraceTest.factorial(StackTraceTest.java:18)
StackTraceTest.factorial(StackTraceTest.java:24)
StackTraceTest.factorial(StackTraceTest.java:24)
StackTraceTest.main(StackTraceTest.java:34)
return 1
return 2
return 6

Listing 11.1. stackTrace/StackTraceTest.java


package stackTrace;
import java.util.*;
/**
* Program wywietlajcy stos wywoa wywoania rekursywnej metody.
* @version 1.01 2004-05-10
* @author Cay Horstmann
*/
public class StackTraceTest
{
/**
* Oblicza silni liczby.
* @param n nieujemna liczba cakowita
* @return n! = 1 * 2 * . . . * n
*/
public static int factorial(int n)
{
System.out.println("factorial(" + n + "):");
Throwable t = new Throwable();
StackTraceElement[] frames = t.getStackTrace();
for (StackTraceElement f : frames)
System.out.println(f);
int r;
if (n <= 1) r = 1;
else r = n * factorial(n - 1);
System.out.println("return " + r);
return r;
}
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Wpisz n: ");
int n = in.nextInt();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

factorial(n);

java.lang.Throwable 1.0

Throwable(Throwable cause) 1.4

Throwable(String message, Throwable cause) 1.4

Tworzy obiekt typu Throwable z okrelonym powodem cause.

Throwable initCause(Throwable cause) 1.4

Ustawia powd cause dla obiektu lub zgasza wyjtek, jeli obiekt ten ma ju powd.
Zwraca this.

Throwable getCause() 1.4

Zwraca obiekt wyjtku, ktry zosta ustawiony jako powd tego obiektu,
lub warto null, jeli aden powd nie zosta ustawiony.

StackTraceElement[] getStackTrace() 1.4

Pobiera dane ze ledzenia stosu w czasie, kiedy zosta utworzony obiekt.

void addSuppressed(Throwable t) 7

Dodaje stumiony wyjtek do wyjtku. Ma to miejsce w instrukcji try z zasobami,


gdzie t jest wyjtkiem zgoszonym przez metod close.

Throwable[] getSuppressed() 7

Pobiera wszystkie stumione wyjtki wyjtku. Najczciej s to wyjtki zgoszone


przez metod close w instrukcji try z zasobami.
java.lang.Exception 1.0

Exception(Throwable cause) 1.4

Exception(String message, Throwable cause)

Tworzy obiekt typu Exception z okrelonym powodem.


java.lang.RuntimeException 1.0

RuntimeException(Throwable cause) 1.4

RuntimeException(String message, Throwable cause) 1.4

Tworzy wyjtek typu RuntimeException z okrelonym powodem.


java.lang.StackTraceElement 1.4

String getFileName()

Zwraca nazw pliku rdowego zawierajcego punkt wykonania elementu


lub warto null, jeli informacja ta jest niedostpna.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

583

584

Java. Podstawy

int getLineNumber()

Zwraca numer wiersza w pliku rdowym zawierajcym punkt wykonania


elementu lub warto -1, jeli informacja ta jest niedostpna.

String getClassName()

Zwraca nazw klasy z penym kwalifikatorem, zawierajcej punkt wykonania


elementu.

String getMethodName()

Zwraca nazw metody zawierajcej punkt wykonania elementu. Nazwa konstruktora


to <init>. Nazwa statycznego inicjatora to <clinit>. Nie mona odrni
przeadowanych metod o tej samej nazwie.

boolean isNativeMethod()

Zwraca warto true, jeli punkt wykonania elementu znajduje si w metodzie


rodzimej.

String toString()

Zwraca sformatowany acuch zawierajcy nazw metody i klasy oraz nazw


pliku i numer wiersza, jeli dane te s dostpne.

11.3. Wskazwki dotyczce stosowania wyjtkw


Zdania na temat prawidowego stosowania wyjtkw s podzielone. Niektrzy programici
uwaaj wszystkie wyjtki kontrolowane za niepotrzebny ciar, inni za sprawiaj wraenie,
jakby nie mogli si nimi nacieszy. Naszym zdaniem wyjtki (nawet kontrolowane) maj swj
obszar zastosowa. Ponisze wskazwki dotycz ich poprawnego wykorzystania.
1.

Obsuga wyjtkw nie moe zastpi prostych testw.


Aby to udowodni, napisalimy program, ktry prbuje 10 000 000 razy pobra
element z pustego stosu. Najpierw sprawdzilimy, czy stos jest pusty.
if (!s.empty()) s.pop();

Nastpnie pobralimy element bez wzgldu na warunki. W wyniku tego


przechwycilimy wyjtek informujcy nas, e nie powinnimy byli tego robi.
try()
{
s.pop();
}
catch (EmptyStackException e)
{
}

Zmierzylimy czas obu tych operacji i uzyskalimy nastpujce wyniki: wersja


z isEmpty zostaa wykonana w 646 milisekund, natomiast wersja przechwytujca
wyjtek EmptyStackException dziaaa 21 739 milisekund.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

585

Jak wida, przechwycenie wyjtku zajo znacznie wicej czasu ni przeprowadzenie


prostego testu. Wniosek: wyjtkw uywaj wycznie w sytuacjach wyjtkowych.
2. Nie rozdrabniaj si.

Wielu programistw kad instrukcj umieszcza w osobnym bloku try.


OutputStream out;
Stack s;
for (i = 0; i < 100; i++)
{
try
{
n = s.pop();
}
catch (EmptyStackException s)
{
// stos by pusty
}
try
{
out.writeInt(n);
}
catch (IOException e)
{
// problem z zapisem w pliku
}
}

Taki sposb programowania drastycznie zwiksza ilo kodu. Miej na uwadze cel,
ktry chcesz osign. W tym przypadku chcemy pobra 100 liczb ze stosu i zapisa
je w pliku (nie zastanawiaj si po co to tylko dla zabawy). Jeli pojawi si jaki
problem, nic nie moemy zrobi. Jeli stos jest pusty, to si nagle nie zapeni. Jeli
plik zawiera bd, to bd ten si magicznie nie naprawi. W takim razie rozsdnie
by byo umieci cay kod tego zadania w jednym bloku try. Jeli ktre z dziaa
si nie powiedzie, mona zatrzyma cae zadanie.
try
{
for (i = 0; i < 100; i++)
{
n = s.pop();
out.writeInt(n);
}
}
catch (IOException e)
{
// problem z zapisem w pliku
}
catch (EmptyStackException s)
{
// stos by pusty
}

Ten kod jest znacznie bardziej przejrzysty i spenia jedn z zasad obsugi bdw
oddziela normalny tok dziaania od procedur obsugi bdw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

586

Java. Podstawy
3. Waciwie wykorzystuj hierarchi wyjtkw.

Nie generuj zawsze wyjtku RuntimeException. Znajd odpowiedni podklas


lub utwrz wasn.
Nie przechwytuj tylko typu Throwable, poniewa przez to kod jest trudniejszy
do odczytania i utrzymania.
Respektuj rnic midzy wyjtkami kontrolowanymi a niekontrolowanymi.
Te pierwsze s z natury uciliwe i nie naley ich generowa dla bdw logicznych
(na przykad problem z tym ma biblioteka refleksyjna, w ktrej wywoujcy czsto
musi przechwytywa wyjtki, cho wiadomo, e one nigdy nie powstan).
Nie wahaj si zamieni wyjtku jednego typu na inny typ, ktry jest bardziej
odpowiedni. Jeli na przykad przetwarzasz liczb cakowit w pliku, przechwy
wyjtek NumberFormatException i zamie jego typ na podklas klasy IOException
lub swoj wasn.
4. Nie ukrywaj wyjtkw.

W Javie bardzo silna jest pokusa wyciszania wyjtkw. Napisalimy


na przykad metod wywoujc inn metod, ktra z kolei z bardzo maym
prawdopodobiestwem moe spowodowa wyjtek. Kompilator podnosi alarm,
poniewa nie umiecilimy tego wyjtku na licie throws tej metody. Nie chcemy
jednak tego robi, poniewa wtedy kompilator bdzie marudzi o pozostaych
metodach, ktre wywouj t metod. W zwizku z tym wyciszamy ten wyjtek:
public Image loadImage(String s)
{
try
{
kod mogcy spowodowa wyjtek kontrolowany
}
catch (Exception e)
{}
// po kopocie
}

Dziki temu kod przejdzie kompilacj bez problemu. Bdzie dziaa jak naley,
dopki nie wystpi wyjtek, ktry zostanie zignorowany. Jeli uwaasz, e wyjtki
s wane, powi nieco czasu, aby je prawidowo obsuy.
5. Nie bd pobaliwy.

Niektrzy programici czuj obawy przed generowaniem wyjtkw w odpowiedzi


na bdy. Myl, e moe lepiej byoby w zamian zwrci jak warto, kiedy
metoda zostanie wywoana z nieprawidowymi parametrami. Czy na przykad metoda
Stack.pop nie mogaby zwrci wartoci null zamiast wyjtku, kiedy stos jest pusty?
Naszym zdaniem lepiej jest zgosi wyjtek EmptyStackException w punkcie
wystpienia awarii, ni pniej odebra wyjtek NullPointerException.
6. Przekazywanie wyjtkw nie stanowi ujmy.

Wielu programistw czuje si zobowizanych do przechwycenia wszystkich


zgaszanych wyjtkw. Kiedy wywouj metod, ktra moe spowodowa wyjtek,

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

587

np. konstruktor FileInputStream lub metoda readLine, staraj si przechwyci


wszystkie wyjtki, ktre mog wystpi. Czsto jednak lepiej jest taki wyjtek
przekaza:
public void readStuff(String filename) throws IOException

// To nie jest powd


// do wstydu!

{
InputStream in = new FileInputStream(filename);
. . .
}

Metody wyszego poziomu czsto dysponuj lepszymi mechanizmami


informowania uytkownika o bdach lub porzucania nieudanych polece.
Zasady 5. i 6. mona podsumowa sowami generuj wczenie, przechwytuj pno.

11.4. Asercje
Asercje s powszechnie stosowan technik programowania zachowawczego. Zamy, e
mamy pewno, i okrelony warunek, na ktrym polegamy w programie, jest speniony.
Moemy na przykad wykonywa nastpujce dziaanie:
double y = Math.sqrt(x);

Jestemy pewni, e zmienna x nie ma wartoci ujemnej. Moe ona by wynikiem innego
dziaania, ktre nie moe zwraca ujemnych wynikw, lub jest parametrem metody, ktra
przyjmuje tylko wartoci dodatnie. Mimo to wolimy jednak sprawdzi dwa razy, ni pozwoli
wkra si do swoich oblicze nieprawidowym wartociom. Oczywicie jednym z wyj jest
wygenerowanie wyjtku:
if (x < 0) throw new IllegalArgumentException("x < 0");

Niestety ten kod pozostanie w programie nawet po skoczeniu testw. Jeli takich miejsc
jest duo, program bdzie dziaa zauwaalnie wolniej, ni powinien.
Lepszym wyjciem z tej sytuacji s asercje, poniewa mona je automatycznie usun po
zakoczeniu testowania.
W Javie dostpne jest sowo kluczowe assert. Istniej dwa podstawowe sposoby jego stosowania:
assert warunek;

i
assert warunek : wyraenie

Kada z tych wersji sprawdza warunek i zgasza bd AssertionError, a jeli warunek nie
zostanie speniony warto false. W drugiej wersji wyraenie jest przekazywane do konstruktora obiektu AssertionError i zamieniane na acuch komunikatu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

588

Java. Podstawy

Jedynym przeznaczeniem wyraenia jest utworzenie acucha komunikatu. Obiekt


AssertionError nie przechowuje rzeczywistej wartoci wyraenia, a wic nie mona
jej z niego pniej wydoby. W dokumentacji JDK napisano w protekcjonalnym tonie, e
mogoby to skania programistw do prbowania rozwizania problemw w asercjach,
co przeczyoby sensowi istnienia samego narzdzia.

Aby zapewni, e zmienna x nie jest ujemna, uywamy poniszej asercji:


assert x >= 0;

Moemy te przekaza rzeczywist warto x do obiektu AssertionError, aby j pniej


wywietli:
assert x >= 0;

Makro assert w jzyku C zamienia warunek asercji w acuch, ktry jest drukowany w przypadku niespenienia warunku asercji. Jeli na przykad warunek
assert(x>=0) nie zostanie speniony, zostanie wydrukowane, e warunek, ktry nie zosta
speniony, to x >= 0. W Javie warunek nie staje si automatycznie czci komunikatu
o bdzie. Aby go zobaczy, trzeba go przekaza jako acuch do obiektu Assertion
Error: x>=0 : "x>=0".

11.4.1. Wczanie i wyczanie asercji


Przy standardowych ustawieniach asercje s wyczone. Aby je wczy, naley przy uruchamianiu programu uy opcji -enableassertions, w skrcie -ea:
java -enableassertions MyApp

Pamitajmy, e, aby wczy lub wyczy asercje, nie trzeba ponownie kompilowa programu.
Funkcja ta naley do moduu adujcego klasy (ang. class loader). Kiedy asercje s wyczone, modu ten usuwa ich kod, aby nie spowalniay programu.
Asercje mona nawet wczy tylko w wybranej klasie lub pakiecie. Na przykad:
java -ea:MyClass -ea:com.mycompany.mylib... MyApp

Powysze polecenie wcza asercje w klasie MyClass i wszystkich klasach nalecych do


pakietu com.mycompany.mylib oraz jego podpakietw. Opcja -ea... wcza asercje we wszystkich klasach pakietu domylnego.
Mona te wyczy asercje w wybranych klasach i pakietach. Suy do tego opcja -disa
bleassertions, w skrcie -da:
java -ea:... -da:MyClass MyApp

Niektre klasy nie s adowane przez modu adujcy, a bezporednio przez maszyn wirtualn.
Za pomoc opisywanych opcji mona wcza i wycza wybrane asercje take w tych
klasach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

589

Opcje -ea i -da, ktre wczaj i wyczaj wszystkie asercje, nie dziaaj w klasach biblioteki
systemowej. Do wczania asercji w tych klasach suy opcja -enablesystemassertions,
w skrcie -esa.
Stan asercji moduw adujcych mona take kontrolowa z poziomu programu. Wicej
informacji na ten temat znajduje si w wycigach z API na kocu tego podrozdziau.

11.4.2. Zastosowanie asercji do sprawdzania parametrw


W Javie dostpne s trzy mechanizmy dziaania w sytuacjach awaryjnych:

generowanie wyjtkw,

dzienniki,

asercje.

Kiedy naley stosowa asercje:

W przypadkach awarii, ktrych nie da si naprawi.

Asercje s wczane wycznie w fazie rozwoju i testw (czasami artuje si,


e przypomina to zakadanie kapoka, bdc w pobliu brzegu, i zrzucanie
go po wypyniciu na pene morze)

W zwizku z tym asercji nie naley stosowa do sygnalizowania innym czciom programu
bdw, ktre mona naprawi, lub informowania uytkownika o problemach. Asercji naley
uywa wycznie do lokalizacji bdw wewntrznych powstajcych w fazie testowej.
Przeanalizujmy typowy scenariusz sprawdzanie parametrw metody. Czy do sprawdzenia
niedozwolonych indeksw lub referencji null powinnimy uy asercji? Aby odpowiedzie
na to pytanie, trzeba zajrze do dokumentacji tej metody. Zamy, e implementujemy metod
sortujc.
/**
Sortuje okrelony zakres danych wskazanej tablicy w rosncej kolejnoci liczbowej.
Pocztek sortowanego zakresu wyznacza parametr fromIndex (wcznie), a koniec parametr toIndex
(wycznie).
@param a tablica do posortowania
@param fromIndex indeks pierwszego elementu (wcznie) zakresu do posortowania
@param toIndex indeks ostatniego elementu zakresu do posortowania (wycznie)
@throws IllegalArgumentException, jeli fromIndex > toIndex
@throws ArrayIndexOutOfBoundsException, jeli fromIndex < 0 lub toIndex > a.length
*/
static void sort(int[] a, int fromIndex, int toIndex)

Wedug dokumentacji metoda ta zgasza wyjtek, jeli wartoci indeksw s nieprawidowe.


Dziaanie to stanowi cz umowy zawartej pomidzy metod a wywoujcym t metod.
Implementujc j, musimy stosowa si do postanowie tej umowy i zgasza wymienione
wyjtki. Nie byoby waciwe uycie w zamian asercji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

590

Java. Podstawy
Czy powinnimy utworzy asercj zapewniajc, e a nie ma wartoci null? Nie. Dokumentacja milczy, jeli chodzi o zachowanie metody, kiedy a ma warto null. Wywoujcy mog
si spodziewa, e w takim przypadku zwrci ona warto, a nie zgosi bd niespenienia
warunku asercji.
Zobaczmy jednak, jak by wygldaa sytuacja, gdyby umowa bya nieco inna:
@param a tablica do posortowania (nie moe by null)

Teraz wywoujcy metod wie, e tej metody nie mona wywoywa na rzecz pustej tablicy.
W zwizku z tym moe si ona zaczyna od asercji:
assert a != null;

W informatyce ten rodzaj umowy nazywa si warunkiem wstpnym (ang. precondition).


Pierwotna wersja metody nie posiadaa adnych warunkw wstpnych dotyczcych parametrw zapewniaa przewidywalne zachowanie we wszystkich sytuacjach. Zmodyfikowana
wersja zawiera jeden warunek wstpny a nie moe mie wartoci null. Jeli wywoujcy
nie speni tego warunku, wszystkie zaoenia przestaj dziaa i metoda moe zrobi, co zechce.
W rzeczywistoci, kiedy zastosujemy asercj, dziaanie metody w przypadku nieprawidowego
wywoania jest nieprzewidywalne. Moe zgosi bd asercji albo wyjtek NullPointer
Exception, w zalenoci od ustawie moduu adujcego klasy.

11.4.3. Zastosowanie asercji do dokumentowania zaoe


Wielu programistw uywa komentarzy do dokumentowania swoich zaoe. Spjrzmy na
przykad z pliku http://docs.oracle.com/javase/6/docs/technotes/guides/language/assert.html:
if (i % 3 == 0)
. . .
else if (i % 3 == 1)
. . .
else
// (i % 3 == 2)
. . .

W tym przypadku rozsdnie byoby skorzysta z asercji:


if (i % 3 == 0)
. . .
else if (i % 3 == 1)
. . .
else
{
assert i % 3 == 2;
. . .
}

Oczywicie jeszcze lepiej byoby przemyle to dokadniej. Jakie s moliwe wartoci dziaania i % 3? Jeli i jest liczb dodatni, reszta moe wynosi 0, 1 lub 2. Jeli i jest liczb ujemn,
reszta moe mie warto -1 lub -2. W zwizku z tym prawdziwe zaoenie mwi, e i nie
moe by liczb ujemn. Lepsza byaby nastpujca asercja przed instrukcj if:
assert i >= 0;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

591

W kadym razie ten przykad pokazuje dobry sposb uycia asercji do sprawdzenia samego
siebie przez programist. Jak wida, asercje s taktycznym narzdziem uywanym w testowaniu i debugowaniu. Natomiast rejestracja danych w dzienniku jest narzdziem strategicznym uywanym w caym cyklu ycia programu. Dziennikami zajmiemy si w kolejnym podrozdziale.
java.lang.ClassLoader 1.0

void setDefaultAssertionStatus(boolean b) 1.4

Wcza lub wycza asercje we wszystkich klasach adowanych przez modu


adujcy. Ustawienie to moe zosta przesonite na poziomie konkretnego
pakietu lub konkretnej klasy.

void setClassAssertionStatus(String className, boolean b) 1.4

Wcza lub wycza asercje w danej klasie i jej klasach wewntrznych.

void setPackageAssertionStatus(String packageName, boolean b) 1.4

Wcza lub wycza asercje we wszystkich klasach w danym pakiecie


i ich podklasach.

void clearAssertionStatus() 1.4

Usuwa wszystkie ustawienia klasowe i pakietowe stanu asercji oraz wycza


wszystkie asercje w klasach adowanych przez modu.

11.5. Dzienniki
adnemu programicie nie jest obce wstawianie instrukcji System.out.println do kodu sprawiajcego problemy w celu uzyskania wgldu w dziaanie programu. Po odkryciu rda
problemw instrukcj tak usuwamy, aby uy jej ponownie, gdy wystpi kolejny problem. API
Logging ma za zadanie zlikwidowa t niedogodno. Poniej znajduje si lista jego najwaniejszych zalet:

Mona atwo wyczy rejestracj wszystkich lub wybranych poziomw rekordw


w dzienniku. Ich ponowne wczenie jest rwnie atwe.

Wyczanie rejestracji danych jest bardzo mao kosztowne, co znaczy,


e pozostawiony w programie kod rejestrujcy powoduje minimalne opnienia.

Rekordy dziennika mona wysya do rnych procedur obsugi, ktre wywietl


go w konsoli, zapisz w pliku itd.

Zarwno rejestratory (ang. logger), jak i procedury obsugi mog filtrowa rekordy.
Filtr odsiewa niepotrzebne dane przy uyciu kryteriw podanych przez programist.

Rekordy w dzienniku mog by formatowane na rne sposoby, na przykad jako


czysty tekst lub XML.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

592

Java. Podstawy

Aplikacja moe posiada wiele obiektw typu Logger (rejestratorw) o hierarchicznej


strukturze nazw, na przykad com.mycompany.myapp, przypominajcej nazwy pakietw.

Domylnie opcje konfiguracyjne rejestracji s zapisywane w pliku konfiguracyjnym.


W razie potrzeby istnieje moliwo zmiany tego mechanizmu w aplikacji.

11.5.1. Podstawy zapisu do dziennika


Zapisu w dzienniku najprociej dokona przy uyciu globalnego rejestratora i jego metody info:
Logger.global.info("Plik->Otwrz - wybrany element menu");

Domylnie rekord jest drukowany nastpujco:


May 10, 2013 10:12:15 PM LoggingImageViewer fileOpen
INFO: Plik->Otwrz - wybrany element menu

Natomiast wywoanie:
Logger.global.setLevel(Level.OFF);

umieszczone w odpowiednim miejscu (na przykad na pocztku metody main) cakowicie


wycza zapis do dziennika.
Dopki nie zostanie poprawiony bd 7184195, globalny rejestrator naley wcza za pomoc wywoania Logger.getGlobal().setLevel(Level.INFO).

11.5.2. Zaawansowane techniki zapisu do dziennika


Znajc podstawy, moemy przej do bardziej zaawansowanych technik. W profesjonalnej
aplikacji nie naley zapisywa wszystkich danych w jednym globalnym rejestratorze. Zamiast
tego definiujemy wasne rejestratory.
Aby utworzy lub pobra rejestrator, naley uy metody getLogger:
private static final Logger myLogger =
Logger.getLogger("com.mycompany.myapp");

Podobnie jak nazwy pakietw, nazwy rejestratorw wykazuj struktur hierarchiczn. W rzeczywistoci s one bardziej hierarchiczne ni pakiety. Pomidzy pakietem a jego przodkiem
nie ma adnych zwizkw semantycznych. Natomiast rejestratory potomne dziel pewne wasnoci ze swoimi przodkami. Jeli na przykad ustawimy priorytet informacji zapisywanych
przez rejestrator com.mycompany, rejestratory potomne odziedzicz ten priorytet.
Istnieje siedem poziomw wanoci komunikatw:

SEVERE,

WARNING,

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

INFO,

CONFIG,

FINE,

FINER,

FINEST.

Wyjtki, dzienniki, asercje i debugowanie

593

Domylnie wczone s trzy pierwsze poziomy. Aby ustawi inny poziom, mona uy poniszej instrukcji:
logger.setLevel(Level.FINE);

Od tej pory wczona jest rejestracja wszystkich poziomw od FINE w gr.


Aby wczy zapis wszystkich poziomw, naley napisa Level.ALL, a aby cakowicie wyczy zapis do dziennika, naley napisa Level.OFF.
Kady z poziomw posiada swoje metody, np.:
logger.warning(message);
logger.fine(message);

Mona te uy metody log z podanym poziomem:


logger.log(Level.FINE, message);

Przy ustawieniach domylnych zapisywane s wszystkie rekordy poziomu INFO lub


wyszego. W zwizku z tym dla komunikatw dotyczcych debugowania naley
uywa poziomw CONFIG, FINE, FINER i FINEST, ktre przydaj si w diagnostyce, ale
s bezuyteczne dla uytkownika.

Zmieniajc poziom zapisu na niszy od INFO, naley pamita o wprowadzeniu


zmian w konfiguracji procedury obsugi dziennika. Domylna procedura obsugi
dziennika tumi wszystkie komunikaty poniej poziomu INFO. Szczegy znajduj si
w kolejnym podrozdziale.

W domylnym rekordzie dziennika znajduje si nazwa klasy i metody zawierajcej wywoanie rejestratora, zgodnie z danymi pobranymi ze stosu wywoa. Jeli jednak maszyna wirtualna zoptymalizuje wykonywanie, precyzyjne informacje mog by niedostpne. Aby uzyska dokadne informacje o lokalizacji klasy i metody odpowiedzialnych za to wywoanie,
mona uy metody logp. Jej sygnatura jest nastpujca:
void logp(Level l, String className, String methodName, String message)

Istniej metody suce do ledzenia przepywu wykonywania:


void
void
void
void
void

entering(String className, String methodName)


entering(String className, String methodName, Object param)
entering(String className, String methodName, Object[] params)
exiting(String className, String methodName)
exiting(String className, String methodName, Object result)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

594

Java. Podstawy
Na przykad:
int read(String file, String pattern)
{
logger.entering("com.mycompany.mylib.Reader", "read",
new Object[] { file, pattern });
. . .
logger.exiting("com.mycompany.mylib.Reader", "read", count);
return count;
}

Te wywoania generuj rekordy dziennika na poziomie FINER, ktre zaczynaj si od acuchw


ENTRY i RETURN.
W przyszoci metody rejestrujce z parametrem Object[] bd obsugiway
zmienne listy parametrw (tzw. varargs). Wtedy bdzie mona tworzy wywoania
typu logger.entering("com.mycompany.mylib.Reader", "read", file, pattern).

Zapis do dziennika jest czsto wykorzystywany w celu rejestrowania nieprzewidywalnych


wyjtkw. S dwie metody, ktre zapisuj w dzienniku opis wyjtku.
void throwing(String className, String methodName, Throwable t)
void log(Level l, String message, Throwable t)

Typowe zastosowania:
if (. . .)
{
IOException exception = new IOException(". . .");
logger.throwing("com.mycompany.mylib.Reader", "read", exception);
throw exception;
}

i
try
{
. . .
}
catch (IOException e)
{
Logger.getLogger("com.mycompany.myapp").log(Level.WARNING, "Reading image", e);
}

Metoda throwing zapisuje rekordy na poziomie FINER i komunikaty zaczynajce si od sowa


THROW.

11.5.3. Zmiana konfiguracji menedera dziennikw


Ustawienia systemu rejestrujcego mona zmieni w specjalnym pliku konfiguracyjnym.
Domylny plik konfiguracyjny to jre/lib/jogging.properties.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

595

Aby uy innego pliku, naley ustawi wasno java.util.logging.config.file na ten plik,


uruchamiajc aplikacj za pomoc nastpujcego polecenia:
java -Djava.util.logging.config.file=configFile MainClass

Meneder dziennikw jest inicjowany razem z maszyn wirtualn, zanim zostanie


wykonana metoda main. Jeli stosujesz wywoanie System.setProperty("java.
util.logging.config.file", file) w metodzie main, to dodatkowo wywoaj metod
LogManager.readConfiguration(), aby ponownie zainicjowa meneder.

Aby zmieni domylny poziom rejestracji, naley w pliku konfiguracyjnym zmieni poniszy
wiersz:
.level=INFO

Poziomy rejestracji mona okreli dla kadego rejestratora osobno:


com.mycompany.myapp.level=FINE

Naley doda przyrostek .level do nazwy rejestratora.


Jak przekonamy si dalej, rejestratory nie wysyaj komunikatw do konsoli, poniewa jest
to zadanie dla obiektw typu Handler. Obiekty te rwnie maj swoje poziomy. Aby w konsoli byy wywietlane komunikaty poziomu FINE, naley zastosowa nastpujce ustawienie:
java.util.logging.ConsoleHandler.level=FINE

Ustawienia menedera dziennikw nie s ustawieniami systemowymi. Uruchomienie


aplikacji za pomoc polecenia -Dcom.mycompany.myapp.level=FINE nie ma adnego wpywu na rejestrator.

Przynajmniej do Java SE 7 w dokumentacji klasy LogManager jest napisane, e


wasnoci java.util.logging.config.class i java.util.logging.config.file
mona ustawia za porednictwem API Preferences. To nieprawda zobacz http://bugs.
sun.com/bugdatabase/.

Plik konfiguracji zapisu do dziennika jest przetwarzany przez klas java.util.


logging.LogManager. Mona wybra inny meneder dziennikw, ustawiajc
wasno systemow java.util.logging.manager na nazw jakiej podklasy. Alternatywnie mona zatrzyma standardowy meneder, omijajc inicjacj z pliku konfiguracji zapisu do dziennika. Naley ustawi wasno systemow java.util.logging.
config.class na klas, ktra ustawia wasnoci menedera dziennikw w inny sposb.
Wicej informacji mona znale w dokumentacji klasy LogManager.

Poziomy rejestracji mona take zmienia w dziaajcym programie za pomoc programu


jconsole. Informacje na ten temat mona znale pod adresem www.oracle.com/technetwork/
articles/java/jconsole-1564139.html#LoggingControl.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

596

Java. Podstawy

11.5.4. Lokalizacja
Czasami konieczna jest lokalizacja komunikatw dziennika, aby byy zrozumiae dla uytkownikw z rnych krajw. Lokalizacji powicilimy rozdzia 5. drugiego tomu. Tutaj krtko
opisujemy, o czym trzeba pamita przy lokalizacji komunikatw dziennika.
Dane lokalizacyjne aplikacji s przechowywane w tak zwanych pakietach lokalizacyjnych
(ang. resource boundle). Pakiet taki zawiera odwzorowania dla poszczeglnych lokalizacji
(jak Stany Zjednoczone czy Niemcy). Na przykad pakiet lokalizacyjny moe odwzorowywa
acuch reading-File na acuchy Reading file po angielsku i Achtung! Datei wird eingele
sen po niemiecku.
Program moe zawiera kilka pakietw lokalizacyjnych, np. jeden dla menu i jeden dla
komunikatw dziennika. Kady pakiet ma swoj nazw (np. com.mycompany.logmessages).
Dodajc odwzorowania do pakietu lokalizacyjnego, naley dostarczy plik dla kadej lokalizacji. Dane dotyczce jzyka angielskiego znajduj si w pliku o nazwie com/mycompany/
logmessages_en.properties, a jzyka niemieckiego w pliku com/mycompany/logmessages_
de.properties (en i de to kody jzykw). Pliki te powinny si znajdowa w tym samym miejscu
co pliki klas aplikacji, aby klasa ResourceBundle moga je automatycznie odszuka. Format tych
plikw to czysty tekst, a ich struktura wyglda nastpujco:
readingFile=Achtung! Datei wird eingelesen
renamingFile=Datei wird umbenannt
...

Pakiet lokalizacyjny mona okreli w chwili uzyskiwania dostpu do rejestratora:


Logger logger = Logger.getLogger(loggerName, "com.mycompany.logmessages");

Nastpnie naley poda klucz pakietu lokalizacyjnego (a nie rzeczywisty acuch komunikatu)
dla komunikatu dziennika.
logger.info("readingFile");

Do lokalizowanych komunikatw czsto trzeba dodawa argumenty. W takich sytuacjach


komunikat powinien zawiera symbole zastpcze {0}, {1} itd. Aby na przykad do komunikatu
dziennika doda nazw pliku, naley wstawi nastpujcy symbol zastpczy:
Reading file {0}.
Achtung! Datei {0} wird eingelesen.

Nastpnie wartoci do symboli zastpczych wstawiamy za pomoc jednego z poniszych


wywoa:
logger.log(Level.INFO, "readingFile", fileName);
logger.log(Level.INFO, "renamingFile", new Object[] { oldName, newName });

11.5.5. Obiekty typu Handler


Domylnie rejestratory wysyaj rekordy do obiektu typu ConsoleHandler, ktry drukuje je
w strumieniu System.err. cilej rzecz biorc, rejestrator wysya rekord do nadrzdnego obiektu
Handler, a przodek najwyszego poziomu (o nazwie "") ma obiekt typu ConsoleHandler.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

597

Podobnie jak rejestratory, obiekty Handler maj poziomy rejestracji. Aby rekord zosta zapisany, jego poziom musi by wyszy ni poziom zarwno rejestratora, jak i obiektu Handler.
Plik konfiguracyjny menedera dziennikw ustawia poziom rejestracji domylnego obiektu
Handler konsoli nastpujco:
java.util.logging.ConsoleHandler.level=INFO

Aby rejestrowa rekordy poziomu FINE, naley zmieni poziom domylnego rejestratora
i obiektu Handler w konfiguracji. Istnieje te moliwo cakowitego pominicia pliku konfiguracyjnego i instalacji wasnego obiektu Handler.
Logger logger = Logger.getLogger("com.mycompany.myapp");
logger.setLevel(Level.FINE);
logger.setUseParentHandlers(false);
Handler handler = new ConsoleHandler();
handler.setLevel(Level.FINE);
logger.addHandler(handler);

Domylnie rejestrator wysya rekordy zarwno do swoich wasnych obiektw Handler, jak
i obiektw Handler swojego przodka. Nasz rejestrator jest potomkiem pierwotnego rejestratora (o nazwie ""), ktry wysya wszystkie rekordy poziomu INFO lub wyszego do
konsoli. Nie chcemy jednak oglda tych rekordw dwa razy. Dlatego ustawiamy wasno
useParentHandlers na false.
Aby wysa rekordy dziennika gdzie indziej, trzeba doda jeszcze jeden obiekt Handler. API
dziennikw udostpnia dwa przydatne typy obiektw Handler : FileHandler i SocketHandler.
Ten drugi wysya rekordy do okrelonego hosta i portu. Nas bardziej interesuje FileHandler,
ktry zapisuje rekordy w plikach.
Aby wysa rekordy do domylnego obiektu Handler zapisujcego do plikw, mona uy
poniszego kodu:
FileHandler handler = new FileHandler();
logger.addHandler(handler);

Rekordy s wysyane do pliku o nazwie javan.log znajdujcego si w katalogu gwnym


uytkownika n to liczba odrniajca poszczeglne pliki. Jeli w systemie (np. Windows
95/98/Me) nie ma czego takiego jak katalog gwny, plik jest zapisywany w domylnej lokalizacji, np. C:\Windows. Domylnym formatem rekordw jest XML. Typowy rekord w dzienniku wyglda nastpujco:
<record>
<date>2002-02-04T07:45:15</date>
<millis>1012837515710</millis>
<sequence>1</sequence>
<logger>com.mycompany.myapp</logger>
<level>INFO</level>
<class>com.mycompany.mylib.Reader</class>
<method>read</method>
<thread>10</thread>
<message>Reading file corejava.gif</message>
</record>

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

598

Java. Podstawy
Domylne zachowanie obiektu Handler zapisujcego do pliku mona zmieni za pomoc
rnych ustawie w konfiguracji menedera dziennikw (tabela 11.1) lub przy uyciu innego
konstruktora (zobacz wycigi z API na kocu tego podrozdziau).

Tabela 11.1. Parametry konfiguracyjne obiektu Handler zapisujcego do pliku


Wasno konfiguracyjna

Opis

Warto domylna

java.util.logging.FileHandler.level

Poziom rejestracyjny obiektu

Level.ALL

Handler
java.util.logging.FileHandler.append

false
Okrela, czy handler powinien
dopisywa dane do istniejcego
pliku, czy dla kadego
uruchomionego program otwiera
nowy plik

java.util.logging.FileHandler.limit

Przybliona maksymalna liczba


bajtw, ktr mona zapisa
w pliku, zanim zostanie otwarty
kolejny (0 oznacza brak limitu)

0 (brak limitu) w klasie


FileHandler,
50000 w konfiguracji

domylnego menedera
dziennikw

java.util.logging.FileHandler.pattern

Wzorzec nazwy pliku dziennika.


Zmienne wzorcw zostay
opisane w tabeli 11.2

%h/java%u.log

java.util.logging.FileHandler.count

Liczba dziennikw w cyklu


rotacyjnym

1 (brak rotacji)

java.util.logging.FileHandler.filter

Klasa filtru, ktra ma zosta


zastosowana

Brak filtru

java.util.logging.FileHandler.encoding

Kodowanie znakw

Kodowanie platformy

java.util.logging.FileHandler.formatter

Formater rekordw

java.util.logging.
XMLFormatter

Zazwyczaj programista nie chce uywa domylnej nazwy pliku dziennika. W tym celu naley
zastosowa inny wzorzec, jak %h/myapp.log (zmienne wzorcw zostay przedstawione
w tabeli 11.2).
Jeli kilka aplikacji (lub kilka kopii jednej aplikacji) uywa tego samego dziennika, naley
wczy znacznik append lub uy zmiennej %u we wzorcu nazwy pliku, aby kada aplikacja
tworzya unikaln kopi dziennika.
Dobrym pomysem jest te wczenie rotacji plikw. Pliki dziennikw s przechowywane
w kolejnoci rotacyjnej myapp.log.0, myapp.log.1, myapp.log.2 itd. Kiedy plik przekroczy
dopuszczalny rozmiar, najstarszy dziennik jest usuwany, pozostae pliki maj zmieniane nazwy
i tworzony jest nowy plik z numerem pokolenia 0.
Istnieje moliwo zdefiniowania wasnego typu Handler poprzez rozszerzenie klasy Handler
lub StreamHandler. Przykad takiego typu prezentujemy w programie demonstracyjnym
zamieszczonym na kocu tego podrozdziau. Wywietla on rekordy w oknie (zobacz rysunek 11.2).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

599

Tabela 11.2. Zmienne wzorcw nazw plikw dziennika


Zmienna

Opis

%h

Warto wasnoci user.home

%t

Katalog tymczasowy systemu

%u

Unikalna liczba pozwalajca unikn konfliktw

%g

Numer pokolenia dziennikw (przyrostek .%g jest uywany, jeli rotacja jest wczona,
a wzorzec nie zawiera zmiennej %g)

%%

Znak %

Wielu programistw wykorzystuje dzienniki jako pomoc dla obsugi technicznej. Jeli
program le dziaa, uytkownik moe odesa dzienniki do sprawdzenia. W takim
przypadku naley wczy znacznik append bd uywa dziennikw rotacyjnych albo jedno
i drugie.
Rysunek 11.2.
Klasa
typu Handler
wywietlajca
rekordy w oknie

Nasza klasa rozszerza klas StreamHandler i instaluje strumie, ktrego metody write drukuj
dane wyjciowe w obszarze tekstowym.
class WindowHandler extends StreamHandler
{
public WindowHandler()
{
. . .
final JTextArea output = new JTextArea();
setOutputStream(new
OutputStream()
{
public void write(int b) {}
// nie jest wywoywana
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
. . .
}

Z metod t zwizany jest tylko jeden problem obiekt Handler buforuje rekordy i wysya je
do strumienia dopiero po zapenieniu bufora. Dlatego przedefiniowalimy metod publish, aby
oprniaa bufor po kadym rekordzie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

600

Java. Podstawy
class WindowHandler extends StreamHandler
{
. . .
public void publish(LogRecord record)
{
super.publish(record);
flush();
}
}

Aby napisa bardziej niezwyk klas Handler dla strumieni, mona rozszerzy klas Handler
i zdefiniowa metody publish, flush oraz close.

11.5.6. Filtry
Domylnie rekordy s filtrowane zgodnie z ich priorytetami. Jednak kady rejestrator i obiekt
Handler moe posiada dodatkowy filtr rekordw. Definicja filtru polega na zaimplementowaniu interfejsu Filter i zdefiniowaniu poniszej metody:
boolean isLoggable(LogRecord record)

Po przeanalizowaniu rekordu dziennika, stosujc dowolne kryteria, zwracamy warto true


dla tych rekordw, ktre powinny zosta dodane do dziennika. Na przykad mona utworzy
filtr akceptujcy tylko komunikaty wygenerowane przez metody entering i exiting. Filtr ten
powinien zatem wywoywa metod record.getMessage() i sprawdza, czy komunikat zaczyna
si od sowa ENTRY lub RETURN.
Instalacja filtru w rejestratorze lub obiekcie Handler sprowadza si do wywoania metody
setFilter. Pamitajmy, e mona uywa tylko jednego filtru naraz.

11.5.7. Formatery
Klasy ConsoleHandler i FileHandler tworz rekordy dziennikw w formacie tekstowym
lub XML. Mona jednak zdefiniowa wasny format. W tym celu naley rozszerzy klas
Formatter i przedefiniowa ponisz metod:
String format(LogRecord record)

Informacje zawarte w rekordzie formatujemy w dowolny sposb i zwracamy powstay acuch.


We wasnej metodzie format moemy wywoa ponisz metod:
String formatMessage(LogRecord record)

Ta metoda formatuje komunikat zawarty w rekordzie, zastpujc parametry i stosujc lokalizacj.


Wiele formatw plikw (np. XML) wymaga, aby formatowane rekordy miay jaki nagwek
i stopk. W takim przypadku naley przesoni ponisze metody:
String getHead(Handler h)
String getTail(Handler h)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

601

Na koniec wywoujemy metod setFormatter, aby zainstalowa formater w obiekcie Handler.

11.5.8. Przepis na dziennik


Przy tak duej liczbie opcji zwizanych z dziennikami atwo zapomnie o podstawach. Poniszy
przepis podsumowuje najczstsze operacje.
1.

W prostej aplikacji uyj jednego rejestratora. Dobrym pomysem jest nadanie


temu rejestratorowi takiej samej nazwy jak gwnemu pakietowi aplikacji,
np. com.mycompany.myprog. Rejestrator mona zawsze utworzy za pomoc
poniszej instrukcji:
Logger logger = Logger.getLogger("com.mycompany.myprog");

Dla wygody do klas, ktre robi duo zapisw, mona doda pola statyczne:
private static final Logger logger = Logger.getLogger("com.mycompany.myprog");

2. Przy domylnych ustawieniach wszystkie komunikaty poziomu INFO lub wyszego

s zapisywane w konsoli. Mona zmieni to domylne ustawienie, ale jak si


przekonalimy wymaga to nieco pracy. W zwizku z tym lepiej jest zastosowa
w aplikacji bardziej rozsdne ustawienie domylne.
Poniszy kod zapewnia, e wszystkie komunikaty s zapisywane do pliku
waciwego dla danej aplikacji. Naley go umieci w metodzie main.
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null)
{
try
{
Logger.getLogger("").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
Handler handler = new FileHandler("%h/myapp.log", 0, LOG_ROTATION_COUNT);
Logger.getLogger("").addHandler(handler);
}
catch (IOException e)
{
logger.log(Level.SEVERE, "Nie mona utworzy handlera pliku dziennika", e);
}
}

3. Teraz jestemy gotowi do zapisywania danych w dzienniku. Pamitajmy, e wszystkie


komunikaty poziomw INFO, WARNING i SEVERE s drukowane w konsoli. Naley zatem

zarezerwowa je dla komunikatw majcych znaczenie dla uytkownikw programu.


Poziom FINE doskonale nadaje si do komunikatw przeznaczonych
dla programistw.
Wszdzie, gdzie kusi uycie metody System.out.println, lepiej utworzy komunikat
dziennika:
logger.fine("Okno dialogowe wyboru pliku zostao anulowane");

Dobrym pomysem jest te zapisywanie w dzienniku niespodziewanych wyjtkw.


Na przykad:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

602

Java. Podstawy
try
{
. . .
}
catch (SomeException e)
{
logger.log(Level.FINE, "objanienie", e);
}

Listing 11.2 pokazuje zastosowanie powyszego przepisu w praktyce z jedn modyfikacj:


komunikaty dziennika s dodatkowo wywietlane w oknie.
Listing 11.2. logging/LoggingImageViewer.java
package logging;
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.io.*;
java.util.logging.*;
javax.swing.*;

/**
* Zmodyfikowana przegldarka grafiki, ktra zapisuje w dzienniku informacje o rnych zdarzeniach
* @version 1.02 2007-05-31
* @author Cay Horstmann
*/
public class LoggingImageViewer
{
public static void main(String[] args)
{
if (System.getProperty("java.util.logging.config.class") == null
&& System.getProperty("java.util.logging.config.file") == null)
{
try
{
Logger.getLogger("com.horstmann.corejava").setLevel(Level.ALL);
final int LOG_ROTATION_COUNT = 10;
Handler handler = new FileHandler("%h/LoggingImageViewer.log", 0,
LOG_ROTATION_COUNT);
Logger.getLogger("com.horstmann.corejava").addHandler(handler);
}
catch (IOException e)
{
Logger.getLogger("com.horstmann.corejava").log(Level.SEVERE,
"Nie mona utworzy obiektu obsugi pliku dziennika", e);
}
}
EventQueue.invokeLater(new Runnable()
{
public void run()
{
Handler windowHandler = new WindowHandler();
windowHandler.setLevel(Level.ALL);
Logger.getLogger("com.horstmann.corejava").addHandler(windowHandler);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

603

JFrame frame = new ImageViewerFrame();


frame.setTitle("LoggingImageViewer");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Logger.getLogger("com.horstmann.corejava").fine("Wywietlanie ramki");
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca obraz
*/
class ImageViewerFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 400;
private JLabel label;
private static Logger logger = Logger.getLogger("com.horstmann.corejava");
public ImageViewerFrame()
{
logger.entering("ImageViewerFrame", "<init>");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Pasek menu
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
JMenuItem openItem = new JMenuItem("Otwrz");
menu.add(openItem);
openItem.addActionListener(new FileOpenListener());
JMenuItem exitItem = new JMenuItem("Zamknij");
menu.add(exitItem);
exitItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
logger.fine("Zamykanie.");
System.exit(0);
}
});
// Etykieta
label = new JLabel();
add(label);
logger.exiting("ImageViewerFrame", "<init>");
}
private class FileOpenListener implements ActionListener
{
public void actionPerformed(ActionEvent event)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

604

Java. Podstawy
{
logger.entering("ImageViewerFrame.FileOpenListener", "actionPerformed",
event);
// Okno wyboru plikw
JFileChooser chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
// Akceptowanie wszystkich plikw z rozszerzeniem .gif
chooser.setFileFilter(new javax.swing.filechooser.FileFilter()
{
public boolean accept(File f)
{
return f.getName().toLowerCase().endsWith(".gif") ||
f.isDirectory();
}
public String getDescription()
{
return "Obrazy GIF";
}
});
// Wywietlanie okna dialogowego wyboru plikw
int r = chooser.showOpenDialog(ImageViewerFrame.this);
// Jeli plik obrazu zosta zaakceptowany, jest on ustawiany jako ikona etykiety
if (r == JFileChooser.APPROVE_OPTION)
{
String name = chooser.getSelectedFile().getPath();
logger.log(Level.FINE, "Wczytywanie pliku {0}", name);
label.setIcon(new ImageIcon(name));
}
else logger.fine("Anulowano okno otwierania pliku.");
logger.exiting("ImageViewerFrame.FileOpenListener", "actionPerformed");
}
}
}
/**
* Klasa obsugi wywietlania rekordw dziennika w oknie
*/
class WindowHandler extends StreamHandler
{
private JFrame frame;
public WindowHandler()
{
frame = new JFrame();
final JTextArea output = new JTextArea();
output.setEditable(false);
frame.setSize(200, 200);
frame.add(new JScrollPane(output));
frame.setFocusableWindowState(false);
frame.setVisible(true);
setOutputStream(new OutputStream()
{
public void write(int b)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

{
} // nie jest wywoywana
public void write(byte[] b, int off, int len)
{
output.append(new String(b, off, len));
}
});
}
public void publish(LogRecord record)
{
if (!frame.isVisible()) return;
super.publish(record);
flush();
}
}
java.util.logging.Logger 1.4

Logger getLogger(String loggerName)

Logger getLogger(String loggerName, String bundleName)

Zwraca rejestrator o podanej nazwie. Jeli rejestrator ten nie istnieje, tworzy go.
Parametry:

loggerName

Hierarchiczna nazwa rejestratora,


np. com.mycompany.myapp

bundleName

Nazwa pakietu zasobu, w ktrym mona szuka


zlokalizowanych tekstw

void severe(String message)

void warning(String message)

void info(String message)

void config(String message)

void fine(String message)

void finer(String message)

void finest(String message)

Zapisuje rekord z poziomem okrelonym przez nazw metody i podanym


komunikatem.

void entering(String className, String methodName)

void entering(String className, String methodName, Object param)

void entering(String className, String methodName, Object[] param)

void exiting(String className, String methodName)

void exiting(String className, String methodName, Object result)

Zapisuje rekord opisujcy uruchamianie lub zamykanie metody o podanych


parametrach lub wartoci zwrotnej.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

605

606

Java. Podstawy

void throwing(String className, String methodName, Throwable t)

Zapisuje rekord opisujcy generowanie danego obiektu wyjtku.

void log(Level level, String message)

void log(Level level, String message, Object obj)

void log(Level level, String message, Object[] objs)

void log(Level level, String message, Throwable t)

Zapisuje rekord z podanym poziomem i komunikatem oraz opcjonalnie dodaje


obiekty typu Throwable. Aby obiekty zostay dodane, komunikat musi zawiera
symbole zastpcze formatu {0}, {1} itd.

void logp(Level level, String className, String methodName,


String message)

void logp(Level level, String className, String methodName,


String message, Object obj)

void logp(Level level, String className, String methodName,


String message, Object[] objs)

void logp(Level level, String className, String methodName,


String message, Throwable t)

Zapisuje rekord o danym poziomie, szczegowe dane wywoujcego i komunikat


oraz opcjonalnie docza obiekty lub obiekt Throwable.

void logrb(Level level, String className, String methodName,


String bundleName, String message)

void logrb(Level level, String className, String methodName,


String bundleName, String message, Object obj)

void logrb(Level level, String className, String methodName,


String bundleName, String message, Object[] objs)

void logrb(Level level, String className, String methodName,


String bundleName, String message, Throwable t)

Zapisuje rekord o danym poziomie, szczegowe dane wywoujcego, nazw


pakietu lokalizacyjnego, komunikat oraz opcjonalnie docza obiekty lub obiekt
Throwable.

Level getLevel()

void setLevel(Level l)

Pobiera i ustawia poziom rejestratora.

Logger getParent()

void setParent(Logger l)

Pobiera i ustawia rejestrator nadrzdny rejestratora.

Handler[] getHandlers()

Zwraca wszystkie obiekty Handler rejestratora.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

void addHandler(Handler h)

void removeHandler(Handler h)

Wyjtki, dzienniki, asercje i debugowanie

607

Dodaje lub usuwa obiekt Handler rejestratora.

boolean getUseParentHandlers()

void setUseParentHandlers(boolean b)

Sprawdza bd ustawia wasno use parent handler. Jeli wasno ta ma warto


true, rejestrator przesya wszystkie zapisane rekordy do obiektw Handler swojego
przodka.

Filter getFilter()

void setFilter(Filter f)

Sprawdza i ustawia filtr dla rejestratora.


java.util.logging.Handler 1.4

abstract void publish(LogRecord record)

Wysya rekord w odpowiednie miejsce.

abstract void flush()

Oprnia bufor.

abstract void close()

Oprnia bufor i zwalnia wszystkie powizane zasoby.

Filter getFilter()

void setFilter(Filter f)

Sprawdza i ustawia filtr dla obiektu Handler.

Formatter getFormatter()

void setFormatter(Formatter f)

Sprawdza i ustawia formater obiektu Handler.

Level getLevel()

void setLevel(Level l)

Sprawdza i ustawia poziom obiektu Handler.


java.util.logging.ConsoleHandler 1.4

ConsoleHandler()

Tworzy nowy obiekt Handler konsoli.


java.util.logging.FileHandler 1.4

FileHandler(String pattern)

FileHandler(String pattern, boolean append)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

608

Java. Podstawy

FileHandler(String pattern, int limit, int count)

FileHandler(String pattern, int limit, int count, boolean append)

Tworzy obiekt Handler zapisujcy do plikw.


Parametry:

pattern

Wzorzec nazwy dziennika. Zmienne tego wzorca


zostay opisane w tabeli 11.2

limit

Przybliona maksymalna liczba bajtw, ktra musi


by zapisana przed otwarciem kolejnego pliku

count

Liczba plikw w cyklu rotacyjnym

append

Warto true oznacza, e nowo utworzony obiekt


Handler zapisu do plikw powinien dodawa dane
do istniejcego pliku dziennika

java.util.logging.LogRecord 1.4

Level getLevel()

Zwraca poziom rekordu.

String getLoggerName()

Zwraca nazw rejestratora, ktry zarejestrowa dany rekord.

ResourceBundle getResourceBundle()

String getResourceBundleName()

Zwraca pakiet lokalizacyjny, ktry ma zosta uyty do lokalizacji komunikatu,


bd jego nazw albo warto null, jeli nie dostarczono adnego pakietu.

String getMessage()

Pobiera surowy komunikat przed lokalizacj lub formatowaniem.

Object[] getParameters()

Zwraca obiekty parametrw lub warto null, jeli nie ma adnego parametru.

Throwable getThrown()

Zwraca wyrzucony obiekt lub warto null, jeli nie ma takiego obiektu.

String getSourceClassName()

String getSourceMethodName()

Zwraca lokalizacj procedury, ktra zarejestrowaa rekord. Informacja ta moe


zosta dostarczona przez procedur rejestrujc lub pobrana bezporednio ze stosu
wywoa. Moe by nieprawidowa, jeli procedura rejestrujca poda nieprawidow
warto lub uruchomiony fragment kodu zosta zoptymalizowany, przez co dokadnej
lokalizacji nie da si sprawdzi.

long getMillis()

Zwraca czas tworzenia w milisekundach, od 1970 roku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

609

long getSequenceNumber()

Zwraca unikalny numer sekwencji rekordu.

int getThreadID()

Zwraca unikalny identyfikator ID wtku, w ktrym zosta utworzony rekord.


Identyfikatory te s przypisywane przez klas LogRecord i nie maj adnego
zwizku z identyfikatorami innych wtkw.
java.util.logging.Filter 1.4

boolean isLoggable(LogRecord record)

Zwraca warto true, jeli dany rekord dziennika powinien zosta zapisany.
java.util.logging.Formatter 1.4

abstract String format(LogRecord record)

Zwraca acuch powstay w wyniku sformatowania danego rekordu.

String getHead(Handler h)

String getTail(Handler h)

Zwraca acuchy, ktre powinny si znajdowa w nagwku i na dole dokumentu


zawierajcego rekordy dziennika. Zgodnie z definicj w nadklasie Formatter metody
te zwracaj pusty acuch. W razie potrzeby naley je przesoni.

String formatMessage(LogRecord record)

Zwraca zlokalizowany i sformatowany komunikat zawarty w rekordzie.

11.6. Wskazwki dotyczce debugowania


Wyobramy sobie, e napisalimy solidny program, w ktrym przechwycilimy i odpowiednio obsuylimy wszystkie wyjtki. Uruchamiamy go i okazuje si, e nie dziaa zgodnie z oczekiwaniami. Co teraz? (Osoby, ktre nigdy nie miay takiego problemu, mog pomin
t cz rozdziau).
Oczywicie najlepiej postara si o dobry i wszechstronny debuger. Kade profesjonalne
rodowisko programistyczne, takie jak Eclipse czy NetBeans, ma wbudowany debuger. Narzdziem tym zajmiemy si dalej, a teraz przedstawiamy kilka wskazwek, ktre mona wyprbowa przed jego wczeniem.
1.

Warto kadej zmiennej mona wydrukowa lub zarejestrowa, uywajc poniszego


kodu:
System.out.println("x=" + x);

lub
Logger.global.info("x=" + x);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

610

Java. Podstawy
Jeli x jest liczb, zostanie przekonwertowany na acuch. Jeli jest obiektem,
zostanie wywoana na jego rzecz metoda toString(). Aby sprawdzi stan obiektu
parametru niejawnego, naley wydrukowa stan obiektu this.
Logger.global.info("this=" + this);

Wikszo klas w bibliotece Javy bardzo sumiennie przesania metod toString,


aby mc zwrci przydatne informacje o danej klasie. Jest to prawdziwy skarb
w trakcie debugowania. Naley to samo robi we wasnych klasach.
2. Jedna sztuczka wydaje si, e mao znana polega na umieszczeniu w kadej
klasie osobnej metody main. W metodzie tej mona umieci namiastk testu

jednostkowego umoliwiajc przetestowanie klasy w odosobnieniu.


public class MyClass
{
metody i pola
. . .
public static void main(String[] args)
{
procedury testowe
}
}

Utwrz kilka obiektw, wywoaj wszystkie metody i sprawd, czy kada z nich
zwraca prawidow warto. Aby uruchomi testy, naley kady z plikw wykona
w maszynie wirtualnej osobno. Przy uruchamianiu apletu adna z tych metod nie
jest w ogle wywoywana. Przy uruchamianiu aplikacji maszyna wirtualna wywouje
tylko metod main klasy uruchomieniowej.
3. Osoby, ktrym spodobaa si poprzednia wskazwka, powinny zainteresowa

si narzdziem JUnit ze strony http://junit.org. Jest to bardzo popularny framework


testowy, ktry uatwia organizacj zestaww przypadkw testowych. Testy powinno
si przeprowadza po wprowadzeniu jakich zmian w klasie, a po znalezieniu bdu
naley doda nowe testy.
4. Rejestrujcy obiekt poredni (ang. logging proxy) to obiekt podklasy, ktry

przechwytuje wszystkie wywoania metod, rejestruje je, a nastpnie wywouje


nadklas. Jeli na przykad mamy problem z metod nextDouble z klasy Random,
moemy utworzy obiekt poredni bdcy egzemplarzem podklasy anonimowej:
Random generator = new
Random()
{
public double nextDouble()
{
double result = super.nextDouble();
Logger.getGlobal().info("nextDouble: " + result);
return result;
}
};

Przy kadym wywoaniu metody getDouble tworzony jest rekord w dzienniku.


Aby dowiedzie si, kto wywoa metod, naley sprawdzi stos wywoa.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

5. Stos mona uzyska z kadego obiektu wyjtku za pomoc metody printStackTrace


z klasy Throwable. Poniszy fragment programu przechwytuje wszystkie wyjtki,

drukuje ich obiekty i lad stosu oraz ponownie je generuje, aby mogy odnale
swoj procedur obsugi.
try
{
. . .
}
catch (Throwable t)
{
t.printStackTrace();
throw t;
}

Aby wygenerowa lad stosu, nie trzeba nawet przechwytywa wyjtku. Wystarczy
ponisza instrukcja w dowolnym miejscu programu:
Thread.dumpStack();

6. Normalnie dane ze ledzenia stosu s wysyane do strumienia System.err. Mona


uy metody void printStackTrace(PrintWriter s), aby zapisa je w pliku. Aby

zarejestrowa lub wywietli dane ze ledzenia stosu, mona uy poniszego kodu:


ByteArrayOutputStream out = new ByteArrayOutputStream();
new Throwable().printStackTrace(out);
String description = out.toString();

7. Czsto wygodnym rozwizaniem jest zapisanie bdw programu w pliku. S one


jednak wysyane do strumienia System.err, a nie System.out, przez co nie mona

ich zapisa przy uyciu poniszego polecenia:


java MyProgram > errors.txt

Zamiast tego naley przechwyci strumie bdw jako:


java MyProgram 2> errors.txt

Aby zapisa zarwno strumie System.err, jak i System.out w jednym pliku, naley
uy nastpujcego polecenia:
java MyProgram >& errors.txt

Sposb ten dziaa w powoce bash i Windows.


8. Zapisywanie danych ze ledzenia stosu nieprzechwyconych wyjtkw w strumieniu
System.err nie jest idealnym rozwizaniem. Komunikaty te wprowadzaj zamt

u uytkownikw kocowych, ktrzy je przez przypadek zobacz, i nie s dostpne


do celw diagnostycznych, kiedy s potrzebne. Lepiej jest zapisywa je w pliku.
Mona te zmieni procedur obsugi nieprzechwyconych wyjtkw za pomoc
metody Thread.setDefaultUncaughtExceptionHandler:
Thread.setDefaultUncaughtExceptionHandler(
new Thread.UncaughtExceptionHandler()
{
public void uncaughtException(Thread t, Throwable e)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

611

612

Java. Podstawy
zapis informacji w pliku dziennika
};
});

9. Aby zobaczy, jakie klasy s adowane, naley uruchomi maszyn wirtualn


przy uyciu znacznika -verbose. Zostan wydrukowane informacje podobne

do poniszych:
[Opened
[Opened
[Opened
[Opened
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
[Loaded
...

/usr/local/jdk5.0/jre/lib/rt.jar]
/usr/local/jdk5.0/jre/lib/jsse.jar]
/usr/local/jdk5.0/jre/lib/jce.jar]
/usr/local/jdk5.0/jre/lib/charsets.jar]
java.lang.Object from shared objects file]
java.io.Serializable from shared objects file]
java.lang.Comparable from shared objects file]
java.lang.CharSequence from shared objects file]
java.lang.String from shared objects file]
java.lang.reflect.GenericDeclaration from shared objects file]
java.lang.reflect.Type from shared objects file]
java.lang.reflect.AnnotatedElement from shared objects file]
java.lang.Class from shared objects file]
java.lang.Cloneable from shared objects file]

Metoda ta moe czasami pomc w diagnozowaniu problemw ze ciek klas.


10. Opcja -Xlint znajduje najczstsze problemy z kodem. Jeli na przykad program

skompilujemy za pomoc poniszego polecenia:


javac -Xlint:fallthrough

kompilator zgosi brakujce instrukcje break w instrukcjach switch (mianem lint


pierwotnie okrelano narzdzie suce do znajdowania potencjalnych problemw
w programach w jzyku C, a obecnie nazw t stosuje si do narzdzi, ktre
oznaczaj konstrukcje budzce wtpliwoci, ale nie niedozwolone).
Dostpne s nastpujce opcje:
-Xlint lub -Xlint:all Przeprowadza wszystkie testy.
-Xlint:depreciation

Dziaa tak samo jak opcja -depreciation


wyszukuje odradzane metody.

-Xlint:fallthrough

Szuka brakujcych instrukcji break w instrukcjach switch.

-Xlint:finally

Ostrzega o klauzulach finally, ktre nie mog si


normalnie zakoczy.

-Xlint:none

Nie przeprowadza adnego testu.

-Xlint:path

Sprawdza, czy wszystkie katalogi na ciece klas


istniej.

-Xlint:serial

Ostrzega o serializowalnych klasach bez serialVersionID


(zobacz rozdzia 1. drugiego tomu).

-Xlint:unchecked

Ostrzega przed niebezpiecznymi konwersjami pomidzy


typami uoglnionymi a surowymi (zobacz rozdzia 12.).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

613

11. Maszyna wirtualna Javy moe te monitorowa aplikacje i zarzdza nimi. Polega

to na instalacji agentw w maszynie wirtualnej, ktre ledz zuycie pamici,


wykorzystanie wtkw, adowanie klas itd. Funkcja ta jest szczeglnie przydatna
w duych i dugo dziaajcych aplikacjach, takich jak serwery. Demonstracj tych
moliwoci jest dostpne w JDK narzdzie graficzne o nazwie jconsole, ktre
wywietla statystyki dotyczce dziaania maszyny wirtualnej (rysunek 11.3). Naley
znale identyfikator procesu, ktry w systemie operacyjnym obsuguje maszyn
wirtualn. W systemach Unix/Linux naley w tym celu uy narzdzia ps, w systemie
Windows trzeba skorzysta z menedera zada1. Nastpnie uruchamiamy program
jconsole:
jconsole IDprocesu

Rysunek 11.3. Program jconsole

Konsola dostarcza mnstwo informacji na temat uruchomionego programu. Wicej


informacji mona znale na stronie www.oracle.com/technetwork/articles/java/
jconsole-1564139.html.

W systemie Windows po uruchomieniu okna Meneder zada za pomoc klawiszy Ctrl+Alt+Delete naley
przej do karty Procesy i z menu Widok wybra opcj Wybierz kolumny oraz zaznaczy pole PID
(identyfikator procesu) przyp. tum.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

614

Java. Podstawy
12. Za pomoc narzdzia jmap mona sprawdzi, jakie obiekty znajduj si na stercie.

Su do tego nastpujce polecenia:


jmap -dump:format=b,file=nazwaPlikuZrzutu IDprocesu
jhat NazwaPlikuZrzutu

Nastpnie w przegldarce naley wpisa adres localhost:7000. Umoliwia to dostp


do zawartoci sterty w czasie zrzutu.
13. Uruchomienie maszyny wirtualnej ze znacznikiem -Xprof powoduje wywoanie

prostego narzdzia profilujcego, ktre ledzi najczciej uruchamiane metody


w kodzie. Informacje te s wysyane do strumienia System.out i informuj midzy
innymi o tym, ktre metody zostay skompilowane przez kompilator JIT.
Opcje -X kompilatora nie s oficjalnie obsugiwane i mog by niedostpne w niektrych wersjach JDK. Aby sprawdzi, jakie niestandardowe opcje s dostpne,
naley uy polecenia java -X.

11.7. Wskazwki dotyczce


debugowania aplikacji z GUI
W tej czci rozdziau zamiecilimy kilka dodatkowych porad na temat znajdowania bdw
w programach z graficznym interfejsem uytkownika. Pniej pokaemy, jak zautomatyzowa testy GUI za pomoc robota AWT.
1.

Jeli kiedykolwiek zdarzyo Ci si spojrze na okno Swinga i zastanawia, jak jego


twrca uzyska tak dobry efekt, moesz uzyska nieco informacji. Nacinij
kombinacj klawiszy Ctrl+Shift+F1, aby wywietli wydruk wszystkich
komponentw w hierarchii:
FontDialog[frame0,0,0,300x200,layout=java.awt.BorderLayout,...
javax.swing.JRootPane[,4,23,292x173,layout=javax.swing.JRootPane$RootLayout,...
javax.swing.JPanel[null.glassPane,0,0,292x173,hidden,layout=java.awt.FlowLayout,...
javax.swing.JLayeredPane[null.layeredPane,0,0,292x173,...
javax.swing.JPanel[null.contentPane,0,0,292x173,layout=java.awt.GridBagLayout,...
javax.swing.JList[,0,0,73x152,alignmentX=null,alignmentY=null,...
javax.swing.CellRendererPane[,0,0,0x0,hidden]
javax.swing.DefaultListCellRenderer$UIResource[,-73,-19,0x0,...
javax.swing.JCheckBox[,157,13,50x25,layout=javax.swing.OverlayLayout,...
javax.swing.JCheckBox[,156,65,52x25,layout=javax.swing.OverlayLayout,...
javax.swing.JLabel[,114,119,30x17,alignmentX=0.0,alignmentY=null,...
javax.swing.JTextField[,186,117,105x21,alignmentX=null,alignmentY=null,...
javax.swing.JTextField[,0,152,291x21,alignmentX=null,alignmentY=null,...

2. Osoby majce problemy z prawidow prezentacj komponentw Swing polubi

narzdzie Swing graphics debugger. Nawet jeli nie piszemy wasnych klas
komponentw, ciekawe i ksztacce jest podejrzenie, jak dokadnie rysowana jest

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

615

zawarto komponentu. Aby wczy debugowanie komponentu Swing, naley


uy metody setDebugGraphicsOptions z klasy JComponent. Dostpne s nastpujce
opcje:
DebugGraphics.FLASH_OPTION

Zabarwia na chwil na czerwono kad lini,


prostokt i fragment tekstu przed ich
narysowaniem.

DebugGraphics.LOG_OPTION

Drukuje komunikat dotyczcy kadej operacji


rysowania.

DebugGraphics.BUFFERED_OPTION Wywietla operacje, ktre s wykonywane

w buforze pozaekranowym.
DebugGraphics.NONE_OPTION

Wycza debugowanie grafiki.

Odkrylimy, e aby opcja FLASH dziaaa, trzeba wyczy double buffering,


funkcj Swing majc na celu redukcj migotania podczas aktualizacji okna.
Magiczne zaklcie wczajce opcj FLASH brzmi nastpujco:
RepaintManager.currentManager(getRootPane()).setDoubleBufferingEnabled(false);
((JComponent) getContentPane()).setDebugGraphicsOptions(DebugGraphics.FLASH_OPTION);

Kod ten naley umieci na kocu konstruktora ramki. W czasie dziaania programu
panel gwny bdzie si zapenia w zwolnionym tempie. Bardziej precyzyjne
debugowanie mona wykona, wywoujc metod setDebugGraphicsOptions
na rzecz jednego komponentu. Mona ustawi czas, liczb i kolor byskw
szczegy na ten temat mona znale w dokumentacji internetowej metody
DebugGraphics.
3. Jeli chcesz otrzyma rejestr wszystkich zdarze AWT wygenerowanych w aplikacji

z GUI, moesz w kadym komponencie emitujcym zdarzenia zainstalowa


suchacza. Dziki refleksji mona to atwo zautomatyzowa. Na listingu 11.3
przedstawiony jest kod rdowy klasy EventTracer.
Aby ledzi komunikaty, naley doda komponent, ktrego zdarzenia maj by
ledzone, do obiektu ledzcego zdarzenia:
EventTracer tracer = new EventTracer();
tracer.add(frame);

Spowoduje to wydrukowanie opisu tekstowego wszystkich zdarze (rysunek 11.4).


Listing 11.3. eventTracer/EventTracer.java
package eventTracer;
import java.awt.*;
import java.beans.*;
import java.lang.reflect.*;
/**
* @version 1.31 2004-05-10
* @author Cay Horstmann
*/
public class EventTracer
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

616

Java. Podstawy

Rysunek 11.4. Klasa EventTracer w akcji


private InvocationHandler handler;
public EventTracer()
{
// Handler wszystkich obiektw porednich zdarze
handler = new InvocationHandler()
{
public Object invoke(Object proxy, Method method, Object[] args)
{
System.out.println(method + ":" + args[0]);
return null;
}
};
}
/**
* Dodawanie obiektw ledzcych zdarzenia dla wszystkich zdarze, ktrych ten komponent i jego
* potomkowie mog nasuchiwa
* @param c a komponent
*/
public void add(Component c)
{
try
{
// Pobranie wszystkich zdarze, ktrych ten komponent moe nasuchiwa
BeanInfo info = Introspector.getBeanInfo(c.getClass());
EventSetDescriptor[] eventSets = info.getEventSetDescriptors();
for (EventSetDescriptor eventSet : eventSets)
addListener(c, eventSet);
}
catch (IntrospectionException e)
{
}
// W razie wystpienia wyjtku nie dodawa suchaczy
if (c instanceof Container)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

617

{
// Pobranie wszystkich potomkw i rekursywne wywoanie metody add
for (Component comp : ((Container) c).getComponents())
add(comp);
}
}
/**
* Dodanie suchacza do danego zbioru zdarze
* @param c a komponent
* @param eventSet deskryptor interfejsu nasuchujcego
*/
public void addListener(Component c, EventSetDescriptor eventSet)
{
// Utworzenie obiektu poredniego dla tego typu suchaczy i przekazanie wszystkich wywoa
// do handlera
Object proxy = Proxy.newProxyInstance(null, new Class[] {
eventSet.getListenerType() },
handler);
// Dodanie obiektu poredniego jako suchacza do komponentu
Method addListenerMethod = eventSet.getAddListenerMethod();
try
{
addListenerMethod.invoke(c, proxy);
}
catch (ReflectiveOperationException e)
{
}
// W razie wystpienia wyjtku nie dodawa suchaczy
}
}

11.7.1. Zaprzganie robota AWT do pracy


Klasa Robot umoliwia wysyanie zdarze nacinicia klawiszy i kliknicia przyciskiem
myszy do dowolnego programu AWT. Przeznaczeniem tej klasy jest umoliwienie automatycznego testowania interfejsw uytkownika.
Aby utworzy robota, najpierw trzeba utworzy obiekt klasy GraphicsDevice. Aby uzyska
domylne urzdzenie ekranowe, mona uy poniszych instrukcji:
GraphicsEnvironment environment = GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screen = environment.getDefaultScreenDevice();

Nastpnie tworzymy robota:


Robot robot = new Robot(screen);

Aby wysa zdarzenie nacinicia klawisza, naley kaza robotowi, aby zasymulowa nacinicie i zwolnienie klawisza:
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

618

Java. Podstawy
W przypadku myszy najpierw naley ni poruszy, a potem nacisn i zwolni przycisk:
robot.mouseMove(x, y);
// x i y to bezwzgldne wsprzdne piksela na ekranie.
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);

Wszystko sprowadza si do symulowania zdarze klawiatury i myszy oraz robienia zrzutw


ekranu, aby sprawdzi, czy aplikacja zachowaa si w odpowiedni sposb. Do robienia zrzutw
ekranu suy metoda createScreenCapture:
Rectangle rect = new Rectangle(x, y, width, height);
BufferedImage image = robot.createScreenCapture(rect);

Wsprzdne prostokta (ang. rectangle) s take bezwzgldne.


Pomidzy kolejnymi instrukcjami robota powinna by krtka przerwa, aby aplikacja moga
nady. Do tego celu mona uy metody delay, ktrej parametrem jest liczba milisekund
opnienia. Na przykad:
robot.delay(1000);

// opnienie o 1000 milisekund

Program przedstawiony na listingu 11.4 demonstruje sposb wykorzystania robota. Ten robot
testuje program ButtonTest z rozdziau 8. Najpierw nacinicie spacji aktywuje pierwszy
przycisk. Nastpnie robot odczekuje dwie sekundy, aby mona byo zobaczy, co si stao.
Nastpnie symuluje klawisz Tab i kolejne nacinicie spacji powodujce nacinicie kolejnego przycisku. Na zakoczenie symulujemy kliknicie przyciskiem myszy trzeciego przycisku (moliwe, e bdzie trzeba dostosowa wsprzdne x i y w programie, aby przycisk by
rzeczywicie wciskany). Program koczy si zrobieniem zrzutu ekranu i wywietleniem go
w dodatkowej ramce (rysunek 11.5).
Robot powinien dziaa w osobnym wtku, jak w przykadowym kodzie. Wicej infor
macji o wtkach znajduje si w rozdziale 14.

Przykad ten pokazuje jasno, e klasa Robot sama w sobie nie jest wygodnym sposobem testowania interfejsu uytkownika. Moe natomiast suy jako podstawa narzdzia testujcego.
Profesjonalne narzdzie powinno przechwytywa, zapisywa i powtarza scenariusze zachowania uytkownika oraz znajdowa lokalizacje komponentw na ekranie, dziki czemu miejsca klikni mysz nie s wybierane na drodze prb i bdw.
Listing 11.4. robot/RobotTest.java
package robot;
import
import
import
import

java.awt.*;
java.awt.event.*;
java.awt.image.*;
javax.swing.*;

/**
* @version 1.04 2012-05-17
* @author Cay Horstmann
*/
public class RobotTest

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

Rysunek 11.5.
Zrzut ekranu
zrobiony przez
robota AWT

{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
// Ramka z panelem zawierajcym przycisk
ButtonFrame frame = new ButtonFrame();
frame.setTitle("ButtonTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
// Powizanie robota z ekranem
GraphicsEnvironment environment =
GraphicsEnvironment.getLocalGraphicsEnvironment();
GraphicsDevice screen = environment.getDefaultScreenDevice();
try
{
final Robot robot = new Robot(screen);
robot.waitForIdle();
new Thread()
{
public void run()
{
runTest(robot);
};
}.start();
}
catch (AWTException e)
{
e.printStackTrace();
}
}
/**
* Uruchamia procedur testow
* @param robot robot zwizany z ekranem
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

619

620

Java. Podstawy
public static void runTest(Robot robot)
{
// Symulacja nacinicia spacji
robot.keyPress(' ');
robot.keyRelease(' ');
// Symulacja nacinicia klawisza Tab i spacji
robot.delay(2000);
robot.keyPress(KeyEvent.VK_TAB);
robot.keyRelease(KeyEvent.VK_TAB);
robot.keyPress(' ');
robot.keyRelease(' ');
// Symulacja kliknicia mysz prawego przycisku w oknie
robot.delay(2000);
robot.mouseMove(220, 40);
robot.mousePress(InputEvent.BUTTON1_MASK);
robot.mouseRelease(InputEvent.BUTTON1_MASK);
// Zrobienie zrzutu ekranu i wywietlenie obrazu
robot.delay(2000);
BufferedImage image = robot.createScreenCapture(new Rectangle(0, 0, 400, 300));
ImageFrame frame = new ImageFrame(image);
frame.setVisible(true);
}
}
/**
* Ramka zawierajca wywietlany obraz
*/
class ImageFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 450;
private static final int DEFAULT_HEIGHT = 350;
/**
* @param image obraz do wywietlenia
*/
public ImageFrame(Image image)
{
setTitle("Zrzut ekranu");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
JLabel label = new JLabel(new ImageIcon(image));
add(label);
}
}
java.awt.GraphicsEnvironment 1.2

static GraphicsEnvironment getLocalGraphicsEnvironment()

Zwraca lokalne rodowisko graficzne.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

621

GraphicsDevice getDefaultScreenDevice()

Zwraca domylne urzdzenie z ekranem. Naley pamita, e w komputerach,


do ktrych podczono kilka monitorw, na kady ekran przypada jedno urzdzenie
graficzne do utworzenie tablicy wszystkich urzdze ekranowych mona uy
metody getScreenDevices.
java.awt.Robot 1.3

Robot(GraphicsDevice device)

Tworzy robota, ktry moe wsppracowa z danym urzdzeniem.

void keyPress(int key)

void keyRelease(int key)

Symuluje nacinicie lub zwolnienie klawisza.


Parametr:

key

Kod klawisza. Wicej informacji na temat kodw


klawiszy mona znale w opisie klasy KeyStroke.

void mouseMove(int x, int y)

Symuluje ruch myszk.


Parametry:

x, y

Pooenie kursora myszy wyraone wsprzdnymi


bezwzgldnymi.

void mousePress(int eventMask)

void mouseRelease(int eventMask)

Symuluje nacinicie i zwolnienie przycisku myszy.


Parametr:

eventMask

Maska zdarzenia opisujca przyciski myszy. Wicej


informacji na temat masek zdarze znajduje si
w opisie klasy InputEvent.

void delay(int miliseconds)

Opnia dziaanie robota o okrelon liczb milisekund.

BufferedImage createScreenCapture(Rectangle rect)

Robi zrzut okrelonej czci ekranu.


Parametr:

rect

Prostokt, ktry ma zosta objty w zrzucie ekranu.


Wsprzdne s bezwzgldne.

11.8. Praca z debugerem


Debugowanie polegajce na wstawianiu instrukcji drukujcych nie naley do najwikszych
przyjemnoci w yciu. Trzeba bez przerwy wstawia i usuwa instrukcje, a nastpnie ponownie
kompilowa program. Lepszym rozwizaniem jest uycie debugera. Narzdzie to uruchamia

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

622

Java. Podstawy
program w normalnym tempie i zatrzymuje si w punkcie wstrzymania, w ktrym programista
moe obejrze wszystkie interesujce go dane.
Listing 11.5 przedstawia celowo uszkodzon wersj programu ButtonTest z rozdziau 8.
Klikanie przyciskw w oknie niczego nie zmienia. Spjrzmy na kod kliknicie przycisku
powinno ustawia w tle kolor okrelony w nazwie przycisku.

Listing 11.5. debugger/BuggyButtonTest.java


package debugger;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* @version 1.22 2007-05-14
* @author Cay Horstmann
*/
public class BuggyButtonTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new BuggyButtonFrame();
frame.setTitle("BuggyButtonTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class BuggyButtonFrame extends JFrame
{
private static final int DEFAULT_WIDTH = 300;
private static final int DEFAULT_HEIGHT = 200;
public BuggyButtonFrame()
{
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
// Dodanie panelu do ramki
BuggyButtonPanel panel = new BuggyButtonPanel();
add(panel);
}
}
class BuggyButtonPanel extends JPanel
{
public BuggyButtonPanel()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

623

ActionListener listener = new ButtonListener();


JButton yellowButton = new JButton("ty");
add(yellowButton);
yellowButton.addActionListener(listener);
JButton blueButton = new JButton("Niebieski");
add(blueButton);
blueButton.addActionListener(listener);
JButton redButton = new JButton("Czerwony");
add(redButton);
redButton.addActionListener(listener);
}
private class ButtonListener implements ActionListener
{
public void actionPerformed(ActionEvent event)
{
String arg = event.getActionCommand();
if (arg.equals("ty")) setBackground(Color.yellow);
else if (arg.equals("niebieski")) setBackground(Color.blue);
else if (arg.equals("czerwony")) setBackground(Color.red);
}
}
}

W tak krtkim programie znalezienie bdu moe by moliwe dziki samemu przeczytaniu
kodu rdowego. Zamy jednak, e analiza kodu rdowego nie jest praktycznym rozwizaniem. Nauczysz si teraz lokalizowa bdy za pomoc debugera dostpnego w rodowisku
Eclipse.
Jeli uywasz niezalenego debugera, takiego jak JSwat (http://code.google.com/
p/jswat/), czy sdziwego i niezwykle niezdarnego jdb, musisz skompilowa swj
program z opcj -g. Na przykad:
javac -g BuggyButtonTest.java

W rodowisku zintegrowanym odbywa si to automatycznie.

W Eclipse debuger uruchamia opcja menu Run/Debug As/Java Application.


Ustawimy punkt wstrzymania (ang. breakpoint) w pierwszym wierszu metody actionPerfor
med. Kliknij prawym przyciskiem myszy na marginesie znajdujcym si po lewej stronie
wybranego wiersza kodu i z menu podrcznego wybierz opcj Toggle Breakpoint.
Punkt wstrzymania zostanie osignity w chwili, gdy Java dojdzie do metody actionPerformed.
Aby tak si stao, kliknij przycisk ty. Debuger zatrzymuje si na pocztku metody action
Performed rysunek 11.6.
Do uruchamiania programu krok po kroku su dwa polecenia. Opcja Step Into wchodzi
do kadego wywoania metody. Opcja Step Over przechodzi do nastpnego wiersza bez

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

624

Java. Podstawy

Rysunek 11.6. Zatrzymanie w punkcie wstrzymania

wchodzenia do adnych dalszych metod. W Eclipse opcje te to Run/Step Into i Run/Step Over,
a odpowiadajce im skrty klawiaturowe to F5 i F6. Zastosuj dwukrotnie opcj Step Over
i sprawd, gdzie jestemy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 11.

Wyjtki, dzienniki, asercje i debugowanie

625

Planowalimy, e program zrobi co innego wywoa metod setColor(Color.yellow) i z niej


wyjdzie.
Sprawdmy w panelu zmiennych lokalnych warto zmiennej arg:

Wiadomo ju, gdzie tkwi bd. Zmienna arg miaa warto ty, z wielk liter na pocztku,
a program do porwnywania uywa sowa ty pisanego ma liter:
if (arg.equals("ty"))

Aby wyczy debuger, naley w menu Run klikn opcj Terminate.


Eclipse posiada o wiele wicej opcji debugera, ale te podstawowe, ktre zostay omwione,
daj ju cakiem due pole do popisu. Inne debugery, na przykad NetBeans, udostpniaj
bardzo podobne zestawy opcji.
Ten rozdzia stanowi wprowadzenie do obsugi wyjtkw oraz testowania i debugowania.
Kolejne dwa rozdziay powicilimy programowaniu uoglnionemu i najwaniejszemu sposobowi jego zastosowania kolekcjom.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

626

Java. Podstawy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

12

Programowanie oglne
W tym rozdziale:

Dlaczego programowanie oglne

Definicja prostej klasy oglnej

Metody oglne

Ograniczenia zmiennych typowych

Kod generyczny a maszyna wirtualna

Ograniczenia i braki

Zasady dziedziczenia dla typw oglnych

Typy wieloznaczne

Refleksja a typy oglne

Typy oglne (take generyczne lub parametryzowane) stanowi najwiksz zmian w jzyku
Java od chwili jego zaistnienia. Mimo e funkcjonalno t wprowadzono dopiero w Java
SE 5.0, o jej dodanie postulowano w jednym z pierwszych dokumentw JSR (ang. Java
Specification Requests), dokadniej mwic JSR 14 z 1999 roku. Prace nad specyfikacj i testowanie implementacji zajy ekspertom okoo piciu lat.
Zalet programowania oglnego jest moliwo pisania bezpieczniejszego i atwiejszego do
odczytu kodu w porwnaniu do programw usianych zmiennymi Object i konwersjami typw.
Typy oglne s szczeglnie przydatne w klasach kolekcyjnych, jak wszdobylska ArrayList.
Typy parametryzowane, przynajmniej na pierwszy rzut oka, przypominaj szablony w C++.
Szablony w tym jzyku, podobnie jak typy parametryzowane w Javie, zostay wprowadzone
ze wzgldu na kolekcje ze cis kontrol typw. Jednak z czasem odkryto dla nich wiele
nowych zastosowa. Niewykluczone, e po przeczytaniu tego rozdziau odkryjesz wasne
nowatorskie zastosowania dla typw oglnych w swoich programach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

628

Java. Podstawy

12.1. Dlaczego programowanie oglne


Programowanie oglne (ang. generic programming) oznacza pisanie kodu, za pomoc ktrego mona tworzy obiekty wielu rnych typw. Na przykad nie chcemy tworzy osobnych
klas do gromadzenia obiektw typu String oraz File i nie musimy klasa ArrayList pozwala
gromadzi obiekty dowolnego typu. Jest to przykad programowania oglnego.
Zanim w Javie pojawiy si klasy oglne, programowanie oglne polegao na wykorzystaniu
dziedziczenia. Klasa ArrayList po prostu przechowywaa tablic referencji do elementw
typu Object:
public class ArrayList
// przed klasami oglnymi
{
private Object[] elementData;
public Object get(int i) { . . . }
public void add(Object o) { . . . }
. . .
}

Metoda ta ma dwie wady. Po pierwsze, kada pobierana warto musi zosta poddana rzutowaniu:
ArrayList files = new ArrayList();
. . .
String filename = (String) names.get(0);

Po drugie, nie ma adnego mechanizmu sprawdzania bdw. Mona wstawia wartoci


dowolnego typu:
files.add(new File(". . ."));

Powysza instrukcja przejdzie z powodzeniem kompilacj i program bdzie normalnie dziaa.


Natomiast w innej czci programu konwersja wartoci zwrconej przez metod get na typ
String moe spowodowa bd.
Programowanie oglne oferuje lepsze rozwizanie: parametry typowe (ang. type parameters).
Obecnie klasa ArrayList ma parametr typowy, ktry okrela typ jej elementw:
ArrayList<String> files = new ArrayList<String>();

Dziki temu kod jest mniej zawiy. Od razu wida, e dana lista tablicowa przechowuje
obiekty typu String.
Jak ju wspominalimy, w Java SE 7 i nowszych typ oglny w konstruktorze mona
opuci:
private Object[] elementData;

Typ ten jest dedukowany z typu zmiennej.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

629

Poytki pynce z tych informacji mog zosta wykorzystane take przez kompilator. Wywoanie metody get nie pociga za sob koniecznoci wykonywania konwersji, poniewa kompilator wie, e zostanie zwrcony typ String, a nie Object:
String filename = files.get(0);

Ponadto kompilator wie, e metoda add klasy ArrayList<String> ma parametr typu String,
ktry jest znacznie bardziej bezpieczny ni Object. Teraz kompilator moe sprawdzi, czy
nie s wstawiane obiekty nieprawidowego typu. Na przykad ponisza instrukcja spowoduje
bd kompilacji:
files.add(new File(". . ."));

// Do ArrayList<String> mona wstawia tylko obiekty typu String.

O wiele korzystniej jest spowodowa bd kompilatora ni wyjtek konwersji w trakcie dziaania programu.
W tym tkwi atrakcyjno parametrw typowych: pozwalaj uproci kod i s bezpieczniejsze.

12.1.1. Dla kogo programowanie oglne


Klasy oglne (ang. generic class; take generyczne lub parametryzowane), jak ArrayList,
s atwe w uyciu. Wikszo programistw uywa takich typw jak ArrayList<String>,
tak jakby byy one czci jzyka, podobnie jak tablice String[] (oczywicie listy tablicowe
s lepsze, poniewa mog si automatycznie powiksza).
Natomiast implementacja klasy oglnej jest trudniejsza. Programici uywajcy takiej konstrukcji w miejsce parametru typu mog wstawia najrniejsze klasy. Oczekuj, e nie bdzie
adnych uciliwych ogranicze ani niejasnych komunikatw o bdach. W zwizku z tym
twrca klasy oglnej musi przewidzie wszystkie potencjalne sposoby uycia jego produktu.
Jak due trudnoci moe to sprawi? Projektanci biblioteki standardowej musieli poradzi
sobie z nastpujcym problemem. Klasa ArrayList zawiera metod addAll, ktra dodaje do
listy wszystkie elementy znajdujce si w innej kolekcji. Programista moe chcie wstawi
wszystkie elementy z kolekcji ArrayList<Manager> do kolekcji ArrayList<Employee>. Oczywicie operacja w przeciwn stron powinna by zabroniona. Jak sprawi, by jeden rodzaj
operacji by moliwy, a drugi nie? Projektanci Javy wykazali si pomysowoci i rozwizali
ten problem, opracowujc now koncepcj typu wieloznacznego (ang. wildcard type). Jest to
twr abstrakcyjny, ale umoliwia twrcy biblioteki tworzenie maksymalnie oglnych metod.
W programowaniu oglnym wyrniamy trzy poziomy zaawansowania. Na poziomie podstawowym programista uywa tylko klas oglnych z reguy takich kolekcji jak ArrayList
nie wiedzc nic o zasadzie ich dziaania. Wikszo programistw pozostaje na tym poziomie, dopki nie natkn si na jakie problemy. Mona na przykad odebra niejasne komunikaty o bdach, jeli pomiesza si rne klasy oglne lub uyje starego kodu, ktry zosta
napisany w czasach, kiedy nie istniao programowanie oglne. W takiej sytuacji konieczne
jest zdobycie wiedzy na temat typw oglnych, co pozwoli na systematyczne, a nie wyrywkowe rozwizywanie problemw. Wreszcie, niektrzy mog zechcie pisa wasne klasy
i metody oglne.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

630

Java. Podstawy
Twrcy aplikacji raczej nie musz pisa duo kodu generycznego. Wikszo trudnej pracy
zostaa wykonana przez programistw z firmy Sun, ktrzy dostarczyli parametrw typowych
dla wszystkich klas kolekcyjnych. Oglna regua jest taka, e na uyciu parametrw typowych
skorzysta mog tylko programy zawierajce duo konwersji z bardzo oglnych typw
(jak Object czy interfejs Comparable).
Rozdzia ten zawiera wszystkie informacje potrzebne do pisania wasnych procedur oglnych.
Przewidujemy jednak, e wikszo Czytelnikw wykorzysta t wiedz przede wszystkim
do rozwizywania problemw oraz zaspokojenia wasnej ciekawoci na temat zasad dziaania
parametryzowanych klas kolekcyjnych.

12.2. Definicja prostej klasy oglnej


Klasa oglna (ang. generic class) to taka, ktra ma co najmniej jedn zmienn typow. W tym
rozdziale jako przykad przedstawiamy prost klas o nazwie Pair. Pozwoli nam ona skoncentrowa si na typach oglnych bez rozpraszania si na zagadnienia zwizane z przechowywaniem danych. Oto kod oglnej klasy Pair:
public class Pair<T>
{
private T first;
private T second;
public Pair() { first = null; second = null; }
public Pair(T first, T second) { this.first = first; this.second = second; }
public T getFirst() { return first; }
public T getSecond() { return second; }

public void setFirst(T newValue) { first = newValue; }


public void setSecond(T newValue) { second = newValue; }

Klasa ta ma zmienn typu T umieszczon w nawiasach ostrych < > za nazw klasy. Klasa
oglna (parametryzowana) moe mie wicej zmiennych typowych. Na przykad klasa Pair
mogaby mie rne typy dla pierwszego i drugiego pola:
public class Pair<T, U> { . . . }

Zmienne typowe uywane w definicji klasy okrelaj typy zwrotne metod oraz typy pl i zmiennych lokalnych. Na przykad:
private T first;

// uywa zmiennej typowej

Przyjta powszechnie zasada nakazuje nadawanie zmiennym typowym krtkich nazw


zaczynajcych si wielk liter. W bibliotece Javy zmienna E oznacza typ elementu
kolekcji, zmienne K i V oznaczaj typy kluczy i wartoci tablic, a T (oraz w razie potrzeby
ssiednie litery U i S) dowolny typ.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

631

Aby utworzy egzemplarz typu oglnego, naley zastpi zmienne typowe rzeczywistymi
typami, na przykad:
Pair<String>

Wynik tego dziaania mona traktowa jako zwyk klas z konstruktorami:


Pair<String>()
Pair<String>(String, String)

i metodami:
String getFirst()
String getSecond()
void setFirst(String)
void setSecond(String)

Innymi sowy, klasa oglna jest jakby fabryk zwykych klas.


Program z listingu 12.1 przedstawia klas Pair w dziaaniu. Statyczna metoda minmax przemierza tablic i jednoczenie znajduje najmniejsz i najwiksz warto. Znalezione wyniki
zwraca w postaci obiektu klasy Pair. Przypomnijmy, e metoda compareTo porwnuje dwa
acuchy i zwraca warto 0, jeli s one identyczne, liczb cakowit ujemn, jeli pierwszy
acuch wystpuje przed drugim w kolejnoci sownikowej, oraz liczb dodatni cakowit
w pozostaych przypadkach.
Listing 12.1. pair1/PairTest1.java
package pair1;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest1
{
public static void main(String[] args)
{
String[] words = { "Ala", "ma", "kota", "i", "psa" };
Pair<String> mm = ArrayAlg.minmax(words);
System.out.println("min = " + mm.getFirst());
System.out.println("max = " + mm.getSecond());
}
}
class ArrayAlg
{
/**
* Pobiera najmniejsz i najwiksz warto z tablicy acuchw
* @param a tablica acuchw
* @return zoona z najmniejszej i najwikszej wartoci lub null, jeli tablica a jest null bd pusta
*/
public static Pair<String> minmax(String[] a)
{
if (a == null || a.length == 0) return null;
String min = a[0];
String max = a[0];
for (int i = 1; i < a.length; i++)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

632

Java. Podstawy
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
}

Powierzchownie klasy oglne w Javie s podobne do klas szablonowych w C++.


Jedyna rzucajca si w oczy rnica polega na tym, e w Javie nie ma specjalnego
sowa kluczowego template. W dalszej czci tego rozdziau przekonasz si jednak, e
rnic tych jest wicej.

12.3. Metody oglne


W poprzednim podrozdziale nauczylimy si definiowa klasy oglne. Istnieje take moliwo tworzenia metod z parametrami typowymi.
class ArrayAlg
{
public static <T> T getMiddle(T[] a)
{
return a[a.length / 2];
}
}

Ta metoda jest zdefiniowana w zwykej, nieoglnej klasie. Jednak nawiasy ostre i zmienna
typowa jednoznacznie wskazuj, e jest to metoda oglna. Naley zauway, e zmienne typu
znajduj si za modyfikatorami (w tym przypadku public static), a przed typem zwrotnym.
Metody oglne (parametryzowane) mona definiowa zarwno w zwykych klasach, jak i klasach oglnych.
Rzeczywisty typ w wywoaniu metody oglnej naley poda w nawiasach ostrych przed
nazw metody podczas wywoania:
String middle = ArrayAlg.<String>getMiddle("Jan", "S.", "Kowalski");

W tym przypadku (i w wikszoci innych) parametr typu <String> mona opuci. Kompilator i tak ma wystarczajco danych, aby wywoa odpowiedni metod. Porwnuje typ
zmiennej names (czyli String[]) z typem generycznym T[] i dedukuje, e T musi by typu
String. Oznacza to, e mona uy poniszego prostszego wywoania:
String middle = ArrayAlg.getMiddle("Jan", "S.", "Kowalski");

Wnioskowanie o typie metod generycznych w wikszoci sytuacji doskonale si sprawdza.


Moe si jednak zdarzy, e kompilator popeni bd, a wtedy konieczne jest rozszyfrowanie zwrconego komunikatu o bdzie. Przeanalizujmy poniszy przykad:
double middle = ArrayAlg.getMiddle(3.14, 1729, 0);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

633

Powysza instrukcja spowoduje wywietlenie trudnego do rozszyfrowania komunikatu,


ktry w kadym kompilatorze moe by nieco inny, informujcego, e kod mona zinterpretowa na dwa rwnowane sposoby. Rozumienia deklaracji typu found nauczysz si nieco
dalej. W najwikszym skrcie kompilator automatycznie opakowa parametry w obiekty
typu Double i Integer, a nastpnie sprbowa znale wsplny nadtyp dla obu tych klas. Znalaz
dwa: Number i interfejs Comparable, ktry sam jest typem oglnym. Ten problem mona rozwiza, zapisujc wszystkie parametry jako typ double.
Aby sprawdzi, jaki typ kompilator zastosuje dla wywoania metody parametryzowanej, mona zastosowa sztuczk opracowan przez Petera von der Ah. Naley
celowo popeni bd i przestudiowa zgoszony komunikat o bdzie. Wemy na przykad
instrukcj ArrayAlg.getMiddle("Witaj, ", 0, null). Przypiszemy jej wynik do komponentu JButton, co nie moe si uda. Zostanie wywietlony komunikat:
found:
java.lang.Object&java.io.Serializable&java.lang.Comparable<? extends
java.lang.Object&java.io.Serializable&java.lang.Comparable<?>>

Krtko mwic, wynik ten mona przypisa do typu Object, Serializable lub Comparable.

W C++ parametry typowe umieszcza si za nazw metody, co moe prowadzi do


niezbyt przyjemnych dwuznacznoci. Na przykad g(f<a, b>(c)) moe oznacza:
wywoaj g z wynikiem zwrconym przez f<a, b>(c) lub wywoaj g z dwiema wartociami logicznymi f<a i b>(c).

12.4. Ograniczenia zmiennych typowych


Czasami konieczne jest naoenie pewnych ogranicze na zmienne typowe klas i metod.
Chcemy znale najmniejszy element w tablicy:
class ArrayAlg
{
public static <T> T min(T[] a)
// prawie dobrze
{
if (a == null || a.length == 0) return null;
T smallest = a[0];
for (int i = 1; i < a.length; i++)
if (smallest.compareTo(a[i]) > 0) smallest = a[i];
return smallest;
}
}

Jest jednak jeden problem. Zajrzyjmy do kodu metody min. Zmienna smallest jest typu T,
co oznacza, e moe by obiektem dowolnej klasy. Skd wiadomo, e klasa, do ktrej
naley T, ma metod compareTo?

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

634

Java. Podstawy
Rozwizanie polega na ograniczeniu T do klas, ktre implementuj interfejs Comparable
standardowy interfejs zawierajcy tylko jedn metod o nazwie compareTo. W tym celu naley
zdefiniowa ograniczenie dla zmiennej typowej T:
public static <T extends Comparable> T min(T[] a) . . .

W rzeczywistoci sam interfejs Comparable jest typem generycznym. Na razie zignorujemy


wynikajce z tego dodatkowe problemy i ostrzeenia zgaszane przez kompilator. Prawidowy sposb uycia parametrw typowych z interfejsem Comparable zosta opisany w podrozdziale 12.8, Typy wieloznaczne.
Od tej pory ogln metod min mona wywoywa wycznie z parametrami tablic klas implementujcych interfejs Comparable, na przykad String, Date itd. Wywoanie metody min
z parametrem tablicy typu Rectangle spowoduje bd kompilacji, poniewa klasa Rectangle
nie implementuje interfejsu Comparable.
W jzyku C++ nie mona ogranicza typw parametrw szablonw. Utworzenie
egzemplarza szablonu przy uyciu zego typu powoduje zgoszenie w kodzie szablonu
(czsto niejasnego) komunikatu o bdzie.

Zagadk moe by, czemu w tej sytuacji uywa si sowa kluczowego extends zamiast
implements przecie Comparable to interfejs. Notacja:
<T extends typ graniczny>

oznacza, e T musi by podtypem typu granicznego. Zarwno T, jak i typ graniczny mog by
klas lub interfejsem. Projektanci Javy wybrali sowo extends, poniewa jest ono bliskie koncepcji podtypw, a poza tym nie chcieli wprowadza nowego sowa kluczowego, na przykad sub.
Zmienna typowa lub typ wieloznaczny mog mie wiele ogranicze. Na przykad:
T extends Comparable & Serializable

Znakiem rozdzielajcym poszczeglne typy graniczne jest ampersand (&), poniewa przecinek oddziela zmienne typowe.
Tak samo jak w przypadku dziedziczenia, wrd nadtypw moe istnie dowolna liczba interfejsw, ale tylko jedna klasa. Jeli wrd typw granicznych znajduje si klasa, musi by ona
umieszczona na pocztku listy.
W kolejnym przykadowym programie (listing 12.2) przerobilimy metod minmax na metod
ogln. Znajduje ona najmniejsz i najwiksz warto tablicy parametryzowanej i zwraca
obiekt typu Pair<T>.
Listing 12.2. pair2/PairTest2.java
package pair2;
import java.util.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest2
{
public static void main(String[] args)
{
GregorianCalendar[] birthdays =
{
new GregorianCalendar(1906, Calendar.DECEMBER, 9),
new GregorianCalendar(1815, Calendar.DECEMBER, 10),
new GregorianCalendar(1903, Calendar.DECEMBER, 3),
new GregorianCalendar(1910, Calendar.JUNE, 22),
};
Pair<GregorianCalendar> mm = ArrayAlg.minmax(birthdays);
System.out.println("min = " + mm.getFirst().getTime());
System.out.println("max = " + mm.getSecond().getTime());
}
}

635

// G. Hopper
// A. Lovelace
// J. von Neumann
// K. Zuse

class ArrayAlg
{
/**
Pobiera najmniejsz i najwiksz warto z tablicy obiektw typu T.
@param a tablica obiektw typu T
@return para zoona z najmniejszej i najwikszej wartoci lub warto null, jeli tablica a jest
null bd pusta
*/
public static <T extends Comparable> Pair<T> minmax(T[] a)
{
if (a == null || a.length == 0) return null;
T min = a[0];
T max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.compareTo(a[i]) > 0) min = a[i];
if (max.compareTo(a[i]) < 0) max = a[i];
}
return new Pair<>(min, max);
}
}

12.5. Kod oglny a maszyna wirtualna


Maszyna wirtualna nie przetwarza obiektw typw oglnych wszystkie obiekty nale
do zwykych klas. Wczeniejsza wersja implementacji typw oglnych umoliwiaa kompilacj programu wykorzystujcego typy parametryzowane na pliki klas, ktre mogy by
wykonywane nawet przez pierwsze maszyny wirtualne! W pniejszej fazie prac nad typami
oglnymi zrezygnowano jednak z tej zgodnoci wstecznej.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

636

Java. Podstawy
Kademu typowi oglnemu odpowiada typ surowy o takiej samej nazwie, ale z usunitymi
parametrami typu. W miejsce parametrw typowych wstawiane s typy graniczne (lub typ
Object w przypadku zmiennych bez ogranicze).
Na przykad typ surowy odpowiadajcy typowi Pair<T> wyglda nastpujco:
public class Pair
{
private Object first;
private Object second;
public Pair(Object first, Object second)
{
this.first = first;
this.second = second;
}
public Object getFirst() { return first; }
public Object getSecond() { return second; }
public void setFirst(Object newValue) { first = newValue; }
public void setSecond(Object newValue) { second = newValue; }
}

Poniewa T jest nieograniczon zmienn typow, w jej miejsce zosta wstawiony typ Object.
W wyniku powstaa zwyka klasa, ktra mogaby zosta utworzona przed wprowadzeniem
typw oglnych do Javy.
Program moe zawiera rnego rodzaju typy Pair, na przykad Pair<String> i Pair
<GregorianCalendar>, ale usunicie parametrw zawsze zamienia je w surowy typ Pair.
Pod tym wzgldem typy oglne Javy znacznie rni si od szablonw w C++. Drugi
z jzykw dla kadej instancji szablonu tworzy inny typ. Nazywa si to puchniciem
kodu szablonw (ang. template code bloat). W jzyku Java problem ten nie wystpuje.

Zmienne typw w typie surowym s zastpowane pierwsz klas graniczn lub typem Object,
jeli nie podano adnych ogranicze. Na przykad zmienna typowa w klasie Pair<T> nie ma
ogranicze, dlatego w jej typie surowym parametr T zosta zastpiony przez typ Object. Zadeklarujmy teraz nieco inny typ:
public class Interval<T extends Comparable & Serializable> implements Serializable
{
private T lower;
private T upper;
. . .
public Interval(T first, T second)
{
if (first.compareTo(second) <= 0) { lower = first; upper = second; }
else { lower = second; upper = first; }
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

637

Surowy typ Interval wyglda nastpujco:


public class Interval implements Serializable
{
private Comparable lower;
private Comparable upper;
. . .
public Interval(Comparable first, Comparable second) { . . . }
}

Mona si zastanawia, co by byo, gdyby zamieniono kolejno ogranicze class


Interval<Serializable & Comparable>. W takim przypadku w typie surowym
parametr T zostaby zastpiony typem Serializable, a kompilator wykonywaby konwersje na typ Comparable tam, gdzie to potrzebne. Dlatego ze wzgldu na wydajno interfejsy
niezawierajce adnych metod naley umieszcza na samym kocu listy ogranicze.

12.5.1. Translacja wyrae generycznych


W przypadku wyczyszczenia ze zmiennych typowych typu zwrotnego w wywoaniu metody
oglnej kompilator stosuje rzutowanie. Przeanalizujmy na przykad ponisze instrukcje:
Pair<Employee> buddies = . . .;
Employee buddy = buddies.getFirst();

Metoda getFirst po wymazaniu typw zwraca typ Object. Kompilator automatycznie


wstawia rzutowanie do typu Employee. Oznacza to, e konwertuje wywoanie metody na dwie
instrukcje maszyny wirtualnej:

odwoanie do surowej metody Pair.getFirst;

rzutowanie zwrconego typu Object na typ Employee.

Rzutowanie jest take stosowane przy uzyskiwaniu dostpu do pl generycznych. Zamy,


e pola first i second klasy Pair s publiczne (nie jest to dobry styl programowania, ale
dozwolony w Javie). Wtedy w kodzie bajtowym poniszego wyraenia rwnie zostanie wstawione rzutowanie:
Employee buddy = buddies.first;

12.5.2. Translacja metod oglnych


Wymazywanie typw zdarza si take w metodach oglnych. Programici czsto wyobraaj sobie metody oglne typu:
public static <T extends Comparable> T min(T[] a)

jako cae rodziny metod. Ale po wymazaniu typw zostaje tylko jedna metoda:
public static Comparable min(Comparable[] a)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

638

Java. Podstawy
Naley zauway, e parametr T zosta usunity, a pozostawiono tylko jego typ graniczny
Comparable.
Z czyszczeniem metod z typw generycznych wi si pewne komplikacje. Przyjrzyjmy si
poniszemu fragmentowi programu:
class DateInterval extends Pair<Date>
{
public void setSecond(Date second)
{
if (second.compareTo(getFirst()) >= 0)
super.setSecond(second);
}
. . .
}

Obiekt klasy DateInterval zawiera par obiektw klasy Date. Przesonimy metody, aby
mie pewno, e druga warto nie bdzie nigdy mniejsza od pierwszej. Po wymazaniu
typw powysza klasa przyjmie nastpujc posta:
class DateInterval extends Pair
// po wymazaniu typw
{
public void setSecond(Date second) { . . . }
. . .
}

Zaskakujce moe by to, e istnieje jeszcze jedna metoda setSecond, odziedziczona po


klasie Pair:
public void setSecond(Object second)

Jest to z pewnoci inna metoda, o czym wiadczy inny typ parametru Object zamiast
Date, a nie powinna by inna. Przyjrzyjmy si poniszym instrukcjom:
DateInterval interval = new DateInterval(. . .);
Pair<Date> pair = interval;
// OK przypisanie do nadklasy
pair.setSecond(aDate);

Spodziewamy si polimorficznego wywoania waciwej metody setSecond. Poniewa pair


jest referencj do obiektu klasy DateInterval, powinna to by metoda DateInterval.setSecond.
Problem polega na tym, e wymazywanie typw zakca polimorfizm. Dlatego kompilator
stara si unikn problemu, generujc metod pomostow (ang. bridge method) w klasie
DateInterval:
public void setSecond(Object second) { setSecond((Date) second); }

Aby zrozumie, jak to dziaa, szczegowo przeanalizujemy wykonywanie poniszej instrukcji:


pair.setSecond(aDate)

Zmienna pair jest typu Pair<Date>, ktry ma tylko jedn metod setSecond(Object). Maszyna
wirtualna wywouje t metod na rzecz obiektu wskazywanego przez zmienn pair. Obiekt
ten jest typu DateInterval. W zwizku z tym wywoywana jest metoda DateInterval.set
Second(Object), ktra jest zsyntetyzowan metod pomostow. Wywouje ona metod
DateInterval.setSecond(Date), czyli t, ktr chcemy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

639

Metody pomostowe mog by nawet jeszcze dziwniejsze. Przypumy, e w klasie Date


Interval przesonito take metod getSecond:
class DateInterval extends Pair<Date>
{
public Date getSecond() { return (Date) super.getSecond().clone(); }
. . .
}

W klasie DateInterval znajduj si dwie metody getSecond:


Date getSecond()
Object getSecond()

// w klasie DateInterval
// przesania metod zdefiniowan w klasie Pair, aby wywoywaa pierwsz metod

W Javie nie mona pisa takiego kodu, poniewa dwie metody nie mog mie takich samych
typw parametrw w tym przypadku nie maj ich w ogle. Natomiast w maszynie wirtualnej metod identyfikuj typy parametrw i typ zwrotny. Dlatego kompilator moe utworzy kod bajtowy dwch metod rnicych si tylko typem zwrotnym, ktry zostanie prawidowo obsuony przez maszyn wirtualn.
Metody pomostowe nie ograniczaj si tylko do typw oglnych. W rozdziale 5.
zauwaylimy, e metoda przesaniajca inn metod moe mie bardziej ograniczony typ zwrotny. Na przykad:
public class Employee implements Cloneable
{
public Employee clone() throws CloneNotSupportedException { ... }
}

Mwi si, e metody Object.clone i Employee.clone maj kowariantne typy zwrotne.


W rzeczywistoci klasa Employee zawiera dwie metody clone:
Employee clone()
Object clone()

// zdefiniowana powyej
// zsyntetyzowana metoda pomostowa przesania metod Object.clone

Zsyntetyzowana metoda pomostowa wywouje nowo utworzon metod.

Podsumowujc, naley zapamita nastpujce fakty dotyczce translacji typw oglnych:

W maszynie wirtualnej nie ma typw oglnych, tylko zwyke klasy i metody.

Parametry typowe s zastpowane odpowiadajcymi im typami granicznymi.

Metody pomostowe s syntetyzowane w celu zachowania polimorfizmu.

Rzutowanie jest wstawiane w razie potrzeby w celu zachowania


bezpieczestwa typw.

12.5.3. Uywanie starego kodu


Gdy projektowano modu typw oglnych w Javie, jednym z najwaniejszych celw inynierw byo zapewnienie zgodnoci nowego kodu ze starym. Spjrzmy na konkretny przykad. Do ustawiania etykiet komponentw JSlider uywa si poniszej metody:
void setLabelTable(Dictionary table)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

640

Java. Podstawy
W tym przypadku Dictionary jest surowym typem, poniewa komponent JSlider zosta
zaimplementowany przed typami oglnymi. Jednak do wstawiania wartoci do sownika
naley uywa typu oglnego:
Dictionary<Integer, Component> labelTable = new Hashtable<>();
labelTable.put(0, new JLabel(new ImageIcon("nine.gif")));
labelTable.put(20, new JLabel(new ImageIcon("ten.gif")));
. . .

Gdy do metody setLabelTable przekaemy obiekt typu Dictionary<Integer, Component>,


kompilator zgosi ostrzeenie:
slider.setLabelTable(labelTable);

// ostrzeenie

Kompilator nie ma pewnoci, co metoda setLabelTable moe zrobi z obiektem typu Dic
tionary. Moe na przykad zamieni wszystkie klucze na acuchy, co zamaoby gwarancj, e klucze s typu Integer. To z kolei mogoby doprowadzi do wyjtkw rzutowania
w kolejnych operacjach.
Z ostrzeeniem tym nie mona wiele zrobi. Co najwyej moemy sprbowa dowiedzie
si, co JSlider najprawdopodobniej moe zrobi z danym obiektem Dictionary. W tym przypadku jest oczywiste, e JSlider tylko odczytuje dane, a wic ostrzeenie mona zignorowa.
Teraz wyobramy sobie odwrotn sytuacj, w ktrej otrzymujemy obiekt surowego typu ze
starej klasy. Moemy przypisa go do zmiennej typu oglnego, ale wtedy kompilator zgosi
ostrzeenie. Na przykad:
Dictionary<Integer, Components> labelTable = slider.getLabelTable();

// ostrzeenie

Przegldamy ostrzeenie i sprawdzamy, czy tablica etykiet rzeczywicie zawiera obiekty


typu Integer i Component. Oczywicie nigdy nie mona mie cakowitej pewnoci. Moe si
zdarzy, e zoliwy uytkownik zainstaluje inny obiekt Dictionary w suwaku. Sytuacja ta nie
jest jednak gorsza ni przed pojawieniem si typw oglnych. W najgorszym wypadku program wygeneruje wyjtek.
Po przeanalizowaniu ostrzeenia mona si go pozby za pomoc adnotacji (ang. annotation).
Adnotacj mona umieci przed lokaln zmienn:
@SuppressWarnings("unchecked")
Dictionary<Integer, Components> labelTable = slider.getLabelTable(); // brak ostrzeenia

Mona te doda adnotacj dla caej metody:


@SuppressWarnings("unchecked")
public void configureSlider() { . . . }

Ta adnotacja wycza sprawdzanie bdw dla caej metody.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

641

12.6. Ograniczenia i braki


W tej sekcji omawiamy ograniczenia, ktre trzeba wzi pod uwag, decydujc si na uycie
typw oglnych. Wikszo z nich ma swoje rdo w wymazywaniu typw.

12.6.1. Nie mona podawa typw prostych jako parametrw typowych


Parametru typowego nie mona zastpi typem prostym. Dlatego nie da si utworzy klasy
Pair<double>, ale Pair<Double>. Powodem jest tu oczywicie wymazywanie typw. Po oczyszczeniu klasa Pair ma pola typu Object, w ktrych nie mona przechowywa wartoci typu
double.
Mimo e zasada ta jest denerwujca, jest ona zgodna z zasad odrbnoci typw prostych
w jzyku Java. Nie jest to wielka strata typw prostych jest tylko osiem i zawsze mona je
obsuy za pomoc osobnych klas i metod, jeli typy opakowujce nie mog by uyte.

12.6.2. Sprawdzanie typw w czasie dziaania programu


jest moliwe tylko dla typw surowych
Obiekty w maszynie wirtualnej zawsze nale do konkretnego typu nieoglnego. Dlatego
operacja sprawdzania typu zawsze zwraca typ surowy. Na przykad instrukcja:
if (a instanceof Pair<String>)

// BD

sprawdza tylko, czy a jest obiektem jakiejkolwiek klasy Pair. To samo dotyczy poniszego
testu:
if (a instanceof Pair<T>)

// BD

i rzutowania:
Pair<String> p = (Pair<String>) a; // OSTRZEENIE mona tylko sprawdzi, czy a jest typu Pair

Kade uycie operatora instanceof lub zastosowanie rzutowania zwizane z typami oglnymi bdzie skutkowao zgoszeniem przez kompilator ostrzeenia, majcego na celu powiadomienie programisty o ryzykownoci operacji.
Z tego samego powodu metoda getClass zawsze zwraca typ surowy, na przykad:
Pair<String> stringPair = . . .;
Pair<Employee> employeePair = . . .;
if (stringPair.getClass() == employeePair.getClass())

// s rwne

Wynikiem porwnania jest warto true, poniewa oba wywoania metody getClass zwracaj
Pair.class.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

642

Java. Podstawy

12.6.3. Nie mona tworzy tablic typw oglnych


Nie mona tworzy tablic typw parametryzowanych, na przykad:
Pair<String>[] table = new Pair<String>[10];

// bd

Dlaczego powyszy kod jest zy? Po wymazaniu parametrw zmienna table jest typu Pair[].
Mona j przekonwertowa na typ Object:
Object[] objarray = table;

Tablica pamita typ przechowywanych elementw i w przypadku zapisu w niej elementu


zego typu generuje wyjtek ArrayStoreException:
objarray[0] = "Witaj, ";

// bd typ elementw to Pair

Wymazywanie typw powoduje jednak, e mechanizm ten nie dziaa w przypadku typw
oglnych. Ponisza instrukcja przeszaby z powodzeniem kontrol tablicy, ale nadal powodowaaby bd typu. Dlatego zabroniono tworzenia tablic typw oglnych.
objarray[0] = new Pair<Employee>();

Naley podkreli, e zabronione jest tylko tworzenie tych tablic. Mona zadeklarowa zmienn
typu Pair<String>, ale nie mona jej zainicjowa przy uyciu instrukcji new Pair<String>[10].
Mona deklarowa tablice typw wieloznacznych, a nastpnie poddawa je rzutowaniu:
Pair<String>[] table = (Pair<String>[]) new Pair<?>[10];

Wynik nie jest bezpieczny. Jeli zapiszesz Pair<Employee> w elemencie table[0], a nastpnie wywoasz metod klasy String na table[0].getFirst(), otrzymasz wyjtek
ClassCastException.

Aby utworzy kolekcj obiektw typw oglnych, naley uy klasy ArrayList: zapis
ArrayList<Pair<String>> jest bezpieczny i efektywny.

12.6.4. Ostrzeenia dotyczce zmiennej liczby argumentw


W poprzednim podrozdziale dowiedziae si, e Java nie obsuguje tablic typw oglnych.
W tej czci rozdziau znajduje si omwienie podobnego problemu: przekazywania egzemplarzy oglnego typu do metody ze zmienn liczb argumentw.
Spjrz na ponisz prost metod:
public static <T> void addAll(Collection<T> coll, T... ts)
{
for (t : ts) coll.add(t);
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

643

Przypomnijmy, e parametr ts jest tablic zawierajc wszystkie dostarczone argumenty.


Teraz spjrz na ponisze wywoanie:
Collection<Pair<String>> table = ...;
Pair<String> pair1 = ...;
Pair<String> pair2 = ...;
addAll(table, pair1, pair2);

Aby wywoa t metod, maszyna wirtualna Javy musi utworzy tablic Pair<String>, co
jest wbrew zasadom. Reguy zostay jednak rozlunione dla takich przypadkw i zamiast bdu
zostanie zgoszone tylko ostrzeenie.
Ostrzeenie to mona stumi na dwa sposoby. Mona doda adnotacj @SuppressWarnings
("unchecked") do metody zawierajcej wywoanie addAll lub (od Java SE 7) adnotacj
@SafeVarargs do metody addAll:
@SafeVarargs
public static <T> void addAll(Collection<T> coll, T... ts)

Teraz metod t mona wywoywa z typami oglnymi. Adnotacj t mona stosowa do


wszystkich metod, ktre tylko odczytuj elementy z tablicy parametrw, i jest to z pewnoci
jej najczstsze zastosowanie.
Dziki adnotacji @SafeVarargs mona obej ograniczenie dotyczce tworzenia oglnych tablic. Mona w tym celu uy poniszej metody:
@SafeVarargs static <E> E[] array(E... array) { return array; }

Teraz mona zastosowa ponisze wywoanie:


Pair<String>[] table = array(pair1, pair2);

Wydaje si to wygodne, ale kryje si tu niebezpieczestwo. Kod:


Object[] objarray = table;
objarray[0] = new Pair<Employee>();

zostanie wykonany bez wyjtku ArrayStoreException (poniewa tablica sprawdza tylko


typ wymazywany), a wyjtek otrzymamy w innym miejscu, w ktrym uyjemy elementu
table[0].

12.6.5. Nie wolno tworzy egzemplarzy zmiennych typowych


Zmiennych typowych nie mona uywa w wyraeniach typu new T(...), new T[...] czy
T.class. Na przykad poniszy konstruktor Pair<T> jest niedozwolony:
public Pair() { first = new T(); second = new T(); }

// bd

W wyniku wymazywania typw parametr T zostaby zamieniony na typ Object, a z pewnoci


naszym zamiarem nie jest utworzenie wywoania new Object().
Mona sobie z tym poradzi, konstruujc obiekty oglne poprzez refleksj i wywoujc
metod Class.newInstance.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

644

Java. Podstawy
Niestety istniej pewne komplikacje. Nie mona uy takiego wywoania:
first = T.class.newInstance();

// bd

Wyraenie T.class jest ze. W zamian trzeba tak zaprojektowa API, aby otrzyma obiekt klasy
Class, na przykad:
public static <T> Pair<T> makePair(Class<T> cl)
{
try { return new Pair<T>(cl.newInstance(), cl.newInstance()) }
catch (Exception ex) { return null; }
}

T metod mona wywoa nastpujco:


Pair<String> p = Pair.makePair(String.class);

Naley zauway, e klasa Class sama jest generyczna. Na przykad String.class jest egzemplarzem (i to jedynym) klasy Class<String>. Dlatego metoda makePair moe wywnioskowa
typ pary, ktr tworzy.
Nie mona utworzy tablicy generycznej:
public static <T extends Comparable> T[] minmax(T[] a) { T[] mm = new T[2]; . . . } // bd

Wymazywanie typw spowodowaoby, e metoda ta zawsze tworzyaby tablic Comparable[2].


Jeli tablica jest uywana wycznie jako prywatne pole egzemplarza klasy, mona j zadeklarowa jako Object[] i przy pobieraniu elementw stosowa rzutowanie. Na przykad klas
ArrayList mona zaimplementowa nastpujco:
public class ArrayList<E>
{
private Object[] elements;
. . .
@SuppressWarnings("unchecked") public E get(int n) { return (E) elements[n]; }
public void set(int n, E e) { elements[n] = e; }
// nie jest potrzebne rzutowanie
}

Rzeczywista implementacja nie jest taka prosta:


public class ArrayList<E>
{
private E[] elements;
public ArrayList() { elements = (E[]) new Object[10]; }
. . .
}

W tym przypadku rzutowanie na typ E[] jest ze, ale przez wymazywanie typw jest to nie
do wykrycia.
Technika ta nie zadziaa w naszej metodzie minmax, poniewa zwracamy tablic T[] i podanie
jej zego typu spowoduje bd wykonawczy. Zamy, e mamy nastpujc implementacj:
public static <T extends Comparable> T[] minmax(T[] a)
{
Object[] mm = new Object[2];

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.
. . .;
return (T[]) mm;

Programowanie oglne

645

// kompilator zgosi ostrzeenie

Ponisze wywoanie przejdzie kompilacj bez adnego ostrzeenia:


String[] ss = minmax("Tomasz", "Darek", "Henryk");

Wyjtek ClassCastException jest generowany, kiedy referencja do typu Object[] jest rzutowana na typ Comparable[] podczas zwracania wartoci przez metod.
W takiej sytuacji mona skorzysta z refleksji i wywoa metod Array.newInstance:
public static <T extends Comparable> T[] minmax(T[] a)
{
T[] mm = (T[]) Array.newInstance(a.getClass().getComponentType(), 2);
. . .
}

Metoda toArray z klasy ArrayList nie ma tyle szczcia. Musi utworzy tablic T[], ale nie
zna typu elementw. Dlatego istniej jej dwa warianty:
Object[] toArray()
T[] toArray(T[] result)

Druga wersja pobiera parametr w postaci tablicy. Jeli tablica ta jest wystarczajco dua,
zostanie uyta. W przeciwnym razie tworzona jest nowa tablica o odpowiednim rozmiarze,
a typem jej komponentw jest result.

12.6.6. Zmiennych typowych nie mona uywa


w statycznych kontekstach klas oglnych
Do zmiennych typowych nie mona odwoywa si w polach i metodach statycznych. Na
przykad:
public class Singleton<T>
{
private static T singleInstance;

// bd

public static T getSingleInstance()


// bd
{
if (singleInstance == null) utwrz nowy egzemplarz T
return singleInstance;
}
}

Gdyby to byo moliwe, mona by byo zadeklarowa klas Singleton<Random> dla liczb
losowych i klas Singleton<JFileChooser> do tworzenia okien wyboru pliku. Jest to jednak
niemoliwe, poniewa dziki wymazywaniu typw istnieje tylko jedna klasa Singleton
i tylko jedno pole singleInstance. Dlatego pola i metody statyczne ze zmiennymi typowymi
s zabronione.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

646

Java. Podstawy

12.6.7. Obiektw klasy oglnej nie mona generowa ani przechwytywa


Nie mona generowa ani przechwytywa obiektw klas oglnych. Niedozwolone jest nawet
rozszerzanie klasy Throwable przez klas generyczn. Na przykad ponisza definicja spowoduje bd kompilacji:
public class Problem<T> extends Exception { /* . . . */ }

// bd nie mona rozszerza


// klasy Throwable

Zmiennych typowych nie mona uywa w klauzulach catch. Dlatego ponisza metoda
spowoduje bd kompilacji:
public static <T extends Throwable> void doWork(Class<T> t)
{
try
{
procedury
}
catch (T e)
// bd nie mona przechwyci zmiennej typowej
{
Logger.global.info(...)
}
}

Mona natomiast uywa zmiennych typowych w specyfikacjach wyjtkw. Ponisza metoda


jest poprawna:
public static <T extends Throwable> void doWork(T t) throws T
{
try
{
procedury
}
catch (Throwable realCause)
{
t.initCause(realCause);
throw t;
}
}

// OK

12.6.7.1. Mona wyczy sprawdzanie wyjtkw kontrolowanych


Podstawow zasad obsugi wyjtkw w Javie jest utworzenie procedury obsugi dla kadego
kontrolowanego wyjtku. Za pomoc typw oglnych mona to obej. Najwaniejsza w realizacji tego celu jest ponisza metoda:
@SuppressWarnings("unchecked")
public static <T extends Throwable> void throwAs(Throwable e) throws T
{
throw (T) e;
}

Przypumy, e metoda ta znajduje si w klasie Block. Gdy zastosujemy ponisze wywoanie, kompilator uzna, e t staje si wyjtkiem niekontrolowanym.
Block.<RuntimeException>throwAs(t);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

647

Ponisza konstrukcja zamienia wszystkie wyjtki w takie, ktre dla kompilatora s niekontrolowane:
try
{

instrukcje
}
catch (Throwable t)
{
Block.<RuntimeException>throwAs(t);
}

Zapakujemy to w abstrakcyjn klas. Uytkownik przesoni metod body, aby dostarczy


konkretn akcj. Wywoujc metod toThread, otrzymasz obiekt klasy Thread, ktrego
metoda run nie zwraca uwagi na niekontrolowane wyjtki.
public abstract class Block
{
public abstract void body() throws Exception;
public Thread toThread()
{
return new Thread()
{
public void run()
{
try
{
body();
}
catch (Throwable t)
{
Block.<RuntimeException>throwAs(t);
}
}
};
}

@SuppressWarnings("unchecked")
public static <T extends Throwable> void throwAs(Throwable e) throws T
{
throw (T) e;
}

Przykadowo poniszy program uruchamia wtek zgaszajcy niekontrolowany wyjtek.


public class Test
{
public static void main(String[] args)
{
new Block()
{
public void body() throws Exception
{
Scanner in = new Scanner(new File("ququx"));
while (in.hasNext())
System.out.println(in.next());
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

648

Java. Podstawy
}
.toThread().start();
}
}

Gdy uruchomisz ten program, otrzymasz dane stosu z wyjtkiem FileNotFoundException


(oczywicie pod warunkiem e nie podae pliku o nazwie ququx).
Co w tym takiego niezwykego? Standardowo trzeba przechwytywa wszystkie kontrolowane wyjtki w metodzie run wtku i opakowywa je w wyjtkach niekontrolowanych
metoda run nie zgasza wyjtkw kontrolowanych.
Tutaj jednak nie stosujemy opakowywania. Po prostu zgaszamy wyjtek, tak by kompilator
uzna, e nie jest to wyjtek kontrolowany.
Przy uyciu klas oglnych, wymazywania typw i adnotacji @SuppressWarnings obeszlimy
bardzo wan cz systemu typw Javy.

12.6.8. Uwaaj na konflikty, ktre mog powsta po wymazaniu typw


Nie mona tworzy warunkw powodujcych konflikty po usuniciu typw generycznych.
Zamy na przykad, e do klasy Pair dodajemy metod equals:
public class Pair<T>
{
public boolean equals(T value) { return first.equals(value) &&
second.equals(value); }
. . .
}

Wemy klas Pair<String>. W zasadzie ma ona dwie metody equals:


boolean equals(String)
boolean equals(Object)

// zdefiniowana w klasie Pair<T>


// odziedziczona po klasie Object

Tym razem jednak nasza intuicja zawodzi. Metoda boolean equals(T) po wymazaniu typw
ma posta boolean equals(Object) i wchodzi w konflikt z metod Object.equals.
Aby sobie z tym poradzi, naley oczywicie zmieni nazw metody sprawiajcej problem.
W specyfikacji typw oglnych opisano jeszcze inn regu: Aby translacja poprzez wymazywanie typw bya moliwa, klasa lub zmienna typowa nie moe by w jednym czasie podtypem dwch interfejsw bdcych rnymi wersjami parametrycznymi tego samego interfejsu. Na przykad poniszy kod jest zy:
class Calendar implements Comparable<Calendar> { . . . }
class GregorianCalendar extends Calendar implements Comparable<GregorianCalendar>
{ . . . }
// bd

W takiej sytuacji klasa GregorianCalendar implementowaaby interfejsy Comparable<Calendar>


i Comparable<GregorianCalendar>, ktre s rnymi wersjami parametrycznymi tego samego
interfejsu.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

649

Zwizek tego ograniczenia z wymazywaniem typw nie jest oczywisty. Niegeneryczna wersja
jest dozwolona:
class Calendar implements Comparable { . . . }
class GregorianCalendar extends Calendar implements Comparable { . . . }

Powd tkwi znacznie gbiej. Konflikt wystpiby pomidzy zsyntetyzowanymi metodami


pomostowymi. Metoda pomostowa klasy implementujcej interfejs Comparable<X> jest nastpujca:
public int compareTo(Object other) { return compareTo((X) other); }

Nie mona mie dwch takich metod dla rnych typw X.<<F1-k>>

12.7. Zasady dziedziczenia dla typw oglnych


Aby mc pracowa z klasami oglnymi, trzeba zna kilka regu dotyczcych dziedziczenia
i podtypw. Zaczniemy od kwestii, ktra wielu programistom sprawia problemy. Wyobramy
sobie klas i jej podklas, na przykad Employee i Manager. Czy klasa Pair<Manager> jest
podklas klasy Pair<Employee>? Moe si to wyda zaskakujce, ale nie. Na przykad nie
mona skompilowa poniszego fragmentu kodu:
Manager[] topHonchos = . . .;
Pair<Employee> result = ArrayAlg.minmax(topHonchos);

// bd

Metoda minmax zwraca typ Pair<Manager>, a nie Pair<Employee>, nie mona jej te przypisa
jednego z tych typw do drugiego.
Oglna zasada jest taka, e pomidzy typami Pair<S> i Pair<T> nie ma adnego zwizku,
bez wzgldu na to, co czy S i T (zobacz rysunek 12.1).

Rysunek 12.1. Brak relacji dziedziczenia pomidzy klasami par

Ograniczenie to wydaje si okrutne, ale jest konieczne ze wzgldu na bezpieczestwo typw.


Wyobramy sobie, e moemy przekonwertowa typ Pair<Manager> na typ Pair<Employee>.
Spjrzmy na poniszy kod:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

650

Java. Podstawy
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<Employee> employeeBuddies = managerBuddies;
// niedozwolone, ale zamy, e tak
employeeBuddies.setFirst(lowlyEmployee);

Ostatnia instrukcja jest bez wtpienia poprawna, ale zmienne employeeBuddies i managerBuddies
odwouj si do tego samego obiektu. W ten sposb w jednej parze umiecilimy osob
z kierownictwa i zwykego pracownika. Operacja ta nie powinna by moliwa przy uyciu typu
Pair<Manager>.
Wanie zostaa opisana wana rnica pomidzy typami oglnymi i tablicami
w Javie. Tablic Manager[] mona przypisa do zmiennej typu Employee[]:
Manager[] managerBuddies = { ceo, cfo };
Employee[] employeeBuddies = managerBuddies;

// OK

Tablice jednak maj specjaln ochron. Prba zapisu zwykego pracownika w employeeBuddies[] zakoczy si wygenerowaniem przez maszyn wirtualn wyjtku ArrayStoreException.

Typ parametryzowany zawsze mona przekonwertowa na typ surowy. Na przykad Pair


<Employee> jest podtypem typu surowego Pair. Taka konwersja jest potrzebna ze wzgldu
na zgodno ze starym kodem.
Czy mona dokona konwersji na typ surowy, a nastpnie spowodowa bd typu? Niestety
tak. Spjrzmy na poniszy przykad:
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair rawBuddies = managerBuddies;
// OK
rawBuddies.setFirst(new File(". . ."));
// tylko ostrzeenie kompilatora

To wydaje si straszne, ale naley pamita, e nie jest gorzej ni w starszych wersjach
Javy. Bezpieczestwo maszyny wirtualnej nie wchodzi w gr. Jeli metoda getFirst pobierze obcy obiekt i przypisze go do zmiennej typu Manager, zostanie wygenerowany wyjtek
ClassCastException.
Ostatecznie klasy oglne mog rozszerza lub implementowa inne klasy oglne. Pod tym
wzgldem nie rni si niczym od zwykych klas. Na przykad klasa ArrayList<T> implementuje interfejs List<T>. Oznacza to, e typ ArrayList<Manager> mona przekonwertowa na
typ List<Manager>. Jak ju jednak wiemy, ArrayList<Manager> to nie ArrayList<Employee>
ani List<Employee>. Relacje te przedstawia rysunek 12.2.

12.8. Typy wieloznaczne


Naukowcy zajmujcy si systemami typw ju od duszego czasu wiedzieli, e uywanie
sztywnych systemw typw oglnych nie naley do przyjemnoci. Projektanci Javy wpadli
na pomysowe (ale i bezpieczne) rozwizanie tego problemu typ wieloznaczny (ang.
wildcard type). Na przykad poniszy typ wieloznaczny oznacza dowolny typ oglny Pair,
ktrego parametr typowy jest podklas klasy Employee, na przykad Pair<Manager>, ale nie
Pair<String>.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

651

Rysunek 12.2. Relacje pomidzy generycznymi typami listowymi


Pair<? extends Employee>

Zamy, e chcemy napisa metod drukujc pary pracownikw:


public static void printBuddies(Pair<Employee> p)
{
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + " i " + second.getName() + " to kumple.";
}

Jak wiadomo z poprzedniego podrozdziau, do metody tej nie mona przekaza typu Pair
<Manager>, co stanowi spore ograniczenie. Jest jednak proste rozwizanie w postaci typw
wieloznacznych:
public static void printBuddies(Pair<? extends Employee> p)

Typ Pair<Manager> jest podtypem Pair<? extends Employee> (zobacz rysunek 12.3).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

652

Java. Podstawy

Rysunek 12.3.
Relacje klasowe
przy zastosowaniu
typw
wieloznacznych

Czy za pomoc typu wieloznacznego mona uszkodzi typ Pair<Manager> poprzez referencj
typu Pair<? extends Employee>?
Pair<Manager> managerBuddies = new Pair<Manager>(ceo, cfo);
Pair<? extends Employee> wildcardBuddies = managerBuddies;
wildcardBuddies.setFirst(lowlyEmployee);

// OK
// bd kompilacji

Uszkodzenie jest niemoliwe. Wywoanie metody setFirst spowodowao bd nieprawidowego typu. Aby dowiedzie si dlaczego, szczegowo przeanalizujemy typ Pair<? extends
Employee>. Jego metody s nastpujce:
? extends Employee getFirst()
void setFirst(? extends Employee)

To uniemoliwia wywoanie metody setFirst. Kompilator wie tylko, e potrzebny jest jaki
podtyp Employee, ale nie wie jaki. Nie zgadza si na przekazanie adnego konkretnego typu,
poniewa doker (?) moe do niego nie pasowa.
Ten problem nie istnieje w przypadku metody getFirst. Warto zwrotn tej metody mona
z powodzeniem przypisa do referencji typu Employee.
Jest to kluczowa cecha typw wieloznacznych z ograniczeniami. Teraz dysponujemy moliwoci rozrnienia bezpiecznych metod akcesora i niebezpiecznych metod mutatora.

12.8.1. Ograniczenia nadtypw typw wieloznacznych


Ograniczenia typw wieloznacznych s podobne do ogranicze zmiennych typowych, ale
maj jedn dodatkow cech mona okrela ograniczenia nadtypw:
? super Manager

Ten typ wieloznaczny jest ograniczony do wszystkich nadtypw typu Manager (projektanci
mieli duo szczcia, e istniejce sowo kluczowe super tak precyzyjnie okrela ten rodzaj
relacji).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

653

Do czego moe si to przyda? Typy wieloznaczne z ograniczeniami nadtypw s przeciwiestwem typw wieloznacznych opisanych w podrozdziale 12.8, Typy wieloznaczne.
Mona przekazywa do metod parametry, ale nie mona uywa ich wartoci zwrotnych.
Na przykad klasa Pair<? super Manager> zawiera nastpujce metody:
void setFirst(? super Manager)
? super Manager getFirst()

Kompilator nie wie, jaki dokadnie typ ma metoda setFirst, ale moe j wywoa na rzecz
kadego obiektu typu Manager, Employee i Object, jednak nie na rzecz obiektw nalecych
do jej podtypw, jak na przykad Executive. Dlatego wywoujcy metod getFirst nie wie
na pewno, jakiego typu obiekt ona zwrci. W zwizku z tym warto t mona przypisa tylko
do typu Object.
Oto typowy przykad takiej sytuacji. Mamy tablic obiektw Manager. Chcemy, aby kierownicy z najwiksz i najmniejsz premi znaleli si w jednym obiekcie Pair. Jakiego rodzaju
ma to by para? Moe to by Pair<Employee> albo Pair<Object> (zobacz rysunek 12.4).
Ponisza metoda przyjmie kady odpowiedni obiekt Pair:
public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
{
if (a == null || a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}

Podsumowujc, typy wieloznaczne z ograniczeniami nadtypw pozwalaj na zapis w obiektach


oglnych, a typy wieloznaczne z ograniczeniami podtypw pozwalaj na odczyt z obiektw
oglnych.
Oto jeszcze jeden przykad zastosowania ogranicze nadtypw. Interfejs Comparable jest typem
generycznym. Jego deklaracja jest nastpujca:
public interface Comparable<T>
{
public int compareTo(T other);
}

Tutaj zmienna typowa okrela typ parametru other. Na przykad klasa String implementuje
interfejs Comparable<String>, a deklaracja jej metody compareTo jest nastpujca:
public int compareTo(String other)

Jest to zgrabne rozwizanie, poniewa parametr jawny ma odpowiedni typ. Przed Java SE 5.0
parametr other by typu Object i implementacja metody musiaa zawiera operacj konwersji
typw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

654

Java. Podstawy

Rysunek 12.4.
Typ wieloznaczny
z ograniczeniem
nadtypw

Poniewa interfejs Comparable jest typem oglnym, metod min z klasy ArrayAlg mona napisa nieco lepiej. Jej deklaracja moe wyglda nastpujco:
public static <T extends Comparable<T>> T min(T[] a)

Taki zapis wydaje si bardziej staranny ni T extends Comparable, dziki czemu moe
nadawa si dla wielu klas. Jeli na przykad chcemy znale najmniejsz warto w tablicy
acuchw, parametr T bdzie typu String, a String jest podtypem Comparable<String>.
Pojawia si jednak problem przy przetwarzaniu tablicy obiektw typu GregorianCalendar.
Tak si skada, e klasa GregorianCalendar jest podklas klasy Calendar, ktra z kolei implementuje interfejs Comparable<Calendar>. Dlatego klasa GregorianCalendar implementuje interfejs Comparable<Calendar>, a nie Comparable<GregorianCalendar>.
W takiej sytuacji na ratunek przychodz nadtypy:
public static <T extends Comparable<? super T>> T min(T[] a) . . .

Teraz metoda compareTo przyjmie nastpujc form:


int compareTo(? super T)

Moe ona przyjmowa obiekty typu T jeli T jest na przykad GregorianCalendar, albo nadtypu T. Bez wzgldu na wszystko przekazanie obiektu typu T do tej metody jest bezpieczne.
Deklaracje typu <T extends Comparable<? super T>> dla niedowiadczonego programisty
wygldaj przytaczajco. Jak na ironi, celem tej deklaracji jest pomoc programistom poprzez
usunicie niepotrzebnych ogranicze parametrw wywoania. Ci, ktrzy nie s zainteresowani typami generycznymi, szybko ucz si nie zwraca szczeglnej uwagi na te deklaracje
i przyjmowa, e programici biblioteki si nie pomylili. Programici bibliotek natomiast

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

655

musz si przyzwyczai do typw wieloznacznych, jeli nie chc, aby uytkownicy przeklinali ich, kiedy zostan zmuszeni do wykonywania losowych konwersji, a program w kocu
si skompiluje.

12.8.2. Typy wieloznaczne bez ogranicze


Typy wieloznaczne mona stosowa nawet bez adnych ogranicze, na przykad Pair<?>.
Na pierwszy rzut oka zapis ten wydaje si identyczny z surowym typem Pair. W rzeczywistoci midzy tymi typami s bardzo due rnice. Typ Pair<?> ma takie metody:
? getFirst()
void setFirst(?)

Warto zwrotn metody getFirst mona przypisa tylko do typu Object. Metody setFirst
nie mona w oglne wywoa, nawet z typem Object. Na tym polega gwna rnica pomidzy typami Pair<?> i Pair: metod setObject surowej klasy Pair mona wywoa z dowolnym
obiektem typu Object.
Mona zastosowa wywoanie setFirst(null).

Do czego moe si przyda taki typ? Znajduje on zastosowanie w bardzo prostych dziaaniach. Na przykad ponisza metoda sprawdza, czy para zawiera dany obiekt. Nie potrzebuje
zna rzeczywistego typu.
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}

Stosowania typu wieloznacznego mona unikn, zamieniajc metod na generyczn:


public static <T> boolean hasNulls(Pair<T> p)

jednak wersja z typem wieloznacznym wydaje si mniej zawia.

12.8.3. Chwytanie typu wieloznacznego


Napiszmy metod przestawiajc elementy w parze:
public static void swap(Pair<?> p)

Doker nie jest zmienn typow, dlatego nie mona go uywa jako typu w programie. Innymi
sowy, nie mona napisa poniszego kodu:
? t = p.getFirst();
// bd
p.setFirst(p.getSecond());
p.setSecond(t);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

656

Java. Podstawy
Mamy problem, poniewa musimy przechowa tymczasowo pierwszy element, aby wykona
zamian. Na szczcie istnieje pewne ciekawe rozwizanie tego problemu. Moemy napisa
metod pomocnicz, na przykad o nazwie swapHelper:
public static <T> void swapHelper(Pair<T> p)
{
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}

Naley zauway, e metoda swapHelper jest oglna, podczas gdy majca stay parametr typu
Pair<?> swap nie.
Metod swapHelper moemy wywoa w metodzie swap:
public static void swap(Pair<?> p) { swapHelper(p); }

W tym przypadku parametr T metody swapHelper chwyta typ wieloznaczny. Nie wiadomo,
jaki typ okrela doker, ale jest to typ okrelony, dziki czemu definicja <T>swapHelper jest
w peni prawidowa, jeli T okrela tamten typ.
Oczywicie w tym przypadku nie musielimy uywa typu wieloznacznego. Mona byo
bezporednio zaimplementowa metod <T> void swap(Pair<T> p) jako ogln, nie uywajc
dokerw. Spjrzmy jednak na poniszy fragment kodu, w ktrym typ wieloznaczny ma swoje
naturalne miejsce w obliczeniach:
public static void maxminBonus(Manager[] a, Pair<? super Manager> result)
{
minmaxBonus(a, result);
PairAlg.swapHelper(result);
// OK metoda swapHelper chwyta typ wieloznaczny
}

W tym przypadku mechanizmu chwytania typu wieloznacznego nie mona byo pomin.
Chwytanie typu wieloznacznego jest dozwolone tylko w cile okrelonych warunkach.
Kompilator musi by w stanie zagwarantowa, e symbol wieloznaczny reprezentuje jeden
okrelony typ. Na przykad T w ArrayList<Pair<T>> nie moe uchwyci typu wieloznacznego w ArrayList<Pair<?>>. Lista ta moe zawiera dwa typy Pair<?>, a symbol ? w kadym
z nich moe reprezentowa inny typ.
Program przedstawiony na listingu 12.3 demonstruje zastosowanie w praktyce omwionych
do tej pory technik.
Listing 12.3. pair3/PairTest3.java
package pair3;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class PairTest3
{
public static void main(String[] args)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.
{

Programowanie oglne

Manager ceo = new Manager("Stanisaw Skpy", 800000, 2003, 12, 15);


Manager cfo = new Manager("Piotr Podstpny", 600000, 2003, 12, 15);
Pair<Manager> buddies = new Pair<>(ceo, cfo);
printBuddies(buddies);
ceo.setBonus(1000000);
cfo.setBonus(500000);
Manager[] managers = { ceo, cfo };

Pair<Employee> result = new Pair<>();


minmaxBonus(managers, result);
System.out.println("pierwszy: " + result.getFirst().getName()
+ ", drugi: " + result.getSecond().getName());
maxminBonus(managers, result);
System.out.println("pierwszy: " + result.getFirst().getName()
+ ", drugi: " + result.getSecond().getName());

public static void printBuddies(Pair<? extends Employee> p)


{
Employee first = p.getFirst();
Employee second = p.getSecond();
System.out.println(first.getName() + " i " + second.getName() + "
s kumplami.");
}
public static void minmaxBonus(Manager[] a, Pair<? super Manager> result)
{
if (a == null || a.length == 0) return;
Manager min = a[0];
Manager max = a[0];
for (int i = 1; i < a.length; i++)
{
if (min.getBonus() > a[i].getBonus()) min = a[i];
if (max.getBonus() < a[i].getBonus()) max = a[i];
}
result.setFirst(min);
result.setSecond(max);
}

public static void maxminBonus(Manager[] a, Pair<? super Manager> result)


{
minmaxBonus(a, result);
PairAlg.swapHelper(result); // metoda swapHelper chwyta typ wieloznaczny
}

class PairAlg
{
public static boolean hasNulls(Pair<?> p)
{
return p.getFirst() == null || p.getSecond() == null;
}
public static void swap(Pair<?> p) { swapHelper(p); }
public static <T> void swapHelper(Pair<T> p)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

657

658

Java. Podstawy
T t = p.getFirst();
p.setFirst(p.getSecond());
p.setSecond(t);
}
}

12.9. Refleksja a typy oglne


Klasa Class jest obecnie oglna. Na przykad String.class jest obiektem (w rzeczywistoci
jedynym obiektem) klasy Class<String>.
Przydatno parametru typowego polega na tym, e pozwala on na dokadniejsze okrelenie
typw zwrotnych metod klasy Class<T>. Ponisze metody klasy Class<T> korzystaj z parametru typowego:
T newInstance()
T cast(Object obj)
T[] getEnumConstants()
Class<? super T> getSuperclass()
Constructor<T> getConstructor(Class... parameterTypes)
Constructor<T> getDeclaredConstructor(Class... parameterTypes)

Metoda newInstance zwraca egzemplarz tej klasy utworzony za pomoc konstruktora domylnego. Jej typ zwrotny mona teraz okreli jako T, czyli taki sam jak klasy Class<T>. W ten
sposb unikamy rzutowania.
Metoda cast zwraca przekazany do niej obiekt rzutowany na typ T, jeli jego typ jest podtypem T. W przeciwnym razie generuje wyjtek BadCastException.
Metoda getEnumConstants zwraca null, jeli klasa nie jest klas enum, lub tablic wartoci
wyliczenia, ktre wiadomo, e s typu T.
Metody getConstructor i getDeclaredConstructor zwracaj obiekt typu Constructor<T>. Klasa
Constructor rwnie jest ju oglna, dlatego jej metoda newInstance ma odpowiedni typ
zwrotny.
java.lang.Class<T> 1.0

T newInstance() 5.0

Zwraca egzemplarz utworzony za pomoc konstruktora domylnego.

T cast(Object obj) 5.0

Zwraca obj, jeli jest null, lub moe by przekonwertowany na typ T, w przeciwnym
przypadku generuje wyjtek BadCastException.

T[] getEnumConstants() 5.0

Zwraca tablic zapenion wartociami, jeli T jest typem wyliczeniowym, lub null
w przeciwnym przypadku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

659

Class<? super T> getSuperclass() 5.0

Zwraca nadklas tej klasy lub null, jeli T nie jest w ogle klas lub jest klas Object.

Constructor<T> getConstructor(Class... parameterTypes) 5.0

Constructor<T> getDeclaredConstructor(Class... parameterTypes) 5.0

Zwraca konstruktor publiczny lub konstruktor majcy parametry o podanych typach.


java.lang.reflect.Constructor<T> 1.1

T newInstance(Object... parameters) 5.0

Tworzy nowy egzemplarz powstay przy uyciu podanych parametrw.

12.9.1. Zastosowanie parametrw Class<T> do dopasowywania typw


Czasami dobrze jest dopasowa zmienn typow parametru Class<T> w metodzie generycznej.
Oto kanoniczny przykad:
public static <T> Pair<T> makePair(Class<T> c) throws InstantiationException,
IllegalAccessException
{
return new Pair<T>(c.newInstance(), c.newInstance());
}

Jeli wywoamy ponisz metod:


makePair(Employee.class)

Employee.class bdzie obiektem typu Class<Employee>. Parametr typu T metody makePair


odpowiada Employee, dziki czemu kompilator moe wywnioskowa, e metoda ta zwrci typ
Pair<Employee>.

12.9.2. Informacje o typach generycznych w maszynie wirtualnej


Jedn z godnych odnotowania cech typw oglnych w Javie jest wymazywanie typw
w maszynie wirtualnej. Moe to brzmie zaskakujco, ale oczyszczone klasy pamitaj o tym,
e byy oglne. Na przykad surowa klasa Pair wie, e powstaa z parametryzowanej klasy
Pair<T>, mimo e obiekt typu Pair nie ma informacji, czy zosta utworzony jako Pair<String>,
czy Pair<Employee>.
Spjrzmy na ponisz metod:
public static Comparable min(Comparable[] a)

Powstaa ona w wyniku oczyszczenia poniszej metody oglnej:


public static <T extends Comparable<? super T>> T min(T[] a)

Za pomoc rozszerze API refleksji mona zdoby nastpujce informacje:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

660

Java. Podstawy

Metoda generyczna ma parametr typu o nazwie T.

Parametr typu ma ograniczenie podtypu, ktre samo jest typem generycznym.

Typ ograniczajcy ma parametr wieloznaczny.

Parametr wieloznaczny ma ograniczenie nadtypu.

Metoda oglna ma parametr w postaci tablicy oglnej.

Innymi sowy, mona zdoby wszystkie dane dotyczce klas i metod parametryzowanych,
ktre zostay podane w ich deklaracjach. Nie ma natomiast sposobu na dowiedzenie si, jak
parametry typowe zostay zastpione w konkretnych obiektach lub wywoaniach metod.
Informacje o typach zawarte w plikach klas, ktre umoliwiaj stosowanie refleksji
wzgldem typw oglnych, nie s zgodne ze starszymi wersjami maszyny wirtualnej.

W pakiecie java.lang.reflect znajduje si interfejs o nazwie Type, ktrego celem jest informowanie o deklaracjach typw oglnych. Interfejs ten ma nastpujce podtypy:

klasa Class opisujca typy konkretne;

interfejs TypeVariable opisujcy zmienne typowe (jak T extends Comparable<?


super T>);

interfejs WildcardType opisujcy typy wieloznaczne (jak ? super T);

interfejs ParameterizedType opisujcy typy klas oglnych lub interfejsowe


(na przykad Comparable<? super T>);

interfejs GenericArrayType opisujcy tablice oglne (na przykad T[]).

Rysunek 12.5 przedstawia hierarchi dziedziczenia tego interfejsu. Naley zauway, e cztery
ostatnie podtypy s interfejsami maszyna wirtualna tworzy egzemplarze odpowiednich klas
implementujcych te interfejsy.

Rysunek 12.5. Interfejs Type i jego potomkowie

Program przedstawiony na listingu 12.4 wykorzystuje API refleksji oglnej do drukowania


informacji na temat klas. Dla klasy Pair zwrci nastpujcy raport:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

class Pair<T> extends java.lang.Object


public T getFirst()
public T getSecond()
public void setFirst(T)
public void setSecond(T)

Listing 12.4. genericReflection/GenericReflectionTest.java


package genericReflection;
import java.lang.reflect.*;
import java.util.*;
/**
* @version 1.10 2007-05-15
* @author Cay Horstmann
*/
public class GenericReflectionTest
{
public static void main(String[] args)
{
// Wczytanie nazwy klasy z argumentw wiersza polece lub danych wprowadzonych przez uytkownika
String name;
if (args.length > 0) name = args[0];
else
{
Scanner in = new Scanner(System.in);
System.out.println("Wpisz nazw klasy (np. java.util.Collections): ");
name = in.next();
}
try
{
// Wydrukuj dane generyczne o klasie i jej metodach publicznych
Class<?> cl = Class.forName(name);
printClass(cl);
for (Method m : cl.getDeclaredMethods())
printMethod(m);
}
catch (ClassNotFoundException e)
{
e.printStackTrace();
}
}
public static void printClass(Class<?> cl)
{
System.out.print(cl);
printTypes(cl.getTypeParameters(), "<", ", ", ">", true);
Type sc = cl.getGenericSuperclass();
if (sc != null)
{
System.out.print(" extends ");
printType(sc, false);
}
printTypes(cl.getGenericInterfaces(), " implements ", ", ", "", false);
System.out.println();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

661

662

Java. Podstawy
}
public static void printMethod(Method m)
{
String name = m.getName();
System.out.print(Modifier.toString(m.getModifiers()));
System.out.print(" ");
printTypes(m.getTypeParameters(), "<", ", ", "> ", true);
printType(m.getGenericReturnType(), false);
System.out.print(" ");
System.out.print(name);
System.out.print("(");
printTypes(m.getGenericParameterTypes(), "", ", ", "", false);
System.out.println(")");
}
public static void printTypes(Type[] types, String pre, String sep, String suf,
boolean isDefinition)
{
if (pre.equals(" extends ") && Arrays.equals(types, new Type[]
{ Object.class })) return;
if (types.length > 0) System.out.print(pre);
for (int i = 0; i < types.length; i++)
{
if (i > 0) System.out.print(sep);
printType(types[i], isDefinition);
}
if (types.length > 0) System.out.print(suf);
}
public static void printType(Type type, boolean isDefinition)
{
if (type instanceof Class)
{
Class<?> t = (Class<?>) type;
System.out.print(t.getName());
}
else if (type instanceof TypeVariable)
{
TypeVariable<?> t = (TypeVariable<?>) type;
System.out.print(t.getName());
if (isDefinition)
printTypes(t.getBounds(), " extends ", " & ", "", false);
}
else if (type instanceof WildcardType)
{
WildcardType t = (WildcardType) type;
System.out.print("?");
printTypes(t.getUpperBounds(), " extends ", " & ", "", false);
printTypes(t.getLowerBounds(), " super ", " & ", "", false);
}
else if (type instanceof ParameterizedType)
{
ParameterizedType t = (ParameterizedType) type;
Type owner = t.getOwnerType();
if (owner != null)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 12.

Programowanie oglne

663

{
printType(owner, false);
System.out.print(".");
}
printType(t.getRawType(), false);
printTypes(t.getActualTypeArguments(), "<", ", ", ">", false);
}
else if (type instanceof GenericArrayType)
{
GenericArrayType t = (GenericArrayType) type;
System.out.print("");
printType(t.getGenericComponentType(), isDefinition);
System.out.print("[]");
}
}
}

Jeli uruchomimy go na rzecz klasy ArrayAlg w katalogu PairTest2, raport bdzie zawiera
nastpujce dane:
public static <T extends java.lang.Comparable> Pair<T> minmax(T[])

Metody uyte w tym programie zostay opisane w wycigu z API na kocu podrozdziau.
java.lang.Class<T> 1.0

TypeVariable[] getTypeParameters() 5.0

Zwraca zmienne typowe typu oglnego, jeli typ ten zosta zadeklarowany
jako oglny, lub tablic o dugoci 0 w przeciwnym przypadku.

Type getGenericSuperclass() 5.0

Zwraca typ oglny nadklasy, ktra zostaa zadeklarowana dla tego typu, lub null,
jeli typem tym jest Object albo typ niebdcy klas.

Type[] getGenericInterfaces() 5.0

Zwraca typy oglne interfejsw, ktre zostay zadeklarowane dla tego typu,
przy zachowaniu kolejnoci z deklaracji, lub tablic o dugoci 0, jeli typ ten
nie implementuje interfejsw.
java.lang.reflect.Method 1.1

TypeVariable[] getTypeParameters() 5.0

Zwraca zmienne typowe typu oglnego, jeli metoda ta zostaa zadeklarowana


jako oglna, lub tablic o dugoci 0 w przeciwnym przypadku.

Type getGenericReturnType() 5.0

Zwraca generyczny typ zwrotny, z ktrym zostaa zadeklarowana ta metoda.

Type[] getGenericParameterTypes() 5.0

Zwraca generyczne parametry typowe, z ktrymi zostaa zadeklarowana ta metoda.


Jeli metoda ta nie ma adnych parametrw, zwracana jest tablica o dugoci 0.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

664

Java. Podstawy
java.lang.reflect.TypeVariable 5.0

String getName()

Zwraca nazw zmiennej typowej.

Type[] getBounds()

Zwraca ograniczenia podklasy zmiennej typowej lub tablic o dugoci 0,


jeli zmienna nie ma ogranicze.
java.lang.reflect.WildcardType 5.0

Type[] getLowerBounds()

Zwraca ograniczenia (extends) podklasy zmiennej typowej lub tablic o dugoci


0, jeli nie ma adnych ogranicze podklasowych.

Type[] getUpperBounds()

Zwraca ograniczenia (super) nadklasy zmiennej typowej lub tablic o dugoci 0,


jeli nie ma adnych ogranicze nadklasowych.
java.lang.reflect.ParameterizedType 5.0

Type getRawType()

Zwraca typ surowy typu parametryzowanego.

Type[] getActualTypeArguments()

Zwraca parametry typu, ktre zostay uyte w deklaracji typu parametryzowanego.

Type getOwnerType()

Zwraca typ klasy zewntrznej, jeli jest wywoana na rzecz klasy wewntrznej,
lub null w przypadku wywoania na rzecz klasy najwyszego poziomu.
java.lang.reflect.GenericArrayType 5.0

Type getGenericComponentType()

Zwraca generyczny typ komponentu, ktry zosta uyty w deklaracji


tego typu tablicy.
Potrafimy ju uywa klas oglnych i tworzy wasne klasy oraz metody tego typu. Nie
mniej wan umiejtnoci zdobyt w tym rozdziale jest zdolno rozumienia deklaracji typw
oglnych, ktre mona spotka w dokumentacji API i komunikatach o bdach. Wyczerpujcym rdem wiedzy na temat typw oglnych w Javie jest, sporzdzona przez Angelik Langer, znakomita lista czsto (i niezbyt czsto) zadawanych pyta na ten temat:
http://angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html.
W kolejnym rozdziale zobaczymy, jak z typw generycznych korzystaj kolekcje.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

13

Kolekcje
W tym rozdziale:

Interfejsy kolekcyjne

Konkretne klasy kolekcyjne

Architektura kolekcji

Algorytmy

Stare kolekcje

Dobr struktur danych do uycia w programie moe mie niebagatelne znaczenie dla pniejszej implementacji metod i wydajnoci caej aplikacji. Decydujc si na konkretne struktury, naley odpowiedzie sobie na pytania typu: czy konieczne bdzie przeszukiwanie tysicy
(moe nawet milionw) posortowanych elementw? Czy konieczne bdzie szybkie wstawianie i usuwanie elementw do i ze rodka uporzdkowanego szeregu elementw? Czy konieczne
bdzie powizanie wartoci z ich kluczami?
Ten rozdzia traktuje o strukturach danych dostpnych w bibliotece Javy. Na studiach informatycznych przedmiot dotyczcy struktur danych trwa z reguy jeden semestr, dziki czemu
ta wana tematyka zostaa wyczerpujco przedstawiona w bardzo licznych publikacjach i opracowaniach. W tej ksice prezentujemy odmienne podejcie w stosunku do innych publikacji
z tego zakresu pomijamy teori, a koncentrujemy si na zastosowaniu kolekcji dostpnych
w bibliotece standardowej w profesjonalnym programowaniu.

13.1. Interfejsy kolekcyjne


Pocztkowo w Javie dostpnych byo tylko kilka klas implementujcych najbardziej przydatne struktury danych: Vector, Stack, Hashtable, BitSet oraz interfejs Enumerable odpowiadajcy za abstrakcyjny mechanizm dostpu do elementw w kadym z tych kontenerw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

666

Java. Podstawy
Byo to z pewnoci mdre posunicie ze strony projektantw jzyka opracowanie penej
biblioteki klas kolekcyjnych wymaga czasu i dowiadczenia.
Projektanci doszli do wniosku, e czas na zaprezentowanie penego zestawu struktur danych
nadszed w Java 1.2. Musieli dokona wielu trudnych wyborw, poniewa chcieli stworzy
bibliotek majc niewielkie rozmiary i atw do opanowania dla programistw. Starali si
unikn poziomu zoonoci charakteryzujcego bibliotek C++ STL (ang. Standard Template
Library), jednoczenie prbujc skorzysta z dobrodziejstw algorytmw uoglnionych, ktrych pionierem bya wanie wymieniona biblioteka. Stare klasy musiay pasowa do nowej
architektury. Jak to zwykle bywa przy projektowaniu bibliotek kolekcji, kilkakrotnie stawano
przed trudnym wyborem, co zaowocowao kilkoma osobliwymi decyzjami projektowymi.
W tym podrozdziale przedstawiamy podstawow struktur architektury kolekcji Javy, pokazujemy sposoby jej wykorzystania oraz wyjaniamy, czym kierowali si projektanci, podejmujc niektre bardziej kontrowersyjne decyzje.

13.1.1. Oddzielenie warstwy interfejsw od warstwy klas konkretnych


Podobnie jak wikszo nowoczesnych bibliotek struktur danych, biblioteka kolekcji w Javie
dzieli si na warstw interfejsw i warstw implementacji. Przyjrzymy si temu podziaowi
na przykadzie znanej struktury danych o nazwie kolejka (ang. queue).
Interfejs Queue pozwala na dodawanie elementw na kocu kolejki, usuwanie ich z pocztku
struktury danych oraz sprawdzanie liczby elementw w kolejce. Ta struktura danych znajduje
zastosowanie przy tworzeniu zbiorw obiektw, z ktrych elementy s pobierane zgodnie
z zasad pierwszy przyjdzie, pierwszy wyjdzie (zobacz rysunek 13.1).
Rysunek 13.1.
Kolejka

Interfejs Queue w swojej uproszczonej formie moe wyglda nastpujco:


interface Queue<E>
// uproszczona wersja interfejsu z biblioteki standardowej
{
void add(E element);
E remove();
int size();
}

Sam interfejs nie zawiera adnych danych na temat implementacji kolejki. Z dwch najczciej spotykanych implementacji kolejki jedna dziaa na zasadzie listy cyklicznej, a druga listy
powizanej (rysunek 13.2).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

667

Rysunek 13.2. Implementacje kolejki

Kad z tych implementacji mona zrealizowa za pomoc klasy implementujcej interfejs


Queue.
class CircularArrayQueue<E> implements Queue<E>
{
CircularArrayQueue(int capacity) { . . . }
public void add(E element) { . . . }
public E remove() { . . . }
public int size() { . . . }

// klasa niebiblioteczna

private E[] elements;


private int head;
private int tail;
}
class LinkedListQueue<E> implements Queue<E>
{
LinkedListQueue() { . . . }
public void add(E element) { . . . }
public E remove() { . . . }
public int size() { . . . }

// klasa niebiblioteczna

private Link head;


private Link tail;
}

Programista uywajcy w programie kolejki po utworzeniu kolekcji nie musi wiedzie,


ktra jej implementacja zostaa uyta. Dlatego dobrym rozwizaniem jest uywanie klas
konkretnych tylko do konstruowania obiektw kolekcyjnych. Typw interfejsowych naley
uywa do przechowywania referencji do tych obiektw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

668

Java. Podstawy

W bibliotece Javy nie ma klas o nazwach CircularArrayQueue i LinkedListQueue.


Uywamy ich do wyjanienia rnicy pomidzy interfejsami kolekcyjnymi a implementacjami. Do utworzenia listy cyklicznej mona uy wprowadzonej w Java SE 6 klasy
ArrayDequeue. Listy powizane tworzy klasa LinkedList, ktra implementuje interfejs
Queue.
Queue<Customer> expressLane = new CircularArrayQueue<Customer>(100);
expressLane.add(new Customer("Henryk"));

Dziki takiemu podejciu w razie zmiany zdania mona atwo uy innej implementacji.
Wystarczy tylko jedna zmiana w programie wywoanie konstruktora. Jeli na przykad
dojdziemy do wniosku, e lepszym wyborem byby obiekt typu LinkedListQueue, zmieniamy
kod na nastpujcy:
Queue<Customer> expressLane = new LinkedListQueue<Customer>();
expressLane.add(new Customer("Henryk"));

Co sprawia, e wybieramy jedn implementacj zamiast drugiej? Interfejs nie dostarcza adnych informacji na temat wydajnoci. Lista cykliczna jest nieco szybsza od listy powizanej, a wic oglnie rzecz biorc jest preferowana. Jednak jak zawsze jest co za co.
Lista cykliczna jest kolekcj ograniczon, czyli ma ograniczon pojemno. Jeli nie okrelimy limitu obiektw przechowywanych w takiej licie, niewykluczone, e lepiej bymy na
tym wyszli, gdybymy zastosowali list powizan.
W dokumentacji API mona znale jeszcze jeden zestaw klas, ktrych nazwy zaczynaj si
od sowa Abstract, na przykad AbstractQueue. Klasy te s przeznaczone dla twrcw bibliotek. W razie (mao prawdopodobnej) potrzeby zaimplementowania wasnej klasy kolejki atwiej
rozszerzy klas AbstractQueue, ni zaimplementowa wszystkie metody interfejsu Queue.

13.1.2. Interfejsy Collection i Iterator


Interfejsem o kluczowym znaczeniu w hierarchii kolekcji jest interfejs Collection. Dwie
z jego metod maj fundamentalne znaczenie:
public interface Collection<E>
{
boolean add(E element);
Iterator<E> iterator();
. . .
}

Pozostae metody tego interfejsu omawiamy dalej.


Metoda add dodaje elementy do kolekcji i zwraca warto true, jeli wykonana przez ni
operacja powoduje zmian stanu kolekcji, lub warto false, jeli kolekcja nie ulega zmianie.
Jeli na przykad do zbioru (ang. set) sprbujemy doda obiekt, ktry ju si tam znajduje,
metoda add nie przyniesie adnego skutku, poniewa w zbiorach nie moe by duplikatw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

669

Metoda iterator zwraca obiekt implementujcy interfejs Iterator. Za pomoc tego obiektu
mona odwiedzi kolejno wszystkie znajdujce si w kolekcji elementy.

13.1.2.1. Iteratory
W interfejsie Iterator wystpuj trzy metody:
public interface Iterator<E>
{
E next();
boolean hasNext();
void remove();
}

Wywoujc wielokrotnie metod next, mona kolejno odwiedzi wszystkie elementy kolekcji.
Jeli metoda ta napotka koniec kolekcji, zgosi wyjtek NoSuchElementException. Dlatego
przed ni naley zawsze wywoywa metod hasNext, ktra zwraca warto true, jeli s
jeszcze jakie elementy. Aby przejrze wszystkie elementy kolekcji, naley utworzy obiekt
typu Iterator i wywoywa metod next tak dugo, jak metoda hasNext zwraca warto true.
Na przykad:
Collection<String> c = . . .;
Iterator<String> iter = c.iterator();
while (iter.hasNext())
{
String element = iter.next();
obrbka elementu
}

W Java SE 5.0 wprowadzono zgrabniejsz wersj tej ptli. Jej bardziej zwizy zapis realizuje si za pomoc ptli w stylu for each:
for (String element : c)
{
obrbka elementu
}

Kompilator konwertuje ptl w stylu for each na ptl z iteratorem.


Ptl for each mona stosowa do wszystkich obiektw implementujcych interfejs Iterable,
w ktrym znajduje si tylko jedna metoda:
public interface Iterable<E>
{
Iterator<E> iterator();
}

Interfejs Collection rozszerza interfejs Iterable. Dziki temu ptli for each mona uywa
do dziaa na wszystkich kolekcjach ze standardowej biblioteki.
Kolejno odwiedzania elementw zaley od rodzaju kolekcji. W przypadku listy ArrayList
iterator zaczyna od indeksu o numerze 0 i zwiksza ten numer w kadym kolejnym powtrzeniu (iteracji). Natomiast dostp do elementw w zbiorze HashSet odbywa si w zasadzie

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

670

Java. Podstawy
losowo. Pewne jest, e przemierzajc t kolekcj, uzyskamy dostp do kadego jej elementu,
ale nie wiadomo, w jakiej kolejnoci bdzie si to odbywa. Nie sprawia to zazwyczaj problemu, poniewa w dziaaniach typu obliczanie sumy lub zliczanie elementw pasujcych do
wzorca kolejno jest nieistotna.
Ci, ktrzy znaj wczeniejsze wersje Javy, zauwa, e metody next i hasNext interfejsu Iterator speniaj t sam funkcj co nextElement i hasMoreElements
w interfejsie Enumeration. Projektanci biblioteki kolekcyjnej mogli wykorzysta w swojej pracy interfejs Enumeration, ale nie podobay im si niezgrabne nazwy jego metod.
Dlatego utworzono nowy interfejs z krtszymi nazwami metod.

Pomidzy iteratorami w bibliotece kolekcyjnej w Javie a iteratorami z innych bibliotek istnieje wana rnica koncepcyjna. Modelem iteratorw w tradycyjnych bibliotekach, takich jak
Standard Template Library w C++, s indeksy tablicowe. Za pomoc takiego tradycyjnego
iteratora element znajdujcy si w okrelonym miejscu mona odszuka w podobny sposb
jak element a[i] w tablicy, jeli znany jest indeks i. Niezalenie od wyszukiwania, iterator
taki mona przesun do kolejnego elementu, co niczym si nie rni od operacji zwikszania indeksu za pomoc instrukcji i++, bez wyszukiwania. Iteratory w Javie dziaaj nieco
inaczej. Wyszukiwanie i zmiana pooenia s ze sob cile zwizane. Jedynym sposobem
na znalezienie elementu jest wywoanie metody next, a to powoduje przejcie do kolejnego
elementu.
Iteratory w Javie naley wyobraa sobie jako obiekty znajdujce si pomidzy elementami. W chwili wywoania metody next iterator przeskakuje kolejny element i zwraca referencj do elementu, ktry wanie przeskoczy (zobacz rysunek 13.3).
Istnieje jeszcze inna ciekawa analogia. Instrukcj Iterator.next mona traktowa
jako odpowiednik instrukcji InputStream.read. Odczyt bajta ze strumienia automatycznie oznacza jego poknicie. Kolejne wywoanie metody read powoduje poknicie i zwrcenie nastpnego bajta z danych wejciowych. W podobny sposb seria
wywoa metody next zwraca wszystkie elementy znajdujce si w kolekcji.

13.1.2.2. Usuwanie elementw


Metoda remove z interfejsu Iterator usuwa element zwrcony przez ostatnie wywoanie
metody next. W wikszoci sytuacji jest to rozsdne dziaanie aby podj decyzj o usuniciu elementu, najczciej trzeba go wpierw zobaczy. Aby usun element znajdujcy
si w okrelonym miejscu, take trzeba za niego przej. Na przykad ponisza procedura
usuwa pierwszy element z kolekcji acuchw:
Iterator<String> it = c.iterator();
it.next();
// przejcie za pierwszy element
it.remove();
// usunicie pierwszego elementu

Midzy metodami next i remove istnieje pewna bardzo wana zaleno. Tej drugiej nie mona
wywoa, jeli wczeniej nie wywoano pierwszej. Prba zrobienia tego zakoczy si rzuceniem wyjtku IllegalStateException.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

671

Rysunek 13.3. Przesuwanie iteratora

Aby usun dwa kolejne elementy, nie mona zastosowa dwch kolejnych wywoa metody
remove:
it.remove();
it.remove();

// Bd!

Najpierw trzeba wywoa metod next, aby przej za kolejny element, ktry ma zosta
usunity.
it.remove();
it.next();
it.remove();

// OK

13.1.2.3. Uoglnione metody uytkowe


Dziki temu, e interfejsy Collection i Iterator s uoglnione, mona pisa metody uytkowe operujce na dowolnych rodzajach kolekcji. Poniej znajduje si przykadowa metoda
uoglniona sprawdzajca, czy dowolnego rodzaju kolekcja zawiera okrelony element:
public static <E> boolean contains(Collection<E> c, Object obj)
{
for (E element : c)
if (element.equals(obj))
return true;
return false;
}

Twrcy biblioteki standardowej doszli do wniosku, e niektre z tych metod s tak przydatne, i powinny by dostpne w bibliotece. Dziki temu uytkownicy tego zbioru klas nie
musz wielokrotnie wynajdywa koa. Jedna z tych metod nosi nazw contains.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

672

Java. Podstawy
W rzeczywistoci w interfejsie Collection znajduje si spora liczba przydatnych metod,
ktre musz by udostpniane przez wszystkie implementujce go klasy. Nale do nich:
int size()
boolean isEmpty()
boolean contains(Object obj)
boolean containsAll(Collection<?> c)
boolean equals(Object other)
boolean addAll(Collection<? extends E> from)
boolean remove(Object obj)
boolean removeAll(Collection<?> c)
void clear()
boolean retainAll(Collection<?> c)
Object[] toArray()
<T> T[] toArray(T[] arrayToFill)

Przeznaczenie wielu z tych metod atwo odgadn po nazwie. Peny ich opis znajduje si na
kocu podrozdziau w wycigach z API.
Oczywicie definiowanie tylu metod we wszystkich klasach implementujcych interfejs
Collection jest bardzo uciliwe. Aby uatwi ycie programistom, utworzono klas
AbstractCollection implementujc wszystkie metody tego interfejsu w kategorii metod
size i iterator, ktre jako jedyne pozostay w niej abstrakcyjne. Na przykad:
public abstract class AbstractCollection<E>
implements Collection<E>
{
. . .
public abstract Iterator<E> iterator();
public boolean contains(Object obj)
{
for (E element : c)
// wywouje metod iterator()
if (element.equals(obj))
return = true;
return false;
}
. . .
}

Konkretna klasa kolekcyjna moe by rozszerzeniem klasy AbstractCollection. W klasie


konkretnej konieczne jest zdefiniowanie metody iterator, ale metoda contains jest ju
w niej dostpna, poniewa zostaje odziedziczona po nadklasie abstrakcyjnej AbstractCol
lection. Jeli jednak w podklasie istnieje moliwo zdefiniowania efektywniejszej metody
contains, nic nie stoi na przeszkodzie, aby to zrobi.
Jest to bardzo dobre podejcie do projektowania architektury klas. Uytkownicy kolekcji maj
do dyspozycji bogaty zestaw metod, a twrcy struktur danych nie s obciani implementacj
wszystkich rutynowych metod.
java.util.Collection<E> 1.2

Iterator<E> iterator()

Zwraca obiekt Iterator, za pomoc ktrego mona odwiedza elementy kolekcji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

int size()

Zwraca liczb elementw przechowywanych w kolekcji.

boolean isEmpty()

Zwraca warto true, jeli kolekcja nie zawiera adnych elementw.

boolean contains(Object obj)

Zwraca warto true, jeli kolekcja zawiera obiekt identyczny z obiektem obj.

boolean containsAll(Collection<?> other)

Zwraca warto true, jeli kolekcja zawiera wszystkie elementy znajdujce


si w innej kolekcji.

boolean add(Object element)

Dodaje element do kolekcji. Zwraca warto true, jeli w wyniku wywoania


w kolekcji nastpiy zmiany.

boolean addAll(Collection<? extends E> other)

Dodaje do kolekcji wszystkie elementy z kolekcji other. Zwraca warto true,


jeli w wyniku wywoania w kolekcji nastpiy zmiany.

boolean remove(Object obj)

Usuwa obiekt obj z kolekcji. Zwraca warto true, jeli obiekt zosta znaleziony
i usunity.

boolean removeAll(Collection<?> other)

Usuwa z kolekcji wszystkie elementy, ktre mona znale w kolekcji other.


Zwraca warto true, jeli w wyniku wywoania w kolekcji nastpiy zmiany.

void clear()

Usuwa wszystkie elementy z kolekcji.

boolean retainAll(Collection<?> other)

Usuwa z kolekcji wszystkie elementy, ktre nie s takie same jak jeden z obiektw
w kolekcji other. Zwraca warto true, jeli w wyniku wywoania w kolekcji
nastpiy zmiany.

Object[] toArray()

Zwraca tablic obiektw zapenion elementami z kolekcji.

<T> T[] toArray(T[] arrayToFill)

Zwraca tablic zapenion obiektami z kolekcji. Jeli tablica arrayToFill


jest odpowiedniej dugoci, zostaje zapeniona elementami kolekcji. Dodatkowe
miejsca s zapeniane wartociami null. W przeciwnym przypadku tworzona
jest nowa tablica takiego samego typu jak arrayToFill i o takiej samej dugoci
jak rozmiar kolekcji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

673

674

Java. Podstawy
java.util.Iterator<E> 1.2

boolean hasNext()

Zwraca warto true, jeli s jeszcze elementy do odwiedzenia.

E next()

Zwraca nastpny obiekt do odwiedzenia. Wyrzuca wyjtek NoSuchElementException,


jeli osignie koniec kolekcji.

void remove()

Usuwa ostatnio odwiedzony obiekt. Wywoanie tej metody musi nastpowa


bezporednio po odwiedzeniu elementu. Jeli kolekcja zmienia si od ostatniego
odwiedzenia elementu, metoda ta wyrzuci wyjtek IllegalStateException.

13.2. Konkretne klasy kolekcyjne


Zamiast zbytnio zagbia si w tajniki wszystkich interfejsw, postanowilimy opisa najpierw konkretne struktury danych. Po dokadnym zapoznaniu si z klasami konkretnymi
wrcimy do tematyki abstrakcyjnej, a zwaszcza przyjrzymy si organizacji architekturalnej
tych klas. Tabela 13.1 przedstawia zestawienie kolekcji dostpnych w standardowej bibliotece
oraz ich krtkie opisy (dla uproszczenia pominlimy kolekcje bezpieczne dla wtkw, ktre
zostay opisane w rozdziale 14.). Wszystkie klasy prezentowane w tabeli, z wyjtkiem tych,
ktrych nazwy kocz si sowem Map, implementuj interfejs Collection. Mapy implementuj interfejs Map i zostay opisane w podrozdziale 13.2.8, Mapy.

13.2.1. Listy powizane


W wielu prezentowanych do tej pory przykadach kodu wykorzystywalimy tablice i ich dynamicznego krewniaka, czyli klas ArrayList. Niestety tablice i listy tablicowe maj jedn
powan wad usuwanie elementw z ich rodka jest mao efektywne, poniewa czynno ta wymaga przesunicia wszystkich elementw znajdujcych si za tym usuwanym
w stron pocztku (zobacz rysunek 13.4). To samo dotyczy wstawiania elementw.
Problem ten pomaga rozwiza inna powszechnie znana struktura danych, nazywana list
powizan (ang. linked list). Podczas gdy obiekty w tablicy s zapisywane w kolejnych komrkach pamici, w licie powizanej znajduj si one w osobnych ogniwach (ang. link). Kade
ogniwo przechowuje take referencj do nastpnego ogniwa w szeregu. W Javie praktycznie
wszystkie listy powizane s listami dwukierunkowymi (ang. doubly linked list), co oznacza, e kade ogniwo przechowuje take referencj do ogniwa je poprzedzajcego (zobacz
rysunek 13.5).
Usuwanie elementw z rodka listy powizanej charakteryzuje si krtkim czasem wykonania,
poniewa aktualizowane s tylko elementy znajdujce si po obu stronach usuwanego obiektu
(zobacz rysunek 13.6).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

675

Tabela 13.1. Kolekcje konkretne w bibliotece Javy


Typ kolekcji

Opis

ArrayList

Indeksowana lista o dynamicznie zmieniajcych si rozmiarach

LinkedList

Uporzdkowana lista pozwalajca na szybkie wstawianie i usuwanie elementw


w dowolnej lokalizacji

ArrayDeque

Nieposiadajca ani pocztku, ani koca lista cykliczna

HashSet

Nieuporzdkowana kolekcja, w ktrej wszystkie obiekty musz by unikatowe

TreeSet

Uporzdkowany zbir

EnumSet

Zbir wartoci typu wyliczeniowego

LinkedHashSet

Zbir pamitajcy kolejno wstawianych do niego elementw

PriorityQueue

Kolekcja pozwalajca na szybkie usunicie najmniejszego elementu

HashMap

Struktura danych przechowujca pary klucz warto

TreeMap

Mapa sortujca klucze

EnumMap

Mapa, w ktrej klucze s typami wyliczeniowymi

LinkedHashMap

Mapa pamitajca kolejno wstawianych do niej elementw

WeakHashMap

Mapa, ktrej wartoci mog zosta usunite przez system zbierania nieuytkw,
jeli nie s uywane gdzie indziej

IdentityHashMap

Mapa przechowujca klucze porwnywane za pomoc operatora == zamiast equals

Rysunek 13.4.
Usuwanie
elementu
z tablicy

Wiele osb ukoczyo kurs struktur danych, na ktrym uczy si implementacji list powizanych. Niektrzy mog mie ze wspomnienia zwizane z pltanin pocze przy usuwaniu
lub dodawaniu elementw do list powizanych. Dla tych osb mamy dobr wiadomo
w bibliotece kolekcji Javy znajduje si gotowa do uycia klasa LinkedList.
Poniszy fragment programu dodaje do listy trzy elementy, a nastpnie usuwa drugi element:
List<String> staff = new LinkedList<String>();
staff.add("Ania");
staff.add("Bartek");

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

// Klasa LinkedList implementuje interfejs List

676

Java. Podstawy

Rysunek 13.5. Lista dwukierunkowa

Rysunek 13.6. Usuwanie elementu z listy powizanej


staff.add("Karol");
Iterator iter = staff.iterator();
String first = iter.next();
String second = iter.next();
iter.remove();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

// dojcie do pierwszego elementu


// dojcie do drugiego elementu
// usunicie ostatnio odwiedzonego elementu

Rozdzia 13.

Kolekcje

677

Pomidzy listami powizanymi a kolekcjami uoglnionymi istnieje pewna istotna rnica.


Lista powizana jest kolekcj uporzdkowan, w ktrej pooenie obiektw ma znaczenie.
Metoda LinkedList.add dodaje obiekt na kocu listy. Czsto jednak elementy s wstawiane
do rodka listy. Metoda add zwizana z pooeniem elementu znajduje si w zestawie obowizkw iteratora, poniewa iteratory su do opisu pooenia elementw w kolekcjach.
Zastosowanie iteratorw do dodawania elementw jest uzasadnione tylko w uporzdkowanych kolekcjach. Na przykad omawiane w nastpnej kolejnoci zbiory s strukturami danych,
ktre nie porzdkuj w aden sposb swoich elementw. Dlatego w interfejsie Iterator nie
ma metody add. Umieszczono j za to w interfejsie ListIterator:
interface ListIterator<E> extends Iterator<E>
{
void add(E element);
. . .
}

W przeciwiestwie do metody Collection.add, ta metoda nie zwraca wartoci logicznej


zakada si, e metoda add zawsze modyfikuje list.
Dodatkowo interfejs ListIterator zawiera dwie metody, za pomoc ktrych mona porusza
si po licie wstecz.
E previous()
boolean hasPrevious()

Metoda previous, podobnie jak next, zwraca obiekt, ktry wanie przeskoczya.
Metoda listIterator z klasy LinkedList zwraca obiekt iteratora implementujcy interfejs
ListIterator.
ListIterator<String> iter = staff.listIterator();

Metoda add dodaje element przed iteratorem. Na przykad ponisze instrukcje pomijaj pierwszy element na licie powizanej i dodaj element Julia przed drugim elementem (zobacz
rysunek 13.7):
List<String> staff = new LinkedList<String>();
staff.add("Ania");
staff.add("Bartek");
staff.add("Karol");
ListIterator<String> iter = staff.listIterator();
iter.next();
// pominicie pierwszego elementu
iter.add("Julia");

Jeli metoda add zostanie wywoana kilka razy, elementy zostan dodane do listy w kolejnoci podawania. Kady z nich jest dodawany przed aktualnym miejscem pobytu iteratora.
Jeli metoda add zostanie uyta z nieuywanym jeszcze iteratorem utworzonym przez metod listIterator, wskazujcym pocztek listy powizanej, nowy element zostanie dodany
na samym pocztku listy. Jeli iterator przejdzie za ostatni element listy (czyli znajdzie si
w miejscu, w ktrym metoda hasNext zwraca warto false), dodawany element bdzie
stanowi nowy ogon listy. W licie o dugoci n istnieje n+1 miejsc, w ktrych mona wstawia elementy. Ta liczba odpowiada liczbie pooe, w ktrych moe si znale iterator.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

678

Java. Podstawy

Rysunek 13.7. Dodawanie elementu do listy powizanej

Jeli na przykad lista powizana zawiera trzy elementy A, B i C, to istniej w niej cztery
miejsca (oznaczone znakiem |), w ktrych mona wstawi nowy element:
|ABC
A|BC
AB|C
ABC|

Przy stosowaniu analogii do kursora trzeba zachowa ostrono. Metoda remove


nie dziaa dokadnie tak jak klawisz Backspace. Rzeczywicie bezporednio po wywoaniu metody next usuwa ona element znajdujcy si po lewej stronie iteratora, dokadnie
jak klawisz Backspace. Jeli jednak przed ni zostaa wywoana metoda previous, zostanie usunity element znajdujcy si po prawej stronie iteratora. Ponadto metody remove
nie mona wywoa dwa razy z rzdu.
Metoda remove, w przeciwiestwie do add, polegajcej wycznie na pooeniu iteratora,
bierze pod uwag stan iteratora.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

679

W kocu metoda set zastpuje ostatni element zwrcony przez metod next lub previous
nowym elementem. Na przykad ponisza procedura zastpuje pierwszy element listy now
wartoci:
ListIterator<String> iter = list.listIterator();
String oldValue = iter.next();
// zwraca pierwszy element
iter.set(newValue);
// ustawia pierwszy element na newValue

Nietrudno sobie wyobrazi, e w sytuacji, w ktrej jeden iterator przemierza kolekcj, a inny
j modyfikuje, mog wystpi nieprzyjemne zdarzenia. Wyobramy sobie, e jaki iterator
wskazuje miejsce przed elementem, ktry zosta usunity przez inny iterator. Ten pierwszy
iterator traci w takiej sytuacji racj bytu i nie powinien by uywany. Iteratory list powizanych zostay zaprojektowane w taki sposb, aby wykrywa tego typu modyfikacje. Jeli iterator odkryje, e jego kolekcja zostaa zmodyfikowana przez inny iterator lub metod samej
kolekcji, wyrzuca wyjtek ConcurrentModificationException. Przeanalizujmy poniszy przykadowy kod:
List<String> list = . . .;
ListIterator<String> iter1 = list.listIterator();
ListIterator<String> iter2 = list.listIterator();
iter1.next();
iter1.remove();
iter2.next();
// wyrzuca wyjtek ConcurrentModificationException

Wywoanie metody iter2.next powoduje wyjtek ConcurrentModificationException, poniewa iterator iter2 odkry, e lista zostaa zmodyfikowana przez jaki czynnik zewntrzny.
Aby unikn tego typu wyjtkw, naley stosowa si do prostej reguy: do kolekcji mona
wstawi dowoln liczb iteratorw, pod warunkiem e su one tylko do odczytu. Alternatywnie jeden z nich moe by zarwno do odczytu, jak i zapisu.
Jednoczesne operacje modyfikujce s wykrywane w bardzo prosty sposb. Kolekcja pamita
liczb operacji modyfikujcych (takich jak dodawanie i usuwanie elementw). Kady iterator
pamita, ile tego typu operacji wykona. Przed przystpieniem do dziaania iterator sprawdza,
czy jego liczba zgadza si z liczb kolekcji. Jeli nie, zgasza wyjtek ConcurrentModifica
tionException.
Od operacji wykrywania jednoczesnych operacji modyfikujcych istnieje jeden ciekawy wyjtek. Lista powizana zapamituje tylko zmiany struktury, jak dodawanie i
usuwanie ogniw. Metoda set nie liczy si jako modyfikacja strukturalna. W licie powizanej moe by kilka iteratorw zmieniajcych zawarto ogniw za pomoc metody set.
To zachowanie jest potrzebne w kilku algorytmach w klasie Collections, ktre opisujemy dalej.

Poznalimy wszystkie podstawowe metody klasy LinkedList. Do przemierzania jej w dowolnym kierunku i dodawania oraz usuwania elementw suy obiekt klasy ListIterator.
Wiemy ju z poprzedniego podrozdziau, e wiele przydatnych metod do dziaa na listach
powizanych znajduje si w interfejsie Collection. Wikszo z nich jest zdefiniowana w nadklasie AbstractCollection klasy LinkedList. Na przykad metoda toString wywouje metod
toString na rzecz kadego elementu i tworzy jeden dugi acuch w formacie [A, B, C], co

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

680

Java. Podstawy
przydaje si podczas debugowania. Aby sprawdzi, czy na licie znajduje si okrelony element, naley uy metody contains. Na przykad instrukcja staff.contains("Henryk") zwrci
warto true, jeli lista powizana zawiera acuch Henryk.
Biblioteka zawiera take kilka wtpliwych z teoretycznego punktu widzenia metod. Listy
powizane nie umoliwiaj szybkiego dostpu do dowolnego elementu. Aby dotrze do n-tego
elementu listy, trzeba zacz wdrwk od pocztku i omin pierwszych n1 elementw.
Nie da si nic skrci. Z tego powodu listy powizane rzadko s uywane w sytuacjach,
w ktrych potrzebny jest dostp do elementw za pomoc indeksu cakowitoliczbowego.
Pomimo tego klasa LinkedList udostpnia metod get, ktra pozwala na dostp do wybranego elementu:
LinkedList<String> list = . . .;
String obj = list.get(n);

Oczywicie efektywno tej metody jest niska. Jeli jej uywasz, to zastanw si, czy nie
naleaoby zmieni zastosowanej struktury danych do rozwizywanego problemu.
Nigdy nie naley przemierza listy powizanej za pomoc tej metody, dajcej zudzenie
swobodnego dostpu. Poniszy kod jest niebywale powolny:
for (int i = 0; i < list.size(); i++)
operacje na elemencie list.get(i);

Za kadym razem, kiedy potrzebujemy nowego elementu, poszukiwanie zaczyna si od


pocztku listy. Obiekty LinkedList nie zapisuj informacji o pozycji.
Metoda get posiada jedn niewielk optymalizacj jeli warto indeksu wynosi
co najmniej size()/2, szukanie elementu zaczyna si od koca listy.

Interfejs Iterator listy zawiera take metod informujc o aktualnym pooeniu iteratora.
W rzeczywistoci, ze wzgldu na to, e iteratory w Javie wskazuj miejsce pomidzy elementami, istniej dwie takie metody. Metoda nextIndex zwraca indeks cakowitoliczbowy elementu, ktry zostaby zwrcony przez nastpne wywoanie metody next. Metoda previous
Index zwraca indeks elementu, ktry zostaby zwrcony przez nastpne wywoanie metody
previous. Oczywicie warto ta jest o jeden mniejsza od wartoci nextIndex. Metody te s
efektywne, poniewa iteratory pamitaj swoj aktualn pozycj. W kocu, jeli mamy
indeks n, instrukcja list.listIterator(n) zwrci iterator wskazujcy miejsce bezporednio
przed elementem znajdujcym si w polu o indeksie n. Oznacza to, e metoda next zwraca
ten sam wynik co wywoanie list.get(n).
Dysponujc list powizan zawierajc tylko kilka elementw, nie trzeba przesadnie obawia si braku szybkoci metod get i set. Po co wic w ogle w takiej sytuacji uywa listy
powizanej? Jedynym powodem do uywania list powizanych jest szybko wstawiania
i usuwania elementw do i ze rodka kolekcji. Jeli masz tylko kilka elementw, moesz
uy struktury ArrayList.
Zalecamy unikanie wszystkich metod okrelajcych pooenie na licie za pomoc indeksw.
Aby mie wolny dostp do wszystkich elementw kolekcji, naley uywa obiektw Array
List zamiast list powizanych.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

681

Program przedstawiony na listingu 13.1 demonstruje praktyczne zastosowanie list powizanych. Tworzy on dwie listy, scala je, usuwa co drugi element z drugiej z nich, a na kocu
testuje dziaanie metody removeAll. Zachcamy do przeledzenia przepywu sterowania w
tym programie i przyjrzenia si iteratorom. Pomocne mog si okaza rysunki przedstawiajce pooenie iteratorw:
|ACE |BDFG
A|CE |BDFG
AB|CE B|DFG
...

Listing 13.1. linkedList/LinkedListTest.java


package linkedList;
import java.util.*;
/**
* Program demonstrujcy dziaania na listach powizanych
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class LinkedListTest
{
public static void main(String[] args)
{
List<String> a = new LinkedList<>();
a.add("Ania");
a.add("Karol");
a.add("Eryk");
List<String> b = new LinkedList<>();
b.add("Bartek");
b.add("Daniel");
b.add("Franek");
b.add("Gosia");
// Scalenie list a i b
ListIterator<String> aIter = a.listIterator();
Iterator<String> bIter = b.iterator();
while (bIter.hasNext())
{
if (aIter.hasNext()) aIter.next();
aIter.add(bIter.next());
}
System.out.println(a);
// Usunicie co drugiego sowa z listy b
bIter = b.iterator();
while (bIter.hasNext())
{
bIter.next(); // Opuszczenie jednego elementu
if (bIter.hasNext())

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

682

Java. Podstawy
{

bIter.next(); // Opuszczenie nastpnego elementu


bIter.remove(); // Usunicie elementu

}
}
System.out.println(b);
// Usunicie wszystkich sw znajdujcych si w licie b z listy a
a.removeAll(b);
System.out.println(a);
}
}

Warto zauway, e instrukcja System.out.println(a) drukuje wszystkie elementy listy powizanej a za pomoc metody toString z klasy AbstractCollection.
java.util.List<E> 1.2

ListIterator<E> listIterator()

Zwraca iterator listy.

ListIterator<E> listIterator(int index)

Zwraca iterator listy, ktrego pierwsze wywoanie metody next zwrci element
znajdujcy si pod podanym indeksem.

void add(int i, E element)

Dodaje element w okrelonym miejscu.

void addAll(int i, Collection<? extends E> elements)

Dodaje wszystkie elementy z kolekcji w okrelonym miejscu.

E remove(int i)

Usuwa i zwraca element znajdujcy si w okrelonym miejscu.

E get(int i)

Zwraca element znajdujcy si w okrelonym miejscu.

E set(int i, E element)

Zastpuje element znajdujcy si w okrelonym miejscu nowym elementem


i zwraca stary element.

int indexOf(Object element)

Zwraca pooenie pierwszego wystpienia elementu element lub warto -1,


jeli element nie zostanie znaleziony.

int lastIndexOf(Object element)

Zwraca pooenie ostatniego wystpienia elementu element lub warto -1,


jeli element nie zostanie znaleziony.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

java.util.ListIterator<E> 1.2

void add(E newElement)

Dodaje element przed biec pozycj.

void set(E newElement)

Zastpuje ostatni element odwiedzony przez metod next lub previous nowym
elementem. Zgasza wyjtek IllegalStateException, jeli lista zostaa
zmodyfikowana od czasu ostatniego wywoania metody next lub previous.

boolean hasPrevious()

W trakcie przemierzania listy wstecz zwraca warto true, jeli s jeszcze


nieodwiedzone elementy.

E previous()

Zwraca poprzedni obiekt. W przypadku osignicia pocztku listy zgasza


wyjtek NoSuchElementException.

int nextIndex()

Zwraca indeks elementu, ktry zostaby zwrcony przez nastpne wywoanie


metody next.

int previousIndex()

Zwraca indeks elementu, ktry zostaby zwrcony przez nastpne wywoanie


metody previous.
java.util.LinkedList<E> 1.2

LinkedList()

Tworzy pust list powizan.

LinkedList(Collection<? extends E> elements)

Tworzy list powizan i dodaje do niej wszystkie elementy z okrelonej kolekcji.

void addFirst(E element)

void addLast(E element)

Dodaje element na pocztku lub kocu listy.

E getFirst()

E getLast()

Zwraca element znajdujcy si na pocztku lub kocu listy.

E removeFirst()

E removeLast()

Usuwa element z pocztku lub koca listy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

683

684

Java. Podstawy

13.2.2. Listy tablicowe


W poprzednim podrozdziale zapoznalimy si z interfejsem List i klas LinkedList, ktra
go implementuje. Interfejs ten opisuje uporzdkowan kolekcj, w ktrej pooenie elementw
odgrywa rol. Elementy mona odwiedza na dwa sposoby: za pomoc iteratora lub dowolnie
przy uyciu metod get i set. Drugi z wymienionych sposobw nie nadaje si do list powizanych, natomiast doskonale sprawdza si w tablicach. W bibliotece kolekcji znajduje si
dobrze znana nam klasa ArrayList, ktra rwnie implementuje interfejs List. Obiekt tej klasy
zawiera dynamiczn tablic obiektw.
Wielu dowiadczonych programistw uywao do tej pory klasy Vector jako dynamicznej tablicy. Czemu uywa klasy ArrayList zamiast Vector? Z jednego powodu wszystkie metody klasy Vector s synchronizowane, dziki czemu mona bezpiecznie uzyska dostp do jednego obiektu tej klasy z dwch wtkw. Jeli natomiast
pracujemy tylko w jednym wtku co zdarza si nieporwnywalnie czciej tracimy bardzo duo czasu na synchronizacj. Natomiast metody klasy ArrayList nie s
synchronizowane. Zalecamy stosowanie klasy ArrayList zamiast Vector zawsze wtedy,
gdy nie jest potrzebna synchronizacja.

13.2.3. Zbir HashSet


W listach powizanych i tablicach istnieje moliwo ustawienia przechowywanych w nich
elementw w okrelonej kolejnoci. Aby jednak znale jaki element o nieznanym pooeniu, trzeba przej przez wszystkie elementy w jego poszukiwaniu. Jeli kolekcja zawiera
duo elementw, operacja ta moe zaj bardzo duo czasu. Istniej struktury danych, ktre
pozwalaj znacznie szybciej znajdowa elementy, ale nie umoliwiaj ich ustawiania w wybranej kolejnoci. Elementy w nich s ustawiane w takiej kolejnoci, ktra odpowiada ich wasnym celom.
Powszechnie znan struktur danych pozwalajc szybko wyszukiwa obiekty jest tablica
mieszajca (ang. hash table; nazywana te haszow). Tablica ta oblicza liczb cakowit,
zwan kodem mieszajcym (ang. hash code), dla kadego obiektu. Kod mieszajcy powstaje
w jaki sposb z pl obiektu, najlepiej w taki, aby obiekty zawierajce rne dane daway
rne kody. Tabela 13.2 zawiera kilka przykadowych kodw mieszajcych zwrconych przez
metod hashCode z klasy String.
Tabela 13.2. Kody mieszajce zwrcone przez metod hashCode
acuch

Kod mieszajcy

Lee

76268

lee

107020

eel

100300

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

685

Definiujc wasne klasy, naley zaimplementowa wasn metod hashCode wicej na ten
temat mona znale w rozdziale 5. Implementacja ta musi by zgodna z metod equals
jeli a jest rwne b (a.equals(b)), to a i b musz mie ten sam kod mieszajcy.
To, co jest wane teraz, to fakt, e obliczanie kodu mieszajcego przebiega szybko, a wynik
tego dziaania jest uzaleniony tylko od stanu obiektu, ktrego kod jest tworzony, a nie od
innych obiektw w tablicy.
Tablice mieszajce w Javie s zaimplementowane jako tablice list powizanych. Listy w tej
sytuacji nosz miano komrek lub kubekw (ang. bucket; zobacz rysunek 13.8). Aby okreli miejsce do przechowywania obiektu w tablicy, naley obliczy jego kod mieszajcy
i znale reszt z dzielenia tej liczby przez liczb wszystkich komrek. Jeli na przykad obiekt
ma kod mieszajcy 76268, a wszystkich komrek jest 128, zostanie on umieszczony w komrce
108 (poniewa reszta z dzielenia 76 268 przez 128 wynosi 108). Jeli mamy szczcie i nie
ma w tej komrce adnego innego elementu, umieszczamy tam nasz obiekt. Oczywicie nie
da si unikn trafienia komrki ze znajdujcym si wewntrz obiektem. Sytuacj tak nazywamy kolizj. Wtedy porwnujemy nowy obiekt z wszystkimi obiektami w tej komrce,
aby sprawdzi, czy go tam jeszcze nie ma. Jeli kody mieszajce s rozsdnie rozoone losowo,
a liczba komrek wystarczajco dua, powinno by konieczne przeprowadzenie tylko kilku
porwna.
Rysunek 13.8.
Tablica
mieszajca

Aby zyska wiksz kontrol nad dziaaniem tablicy, mona okreli pocztkowy licznik
komrek. Licznik ten okrela liczb komrek, w ktrych przechowywane s obiekty o identycznych wartociach mieszajcych. Jeli do tablicy wstawi si zbyt wiele elementw, zwiksza
si liczba kolizji i pogarsza szybko znajdowania elementw.
Jeli znana jest przybliona ostateczna liczba elementw tablicy, to mona ustawi licznik
komrek. Zazwyczaj ustawia si go na 75 do 150% spodziewanej liczby elementw. Niektrzy badacze utrzymuj, e dobrze jest ustawi ten licznik na liczb pierwsz, co pozwoli
unikn grupowania si kluczy, jednak nie ma na to przekonujcych dowodw. Biblioteka
standardowa stosuje liczniki komrek bdce potgami liczby 2, a domylna liczba to 16 (kada
warto podawana jako wielko tablicy jest automatycznie zaokrglana do najbliszej potgi
dwjki).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

686

Java. Podstawy
Oczywicie nie zawsze wiadomo, ile elementw bdzie trzeba przechowa. Czasami mona
te poda za ma liczb. Jeli tablica zostanie przepeniona, konieczna jest jej reorganizacja.
Polega to na utworzeniu nowej tablicy z wiksz liczb komrek i przeniesieniu wszystkich
danych do tej nowej tablicy. Stara tablica zostaje usunita. O reorganizacji tablicy decyduje
wspczynnik zapenienia (ang. load factor). Jeli na przykad wspczynnik ten wynosi 0,75
(warto domylna), a tablica zostanie zapeniona w ponad 75 procentach, nastpuje jej automatyczna reorganizacja, w wyniku ktrej tworzona jest tablica o dwukrotnie wikszej liczbie
komrek. W wikszoci zastosowa najlepiej pozostawi wspczynnik 0,75 bez zmian.
Za pomoc tablic mieszajcych mona zaimplementowa kilka bardzo wanych struktur
danych. Najprostsz z nich jest zbir (ang. set). Zbir to kolekcja unikatowych elementw.
Metoda add zbioru najpierw sprawdza, czy w zbiorze nie ma wstawianego obiektu, i wstawia
go, jeli nic nie znajdzie.
W bibliotece kolekcji Javy znajduje si klasa HashSet, ktra implementuje zbir bazujcy na
tablicy mieszajcej. Dodawanie elementw do tego zbioru odbywa si za pomoc metody
add. Metoda contains zostaa przedefiniowana w taki sposb, e szybko sprawdza, czy dodawany element nie znajduje si ju w zbiorze. Sprawdza tylko elementy w jednej komrce, a nie
caej kolekcji.
Iterator zbioru HashSet odwiedza wszystkie komrki po kolei. Poniewa elementy te s porozrzucane po tablicy, iterator odwiedza je w losowej kolejnoci. Dlatego zbioru HashSet naley
uywa wycznie wwczas, gdy kolejno elementw w kolekcji nie jest wana.
Przykadowy program zaprezentowany na kocu tego podrozdziau (listing 13.2) wczytuje
sowa ze strumienia System.in, dodaje je do zbioru, a nastpnie drukuje je na ekranie.
Mona na przykad wprowadzi tekst Alice in Wonderland (ktry mona pobra ze strony
http://www.gutenberg.net) za pomoc poniszego polecenia:
java SetTest < alice30.txt

Program wczyta wszystkie sowa ze strumienia wejciowego i wstawi je do zbioru HashSet.


Nastpnie przejdzie przez wszystkie unikatowe sowa i wydrukuje ich liczb (ksika Alice
in Wonderland zawiera 5909 unikatowych sw, wliczajc informacj o prawach autorskich
zamieszczon na pocztku pliku). Sowa s wywietlane w losowej kolejnoci.
Listing 13.2. set/SetTest.java
package set;
import java.util.*;
/**
* Program drukujcy wszystkie sowa ze strumienia wejciowego przy uyciu zbioru
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class SetTest
{
public static void main(String[] args)
{
Set<String> words = new HashSet<>(); // Klasa HashSet implementuje interfejs Set
long totalTime = 0;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

687

Scanner in = new Scanner(System.in);


while (in.hasNext())
{
String word = in.next();
long callTime = System.currentTimeMillis();
words.add(word);
callTime = System.currentTimeMillis() - callTime;
totalTime += callTime;
}
Iterator<String> iter = words.iterator();
for (int i = 1; i <= 20 && iter.hasNext(); i++)
System.out.println(iter.next());
System.out.println(". . .");
System.out.println(words.size() + " niepowtarzajcych si sw. " + totalTime
+ " milisekund.");
}
}

Naley zachowa ostrono przy modyfikowaniu elementw zbioru. Jeli kod mieszajcy elementu zmieni si, element ten bdzie si znajdowa w niewaciwym
miejscu w strukturze danych.
java.util.HashSet<E> 1.2

HashSet()

Tworzy pusty obiekt HashSet.

HashSet(Collection<? extends E> elements)

Tworzy obiekt HashSet i wstawia do niego wszystkie elementy okrelonej kolekcji.

HashSet(int initialCapacity)

Tworzy pusty obiekt HashSet o okrelonej pojemnoci (liczbie komrek).

HashSet(int initialCapacity, float loadFactor)

Tworzy obiekt HashSet o okrelonej pojemnoci i z okrelonym wspczynnikiem


zapenienia (liczba od 0,0 do 1,0 okrela poziom zapenienia zbioru, przy ktrym
jest on reorganizowany na zbir o wikszej pojemnoci).
java.lang.Object 1.0

int hashCode()

Zwraca kod mieszajcy obiektu. Kod mieszajcy moe by dowoln liczb


cakowit, take ujemn. Definicje metod equals i hashCode musz by ze sob
zgodne jeli instrukcja x.equals(y) zwraca warto true, to instrukcja
x.hashCode() musi zwraca tak sam warto jak y.hashCode().

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

688

Java. Podstawy

13.2.4. Zbir TreeSet


Klasa TreeSet jest podobna do klasy HashSet, ale ma jedn zalet w stosunku do niej. Zbir
TreeSet jest zbiorem uporzdkowanym. Mimo e elementy dodaje si do niego w dowolnej kolejnoci w trakcie iteracji przez zbir, s one automatycznie prezentowane w uporzdkowanej kolejnoci. Wyobramy sobie na przykad, e dodajemy do zbioru trzy acuchy, a nastpnie odwiedzamy kady z nich.
SortedSet<String> sorter = new TreeSet<String>(); // Klasa TreeSet implementuje klas
SortedSet.
sorter.add("Bartek");
sorter.add("Ania");
sorter.add("Karol");
for (String s : sorter) System.println(s);

Wartoci zostan wydrukowane w kolejnoci alfabetycznej: Ania, Bartek, Karol. Jak wskazuje sama nazwa klasy, elementy w tym zbiorze s przechowywane w strukturze drzewiastej
(obecnie uywane s drzewa czerwono-czarne; ich opis mona znale na przykad w ksice
pod tytuem Wprowadzenie do algorytmw, ktrej autorami s Thomas Cormen, Charles Leiserson, Ronald Rivest i Clifford Stein, WNT, Warszawa 2007). Kady nowy element jest umieszczany w odpowiednim miejscu zgodnie z kolejnoci. Dziki temu elementy odwiedzane przez
iterator s zawsze posortowane.
Operacja dodawania elementw do drzewa jest wolniejsza ni w przypadku tablicy mieszajcej, ale i tak znacznie szybsza ni dodawanie ich w odpowiednim miejscu tablicy lub listy
powizanej. Jeli drzewo zawiera n elementw, znalezienie odpowiedniego pooenia dla nowego
elementu wymaga okoo log2n testw. Jeli na przykad drzewo zawiera 1000 elementw,
dodanie nowego elementu wie si z przeprowadzeniem okoo 10 sprawdze.
W zwizku z tym dodawanie elementw do zbioru TreeSet odbywa si nieco wolniej ni
do zbioru HashSet (tabela 13.3 przedstawia dane porwnawcze) ale TreeSet automatycznie
sortuje elementy.
Tabela 13.3. Dodawanie elementw do zbiorw HashSet i TreeSet
Dokument

Liczba wszystkich sw

Liczba unikatowych sw

HashSet

TreeSet

Alice in Wonderland

28 195

5909

5 sekund

7 sekund

The Count of Monte Cristo

46 630

37 545

75 sekund

98 sekund

java.util.TreeSet<E> 1.2

TreeSet()

Tworzy pusty zbir TreeSet.

TreeSet(Collection<? extends E> elements)

Tworzy zbir TreeSet i wstawia do niego wszystkie elementy z okrelonej kolekcji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

689

13.2.5. Porwnywanie obiektw


Skd zbir TreeSet wie, w jaki sposb posortowa elementy? Standardowo zakada, e wstawiane elementy implementuj interfejs Comparable, w ktrym znajduje si jedna metoda:
public interface Comparable<T>
{
int compareTo(T other);
}

Instrukcja a.compareTo(b) musi zwrci warto 0, jeli obiekty a i b s rwne, cakowit


liczb ujemn, jeli a znajduje si przed b, lub dodatni liczb cakowit, jeli a wystpuje
za b. Sama warto nie ma znaczenia, liczy si tylko jej znak (>0, 0 lub <0). Interfejs Compa
rable jest implementowany przez kilka standardowych klas biblioteki Javy. Jedn z nich
jest klasa String. Jej metoda compareTo porwnuje acuchy wedug porzdku leksykograficznego.
Aby mc wstawia wasne obiekty, naley we wasnym zakresie zdefiniowa porzdek sortowania, implementujc interfejs Comparable. W klasie Object nie istnieje domylna implementacja metody compareTo.
Ponisza procedura na przykad sortuje obiekty typu Item wedug numeru czci:
class Item implements Comparable<Item>
{
public int compareTo(Item other)
{
return partNumber - other.partNumber;
}
. . .
}

Porwnywanie dwch cakowitych liczb dodatnich, takich jak numery seryjne, mona zaimplementowa, wykorzystujc ich rnic. Jeli rnica jest ujemna, pierwszy element powinien si znajdowa przed drugim, zero oznacza, e elementy s identyczne, a liczba dodatnia
pozostae moliwoci.
Sztuczka ta dziaa wycznie dla niezbyt duych liczb. Jeli x jest bardzo du
liczb dodatni, a y jest bardzo du liczb ujemn, rnica x-y moe przekroczy
zakres typu.

Niestety zastosowanie interfejsu Comparable do definicji kolejnoci sortowania jest obwarowane ograniczeniami. Jedna klasa moe implementowa go tylko jeden raz. Co w takim
razie zrobi, jeli w jednej kolekcji elementy musz by posortowane wedug numerw seryjnych, a w innej wedug opisw? Dodatkow trudnoci s obiekty klas, ktrych twrcy
nie zadali sobie trudu implementacji interfejsu Comparable.
W tego rodzaju sytuacjach naley zmusi zbir TreeSet do uycia rnych metod porwnujcych, przekazujc obiekt Comparator do konstruktora TreeSet. Interfejs Comparator zawiera metod compare z dwoma parametrami jawnymi:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

690

Java. Podstawy
public interface Comparator<T>
{
int compare(T a, T b);
}

Metoda compare, podobnie jak compareTo, zwraca ujemn liczb cakowit, jeli a wystpuje przed b, zero, jeli a i b s identyczne, lub dodatni liczb cakowit w pozostaych
przypadkach.
Aby posortowa elementy wedug ich opisw, wystarczy zdefiniowa klas implementujc
interfejs Comparable:
class ItemComparator implements Comparator<Item>
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
}

Nastpnie obiekt tej klasy naley przekaza do konstruktora zbioru TreeSet:


ItemComparator comp = new ItemComparator();
SortedSet<Item> sortByDescription = new TreeSet<Item>(comp);

Elementy drzewa utworzonego przy uyciu komparatora (obiektu typu Comparator) s porwnywane za pomoc tego komparatora.
Naley zauway, e komparator ten nie zawiera adnych danych, a jedynie metod porwnujc. Tego typu obiekty czasami nazywane s obiektami funkcyjnymi.
Obiekty funkcyjne s czsto definiowane w locie jako egzemplarze anonimowych klas
wewntrznych:
SortedSet<Item> sortByDescription = new TreeSet<Item>(new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});

W rzeczywistoci interfejs Comparator<T> zawiera dwie metody: compare i equals.


Oczywicie kada klasa udostpnia metod equals, przez co wydaje si, e dodanie
jej do tego interfejsu nie daje wielkiego poytku. Dokumentacja API wyjania, e nie trzeba
przesania tej metody, ale w niektrych przypadkach zrobienie tego moe spowodowa
zwikszenie jej szybkoci dziaania. Na przykad metoda addAll z klasy TreeSet moe by
bardziej efektywna, jeli dodamy elementy z innego zbioru, ktry uywa tego samego
komparatora.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

691

Biorc pod uwag liczby przedstawione w tabeli 13.3, mona si zastanawia, czy nie lepiej
byoby zawsze uywa zbiorw TreeSet zamiast HashSet. Nie da si ukry, e wstawianie
elementw do tego pierwszego nie zajmuje wiele wicej czasu, a przy okazji obiekty s automatycznie sortowane. Konkretna decyzja zaley od danych, ktre maj by przechowywane.
Jeli nie musz one by uporzdkowane, nie ma sensu marnowa czasu na ich sortowanie.
Co wicej, w niektrych przypadkach atwiej jest napisa funkcj mieszajc ni sortujc.
Funkcja mieszajca musi tylko dobrze miesza obiekty, natomiast funkcja porwnujca musi
je precyzyjnie rozrnia.
Przeanalizujmy na przykad przypadek zbioru prostoktw. Jeli uyjemy zbioru TreeSet,
musimy dostarczy obiekt Comparator<Rectangle>. Powstaje pytanie, jakie kryteria zastosowa do porwnywania tych figur. Mona na przykad porwnywa pola powierzchni, ale to
nie jest dobre rozwizanie, poniewa dwa inaczej wygldajce prostokty o innych wsprzdnych mog mie takie samo pole. Zbir TreeSet musi by zorganizowany w porzdku
liniowym. Musi istnie moliwo porwnania dowolnych dwch elementw zbioru, a wynikiem porwnywania, jeli obiekty s rwne, musi by zero. Istnieje porzdek, ktry nadaje
si do zastosowania z prostoktami (porzdek leksykograficzny dla wsprzdnych), ale jest
on nienaturalny i sprawia trudnoci obliczeniowe. Funkcja mieszajca jest ju natomiast
zdefiniowana w klasie Rectangle. Jej dziaanie polega na mieszaniu wsprzdnych.
Od Java SE 6.0 klasa TreeSet implementuje interfejs NavigableSet. Znajduje
si w nim kilka bardzo przydatnych metod sucych do lokalizowania elementw
i przemierzania zbiorw wstecz. Szczegowe informacje na ten temat znajduj si
w wycigach z API.

Program przedstawiony na listingu 13.3 tworzy dwa zbiory TreeSet zapenione obiektami Item.
Pierwszy z nich jest sortowany wedug numerw czci, czyli w domylny sposb. Drugi
natomiast posortowano wedug opisw za pomoc niestandardowego komparatora. Na listingu 13.4 przedstawiona jest klasa Item.
Listing 13.3. treeSet/TreeSetTest.java
package treeSet;
/**
@version 1.12 2012-01-26
@author Cay Horstmann
*/
import java.util.*;
/**
Program sortujcy zbir elementw poprzez porwnanie ich opisw
*/
public class TreeSetTest
{
public static void main(String[] args)
{
SortedSet<Item> parts = new TreeSet<>();
parts.add(new Item("Toster", 1234));
parts.add(new Item("Widget", 4562));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

692

Java. Podstawy
parts.add(new Item("Modem", 9912));
System.out.println(parts);
SortedSet<Item> sortByDescription = new TreeSet<>(new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
String descrA = a.getDescription();
String descrB = b.getDescription();
return descrA.compareTo(descrB);
}
});
sortByDescription.addAll(parts);
System.out.println(sortByDescription);
}
}

Listing 13.4. treeSet/Item.java


package treeSet;
import java.util.*;
/**
* Element z opisem i numerem czci
*/
public class Item implements Comparable<Item>
{
private String description;
private int partNumber;
/**
* Tworzy element
*
* @param aDescription
*
opis elementu
* @param aPartNumber
*
numer czci elementu
*/
public Item(String aDescription, int aPartNumber)
{
description = aDescription;
partNumber = aPartNumber;
}
/**
* Pobiera opis elementu
*
* @return opis
*/
public String getDescription()
{
return description;
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

693

public String toString()


{
return "[descripion=" + description + ", partNumber=" + partNumber + "]";
}
public boolean equals(Object otherObject)
{
if (this == otherObject) return true;
if (otherObject == null) return false;
if (getClass() != otherObject.getClass()) return false;
Item other = (Item) otherObject;
return Objects.equals(description, other.description) && partNumber ==
other.partNumber;
}
public int hashCode()
{
return Objects.hash(description, partNumber);
}
public int compareTo(Item other)
{
return Integer.compare(partNumber, other.partNumber);
}
}
java.lang.Comparable<T> 1.2

int compareTo(T other)

Porwnuje dwa obiekty i zwraca warto ujemn, jeli pierwszy z nich znajduje
si przed drugim, zero, jeli s identyczne, lub warto dodatni, jeli pierwszy
z nich wystpuje za drugim.
java.util.Comparator<T> 1.2

int compare(T a, T b)

Porwnuje dwa obiekty i zwraca warto ujemn, jeli a wystpuje przed b, zero,
jeli obiekty s identyczne, lub warto dodatni, jeli a wystpuje za b.
java.util.SortedSet<E> 1.2

Comparator<? super E> comparator()

Zwraca obiekt Comparator, ktry zosta uyty do posortowania elementw,


lub warto null, jeli elementy s porwnywane za pomoc metody compareTo
z interfejsu Comparable.

E first()

E last()

Zwraca najmniejszy lub najwikszy element posortowanego zbioru.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

694

Java. Podstawy
java.util.NavigableSet<E> 6

E higher(E value)

E lower(E value)

Zwraca najmniejszy element wikszy od wartoci value lub najwikszy element


mniejszy od wartoci value, lub warto null, jeli nie ma takiego elementu.

E ceiling(E value)

E floor (E value)

Zwraca najmniejszy element wikszy od lub rwny wartoci value lub najwikszy
element mniejszy od lub rwny wartoci value, lub warto null, jeli nie ma
takiego elementu.

E pollFirst()

E pollLast()

Usuwa i zwraca najmniejszy lub najwikszy element zbioru lub warto null,
jeli zbir jest pusty.

Iterator<E> descendingIterator()

Zwraca iterator, ktry przemierza zbir w kolejnoci malejcej.


java.util.TreeSet<E> 1.2

TreeSet()

Tworzy zbir TreeSet do przechowywania obiektw implementujcych interfejs


Comparable.

TreeSet(Comparator<? super E> c)

Tworzy zbir TreeSet i sortuje jego elementy przy uyciu okrelonego komparatora.

TreeSet(SortedSet<? extends E> elements)

Tworzy zbir TreeSet, wstawia do niego wszystkie elementy z posortowanego


zbioru oraz wykorzystuje ten sam komparator co okrelony zbir uporzdkowany.

13.2.6. Kolejki Queue i Deque


Z wczeniejszych podrozdziaw wiemy ju, e kolejka jest struktur danych pozwalajc na
szybkie dodawanie elementw na kocu i usuwanie elementw z czoa. Kolejka dwukierunkowa (deque) pozwala na szybkie dodawanie i usuwanie elementw po obu stronach. Nie
mona natomiast dodawa elementw w rodku kolejki. W Java SE 6 zaprezentowano nowy
interfejs o nazwie Deque. Implementuj go klasy ArrayDeque i LinkedList. Su one do tworzenia kolejek dwukierunkowych, ktre mog zmienia swj rozmiar w zalenoci od zapotrzebowania. W rozdziale 14. piszemy jeszcze o kolejkach Queue i Deque z ograniczeniami.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

java.util.Queue<E> 5.0

boolean add(E element)

boolean offer(E element)

Wstawia element na kocu kolejki i zwraca warto true, jeli kolejka nie jest
pena. Jeli w kolejce nie ma miejsca, pierwsza z powyszych metod zgasza
wyjtek IllegalStateException, a druga warto false.

E remove()

E poll()

Usuwa i zwraca element znajdujcy si na czole kolejki, jeli nie jest ona pusta.
Jeli kolejka jest pusta, pierwsza z powyszych metod zgasza wyjtek
NoSuchElementException, a druga warto null.

E element()

E peek()

Zwraca element znajdujcy si na czole kolejki (ale go nie usuwa), pod warunkiem
e kolejka nie jest pusta. Jeli kolejka jest pusta, pierwsza z powyszych metod
zgasza wyjtek NoSuchElementException, a druga warto null.
java.util.Deque<E> 6

void addFirst(E element)

void addLast(E element)

boolean offerFirst(E element)

boolean offerLast(E element)

Wstawia element na czoo lub ogon kolejki dwukierunkowej. Jeli kolejka


jest pena, dwie pierwsze metody zgaszaj wyjtek IllegalStateException,
a pozostae dwie zwracaj warto false.

E removeFirst()

E removeLast()

E pollFirst()

E pollLast()

Usuwa i zwraca element znajdujcy si na czole kolejki, jeli nie jest ona pusta.
Jeli kolejka jest pusta, pierwsze dwie z powyszych metod zgaszaj wyjtek
NoSuchElementException, a pozostae dwie zwracaj warto null.

E getFirst()

E getLast()

E peekFirst()

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

695

696

Java. Podstawy

E peekLast()

Zwraca element znajdujcy si na czole kolejki (ale go nie usuwa), pod warunkiem
e kolejka nie jest pusta. Jeli kolejka jest pusta, pierwsze dwie z powyszych
metod zgaszaj wyjtek NoSuchElementException, a pozostae zwracaj warto null.
java.util.ArrayDeque<E> 6

ArrayDeque()

ArrayDeque(int initialCapacity)

Tworzy nieograniczon kolejk dwukierunkow o pocztkowej pojemnoci


16 elementw lub odpowiadajcej wartoci initialCapacity.

13.2.7. Kolejki priorytetowe


Zasada dziaania tej struktury danych polega na przyjmowaniu elementw w losowej kolejnoci i oddawaniu ich w kolejnoci uporzdkowanej. To znaczy, e metoda remove wywoana
na rzecz kolejki priorytetowej zawsze zwraca najmniejszy element znajdujcy si w kolekcji.
Nie oznacza to jednak, e elementy w kolejce priorytetowej s przechowywane w kolejnoci
uporzdkowanej. Jeli przejdziemy iteratorem po zawartoci kolejki, znajdujce si w niej
obiekty mog nie by posortowane. Jednym ze sposobw realizacji kolejki priorytetowej jest
bardzo elegancka i szybka struktura danych o nazwie sterta (ang. heap). Sterta jest pewnego
rodzaju samoorganizujcym drzewem binarnym, w ktrym metody add i remove powoduj
przemieszczanie najmniejszego elementu w stron korzenia bez straty czasu na sortowanie.
Kolejka priorytetowa, podobnie jak struktura TreeSet, moe przechowywa elementy klasy
implementujcej interfejs Comparable lub obiekt Comparator przekazywany do konstruktora.
Typowym zastosowaniem kolejek priorytetowych s harmonogramy zada. Zadania s dodawane w losowej kolejnoci i kade z nich ma okrelony priorytet. Kiedy jakie zadanie moe
zosta rozpoczte, z kolejki usuwane jest zadanie o najwyszym priorytecie (poniewa tradycyjnie najwyszy priorytet jest oznaczany numerem 1, metoda remove usuwa najmniejszy
element).
Listing 13.5 demonstruje dziaanie kolejki priorytetowej. W przeciwiestwie do zbioru TreeSet,
tutaj iterator nie odwiedza elementw w uporzdkowanej kolejnoci. Natomiast usuwany jest
zawsze najmniejszy dostpny element.
Listing 13.5. priorityQueue/PriorityQueueTest.java
package priorityQueue;
import java.util.*;
/**
* Program demonstrujcy zastosowanie kolejki priorytetowej
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

697

public class PriorityQueueTest


{
public static void main(String[] args)
{
PriorityQueue<GregorianCalendar> pq = new PriorityQueue<>();
pq.add(new GregorianCalendar(1906, Calendar.DECEMBER, 9)); // G. Hopper
pq.add(new GregorianCalendar(1815, Calendar.DECEMBER, 10)); // A. Lovelace
pq.add(new GregorianCalendar(1903, Calendar.DECEMBER, 3)); // J. von Neumann
pq.add(new GregorianCalendar(1910, Calendar.JUNE, 22)); // K. Zuse
System.out.println("Iteracja przez elementy...");
for (GregorianCalendar date : pq)
System.out.println(date.get(Calendar.YEAR));
System.out.println("Usuwanie elementw...");
while (!pq.isEmpty())
System.out.println(pq.remove().get(Calendar.YEAR));
}
}
java.util.PriorityQueue 5.0

PriorityQueue()

PriorityQueue(int initialCapacity)

Tworzy kolejk priorytetow do przechowywania obiektw implementujcych


interfejs Comparable.

PriorityQueue(int initialCapacity, Comparator<? super E> c)

Tworzy kolejk priorytetow i wykorzystuje do jej sortowania okrelony


komparator.

13.2.8. Mapy
Zbir (ang. set) to rodzaj kolekcji pozwalajcy na szybkie wyszukiwanie elementw. Aby
jednak znale jaki element, trzeba posiada jego wiern kopi. Nie jest to zbyt czsto wykonywany rodzaj wyszukiwania zazwyczaj do dyspozycji jest jaki rodzaj informacji kluczowej i zadanie polega na znalezieniu zwizanego z ni elementu. Tego typu struktury realizowane s za pomoc map. Mapy przechowuj pary klucz warto. Aby znale element
w mapie, trzeba zna jego klucz. Na przykad w mapie mona przechowywa tabel danych
o pracownikach, gdzie kluczami bd identyfikatory pracownikw, a wartociami obiekty typu
Employee.
W bibliotece Javy znajduj si dwie implementacje map oglnego przeznaczenia: HashMap
i TreeMap. Obie te klasy implementuj interfejs Map.
Struktura HashMap miesza klucze, natomiast w strukturze TreeMap klucze s zorganizowane
w drzewie poszukiwa posortowanym w porzdku liniowym. Funkcja mieszajca lub porwnujca jest wywoywana wycznie na rzecz kluczy. Wartoci zwizane z tymi kluczami nie
s mieszane ani porwnywane.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

698

Java. Podstawy
Ktr struktur wybra: HashMap czy TreeMap? Podobnie jak ze zbiorami, mieszanie jest
nieco szybsze i naley je wybiera, gdy kolejno kluczy nie ma znaczenia.
Poniej tworzona jest struktura HashMap do przechowywania danych pracownikw:
Map<String, Employee> staff = new HashMap<String, Employee>(); // Klasa HashMap
// implementuje interfejs Map
Employee harry = new Employee("Henryk Kwiatek");
staff.put("987-98-9996", harry);
. . .

Dla kadego obiektu dodawanego do mapy HashMap naley poda jego klucz. W tym przypadku kluczem jest acuch, a odpowiadajc mu wartoci obiekt typu Employee.
Aby pobra (i zapisa w pamici) obiekt z mapy, naley posuy si jego kluczem.
String s = "987-98-9996";
e = staff.get(s);
// pobiera obiekt harry

Jeli z danym kluczem nie jest skojarzona adna warto w mapie, metoda get zwraca warto null.
Klucze musz by unikatowe, to znaczy, e nie mona dwm rnym wartociom przypisa
takiego samego klucza. Jeli metoda put zostanie wywoana dwa razy przy uyciu jednego
klucza, warto z drugiego wywoania zastpi t z pierwszego. W rzeczywistoci metoda ta
zwrci poprzedni warto przechowywan pod tym kluczem.
Do usuwania elementw z okrelonym kluczem z mapy suy metoda remove. Natomiast
metoda size zwraca liczb elementw znajdujcych si w mapie.
W architekturze kolekcji mapa nie jest uznawana za kolekcj (inne architektury struktur
danych traktuj map jako kolekcj par lub kolekcj wartoci indeksowan za pomoc kluczy).
Istnieje jednak moliwo utworzenia widoku mapy w postaci obiektw implementujcych
interfejs Collection lub jeden z jego podinterfejsw.
Istniej trzy rodzaje widokw: zbir kluczy, kolekcja wartoci (ktra nie jest zbiorem) i zbir
par klucz warto. Klucze i pary klucz warto tworz zbiory, poniewa mapa moe
zawiera tylko jeden egzemplarz kadego klucza. Ponisze metody zwracaj wymienione
widoki (elementy zbioru entrySet s obiektami statycznej klasy wewntrznej Map.Entry):
Set<K> keySet()
Collection<K> values()
Set<Map.Entry<K, V>> entrySet()

Naley zauway, e keySet nie jest zbiorem typu HashSet ani TreeSet, tylko obiektem jeszcze
innej klasy implementujcej interfejs Set. Interfejs Set rozszerza interfejs Collection, dziki
czemu zbioru keySet mona uywa w taki sam sposb jak kadej innej kolekcji.
Mona na przykad sporzdzi list wszystkich kluczy znajdujcych si w mapie:
Set<String> keys = map.keySet();
for (String key : keys)
{
dziaania na kluczu
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

699

Chcc uzyska widok kluczy i wartoci, mona unikn wyszukiwania wartoci


poprzez sporzdzenie listy pozycji (ang. entry). Mona wykorzysta poniszy kod
ramowy:
for (Map.Entry<String, Employee> entry : staff.entrySet())
{
String key = entry.getKey();
Employee value = entry.getValue();
dziaania key i value
}

Metoda remove iteratora usuwa z mapy klucz i skojarzon z nim warto. Nie mona
natomiast doda elementu do widoku keySet, poniewa dodanie klucza bez wartoci
jest bezcelowe. Prba wywoania metody add zakoczy si zgoszeniem wyjtku Unsup
portedOperationException. Widok entrySet jest ograniczony w podobny sposb, mimo
e dodanie nowej pary klucz warto z teoretycznego punktu widzenia nie byoby pozbawione sensu.

Listing 13.6 demonstruje praktyczne zastosowanie mapy. Na pocztku dodawane s pary


klucz warto do mapy. Nastpnie program usuwa jeden klucz z mapy, co pociga za sob
usunicie skojarzonej z ni wartoci. Dalej zostaje zmodyfikowana warto skojarzona z pewnym kluczem i nastpuje wywoanie metody get wyszukujcej warto. Na zakoczenie program iteruje przez zbir entrySet.
Listing 13.6. map/MapTest.java
package map;
import java.util.*;
/**
* Program demonstrujcy uycie mapy z kluczami typu String i wartociami typu Employee
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class MapTest
{
public static void main(String[] args)
{
Map<String, Employee> staff = new HashMap<>();
staff.put("144-25-5464", new Employee("Anna Kowalska"));
staff.put("567-24-2546", new Employee("Henryk Kwiatek"));
staff.put("157-62-7935", new Employee("Marcin Nowak"));
staff.put("456-62-5527", new Employee("Franciszek Frankowski"));
// wydruk wszystkich pozycji
System.out.println(staff);
// usunicie wartoci
staff.remove("567-24-2546");
// podmienienie pozycji
staff.put("456-62-5527", new Employee("Weronika Kowalska"));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

700

Java. Podstawy
// wyszukanie wartoci
System.out.println(staff.get("157-62-7935"));
// iteracja przez wszystkie pozycje
for (Map.Entry<String, Employee> entry : staff.entrySet())
{
String key = entry.getKey();
Employee value = entry.getValue();
System.out.println("key=" + key + ", value=" + value);
}
}
}
java.util.Map<K, V> 1.2

V get(K key)

Zwraca warto skojarzon z kluczem key. Jeli nie znajdzie klucza w mapie,
zwraca warto null. Klucz moe mie warto null.

V put(K key, V value)

Wstawia par klucz warto do mapy. Jeli taki klucz jest ju w mapie, skojarzony
z nim obiekt jest zastpowany nowym. Metoda ta zwraca poprzedni warto
skojarzon z kluczem lub warto null, jeli wczeniej takiego klucza nie byo.
Klasy implementujce mog zabrania stosowania kluczy lub wartoci null.

void putAll(Map<? extends K, ? extends V> entries)

Wstawia do mapy wszystkie pozycje z okrelonej mapy.

boolean containsKey(Object key)

Zwraca warto true, jeli klucz znajduje si w mapie.

boolean containsValue(Object value)

Zwraca warto true, jeli warto znajduje si w mapie.

Set<Map.Entry<K, V>> entrySet()

Zwraca widok zbiorowy obiektw Map.Entry, czyli par klucz warto znajdujcych
si w mapie. Ze zbioru tego mona usuwa elementy, a skutki tego dziaania bd
uwzgldnione w mapie. Nie mona natomiast nic dodawa.

Set<K> keySet()

Zwraca widok zbiorowy wszystkich kluczy znajdujcych si w mapie. Jeli jaki


klucz z tego zbioru zostanie usunity, z mapy zostanie usunity ten sam klucz
i skojarzona z nim warto. Nie mona nic dodawa.

Collection<V> values()

Zwraca widok kolekcyjny wszystkich wartoci znajdujcych si w mapie. Jeli jaka


warto z tego zbioru zostanie usunita, z mapy zostanie usunita ta sama warto
i skojarzony z ni klucz. Nie mona nic dodawa.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

java.util.Map.Entry<K, V> 1.2

K getKey()

V getValue()

Zwraca klucz lub warto okrelonej pozycji.

V setValue(V newValue)

Zamienia warto na now warto i zwraca star warto.


java.util.HashMap<K, V> 1.2

HashMap()

HashMap(int initialCapacity)

HashMap(int initialCapacity, float loadFactor)

Tworzy pust map HashMap o okrelonej pojemnoci i z okrelonym


wspczynnikiem zapenienia (liczba z zakresu od 0,0 do 1,0 okrelajca stopie
zapenienia tablicy HashTable, po przekroczeniu ktrego nastpuje reorganizacja
na wiksz jednostk). Domylny wspczynnik zapenienia wynosi 0,75.
java.util.TreeMap<K, V> 1.2

TreeMap(Comparator<? super K> c)

Tworzy struktur danych TreeMap i sortuje jej klucze za pomoc okrelonego


komparatora.

TreeMap(Map<? extends K, ? extends V> entries)

Tworzy struktur danych TreeMap i dodaje do niej wszystkie elementy z innej


mapy.

TreeMap(SortedMap<? extends K, ? extends V> entries)

Tworzy struktur danych TreeMap, dodaje do niej wszystkie pozycje


z posortowanej mapy oraz wykorzystuje ten sam komparator co okrelona
posortowana mapa.
java.util.SortedMap<K, V> 1.2

Comparator<? super K> comparator()

Zwraca komparator uyty do sortowania kluczy lub warto null, jeli klucze
s porwnywane za pomoc metody compareTo z interfejsu Comparable.

K firstKey()

K lastKey()

Zwraca najmniejszy lub najwikszy klucz dostpny w mapie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

701

702

Java. Podstawy

13.2.9. Specjalne klasy Set i Map


W bibliotece kolekcji znajduje si kilka klas map, ktre maj specjalne przeznaczenie. Omawiamy je krtko w kilku kolejnych podrozdziaach.

13.2.9.1. Klasa WeakHashMap


Klasa WeakHashMap rozwizuje pewien bardzo interesujcy problem. Sprbujmy sobie wyobrazi, co si dzieje z wartoci, ktrej klucz nie jest ju uywany nigdzie w programie. Zamy,
e ostatnia referencja do tego klucza zostaa utracona. W takiej sytuacji nie ma ju adnego
sposobu dostania si do skojarzonego z tym kluczem obiektu. Poniewa klucza ju nigdzie
nie ma, nie mona usun z mapy pary klucz warto. Nasuwa si myl, e powinien si
zaj tym system zbierania nieuytkw.
Niestety nie jest to takie proste. System ten przechowuje informacje o aktywnych obiektach.
Dopki mapa jest aktywna, wszystkie jej komrki rwnie s aktywne, przez co nie mona
ich usun. Dlatego naley pamita, aby usuwa z programu nieuywane wartoci z dugo
wykorzystywanych map. Mona te wybra inne rozwizanie i uy klasy WeakHashMap. Struktura ta wsppracuje z systemem zbierania nieuytkw w zakresie usuwania par klucz warto, jeli jedyne odwoanie do klucza pochodzi z pozycji w tablicy.
Oto zasada dziaania tego mechanizmu. Klucze w WeakHashMap s przechowywane w tak
zwanych sabych referencjach (ang. weak reference). Obiekt typu WeakReference przechowuje referencj do innego obiektu, w tym przypadku do klucza w tablicy. Obiekty tego
typu s traktowane przez system usuwania nieuytkw (ang. Garbage Collector GC)
w specjalny sposb. Standardowo, jeli GC odkryje, e do jakiego obiektu nie ma adnych
referencji, usuwa go. Jeli natomiast obiekt taki jest dostpny wycznie za porednictwem
obiektu WeakReference, GC take go usuwa, ale sab referencj odnoszc si do niego wstawia do kolejki. Obiekt WeakHashMap sprawdza w rwnych odstpach czasu, czy w kolejce nie
pojawiy si jakie nowe sabe referencje. Pojawienie si takiej referencji w kolejce oznacza,
e klucz nie by nigdzie uywany i e zosta zabrany. Wtedy WeakHashMap usuwa odpowiedni
pozycj.

13.2.9.2. Klasy LinkedHashSet i LinkedHashMap


W Java SE 1.5 wprowadzono klasy LinkedHashSet i LinkedHashMap, ktre pamitaj kolejno wstawiania elementw. Tym samym rozwizuj problem losowej kolejnoci przechowywanych elementw. Elementy wstawiane do tablicy s czone w listy dwustronne (zobacz
rysunek 13.9).
Przeanalizujmy na przykad ponisze instrukcje wstawiajce elementy do mapy (pochodz
one z listingu 13.6):
Map staff = new LinkedHashMap();
staff.put("144-25-5464", new Employee("Anna Kowalska"));
staff.put("567-24-2546", new Employee("Henryk Kwiatek"));
staff.put("157-62-7935", new Employee("Marcin Nowak"));
staff.put("456-62-5527", new Employee("Franciszek Frankowski"));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

703

Rysunek 13.9. Powizana tablica mieszajca

Instrukcja staff.keySet().iterator() odwiedza klucze w nastpujcej kolejnoci:


144-25-5464
567-24-2546
157-62-7935
456-62-5527

Natomiast instrukcja staff.values().iterator() tworzy nastpujc list odpowiadajcych


im wartoci:
Anna Kowalska
Henryk Kwiatek
Marcin Nowak
Franciszek Frankowski

Po elementach struktur LinkedHashMap mona take iterowa wedug kolejnoci dostpu


zamiast kolejnoci wstawiania. Kade wywoanie metody get lub put na rzecz jakiego
elementu powoduje jego przeniesienie na koniec listy powizanej (zmienia si tylko kolejno
w licie powizanej, nie w komrce w tablicy; pozycja pozostaje w komrce, ktra odpowiada
kodowi mieszajcemu klucza). Aby utworzy tak struktur danych, mona uy nastpujcej instrukcji:
LinkedHashMap<K, V>(initialCapacity, loadFactor, true)

Porzdek wedug kolejnoci dostpu znajduje zastosowanie w usuwaniu najduej nieuywanych elementw z pamici podrcznej. Na przykad czsto uywane obiekty mog by przechowywane w pamici, a rzadziej uywane w bazie danych. Jeli nie uda si znale jakiego
obiektu w tablicy i jest ona zapeniona, mona do niej wpuci iterator, ktry usunie kilka
pierwszych elementw. Bd to te obiekty, ktre byy uywane w najdalszej przeszoci.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

704

Java. Podstawy
Proces ten mona nawet zautomatyzowa. W tym celu naley utworzy podklas klasy
LinkedHashMap i przedefiniowa ponisz metod:
protected boolean removeEldestEntry(Map.Entry<K, V> eldest)

Dziki temu dodanie nowego elementu spowoduje usunicie najstarszego elementu (eldest),
pod warunkiem e wywoana metoda zwrci warto true. Ponisza przykadowa pami podrczna przechowuje maksymalnie sto elementw:
Map<K, V> cache = new
LinkedHashMap<K, V>(128, 0.75F, true)
{
protected boolean removeEldestEntry(Map.Entry<K, V> eldest)
{
return size() > 100;
}
};

Istnieje te moliwo podjcia na jakiej podstawie decyzji o usuniciu najstarszego elementu.


Mona na przykad sprawdzi jego znacznik czasu.

13.2.9.3. Klasy EnumSet i EnumMap


Struktura danych realizowana przez klas EnumSet przechowuje elementy typu wyliczeniowego.
Poniewa typ wyliczeniowy ma skoczon liczb egzemplarzy, wewntrzna implementacja
struktury EnumSet jest szeregiem bitw. Jeli odpowiednia warto znajduje si w zbiorze,
wczany jest jej bit.
Klasa EnumSet nie posiada konstruktora publicznego. Do utworzenia tego typu zbioru trzeba
uy statycznej metody fabrycznej:
enum Weekday { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };
EnumSet<Weekday> always = EnumSet.allOf(Weekday.class);
EnumSet<Weekday> never = EnumSet.noneOf(Weekday.class);
EnumSet<Weekday> workday = EnumSet.range(Weekday.MONDAY, Weekday.FRIDAY);
EnumSet<Weekday> mwf = EnumSet.of(Weekday.MONDAY, Weekday.WEDNESDAY, Weekday.FRIDAY);

Do modyfikacji zbioru EnumSet mona uywa zwykych metod z interfejsu Set.


EnumMap to mapa, w ktrej klucze s typu wyliczeniowego. Jest ona prost i wydajn tablic
wartoci. Typ klucza naley okreli w konstruktorze:
EnumMap<Weekday, Employee> personInCharge = new EnumMap<Weekday, Employee>(Weekday.class);

W dokumentacji API klasy EnumSet znajduj si dziwnie wygldajce parametry


typowe w formie E extends Enum<E>. Zapis ten oznacza, e E jest typem wyliczeniowym. Wszystkie typy wyliczeniowe dziedzicz po uoglnionej klasie Enum. Na przykad klasa
Weekday dziedziczy po klasie Enum<Weekday>.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

705

13.2.9.4. Klasa IdentityHashMap


W Java SE 1.4 wprowadzono take klas IdentityHashMap, ktra rwnie jest przeznaczona
do specjalnych celw. Wartoci haszowe kluczy nie powinny by w niej obliczane przez
metod hashCode, ale przez System.identityHashCode. Metody tej uywa metoda Object.
hashCode do obliczania kodu mieszajcego z adresu obiektu w pamici. Ponadto klasa ta
porwnuje obiekty za pomoc operatora == zamiast metody equals.
Dziki temu dwa obiekty s uznawane za rne, nawet jeli maj tak sam zawarto. Klasa
ta znajduje zastosowanie w implementacji algorytmw przemierzajcych obiekty (na przykad
serializujcych), gdzie konieczne jest pamitanie, ktre obiekty zostay ju przetworzone.
java.util.WeakHashMap<K, V> 1.2

WeakHashMap()

WeakHashMap(int initialCapacity)

WeakHashMap(int initialCapacity, float loadFactor)

Tworzy pust map WeakHashMap o okrelonej pojemnoci i okrelonym


wspczynniku zapenienia.
java.util.LinkedHashSet<E> 1.4

LinkedHashSet()

LinkedHashSet(int initialCapacity)

LinkedHashSet(int initialCapacity, float loadFactor)

Tworzy pusty zbir LinkedHashSet o okrelonej pojemnoci i okrelonym


wspczynniku zapenienia.
java.util.LinkedHashMap<K, V> 1.4

LinkedHashMap()

LinkedHashMap(int initialCapacity)

LinkedHashMap(int initialCapacity, float loadFactor)

LinkedHashMap(int initialCapacity, float loadFactor, boolean accessOrder)

Tworzy pust map LinkedHashMap o okrelonej pojemnoci oraz okrelonym


wspczynniku zapenienia i porzdku. Jeli parametr accessOrder ma warto true,
stosowany jest porzdek wedug kolejnoci dostpu. Warto false oznacza
porzdek w kolejnoci wstawiania.

protected boolean removeEldestEntry(Map.Entry<K, V> eldest)

T metod naley przedefiniowa, aby zwracaa warto true, jeli najstarsza


pozycja ma by usuwana. Parametr eldest okrela pozycj, ktrej usunicie
jest rozwaane. Metoda ta jest wywoywana po dodaniu pozycji do mapy. Domylna
implementacja zwraca warto false stare elementy nie s domylnie usuwane.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

706

Java. Podstawy
Mona jednak j przedefiniowa, aby selektywnie zwracaa warto true,
na przykad gdy najstarsza pozycja spenia okrelone warunki lub mapa przekracza
okrelony rozmiar.
java.util.EnumSet<E extends Enum<E>> 5.0

static <E extends Enum<E>> EnumSet<E> allOf(Class<E> enumType)

Zwraca zbir zapeniony wartociami z okrelonego typu wyliczeniowego.

static <E extends Enum<E>> EnumSet<E> noneOf(Class<E> enumType)

Zwraca pusty zbir pozwalajcy przechowywa wartoci okrelonego typu


wyliczeniowego.

static <E extends Enum<E>> EnumSet<E> range(E from, E to)

Zwraca zbir zapeniony wartociami z zakresu od from do to (wcznie).

static <E extends Enum<E>> EnumSet<E> of(E value)

static <E extends Enum<E>> EnumSet<E> of(E value, E... values)

Zwraca zbir zawierajcy okrelone wartoci.


java.util.EnumMap<K extends Enum<K>, V> 5.0

EnumMap(Class<K> keyType)

Tworzy map, ktrej klucze s okrelonego typu.


java.util.IdentityHashMap<K, V> 1.4

IdentityHashMap()

IdentityHashMap(int expectedMaxSize)

Tworzy pust map IdentityHashMap o pojemnoci rwnej najmniejszej potdze


cyfry 2 wikszej ni 1,5*expectedMaxSize (domylna warto parametru
expectedMaxSize to 21).
java.lang.System 1.0

static int identityHashCode(Object obj) 1.1

Zwraca ten sam kod mieszajcy (obliczony na podstawie adresu w pamici obiektu)
co metoda Object.hashCode, nawet jeli w klasie, do ktrej naley obiekt obj,
przedefiniowano metod hashCode.

13.3. Architektura kolekcji


Architektura (ang. framework) to zbir klas stanowicych podstaw do budowy bardziej
zaawansowanych funkcji. Architektura zawiera nadklasy charakteryzujce si okrelon funkcjonalnoci, kierujce pewnymi zasadami oraz udostpniajce rne przydatne mechanizmy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

707

Uytkownik architektury rozszerza jej klasy, dziki czemu nie musi opracowywa niektrych podstawowych mechanizmw od nowa. Na przykad Swing jest architektur interfejsw uytkownika.
Biblioteka kolekcji w Javie stanowi architektur klas kolekcyjnych. Zawiera definicje kilku
interfejsw i klas abstrakcyjnych przeznaczonych do uytku przez twrcw kolekcji (zobacz
rysunek 13.10) oraz udostpnia pewne mechanizmy, jak na przykad protok iteracyjny.
Aby uywa klas kolekcyjnych, nie trzeba posiada wiedzy na temat ich architektury udowodnilimy to w poprzednich podrozdziaach. Aby jednak tworzy algorytmy generyczne
dziaajce na wielu typach kolekcji lub cakiem nowe typy kolekcji, trzeba zna budow tej
architektury.

Rysunek 13.10. Interfejsy architektury kolekcji

Dwa podstawowe interfejsy kolekcyjne to Collection i Map. Elementy do kolekcji dodaje si


za pomoc metody add:
boolean add(E element)

Poniewa mapy przechowuj pary klucz warto, elementy wstawia si do nich za pomoc
metody put:
V put(K key, V value)

Do odczytu elementw z kolekcji suy iterator. Elementy z mapy mona take odczyta za
pomoc metody get:
V get(K key)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

708

Java. Podstawy
Lista to kolekcja uporzdkowana. Elementy s dodawane w okrelonych miejscach zbiornika.
Obiekt mona wstawi w odpowiednie miejsce na dwa sposoby: wedug indeksu cakowitoliczbowego lub przez iterator listy. W interfejsie List znajduj si metody dajce dostp do
dowolnego elementu kolekcji:
void add(int index, E element)
E get(int index)
void remove(int index)

Jak byo ju wczeniej wspominane, interfejs List udostpnia te metody bez wzgldu na to,
czy s one wydajne w okrelonej implementacji, czy nie. Aby pozwoli na uniknicie powolnych operacji dostpu swobodnego, w Java SE 1.4 wprowadzono interfejs o nazwie
RandomAccess. Nie posiada on adnych metod, ale mona za jego pomoc sprawdzi, czy
okrelona kolekcja umoliwia szybki dostp swobodny do elementw:
if (c instanceof RandomAccess)
{
algorytm dostpu losowego
}
else
{
algorytm dostpu liniowego
}

Interfejs RandomAccess implementuj klasy ArrayList i Vector.


Teoretycznie mona by byo utworzy osobny interfejs o nazwie Array, ktry rozszerzaby interfejs List i deklarowa metody dostpu swobodnego. Gdyby istnia
taki osobny interfejs, algorytmy wymagajce dostpu swobodnego uywayby parametrw typu Array i nie mona by byo zastosowa ich do kolekcji o powolnym dostpie
swobodnym. Jednak projektanci architektury kolekcji zdecydowali si nie definiowa osobnego interfejsu, poniewa chcieli, aby liczba interfejsw bya jak najmniejsza. Nie chciano
te traktowa protekcjonalnie programistw. Programista moe przekaza list powizan
do algorytmu implementujcego dostp swobodny musi tylko mie wiadomo kosztw wydajnociowych tego dziaania.

W interfejsie ListIterator znajduje si metoda wstawiajca element przed miejscem,


w ktrym znajduje si iterator:
void add(E element)

Do pobierania i usuwania elementw znajdujcych si w okrelonych miejscach su metody


next i remove z interfejsu Iterator.
Interfejs Set jest identyczny z interfejsem Collection, ale jego metody s nieco bardziej restrykcyjne. Metoda add zbioru nie powinna dodawa duplikatw. Metoda equals powinna by
zdefiniowana w taki sposb, aby dwa zbiory byy identyczne, jeli maj takie same elementy,
ale niekoniecznie w takiej samej kolejnoci. Metoda hashCode dla dwch zbiorw zawierajcych takie same elementy powinna zwraca ten sam kod mieszajcy.
Po co tworzy osobny interfejs, skoro sygnatury metod s takie same? Koncepcyjnie nie
wszystkie kolekcje s zbiorami. Dziki istnieniu interfejsu Set programici mog pisa
metody, ktre dziaaj tylko na zbiorach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

709

Interfejsy SortedSet i SortedMap udostpniaj obiekt komparatora uyty do sortowania oraz


definiuj metody tworzce widoki podzbiorw kolekcji. Widoki te opisujemy w kolejnym
podrozdziale.
Wreszcie, w Java SE 6 wprowadzono interfejsy NavigableSet i NavigableMap, ktre zawieraj dodatkowe metody suce do przemierzania i przeszukiwania uporzdkowanych zbiorw i map (w idealnej sytuacji metody te powinny si znajdowa w interfejsach SortedSet
i SortedMap). Interfejsy te s implementowane przez klasy TreeSet i TreeMap.
Kolej na klasy implementujce wymienione interfejsy. Wiemy ju, e niektre metody interfejsw kolekcyjnych mona z atwoci zaimplementowa na bazie bardziej podstawowych
metod. Wiele z tych implementacji znajduje si w klasach abstrakcyjnych:
AbstractCollection
AbstractList
AbstractSequentialList
AbstractSet
AbstractQueue
AbstractMap

Klasy te mona rozszerza przy tworzeniu wasnych klas kolekcyjnych, dziki czemu dziedziczy si po nich wiele rutynowych procedur.
Konkretne klasy dostpne w bibliotece Javy to:
LinkedList
ArrayList
ArrayDeque
HashSet
TreeSet
PriorityQueue
HashMap
TreeMap

Rysunek 13.11 przedstawia relacje zachodzce midzy tymi klasami.


Na koniec naley jeszcze wymieni kilka starszych klas kontenerowych, ktre s dostpne
w Javie od pocztku, zanim jeszcze powstaa architektura kolekcji:
Vector
Stack
Hashtable
Properties

Zostay one wcielone do kolekcji rysunek 13.12. Omawiamy te klasy nieco dalej.

13.3.1. Widoki i obiekty opakowujce


Z rysunkw 13.10 i 13.11 mona wycign wniosek, e projektanci Javy wpadli w lekk
przesad, tworzc tak wiele interfejsw i klas abstrakcyjnych do implementacji raczej umiarkowanej liczby konkretnych klas kolekcyjnych. Jednak rysunki te nie pokazuj wszystkiego.
interfejsy Collection i Map. Przykad takiego dziaania zaprezentowalimy, kiedy uylimy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

710

Java. Podstawy

Rysunek 13.11. Klasy w architekturze kolekcji


Rysunek 13.12.
Starsze klasy
w architekturze
kolekcji

Za pomoc tak zwanych widokw (ang. view) mona tworzy inne obiekty implementujce
metody keySet mapy. Na pierwszy rzut oka wydaje si, e metoda ta tworzy nowy zbir, zapenia go wszystkimi kluczami z mapy i zwraca go. Nie jest to jednak prawda. Metoda keySet
zwraca obiekt klasy implementujcej interfejs Set, ktrego metody operuj na oryginalnej
mapie. Taka kolekcja nazywana jest widokiem.
Widoki maj kilka wanych zastosowa w architekturze kolekcji. Opisujemy je w poniszych podrozdziaach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

711

13.3.1.1. Lekkie obiekty opakowujce kolekcje


Metoda asList z klasy Arrays zwraca obiekt osonowy typu List opakowujcy czyst tablic.
Umoliwia ona przekazywanie tablic do metod, ktre na wejciu przyjmuj tylko listy i kolekcje. Na przykad:
Card[] cardDeck = new Card[52];
. . .
List<Card> cardList = Arrays.asList(cardDeck);

Zwrcony obiekt nie jest typu ArrayList. Jest to obiekt widokowy udostpniajcy metody
get i set, ktre operuj na lecej u podoa tablicy. Wszystkie metody, ktre mog zmieni
rozmiar tej tablicy (na przykad metody add i remove doczonego iteratora), zgaszaj wyjtek
UnsupportedOperationException.
Od Java SE 5.0 metoda asList moe przyjmowa rne zestawy argumentw. Zamiast tablicy
mona jej przekaza poszczeglne elementy. Na przykad:
List<String> names = Arrays.asList("Ania", "Bartek", "Karol");

Ponisza instrukcja zwraca niemodyfikowalny obiekt implementujcy interfejs List oraz


tworzy zudzenie istnienia n elementw typu anObject.
Collections.nCopies(n, anObject)

Na przykad ponisza instrukcja tworzy list zawierajc 100 acuchw DEFAULT:


List<String> settings = Collections.nCopies(100, "DEFAULT");

Struktura ta zajmuje bardzo mao miejsca w pamici obiekt jest zapisany tylko w jednym
miejscu. Jest to sprytne zastosowanie techniki widokw.
Klasa Collections zawiera kilka metod, ktrych parametry lub wartoci zwrotne
s kolekcjami. Nie naley jej myli z interfejsem Collection.

Metoda wywoana poniej zwraca obiekt widokowy implementujcy interfejs Set (w odrnieniu od metody nCopies, ktra tworzy list). Zwrcony obiekt implementuje niemodyfikowalny jednoelementowy zbir pozbawiony narzutu struktury danych. Metody singletonList
i singletonMap maj podobne dziaanie.
Collections.singleton(anObject)

13.3.1.2. Widoki przedziaowe


Przedziay mona tworzy z kilku rodzajw kolekcji. Wyobramy sobie na przykad, e
mamy list o nazwie staff i chcemy z niej wyodrbni elementy od 10 do 19. Mona utworzy widok tego przedziau za pomoc metody subList.
List group2 = staff.subList(10, 20);

Pierwszy indeks jest wliczany, drugi nie podobnie jak z parametrami metody substring
z klasy String.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

712

Java. Podstawy
Na przedziale mona wykonywa dowolne dziaania, a ich rezultat bdzie automatycznie
widoczny w caej licie. Mona na przykad usun cay przedzia:
group2.clear();

// redukcja personelu

Przedzia group2 jest teraz pusty, a z listy staff zostay usunite te elementy, ktre znajdoway
si w tym przedziale.
Przedziay uporzdkowanych zbiorw i map tworzy si przy uyciu kolejnoci sortowania,
a nie pozycji elementw w kolekcji. W interfejsie SortedSet znajduj si trzy metody:
SortedSet<E> subSet(E from, E to)
SortedSet<E> headSet(E to)
SortedSet<E> tailSet(E from)

Zwracaj one podzbiory wszystkich elementw, ktre s wiksze ni lub rwne from i mniejsze od to. Podobne metody dla uporzdkowanych map to:
SortedMap<K, V> subMap(K from, K to)
SortedMap<K, V> headMap(K to)
SortedMap<K, V> tailMap(K from)

Zwracaj one widoki map zawierajce pozycje, ktrych klucze mieszcz si w okrelonych
zakresach.
Wprowadzony w Java SE 6 interfejs NavigableSet daje wiksze moliwoci kontroli dziaa
na przedziaach. Mona okreli, czy wartoci graniczne maj by wliczane (ang. inclusive):
NavigableSet<E> subSet(E from, boolean fromInclusive, E to, boolean toInclusive)
NavigableSet<E> headSet(E to, boolean toInclusive)
NavigableSet<E> tailSet(E from, boolean fromInclusive)

13.3.1.3. Widoki niemodyfikowalne


Klasa Collections udostpnia metody tworzce niemodyfikowalne widoki kolekcji. Widoki
te sprawdzaj, czy istniejca kolekcja nie jest modyfikowalna. Jeli wykryj prb modyfikacji kolekcji, wyrzucaj wyjtek, a kolekcja pozostaje nietknita.
Widoki niemodyfikowalne tworzy sze metod:
Collections.unmodifiableCollection
Collections.unmodifiableList
Collections.unmodifiableSet
Collections.unmodifiableSortedSet
Collections.unmodifiableMap
Collections.unmodifiableSortedMap

Kada z tych metod dziaa w kooperacji z jakim interfejsem. Na przykad metoda Collec
tions.unmodifiableList wsppracuje z klasami ArrayList, LinkedList i wszystkimi innymi,
ktre implementuj interfejs List.
Wyobramy sobie, e chcemy zezwoli pewnej procedurze pobra zawarto jakiej kolekcji,
ale nie chcemy, by j modyfikowaa. Oto, co mona w takiej sytuacji zrobi:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

713

List<String> staff = new LinkedList<String>();


. . .
lookAt(new Collections.unmodifiableList(staff));

Metoda Collections.unmodifiableList zwraca obiekt klasy implementujcej interfejs List.


Jego metody dostpowe pobieraj wartoci z kolekcji staff. Oczywicie metoda lookAt
moe wywoywa wszystkie metody interfejsu List, nie tylko metody dostpowe. Jednak
wszystkie metody modyfikujce (set) zostay przedefiniowane w taki sposb, aby zamiast
przekazywa wywoania do kolekcji, zgaszay wyjtek UnsupportedOperationException.
Niemodyfikowalny widok nie czyni samej kolekcji niemodyfikowaln. Nadal mona j modyfikowa przy uyciu jej pierwotnej referencji (w naszym przypadku staff). Mona te wywoywa metody modyfikujce na rzecz jej elementw.
Poniewa widoki opakowuj interfejs, a nie prawdziwy obiekt kolekcyjny, dostpne s tylko
te metody, ktre zostay zdefiniowane w tym interfejsie. Na przykad klasa LinkedList zawiera
metody addFirst i addLast, ktre nie nale do interfejsu List. Dlatego nie mona ich uywa
w widoku niemodyfikowalnym.
Metoda unmodifiableCollection (a take synchronizedCollection i checkedCollec
tion, ktre s opisane dalej) zwraca kolekcj, ktrej metoda equals nie wywouje
metody equals oryginalnej kolekcji. Zamiast tego dziedziczy metod equals po klasie
Object, ktra tylko sprawdza, czy obiekty s identyczne. Jeli zamienisz zbir lub list
w kolekcj, pozbawisz si moliwoci porwnywania zawartoci. Widoki dziaaj w ten
sposb, poniewa na tym poziomie hierarchii porwnywanie nie jest dobrze zdefiniowane. W ten sam sposb traktowana jest metoda hashCode.
Jednak metody unmodifiableSet i unmodifiableList uywaj metod equals i hashCode
oryginalnej kolekcji.

13.3.1.4. Widoki synchronizowane


Jeli do kolekcji maj dostp procedury z kilku wtkw, naley zadba o to, aby nie zostaa ona
przypadkowo zniszczona. Na przykad sytuacja, w ktrej jeden wtek prbuje doda elementy
do kolekcji HashTable, podczas gdy inny j odwiea, mogaby by fatalna w skutkach.
Projektanci jzyka zdecydowali, e zamiast implementowa kolekcje bezpieczne dla wtkw,
uczyni zwyke kolekcje bezpiecznymi dla wtkw za pomoc mechanizmu widokw. Na
przykad statyczna metoda synchronizedMap z klasy Collections zamienia zwyke mapy na
mapy z synchronizowanymi metodami dostpowymi:
Map<String, Employee> map = Collections.synchronizedMap(new HashMap<String, Employee>());

Teraz mona dostawa si do obiektu map z rnych wtkw. Metody takie jak get i put s
synchronizowane, to znaczy kade wywoanie metody musi zosta w peni ukoczone, zanim
inny wtek bdzie mg wywoa kolejn metod. Kwesti synchronizowanego dostpu do
struktur danych szczegowo omawiamy w rozdziale 14.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

714

Java. Podstawy

13.3.1.5. Widoki kontrolowane


W Java SE 5.0 wprowadzono zestaw widokw kontrolowanych majcych na celu wspieranie
programisty w procesie usuwania bdw zwizanych z typami uoglnionymi. Wiemy ju
z rozdziau 12., e do uoglnionej kolekcji da si przemyci elementy zego typu. Na przykad:
ArrayList<String> strings = new ArrayList<String>();
ArrayList rawList = strings;
// Zostanie zgoszone tylko ostrzeenie (nie bd) dotyczce
// zgodnoci ze starszym kodem.
rawList.add(new Date());
// Teraz referencja strings wskazuje na obiekt typu Date!

Bd w instrukcji add nie zostanie wykryty w czasie dziaania programu. W zamian wystpi
wyjtek rzutowania, kiedy w innej czci kodu zostanie wywoana metoda get i zostanie
wykonane rzutowanie jej wyniku na typ String.
Widok kontrolowany wykryje taki bd. Zdefiniujmy nastpujc bezpieczn list:
List<String> safeStrings = Collections.checkedList(strings, String.class);

Metoda add widoku sprawdza, czy wstawiany obiekt naley do okrelonej klasy; jeli nie,
generuje wyjtek ClassCastException. Jest to korzystne, poniewa bd zostaje zgoszony
w odpowiednim miejscu:
ArrayList rawList = safeStrings;
rawList.add(new Date());
// Lista kontrolowana generuje wyjtek ClassCastException.

Widoki kontrolowane s ograniczone zestawem testw, ktre moe przeprowadza


maszyna wirtualna w czasie dziaania programu. Na przykad struktury ArrayList
<Pair<String>> nie mona ochroni przed wstawieniem do niej typu Pair<Date>,
poniewa maszyna wirtualna dysponuje tylko jedn surow klas Pair.

13.3.1.6. Uwagi dotyczce operacji opcjonalnych


Widoki z reguy maj jakie ograniczenia mog by tylko do odczytu, mog nie mie
moliwoci zmiany rozmiaru lub pozwala na usuwanie elementw, ale nie na dodawanie, jak
na przykad widok kluczy mapy. Prba wykonania niedozwolonej operacji na ograniczonym
widoku koczy si zgoszeniem wyjtku UnsupportedOperationException.
W dokumentacji API wiele metod klas i interfejsw kolekcyjnych oznaczono jako operacje
opcjonalne (ang. optional operations). Wydaje si to sprzeczne z ide interfejsu, ktry przecie jak wiadomo okrela zestaw metod, ktre klasa musi implementowa. Rzeczywicie ten stan rzeczy nie jest satysfakcjonujcy z teoretycznego punktu widzenia. Lepszym
rozwizaniem byoby utworzenie osobnych interfejsw dla widokw tylko do odczytu i widokw, ktre nie mog zmieni rozmiaru kolekcji. Wtedy jednak liczba interfejsw potroiaby
si, a to dla projektantw biblioteki byoby nie do zaakceptowania.
Czy powinno si rozszerza technik opcjonalnych operacji na wasne projekty? Naszym
zdaniem nie. Mimo i kolekcje s uywane czsto, styl programowania zwizany z ich implementacj nie jest typowy dla innych dziedzin. Projektanci biblioteki kolekcyjnej stoj przed

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

715

niezwykle trudnym zadaniem zaspokojenia sprzecznych wymaga. Z jednej strony uytkownicy wymagaj, aby biblioteka bya atwa do nauki, wygodna w uyciu, w peni uoglniona
i niepodatna na bdy, a z drugiej strony ma by tak samo wydajna jak rcznie optymalizowane algorytmy. Spenienie tych wszystkich da naraz (czy choby zblienie si do nich)
jest najzwyczajniej niemoliwe. Jednak we wasnej praktyce programistycznej nie bdziesz
czsto rozwizywa tak ekstremalnych problemw. Powinno by moliwe znalezienie rozwiza, ktre nie polegaj na wykorzystaniu ostatecznego rodka w postaci opcjonalnych
operacji interfejsu.
java.util.Collections 1.2

static <E> Collection unmodifiableCollection(Collection<E> c)

static <E> List unmodifiableList(List<E> c)

static <E> Set unmodifiableSet(Set<E> c)

static <E> SortedSet unmodifiableSortedSet(SortedSet<E> c)

static <K, V> Map unmodifiableMap(Map<K, V> c)

static <K, V> SortedMap unmodifiableSortedMap(SortedMap<K, V> c)

Tworzy widoki kolekcji, ktrych metody modyfikujce generuj wyjtek


UnsupportedOperationException.

static <E> Collection<E> synchronizedCollection(Collection<E> c)

static <E> List synchronizedList(List<E> c)

static <E> Set synchronizedSet(Set<E> c)

static <E> SortedSet synchronizedSortedSet(SortedSet<E> c)

static <K, V> Map<K, V> synchronizedMap(Map<K, V> c)

static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> c)

Tworzy widoki kolekcji, ktrych metody s zsynchronizowane.

static <E> Collection checkedCollection(Collection<E> c, Class<E>


elementType)

static <E> List checkedList(List<E> c, Class<E> elementType)

static <E> Set checkedSet(Set<E> c, Class<E> elementType)

static <E> SortedSet checkedSortedSet(SortedSet<E> c, Class<E> elementType)

static <K, V> Map checkedMap(Map<K, V> c, Class<K> keyType,


Class<V> valueType)

static <K, V> SortedMap checkedSortedMap(SortedMap<K, V> c,


Class<K> keyType, Class<V> valueType)

Tworzy widoki kolekcji, ktrych metody zgaszaj wyjtek ClassCastException,


jeli wstawiany element jest zego typu.

static <E> List<E> nCopies(int n, E value)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

716

Java. Podstawy

static <E> Set<E> singleton(E value)

Tworzy widok obiektu bdcy niemodyfikowaln list zawierajc n identycznych


elementw lub zbir zawierajcy jeden element.
java.util.Arrays 1.2

static <E> List<E> asList(E... array)

Zwraca widok listowy elementw tablicy, ktry mona modyfikowa,


ale nie mona zmienia jego rozmiaru.
java.util.List<E> 1.2

List<E> subList(int firstIncluded, int firstExcluded)

Zwraca widok listowy elementw skadajcy si z pozycji nalecych


do okrelonego przedziau.
java.util.SortedSet<E> 1.2

SortedSet<E> subSet(E firstIncluded, E firstExcluded)

SortedSet<E> headSet(E firstExcluded)

SortedSet<E> tailSet(E firstIncluded)

Zwraca widok elementw z okrelonego przedziau.


API java.util.NavigableSet<E> 6

NavigableSet<E> subSet(E from, boolean fromIncluded, E to, boolean


toIncluded)

NavigableSet<E> headSet(E to, boolean toIncluded)

NavigableSet<E> tailSet(E from, boolean fromIncluded)

Zwraca widok elementw z okrelonego przedziau. Parametr logiczny okrela,


czy element brzegowy ma by wczany do widoku.
java.util.SortedMap<K, V> 1.2

SortedMap<K, V> subMap(K firstIncluded, K firstExcluded)

SortedMap<K, V> headMap(K firstExcluded)

SortedMap<K, V> tailMap(K firstIncluded)

Zwraca widok mapy pozycji, ktrych klucze mieszcz si w okrelonym


przedziale.
java.util.NavigableMap<K, V> 6

NavigableMap<K, V> subMap(K from, boolean fromIncluded, K to,


boolean toIncluded)

NavigableMap<K, V> headMap(K from, boolean fromIncluded)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

717

NavigableMap<K, V> tailMap(K to, boolean toIncluded)

Zwraca widok mapy pozycji, ktrych klucze mieszcz si w okrelonym przedziale.


Parametr logiczny okrela, czy element brzegowy ma by wczany do widoku.

13.3.2. Operacje zbiorcze


W wikszoci prezentowanych do tej pory przykadw kodu przemierzalimy kolekcje po
jednym elemencie za pomoc iteratora. Iteracji mona jednak unikn, stosujc w zamian jedn
z operacji zbiorczych dostpnych w bibliotece.
Chcemy znale cz wspln dwch zbiorw. Zaczniemy od utworzenia nowego zbioru
do przechowywania znalezionego wyniku.
Set<String> result = new HashSet<String>(a);

Wykorzystujemy tutaj fakt, e kada kolekcja posiada konstruktor, ktrego parametr jest
inn kolekcj przechowujc wartoci pocztkowe.
Teraz uyjemy metody retainAll:
result.retainAll(b);

Zwraca ona wszystkie elementy, ktre znajduj si take w zbiorze b. W ten sposb znalelimy cz wspln dwch zbiorw bez tworzenia ptli.
Mona pj nawet dalej i zastosowa operacj zbiorcz do widoku. Wyobramy sobie, e
dysponujemy map przechowujc obiekty typu Employee z kluczami w postaci identyfikatorw ID oraz zbiorem identyfikatorw pracownikw, ktrzy maj zosta zwolnieni.
Map<String, Employee> staffMap = . . .;
Set<String> terminatedIDs = . . .;

Wystarczy utworzy zbir kluczy i usun wszystkie identyfikatory zwolnionych pracownikw.


staffMap.keySet().removeAll(terminatedIDs);

Poniewa zbir kluczy jest widokiem mapy, klucze i skojarzeni z nimi pracownicy s automatycznie usuwani z tej mapy.
Przy uyciu widoku podprzedziau mona ograniczy operacje zbiorcze do podlist i podzbiorw. Zamy, e chcemy doda 10 elementw z listy do innego zbiornika. Pobieramy dziesi
pierwszych elementw i umieszczamy je w podlicie:
relocated.addAll(staff.subList(0, 10));

Na podprzedziale tym mona take wykonywa operacje modyfikujce.


staff.subList(0, 10).clear();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

718

Java. Podstawy

13.3.3. Konwersja pomidzy kolekcjami a tablicami


Poniewa znaczna cz API Javy powstaa przed pojawieniem si kolekcji, czasami
konieczna jest konwersja tradycyjnych tablic na bardziej nowoczesne kolekcje.
Jeli dysponujemy tablic, musimy przekonwertowa j na kolekcj. Mona do tego celu uy
metody opakowujcej Arrays.asList. Na przykad:
String[] values = . . .;
HashSet<String> staff = new HashSet<String>(Arrays.asList(values));

Utworzenie tablicy z kolekcji nie jest ju takie atwe. Mona oczywicie uy do tego metody
toArray:
Object[] values = staff.toArray();

Jednak wynik bdzie tablic obiektw typu Object. Nie mona zastosowa rzutowania, nawet
jeli wiadomo, e kolekcja zawieraa obiekty okrelonego typu:
String[] values = (String[]) staff.toArray();

// Bd!

Metoda toArray zwraca tablic typu Object[], ktrego nie mona zmieni. W zamian naley
uy alternatywnej wersji tej metody. Przekazujemy do niej jako parametr tablic o dugoci 0
takiego typu, jakiego potrzebujemy. Zwrcona tablica bdzie wtedy miaa taki wanie typ:
String[] values = staff.toArray(new String[0]);

W razie potrzeby mona utworzy tablic o odpowiednim rozmiarze:


staff.toArray(new String[staff.size()]);

W tym przypadku nie jest tworzona adna nowa tablica.


Moesz si zastanawia, czemu do metody toArray nie mona po prostu przekaza obiektu typu Class (jak na przykad String.class). Metoda ta wykonuje
podwjn prac zapenia istniejc tablic (pod warunkiem e jest wystarczajco
duga) i tworzy now tablic.

13.4. Algorytmy
Uoglnione interfejsy kolekcyjne maj pewn du zalet algorytmy trzeba implementowa tylko jeden raz. Wemy na przykad prosty algorytm znajdujcy najwikszy element
w kolekcji. Standardowo jego implementacja polegaaby na uyciu ptli. Ponisza procedura
znajduje najwikszy element tablicy:
if (a.length == 0) throw new NoSuchElementException();
T largest = a[0];
for (int i = 1; i < a.length; i++)
if (largest.compareTo(a[i]) < 0)
largest = a[i];

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

719

Oczywicie w przypadku listy tablicowej kod wygldaby nieco inaczej:


if (v.size() == 0) throw new NoSuchElementException();
T largest = v.get(0);
for (int i = 1; i < v.size(); i++)
if (largest.compareTo(v.get(i)) < 0)
largest = v.get(i);

Jak wyglda sytuacja w listach powizanych? Nie istnieje w nich szybki mechanizm dostpu
swobodnego, ale mona uy iteratora:
if (l.isEmpty()) throw new NoSuchElementException();
Iterator<T> iter = l.iterator();
T largest = iter.next();
while (iter.hasNext())
{
T next = iter.next();
if (largest.compareTo(next) < 0)
largest = next;
}

Pisanie tych ptli jest uciliwe, a ponadto s one podatne na bdy. atwo popeni bd
pomyki o jeden (ang. off-by-one error), nie wiadomo, czy ptla zadziaa prawidowo w przypadku pustego kontenera lub zawierajcego tylko jeden element. Perspektywa cigego testowania i debugowania caego tego kodu nie jest zachcajca, ale implementacja caej masy
metod take nie napawa radoci, na przykad:
static <T extends Comparable> T max(T[] a)
static <T extends Comparable> T max(ArrayList<T> v)
static <T extends Comparable> T max(LinkedList<T> l)

W takiej sytuacji z pomoc przychodz interfejsy kolekcyjne. Pomylmy, jak powinien wyglda minimalny interfejs potrzebny do realizacji tego algorytmu. Dostp swobodny za pomoc
metod get i set jest operacj bardziej zoon ni zwyky dostp za pomoc iteratora. Jak przekonalimy si przy okazji szukania najwikszego elementu listy powizanej, do wykonania tego
zadania nie jest potrzebny dostp swobodny. Najwikszy element mona znale za pomoc
prostej iteracji po elementach. Dlatego metod max mona zaimplementowa w taki sposb,
aby przyjmowaa kady obiekt implementujcy interfejs Collection.
public static <T extends Comparable> T max(Collection<T> c)
{
if (c.isEmpty()) throw new NoSuchElementException();
Iterator<T> iter = c.iterator();
T largest = iter.next();
while (iter.hasNext())
{
T next = iter.next();
if (largest.compareTo(next) < 0)
largest = next;
}
return largest;
}

Teraz dysponujemy jedn metod, ktra znajduje najwikszy element w listach powizanych, listach tablicowych i tablicach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

720

Java. Podstawy
Technika ta daje bardzo due moliwoci. W bibliotece standardowej C++ znajduje si mnstwo przydatnych algorytmw, z ktrych kady operuje na kolekcjach uoglnionych. Biblioteka
Javy nie jest tak bogata, ale posiada podstawowe funkcje: sortowanie, wyszukiwanie binarne
i kilka algorytmw uytkowych.

13.4.1. Sortowanie i tasowanie


Weterani komputerowi pamitaj jeszcze czasy, kiedy algorytmy sortujce programowali
rcznie za pomoc kart dziurkowanych. Obecnie algorytmy sortujce wchodz w skad biblioteki standardowej wikszoci jzykw programowania. Java nie jest tu wyjtkiem.
Metoda sort z klasy Collections sortuje kolekcje implementujce interfejs List.
List<String> staff = new LinkedList<String>();
Wstawienie elementw do kolekcji
Collections.sort(staff);

Ta metoda zakada, e elementy listy implementuj interfejs Comparable. Aby posortowa t list w inny sposb, mona jako drugi parametr przekaza obiekt Comparator (komparatory
omawialimy w podrozdziale 13.2.5, Porwnywanie obiektw). Oto sposb sortowania
listy elementw:
Comparator<Item> itemComparator = new
Comparator<Item>()
{
public int compare(Item a, Item b)
{
return a.partNumber - b.partNumber;
}
});
Collections.sort(items, itemComparator);

Aby posortowa list w kolejnoci malejcej, naley uy metody Collections.reverse


Order(). Tworzy ona komparator, ktry zwraca wynik operacji b.compareTo(a). Na przykad
ponisza instrukcja sortuje elementy listy staff w kolejnoci malejcej wedug porzdku
okrelonego przez metod compareTo typu elementu.
Collections.sort(staff, Collections.reverseOrder())

Podobnie ponisza instrukcja odwraca kolejno metody itemComparator:


Collections.sort(items, Collections.reverseOrder(itemComparator))

W tym miejscu moe si rodzi pytanie, jak metoda sort sortuje listy. Typowe algorytmy
sortujce prezentowane w ksikach o algorytmach dziaaj na tablicach i korzystaj z dostpu
swobodnego. W listach ten typ dostpu moe by bardzo wolny. Mona je jednak szybko
sortowa przy uyciu algorytmu sortowania przez scalanie (zobacz Algorithms in C++ autorstwa Roberta Sedgewicka, Addison-Wesley, 1998, s. 336 369). W Javie jednak nie wykorzystano tej metody. W zamian wszystkie elementy listy s wrzucane do tablicy, tam sortowane
przy uyciu innej wersji algorytmu sortowania przez scalanie, a nastpnie kopiowane w uporzdkowanej kolejnoci z powrotem do listy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

721

Algorytm sortowania przez scalanie stosowany w bibliotece kolekcji ustpuje nieco prdkoci algorytmowi quick sort, ktry jest standardowo uywany do implementacji algorytmw
sortujcych oglnego przeznaczenia. Ma on jednak pewn wan zalet jest stabilny, to
znaczy nie zamienia miejscami takich samych elementw. Po co przejmowa si kolejnoci identycznych elementw? Oto typowa sytuacja. Mamy list pracownikw posortowan
wedug nazwisk. Teraz sortujemy wedug wysokoci zarobkw. Stabilny algorytm sortujcy
zachowa kolejno nazwisk. Mwic inaczej, lista bdzie posortowana najpierw wedug zarobkw, a potem wedug nazwisk.
Poniewa kolekcje nie musz implementowa wszystkich swoich metod opcjonalnych, wszystkie metody przyjmujce parametry kolekcyjne musz informowa, kiedy dana kolekcja moe
by bezpiecznie przekazana do algorytmu. Na przykad z pewnoci nie mona przekaza listy
unmodifiableList do algorytmu sort. Jakiego rodzaju list mona przekaza? Zgodnie
z dokumentacj lista musi by modyfikowalna, ale nie musi zezwala na zmian rozmiaru.
Waciwoci list mona przedstawi nastpujco:

Lista jest modyfikowalna (ang. modifiable), jeli obsuguje metod set.

Lista zezwala na zmian jej rozmiaru (ang. resizable), jeli obsuguje metody
add i remove.

W klasie Collections dostpna jest te metoda shuffle, ktra dziaa odwrotnie do sortowania tasuje elementy listy. Na przykad:
ArrayList<Card> cards = . . .;
Collections.shuffle(cards);

Jeli metodzie shuffle zostanie przekazana lista nieimplementujca interfejsu RandomAccess,


jej zawarto zostanie skopiowana do tablicy, przetasowana i wstawiona z powrotem w odmiennym porzdku.
Program przedstawiony na listingu 13.7 wstawia do listy tablicowej 49 obiektw typu Integer
zawierajcych liczby od 1 do 49. Nastpnie tasuje zawarto tej listy i wybiera sze pierwszych elementw. Na kocu sortuje pobrane wartoci i drukuje je.
Listing 13.7. shuffle/ShuffleTest.java
package shuffle;
import java.util.*;
/**
* Program demonstrujcy algorytmy tasowania i sortowania
* @version 1.11 2012-01-26
* @author Cay Horstmann
*/
public class ShuffleTest
{
public static void main(String[] args)
{
List<Integer> numbers = new ArrayList<>();
for (int i = 1; i <= 49; i++)
numbers.add(i);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

722

Java. Podstawy
Collections.shuffle(numbers);
List<Integer> winningCombination = numbers.subList(0, 6);
Collections.sort(winningCombination);
System.out.println(winningCombination);
}
}
java.util.Collections 1.2

static <T extends Comparable<? super T>> void sort(List<T> elements)

static <T> void sort(List<T> elements, Comparator<? super T> c)

Sortuje elementy listy przy uyciu stabilnego algorytmu sortujcego. Dugo


czasu dziaania tego algorytmu to O(n log n), gdzie n oznacza dugo listy.

static void shuffle(List<?> elements)

static void shuffle(List<?> elements, Random r)

Tasuje element listy. Dugo czasu dziaania tego algorytmu to O(n a(n)),
gdzie n to dugo listy, a a(n) to redni czas dostpu do elementu.

static <T> Comparator<T> reverseOrder()

Zwraca komparator sortujcy element w odwrconej kolejnoci w stosunku


do porzdku okrelonego przez metod compareTo z interfejsu Comparable.

static <T> Comparator<T> reverseOrder(Comparator<T> comp)

Zwraca komparator sortujcy element w odwrconej kolejnoci w stosunku


do porzdku okrelonego przez parametr comp.

13.4.2. Wyszukiwanie binarne


Typowy proces poszukiwania obiektu w tablicy polega na odwiedzaniu po kolei wszystkich
elementw a do natrafienia na ten waciwy. Jeli tablica jest posortowana, mona poszukiwany element porwna ze rodkowym elementem tej tablicy. Jeli szukany element jest
wikszy, szukanie kontynuuje si w pierwszej poowie elementw, w przeciwnym przypadku
w drugiej. Dziki temu skala zadania zmniejsza si o poow. Ten sam proces jest powtarzany na wybranej poowie itd. Jeli na przykad tablica zawiera 1024 elementy, szukany
element mona znale (lub stwierdzi, e go nie ma) w dziesiciu krokach, podczas gdy
wyszukiwanie liniowe wymagaoby rednio 512 operacji, aby znale element, i 1024, aby
potwierdzi, e go nie ma.
Algorytm ten implementuje metoda binarySearch z klasy Collections. Naley pamita, e
warunkiem do poprawnego dziaania tego algorytmu jest wczeniejsze posortowanie kolekcji.
Aby znale element, naley na wejciu poda kolekcj, ktra ma zosta przeszukana (musi
implementowa interfejs List wicej na ten temat poniej), i oczywicie sam element. Jeli
kolekcja nie jest posortowana przez metod compareTo z interfejsu Comparable, konieczne jest
dostarczenie dodatkowo komparatora.
i = Collections.binarySearch(c, element);
i = Collections.binarySearch(c, element, comparator);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

723

Warto zwrotna metody binarySearch wiksza bd rwna zeru okrela indeks pasujcego
obiektu. To znaczy c.get(i) jest rwnoznaczne z equal w porzdku porwnywania. Warto
ujemna oznacza, e element nie zosta znaleziony. Mona jednak wykorzysta j do okrelenia miejsca, w ktrym naleaoby ten element wstawi do kolekcji, aby zachowa porzdek
sortowania. Miejsce to jest nastpujce:
insertionPoint = -i - 1;

Nie mona jednak napisa po prostu -i, poniewa warto zero nie byaby jednoznaczna.
Innymi sowy, element w odpowiednim miejscu umieszcza ponisza procedura:
if (i < 0)
c.add(-i - 1, element);

Aby byo warte uwagi, wyszukiwanie binarne musi obsugiwa dostp swobodny. Gdyby
trzeba byo w poszukiwaniu rodkowego elementu przemierzy poow listy powizanej, to
stracilibymy wszystkie jego zalety. Dlatego algorytm binarySearch w przypadku list powizanych przestawia si na przeszukiwanie liniowe.
W Java SE 1.3 nie istnia osobny interfejs dla uporzdkowanych kolekcji z szybkim
dostpem swobodnym, a metoda binarySearch implementowaa bardzo prymitywny
mechanizm sprawdzajcy, czy lista podana na wejciu rozszerza klas AbstractSequen
tialList. Poprawiono to w Java SE 1.4. Obecnie metoda ta sprawdza, czy podana
jako parametr lista implementuje interfejs RandomAccess. Jeli tak, przeprowadzane jest
wyszukiwanie binarne, w przeciwnym przypadku wyszukiwanie liniowe.
java.util.Collections 1.2

static <T extends Comparable<? super T>> int binarySearch(List<T>


elements, T key)

static <T> int binarySearch(List<T> elements, T key, Comparator<? super T> c)

Szuka klucza w licie posortowanej przy uyciu algorytmu wyszukiwania liniowego,


jeli elementy rozszerzaj klas AbstractSequentialList, albo wyszukiwania
binarnego we wszystkich pozostaych przypadkach. Dugo czasu dziaania tych
metod to O(a(n) log n), gdzie n okrela redni czas dostpu do elementu. Zwracaj
indeks klucza w licie lub warto ujemn i, jeli takiego klucza nie ma w licie.
W takim przypadku taki klucz powinien zosta wstawiony w miejscu obliczanym
za pomoc wzoru -i - 1, aby zachowa porzdek sortowania.

13.4.3. Proste algorytmy


W klasie Collections znajduje si jeszcze kilka prostych, ale przydatnych algorytmw. Wrd
nich mona znale prezentowany na pocztku tego rozdziau algorytm wyszukiwania najwikszej wartoci w kolekcji. Z pozostaych mona wymieni algorytm kopiujcy elementy
z jednej listy do innej, zapeniajcy kontener sta wartoci czy odwracajcy list. Po co
w standardowej bibliotece umieszcza takie proste algorytmy? Przecie kady programista
mgby je zaimplementowa za pomoc pojedynczych ptli. Lubimy te algorytmy, poniewa
uatwiaj ycie programistom czytajcym cudzy kod. Czytajc kod napisany przez kogo

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

724

Java. Podstawy
innego, zawsze musimy rozszyfrowa intencje innego programisty. Kiedy widzimy wywoanie metody typu Collections.max, od razu wiemy, jakie jest przeznaczenie danego fragmentu kodu.
Poniszy wycig z API opisuje proste algorytmy dostpne w klasie Collections.
java.util.Collections 1.2

static <T extends Comparable<? super T>> T min(Collection<T> elements)

static <T extends Comparable<? super T>> T max(Collection<T> elements)

static <T> min(Collection<T> elements, Comparator<? super T> c)

static <T> max(Collection<T> elements, Comparator<? super T> c)

Zwraca najmniejszy lub najwikszy element kolekcji (wartoci brzegowe


parametrw uproszczone dla zachowania przejrzystoci).

static <T> void copy(List<? super T> to, List<T> from)

Kopiuje wszystkie elementy z listy rdowej do tych samych miejsc w licie


docelowej. Lista docelowa musi mie przynajmniej tak sam dugo jak lista
rdowa.

static <T> void fill(List<? super T> l, T value)

Wstawia na wszystkich pozycjach w licie t sam warto.

static <T> boolean addAll(Collection<? super T> c, T... values) 5.0

Dodaje wszystkie wartoci do okrelonej kolekcji i zwraca warto true,


jeli kolekcja w wyniku tego dziaania zmienia si.

static <T> boolean replaceAll(List<T> l, T oldValue, T newValue) 1.4

Zastpuje wszystkie elementy identyczne z oldValue wartociami newValue.

static int indexOfSubList(List<?> l, List<?> s) 1.4

static int lastIndexOfSubList(List<?> l, List<?> s) 1.4

Zwraca indeks pierwszej lub ostatniej podlisty listy l rwnej s lub -1, jeli adna
podlista l nie jest rwna s. Jeli na przykad lista l to [s, t, a, r], a s to [t, a, r],
obie metody zwrc indeks 1.

static void swap(List<?> l, int i, int j) 1.4

Zamienia miejscami elementy znajdujce si w okrelonych miejscach.

static void reverse(List<?> l)

Odwraca kolejno elementw listy. Na przykad lista [t, a, r] po odwrceniu


to [r, a, t]. Dugo czasu dziaania tej metody to O(n), gdzie n okrela
dugo listy.

static void rotate(List<?> l, int d) 1.4

Przeprowadza rotacj elementw listy, przenoszc obiekt z indeksem i do miejsca


(i + d) % l.size(). Na przykad rotacja listy [t, a, r] o dwa pola daje w wyniku

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

725

list [a, r, t]. Dugo czasu dziaania tej metody to O(n), gdzie n okrela
dugo listy

static int frequency(Collection<?> c, Object o) 5.0

Zwraca liczb elementw w c, ktre s rwne obiektowi o.

boolean disjoint(Collection<?> c1, Collection<?> c2) 5.0

Zwraca warto true, jeli kolekcje nie maj wicej elementw wsplnych.

13.4.4. Pisanie wasnych algorytmw


Piszc wasny algorytm (a raczej kad metod przyjmujc jako parametr kolekcj), naley,
kiedy to tylko moliwe, wykorzystywa interfejsy, a nie konkretne implementacje. Wyobramy
sobie na przykad, e chcemy zapeni obiekt JMenu zbiorem elementw menu. Metod tak
mona by byo zaimplementowa w tradycyjny sposb nastpujco:
void fillMenu(JMenu menu, ArrayList<JMenuItem> items)
{
for (JMenuItem item : items)
menu.addItem(item);
}

Jednak w ten sposb ograniczamy zakres ruchu wywoujcego metod opcje wyboru musz
by podane w postaci obiektu ArrayList. Jeli zdarzy si, e opcje te bd w jakim innym
kontenerze, konieczne bdzie ich przepakowanie. Znacznie lepiej byoby przyj bardziej
ogln kolekcj.
Naley sobie zada nastpujce pytanie: jaka jest najbardziej oglna metoda, ktra przyda si
w tym zastosowaniu? W tym przypadku konieczne jest tylko odwiedzenie wszystkich elementw, a do tego nadaje si podstawowy interfejs Collection. Poniej znajduje si poprawiona wersja metody fillMenu przyjmujca kolekcje kadego rodzaju:
void fillMenu(JMenu menu, Collection<JMenuItem> items)
{
for (JMenuItem item : items)
menu.addItem(item);
}

Teraz metod t mona wywoa przy uyciu struktury ArrayList, LinkedList bd tablicy
opakowanej przez metod opakowujc Arrays.asList.
Skoro uywanie interfejsw kolekcyjnych jako parametrw metod jest takim dobrym
rozwizaniem, czemu nie jest ono stosowane czciej w bibliotece Javy? Na przykad klasa JComboBox ma dwa konstruktory:
JComboBox(Object[] items)
JComboBox(Vector<?> items)

Powodem jest po prostu czas. Biblioteka Swing zostaa utworzona przed powstaniem
biblioteki kolekcji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

726

Java. Podstawy
Jeli piszemy metod zwracajc kolekcj, moemy zdecydowa si na zwrcenie take
interfejsu zamiast klasy, poniewa pniej moemy zmieni zdanie i zaimplementowa nasz
metod ponownie przy uyciu innej kolekcji.
Napiszmy na przykad metod o nazwie getAllItems zwracajc wszystkie elementy menu.
List<MenuItem> getAllItems(JMenu menu)
{
ArrayList<MenuItem> items = new ArrayList<MenuItem>()
for (int i = 0; i < menu.getItemCount(); i++)
items.add(menu.getItem(i));
return items;
}

Pniej moemy doj do wniosku, e nie chcemy kopiowa elementw, a tylko utworzy ich
widok. Moemy tego dokona, zwracajc anonimow podklas klasy AbstractList.
List<MenuItem> getAllItems(final JMenu menu)
{
return new
AbstractList<MenuItem>()
{
public MenuItem get(int i)
{
return item.getItem(i);
}
public int size()
{
return item.getItemCount();
}
};
}

Jest to oczywicie zaawansowana technika. Stosujc j, naley skrupulatnie dokumentowa,


ktre opcjonalne operacje s obsugiwane. W tym przypadku naley poinformowa wywoujcego, e zwracany obiekt jest niemodyfikowaln list.

13.5. Stare kolekcje


W tym podrozdziale opisujemy klasy kolekcyjne, ktre s dostpne w Javie od samego pocztku.
S to klasa Hashtable i jej przydatna podklasa Properties, podklasa Stack klasy Vector oraz
klasa BitSet.

13.5.1. Klasa Hashtable


Standardowa metoda Hashtable peni takie samo zadanie co HashMap i ma zasadniczo taki sam
jak ona interfejs. Metody tej klasy, podobnie jak klasy Vector, s synchronizowane. Jeli nie
potrzebujesz synchronizacji lub zgodnoci ze starym kodem, uywaj klasy HashMap.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

727

Nazwa klasy to Hashtable, z ma liter t w rodku. W systemie Windows mona


otrzyma dziwne komunikaty o bdach, jeli uyje si nazwy HashTable, poniewa system plikw systemu Windows nie rozrnia maych i wielkich liter, ale kompilator Javy tak.

13.5.2. Wyliczenia
Stare kolekcje do przemierzania elementw uoonych liniowo wykorzystuj interfejs Enume
ration. Znajduj si w nim dwie metody: hasMoreElements i nextElement. S one wiernymi odpowiednikami metod hasNext i next z interfejsu Iterator.
Na przykad metoda elements z klasy Hashtable tworzy wyliczenie z wartoci znajdujcych
si w tablicy:
Enumeration<Employee> e = staff.elements();
while (e.hasMoreElements())
{
Employee e = e.nextElement();
. . .
}

Czasami spotyka si stare metody wymagajce wyliczenia jako parametru. Statyczna metoda
Collections.enumeration tworzy obiekt wyliczeniowy zapeniony elementami pobranymi
z kolekcji. Na przykad:
List<InputStream> streams = . . .;
SequenceInputStream in = new SequenceInputStream(Collections.enumeration(streams));
// Konstruktor SequenceInputStream wymaga na wejciu typu wyliczeniowego.

W jzyku C++ jako parametry czsto stosuje si iteratory. Na szczcie w Javie


niewielu programistw stosuje t technik. O wiele lepiej jest zamiast iteratora
przekaza kolekcj. Kolekcja jest o wiele bardziej uyteczna. Odbiorca moe zawsze
w razie potrzeby wydoby iterator z obiektu kolekcji, a poza tym ma do dyspozycji
wszystkie metody tej kolekcji. W starszym kodzie mona jednak spotka wyliczenia,
poniewa byy one jedynym sposobem uzyskania kolekcji uoglnionych przed wprowadzeniem kolekcji w Java SE 1.2.
java.util.Enumeration<E> 1.0

boolean hasMoreElements()

Zwraca warto true, jeli s jeszcze jakie elementy.

E nextElement()

Zwraca kolejny element do odwiedzenia. Nie naley wywoywa tej metody, jeli
metoda hasMoreElements() zwrci warto false.
java.util.Hashtable<K, V> 1.0

Enumeration<K> keys()

Zwraca obiekt wyliczeniowy, ktry przemierza klucze tablicy Hashtable.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

728

Java. Podstawy

Enumeration<V> elements()

Zwraca obiekt wyliczeniowy, ktry przemierza elementy tablicy Hashtable.


java.util.Vector<E> 1.0

Enumeration<E> elements()

Zwraca obiekt wyliczeniowy, ktry przeglda elementy wektora.

13.5.3. Mapy wasnoci


Mapa wasnoci (ang. property map) to struktura danych o bardzo szczeglnych cechach.
Ma trzy wyrniajce j waciwoci:

Klucze i wartoci s acuchami.

Tablic t mona zapisa w pliku i zaadowa z pliku.

Posiada dodatkow tablic z wartociami domylnymi.

Klasa Javy implementujca map wasnoci nosi nazw Properties.


Mapy te s powszechnie wykorzystywane do okrelania opcji konfiguracyjnych programw
zobacz rozdzia 10.
java.util.Properties 1.0

Properties()

Tworzy pust map wasnoci.

Properties(Properties defaults)

Tworzy pust map wasnoci z zestawem wartoci domylnych.

String getProperty(String key)

Pobiera warto przypisan do wasnoci. Zwraca acuch przypisany


do okrelonego klucza. Jeli nie ma go w tablicy gwnej, szuka w tablicy
wartoci domylnych.

String getProperty(String key, String defaultValue)

Zwraca wasno z domyln wartoci, jeli klucz nie zostanie znaleziony.


Zwraca acuch przypisany do klucza lub acuch domylny, jeli acuch
nie zostanie znaleziony w mapie.

void load(InputStream in)

aduje map wasnoci ze strumienia wejciowego.

void store(OutputStream out, String commentString)

Zapisuje zawarto mapy wasnoci w strumieniu wyjciowym.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

729

13.5.4. Stosy
Biblioteka standardowa od wersji 1.0 zawiera klas Stack udostpniajc powszechnie znane
metody, takie jak push i pop. Klasa ta dziedziczy jednak po klasie Vector, ktra z teoretycznego punktu widzenia nie jest zadowalajca pozwala na stosowanie takich niewaciwych
stosom metod jak insert i remove, wstawiajcych i usuwajcych wartoci w dowolnym
miejscu, nie tylko na wierzchu stosu.
java.util.Stack<E> 1.0

E push(E item)

Wstawia element na stos i go zwraca.

E pop()

Usuwa i zwraca element znajdujcy si na wierzchu stosu. Nie naley uywa


tej metody na pustym stosie.

E peek()

Zwraca element z wierzchu stosu, nie usuwajc go. Nie naley wywoywa
tej metody na pustym stosie.

13.5.5. Zbiory bitw


Klasa BitSet umoliwia przechowywanie szeregw bitowych (nie s to zbiory z matematycznego punktu widzenia lepszymi nazwami byyby tablica bitw lub BitVector). Znajduje
ona zastosowanie w sytuacjach, w ktrych potrzebna jest szybko dziaajca struktura do przechowywania szeregw bitw (na przykad flag). Poniewa obiekt BitSet pakuje bity w bajty,
struktura ta jest znacznie szybsza ni obiekt ArrayList wypeniony obiektami typu Boolean.
Klasa BitSet udostpnia wygodny interfejs do odczytu, ustawiania oraz resetowania poszczeglnych bitw. Interfejs ten pozwala unikn tworzenia masek bitowych i innych operacji na
bitach, ktre s konieczne przy przechowywaniu bitw w zmiennych typu int lub long.
Na przykad dla zbioru BitSet o nazwie bucketOfBits ponisza instrukcja zwrci warto
true, jeli i-ty bit jest wczony, lub false w przeciwnym przypadku.
bucketOfBits.get(i)

Podobnie ponisza instrukcja wcza i-ty bit.


bucketOfBits.set(i)

Z kolei ponisza instrukcja wycza i-ty bit.


bucketOfBits.clear(i)

Szablon bitset w C++ ma tak sam funkcjonalno jak klasa BitSet w Javie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

730

Java. Podstawy
java.util.BitSet 1.0

BitSet(int initialCapacity)

Tworzy zbir bitw.

int length()

Zwraca logiczn dugo zbioru bitw 1 plus indeks najwyszego


ustawionego bitu.

boolean get(int bit)

Pobiera bit.

void set(int bit)

Ustawia bit.

void clear(int bit)

Usuwa bit.

void and(BitSet set)

Tworzy sum logiczn dwch zbiorw bitw.

void or(BitSet set)

Tworzy alternatyw logiczn dwch zbiorw.

void xor(BitSet set)

Wykonuje dziaanie logiczne XOR (lub wykluczajce) na dwch zbiorach bitw.

void andNot(BitSet set)

Usuwa wszystkie bity ze zbioru bitw, ktre s ustawione w innym zbiorze bitw.

13.5.5.1. Test wydajnoci za pomoc sita Eratostenesa


Zastosowanie zbiorw bitw zademonstrujemy za pomoc algorytmu do znajdowania liczb
pierwszych, czyli sita Eratostenesa (liczba pierwsza to taka, ktra dzieli si bez reszty tylko
przez siebie sam i jeden, a sito Eratostenesa jest pierwsz odkryt metod obliczania tych
liczb). Nie jest to najlepszy algorytm do znajdowania liczb pierwszych, ale z niewyjanionych
powodw zyska du popularno jako test wydajnoci kompilatorw (nie jest to te dobry
test wydajnoci, poniewa testuje przede wszystkim operacje bitowe).
Jest to jednak ukon w stron tradycji przedstawiamy implementacj tego algorytmu. Prezentowany program zlicza wszystkie liczby pierwsze w zakresie od 2 do 2 000 000 (jest ich
148 933, a wic pewnie lepiej ich wszystkich nie drukowa).
Nie zagbiajc si zbytnio w szczegy implementacyjne tego programu, jego zadaniem jest
przejcie przez zbir bitw zawierajcy dwa miliony elementw. Najpierw wczamy wszystkie bity. Nastpnie wyczamy bity bdce wielokrotnociami liczb, ktre wiadomo, e s
pierwsze. Liczby odpowiadajce bitom pozostaym po tym procesie s pierwsze. Listing 13.8
przedstawia implementacj tego programu w jzyku Java, a listing 13.9 w jzyku C++.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

Listing 13.8. sieve/Sieve.java


package sieve;
import java.util.*;
/**
* Program wykonujcy test porwnawczy na bazie algorytmu sita Eratostenesa. Oblicza wszystkie
* liczby pierwsze do 2 000 000.
* @version 1.21 2004-08-03
* @author Cay Horstmann
*/
public class Sieve
{
public static void main(String[] s)
{
int n = 2000000;
long start = System.currentTimeMillis();
BitSet b = new BitSet(n + 1);
int count = 0;
int i;
for (i = 2; i <= n; i++)
b.set(i);
i = 2;
while (i * i <= n)
{
if (b.get(i))
{
count++;
int k = 2 * i;
while (k <= n)
{
b.clear(k);
k += i;
}
}
i++;
}
while (i <= n)
{
if (b.get(i)) count++;
i++;
}
long end = System.currentTimeMillis();
System.out.println(count + " liczb pierwszych");
System.out.println((end - start) + " milisekund");
}
}

Listing 13.9. Sieve.cpp


/**
@version 1.21 2004-08-03
@author Cay Horstmann
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

731

732

Java. Podstawy

Mimo e sito nie jest dobrym testem wydajnoci, nie moglimy si oprze pokusie zmierzenia czasu obu implementacji algorytmu. Oto wyniki uzyskane na notebooku ThinkPad z dwurdzeniowym procesorem 2,4 GHz, 4 GB pamici RAM i systemem
operacyjnym Linux Ubuntu 7.04:
C++ (g++ 4.6.3): 160 milisekund,
Java (Java SE 7): 84 milisekundy.

Test ten przeprowadzilimy we wszystkich dziewiciu wydaniach tej ksiki i w ostatnich piciu Java bije C++ na gow. Trzeba jednak ujawni, e jeli zoptymalizujemy
kompilator C++, pobije on Jav, uzyskujc czas 20 milisekund. Taki wynik w Javie mona
by byo uzyska tylko wtedy, gdyby program dziaa na tyle dugo, aby wczy si kompilator JIT Hotspot.
#include <bitset>
#include <iostream>
#include <ctime>
using namespace std;
int main()
{
const int N = 2000000;
clock_t cstart = clock();
bitset<N + 1> b;
int count = 0;
int i;
for (i = 2; i <= N; i++)
b.set(i);
i = 2;
while (i * i <= N)
{
if (b.test(i))
{
count++;
int k = 2 * i;
while (k <= N)
{
b.reset(k);
k += i;
}
}
i++;
}
while (i <= N)
{
if (b.test(i))
count++;
i++;
}
clock_t cend = clock();
double millis = 1000.0 * (cend - cstart) / CLOCKS_PER_SEC;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 13.

Kolekcje

733

cout << count << " liczb pierwszych\n" << millis << " milisekund\n";
return 0;
}

Na tym koczy si nasza podr po architekturze kolekcji w jzyku Java. Jak wida, biblioteka
w tym jzyku udostpnia bogaty zestaw klas kolekcyjnych, majcych na celu zaspokajanie
potrzeb programisty. W ostatnim rozdziale ksiki zajmiemy si bardzo wanym tematem
wspbienoci.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

734

Java. Podstawy

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

14

Wielowtkowo
W tym rozdziale:

Czym s wtki

Przerywanie wtkw

Stany wtkw

Wasnoci wtkw

Synchronizacja

Kolejki blokujce

Kolekcje bezpieczne wtkowo

Interfejsy Callable i Future

Klasa Executors

Synchronizatory

Wtki a biblioteka Swing

Wikszo uytkownikw systemw operacyjnych zna pojcie wielozadaniowoci, czyli


zdolnoci systemu do uruchamiania wicej ni jednego programu pozornie jednoczenie.
Mona na przykad pisa lub wysya e-mail i w tym samym czasie drukowa jaki dokument.
W dzisiejszych czasach coraz czciej spotyka si komputery wyposaone w wicej ni jeden
procesor, cho liczba procesw dziaajcych jednoczenie nie jest ograniczona liczb procesorw. System operacyjny stwarza pozory rwnolegego wykonywania zada, kademu
procesowi przydzielajc odpowiedni czas pracy procesora.
Programy wielowtkowe przenosz koncepcj wielozadaniowoci o jeden poziom niej,
gdzie poszczeglne programy sprawiaj zudzenie wykonywania wielu zada naraz. Kade
z tych zada jest zwyczajowo nazywane wtkiem (ang. thread), a pena nazwa to wtek
sterowania (ang. thread of control). Programy potrafice dziaa w wicej ni jednym wtku
nazywaj si programami wielowtkowymi (ang. multithreaded).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

736

Java. Podstawy
Jaka jest zatem rnica pomidzy wieloma procesami a wieloma wtkami? Przede wszystkim
naley zauway, e kady proces posiada peen zestaw wasnych zmiennych, podczas gdy
wtki wspdziel dane z innymi wtkami. Brzmi to dosy ryzykownie i rzeczywicie moe
czasami sprawia problemy, o czym przekonasz si za chwil. Z drugiej strony dziki wspdzieleniu zmiennych komunikacja pomidzy wtkami zachodzi sprawniej i jest atwiejsza do
zaprogramowania ni komunikacja midzyprocesowa. Ponadto wtki w niektrych systemach
operacyjnych s lejsze od procesw, to znaczy utworzenie i zniszczenie pojedynczego wtku
zajmuje mniej czasu ni uruchomienie nowego procesu.
Wielowtkowo jest niezwykle praktycznym narzdziem. Wiadomo na przykad, e przegldarka powinna mie moliwo pobierania kilku obrazw jednoczenie, a serwer sieciowy
musi obsugiwa wiele da w tym samym czasie. Programy z graficznym interfejsem uytkownika dysponuj osobnym wtkiem do zbierania zdarze z interfejsu pochodzcych od
rodowiska operacyjnego. Ten rozdzia dotyczy pisania aplikacji wielowtkowych w Javie.
Ostrzeenie: programy wielowtkowe bywaj bardzo skomplikowane. My opisujemy wszystkie
narzdzia, ktrych moe potrzebowa programista. Jednak w poszukiwaniu opisw bardziej
zaawansowanych technik programowania systemowego odsyamy do innych rde, na przykad ksiki Java. Wspbieno dla praktykw, ktrej autorami s Brian Goetz, Tim Peierls,
Joshua Bloch, Joseph Bowbeer, David Holmes i Doug Lea (Helion, Gliwice 2007).

14.1. Czym s wtki


Zaczniemy od programu jednowtkowego, w ktrym wykonywanie kilku czynnoci naraz jest
utrudnione. Po jego szczegowym przeanalizowaniu pokaemy, jak atwo mona go przerobi, aby dziaa w kilku wtkach. Program ten jest animacj odbijajcej si piki, ktra pozostaje w cigym ruchu. Program sprawdza, czy pika nie uderza o cian, i rysuje j ponownie (rysunek 14.1).
Rysunek 14.1.
Jednowtkowa
animacja
odbijajcej
si piki

Po uruchomieniu programu i naciniciu przycisku Start pika zaczyna si odbija, wychodzc


z lewego grnego rogu okna. Obiekt obsugujcy przycisk Start wywouje metod addBall,
ktra zawiera ptl powtarzajc si przez 1000 ruchw. Metoda move przesuwa nieco pik,
koryguje kierunek w przypadku odbicia od ciany i ponownie rysuje panel.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

737

Ball ball = new Ball();


panel.add(ball);
for (int i = 1; i <= STEPS; i++)
{
ball.move(panel.getBounds());
panel.paint(panel.getGraphics());
Thread.sleep(DELAY);
}

Wywoanie metody Thread.sleep nie powoduje utworzenia nowego wtku, poniewa metoda
ta wstrzymuje dziaanie biecego wtku na okrelon liczb milisekund.
Metoda sleep moe zgosi wyjtek InterruptedException. Zajmiemy si nim dalej. Na razie
jeli wystpi ten wyjtek, po prostu koczymy odbijanie piki.
Po uruchomieniu programu pika bardzo adnie odbija si od cian, ale niestety cakowicie
pochania aplikacj. Jeli zechcemy zatrzyma odbijanie przed wykonaniem przez pik tysica
ruchw i naciniemy w tym celu przycisk Zamknij, nic si nie stanie. Pika bdzie dalej si
odbija, poniewa nie mona robi w programie nic innego, dopki nie zakoczy si jedno
zadanie.
Na kocu kodu prezentowanego w tym podrozdziale znajduje si ponisza
instrukcja:
comp.paint(comp.getGraphics())

Mieci si ona w metodzie addBall klasy BounceFrame. Jest to bardzo dziwna technika
programowania normalnie wywoano by metod repaint, a reszt zajaby si biblioteka AWT. Jeli jednak wywoamy metod repaint w tym programie, panel nie zostanie
nigdy odwieony, poniewa metoda addBall cakowicie zaja wszystkie procesy. Dodatkowo warto zauway, e komponent piki dziedziczy po klasie JPanel, dziki czemu atwiej
jest wyczyci to. W nastpnym programie, w ktrym pooenie piki jest obliczane
w osobnym wtku, wrcimy do znanej nam metody repaint z klasy JComponent.

Oczywicie funkcjonalno tego programu nie jest zbyt bogata. Z pewnoci nie chcielibymy, aby programy wykonujce czasochonne zadania dziaay w ten sposb. Na przykad
podczas pobierania danych z sieci bardzo czsto zdarza si nam utkn w zadaniu, ktre
chcielibymy przerwa. Wyobramy sobie na przykad, e pobieramy duy obraz i po zobaczeniu jego fragmentu wiemy ju, e nie chcemy oglda reszty. W takiej sytuacji przydaje
si przycisk Stop lub Wstecz, ktry przerywa proces adowania. W kolejnym podrozdziale
nauczysz si pozostawia kontrol w rkach uytkownika dziki uruchamianiu najwaniejszych czci kodu w osobnym wtku.
Kod programu jest przedstawiony na listingach 14.1 14.3.
Listing 14.1. bounce/Bounce.java
package bounce;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

738

Java. Podstawy
/**
* Wywietla animowan pik
* @version 1.33 2007-05-17
* @author Cay Horstmann
*/
public class Bounce
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new BounceFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka zawierajca komponent piki i przyciski
*/
class BounceFrame extends JFrame
{
private BallComponent comp;
public static final int STEPS = 1000;
public static final int DELAY = 3;
/**
* Tworzy ramk z komponentem zawierajcym odbijajc si pik oraz przyciskami
* Start i Zamknij.
*/
public BounceFrame()
{
setTitle("Pika");
comp = new BallComponent();
add(comp, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start", new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
addBall();
}
});
addButton(buttonPanel, "Zamknij", new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
add(buttonPanel, BorderLayout.SOUTH);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

pack();
}
/**
* Dodaje przycisk do kontenera.
* @param c kontener
* @param title tytu przycisku
* @param listener suchacz akcji przycisku
*/
public void addButton(Container c, String title, ActionListener listener)
{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}
/**
* Dodaje odbijajc si pik do panelu i odbija j 1000 razy
*/
public void addBall()
{
try
{
Ball ball = new Ball();
comp.add(ball);
for (int i = 1; i <= STEPS; i++)
{
ball.move(comp.getBounds());
comp.paint(comp.getGraphics());
Thread.sleep(DELAY);
}
}
catch (InterruptedException e)
{
}
}
}

Listing 14.2. bounce/Ball.java


package bounce;
import java.awt.geom.*;
/**
* Pika, ktra porusza si i odbija od krawdzi prostokta
* @version 1.33 2007-05-17
* @author Cay Horstmann
*/
public class Ball
{
private static final int XSIZE = 15;
private static final int YSIZE = 15;
private double x = 0;
private double y = 0;
private double dx = 1;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

739

740

Java. Podstawy
private double dy = 1;
/**
* Przesuwa pik do nastpnego pooenia, odwracajc kierunek, jeli pika uderzy w krawd
*/
public void move(Rectangle2D bounds)
{
x += dx;
y += dy;
if (x < bounds.getMinX())
{
x = bounds.getMinX();
dx = -dx;
}
if (x + XSIZE >= bounds.getMaxX())
{
x = bounds.getMaxX() - XSIZE;
dx = -dx;
}
if (y < bounds.getMinY())
{
y = bounds.getMinY();
dy = -dy;
}
if (y + YSIZE >= bounds.getMaxY())
{
y = bounds.getMaxY() - YSIZE;
dy = -dy;
}
}
/**
* Ustawia pik w jej aktualnym pooeniu
*/
public Ellipse2D getShape()
{
return new Ellipse2D.Double(x, y, XSIZE, YSIZE);
}
}

Listing 14.3. bounce/BallComponent.java


package bounce;
import java.awt.*;
import java.util.*;
import javax.swing.*;
/**
* Komponent rysujcy piki
* @version 1.34 2012-01-26
* @author Cay Horstmann
*/
public class BallComponent extends JPanel
{
private static final int DEFAULT_WIDTH = 450;
private static final int DEFAULT_HEIGHT = 350;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

741

private java.util.List<Ball> balls = new ArrayList<>();


/**
* Dodaje pik do komponentu
* @param b pika, ktra ma zosta dodana
*/
public void add(Ball b)
{
balls.add(b);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g); // Czyszczenie ta
Graphics2D g2 = (Graphics2D) g;
for (Ball b : balls)
{
g2.fill(b.getShape());
}
}
public Dimension getPreferredSize() { return new Dimension(DEFAULT_WIDTH,
DEFAULT_HEIGHT); }
}
java.lang.Thread 1.0

static void sleep(long millis)

Zatrzymuje wykonywanie na okrelon liczb milisekund.


Parametr:

millis

Liczba milisekund

14.1.1. Wykonywanie zada w osobnych wtkach


Usprawnimy nasz program, uruchamiajc kod poruszajcy pik w osobnym wtku. Bdzie
mona nawet wypuci kilka piek naraz, a kada z nich bdzie poruszana przez oddzielny
wtek. Dodatkowo rwnolegle z nimi bdzie dziaa wtek dystrybucji zdarze, ktry
zajmuje si zdarzeniami interfejsu uytkownika. Dziki temu, e kady wtek moe zosta
uruchomiony, wtek dystrybucji zdarze moe odnotowa nacinicie przycisku Zakocz
w czasie, gdy piki s w ruchu, i wykona akcj zamknicia aplikacji.
Program z pik ma za zadanie wykaza, e wspbieno jest niezbdna. Oglnie rzecz
biorc, zawsze trzeba uwaa na dugotrwae operacje. Wchodz one w skad jakiego
wikszego szkieletu, jak na przykad GUI albo interfejs sieciowy. Uytkownik zawsze oczekuje, e dostanie szybk odpowied. Dlatego wszystkie czasochonne operacje naley wykonywa w osobnych wtkach.
Poniej przedstawiamy prosty zestaw czynnoci, ktre naley wykona, aby uruchomi zadanie
w osobnym wtku:
1.

Kod zadania umie w metodzie run klasy implementujcej interfejs Runnable.


Ten bardzo prosty interfejs posiada tylko jedn metod:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

742

Java. Podstawy
public interface Runnable
{
void run();
}

Implementacja klasy jest prosta i wyglda nastpujco:


class MyRunnable implements Runnable
{
public void run()
{
kod zadania
}
}

2. Utwrz obiekt powstaej klasy:


Runnable r = new MyRunnable();

3. Utwrz obiekt typu Thread z obiektu Runnable:


Thread t = new Thread(r);

4. Uruchom wtek:
t.start();

Umieszczenie programu odbijajcej si piki w osobnym wtku wymaga tylko zaimplementowania klasy BallRunnable i umieszczenia kodu animacji w metodzie run:
class BallRunnable implements Runnable
{
. . .
public void run()
{
try
{
for (int i = 1; i <= STEPS; i++)
{
ball.move(component.getBounds());
component.repaint();
Thread.sleep(DELAY);
}
}
catch (InterruptedException exception)
{
}
}
. . .
}

Nie mona zapomnie o przechwyceniu wyjtku InterruptedException, ktrym straszy


metoda sleep. Wyjtek ten opisujemy dokadnie w kolejnym podrozdziale. Z reguy oznacza
on konieczno przerwania wtku. Dlatego nasza metoda run koczy dziaanie w chwili jego
wystpienia.
Kade kliknicie przycisku Start powoduje, e metoda addBall uruchamia nowy wtek (zobacz
rysunek 14.2):

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

743

Rysunek 14.2.
Uruchomienie
kilku wtkw

Ball b = new Ball();


panel.add(b);
Runnable r = new BallRunnable(b, panel);
Thread t = new Thread(r);
t.start();

To wszystko, co trzeba zrobi, aby uruchomi zadania w osobnych wtkach! Pozostaa cz


tego rozdziau jest powicona komunikacji pomidzy wtkami.
Peny kod programu przedstawia listing 14.4.
Listing 14.4. bounceThread/BounceThread.java
package bounceThread;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/**
* Wywietla animowane piki
* @version 1.33 2007-05-17
* @author Cay Horstmann
*/
public class BounceThread
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new BounceFrame();
frame.setTitle("BounceThread");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Klasa implementujca interfejs Runnable i tworzca animacj piki
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

744

Java. Podstawy
class BallRunnable implements Runnable
{
private Ball ball;
private Component component;
public static final int STEPS = 1000;
public static final int DELAY = 5;
/**
* Tworzy obiekt Runnable
* @param aBall pika
* @param aComponent komponent, w ktrym odbija si pika
*/
public BallRunnable(Ball aBall, Component aComponent)
{
ball = aBall;
component = aComponent;
}
public void run()
{
try
{
for (int i = 1; i <= STEPS; i++)
{
ball.move(component.getBounds());
component.repaint();
Thread.sleep(DELAY);
}
}
catch (InterruptedException e)
{
}
}
}
/**
* Ramka z panelem i przyciskami
*/
class BounceFrame extends JFrame
{
private BallComponent comp;
/**
* Tworzy ramk z komponentem zawierajcym pik i przyciski Start oraz Zamknij
*/
public BounceFrame()
{
comp = new BallComponent();
add(comp, BorderLayout.CENTER);
JPanel buttonPanel = new JPanel();
addButton(buttonPanel, "Start", new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
addBall();
}
});

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

745

addButton(buttonPanel, "Zamknij", new ActionListener()


{
public void actionPerformed(ActionEvent event)
{
System.exit(0);
}
});
add(buttonPanel, BorderLayout.SOUTH);
pack();
}
/**
* Dodaje przycisk do kontenera
* @param c kontener
* @param title tytu przycisku
* @param listener suchacz akcji przycisku
*/
public void addButton(Container c, String title, ActionListener listener)
{
JButton button = new JButton(title);
c.add(button);
button.addActionListener(listener);
}
/**
* Dodaje pik do obszaru roboczego i uruchamia wtek wykonujcy kod odpowiedzialny za jej odbijanie
*/
public void addBall()
{
Ball b = new Ball();
comp.add(b);
Runnable r = new BallRunnable(b, comp);
Thread t = new Thread(r);
t.start();
}
}

Wtek mona take zdefiniowa, tworzc podklas klasy Thread:


class MyThread extends Thread
{
public void run()
{
kod zadania
}
}

Wtedy naley utworzy obiekt powstaej podklasy i wywoa na jego rzecz metod start.
Podejcie to jest jednak obecnie odradzane. Zadanie, ktre ma by uruchamiane, powinno
by oddzielone od mechanizmu je uruchamiajcego. W przypadku duej liczby zada tworzenie osobnego wtku dla kadego z nich moe by zbyt kosztowne. W takiej sytuacji
naley uy puli wtkw zobacz podrozdzia 14.9, Klasa Executors.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

746

Java. Podstawy

Nie wywouj metody run z klasy Thread lub obiektu typu Runnable. Bezporednie
wywoanie tej metody powoduje jedynie wykonanie zadania w tym samym wtku
aden nowy wtek nie jest uruchamiany. W zamian naley wywoywa metod
Thread.start. Tworzy ona nowy wtek, ktry uruchamia metod run.
java.lang.Thread 1.0

Thread(Runnable target)

Tworzy nowy wtek, ktry wywouje metod run() okrelonego obiektu target.

void start()

Uruchamia wtek, wywoujc metod run(). Ta metoda zwraca warto


natychmiast. Nowy wtek dziaa rwnolegle.

void run()

Wywouj metod run obiektu implementujcego interfejs Runnable.


java.lang.Runnable 1.0

void()

Metod t naley przedefiniowa, wstawiajc do niej instrukcje zadania,


ktre ma zosta wykonane.

14.2. Przerywanie wtkw


Wtek koczy dziaanie w chwili zwrcenia przez jego metod run wartoci w wyniku
wywoania instrukcji return, po wykonaniu ostatniej instrukcji w ciele metody run lub jeli
wystpi nieprzechwycony w tej metodzie wyjtek. W pierwszej wersji Javy istniaa jeszcze
metoda stop, ktr mg wywoa jeden wtek w celu zamknicia innego wtku. Jest ona
jednak obecnie odradzana. Powody tego stanu rzeczy opisujemy w podrozdziale 14.5.15,
Dlaczego metody stop i suspend s wycofywane.
Istnieje sposb na zmuszenie wtku do zamknicia. Suy do tego metoda interrupt, ktra
wysya danie zamknicia wtku.
Wywoanie metody interrupt na rzecz wtku powoduje ustawienie jego statusu przerwania (ang. interrupted state). Jest to zmienna logiczna obecna w kadym wtku. Kady wtek
powinien co jaki czas sprawdza, czy nie zosta przerwany.
Aby sprawdzi, czy status przerwania zosta ustawiony, naley najpierw pobra biecy wtek
za pomoc metody Thread.currentThread, a nastpnie wywoa metod isInterrupted:
while (!Thread.currentThread().isInterrupted() && wicej instrukcji)
{
dodatkowe dziaania
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

747

Statusu przerwania nie mona jednak sprawdzi, jeli wtek jest zablokowany. W takiej
sytuacji do gry wchodzi wyjtek InterruptedException. Kiedy metoda interrupt jest wywoywana na rzecz wtku, ktry blokuj metody takie jak sleep czy wait, wywoania blokujce
zostaj zakoczone przez wyjtek InterruptedException (istniej metody blokujce wejcia-wyjcia, ktrych nie mona przerwa; w takiej sytuacji naley rozway uycie ich
przerywalnych zamiennikw szczegowe informacje na ten temat znajduj si w rozdziaach 1. i 3. drugiego tomu).
Nie istnieje aden wymg formalny, e wtek, ktry zosta przerwany, musi zosta zamknity.
Przerwanie wtku powoduje jedynie zwrcenie jego uwagi, a decyzja, jak na to zareagowa,
naley do niego samego. Niektre bardzo wane wtki powinny obsuy wyjtek i kontynuowa prac. Czsto jednak przerwanie jest przez wtek interpretowane jako danie zamknicia. Metoda run takiego wtku wyglda nastpujco:
public void run()
{
try
{
. . .
while (!Thread.currentThread().isInterrupted() && dodatkowe instrukcje)
{
instrukcje
}
}
catch(InterruptedException e)
{
// Wtek zosta przerwany, bdc w stanie upienia bd oczekiwania.
}
finally
{
czyszczenie w razie potrzeby
}
// Wyjcie z metody run powoduje zakoczenie wtku.
}

Wywoywanie metody isInterrupted staje si bezcelowe, jeli po kadej iteracji wywoywana jest metoda sleep (lub inna pozwalajca na przerwanie). Jeli metoda sleep zostanie
wywoana na rzecz wtku z ustawionym statusem przerwania, wtek ten nie zostanie upiony.
Zamiast tego jego status zostanie wyzerowany oraz zostanie zgoszony wyjtek Interrupted
Exception. Dlatego jeli ptla zawiera wywoanie metody sleep, nie naley w niej sprawdza statusu przerwania. W zamian naley przechwyci wyjtek InterruptedException:
public void run()
{
try
{
. . .
while (instrukcje)
{
instrukcje
Thread.sleep(delay);
}
}
catch(InterruptedException e)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

748

Java. Podstawy
{
// Wtek zosta przerwany w czasie upienia.
}
finally
{
Czyszczenie w razie potrzeby
}
// Wyjcie z metody run powoduje zamknicie wtku.
}

Istniej dwie bardzo podobne do siebie metody: interrupted oraz isInterrupted.


Pierwsza z nich jest statyczna i sprawdza, czy biecy wtek nie zosta przerwany.
Dodatkowo jej wywoanie powoduje wyzerowanie statusu przerwania wtku. Druga natomiast jest metod obiektow, za pomoc ktrej mona sprawdzi status przerwania
dowolnego wtku, bez jego zmiany.

Czsto spotyka si fragmenty kodu, w ktrych wyjtek InterruptedException jest tumiony


w nastpujcy sposb:
void mySubTask()
{
. . .
try { sleep(delay); }
catch (InterruptedException e) {}
. . .
}

// Nie ignoruj!

Nie naley tego robi! Jeli nie masz adnego dobrego pomysu na klauzul catch, masz
jeszcze dwa inne dobre wyjcia:

W klauzuli catch umie instrukcj Thread.currentThread().interrupt(),


ktra ustawi status przerwania. Dziki temu wywoujcy bdzie mg sprawdzi
wyjtek.
void mySubTask()
{
. . .
try { sleep(delay); }
catch (InterruptedException e) { Thread().currentThread().interrupt(); }
. . .
}

Lepszym wyjciem jest dodanie do metody instrukcji throws InterruptedException


i pominicie bloku try. Wtedy wywoujcy (lub ostatecznie metoda run) moe
przechwyci ten wyjtek.
void mySubTask() throws InterruptedException
{
. . .
sleep(delay);
. . .
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

749

java.lang.Thread 1.0

void interrupt()

Wysya danie przerwania do wtku. Status przerwania zostaje ustawiony na true.


Jeli wtek jest aktualnie zablokowany przez metod sleep, zgaszany jest wyjtek
InterruptedException.

static boolean interrupted()

Sprawdza, czy biecy wtek (to znaczy ten, ktry wykonuje t instrukcj)
nie zosta przerwany. Naley zauway, e jest to metoda statyczna. Jej wywoanie
ma jeden efekt uboczny zeruje status przerwania biecego wtku na warto
false.

boolean isInterrupted()

Sprawdza, czy wtek nie zosta przerwany. W przeciwiestwie do statycznej


metody interrupted, ta nie zmienia statusu przerwania wtku.

static Thread currentThread()

Zwraca obiekt Thread reprezentujcy aktualnie wykonywany wtek.

14.3. Stany wtkw


Wtek moe by w jednym z szeciu stanw:

NEW (nowy),

RUNNABLE (wykonywalny),

BLOCKED (zablokowany),

WAITING (oczekujcy),

TIMED WAITING (oczekujcy okrelon ilo czasu),

TERMINATED (zakoczony).

Znaczenie tych wszystkich stanw zostao opisane poniej.


Do sprawdzania aktualnego stanu wtku suy metoda getState.

14.3.1. Wtki NEW


Wtek utworzony za pomoc operatora new na przykad new Thread(r) nie jest od razu
uruchamiany. Oznacza to, e pozostaje on w stanie NEW. Jeli wtek znajduje si w stanie NEW,
program nie zacz jeszcze wykonywa znajdujcego si w nim kodu. Przed uruchomieniem wtku trzeba wykona jeszcze kilka dodatkowych czynnoci.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

750

Java. Podstawy

14.3.2. Wtki RUNNABLE


Po wywoaniu metody start wtek przechodzi w stan RUNNABLE. Wtek taki moe, ale nie
musi by uruchomiony. Przydzia czasu dla wtku ley w gestii systemu operacyjnego (w Javie
dla stanu dziaania wtku nie wprowadzono osobnej nazwy, dlatego uruchomiony wtek nadal
pozostaje w stanie RUNNABLE).
Po uruchomieniu wtek nie musi dziaa cay czas. Zaleca si nawet wstrzymywanie dziaajcych wtkw co jaki czas, aby da szans na dziaanie innym wtkom. Szczegowa kontrola harmonogramu wykonywania wtkw zaley od usug udostpnianych przez system
operacyjny. Systemy planowania wywaszczajcego wtkw przydzielaj kademu wykonywalnemu wtkowi okrelon ilo czasu na wykonanie zadania. Kiedy czas mija, system
operacyjny wywaszcza wtek i przydziela czas innemu wtkowi (zobacz rysunek 14.4).
Przy wybieraniu kolejnego wtku system operacyjny kieruje si priorytetami wtkw
zobacz podrozdzia 14.4.1, Priorytety wtkw.
Wszystkie nowoczesne systemy operacyjne zarwno serwerowe, jak i przeznaczone na
komputery osobiste stosuj planowanie wywaszczajce. Mniejsze urzdzenia, jak telefony
komrkowe, mog wykorzystywa planowanie kooperacyjne. W takim urzdzeniu wtek
traci sterowanie, jeli wywoa metod yield lub zostanie zablokowany czy przestawiony
w stan oczekiwania.
W komputerach z kilkoma procesorami kady procesor moe wykonywa osobny wtek,
dziki czemu wiele wtkw moe dziaa rwnolegle. Oczywicie jeli wtkw jest wicej
ni procesorw, system planujcy i tak musi si zaj przydzielaniem czasu.
Naley zawsze pamita, e wykonywalny wtek moe w danej chwili by uruchomiony lub
nie (dlatego stan ten nazwano RUNNABLE, co oznacza wykonywalny, zamiast na przykad
RUNNING, co znaczyoby wykonywany).

14.3.3. Wtki BLOCKED i WAITING


Kiedy wtek znajduje si w stanie BLOCKED (zablokowany) lub WAITING (oczekujcy), jest
okresowo nieaktywny. Nie wykonuje adnego kodu i zuywa minimalne iloci zasobw.
Decyzja o jego reaktywacji naley do algorytmu planujcego, ktry uzalenia j od sposobu,
w jaki wtek wszed w stan nieaktywnoci:

Kiedy wtek usiuje zaoy blokad wewntrzn na obiekt (ale nie utworzy obiekt
klasy Lock z biblioteki java.util.concurrent), ktry jest aktualnie w dyspozycji
innego wtku, zostaje zablokowany (blokady java.util.concurrent opisujemy
w podrozdziale 14.5.3, Obiekty klasy Lock, a blokady wewntrzne obiektw
w podrozdziale 14.5.5, Sowo kluczowe synchronized). Wtek zostaje
odblokowany, gdy wszystkie pozostae wtki zwolni blokad, a algorytm planujcy
zezwoli mu na przejcie tego obiektu.

Kiedy wtek czeka, a inny wtek powiadomi algorytm planujcy o warunku,


wchodzi w stan WAITING (oczekujcy). Warunki opisujemy w podrozdziale 14.5.4,
Warunki. Stan ten jest wyzwalany przez wywoanie metody Object.wait

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

751

lub Thread.join bd oczekiwanie na obiekt klasy Lock lub Condition z biblioteki


java.util.concurrent. W praktyce rnica pomidzy stanem zablokowania
a oczekiwania nie jest jasna.

Niektre metody posiadaj parametr okrelajcy dugo czasu wykonywania.


Ich wywoanie powoduje wejcie wtku w stan TIMED WAITING. Stan ten utrzymuje
si, a upynie okrelony czas lub wtek odbierze odpowiednie powiadomienie.
Do tego typu metod zalicza si metod Thread.sleep oraz czasowe wersje metod
Object.wait, Thread.join, Lock.tryLock i Condition.wait.

Rysunek 14.3 przedstawia stany, w ktrych moe si znale wtek, oraz moliwe przejcia
pomidzy poszczeglnymi stanami. Kiedy wtek zostanie zablokowany lub przejdzie w stan
oczekiwania (bd zostanie zakoczony), planowane jest uruchomienie kolejnego wtku. Kiedy
wtek jest reaktywowany (poniewa skoczy si jego czas oczekiwania lub zaj blokad),
algorytm planujcy sprawdza, czy ma on wyszy priorytet ni aktualnie dziaajce wtki.
Jeli tak, wywaszcza jeden z uruchomionych wtkw i uruchamia nowy wtek.
Rysunek 14.3.
Stany wtkw

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

752

Java. Podstawy

14.3.4. Zamykanie wtkw


Wtek moe zosta zamknity na jeden z dwch sposobw:

naturalnie wraz z zakoczeniem metody run;

niespodziewanie z powodu nieprzechwyconego wyjtku, ktry zakoczy metod run.

Do zamykania wtkw suy metoda stop. Wyrzuca ona bd ThreadDeath, ktry zabija wtek.
Metoda ta jest jednak odradzana, dlatego nie powinno si jej ju uywa.
java.lang.Thread 1.0

void join()

Oczekuje na zamknicie okrelonego wtku.

void join(long millis)

Czeka na zamknicie okrelonego wtku albo na upyw okrelonej liczby


milisekund.

Thread.State getState() 5.0

Zwraca stan wtku: NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING lub TERMINATED.

void stop()

Zatrzymuje wtek. Metoda ta jest odradzana.

void suspend()

Zawiesza wykonywanie wtku. Metoda odradzana.

void resume()

Ponownie uaktywnia wtek. Metoda ta moe dziaa tylko po wywoaniu metody


suspend(). Metoda odradzana.

14.4. Wasnoci wtkw


W tym podrozdziale opisujemy rne wasnoci wtkw: priorytety, demony, grupy oraz procedury obsugi nieprzechwyconych wyjtkw.

14.4.1. Priorytety wtkw


Kady wtek ma swj priorytet (ang. priority). Domylnie wtek dziedziczy priorytet po
wtku, w ktrym zosta utworzony. Aby zmniejszy lub zwikszy priorytet wtku, naley
uy metody setPriority. Priorytet wtku moe mie dowoln warto z przedziau od
MIN_PRIORITY (1 w klasie Thread) do MAX_PRIORITY (10 w klasie Thread). Priorytet normalny
(NORM_PRIORITY) ma warto 5.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

753

Kiedy algorytm planujcy musi wybra nowy wtek, decyduje si na ten o najwyszym
priorytecie. Naley jednak pamita, e priorytety wtkw s w duym stopniu uzalenione od systemu. Jeli maszyna wirtualna korzysta z implementacji wtkw lecej u podoa platformy, priorytety Javy s odwzorowywane na poziomy platformy, ktra moe dysponowa wiksz lub mniejsz liczb poziomw priorytetu.
Na przykad system Windows wyrnia siedem poziomw priorytetu, a wic niektre z priorytetw Javy zostan odwzorowane na tym samym poziomie priorytetw systemowych.
W maszynie wirtualnej Javy firmy Sun dla systemu Linux priorytety wtkw s cakiem
ignorowane wszystkie wtki maj taki sam priorytet.
Pocztkujcy programici miewaj tendencj do naduywania priorytetw wtkw. Naley
jednak pamita, e powodw do manipulowania nimi jest niewiele. W szczeglnoci nie
naley projektowa programu w taki sposb, aby jego poprawne funkcjonowanie byo zalene
od wysokoci priorytetw.
Kady, kto zdecyduje si na uycie priorytetw, powinien wystrzega si powszechnego bdu popenianego przez pocztkujcych. Jeli istnieje kilka wtkw o wysokim priorytecie, ktre nigdy nie s dezaktywowane, wtki o niszych priorytetach mog
nigdy nie doj do gosu. Algorytm planujcy, wybierajc wtek do uruchomienia, zawsze
wybierze jeden spord tych o najwyszym priorytecie, nawet jeli ma to oznacza, e
wtki o niszych priorytetach nie bd w ogle wykonywane.
java.lang.Thread 1.0

void setPriority(int newPriority)

Ustawia priorytet wtku. Warto priorytetu musi si mieci w przedziale


od Thread.MIN_PRIORITY do Thread.MAX_PRIORITY. Normalny priorytet okrela
warto Thread.NORM_PRIORITY.

static int MIN_PRIORITY

Najmniejszy priorytet, jaki moe mie wtek. Warto minimalnego priorytetu to 1.

static int NORM_PRIORITY

Domylny priorytet domylnie jest to warto 5.

static int MAX_PRIORITY

Najwyszy priorytet, jaki moe mie wtek. Maksymalna warto priorytetu to 10.

static void yield()

Powoduje ustpienie aktualnie wykonywanego wtku. Jeli s jakie inne


wykonywalne wtki o priorytetach nie niszych od tego, zostan one uruchomione
w nastpnej kolejnoci. Warto zwrci uwag, e jest to metoda statyczna.

14.4.2. Wtki demony


Aby zamieni zwyky wtek w demona, naley uy poniszej instrukcji:
t.setDaemon(true);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

754

Java. Podstawy
Nie ma w nim jednak nic demonicznego. Demon to taki wtek, ktrego istnienie polega na
sueniu innym wtkom. Nale do nich wtki zegarowe, ktre wysyaj w rwnych odstpach czasu tyknicia zegara do innych wtkw, lub takie, ktre usuwaj przestarzae obiekty
z pamici podrcznej. Jeli w programie pozostan same demony, maszyna wirtualna zostaje
zamknita, poniewa nie ma sensu kontynuowa dziaania programu, w ktrym nie ma nic
poza demonami.
Niektrzy pocztkujcy programici popeniaj bd uywania demonw w celu uniknicia
obsugi zamykania zasobw. Metoda ta moe by jednak niebezpieczna. Demon nie powinien
nigdy mie dostpu do zasobw staych, jak plik czy baza danych, poniewa moe zakoczy
dziaanie w kadej chwili, nawet w rodku operacji.
java.lang.Thread 1.0

void setDaemon(boolean isDaemon)

Okrela, czy wtek jest demonem, czy zwykym wtkiem. Wywoanie tej metody
musi nastpi przed uruchomieniem wtku.

14.4.3. Procedury obsugi nieprzechwyconych wyjtkw


Metoda run wtku nie moe zgasza wyjtkw kontrolowanych, ale jej dziaanie moe zakoczy wyjtek niekontrolowany. W takiej sytuacji wtek zostaje zamknity.
Nie ma jednak klauzuli catch, do ktrej wyjtek taki mona by byo przesa. Zamiast tego
bezporednio przed zamkniciem wtku wyjtek jest przekazywany do procedury obsugi nieprzechwyconych wyjtkw.
Obiekt ten musi nalee do klasy implementujcej interfejs Thread.UncaughExceptionHandler.
Interfejs ten posiada jedn metod:
void uncaughtException(Thread t, Throwable e)

Procedur obsugi w wtku mona zainstalowa za pomoc metody setUncaughtException


Handler. Mona take doda procedur domyln dla wszystkich wtkw za pomoc statycznej metody setDefaultUncaughtExceptionHandler z klasy Thread. Taka zapasowa procedura mogaby za porednictwem API rejestracyjnego wysya raporty o nieprzechwyconych
wyjtkach do dziennika.
Jeli domylna procedura obsugi nie zostanie zainstalowana, bdzie ona null. Jeli jednak
zabraknie procedury obsugi dla konkretnego wtku, bdzie ni jego obiekt typu ThreadGroup.
Grupa to kolekcja wtkw, ktrymi mona zarzdza razem. Domylnie wszystkie
tworzone wtki nale do tej samej grupy, ale mona zaoy inne zgrupowania.
Poniewa teraz s lepsze narzdzia do operowania kolekcjami wtkw, nie naley w programach uywa grup.

Klasa ThreadGroup implementuje interfejs Thread.UncaughtExceptionHandler. Znajdujca si


w nim metoda uncaughtException wykonuje nastpujce dziaania:

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.
1.

Wielowtkowo

755

Jeli grupa wtkw posiada rodzica, wywoywana jest metoda uncaughtException


grupy nadrzdnej.

2. W przeciwnym razie, jeli metoda Thread.getDefaultExceptionHandler zwraca


procedur obsugi niebdc null, metoda uncaughtException zostaje wywoana.
3. W przeciwnym razie, jeli Throwable jest egzemplarzem klasy ThreadDeath,

nic si nie dzieje.


4. W przeciwnym razie nazwa wtku i dane ze ledzenia stosu zostaj wydrukowane
w strumieniu System.err.

Dane ze ledzenia stosu z pewnoci kady widzia ju wiele razy w swoich programach.
java.lang.Thread 1.0

static void setDefaultUncaughtExceptionHandler(Thread.


UncaughtExceptionHandler handler) 5.0

static Thread.UncaughtExceptionHandler
getDefaultUncaughtExceptionHandler() 5.0

Ustawia lub zwraca domyln procedur obsugi dla nieprzechwyconych


wyjtkw.

void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler handler) 5.0

Thread.UncaughtExceptionHandler getUncaughtExceptionHandler() 5.0

Ustawia lub zwraca procedur obsugi nieprzechwyconych wyjtkw. Jeli nie


ma zainstalowanej procedury, jej funkcj peni obiekt grupy wtkw.
java.lang.Thread.UncaughtExceptionHandler 5.0

void uncaughtException(Thread t, Throwable e)

Zapisuje w dzienniku raport, gdy wtek zostanie zamknity z powodu


nieprzechwyconego wyjtku.
Parametry:

Wtek, ktry zosta zamknity z powodu


nieprzechwyconego wyjtku

Obiekt nieprzechwyconego wyjtku

java.lang.ThreadGroup 1.0

void uncaughtException(Thread t, Throwable e)

Wywouje metod nadrzdnej grupy wtkw, jeli taka istnieje, lub domyln
procedur obsugi klasy Thread, jeli istnieje domylna procedura, lub w przeciwnym
przypadku drukuje dane ze ledzenia stosu w standardowym strumieniu bdw
(jeli e jest obiektem typu ThreadDeath, dane ze ledzenia stosu s tumione;
obiekty ThreadDeath s generowane przez odradzan metod stop).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

756

Java. Podstawy

14.5. Synchronizacja
W wikszoci aplikacji wielowtkowych znajdujcych praktyczne zastosowanie jeden zestaw
danych jest wspdzielony przez co najmniej dwa wtki. Co si stanie, jeli dwa wtki majce
dostp do tego samego obiektu wywoaj metod zmieniajc jego stan? Jak pewnie si
domylasz, wtki mog sobie wzajemnie przeszkadza. Zalenie od kolejnoci dostpu do
danych, w opisanych wyej sytuacjach mog powstawa uszkodzone obiekty. Sytuacje te
nazywa si wycigami (ang. race condition).

14.5.1. Przykad sytuacji powodujcej wycig


Aby unikn uszkodzenia wspdzielonych przez wtki danych, trzeba umie synchronizowa
dostp do nich. W tym podrozdziale zobaczysz, co si dzieje, jeli zabraknie synchronizacji.
W kolejnym natomiast nauczysz si synchronizowa operacje dostpu do danych.
Kolejny przykadowy program jest symulacj banku, w ktrym zaoono kilka kont. Przeprowadzamy rne losowe transakcje majce na celu przemieszczenie pienidzy pomidzy tymi
kontami. Kade konto dysponuje wasnym wtkiem. Kada transakcja przelewa losowo okrelon ilo pienidzy z jednego konta na inne losowo wybrane konto.
Kod symulacji jest bardzo prosty. Zawiera klas Bank z metod transfer, ktra przelewa
rodki pienine pomidzy kontami (na razie nie przejmujemy si tym, e saldo na koncie
moe sta si ujemne). Oto kod metody transfer z klasy Bank:
public void transfer(int from, int to, double amount)
// Ostrzeenie: metoda niebezpieczna, jeli wywoywana w kilku wtkach.
{
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
}

Poniej znajduje si kod klasy TransferRunnable. Jej metoda run przelewa pienidze z okrelonego konta bankowego. W kadej iteracji metoda ta losowo wybiera jedno konto docelowe
i sum pienidzy do przelania, wywouje metod transfer na rzecz obiektu banku i przechodzi w stan upienia.
class TransferRunnable implements Runnable
{
. . .
public void run()
{
try
{
int toAccount = (int) (bank.size() * Math.random());
double amount = maxAmount * Math.random();
bank.transfer(fromAccount, toAccount, amount);
Thread.sleep((int) (DELAY * Math.random()));

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

757

}
catch(InterruptedException e) {}
}
}

W adnym momencie dziaania symulacji nie wiadomo, ile jest pienidzy na kadym z kont.
Wiadomo natomiast, e oglna suma nie powinna si zmienia, poniewa program tylko
przelewa rodki pomidzy rnymi kontami.
Na kocu kadej transakcji metoda transfer oblicza sum pienidzy dostpnych na wszystkich kontach i drukuje wynik.
Program ten nigdy si nie koczy. Aby go zamkn, naley nacisn kombinacj klawiszy
Ctrl+C.
Oto typowy wydruk z programu:
. . .
Thread[Thread-11,5,main] 588.48 z 11 na 44 Saldo oglne: 100000.00
Thread[Thread-12,5,main] 976.11 z 12 na 22 Saldo oglne: 100000.00
Thread[Thread-14,5,main] 521.51 z 14 na 22 Saldo oglne: 100000.00
Thread[Thread-13,5,main] 359.89 z 13 na 81 Saldo oglne: 100000.00
. . .
Thread[Thread-36,5,main] 401.71 z 36 na 73 Saldo oglne: 99291.06
Thread[Thread-35,5,main] 691.46 z 35 na 77 Saldo oglne: 99291.06
Thread[Thread-37,5,main] 78.64 z 37 na 3 Saldo oglne: 99291.06
Thread[Thread-34,5,main] 197.11 z 34 na 69 Saldo oglne: 99291.06
Thread[Thread-36,5,main] 85.96 z 36 na 4 Saldo oglne: 99291.06
. . .
Thread[Thread-4,5,main]Thread[Thread-33,5,main] 7.31 z 31 na 32 Saldo oglne:
99979.24
627.50 z 4 na 5 Saldo oglne: 99979.24
. . .

Jak wida, program zawiera powany bd. Przez kilka transakcji saldo czne wszystkich
rachunkw wynosi 100 000 dolarw, co jest prawidow kwot, zwaywszy, e jest 100 kont
po 1000 dolarw. Jednak po jakim czasie saldo ulega nieznacznej zmianie. Bdy w obliczeniach mog si pojawi na krtko po uruchomieniu programu lub dopiero po duszym czasie.
Taka sytuacja nie napawa optymizmem i z pewnoci nikt nie chciaby zoy w tym banku
swoich ciko zarobionych pienidzy.
Listingi od 14.5 do 14.7 przedstawiaj kompletny kod rdowy omawianego programu. Sprbuj odszuka bd w kodzie, a rozwizanie zagadki znajdziesz w kolejnym podrozdziale.
Listing 14.5. unsynch/UnsynchBankTest.java
package unsynch;
/**
* Program demonstrujcy zniszczenie danych spowodowane dostpem kilku wtkw do struktury
danych
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class UnsynchBankTest

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

758

Java. Podstawy
{
public static final int NACCOUNTS = 100;
public static final double INITIAL_BALANCE = 1000;
public static void main(String[] args)
{
Bank b = new Bank(NACCOUNTS, INITIAL_BALANCE);
int i;
for (i = 0; i < NACCOUNTS; i++)
{
TransferRunnable r = new TransferRunnable(b, i, INITIAL_BALANCE);
Thread t = new Thread(r);
t.start();
}
}
}

Listing 14.6. unsynch/Bank.java


package unsynch;
/**
* Bank z kilkoma kontami
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class Bank
{
private final double[] accounts;
/**
* Tworzy bank.
* @param n liczba kont
* @param initialBalance saldo pocztkowe na kadym koncie
*/
public Bank(int n, double initialBalance)
{
accounts = new double[n];
for (int i = 0; i < accounts.length; i++)
accounts[i] = initialBalance;
}
/**
* Przelewa pienidze pomidzy kontami.
* @param from konto, z ktrego ma nastpi przelew
* @param to konto, na ktre maj zosta przelane rodki
* @param amount kwota do przelania
*/
public void transfer(int from, int to, double amount)
{
if (accounts[from] < amount) return;
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

/**
* Zwraca sum sald wszystkich kont.
* @return saldo oglne
*/
public double getTotalBalance()
{
double sum = 0;
for (double a : accounts)
sum += a;
return sum;
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
public int size()
{
return accounts.length;
}
}

Listing 14.7. unsynch/TransferRunnable.java


package unsynch;
/**
* Obiekt Runnable przelewajcy pienidze z jednego konta bankowego na inne
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class TransferRunnable implements Runnable
{
private Bank bank;
private int fromAccount;
private double maxAmount;
private int DELAY = 10;
/**
* Tworzy obiekt Runnable do przelewania rodkw
* @param b bank, na ktrego kontach wykonywany jest przelew
* @param from konto, z ktrego maj by przelane pienidze
* @param max maksymalna suma, jaka moe zosta przelana za kadym razem
*/
public TransferRunnable(Bank b, int from, double max)
{
bank = b;
fromAccount = from;
maxAmount = max;
}
public void run()
{
try
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

759

760

Java. Podstawy
while (true)
{
int toAccount = (int) (bank.size() * Math.random());
double amount = maxAmount * Math.random();
bank.transfer(fromAccount, toAccount, amount);
Thread.sleep((int) (DELAY * Math.random()));
}
}
catch (InterruptedException e)
{
}
}
}

14.5.2. Wycigi
W poprzednim podrozdziale napisalimy program, w ktrym kilka wtkw aktualizowao
salda na kontach bankowych. Po jakim czasie wkrada si bd, ktry powodowa pojawienie si lub zniknicie pewnej kwoty pienidzy. Problem ten wystpowa w sytuacjach,
w ktrych dwa wtki rwnoczenie prboway zaktualizowa jedno konto. Wyobramy sobie,
e dwa wtki w tej samej chwili wykonuj ponisz instrukcj:
accounts[to] += amount;

Problem polega na tym, e nie s to operacje niepodzielne. Instrukcja ta moe zosta wykonana w nastpujcy sposb:
1.

Zaadowanie accounts[to] do rejestru.

2. Dodanie wartoci amount.


3. Zapisanie wyniku z powrotem w accounts[to].

Wyobramy sobie teraz, e pierwszy z wtkw wykonuje dwa pierwsze kroki i zostaje
wywaszczony. Nastpnie budzi si drugi wtek, ktry aktualizuje t sam pozycj w tablicy
accounts. Potem budzi si pierwszy wtek i koczy dziaanie, wykonujc krok trzeci.
Ta czynno wymazuje zmiany dokonane przez drugi wtek, w wyniku czego zmienia si
oglna suma (zobacz rysunek 14.4).
Nasz program testowy wykrywa ten bd (oczywicie istnieje niewielkie ryzyko faszywego
alarmu, ktry moe nastpi w sytuacji, gdy zostanie zakcona praca wtku przeprowadzajcego test).
Jakie jest ryzyko wystpienia tego bdu? Zwikszylimy je, przeplatajc instrukcje drukowania z instrukcjami aktualizujcymi saldo.
Jeli usuniemy instrukcje drukowania, ryzyko znacznie si zmniejszy, poniewa kady wtek
przed zaniciem bdzie wykonywa bardzo mao pracy, a poza tym jest mao prawdopodobne,
aby algorytm planujcy wywaszczy wtek w trakcie wykonywania oblicze. Nie znaczy to
jednak, e ryzyka wystpienia bdu nie ma ju w ogle. Jeli na powanie obcionej maszynie

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

761

Rysunek 14.4.
Jednoczesny
dostp
przez dwa wtki

Istnieje moliwo podejrzenia kodu bajtowego maszyny wirtualnej wykonujcego


kad z instrukcji klasy. W tym celu naley za pomoc poniszego polecenia zdekompilowa plik Bank.class:
javap -c -v Bank

Na przykad kod bajtowy odpowiadajcy instrukcji accounts[to] += amount jest nastpujcy:


aload_0
getfield #2;
iload_2
dup2
daload
dload_3
dadd
dastore

//Field accounts:[D

Niewane, co oznaczaj te wszystkie instrukcje. Problem polega na tym, e instrukcja


zwikszenia wartoci zostaa rozbita na kilka mniejszych instrukcji, a praca wykonujcego je wtku moe zosta przerwana w kadym momencie.

uruchomimy bardzo duo wtkw, program nadal bdzie robi bdy i nie pomoe usunicie
instrukcji drukujcych. Na wystpienie bdw moe przyj nam czeka kilka minut, godzin,
a nawet dni. Szczerze mwic, w yciu programisty jest niewiele gorszych rzeczy od bdu,
ktry daje o sobie zna tylko raz na kilka dni.
Istot problemu jest to, e dziaanie metody transfer moe zosta przerwane w rodku operacji.
Gdybymy zapewnili ukoczenie metody przed utrat przez wtek kontroli, stan obiektu konta
bankowego byby niezagroony przez bdy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

762

Java. Podstawy

14.5.3. Obiekty klasy Lock


Do dyspozycji programistw s dwa mechanizmy suce do ochrony blokw kodu przed jednoczesnym dostpem kilku wtkw. Suy do tego sowo kluczowe synchronized, a w Java SE 5.0
wprowadzono klas ReentrantLock. Sowo kluczowe synchronized automatycznie zakada
blokad oraz tworzy odpowiadajcy jej warunek, dziki czemu jest bardzo poytecznym
i wygodnym w uyciu narzdziem wykorzystywanym w wikszoci sytuacji, w ktrych
potrzebna jest jawna blokada. Wydaje nam si jednak, e dziaanie tego sowa kluczowego
atwiej zrozumie po zapoznaniu si z blokadami i warunkami osobno. Klasy implementujce
te podstawowe funkcje znajduj si w pakiecie java.util.concurrent. Opisujemy je poniej
i w podrozdziale 14.5.4, Warunki. Po zapoznaniu si z tymi podstawowymi elementami
przejdziemy do sekcji 14.5.5, Sowo kluczowe synchronized.
Szkielet konstrukcji chronicej blok kodu przy uyciu klasy ReentrantLock ma nastpujc
posta:
myLock.lock();
try
{
sekcja krytyczna
}
finally
{
myLock.unlock();

// Obiekt klasy ReentrantLock.

// Zapewnienie, e blokada zostanie zdjta, nawet jeli wystpi wyjtek.

Dostp do sekcji krytycznej powyszej instrukcji w jednym czasie moe mie tylko jeden
wtek. Kiedy jeden wtek zablokuje obiekt blokady, aden inny wtek nie bdzie mg przej
przez instrukcj lock. Jeli jaki inny wtek wywoa metod lock, zostanie dezaktywowany
do czasu, a poprzedni wtek odblokuje obiekt blokady.
Metoda unlock musi si bezwzgldnie znajdowa w bloku finally. Jeli kod w sekcji krytycznej spowoduje wyjtek, blokada musi zosta zdjta. W przeciwnym przypadku reszta wtkw pozostanie zablokowana na zawsze.

Z blokadami nie mona uywa instrukcji try z zasobami. Przede wszystkim metoda
zdejmujca blokad nie nazywa si close. Jednak nawet gdyby zmieniono jej nazw,
to instrukcja try z zasobami i tak by nie dziaaa. W jej nagwku powinna si znale
deklaracja nowej zmiennej, a w blokadzie uywa si jednej zmiennej, z ktrej korzystaj
rne wtki.

Sprbujmy za pomoc blokady ochroni metod transfer z klasy Bank.


public class Bank
{
private Lock bankLock = new ReentrantLock();

// Klasa ReentrantLock implementuje


// interfejs Lock.

. . .
public void transfer(int from, int to, int amount)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

763

{
bankLock.lock();
try
{
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
}
finally
{
bankLock.unlock();
}
}
}

Zamy, e jaki wtek wywouje metod transfer, ale zostaje wywaszczony przed jej
ukoczeniem. Nastpnie inny wtek rwnie wywouje t metod. Nie moe on jednak zaoy
blokady i zostaje zablokowany wywoaniem metody lock. Jest dezaktywowany i musi poczeka, a pierwszy wtek skoczy wykonywanie metody transfer. Kiedy ten zdejmie blokad,
drugi wtek moe kontynuowa (zobacz rysunek 14.5).
Rysunek 14.5.
Porwnanie wtkw
synchronizowanych
i niesynchronizowanych

Wyprbuj, czy to dziaa. Dodaj kod blokujcy do metody transfer i ponownie uruchom
program, a przekonasz si, e saldo bankowe nie zmieni si bez wzgldu na dugo czasu
dziaania programu.
Naley zauway, e kady obiekt klasy Bank posiada wasny obiekt klasy ReentrantLock.
Jeli dwa wtki prbuj uzyska dostp do tego samego obiektu Bank, blokada ustawia je
w kolejce. Jeli natomiast kady z wtkw dobiera si do innego obiektu Bank, zakadaj one
osobne blokady i aden z nich nie jest blokowany. Jest to jak najbardziej prawidowe dziaanie, poniewa wtki dziaajce na rnych obiektach nie mog sobie przeszkadza.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

764

Java. Podstawy
Blokada ta jest wielowejciowa (ang. reentrant), poniewa wtek moe wielokrotnie zakada
blokad, ktr ju posiada. Blokada posiada licznik pamitajcy liczb zagniedonych
wywoa metody lock. Dlatego, aby blokada zostaa zwolniona, wtek musi wywoa tyle razy
metod unlock, ile razy wywoa metod lock. Dziki temu kod chroniony przez blokad
moe wywoa inn metod, ktra wykorzystuje te same blokady.
Na przykad metoda transfer wywouje metod getTotalBalance, ktra blokuje obiekt
bankLock majcy obecnie licznik o wartoci 2. Kiedy metoda getTotalBalance koczy dziaanie, warto licznika spada do 1. Zakoczenie metody transfer zmniejsza go do 0 i wtek
zwalnia blokad.
Z reguy ochron obejmuje si bloki kodu, ktre aktualizuj lub badaj wspdzielone obiekty.
Daje to pewno, e operacja zostanie zakoczona, zanim inny wtek bdzie mg uy tego
samego obiektu.
Trzeba uwaa, aby procedury zawarte w sekcji krytycznej nie zostay pominite
z powodu wystpienia wyjtku. Jeli wyjtek wystpi przed kocem sekcji, klauzula
finally zwolni blokad, ale obiekt moe pozosta w naruszonym stanie.
java.util.concurrent.Locks.Lock 5.0

void lock()

Zakada blokad. Zostaje zablokowana, jeli blokada ta jest aktualnie w posiadaniu


innego wtku.

void unlock()

Zwalnia blokad.
java.util.concurrent.locks.ReentrantLock 5.0

ReentrantLock()

Tworzy wielowejciow blokad, za pomoc ktrej mona chroni sekcj krytyczn.

ReentrantLock(boolean fair)

Tworzy obiekt blokady z okrelon zasad uczciwoci. Uczciwa blokada ustawia


na pierwszym miejscu wtek, ktry czeka najduej. Jednak zasada ta moe
powodowa due straty szybkoci. Dlatego domylnie blokady nie musz
by uczciwe.
Opcja uczciwoci wydaje si lepszym rozwizaniem, ale uczciwe blokady s znacznie
wolniejsze od zwykych. Uczciwe blokady naley stosowa wycznie w sytuacjach,
w ktrych takie zachowanie jest cakowicie niezbdne. Stosujc uczciw blokad, nie ma
gwarancji, e algorytm odpowiedzialny za harmonogram uruchamiania wtkw rwnie
jest uczciwy. Jeli algorytm ten dyskryminuje wtek, ktry oczekiwa przez dugi czas na
blokad, blokada nie ma szansy potraktowa go lepiej.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

765

14.5.4. Warunki
Czsto zdarza si tak, e po wejciu do sekcji krytycznej wtek dowiaduje si, i nie moe
kontynuowa, dopki nie zostanie speniony warunek. Do zarzdzania wtkami, ktre uzyskay blokad, ale nie mog robi nic poytecznego, su obiekty warunkw. W tym rozdziale opisujemy implementacj warunkw w bibliotece Javy (ze wzgldu na przeszo
obiekty warunkw s czasami nazywane zmiennymi warunkowymi).
Ulepszymy nasz symulacj banku. Nie chcemy, aby pienidze byy przelewane z kont, na ktrych nie ma wystarczajcych rodkw. Zauwa, e nie moemy uy instrukcji jak poniej:
if (bank.getBalance(from) >= amount)
bank.transfer(from, to, amount);

Jest cakiem moliwe, e aktualny wtek zostanie dezaktywowany pomidzy pomylnym


wynikiem testu a wywoaniem metody transfer.
if (bank.getBalance(from) >= amount)
// W tym miejscu wtek moe by nieaktywny.
bank.transfer(from, to, amount);

Zanim wtek zostanie ponownie uruchomiony, saldo na koncie moe spa poniej minimalnej potrzebnej kwoty. Naley przypilnowa, aby aden wtek nie zmodyfikowa salda pomidzy testem a wykonaniem przelewu. Dlatego zarwno test, jak i operacj przelewu chronimy
przy uyciu blokady:
public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] < amount)
{
// czekanie
. . .
}
// przelew rodkw
. . .
}
finally
{
bankLock.unlock();
}
}

Kolej na podjcie decyzji, co zrobi, jeli na koncie bdzie za mao pienidzy. W takiej
sytuacji czekamy, a jaki inny wtek zwikszy jego saldo. Pamitamy jednak, e pierwszy
wtek cakowicie zablokowa dostp do obiektu bankLock, przez co aden inny wtek nie
moe dokona depozytu. W takim przypadku do gry wchodz obiekty warunkw.
Z obiektem blokady moe by zwizanych nawet kilka warunkw. Obiekty warunkw tworzy
si za pomoc metody newCondition. Istnieje zwyczaj nadawania obiektom warunkw takich

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

766

Java. Podstawy
nazw, ktre w jaki sposb przypominaj reprezentowane warunki. Na przykad w poniszym
fragmencie programu tworzymy obiekt warunku reprezentujcy warunek wystarczajcych
rodkw.
class Bank
{
private Condition wystSrodki;
. . .
public Bank()
{
. . .
wystSrodki = bankLock.newCondition();
}
}

Jeli metoda transfer odkryje, e na koncie nie ma dostpnych wystarczajcych rodkw,


wykonuje instrukcj WystSrodki.await();.
Dziki temu aktualny wtek zostaje dezaktywowany i nastpuje zdjcie blokady. To umoliwia dziaanie kolejnemu wtkowi, ktry, mamy nadziej, zwikszy saldo konta.
Pomidzy wtkiem, ktry oczekuje na blokad, a wtkiem, ktry wywoa metod await,
istnieje zasadnicza rnica. Ten drugi zostaje umieszczony w kolejce wtkw oczekujcych
(ang. wait set) warunku. Wtek ten nie przechodzi w stan wykonywalnoci, dopki inny
wtek nie wywoa metody signalAll na rzecz tego samego warunku.
Inny wtek przelewajcy pienidze powinien wykona instrukcj wystSrodki.signalAll();.
Powoduje ona reaktywacj wszystkich wtkw oczekujcych na warunek. Wtki usunite
z kolejki oczekujcych s z powrotem wykonywalne, a algorytm odpowiedzialny za harmonogram w kocu ponownie je uaktywni. Wtedy sprbuj one ponownie wej do obiektu. Jak
tylko bdzie dostpna blokada, jeden z wtkw j zaoy i bdzie kontynuowa prac od
momentu, w ktrym j przerwa, wracajc z wywoania metody await.
W tym momencie wtek powinien ponownie sprawdzi warunek. Nie ma gwarancji, e teraz
zostanie on speniony. Metoda signalAll tylko sygnalizuje wtkom, e tym razem warunek
moe zosta speniony i dlatego dobrze by byo to sprawdzi.
Oglnie rzecz biorc, metoda await powinna by wywoywana w ptli o nastpujcej formie:
while (!(mona kontynuowa))
condition.await();

Wane jest, aby metoda signalAll bya wywoywana take przez jaki inny wtek, poniewa
wtek wywoujcy metod await nie ma moliwoci reaktywowania samego siebie. Musi on
liczy na inne wtki. Jeli aden z nich go nie reaktywuje, nie zostanie on nigdy wicej uruchomiony. To moe prowadzi do nieprzyjemnych zakleszcze (ang. deadlock). Jeli prawie wszystkie wtki zostan zablokowane, a ostatni aktywny wtek wywoa metod await,
nie odblokowujc reszty, nie bdzie komu zdj blokady i program zawiesi si.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

767

Kiedy powinno si wywoywa metod signalAll? Gwna regua nakazuje zrobienie tego
zawsze wtedy, gdy stan obiektu zmieni si w taki sposb, ktry moe by korzystny dla wtkw oczekujcych. Na przykad wtki powinny mie moliwo sprawdzenia salda na koncie za kadym razem, gdy ulegnie ono zmianie. W naszym przykadowym programie metod
signalAll wywoujemy po zakoczeniu przelewu pienidzy.
public void transfer(int from, int to, int amount)
{
bankLock.lock();
try
{
while (accounts[from] < amount)
wystSrodki.await();
// przelew rodkw
. . .
wystSrodki.signalAll();
}
finally
{
bankLock.unlock();
}
}

Naley pamita, e wywoanie metody signalAll nie powoduje natychmiastowej aktywacji


oczekujcego wtku. Ona tylko odblokowuje oczekujce wtki, aby mogy konkurowa o wejcie do obiektu po wyjciu przez aktualny wtek z synchronizowanej metody.
Istnieje take metoda signal, ktra odblokowuje tylko jeden losowo wybrany wtek. Jest to
mniej obciajca czynno ni odblokowywanie wszystkich wtkw, ale wie si z ni
pewne ryzyko. Jeli losowo wybrany wtek dojdzie do wniosku, e nadal nie moe nic
zrobi, zostanie z powrotem zablokowany. Jeli aden inny wtek nie wywoa metody signal
jeszcze jeden raz, system ulegnie zakleszczeniu.
Wtek pozostajcy w posiadaniu blokady warunku moe na jego rzecz wywoa
tylko metod await, signalAll lub signal.

Po uruchomieniu programu przedstawionego na listingu 14.8 wida, e nie ma adnych bdw w obliczeniach. Saldo oglne cay czas wynosi 1000 dolarw. Saldo adnego z kont nigdy
nie jest ujemne (przypominamy, e aby zakoczy program, trzeba wcisn kombinacj klawiszy Ctrl+C). Da si rwnie zauway, e program dziaa nieco wolniej jest to cena, jak
pacimy za synchronizacj.
Listing 14.8. synch/Bank.java
package synch;
import java.util.concurrent.locks.*;
/**
* Bank z kilkoma kontami, kontrolujcy dostp za pomoc blokad
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

768

Java. Podstawy
public class Bank
{
private final double[] accounts;
private Lock bankLock;
private Condition sufficientFunds;
/**
* Tworzy bank
* @param n liczba kont
* @param initialBalance saldo pocztkowe na kadym koncie
*/
public Bank(int n, double initialBalance)
{
accounts = new double[n];
for (int i = 0; i < accounts.length; i++)
accounts[i] = initialBalance;
bankLock = new ReentrantLock();
sufficientFunds = bankLock.newCondition();
}
/**
* Przelewa pienidze pomidzy kontami.
* @param from konto, z ktrego ma nastpi przelew
* @param to konto, na ktre maj zosta przelane rodki
* @param amount kwota do przelania
*/
public void transfer(int from, int to, double amount) throws InterruptedException
{
bankLock.lock();
try
{
while (accounts[from] < amount)
sufficientFunds.await();
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
sufficientFunds.signalAll();
}
finally
{
bankLock.unlock();
}
}
/**
* Zwraca sum sald wszystkich kont.
* @return saldo oglne
*/
public double getTotalBalance()
{
bankLock.lock();
try
{
double sum = 0;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

769

for (double a : accounts)


sum += a;
return sum;
}
finally
{
bankLock.unlock();
}
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
public int size()
{
return accounts.length;
}
}

Poprawne zastosowanie warunkw w praktyce moe by sporym wyzwaniem. Przed podjciem prby zaimplementowania wasnych obiektw warunkw dobrze by byo najpierw wzi
pod uwag jedn z konstrukcji opisanych w podrozdziale 14.10, Synchronizatory.
java.util.concurrent.locks.Lock 5.0

Condition newCondition()

Zwraca obiekt warunku zwizany z blokad.


java.util.concurrent.locks.Condition 5.0

void await()

Umieszcza wtek w kolejce oczekujcych do warunku.

void signalAll()

Odblokowuje wszystkie wtki znajdujce si w kolejce oczekujcych do warunku.

void signal()

Odblokowuje losowo wybrany wtek znajdujcy si w kolejce oczekujcych


do warunku.

14.5.5. Sowo kluczowe synchronized


W poprzednich podrozdziaach nauczylimy si uywa obiektw typu Lock i Condition.
Zanim przejdziemy dalej, zrobimy podsumowanie najwaniejszych wiadomoci na temat
blokad i warunkw:

Blokada chroni blok kodu, pozwalajc wykonywa go tylko jednemu wtkowi


w danym czasie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

770

Java. Podstawy

Blokada zarzdza wtkami, ktre prbuj wej do chronionego segmentu kodu.

Z blokad moe by zwizany jeden lub wicej obiektw warunkowych.

Kady obiekt warunkowy zarzdza wtkami, ktre weszy do sekcji kodu


chronionego, ale ktre nie mog kontynuowa dziaania.

Interfejsy Lock i Condition umoliwiaj programistom zyskanie wikszej kontroli nad blokadami. W wikszoci sytuacji kontrola ta jest jednak zbdna, poniewa mona wykorzysta
mechanizm wbudowany w jzyk. Od wersji 1.0 kady obiekt w Javie posiada blokad
wewntrzn. Jeli w deklaracji metody zostanie uyte sowo kluczowe synchronized, blokada
obiektu chroni ca t metod. To znaczy, e aby j wywoa, wtek musi zaoy wewntrzn
blokad obiektu.
Innymi sowy, poniszy kod:
public synchronized void method()
{
ciao metody
}

jest rwnowany z tym:


public void method()
{
this.intrinsicLock.lock();
try
{
ciao metody
}
finally { this.intrinsicLock.unlock(); }
}

Na przykad zamiast stosowa blokad jawn, wystarczy zadeklarowa metod transfer


z klasy Bank jako synchronizowan (synchronized).
Z wewntrzn blokad obiektu jest zwizany jeden warunek. Metoda wait dodaje wtek do
kolejki oczekujcych, a metody notifyAll i notify odblokowuj oczekujce wtki. Innymi
sowy, wywoanie metody wait lub notifyAll jest rwnoznaczne z poniszym:
intrinsicCondition.await();
intrinsicCondition.signalAll();

Metody wait, notifyAll i notify s metodami finalnymi klasy Object. Aby unikn
konfliktw nazw, odpowiadajce im metody w interfejsie Condition zostay nazwane
await, signalAll i signal.

Na przykad implementacja klasy Bank moe wyglda nastpujco:


class Bank
{
private double[] accounts;
public synchronized void transfer(int from, int to, int amount) throws
InterruptedException

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

771

{
while (accounts[from] < amount)
wait();
// Oczekiwanie na warunek wewntrznej blokady obiektu.
accounts[from] -= amount;
accounts[to] += amount;
notifyAll();
// Powiadomienie wszystkich wtkw oczekujcych na warunek.
}
public synchronized double getTotalBalance() { . . . }
}

Jak wida, sowo kluczowe synchronized pozwala na pisanie znacznie bardziej zwizego
kodu. Oczywicie, aby go zrozumie, trzeba wiedzie, e kady obiekt posiada wewntrzn
blokad, ktra z kolei posiada wewntrzny warunek. Blokada zarzdza wtkami, ktre prbuj wej do metody synchronizowanej. Warunek zajmuje si wtkami, ktre wywoay
metod wait.
Metody synchronizowane s wzgldnie proste. Jednak pocztkujcy czsto szarpi
si z warunkami. Przed przejciem do uywania metod wait i notifyAll lepiej
zastanowi si nad uyciem jednej z konstrukcji opisanych w podrozdziale 14.10,
Synchronizatory.

Synchronizowane mog by take metody statyczne. Jeli taka metoda zostanie wywoana,
uzyskuje dostp do blokady wewntrznej obiektu zwizanej z ni klasy. Jeli na przykad
klasa Bank zawieraaby statyczn metod synchronizowan, blokada obiektu Bank.class byaby
blokowana w chwili wywoania tej metody. W wyniku tego aden inny wtek nie mgby
wywoa tej ani adnej innej statycznej metody synchronizowanej tej klasy.
Wewntrzne blokady i warunki maj pewne ograniczenia. Oto niektre z nich:

Nie mona przerwa wtku, ktry prbuje zaoy blokad.

Nie mona okreli maksymalnego czasu prby dostpu do blokady.

Sytuacja, w ktrej na jedn blokad przypada jeden warunek, moe nie by


najlepsza pod ktem wydajnoci.

Czego najlepiej uywa obiektw Lock i Condition czy metod synchronizowanych? Oto
nasze zalecenia w tej kwestii:

Najlepiej nie uywa interfejsw Lock i Condition ani sowa kluczowego synchronized.
W wielu sytuacjach mona poradzi sobie przy uyciu jednego z mechanizmw
z pakietu java.util.concurrent, ktre zajmuj si dziaaniami zwizanymi
z blokowaniem. Na przykad w podrozdziale 14.6, Kolejki blokujce, opisujemy
sposb synchronizacji wtkw pracujcych nad wsplnym zadaniem za pomoc
blokowania kolejek.

Jeli sowo kluczowe synchronized sprawdza si w okrelonej sytuacji, naley


go uy. Technika ta pozwala na zmniejszenie liczby wierszy kodu i pozostawia
mniej okazji do popenienia bdu. Listing 14.9 przedstawia program symulujcy
bank zaimplementowany przy uyciu metod synchronizowanych.

Interfejsw Lock i Condition naley uywa, gdy nie mona si oby bez dodatkowych
funkcji, ktre one udostpniaj.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

772

Java. Podstawy

Listing 14.9. synch2/Bank.java


package synch2;
/**
* Bank z kilkoma kontami, wykorzystujcy synchronizacj
* @version 1.30 2004-08-01
* @author Cay Horstmann
*/
public class Bank
{
private final double[] accounts;
/**
* Tworzy bank.
* @param n liczba kont
* @param initialBalance saldo pocztkowe na kadym koncie
*/
public Bank(int n, double initialBalance)
{
accounts = new double[n];
for (int i = 0; i < accounts.length; i++)
accounts[i] = initialBalance;
}
/**
* Przelewa pienidze pomidzy kontami.
* @param from konto, z ktrego ma nastpi przelew
* @param to konto, na ktre maj zosta przelane rodki
* @param amount kwota do przelania
*/
public synchronized void transfer(int from, int to, double amount) throws
InterruptedException
{
while (accounts[from] < amount)
wait();
System.out.print(Thread.currentThread());
accounts[from] -= amount;
System.out.printf(" %10.2f z %d na %d", amount, from, to);
accounts[to] += amount;
System.out.printf(" Saldo oglne: %10.2f%n", getTotalBalance());
notifyAll();
}
/**
* Zwraca sum sald wszystkich kont.
* @return saldo oglne
*/
public synchronized double getTotalBalance()
{
double sum = 0;
for (double a : accounts)
sum += a;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

return sum;
}
/**
* Zwraca liczb kont w banku.
* @return liczba kont
*/
public int size()
{
return accounts.length;
}
}
java.lang.Object 1.0

void notifyAll()

Odblokowuje wszystkie wtki, ktre wywoay metod wait na rzecz obiektu.


Metod t mona wywoywa wycznie w synchronizowanej metodzie
lub synchronizowanym bloku. Powoduje wyjtek IllegalMonitorStateException,
jeli aktualny wtek nie jest wacicielem blokady obiektu.

void notify()

Odblokowuje jeden losowo wybrany wtek spord tych, ktre wywoay metod
wait na rzecz obiektu. Metod t mona wywoywa wycznie
w synchronizowanej metodzie lub synchronizowanym bloku. Powoduje wyjtek
IllegalMonitorStateException, jeli aktualny wtek nie jest wacicielem blokady
obiektu.

void wait()

Przestawia wtek w stan oczekiwania, a nadejdzie odpowiednie powiadomienie.


Metod t mona wywoywa wycznie w synchronizowanej metodzie
lub synchronizowanym bloku. Powoduje wyjtek IllegalMonitorStateException,
jeli aktualny wtek nie jest wacicielem blokady obiektu.

void wait(long millis)

void wait(long millis, int nanos)

Przestawia wtek w stan oczekiwania, a nadejdzie odpowiednie powiadomienie


lub upynie okrelona ilo czasu. Metod t mona wywoywa wycznie
w synchronizowanej metodzie lub synchronizowanym bloku. Powoduje wyjtek
IllegalMonitorStateException, jeli aktualny wtek nie jest wacicielem blokady
obiektu.
Parametry:

millis

Liczba milisekund

nanos

Liczba nanosekund; musi by mniejsza od 1 000 000

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

773

774

Java. Podstawy

14.5.6. Bloki synchronizowane


Jak ju wiemy, kady obiekt w Javie posiada blokad. Wtek moe j przej za pomoc
metody synchronizowanej. Istnieje jeszcze jeden sposb na pozyskiwanie blokad, ktry polega
na wejciu do bloku synchronizowanego. Wtek, ktre wejdzie do bloku podobnego do tego
poniej, stanie si wacicielem blokady dla obiektu obj.
synchronized (obj)
{
sekcja krytyczna
}

// skadnia bloku synchronizowanego

Czasami mona spotka blokady tworzone ad hoc, na przykad:


public class Bank
{
private double[] accounts;
private Object lock = new Object();
. . .
public void transfer(int from, int to, int amount)
{
synchronized (lock)
// blokada utworzona ad hoc
{
accounts[from] -= amount;
accounts[to] += amount;
}
System.out.println(. . .);
}
}

W tym przypadku obiekt lock zosta utworzony tylko po to, aby mona byo uy blokady,
ktr posiada kady obiekt w Javie.
Czasami programici wykorzystuj blokady obiektw do implementacji dodatkowych niepodzielnych operacji. Technika ta nazywa si blokowaniem po stronie klienta (ang. client-side
locking). Wemy na przykad klas Vector implementujc listy, ktrych metody s synchronizowane. Wyobramy sobie, e salda kont w naszym banku zapisalimy w licie
Vector<Double>. Oto naiwna implementacja metody transfer:
public void transfer(Vector<Double> accounts, int from, int to, int amount)
{
accounts.set(from, accounts.get(from) - amount);
accounts.set(to, accounts.get(to) + amount);
System.out.println(. . .);
}

// bd

Mimo e metody get i set klasy Vector s synchronizowane, nic nam to nie daje. Istnieje moliwo, e wtek zostanie wywaszczony w metodzie transfer po zakoczeniu pierwszego
wywoania metody get. Wtedy inny wtek moe w tym samym miejscu zapisa cakiem inn
warto. Mona jednak przej blokad:
public void transfer(Vector<Double> accounts, int from, int to, int amount)
{
synchronized (accounts)
{
accounts.set(from, accounts.get(from) - amount);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

775

accounts.set(to, accounts.get(to) + amount);


}
System.out.println(. . .);

Technika ta zdaje egzamin, ale jest w peni uzaleniona od tego, e wszystkie metody modyfikujce w klasie Vector maj wewntrzne blokady. Czy tak jest jednak naprawd? W dokumentacji tej klasy nic takiego nie napisano. Trzeba bardzo uwanie przestudiowa jej kod
rdowy i mie nadziej, e w przyszoci nie zostan wprowadzone mutatory niesynchronizowane. Stanowi to dowd na to, e blokowanie po stronie klienta jest bardzo niepewn technik, i dlatego oglnie nie polecamy jej stosowania.

14.5.7. Monitor
Blokady i warunki stwarzaj bardzo due moliwoci, jeli chodzi o synchronizacj wtkw,
ale s mao obiektowe. Badacze przez wiele lat poszukiwali sposobw na uczynienie wielowtkowoci bezpieczn technik, nie zmuszajc jednoczenie programistw do zajmowania si jawnymi blokadami. Jednym z najlepszych rozwiza w tej dziedzinie s monitory,
opracowane w latach 70. ubiegego wieku przez Pera Brincha Hansena i Tonyego Hoarea.
W Javie monitor ma nastpujce wasnoci:

Monitorem jest klasa posiadajca same pola prywatne.

Z kadym obiektem tej klasy zwizana jest blokada.

Wszystkie metody s blokowane przez t blokad. Innymi sowy, jeli klient wykona
instrukcj obj.method(), to blokada obiektu obj zostanie automatycznie zaoona
na pocztku wywoania metody i zwolniona w chwili zwrcenia przez t metod
wartoci. Poniewa wszystkie pola s prywatne, mamy pewno, e podczas gdy
jeden wtek wykonuje dziaania na nich, aden inny wtek nie ma do nich dostpu.

Z blokad moe by skojarzona dowolna liczba warunkw.

Poprzednie wersje monitorw dysponoway tylko jednym warunkiem o dosy eleganckiej


skadni. Mona byo uy wywoania typu await accounts[from] >= balance bez stosowania
jawnej zmiennej warunkowej. Badania wykazay jednak, e masowe powtarzanie testw
warunkw moe by bardzo mao wydajne. Problem ten rozwizuj jawne zmienne warunkowe, z ktrych kada zarzdza osobnym zestawem wtkw.
Projektanci Javy luno potraktowali adaptacj koncepcji monitorw. Kady obiekt w Javie
posiada wewntrzn blokad i wewntrzny warunek. Jeli w deklaracji metody znajduje si
sowo kluczowe synchronized, dziaa ona jak metoda monitorowa. Dostp do zmiennej warunkowej uzyskuje si za porednictwem metod wait, notifyAll i notify.
Pomidzy zwykym obiektem a monitorem s trzy istotne rnice majce wpyw na bezpieczestwo wtkw:

Pola nie musz by prywatne.

Metody nie musz by synchronizowane.

Blokada wewntrzna jest dostpna dla klientw.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

776

Java. Podstawy
Ten brak poszanowania dla zabezpiecze rozwcieczy Pera Brincha Hansena. W zjadliwej
recenzji na temat wielowtkowoci w Javie napisa: Zdumiewa fakt, e pozbawiony zabezpiecze paralelizm w Javie jest powanie traktowany przez programistw, zwaszcza e
od wynalezienia monitorw i powstania jzyka Concurrent Pascal upyno ju p wieku.
To nie ma sensu (Javas Insecure Parallelism, ACM SIGPLAN Notices 1999, nr 34,
s. 38 45).

14.5.8. Pola ulotne


Czasami wydaje si, e obcienie powodowane przez synchronizacj jest zbyt due, jeli
chcemy tylko odczyta lub zapisa jedno czy dwa pola. Co w takiej sytuacji moe si nie
uda? Niestety nowoczesne procesory i kompilatory stwarzaj mnstwo sytuacji, w ktrych
moe zosta popeniony bd.

Komputery wieloprocesorowe mog tymczasowo przechowywa wartoci


w rejestrach lub lokalnych pamiciach podrcznych. W wyniku tego wtki
dziaajce na rnych procesorach mog w tej samej lokalizacji widzie inne
dane!

Kompilatory mog zmienia kolejno instrukcji w celu zoptymalizowania


programu. Kompilator nie zmieni kolejnoci w taki sposb, aby zmieni si sposb
dziaania kodu, ale wyjdzie z zaoenia, e wartoci w pamici ulegaj zmianom
tylko w wyniku konkretnych instrukcji w kodzie. Jednak warto moe zosta
zmodyfikowana przez inny wtek!

Problemy te nie wystpuj po zastosowaniu blokad do ochrony kodu, do ktrego mog mie
dostp rne wtki. Kompilatory musz respektowa blokady poprzez oprnianie w razie
potrzeby lokalnych pamici podrcznych i nieprzestawianie instrukcji w nieodpowiedni sposb.
Szczegowe informacje na ten temat mona znale w dokumencie Java Memory Model
and Thread Specification opracowanym przez grup JSR 133 (http://www.jcp.org/en/jsr/
detail?id=133). Znaczna cz tej specyfikacji jest bardzo skomplikowana i ma czysto techniczny charakter, ale jest kilka janiejszych fragmentw. Bardziej przystpny artyku na ten
temat napisany przez Briana Goetza znajduje si pod adresem http://www-106.ibm.com/
developerworks/java/library/j-jtp02244.html.
Brian Goetz jest autorem motta synchronizacyjnego: jeli zapisujesz zmienn,
ktra moe nastpnie zosta odczytana przez inny wtek, albo odczytujesz zmienn,
ktra moga zosta zapisana przez inny wtek, musisz skorzysta z synchronizacji.

Sowo kluczowe volatile (ulotny) umoliwia synchronizacj dostpu do pl egzemplarza bez


uycia blokad. Jeli w deklaracji pola znajduje si to sowo kluczowe, kompilator i maszyna
wirtualna wiedz, e moe ono by wspbienie modyfikowane przez inny wtek.
Wyobramy sobie na przykad, e pewien obiekt ma znacznik logiczny done, ktry jest ustawiany przez jeden wtek, a sprawdzany przez inny. Zgodnie z wczeniejszymi informacjami
wiemy, e mona w tej sytuacji uy blokady:
private boolean done;

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

777

public synchronized boolean isDone() { return done; }


public synchronized void setDone() { done = true; }

Uycie wewntrznej blokady wydaje si niezbyt dobrym pomysem. Metody isDone i setDone
mog zosta zablokowane, jeli inny wtek zablokuje obiekt. Jeli istnieje taka obawa, mona
zastosowa osobn blokad tylko dla tej zmiennej. To jednak zaczyna robi si coraz bardziej kopotliwe.
Rozsdnym wyjciem z tej sytuacji jest zadeklarowanie pola jako volatile:
private volatile boolean done;
public boolean isDone() { return done; }
public void setDone() { done = true; }

Zmienne ulotne nie zapewniaj niepodzielnoci. Na przykad nie ma gwarancji,


e ponisza metoda zmieni warto pola na przeciwn.
public void flipDone() { done = !done; }

// podzielna

14.5.9. Zmienne finalne


Jak wiesz z poprzedniego podrozdziau, nie mona bezpiecznie odczyta pola w wielu wtkach, jeli nie uyje si blokad lub modyfikatora volatile.
Jest jeszcze jedna sytuacja, w ktrej mona bezpiecznie uzyska dostp do wsplnego pola
gdy pole to jest finalne:
final Map<String, Double> accounts = new HashMap<>();

Inne wtki zobacz zmienn accounts, gdy konstruktor zakoczy dziaanie.


Bez modyfikatora final nie byoby gwarancji, e wtki zobacz zaktualizowan warto
zmiennej accounts mogyby widzie null zamiast utworzonej struktury HashMap.
Oczywicie dziaania na mapie nie s bezpieczne wtkowo. Jeli ma j modyfikowa i odczytywa kilka wtkw, to nadal konieczna jest synchronizacja.

14.5.10. Zmienne atomowe


Wsplne zmienne mona deklarowa jako ulotne, pod warunkiem e jedyn wykonywan
operacj bdzie przypisanie.
W pakiecie java.util.concurrent.atomic znajduje si kilka klas, ktre zapewniaj atomowo
innych operacji dziki wykorzystaniu wydajnych instrukcji na poziomie maszynowym. Przykadowo klasa AtomicInteger zawiera metody incrementAndGet i decrementAndGet, ktre
automatycznie inkrementuj i dekrementuj liczby cakowite. Mona bezpiecznie uywa
obiektw klasy AtomicInteger jako wsplnych licznikw bez stosowania synchronizacji.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

778

Java. Podstawy
Dostpne s te klasy AtomicBoolean, AtomicLong i AtomicReference oraz atomowe tablice
wartoci logicznych, liczb cakowitych, wartoci dugich i referencji. Ich uycie naley pozostawi programistom systemw, ktrzy programuj narzdzia wspbienoci. Programici
aplikacji nie powinni ich uywa.

14.5.11. Zakleszczenia
Blokady i warunki nie wystarcz do rozwizania wszystkich problemw zwizanych z wielowtkowoci. Rozwamy nastpujc sytuacj:
Konto 1: 200 dol.
Konto 2: 300 dol.
Wtek 1: przelew 300 dol. z konta 1 na konto 2
Wtek 2: przelew 400 dol. z konta 2 na konto 1
Jak wida na rysunku 14.6, wtki 1 i 2 s zablokowane. aden z nich nie moe kontynuowa
dziaania, poniewa salda na obu kontach s zbyt niskie.
Rysunek 14.6.
Zakleszczenie

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

779

Sytuacja, w ktrej wszystkie wtki s zablokowane, w tym przypadku (poniewa kady


czeka na wicej pienidzy) nazywa si zakleszczeniem (ang. deadlock).
W naszym programie zakleszczenie nie moe wystpi z prostego powodu. Kwota przelewu
nie moe przekracza 1000 dolarw. Poniewa jest sto kont, na ktrych w sumie znajduje si
100 000 dolarw, przynajmniej jedno z nich zawsze musi zawiera wicej ni 1000 dolarw. Dziki temu wtek przelewajcy pienidze z tego konta moe kontynuowa dziaanie.
Jeli jednak z metody run usuniemy ograniczenie wysokoci transakcji do 1000 dolarw,
zakleszczenia mog nastpi bardzo szybko. Mona to sprawdzi. Ustaw warto staej
NACCOUNTS na 10, a parametr max konstruktora klasy TransferRunnable na 2 * INITIAL_
BALANCE i uruchom program. Podziaa on przez jaki czas i zawiesi si.
Kiedy program zawiesi si, nacinij kombinacj klawiszy Ctrl+\, aby otrzyma list
wszystkich wtkw. Kademu wtkowi towarzyszy informacja ze stosu, dziki czemu
wiadomo, w ktrym miejscu jest on aktualnie zablokowany. Mona te uy narzdzia
jconsole, ktre zostao opisane w rozdziale 11., i sprawdzi dane na karcie Threads
(rysunek 14.7).
Rysunek 14.7.
Karta Threads
w jconsole

Innym sposobem na spowodowanie zakleszczenia jest uczynienie i-tego wtku odpowiedzialnym za umieszczenie pienidzy na i-tym koncie zamiast za pobranie ich z i-tego konta. Istnieje
wtedy szansa, e wszystkie wtki zbiegn si nad jednym kontem i bd prbowa usun
z niego wicej pienidzy, ni si na nim znajduje. Aby to wyprbowa, naley w programie

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

780

Java. Podstawy
SynchBankTest znale metod run w klasie TransferRunnable. W wywoaniu metody transfer
zamie miejscami parametry fromAccount i toAccount. Uruchom program i przekonaj si, e

prawie natychmiast nastpi zakleszczenie.


Oto jeszcze jedna sytuacja, w ktrej moe atwo doj do zakleszczenia: w programie Synch
BankTest zamie metod signalAll na signal. Po pewnym czasie program w kocu zawiesza
si (tym razem take lepiej ustawi warto NACCOUNTS na 10, aby efekt by szybciej zauwaalny). W przeciwiestwie do metody signalAll, ktra wysya powiadomienie do wszystkich
wtkw oczekujcych na zwikszenie funduszy, metoda signal odblokowuje tylko jeden
wtek. Jeli wtek ten nie moe kontynuowa, wszystkie wtki mog zosta zablokowane.
Przeanalizujmy scenariusz prezentujcy, jak moe doj do zakleszczenia.
Konto 1: 1990 dol.
Pozostae konta: po 990 dol.
Wtek 1: przelew 995 dol. z konta 1 na konto 2
Pozostae wtki: przelew 995 dol. ze swoich kont na inne konto
Bez wtpienia wszystkie wtki poza pierwszym s zablokowane, poniewa na ich kontach nie
ma wystarczajco duo pienidzy.
Wtek 1 kontynuuje dziaanie. Dochodzimy do nastpujcej sytuacji:
Konto 1: 995 dol.
Konto 2: 1985 dol.
Pozostae konta: po 990 dol.
Wtedy wtek 1 wywouje metod signal, ktra odblokowuje jeden losowo wybrany wtek.
Zamy, e pado na wtek 3. Zostaje on obudzony, stwierdza, e na jego koncie nie ma
wystarczajco duo rodkw, i ponownie wywouje metod wait. Jednak wtek 1 cay czas
dziaa. Generuje now losow transakcj, na przykad tak jak poniej:
Wtek 1: przelew 997 dol. z konta 1 na konto 2
Tym razem take wtek 1 wywouje metod wait i wszystkie wtki s zablokowane. System
ulega zakleszczeniu.
rdem problemw jest tutaj metoda signal odblokowujca tylko jeden wtek, ktry moe
nie mie znaczenia dla utrzymania postpu programu (w naszym scenariuszu wtek 2 musi
kontynuowa prac, aby pobra pienidze z drugiego konta).
Niestety jzyk Java nie udostpnia adnego mechanizmu pozwalajcego unikn takich
zakleszcze lub je przerwa. Program musi by tak zaprojektowany, aby sytuacje tego typu
nie miay szans si wydarzy.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

781

14.5.12. Zmienne lokalne wtkw


W poprzednich podrozdziaach zostay opisane problemy, jakie mona napotka, uywajc
w wtkach wsplnych zmiennych. Czasami mona unikn wspdzielenia, kademu wtkowi
dajc egzemplarz na wasno. Uywa si do tego celu klasy pomocniczej ThreadLocal. Przykadowo klasa SimpleDateFormat nie jest bezpieczna wtkowo. Przypumy, e mamy statyczn zmienn:
public static final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");

Jeli dwa wtki wykonaj tak operacj jak ponisza:


String dateStamp = dateFormat.format(new Date());

to wynik moe by niepoprawny, poniewa wewntrzne struktury danych uywane przez


dateFormat mog zosta uszkodzone przez wspbieny dostp. Jako rozwizanie mona
zastosowa synchronizacj, ktra jest kosztowna, albo tworzy lokalny obiekt SimpleDate
Format za kadym razem, gdy jest potrzebny. To jednak rwnie oznacza marnotrawstwo
zasobw.
Aby utworzy po jednym egzemplarzu na wtek, naley uy poniszego kodu:
public static final ThreadLocal<SimpleDateFormat> dateFormat =
new ThreadLocal<SimpleDateFormat>()
{
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyy-MM-dd");
}
};

Aby uzyska dostp do formatera, naley zastosowa ponisze wywoanie:


String dateStamp = dateFormat.get().format(new Date());

Przy pierwszym wywoaniu metody get w wtku nastpuje wywoanie metody initialValue.
Od tej pory metoda get zwraca egzemplarz nalecy do biecego wtku.
Podobny problem przedstawia generowanie liczb losowych w wielu wtkach. Klasa java.
util.Random jest bezpieczna wtkowo, ale mimo to nie dziaa wydajnie, gdy wiele wtkw
musi czeka na jeden wsplny generator.
Mona by byo uy klasy pomocniczej ThreadLocal, aby kademu wtkowi da osobny
generator, ale w Java SE 7 udostpniono specjaln klas, dziki ktrej praca ta jest wygodniejsza. Wystarczy wykona ponisze wywoanie:
int random = ThreadLocalRandom.current().nextInt(upperBound);

Metoda ThreadLocalRandom.current() zwraca egzemplarz klasy Random dostpny tylko dla


biecego wtku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

782

Java. Podstawy
java.lang.ThreadLocal<T> 1.2

T get()

Pobiera biec warto wtku. Jeli metoda get jest wywoywana po raz pierwszy,
warto otrzymywana jest poprzez wywoanie metody initialize.

protected initialize()

Metod t naley przesoni, aby dostarczaa warto domyln. Domylnie


zwraca null.

void set(T t)

Ustawia now warto wtku.

void remove()

Usuwa warto wtku.


java.util.concurrent.ThreadLocalRandom 7

static ThreadLocalRandom current()

Zwraca egzemplarz klasy Random nalecy do biecego wtku.

14.5.13. Testowanie blokad i odmierzanie czasu


Jeli wtek wywoa metod lock w celu uzyskania blokady bdcej w posiadaniu innego
wtku, zostaje zablokowany na nieokrelon ilo czasu. Przy zakadaniu blokady mona
zachowa wiksz ostrono. Metoda tryLock prbuje zaoy blokad i jeli operacja ta
zakoczy si powodzeniem, zwraca warto true. W przeciwnym przypadku zwraca warto
false, a wtek moe wykonywa jakie inne dziaania.
if (myLock.tryLock())
// Wtek jest w posiadaniu blokady.
try { . . . }
finally { myLock.unlock(); }
else
// Wtek przechodzi do innych dziaa.

Metod tryLock mona wywoa z parametrem czasowym:


if (myLock.tryLock(100, TimeUnit.MILLISECONDS)) . . .

TimeUnit to wyliczenie zawierajce wartoci SECONDS, MILLISECONDS, MICROSECONDS i NANO


SECONDS.

Metody lock nie mona przerwa. Jeli wtek oczekujcy na blokad zostanie przerwany,
pozostaje on zablokowany, dopki metoda lock nie bdzie dostpna. Jeli wystpi zakleszczenie, metoda lock moe nigdy nie zakoczy dziaania.
Jeli natomiast metoda tryLock zostanie wywoana z parametrem czasowym, przerwanie oczekujcego wtku spowoduje wygenerowanie wyjtku InterruptedException. Jest to niewtpliwie poyteczna funkcja, poniewa pozwala przerywa zakleszczenia.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

783

Mona take wywoa metod lockInterruptibly. Dziaa ona tak samo jak tryLock, tylko
bez ograniczenia czasowego.
Ograniczy czasowo mona take oczekiwanie na warunek:
myCondition.await(100, TimeUnit.MILLISECONDS))

Metoda await zwraca kontrol, jeli inny wtek aktywuje ten wtek za pomoc metody
signalAll lub signal, jeli minie okrelony czas bd gdy nastpi przerwanie tego wtku.
Jeli oczekujcy wtek zostanie przerwany, metoda await zgasza wyjtek InterruptedEx
ception. W (mao prawdopodobnej) sytuacji, w ktrej lepiej kontynuowa czekanie, naley
w zamian uy metody awaitInterruptibly.
java.util.concurrent.locks.Lock 5.0

boolean tryLock()

Prbuje zaoy blokad, nie blokujc wtku. Jeli operacja zakoczy


si powodzeniem, zwraca warto true. Metoda ta przejmuje blokad,
jeli jest ona dostpna, bez wzgldu na zasad uczciwoci i inne oczekujce wtki.

boolean tryLock(long time, TimeUnit unit)

Prbuje zaoy blokad, blokujc wtek przez czas nie duszy od okrelonego.
W razie powodzenia zwraca warto true.

void lockInterruptibly()

Zajmuje blokad i blokuje wtek na nieokrelony czas. Jeli wtek zostanie


przerwany, zgasza wyjtek InterruptedException.
java.util.concurrent.locks.Condition 5.0

boolean await(long time, TimeUnit unit)

Wchodzi do kolejki oczekujcych na warunek, blokujc wtek do czasu,


a zostanie on usunity z kolejki lub minie okrelona ilo czasu. Zwraca warto
false, jeli metoda zakoczy dziaanie z powodu upynicia okrelonego czasu,
lub true w przeciwnym przypadku.

void awaitUninterruptibly()

Wchodzi do kolejki oczekujcych na warunek, blokujc wtek do czasu, a zostanie


on usunity z kolejki. Jeli wtek zostanie przerwany, metoda ta nie zgasza
wyjtku InterruptedException.

14.5.14. Blokady odczytu-zapisu


W pakiecie java.util.concurrent.locks znajduj si dwie klasy blokad: ReentrantLock, ktr
wanie skoczylimy opisywa, oraz ReentrantReadWriteLock. Druga z wymienionych znajduje zastosowanie w sytuacjach, gdy wiele wtkw odczytuje dane ze struktury danych, a mniej
j modyfikuje. Stanowi to podstaw do zezwolenia procedurom odczytujcym na dostp
wspdzielony. Oczywicie algorytm zapisujcy musi mie dostp na wyczno.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

784

Java. Podstawy
Oto lista czynnoci zwizanych z uyciem blokady odczytu-zapisu:
1.

Utwrz obiekt ReentrantReadWriteLock:


private ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

2. Pozyskaj blokady odczytu i zapisu:


private Lock readLock = rwl.readLock();
private Lock writeLock = rwl.writeLock();

3. Uyj blokady odczytu we wszystkich metodach dostpowych:


public double getTotalBalance()
{
readLock.lock();
try { . . . }
finally { readLock.unlock(); }
}

4. Uyj blokady zapisu we wszystkich metodach modyfikujcych:


public void transfer(. . .)
{
writeLock.lock();
try { . . . }
finally { writeLock.unlock(); }
}
java.util.concurrent.locks.ReentrantReadWriteLock 5.0

Lock readLock()

Zwraca blokad odczytu, ktr moe zakada wiele algorytmw odczytujcych,


ale aden zapisujcy.

Lock writeLock()

Zwraca blokad zapisu, ktra wyklucza wszystkie pozostae algorytmy odczytujce


i zapisujce.

14.5.15. Dlaczego metody stop i suspend s wycofywane


Pocztkowo w Javie dostpna bya metoda stop, ktra koczya wtek, i metoda suspend,
ktra blokowaa wtek do czasu, gdy inny wtek wywoa metod resume. Dwie pierwsze
z wymienionych metod co czy: obie prbuj kontrolowa dziaanie wtku, nie wsppracujc z nim.
Obie te metody s wycofywane. Metoda stop jest z gruntu niebezpieczna, a jeli chodzi
o suspend, to z dowiadczenia wiadomo, e czsto prowadzi do zakleszcze. W tym podrozdziale wyjaniamy, dlaczego metody te sprawiaj problemy i co mona zrobi, aby ich
unikn.
Zaczniemy od metody stop. Zamyka ona wszystkie oczekujce metody, wcznie z metod
run. Jeli zostanie zastosowana na rzecz wtku, natychmiast zdejmuje on wszystkie blokady,
ktre zaoy. To moe prowadzi do uszkodzenia obiektw. Wyobramy sobie na przykad,

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

785

e wtek TransferRunnable zosta zatrzymany w trakcie przelewania pienidzy z jednego


konta na inne zdy pobra pienidze, ale nie zdy ich zapisa na drugim koncie.
W tej sytuacji obiekt banku zostaje zniszczony. Poniewa blokada zostaa zdjta, zniszczenie jest widoczne take dla innych wtkw, ktre jeszcze nie zostay zatrzymane.
Wtek chccy zatrzyma inny wtek nie ma sposobu na sprawdzenie, kiedy wywoanie
metody stop jest bezpieczne, a kiedy doprowadzi do zniszczenia obiektu. Dlatego odradza si
uywania tej metody. Aby zatrzyma wtek, naley go przerwa. Przerwany wtek moe si
zatrzyma wtedy, gdy jest to bezpieczne.
Niektrzy twierdz, e metoda stop jest odradzana, poniewa poprzez zatrzymywanie wtkw moe blokowa obiekty na stae. To jednak nieprawda. Zatrzymany
wtek wychodzi z wszystkich metod synchronizowanych, ktre wywoa, zgaszajc wyjtek
ThreadDeath. W rezultacie zwalniane s wszystkie wewntrzne blokady obiektw, ktre
wtek ten zaoy.

Kolej na metod suspend. W przeciwiestwie do metody stop nie powoduje ona uszkodzenia obiektw. Jeli jednak zawieszony zostanie wtek posiadajcy blokad bdzie ona niedostpna a do odwieszenia tego wtku. Jeli wtek, ktry wywoa t metod suspend,
prbuje zaoy t sam blokad, program zostaje zakleszczony zawieszony wtek czeka
na odwieszenie, a wtek, ktry go zawiesi, czeka na blokad.
Sytuacje tego typu czsto zdarzaj si w graficznych interfejsach uytkownika. Zamy, e
mamy graficzn symulacj naszego banku. Przycisk z etykiet Wstrzymaj zawiesza wtki
dokonujce przeleww, a przycisk z etykiet Wznw odwiesza je.
pauseButton.addActionListener(new
ActionListener()
{
public void actionPerformed(ActionEvent event)
{
for (int i = 0; i < threads.length; i++)
threads[i].suspend();
// Nie rb tego.
}
});
resumeButton.addActionListener(. . .);
// Wywouje metod resume na rzecz wszystkich
// wtkw przelewowych.

Metoda paintComponent bdzie rysowa wykres kadego konta. W tym celu utworzy tablic
sald kont za pomoc metody getBalances.
Jak przekonasz si w podrozdziale 14.11, Wtki a biblioteka Swing, zarwno akcje przyciskw, jak i ponowne rysowanie odbywaj si w tym samym wtku wtku dystrybucji
zdarze (ang. event dispatch thread). Przeanalizujmy nastpujcy scenariusz:
1.

Jeden z wtkw przelewowych zakada blokad obiektu bank.

2. Uytkownik klika przycisk Wstrzymaj.


3. Wszystkie wtki przelewowe zostaj zawieszone. Jeden z nich cay czas trzyma
blokad na obiekcie bank.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

786

Java. Podstawy
4. Z jakiego powodu konieczne jest ponowne narysowanie wykresu konta.
5. Metoda paintComponent wywouje metod getBalances.
6. Metoda ta prbuje zaoy blokad obiektu bank.

Program zostaje zamroony.


Wtek dystrybucji zdarze nie moe kontynuowa, poniewa blokada znajduje si w posiadaniu jednego z zawieszonych wtkw. Dlatego uytkownik nie moe klikn przycisku
Wznw i wtki nigdy nie zostan odwieszone.
Aby bezpiecznie zawiesza wtki, naley utworzy zmienn suspendRequested i testowa
j w bezpiecznym miejscu metody run w takim miejscu, w ktrym wtek nie blokuje
obiektw potrzebnych innym wtkom. Kiedy wtek odkryje, e zmienna suspendRequested
zostaa ustawiona, powinien czeka, a bdzie ona ponownie dostpna.
Implementacja opisywanej techniki znajduje si poniej:
private volatile boolean suspendRequested = false;
private Lock suspendLock = new ReentrantLock();
private Condition suspendCondition = suspendLock.newCondition();
public void run()
{
while (. . .)
{
. . .
if (suspendRequested)
{
suspendLock.lock();
try { while (suspendRequested) suspendCondition.await(); }
finally { suspendLock.unlock(); }
}
}
}
public void requestSuspend() { suspendRequested = true; }
public void requestResume()
{
suspendRequested = false;
suspendLock.lock();
try { suspendCondition.signalAll(); }
finally { suspendLock.unlock(); }
}

14.6. Kolejki blokujce


Zapoznalimy si z niskopoziomowymi mechanizmami kadcymi podwaliny pod wspbieno w Javie. Jednak w codziennej pracy programistycznej lepiej jest trzyma si od nich
jak najdalej. O wiele atwiej i bezpieczniej jest uywa struktur wyszego poziomu, ktre
zostay zaimplementowane przez ekspertw od wspbienoci.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

787

Wiele problemw z wtkami mona zgrabnie i bezpiecznie sformuowa za pomoc jednej


lub wikszej liczby kolejek. Wtki producenta umieszczaj elementy w kolejce, a wtki konsumenta pobieraj je stamtd. Kolejka umoliwia bezpieczn wymian danych pomidzy
wtkami. Wemy na przykad nasz program symulujcy bank. Wtki przelewajce zamiast
bezporednio operowa na obiekcie banku wstawiaj obiekty instrukcji przelewu do kolejki.
Inny wtek usuwa te instrukcje i wykonuje przelewy. Tylko ten wtek ma dostp do wntrza obiektu banku. Nie jest potrzebna synchronizacja (oczywicie projektanci klas kolejek
bezpiecznych dla wtkw musieli zaj si blokadami i warunkami, ale to by ich problem,
a nie nasz).
Kolejka blokujca (ang. blocking queue) powoduje zablokowanie wtku podczas prby
dodania elementu, jeli jest pena, lub podczas prby usunicia elementu, jeli jest pusta.
Kolejki tego typu znajduj zastosowanie w koordynacji dziaa wielu wtkw. Niektre
wtki robocze mog co jaki czas odkada porednie wyniki w kolejce blokujcej, a pozostae mog je stamtd usuwa i poddawa dalszej obrbce. Kolejka automatycznie kontroluje
przebieg pracy. Jeli jeden zestaw wtkw dziaa wolniej ni drugi, ten drugi musi poczeka
na wyniki pierwszego. Jeli pierwszy zestaw dziaa szybciej od drugiego, kolejka zapenia
si, dopki drugi zestaw wtkw nada z odbieraniem. Tabela 14.1 zawiera zestawienie
metod blokujcych kolejek.
Metody kolejek blokujcych mona podzieli na trzy kategorie w zalenoci od dziaania,
kiedy kolejka jest pena lub pusta. Jeli kolejka jest wykorzystywana jako narzdzie do zarzdzania wtkami, naley uywa metod put i take. Metody add, remove i element zgaszaj
wyjtek, kiedy element jest dodawany do penej kolejki lub pobierany z pustej. Oczywicie
w programie wielowtkowym kolejka moe si zapeni i zrobi pusta w kadej chwili. Dlatego
w takich sytuacjach naley uywa metod offer, poll i peek. Metody te nie zgaszaj wyjtku, tylko zwracaj warto oznaczajc niepowodzenie operacji, jeli zakocz si niepowodzeniem.
Metody poll i peek informuj o niepowodzeniu za pomoc wartoci zwrotnej null.
Dlatego do tego typu kolejek nie mona wstawia referencji null.

Istniej take wersje czasowe metod offer i poll. Na przykad ponisza instrukcja:
boolean success = q.offer(x, 100, TimeUnit.MILLISECONDS);

przez sto milisekund prbuje wstawi element do ogona kolejki. Jeli si jej powiedzie, zwrci
warto true, w przeciwnym przypadku, jeli nie wykona operacji w wyznaczonym czasie,
zwrci false. Podobnie instrukcja:
Object head = q.poll(100, TimeUnit.MILLISECONDS)

przez sto milisekund prbuje usun element z czoa kolejki. Jeli si jej powiedzie, zwrci
ten element, w przeciwnym przypadku, jeli nie wykona operacji w wyznaczonym czasie,
zwrci false.
Metoda put wcza blokad, jeli kolejka jest pena, a metoda take robi to samo, gdy kolejka
jest pusta. Metody te s odpowiednikami metod offer i poll bez ograniczenia czasowego.
W pakiecie java.util.concurrent znajduje si kilka wersji kolejek blokujcych. Kolejka
LinkedBlockingQueue nie posiada domylnej grnej granicy pojemnoci, ale mona j okreli.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

788

Java. Podstawy

Tabela 14.1. Metody kolejek blokujcych


Metoda

Normalne dziaanie

Dziaanie w specjalnych warunkach

add

Dodaje element.

Zgasza wyjtek IllegalStateException, jeli kolejka


jest pena.

element

Zwraca element z czoa.

Zgasza wyjtek NoSuchElementException, jeli kolejka


jest pusta.

offer

Dodaje element i zwraca warto true. Zwraca warto false, jeli kolejka jest pena.

peek

Zwraca element z czoa.

Zwraca warto null, jeli kolejka jest pusta.

poll

Usuwa i zwraca element z czoa.

Zwraca warto null, jeli kolejka jest pusta.

put

Dodaje element.

Blokuje, jeli kolejka jest pena.

remove

Usuwa i zwraca element z czoa.

Zgasza wyjtek NoSuchElementException, jeli kolejka


jest pusta.

take

Usuwa i zwraca element z czoa.

Blokuje, jeli kolejka jest pusta.

Jej dwustronna wersja to LinkedBlockingDeque. Kolejka ArrayBlockingQueue ma okrelon


pojemno i opcjonalny parametr wczajcy wymg uczciwoci. Jeli kolejka jest uczciwa,
preferencyjnie traktowane s te wtki, ktre czekaj najduej. Naley jednak pamita, e
uczciwo zawsze powoduje straty szybkoci, przez co opcj t powinno si stosowa wycznie wtedy, gdy jest to cakowicie niezbdne.
Kolejka PriorityBlockingQueue jest kolejk priorytetow, nie typu pierwszy wszed, pierwszy wyszed. Elementy s usuwane zgodnie z ich priorytetami. Kolejka ta ma nieograniczon pojemno, ale pobieranie elementw z pustej konstrukcji powoduje blokad (wicej
informacji na temat kolejek priorytetowych znajduje si w rozdziale 13.).
W kocu kolejka DelayQueue przechowuje obiekty, ktre implementuj interfejs Delayed:
interface Delayed extends Comparable<Delayed>
{
long getDelay(TimeUnit unit);
}

Metoda getDelay zwraca ilo pozostaego czasu opnienia obiektu. Warto ujemna oznacza, e czas ten upyn. Elementy z tej kolejki mog zosta usunite dopiero wtedy, gdy
upynie okrelony czas opnienia. Konieczna jest take implementacja metody compareTo.
Kolejka DelayQueue uywa tej metody do sortowania elementw.
W Java SE 7 dodano interfejs TransferQueue pozwalajcy wtkowi producenta poczeka, a
konsument bdzie gotowy do przyjcia elementu. Gdy producent wywouje ponisz metod:
q.transfer(item);

wywoanie to zostaje zablokowane do czasu, a blokad usunie inny wtek. Opisywany interfejs jest zaimplementowany w klasie LinkedTransferQueue.
Program przedstawiony na listingu 14.10 demonstruje sposb kontroli zestawu wtkw za
pomoc kolejki blokujcej. Przeszukuje on wszystkie pliki znajdujce si w katalogu i jego
podkatalogach oraz drukuje linijki, ktre zawieraj dane sowo kluczowe.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

Listing 14.10. blockingQueue/BlockingQueueTest.java


package blockingQueue;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class BlockingQueueTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj katalog bazowy (np. /usr/local/jdk1.6.0/src): ");
String directory = in.nextLine();
System.out.print("Podaj sowo kluczowe (np. volatile): ");
String keyword = in.nextLine();
final int FILE_QUEUE_SIZE = 10;
final int SEARCH_THREADS = 100;
BlockingQueue<File> queue = new ArrayBlockingQueue<>(FILE_QUEUE_SIZE);
FileEnumerationTask enumerator = new FileEnumerationTask(queue, new
File(directory));
new Thread(enumerator).start();
for (int i = 1; i <= SEARCH_THREADS; i++)
new Thread(new SearchTask(queue, keyword)).start();
}
}
/**
* Zadanie tworzce wyliczenie wszystkich plikw w katalogu i jego podkatalogach
*/
class FileEnumerationTask implements Runnable
{
public static File DUMMY = new File("");
private BlockingQueue<File> queue;
private File startingDirectory;
/**
* Tworzy obiekt klasy FileEnumerationTask.
* @param queue kolejka blokujca, do ktrej dodawane s pliki
* @param startingDirectory katalog, od ktrego ma si zacz zbieranie plikw
*/
public FileEnumerationTask(BlockingQueue<File> queue, File startingDirectory)
{
this.queue = queue;
this.startingDirectory = startingDirectory;
}
public void run()
{
try
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

789

790

Java. Podstawy
enumerate(startingDirectory);
queue.put(DUMMY);
}
catch (InterruptedException e)
{
}
}
/**
* Rekursywna enumeracja wszystkich plikw znajdujcych si w danym katalogu i jego podkatalogach
* @param directory katalog pocztkowy
*/
public void enumerate(File directory) throws InterruptedException
{
File[] files = directory.listFiles();
for (File file : files)
{
if (file.isDirectory()) enumerate(file);
else queue.put(file);
}
}
}
/**
* Zadanie przeszukujce pliki w celu znalezienia okrelonego sowa kluczowego
*/
class SearchTask implements Runnable
{
private BlockingQueue<File> queue;
private String keyword;
/**
* Tworzy obiekt klasy SearchTask.
* @param queue kolejka, z ktrej maj by pobierane pliki
* @param keyword sowo kluczowe, ktre ma zosta znalezione
*/
public SearchTask(BlockingQueue<File> queue, String keyword)
{
this.queue = queue;
this.keyword = keyword;
}
public void run()
{
try
{
boolean done = false;
while (!done)
{
File file = queue.take();
if (file == FileEnumerationTask.DUMMY)
{
queue.put(file);
done = true;
}
else search(file);
}
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

791

catch (IOException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
}
}
/**
* Przeszukuje plik w celu znalezienia okrelonego sowa kluczowego i drukuje wszystkie zawierajce je linijki
* @param file plik do przeszukania
*/
public void search(File file) throws IOException
{
try (Scanner in = new Scanner(file))
{
int lineNumber = 0;
while (in.hasNextLine())
{
lineNumber++;
String line = in.nextLine();
if (line.contains(keyword))
System.out.printf("%s:%d:%s%n", file.getPath(), lineNumber, line);
}
}
}
}

Wtek producenta (producent) tworzy wyliczenie wszystkich plikw znalezionych we wszystkich podkatalogach i wstawia je do kolejki blokujcej. Operacja ta jest bardzo szybka i gdyby
nie ograniczenie pojemnoci, kolejka w szybkim tempie zapeniaby si wszystkimi plikami
znajdujcymi si w systemie plikw.
Uruchamiamy take du liczb wtkw przeszukujcych. Kady taki wtek pobiera plik
z kolejki, otwiera go, drukuje wszystkie linijki zawierajce dane sowo kluczowe i pobiera
nastpny plik. Do zakoczenia aplikacji, kiedy dalsza jej praca jest ju zbdna, wykorzystalimy pewn sztuczk. Wtek wyliczeniowy sygnalizuje ukoczenie pracy, umieszczajc
w kolejce atrap obiektu (przypomina to umieszczanie walizki z etykiet Ostatnia torba
na kocu tamy z walizkami na lotnisku). Kiedy wtek przeszukujcy pobierze taki obiekt,
odkada go z powrotem i koczy dziaanie.
Zwr uwag, e nie trzeba bezporednio stosowa synchronizacji. W tej aplikacji do synchronizacji uywamy kolejki.
java.util.concurrent.ArrayBlockingQueue<E> 5.0

ArrayBlockingQueue(int capacity)

ArrayBlockingQueue(int capacity, boolean fair)

Tworzy kolejk blokujc o okrelonej pojemnoci i z ustawion zasad uczciwoci.


Kolejka ta jest zaimplementowana jako tablica cykliczna.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

792

Java. Podstawy
java.util.concurrent.LinkedBlockingQueue<E> 5.0
java.util.concurrent.LinkedBlockingDeque<E> 6

LinkedBlockingQueue()

LinkedBlockingDeque()

Tworzy nieograniczon kolejk blokujc jedno- lub dwustronn,


zaimplementowan jako lista powizana.

LinkedBlockingQueue(int capacity)

LinkedBlockingDeque(int capacity)

Tworzy ograniczon kolejk jedno- lub dwustronn blokujc o okrelonej


pojemnoci, zaimplementowan jako lista powizana.
java.util.concurrent.DelayQueue<E extends Delayed> 5.0

DelayQueue()

Tworzy nieograniczon kolejk elementw typu Delayed. Z kolejki tej mona


usuwa tylko te elementy, ktrych czas opnienia upyn.
java.util.concurrent.Delayed 5.0

long getDelay(TimeUnit unit)

Zwraca opnienie obiektu mierzone w okrelonej jednostce czasu.


java.util.concurrent.PriorityBlockingQueue<E> 5.0

PriorityBlockingQueue()

PriorityBlockingQueue(int initialCapacity)

PriorityBlockingQueue(int initialCapacity, Comparator<? super E>


comparator)

Tworzy nieograniczon priorytetow kolejk blokujc zaimplementowan


jako sterta.
Parametry:

initialCapacity

Pocztkowa pojemno kolejki


priorytetowej. Domylna warto to 11.

comparator

Komparator uywany do porwnywania


elementw. Jeli nie zostanie podany,
elementy musz implementowa interfejs
Comparable.

java.util.concurrent.BlockingQueue<E> 5.0

void put(E element)

Dodaje element i w razie koniecznoci wcza blokowanie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

E take()

Usuwa i zwraca element z czoa, w razie koniecznoci wcza blokowanie.

boolean offer(E element, long time, TimeUnit unit)

Dodaje okrelony element i zwraca warto true, jeli operacja zakoczy si


powodzeniem. W razie koniecznoci wcza blokowanie, a element zostanie
dodany lub upynie okrelony czas.

E poll(long time, TimeUnit unit)

Usuwa i zwraca element z czoa. W razie koniecznoci wcza blokowanie,


a element bdzie dostpny lub upynie okrelony czas. W razie niepowodzenia
zwraca warto null.
java.util.concurrent.BlockingDeque<E> 6

void putFirst(E element)

void putLast(E element)

Dodaje element i w razie potrzeby wcza blokad.

E takeFirst()

E takeLast()

Usuwa i zwraca element z czoa lub ogona i w razie potrzeby wcza blokad.

boolean offerFirst(E element, long time, TimeUnit unit)

boolean offerLast(E element, long time, TimeUnit unit)

Dodaje okrelony element i zwraca warto true, jeli operacja zakoczy si


powodzeniem. W razie potrzeby wcza blokad, a element zostanie dodany
albo upynie wyznaczony czas.

E pollFirst(long time, TimeUnit unit)

E pollLast(long time, TimeUnit unit)

Usuwa i zwraca element z czoa lub ogona. W razie potrzeby wcza blokad,
a element bdzie dostpny lub upynie wyznaczony czas. W przypadku
niepowodzenia zwraca warto null.
java.util.concurrent.TransferQueue<E> 7

void transfer(E element)

boolean tryTransfer(E element, long time, TimeUnit unit)

Przesya warto albo prbuje j przesa w okrelonym czasie, zakadajc blokad


do czasu, a inny wtek usunie element. Druga metoda zwraca true w razie
powodzenia.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

793

794

Java. Podstawy

14.7. Kolekcje bezpieczne wtkowo


Jeli kilka wtkw rwnoczenie modyfikuje jak struktur danych, na przykad tablic
mieszajc, to moe ona atwo ulec uszkodzeniu (wicej informacji na temat tablic mieszajcych znajduje si w rozdziale 13.). Na przykad jeden wtek moe zacz operacj wstawiania nowego elementu. Zamy, e zostaje on wywaszczony w trakcie przekierowywania
czy pomidzy komrkami tablicy. Jeli w tym czasie inny wtek zacznie przemierza t
struktur danych, moe si natkn na ze poczenia i spowodowa kompletny baagan, przy
okazji zgaszajc wyjtek bd wpadajc w nieskoczon ptl.
Wspdzielon struktur danych mona ochroni za pomoc blokady, ale czsto atwiejszym
rozwizaniem jest uycie implementacji bezpiecznej wtkowo. Kolejki blokujce omwione
w poprzednim podrozdziale s przykadem takich bezpiecznych wtkowo kolekcji. Ponisze
podrozdziay opisuj inne kolekcje znajdujce si w bibliotece Javy, ktre s bezpieczne ze
wzgldu na wtki.

14.7.1. Szybkie mapy, zbiory i kolejki


W pakiecie java.util.concurrent znajduj si nastpujce szybkie implementacje map, zbiorw uporzdkowanych i kolejek: ConcurrentHashMap, ConcurrentSkipListMap, Concurrent
SkipListSet oraz ConcurrentLinkedQueue.
W kolekcjach tych zastosowano zaawansowane algorytmy minimalizujce rywalizacj wtkw poprzez umoliwianie rwnolegego dostpu do rnych czci struktury danych.
W przeciwiestwie do wikszoci kolekcji, w tych metoda size niekoniecznie dziaa w staym
czasie. Okrelenie aktualnego rozmiaru tych kolekcji zazwyczaj wymaga ich przemierzenia.
Kolekcje te zwracaj tak zwane sabo spjne iteratory (ang. weakly consistent iterators).
Oznacza to, e mog one (cho nie musz) odzwierciedla wszystkie modyfikacje dokonane
po ich skonstruowaniu. Nie zwracaj one jednak dwukrotnie wartoci i nie zgaszaj wyjtku
ConcurrentModificationException.
W przeciwiestwie do opisywanych iteratorw, iteratory kolekcji z pakietu java.util
zgaszaj wyjtek ConcurrentModificationException, jeli kolekcja zostanie
zmodyfikowana po ich utworzeniu.

Struktura ConcurrentHashMap jest zdolna szybko obsuy du liczb czytnikw i ustalon


liczb algorytmw zapisujcych. Domylnie zaoono, e moe by do 16 algorytmw zapisujcych dziaajcych jednoczenie. Moe by ich wicej, ale jeli wicej ni 16 z nich zapisuje
w tym samym czasie, reszta pozostaje tymczasowo zablokowana. Mona poda wiksz liczb
w konstruktorze, ale istnieje niewielkie prawdopodobiestwo, e bdzie to potrzebne.
Klasy ConcurrentHashMap i ConcurrentSkipListMap udostpniaj metody suce do wstawiania i usuwania par klucz warto za pomoc niepodzielnych operacji. Metoda putIfAbsent
dodaje now par klucz warto, pod warunkiem e nie byo jej wczeniej. Metoda ta jest

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

795

przydatna w pamiciach podrcznych, do ktrych dostp ma wiele wtkw, poniewa daje


pewno, e dana para zostanie wstawiona tylko przez jeden wtek:
cache.putIfAbsent(key, value);

Operacj przeciwn wykonuje metoda remove (ktrej nazwa powinna chyba brzmie remove
IfPresent). Ponisze wywoanie usuwa za pomoc niepodzielnej operacji klucz i jego
warto, jeli znajduj si one w mapie.
cache.remove(key, value)

W kocu ponisze wywoanie zamienia za pomoc niepodzielnej operacji star warto


(oldValue) na now (newValue), pod warunkiem e stara warto jest skojarzona z okrelonym kluczem.
cache.replace(key, oldValue, newValue)
java.util.concurrent.ConcurrentLinkedQueue<E> 5.0

ConcurrentLinkedQueue<E>()

Tworzy nieograniczon kolejk nieblokujc, ktr mona bezpiecznie


przetwarza w wielu wtkach.
java.util.concurrent.ConcurrentSkipListSet<E> 6

ConcurrentSkipListSet<E>()

ConcurrentSkipListSet<E>(Comparator<? super E> comp)

Tworzy zbir uporzdkowany, do ktrego mona bezpiecznie uzyska


dostp w wielu wtkach. Pierwszy z konstruktorw wymaga, aby elementy
implementoway interfejs Comparable.
java.util.concurrent.ConcurrentHashMap<K, V> 5.0
java.util.concurrent.ConcurrentSkipListMap<K, V> 6

ConcurrentHashMap<K, V>()

ConcurrentHashMap<K, V>(int initialCapacity)

ConcurrentHashMap<K, V>(int initialCapacity, float loadFactor,


int concurrencyLevel)

Tworzy map haszow, do ktrej mona bezpiecznie uzyska dostp w wielu wtkach.
Parametry:

initialCapacity

Pocztkowa pojemno kolekcji, domylna


warto to 16.

loadFactor

Kontroluje zmian rozmiaru: jeli rednie


zapenienie komrki przekracza
ten wspczynnik, rozmiar tablicy
jest zmieniany. Domylna warto to 0,75.

concurrencyLevel Przewidywana liczba wspbienych wtkw

zapisujcych.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

796

Java. Podstawy

ConcurrentSkipListMap<K, V>()

ConcurrentSkipListSet<K, V>(Comparator<? super K> comp)

Tworzy uporzdkowan map, do ktrej mona uzyska bezpieczny dostp w wielu


wtkach. Pierwszy z konstruktorw wymaga, aby klucze implementoway interfejs
Comparable.

V putIfAbsent(K key, V value)

Jeli klucza nie ma jeszcze w mapie, zostaje on skojarzony z podan wartoci


i metoda zwraca warto null. W przeciwnym przypadku zwraca istniejc
warto skojarzon z tym kluczem.

boolean remove(K key, V value)

Jeli podany klucz jest aktualnie skojarzony z podan wartoci, metoda ta usuwa
je i zwraca warto true. W przeciwnym przypadku zwraca warto false.

boolean replace(K key, V oldValue, V newValue)

Jeli podany klucz jest aktualnie skojarzony ze star wartoci (oldValue), zostaje
skojarzony z now wartoci (newValue). W przeciwnym przypadku zwraca
warto false.

14.7.2. Tablice kopiowane przy zapisie


CopyOnWriteArrayList i CopyOnWriteArraySet to bezpieczne wtkowo kolekcje, ktrych mutatory tworz kopie tablic. Taki sposb dziaania sprawdza si w sytuacjach, w ktrych liczba
wtkw iterujcych po kolekcji znacznie przewysza liczb wtkw j modyfikujcych.
Utworzony iterator zawiera referencj do aktualnej tablicy. Jeli tablica ta zostanie pniej
zmodyfikowana, iterator ten nadal bdzie mia star tablic, mimo e tablica kolekcji jest
zamieniona. Dziki temu starszy iterator dysponuje spjnym (cho potencjalnie przestarzaym) widokiem, do ktrego ma dostp nieobciony adnym dodatkowym narzutem synchronizacji.

14.7.3. Starsze kolekcje bezpieczne wtkowo


Od samego pocztku istnienia Javy klasy Vector i Hashtable udostpniay bezpieczne wtkowo implementacje tablicy dynamicznej i mieszajcej. Klasy te s ju uwaane za przestarzae
i zastpiono je klasami ArrayList i HashMap. Klasy te nie s bezpieczne wtkowo. W zamian
w bibliotece kolekcji zaproponowano inn technik. Kad klas kolekcji mona uczyni
bezpieczn wtkowo za pomoc synchronizacyjnych obiektw opakowujcych:
List<E> synchArrayList = Collections.synchronizedList(new ArrayList<E>());
Map<K, V> synchHashMap = Collections.synchronizedMap(new HashMap<K, V>());

Metody tak powstaych kolekcji s chronione przez blokad, co umoliwia bezpieczny wtkowo dostp.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

797

Naley zapewni, e aden wtek nie bdzie mia dostpu do struktury danych poprzez oryginalne niesynchronizowane metody. Najprostszym sposobem jest niezapisywanie adnych
referencji do oryginalnego obiektu. Po utworzeniu kolekcji od razu naley przekaza j do
opakowania, tak jak zrobilimy to w prezentowanych przykadach.
Nadal trzeba stosowa blokowanie po stronie klienta, aby mc iterowa po kolekcji, podczas
gdy inny wtek moe j modyfikowa:
synchronized (synchHashMap)
{
Iterator<K> iter = synchHashMap.keySet().iterator();
while (iter.hasNext()) . . .;
}

Tego samego kodu musimy uy, jeli korzystamy z ptli typu for each, poniewa ptla ta
uywa iteratora. Naley pamita, e iterator zgosi wyjtek ConcurrentModificationException,
jeli w trakcie iteracji po kolekcji inny wtek j zmodyfikuje. Synchronizacja jest nadal
wymagana, dziki czemu mona wykry wspbiene modyfikacje.
Zamiast uywa synchronizacyjnych obiektw opakowujcych, zazwyczaj lepiej jest skorzysta z kolekcji z pakietu java.util.concurrent. Mapa ConcurrentHashMap zostaa bardzo
starannie zaimplementowana w taki sposb, aby mona byo uzyska do niej dostp w wielu
wtkach, nie powodujc ich wzajemnego blokowania si, pod warunkiem e dziaaj one na
rnych komrkach. Jeden wyjtek stanowi lista tablicowa, ktra jest czsto modyfikowana.
W takim przypadku synchronizowana lista ArrayList moe si okaza lepsza od listy CopyOn
WriteArrayList.
java.util.Collections 1.2

static <E> Collection<E> synchronizedCollection(Collection<E> c)

static <E> List synchronizedList(List<E> c)

static <E> Set synchronizedSet(Set<E> c)

static <E> SortedSet synchronizedSortedSet(SortedSet<E> c)

static <K, V> Map<K, V> synchronizedMap(Map<K, V> c)

static <K, V> SortedMap<K, V> synchronizedSortedMap(SortedMap<K, V> c)

Tworzy widoki kolekcji, ktrych metody s synchronizowane.

14.8. Interfejsy Callable i Future


Obiekt implementujcy interfejs Runnable opakowuje zadania, ktre dziaaj asynchronicznie.
Mona go traktowa jako asynchroniczn metod bez parametrw i wartoci zwrotnej. Obiekt
Callable jest podobny do Runnable, ale posiada warto zwrotn. Interfejs Callable jest typem
parametryzowanym zawierajcym jedn metod o nazwie call.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

798

Java. Podstawy
public interface Callable<V>
{
V call() throws Exception;
}

Parametr typowy okrela typ zwracanej wartoci. Na przykad interfejs Callable<Integer>


reprezentuje asynchroniczne dziaania, ktrych wynikiem jest obiekt typu Integer.
Obiekt Future przechowuje wynik asynchronicznych oblicze. Mona rozpocz obliczenia,
przekaza gdzie obiekt Future i zapomnie o nim. Waciciel tego obiektu moe pobra wynik,
kiedy bdzie gotowy.
W interfejsie Future znajduj si nastpujce metody:
public interface Future<V>
{
V get() throws . . .;
V get(long timeout, TimeUnit unit) throws . . .;
void cancel(boolean mayInterrupt);
boolean isCancelled();
boolean isDone();
}

Wywoanie pierwszej z metod get jest zablokowane do zakoczenia oblicze. Druga wersja
tej metody zgasza wyjtek TimeoutException, jeli obliczenia nie zakocz si przed upywem
okrelonego czasu. Obie te metody zgaszaj wyjtek InterruptedException, jeli wtek przeprowadzajcy obliczenia zostanie przerwany. Kiedy obliczenia zakocz si, metoda get
natychmiast zwraca warto.
Metoda isDone zwraca warto false, jeli obliczenia s jeszcze w toku, lub true w przeciwnym przypadku.
Operacj mona przerwa za pomoc metody cancel. Jeli jeszcze si nie rozpocza, zostanie anulowana i nigdy si nie rozpocznie. Jeli jest w toku, zostanie przerwana, gdy parametr
mayInterrupt ma warto true.
Obiekt FutureTask jest wygodnym narzdziem do zamieniania obiektw Callable zarwno
na Future, jak i Runnable, poniewa implementuje oba te interfejsy. Na przykad:
Callable<Integer> myComputation = . . .;
FutureTask<Integer> task = new FutureTask<Integer>(myComputation);
Thread t = new Thread(task);
// Runnable
t.start();
. . .
Integer result = task.get();
// Future

Program przedstawiony na listingu 14.11 demonstruje praktyczne zastosowanie omawianych


technik. Jest podobny do poprzedniego programu, ktry znajdowa pliki zawierajce dane sowo
kluczowe. Tym razem jednak poprzestaniemy tylko na zliczaniu znalezionych plikw.
W zwizku z tym mamy jedno dugo trwajce zadanie, ktre zwraca liczb cakowit
przykad interfejsu Callable<Integer>.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

class MatchCounter implements Callable<Integer>


{
public MatchCounter(File directory, String keyword) { . . . }
public Integer call() { . . . }
// Zwraca liczb pasujcych plikw.
}

Listing 14.11. future/FutureTest.java


package future;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class FutureTest
{
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj katalog bazowy (np. /usr/local/jdk1.6.0/src): ");
String directory = in.nextLine();
System.out.print("Podaj sowo kluczowe (np. volatile): ");
String keyword = in.nextLine();
MatchCounter counter = new MatchCounter(new File(directory), keyword);
FutureTask<Integer> task = new FutureTask<>(counter);
Thread t = new Thread(task);
t.start();
try
{
System.out.println("Liczba znalezionych plikw " + task.get() + ".");
}
catch (ExecutionException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{
}
}
}
/**
* Zadanie liczce pliki znajdujce si w katalogu i jego podkatalogach, zawierajce dane sowo kluczowe
*/
class MatchCounter implements Callable<Integer>
{
private File directory;
private String keyword;
private int count;
/**

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

799

800

Java. Podstawy
* Tworzy obiekt klasy MatchCounter.
* @param directory katalog, od ktrego ma si zacz szukanie
* @param keyword sowo kluczowe do znalezienia
*/
public MatchCounter(File directory, String keyword)
{
this.directory = directory;
this.keyword = keyword;
}
public Integer call()
{
count = 0;
try
{
File[] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for (File file : files)
if (file.isDirectory())
{
MatchCounter counter = new MatchCounter(file, keyword);
FutureTask<Integer> task = new FutureTask<>(counter);
results.add(task);
Thread t = new Thread(task);
t.start();
}
else
{
if (search(file)) count++;
}
for (Future<Integer> result : results)
try
{
count += result.get();
}
catch (ExecutionException e)
{
e.printStackTrace();
}
}
catch (InterruptedException e)
{
}
return count;
}
/**
* Przeszukuje plik w celu znalezienia danego sowa kluczowego.
* @param file plik do przeszukania
* @return warto true, jeli plik zawiera dane sowo kluczowe
*/
public boolean search(File file)
{
try
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

801

try (Scanner in = new Scanner(file))


{
boolean found = false;
while (!found && in.hasNextLine())
{
String line = in.nextLine();
if (line.contains(keyword)) found = true;
}
return found;
}
}
catch (IOException e)
{
return false;
}
}
}

Nastpnie konstruujemy obiekt typu FutureTask z obiektu MatchCounter i wykorzystujemy


go do uruchomienia wtku.
FutureTask<Integer> task = new FutureTask<Integer>(counter);
Thread t = new Thread(task);
t.start();

Na kocu drukujemy wynik.


System.out.println("Liczba znalezionych plikw " + task.get() + ".");

Oczywicie wywoanie metody get powoduje blokad do chwili, a wynik jest rzeczywicie
dostpny.
W metodzie call wykorzystujemy rekursywnie ten sam mechanizm. Dla kadego podkatalogu
tworzymy nowy obiekt MatchCounter i uruchamiamy dla niego wtek. Ponadto odkadamy
obiekty FutureTask w tablicy ArrayList<Future<Integer>>. Na kocu sumujemy wszystkie
wyniki.
for (Future<Integer> result : results)
count += result.get();

Kade wywoanie metody get powoduje blokad do chwili, a zostanie udostpniony wynik.
Oczywicie wtki dziaaj rwnolegle, dziki czemu jest dua szansa, e wszystkie wyniki
bd dostpne mniej wicej w tym samym czasie.
java.util.concurrent.Callable<V> 5.0

V call()

Uruchamia zadanie, ktre zwraca wynik.


java.util.concurrent.Future<V> 5.0

V get()

V get(long time, TimeUnit unit)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

802

Java. Podstawy
Zwraca wynik, wczajc blokad, dopki nie jest on dostpny lub nie upynie
okrelona ilo czasu. Druga wersja zgasza wyjtek TimeoutException,
jeli zakoczy si niepowodzeniem.

boolean cancel(boolean mayInterrupt)

Prbuje anulowa wykonywanie zadania. Zadanie, ktre zostao ju uruchomione,


a ma parametr mayInterrupt ustawiony na true, zostanie przerwane. Jeli operacja
anulowania zakoczy si pomylnie, metoda ta zwraca warto true.

boolean isCancelled()

Zwraca warto true, jeli zadanie zostao anulowane przed ukoczeniem.

boolean isDone()

Zwraca warto true, jeli zadanie zostao ukoczone w normalny sposb, zostao
anulowane lub spowodowao wyjtek.
java.util.concurrent.FutureTask<V> 5.0

FutureTask(Callable<V> task)

FutureTask(Runnable task, V result)

Tworzy obiekt, ktry jest zarwno typu Future<V>, jak i Runnable.

14.9. Klasa Executors


Tworzenie nowego wtku jest nieco czasochonne, poniewa wymaga interakcji z systemem
operacyjnym. Jeli w programie tworzona jest dua liczba krtko yjcych wtkw, powinno
si stosowa pule wtkw. Pula taka zawiera pewn liczb nieaktywnych wtkw, ktre s
gotowe do dziaania. Przekazanie do niej obiektu Runnable powoduje wywoanie przez jeden
z wtkw metody run. Kiedy metoda ta zakoczy dziaanie, wtek nie zostaje zakoczony,
a przechodzi w stan oczekiwania na kolejne zadanie.
Dodatkowym powodem przemawiajcym za stosowaniem puli wtkw jest ch zmniejszenia
liczby wtkw wykonywanych jednoczenie. Zbyt dua ich liczba moe powanie spowolni
aplikacj, a nawet doprowadzi do awarii maszyny wirtualnej. Jeli w programie jest algorytm tworzcy du liczb wtkw, powinna si w nim znale take ustalona pula wtkw,
ograniczajca liczb wtkw dziaajcych jednoczenie.
W klasie Executors znajduje si kilka statycznych metod fabrycznych sucych do tworzenia puli wtkw (tabela 14.2).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

803

Tabela 14.2. Metody fabryczne klasy Executors


Metoda

Opis

newCachedThreadPool

W razie potrzeby tworzy nowe wtki. Nieaktywne wtki


s przetrzymywane przez 60 sekund.

newFixedThreadPool

Pula zawierajca ustalon liczb wtkw. Nieaktywne wtki


s zachowywane bezterminowo.

newSingleThreadExecutor

Pula skadajca si z jednego wtku wykonujcego zadania po kolei


(podobnie do wtku dystrybucji zdarze w Swing).

newScheduledThreadPool

Ustalona harmonogramowana pula wtkw. Zastpstwo


dla java.uti.Timer.

newSingleThreadScheduledExecutor

Harmonogramowana pula skadajca si z jednego wtku.

14.9.1. Pule wtkw


Przyjrzymy si dokadniej trzem pierwszym metodom z tabeli 14.2. Pozostae z nich zostay
opisane w podrozdziale 14.9.2, Planowanie wykonywania. Metoda newCachedThreadPool
tworzy pul wtkw, ktra wykonuje zadania natychmiast za pomoc jednego z wtkw
nieaktywnych, jeli taki jest, lub tworzc nowy wtek w przeciwnym przypadku. Metoda
newFixedThreadPool tworzy pul wtkw o ustalonym rozmiarze. Jeli zada jest wicej ni
wolnych wtkw, s one umieszczane w kolejce i wykonywane po zakoczeniu wczeniejszych zada. Metoda newSingleThreadExecutor tworzy pul skadajc si z jednego wtku,
ktry wykonuje zadania jedno po drugim. Wszystkie trzy opisane metody zwracaj obiekt
klasy ThreadPoolExecutor, ktra implementuje interfejs ExecutorService.
Obiekt Runnable lub Callable mona przekaza do ExecutorService za pomoc jednej z poniszych metod:
Future<?> submit(Runnable task)
Future<T> submit(Runnable task, T result)
Future<T> submit(Callable<T> task)

Pula wykona powierzone jej zadanie przy najbliszej sposobnoci. Metoda submit zwraca obiekt
typu Future, ktry zawiera informacje o stanie zadania.
Pierwsza z wymienionych metod zwraca do osobliwie wygldajcy typ Future<?>. Na rzecz
tego obiektu mona wywoa metody isDone, cancel lub isCancelled. Natomiast metoda get
w chwili ukoczenia zwraca warto null.
Druga wersja metody submit take przesya obiekt Runnable, a metoda get interfejsu Future
zwraca wynik operacji, gdy jest ju gotowy.
Trzecia wersja przesya obiekt Callable i zwrcony obiekt Future otrzymuje wynik oblicze,
gdy jest gotowy.
Po zakoczeniu pracy w puli wtkw naley wywoa metod shutdown. Inicjuje ona operacj
zamykajc pul. Egzekutor, ktry jest zamykany, nie przyjmuje adnych nowych zada.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

804

Java. Podstawy
Po zakoczeniu wszystkich zada wtki puli zostaj zakoczone. Istnieje take metoda
shutdownNow, ktra powoduje, e pula anuluje wszystkie jeszcze niezaczte zadania i prbuje przerwa aktualnie uruchomione.
Oto zestawienie dziaa, ktre naley wykona, aby uy puli wtkw:
1.

Wywoaj statyczn metod newCachedThreadPool lub newFixedThreadPool z klasy


Executors.

2. Przeka obiekty Runnable lub Callable za pomoc metody submit.


3. Wykorzystaj zwrcone obiekty Future, jeli chcesz mie moliwo anulowania
zada lub jeli przekaesz obiekty Callable.
4. Jeli nie chcesz przekazywa wicej zada, wywoaj metod shutdown.

Na przykad wczeniej prezentowany program tworzy du liczb krtkotrwaych wtkw


po jednym dla kadego katalogu. Program z listingu 14.12 wykonuje zadania pod kontrol
puli wtkw.
Listing 14.12. threadPool/ThreadPoolTest.java
package threadPool;
import java.io.*;
import java.util.*;
import java.util.concurrent.*;
/**
* @version 1.01 2012-01-26
* @author Cay Horstmann
*/
public class ThreadPoolTest
{
public static void main(String[] args) throws Exception
{
Scanner in = new Scanner(System.in);
System.out.print("Podaj katalog bazowy (np. /usr/local/jdk1.6.0/src): ");
String directory = in.nextLine();
System.out.print("Podaj sowo kluczowe (np. volatile): ");
String keyword = in.nextLine();
ExecutorService pool = Executors.newCachedThreadPool();
MatchCounter counter = new MatchCounter(new File(directory), keyword, pool);
Future<Integer> result = pool.submit(counter);
try
{

System.out.println(result.get() + " pasujcych plikw.");


}
catch (ExecutionException e)
{
e.printStackTrace();
}
catch (InterruptedException e)
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

}
pool.shutdown();

int largestPoolSize = ((ThreadPoolExecutor) pool).getLargestPoolSize();


System.out.println("Najwikszy rozmiar puli=" + largestPoolSize);

/**
* Zadanie liczce pliki w katalogu i jego podkatalogach, zawierajce dane sowo kluczowe
*/
class MatchCounter implements Callable<Integer>
{
private File directory;
private String keyword;
private ExecutorService pool;
private int count;
/**
* Tworzy obiekt typu MatchCounter.
* @param directory katalog, od ktrego ma si zacz szukanie
* @param keyword sowo kluczowe do znalezienia
* @param pool pula wtkw, do ktrej wysyane s zadania
*/
public MatchCounter(File directory, String keyword, ExecutorService pool)
{
this.directory = directory;
this.keyword = keyword;
this.pool = pool;
}
public Integer call()
{
count = 0;
try
{
File[] files = directory.listFiles();
List<Future<Integer>> results = new ArrayList<>();
for (File file : files)
if (file.isDirectory())
{
MatchCounter counter = new MatchCounter(file, keyword, pool);
Future<Integer> result = pool.submit(counter);
results.add(result);
}
else
{
if (search(file)) count++;
}
for (Future<Integer> result : results)
try
{
count += result.get();
}
catch (ExecutionException e)
{
e.printStackTrace();

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

805

806

Java. Podstawy

}
}
catch (InterruptedException e)
{
}
return count;

/**
* Przeszukuje plik w celu znalezienia danego sowa kluczowego.
* @param file plik do przeszukania
* @return warto true, jeli plik zawiera sowo kluczowe
*/

public boolean search(File file)


{
try
{
try (Scanner in = new Scanner(file))
{
boolean found = false;
while (!found && in.hasNextLine())
{
String line = in.nextLine();
if (line.contains(keyword)) found = true;
}
return found;
}
}
catch (IOException e)
{
return false;
}
}

Dla celw informacyjnych program drukuje rozmiar najwikszej puli. Informacja ta nie jest
dostpna za porednictwem interfejsu ExecutorService. Z tego powodu musielimy rzutowa
obiekt puli na klas ThreadPoolExecutor.
java.util.concurrent.Executors 5.0

ExecutorService newCachedThreadPool()

Zwraca pul wtkw, ktra w razie potrzeby tworzy wtki, i koczy te,
ktre s nieaktywne przez 60 sekund.

ExecutorService newFixedThreadPool(int threads)

Zwraca pul wtkw, ktra wykonuje zadania przy uyciu okrelonej liczby
wtkw.

ExecutorService newSingleThreadExecutor()

Zwraca egzekutor, ktry wykonuje zadania kolejno w jednym wtku.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

807

java.util.concurrent.ExecutorService 5.0

Future<T> submit(Callable<T> task)

Future<T> submit(Runnable task, T result)

Future<?> submit(Runnable task)

Przekazuje zadanie do wykonania.

void shutdown()

Zamyka usug. Koczy przekazane wczeniej zadania, ale nie przyjmuje nowych.
java.util.concurrent.ThreadPoolExecutor 5.0

int getLargestPoolSize()

Zwraca najwikszy rozmiar puli wtkw w czasie dziaania egzekutora.

14.9.2. Planowanie wykonywania


Interfejs ScheduledExecutorService zawiera metody suce do planowego i wielokrotnego
wykonywania zada. Tworzenie pul wtkw jest moliwe dziki uoglnieniu klasy java.util.
Timer. Metody newScheduledThreadPool i newSingleThreadScheduledExecutor klasy Execu
tors zwracaj obiekty implementujce interfejs ScheduledExecutorService.
Zadania Runnable i Callable mona zaplanowa do jednorazowego wykonania po uprzednim odoeniu ich na jaki czas. Zadanie Runnable mona take wykonywa w okrelonych
odstpach czasu. Szczegowe informacje na ten temat znajduj si w wycigach z API.
java.util.concurrent.Executors 5.0

ScheduledExecutorService newScheduledThreadPool(int threads)

Zwraca pul wtkw, ktra wykonuje zadania wedug harmonogramu w okrelonej


liczbie wtkw.

ScheduledExecutorService newSingleThreadScheduledExecutor()

Zwraca egzekutor, ktry wykonuje zadania wedug harmonogramu w jednym wtku.


java.util.concurrent.ScheduledExecutorService 5.0

ScheduledFuture<V> schedule(Callable<V> task, long time, TimeUnit unit)

ScheduledFuture<?> schedule(Runnable task, long time, TimeUnit unit)

Wykonuje dane zadanie po upywie okrelonej iloci czasu.

ScheduledFuture<?> scheduleAtFixedRate(Runnable task, long initialDelay,


long period, TimeUnit unit)

Ustawia harmonogram uruchamiania danego zadania w rwnych odstpach czasu,


zaczynajc po upywie pocztkowego opnienia (initialDelay).

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

808

Java. Podstawy

ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, long initialDelay,


long delay, TimeUnit unit)

Ustawia harmonogram uruchamiania danego zadania w okrelonych odstpach czasu.


Dugo czasu opnienia kolejnych wykona wynosi tyle, ile upyno czasu
od ukoczenia jednego wywoania do rozpoczcia kolejnego. Pierwsze wykonanie
nastpuje po upywie initialDelay czasu.

14.9.3. Kontrolowanie grup zada


Wiemy ju, jak wykorzystywa egzekutor w roli puli wtkw majcej na celu zwikszenie
szybkoci wykonywania zada. Czasami egzekutory s wykorzystywane do bardziej taktycznych celw, na przykad kontrolowania grup spokrewnionych zada. Na przykad wszystkie
zadania w egzekutorze mona zakoczy za pomoc jednego wywoania metody shutdownNow.
Metoda invokeAny przekazuje wszystkie obiekty z kolekcji obiektw typu Callable i zwraca
wynik ukoczonego zadania. Nie wiadomo, ktre to zadanie prawdopodobnie to, ktre
zostao ukoczone najwczeniej. Metody tej mona uy w algorytmie wyszukujcym, ktry
moe przyj kade rozwizanie. Wyobramy sobie na przykad, e chcemy rozoy na czynniki du liczb cakowit operacja wymagana do amania szyfru RSA. Mona przekaza kilka zada, z ktrych kade prbuje rozkadu przy uyciu liczb z innego zakresu. Kiedy
tylko ktrekolwiek z nich ma odpowied, dalsze obliczenia mona zatrzyma.
Metoda invokeAll przesya wszystkie obiekty Callable z kolekcji i zwraca list obiektw
Future, ktre reprezentuj rozwizania wszystkich zada. Wyniki te mona przetwarza
w nastpujcy sposb:
List<Callable<T>> tasks = . . .;
List<Future<T>> results = executor.invokeAll(tasks);
for (Future<T> result : results)
processFurther(result.get());

Wad tej metody jest to, e mona niepotrzebnie czeka, jeli pierwsze zadanie zajmuje zbyt
duo czasu. Duo lepiej byoby pobiera wyniki w takiej kolejnoci, w jakiej s udostpniane. Mona to osign za pomoc klasy ExecutorCompletionService.
Naley zacz od utworzenia w normalny sposb egzekutora. Nastpnie tworzymy obiekt
ExecutorCompletionService, do ktrego przekazujemy zadania. Obiekt ten zarzdza kolejk
blokujc zawierajc obiekty typu Future, w ktrych zapisywane s wyniki zada. W zwizku

z tym powysze obliczenia mona wykona szybciej za pomoc poniszego algorytmu:


ExecutorCompletionService service = new ExecutorCompletionService(executor);
for (Callable<T> task : tasks) service.submit(task);
for (int i = 0; i < tasks.size(); i++)
processFurther(service.take().get());
java.util.concurrent.ExecutorService 5.0

T invokeAny(Collection<Callable<T>> tasks)

T invokeAny(Collection<Callable<T>> tasks, long timeout, TimeUnit unit)

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

809

Wykonuje podane zadania i zwraca wynik jednego z nich. Druga z tych metod
zgasza wyjtek TimeoutException, jeli zostanie przekroczony dozwolony czas.

List<Future<T>> invokeAll(Collection<Callable<T>> tasks)

List<Future<T>> invokeAll(Collection<Callable<T>> tasks, long timeout,


TimeUnit unit)

Wykonuje dane zadania i zwraca wyniki ich wszystkich. Druga z tych metod
zgasza wyjtek TimeoutException, jeli zostanie przekroczony dozwolony czas.
java.util.concurrent.ExecutorCompletionService 5.0

ExecutorCompletionService(Executor e)

Tworzy obiekt typu ExecutorCompletionService, ktry przechowuje wyniki zada


okrelonego egzekutora.

Future<T> submit(Callable<T> task)

Future<T> submit(Runnable task, T result)

Przekazuje zadanie do egzekutora.

Future<T> take()

Usuwa nastpny wynik lub wcza blokad, jeli nie ma dostpnych adnych
wynikw.

Future<T> poll()

Future<T> poll(long time, TimeUnit unit)

Usuwa nastpny wynik lub zwraca warto null, jeli nie ma dostpnych adnych
wynikw. Druga wersja tej metody odczekuje okrelon ilo czasu.

14.9.4. Szkielet rozgazienie-zczenie


W niektrych aplikacjach uywanych jest wiele wtkw, z ktrych wikszo jest nieaktywna.
Przykadem jest serwer sieciowy obsugujcy kade poczenie w osobnym wtku. S te
aplikacje tworzce po jednym wtku dla kadego rdzenia procesora. Robi tak choby aplikacje wykonujce wymagajce obliczenia, np. przy przetwarzaniu grafiki albo filmw. Szkielet
rozgazienie-zczenie (ang. fork-join), ktry powsta w Java SE 7, suy do rozwizywania
problemw dotyczcych drugiego z wymienionych przypadkw. Wyobramy sobie, e mamy
zadanie przetwarzania, ktre mona naturalnie rozoy na podzadania:
if (rozmiarProblemu < prog)
rozwi problem bezporednio
else
{
podziel problem na czci
rekursywnie wykonaj kad cz
pocz wyniki
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

810

Java. Podstawy
Jednym z przykadw jest przetwarzanie obrazu. Obraz mona poprawi, przeksztacajc
jego grn i doln poow. Jeli ma si do dyspozycji wystarczajc liczb wolnych procesorw, operacje te mona wykonywa rwnoczenie (trzeba bdzie dodatkowo wykona
prac zwizan z poczeniem powek, ale to mao istotny szczeg).
My przeanalizujemy prostszy przykad. Przypumy, e chcemy si dowiedzie, ile elementw tablicy spenia pewien warunek. Dzielimy j na p, wykonujemy obliczenia dla kadej
z powek osobno, a nastpnie sumujemy wyniki.
Aby wykona nasze rekursywne obliczenia w odpowiedni sposb, utworzymy klas rozszerzajc klas RecursiveTask<T> (jeli wynik obliczenia jest typu T) lub RecursiveAction
(jeli nie ma wyniku). Przesonimy metod compute, aby generowaa i wywoywaa czci
zadania oraz czya ich wyniki.
class Counter extends RecursiveTask<Integer>
{
. . .
protected Integer compute()
{
if (to - from < THRESHOLD)
{
bezporednie rozwizanie problemu
}
else
{
int mid = (from + to) / 2;
Counter first = new Counter(values, from, mid, filter);
Counter second = new Counter(values, mid, to, filter);
invokeAll(first, second);
return first.join() + second.join();
}
}
}

Metoda invokeAll otrzymuje liczb zada i wcza blokad, dopki wszystkie one nie zostan
wykonane. Metoda join generuje wynik. Stosujemy j do wszystkich podzada, aby otrzyma sum.
Istnieje te metoda get do pobierania aktualnego wyniku, ale jest ona mniej atrakcyjna, poniewa moe zgasza kontrolowane wyjtki, ktrych nie moemy ponownie
zgasza w metodzie compute.

Peny kod rdowy przykadu jest przedstawiony na listingu 14.13.


Listing 14.13. forkJoin/forkJoinTest.java
package forkJoin;
import java.util.concurrent.*;
/**
* Program demonstrujcy szkielet rozgazienie-zczenie
* @version 1.00 2012-05-20

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

* @author Cay Horstmann


*/
public class ForkJoinTest
{
public static void main(String[] args)
{
final int SIZE = 10000000;
double[] numbers = new double[SIZE];
for (int i = 0; i < SIZE; i++) numbers[i] = Math.random();
Counter counter = new Counter(numbers, 0, numbers.length,
new Filter()
{
public boolean accept(double x) { return x > 0.5; }
});
ForkJoinPool pool = new ForkJoinPool();
pool.invoke(counter);
System.out.println(counter.join());
}
}
interface Filter
{
boolean accept(double t);
}
class Counter extends RecursiveTask<Integer>
{
public static final int THRESHOLD = 1000;
private double[] values;
private int from;
private int to;
private Filter filter;
public Counter(double[] values, int from, int to, Filter filter)
{
this.values = values;
this.from = from;
this.to = to;
this.filter = filter;
}
protected
{
if (to
{
int
for
{

Integer compute()
- from < THRESHOLD)
count = 0;
(int i = from; i < to; i++)

if (filter.accept(values[i])) count++;
}
return count;
}
else
{
int mid = (from + to) / 2;
Counter first = new Counter(values, from, mid, filter);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

811

812

Java. Podstawy
Counter second = new Counter(values, mid, to, filter);
invokeAll(first, second);
return first.join() + second.join();
}
}
}

Szkielet rozgazienie-zczenie wykorzystuje efektywny algorytm heurystyczny pozwalajcy


zrwnoway obcienie poszczeglnych wtkw, o nazwie podkradanie pracy (ang. work
stealing). Kady wtek roboczy ma kolejk dwukierunkow dla zada i podzadania umieszcza
na jej pocztku (tylko jeden wtek ma dostp do tego miejsca, wic nie ma potrzeby stosowa blokady). Gdy wtek jest nieaktywny, podkrada zadanie z koca innej kolejki dwukierunkowej. Jako e due podzadania s w ogonie, do podkradania dochodzi rzadko.

14.10. Synchronizatory
W pakiecie java.util.concurrent znajduje si kilka klas, ktre uatwiaj zarzdzanie zbiorami spokrewnionych ze sob zada zobacz tabela 14.3. Algorytmy te udostpniaj gotowe
rozwizania czsto spotykanych problemw zwizanych ze wspprac pomidzy wtkami.
Majc zestaw wsppracujcych ze sob wtkw dziaajcych wedug jednego z wzorcw,
naley zamiast we wasnym zakresie tworzy zbir blokad i warunkw uy jednej
z tych klas.

14.10.1. Semafory
Z zaoenia semafor suy do zarzdzania pewn liczb zezwole (ang. permit). Aby przej
obok semafora, wtek prbuje uzyska zezwolenie, wywoujc w tym celu metod acquire.
Liczba dostpnych zezwole jest ograniczona, co pozwala na kontrol liczby wtkw, ktre
mog przej dalej. Inne wtki mog wydawa zezwolenia za pomoc metody release
(w rzeczywistoci nie istniej adne obiekty zezwole, ich licznik jest po prostu przechowywany w semaforze). Jako e dostpna jest okrelona liczba zezwole, semafor ogranicza
liczb wtkw, ktre mog przej. Ponadto zezwolenie nie musi zosta zwolnione przez
wtek, ktry je uzyska. Kady wtek moe wyda dowoln liczb zezwole, a wic potencjalnie moe zwikszy ich liczb powyej pocztkowego limitu.
Semafory wynalaz w 1968 roku programista o nazwisku Edsger Dijkstra, ktry potrzebowa
mechanizmu synchronizacji. Wykaza on, e semafory mog by szybkie i s na tyle wszechstronne, i mog suy do rozwizania wielu czsto wystpujcych problemw zwizanych
z synchronizacj. W prawie kadej ksice na temat systemw operacyjnych znajduje si opis
implementacji kolejek ograniczonych wykorzystujcych semafory.
Oczywicie programici aplikacji nie powinni ponownie wynajdowa kolejek ograniczonych.
Semafory rzadko odpowiadaj typowym sytuacjom programistycznym.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

813

Tabela 14.3. Synchronizatory


Klasa

Dziaanie

Zastosowanie

CyclicBarrier

Pozwala zbiorowi wtkw odczeka,


a okrelona ich liczba osignie pewn
wspln barier, a nastpnie opcjonalnie
wykonuje akcj bariery.

Gdy pewna liczba wtkw musi si


zakoczy, zanim ich wyniki bd mogy
by uyte.

Phaser

Podobny do CyclicBarrier,
ale ze zmiennym licznikiem.

Wprowadzony w Java SE 7.

CountDownLatch

Pozwala zbiorowi wtkw odczeka,


a licznik zostanie zmniejszony do zera.

Kiedy jeden lub wicej wtkw musi


odczeka, a wystpi okrelona liczba
zdarze.

Exchanger

Umoliwia dwm wtkom wymienia


Kiedy dwa wtki dziaaj na dwch
si obiektami, jeli oba s do tej wymiany egzemplarzach tej samej struktury danych
gotowe.
jeden go zapenia, a drugi oprnia.

Semaphore

Pozwala zbiorowi wtkw odczeka,


a bd dostpne pozwolenia
na kontynuacj.

Do ograniczania liczby wtkw majcych


dostp do zasobu. Jeli liczba pozwole
wynosi jeden, blokada wtkw
jest zdejmowana, gdy inny wtek wyda
pozwolenie.

SynchronousQueue

Pozwala wtkowi na przekazanie obiektu


do innego wtku.

Do przesyania obiektw z jednego wtku


do innego, kiedy oba s na to gotowe,
bez jawnej synchronizacji.

14.10.2. Klasa CountDownLatch


Obiekt klasy CountDownLatch zmusza wtki do oczekiwania, a licznik dojdzie do zera. Zatrzask
ten jest jednorazowego uytku, to znaczy, e jeli licznik dojdzie do zera, nie mona go
zwikszy.
Przydatnym rodzajem takiego zatrzasku jest zatrzask z licznikiem o wartoci 1. Stanowi on
jednorazow bramk. Wtki s zatrzymywane przed bramk, dopki inny wtek nie ustawi
licznika na zero.
Wyobramy sobie na przykad zestaw wtkw, ktre do wykonania swoich zada potrzebuj pewnych danych inicjujcych. Wtki s uruchomione i czekaj przed bramk. Inny wtek
przygotowuje dane. Kiedy jest gotowy, wywouje metod countdown i wszystkie wtki kontynuuj swoje zadania.
Aby sprawdzi, kiedy wszystkie wtki zakoczyy swoje dziaania, mona uy kolejnego
zatrzasku. Naley zainicjowa go liczb wtkw. Kady wtek przed zakoczeniem dziaania
odejmuje jeden od licznika zatrzasku. Inny wtek, ktry zbiera wyniki tej pracy, czeka na
zatrzasku i przechodzi do dziaania, gdy wszystkie pozostae wtki zakocz swoje dziaanie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

814

Java. Podstawy

14.10.3. Bariery
Klasa CyclicBarrier suy do tworzenia obiektw nazywanych barierami (ang. barrier).
Wyobramy sobie kilka wtkw, z ktrych kady wykonuje porcj oblicze stanowic fragment jednej caoci. Kiedy wszystkie czci s gotowe, ich wyniki trzeba poczy. Kiedy
wtek zakoczy swoje zadanie, zatrzymuje si na barierze. Kiedy wszystkie wtki dotr do
bariery, zostaje ona otwarta i wtki mog kontynuowa dziaanie.
Oto szczegowa analiza tego problemu. Najpierw tworzymy barier, przekazujc do niej
liczb wtkw biorcych udzia w zadaniu:
CyclicBarrier barrier = new CyclicBarrier(nthreads);

Kady z wtkw wykonuje jakie dziaania i po ich zakoczeniu wywouje na rzecz bariery
metod await:
public void run()
{
doWork();
barrier.await();
. . .
}

Metoda await moe przyjmowa opcjonalny parametr czasowy:


barrier.await(100, TimeUnit.MILLISECONDS);

Jeli ktry z wtkw oczekujcych na barier zniknie, bariera zostaje zamana (wtek moe
znikn, kiedy wywoa metod await z ograniczeniem czasowym lub zostanie przerwany).
W takim przypadku metoda await wszystkich pozostaych wtkw zgasza wyjtek Broken
BarrierException. Metoda await oczekujcych wtkw zostaje natychmiast zakoczona.
Mona okreli opcjonaln akcj bariery, ktra bdzie wykonywana, kiedy wszystkie wtki
osign t barier:
Runnable barrierAction = . . .;
CyclicBarrier barrier = new CyclicBarrier(nthreads, barrierAction);

Akcja ta moe zbiera wyniki poszczeglnych wtkw.


Bariera jest cykliczna (ang. cyclic), poniewa po uwolnieniu wszystkich oczekujcych wtkw
mona jej uy ponownie. Rni si ona pod tym wzgldem od zatrzasku CountDownLatch,
ktry moe zosta uyty tylko jeden raz.
Klasa Phaser pozwala na wiksz elastyczno, umoliwiajc zmienianie liczby wtkw biorcych udzia w fazach.

14.10.4. Klasa Exchanger


Obiekty klasy Exchanger znajduj zastosowanie, gdy dwa wtki dziaaj na dwch egzemplarzach jednego bufora danych. Z reguy jeden z nich zapenia bufor, a drugi pobiera te dane.
Kiedy oba wtki s gotowe, wymieniaj si buforami.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

815

14.10.5. Kolejki synchroniczne


Kolejka synchroniczna jest mechanizmem pozwalajcym skojarzy w pary wtki producenckie
i konsumenckie. Gdy wtek wywoa metod put na obiekcie SynchronousQueue, zostaje on
zablokowany do czasu, a inny wtek wywoa metod take i odwrotnie. W odrnieniu od
obiektw klasy Exchanger, dane s przekazywane tylko w jednym kierunku od producenta do konsumenta.
Mimo e klasa SynchronousQueue implementuje interfejs BlockingQueue, to w istocie nie
jest kolejk nie zawiera adnych elementw i jej metoda size zawsze zwraca warto 0.

14.11. Wtki a biblioteka Swing


Na pocztku tego rozdziau napisalimy, e jednym z powodw uywania wtkw w programach jest usprawnienie moliwoci ich interakcji z uytkownikiem. Kiedy program ma
wykonywa jakie czasochonne obliczenia, powinno si uruchamia nowy wtek roboczy,
zamiast blokowa cay interfejs uytkownika.
Naley jednak cile kontrolowa, jakie zadania wykonuje wtek roboczy, poniewa, co moe
by pewnym zaskoczeniem, Swing nie jest bezpieczny dla wtkw. Manipulacja elementami interfejsu uytkownika w wielu wtkach moe doprowadzi do jego awarii.
Aby sprawdzi, na czym polega ten problem, uruchom program z listingu 14.14. Kliknicie
przycisku Zy powoduje uruchomienie nowego wtku. Jego metoda run drczy pole listy
rozwijalnej, dodajc do niej i usuwajc z niej losowe wartoci.
public void run()
{
try
{
while (true)
{
int i = Math.abs(generator.nextInt());
if (i % 2 == 0)
combo.insertItemAt(new Integer(i), 0);
else if (combo.getItemCount() > 0)
combo.removeItemAt(i % combo.getItemCount());
sleep(1);
}
catch (InterruptedException e) {}
}
}

Wyprbujmy ten program. Kliknij przycisk Zy. Kliknij kilkakrotnie pole listy. Porusz paskiem przewijania. Przesu okno. Jeszcze raz kliknij przycisk Zy. Poklikaj list rozwijaln.
W kocu powinien si pojawi komunikat o wyjtku (rysunek 14.8).
Co si stao? Kiedy do listy dodawany jest element, uruchamia ona zdarzenie aktualizacji
ekranu. Wtedy do akcji wchodzi kod wywietlajcy komponenty na ekranie, ktry wczytuje

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

816

Java. Podstawy

Rysunek 14.8.
Komunikaty
o wyjtkach
w oknie konsoli

aktualny rozmiar pola listy i przygotowuje si do wywietlenia wartoci. Jednak wtek roboczy
nie przestaje dziaa to od czasu do czasu powoduje zmniejszenie licznika wartoci na
licie. Kod odpowiedzialny za wywietlanie komponentw spodziewa si wicej wartoci
w modelu, ni ich rzeczywicie jest, i prosi o nieistniejce wartoci, co powoduje powstanie
wyjtku ArrayIndexOutOfBoundsException.
Sytuacji tej mona by byo unikn, umoliwiajc programicie zablokowanie pola listy podczas jego wywietlania. Jednak projektanci biblioteki Swing postanowili nie robi nic w kierunku bezpieczestwa dla wtkw, i to z dwch powodw. Synchronizacja jest czasochonna,
a nikt nie chcia, aby Swing by jeszcze wolniejszy. Ponadto zesp pracujcy nad Swingiem wzi pod uwag dowiadczenia innych zespow, ktre pracoway nad zestawami narzdzi do budowy bezpiecznych dla wtkw interfejsw. Programici wykorzystujcy tego
typu narzdzia mieli problemy z opanowaniem wymaga zwizanych z synchronizacj
i czsto tworzyli programy, ktre atwo wpaday w zakleszczenia.

14.11.1. Uruchamianie czasochonnych zada


Uywajc wtkw w poczeniu z bibliotek Swing, trzeba dostosowa si do dwch prostych
zasad:

Jeli jakie dziaanie jest czasochonne, naley je wykona w osobnym wtku


roboczym nigdy w wtku dystrybucji zdarze.

Nie naley operowa na komponentach Swing w adnym innym wtku ni wtek


dystrybucji zdarze.

Podstaw do sformuowania pierwszej z wymienionych zasad atwo zrozumie. Jeli w wtku


dystrybucji zdarze bdzie wykonywane czasochonne dziaanie, aplikacja bdzie wygldaa
na zawieszon, poniewa nie bdzie moga reagowa na adne inne zdarzenia. Wtek ten
nie powinien nigdy wywoywa adnych metod wejcia-wyjcia, ktre mog zablokowa si
na stae, ani metody sleep (jeli musisz odczeka okrelon ilo czasu, uyj zdarze zegara).
Druga z wymienionych zasad w programowaniu Swing jest czsto nazywana zasad jednego
wtku. Szerzej na jej temat piszemy w sekcji 14.11.3.
Wydaje si, e zasady te s ze sob sprzeczne. Wyobramy sobie, e uruchamiamy osobny
wtek do wykonania czasochonnego zadania. Zazwyczaj podczas pracy wtku chcemy pokaza

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

817

postp za pomoc aktualizacji interfejsu uytkownika. Po ukoczeniu zadania aktualizujemy


GUI jeszcze jeden raz. Nie moemy jednak operowa na komponentach Swing z poziomu
wtku. Na przykad aktualizacja paska postpu lub etykiety tekstowej nie moe polega na
zmianie wartoci w wtku.
Rozwizaniem tego problemu s dwie metody uytkowe, za pomoc ktrych mona w dowolnym wtku doda dowolne akcje do kolejki zdarze. Wyobramy sobie na przykad, e co
jaki czas chcemy w wtku aktualizowa etykiet, aby pokaza postp operacji. Nie moemy
wywoa metody label.setText bezporednio w tym wtku.
W zamian naley uy metod invokeLater i invokeAndWait z klasy EventQueue, aby wywoanie
to zostao wykonane w wtku dystrybucji zdarze.
Oto wymagane czynnoci. Kod Swing umieszczamy w metodzie run klasy implementujcej
interfejs Runnable. Nastpnie tworzymy obiekt tej klasy i przekazujemy go do statycznej metody
invokeLater lub invokeAndWait. Ponisza przykadowa procedura aktualizuje tekst etykiety:
EventQueue.invokeLater(new
Runnable()
{
public void run()
{
label.setText("ukoczono " + percentage + "%");
}
});

Metoda invokeLater zwraca warto natychmiast po tym, jak zdarzenie zostanie wysane do
kolejki zdarze. Metoda run jest wykonywana asynchronicznie. Metoda invokeAndWait czeka,
a metoda run zostanie wykonana.
W przypadku aktualizacji etykiety postpu bardziej odpowiednia jest metoda invokeLater.
Uytkownicy raczej przedkadaj szybko dziaania wtku roboczego nad precyzj wskanika postpu.
Obie te metody wykonuj metod run w wtku dystrybucji zdarze nie jest tworzony aden
nowy wtek.
Listing 14.14 przedstawia program demonstrujcy bezpieczn modyfikacj zawartoci listy
rozwijalnej za pomoc metody invokeLater. Kliknicie przycisku Dobry powoduje, e wtek
wstawia i usuwa liczby, ale modyfikacje te odbywaj si w wtku dystrybucji zdarze.
Listing 14.14. swing/SwingThreadTest.java
package swing;
import
import
import
import

java.awt.*;
java.awt.event.*;
java.util.*;
javax.swing.*;

/**
* Program udowadniajcy, e wtek dziaajcy rwnolegle z wtkiem dystrybucji zdarze moe
* powodowa bdy w komponentach Swing

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

818

Java. Podstawy
* @version 1.23 2007-05-17
* @author Cay Horstmann
*/
public class SwingThreadTest
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new SwingThreadFrame();
frame.setTitle("SwingThreadTest");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka majca dwa przyciski suce do zapeniania listy w osobnym wtku. Przycisk Dobry
* wykorzystuje kolejk zdarze, a Zy modyfikuje list bezporednio.
*/
class SwingThreadFrame extends JFrame
{
public SwingThreadFrame()
{
final JComboBox<Integer> combo = new JComboBox<>();
combo.insertItemAt(Integer.MAX_VALUE, 0);
combo.setPrototypeDisplayValue(combo.getItemAt(0));
combo.setSelectedIndex(0);
JPanel panel = new JPanel();
JButton goodButton = new JButton("Dobry");
goodButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
new Thread(new GoodWorkerRunnable(combo)).start();
}
});
panel.add(goodButton);
JButton badButton = new JButton("Zy");
badButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
new Thread(new BadWorkerRunnable(combo)).start();
}
});
panel.add(badButton);
panel.add(combo);
add(panel);

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

pack();
}
}
/**
* Klasa modyfikujca list rozwijan poprzez dodanie do niej i usunicie z niej losowych liczb. Moe to
* spowodowa bdy,poniewa metody listy rozwijalnej nie s synchronizowane, przez co wtek roboczy
* i wtek dystrybucji zdarze uzyskuj dostp do tej listy.
*/
class BadWorkerRunnable implements Runnable
{
private JComboBox<Integer> combo;
private Random generator;
public BadWorkerRunnable(JComboBox<Integer> aCombo)
{
combo = aCombo;
generator = new Random();
}
public void run()
{
try
{
while (true)
{
int i = Math.abs(generator.nextInt());
if (i % 2 == 0) combo.insertItemAt(i, 0);
else if (combo.getItemCount() > 0) combo.removeItemAt(i %
combo.getItemCount());
Thread.sleep(1);
}
}
catch (InterruptedException e)
{
}
}
}
/**
* Klasa modyfikujca list rozwijan poprzez dodanie do niej i usunicie z niej losowych liczb.
* Aby unikn uszkodzenia tej listy, operacje edycji s przesyane do wtku dystrybucji zdarze.
*/
class GoodWorkerRunnable implements Runnable
{
private JComboBox<Integer> combo;
private Random generator;
public GoodWorkerRunnable(JComboBox<Integer> aCombo)
{
combo = aCombo;
generator = new Random();
}
public void run()
{

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

819

820

Java. Podstawy
try
{
while (true)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
int i = Math.abs(generator.nextInt());
if (i % 2 == 0) combo.insertItemAt(i, 0);
else if (combo.getItemCount() > 0) combo.removeItemAt(i
% combo.getItemCount());
}
});
Thread.sleep(1);
}
}
catch (InterruptedException e)
{
}
}
}
java.awt.EventQueue 1.1

static void invokeLater(Runnable runnable) 1.2

Po przetworzeniu oczekujcych zdarze powoduje wykonanie metody run


obiektu klasy implementujcej interfejs Runnable w wtku dystrybucji zdarze.

static void invokeAndWait(Runnable runnable) 1.2

Po przetworzeniu oczekujcych zdarze powoduje wykonanie metody run


obiektu klasy implementujcej interfejs Runnable w wtku dystrybucji zdarze.
Metoda ta blokuje do czasu, a metoda run zostanie zakoczona.

static boolean isDispatchThread() 1.2

Zwraca warto true, jeli metoda dziaa w wtku dystrybucji zdarze.

14.11.2. Klasa SwingWorker


Kiedy uytkownik wyda polecenie, ktrego wykonanie zajmuje duo czasu, zazwyczaj do jego
wykonania uruchamiamy nowy wtek. Jak pamitamy z poprzedniego podrozdziau, w wtku
tym do aktualizacji interfejsu uytkownika powinno si uy metody EventQueue.invokeLater.
Klasa SwingWorker pozwala zmniejszy ilo mudnej pracy zwizanej z implementacj zada
wykonywanych w tle.
Program przedstawiony na listingu 14.15 posiada polecenia adowania pliku tekstowego
i anulowania tego procesu. Aplikacj t naley testowa na pliku o duych rozmiarach, jak
The Count of Monte Cristo znajdujcym si w katalogu gutenberg razem z katalogami
z kodem. Plik jest adowany w osobnym wtku. W trakcie tej operacji polecenie Otwrz

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

821

w menu Plik jest nieaktywne, a Anuluj aktywne (rysunek 14.9). Po wczytaniu kadej linijki
tekstu aktualizowany jest licznik w pasku stanu. Po ukoczeniu adowania polecenie Otwrz
staje si z powrotem aktywne, polecenie Anuluj nieaktywne, a w pasku stanu wywietla si
napis Zakoczono.
Rysunek 14.9.
adowanie pliku
w osobnym wtku

Listing 14.15. swingWorker/SwingWorkerTest.java


package swingWorker;
import
import
import
import
import
import

java.awt.*;
java.awt.event.*;
java.io.*;
java.util.*;
java.util.List;
java.util.concurrent.*;

import javax.swing.*;
/**
* Program demonstrujcy wtek roboczy wykonujcy potencjalnie czasochonne zadanie
* @version 1.1 2007-05-18
* @author Cay Horstmann
*/
public class SwingWorkerTest
{
public static void main(String[] args) throws Exception
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new SwingWorkerFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
/**
* Ramka majca obszar tekstowy pokazujcy zawarto pliku tekstowego, menu pozwalajce otworzy plik
* i anulowa proces otwierania pliku oraz wiersz stanu pokazujcy postp adowania pliku
*/

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

822

Java. Podstawy
class SwingWorkerFrame extends JFrame
{
private JFileChooser chooser;
private JTextArea textArea;
private JLabel statusLine;
private JMenuItem openItem;
private JMenuItem cancelItem;
private SwingWorker<StringBuilder, ProgressData> textReader;
public static final int TEXT_ROWS = 20;
public static final int TEXT_COLUMNS = 60;
public SwingWorkerFrame()
{
chooser = new JFileChooser();
chooser.setCurrentDirectory(new File("."));
textArea = new JTextArea(TEXT_ROWS, TEXT_COLUMNS);
add(new JScrollPane(textArea));
statusLine = new JLabel(" ");
add(statusLine, BorderLayout.SOUTH);
JMenuBar menuBar = new JMenuBar();
setJMenuBar(menuBar);
JMenu menu = new JMenu("Plik");
menuBar.add(menu);
openItem = new JMenuItem("Otwrz");
menu.add(openItem);
openItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
// Wywietlenie okna dialogowego wyboru pliku
int result = chooser.showOpenDialog(null);
// Jeli plik zosta wybrany, zostanie on ustawiony jako ikona etykiety
if (result == JFileChooser.APPROVE_OPTION)
{
textArea.setText("");
openItem.setEnabled(false);
textReader = new TextReader(chooser.getSelectedFile());
textReader.execute();
cancelItem.setEnabled(true);
}
}
});
cancelItem = new JMenuItem("Anuluj");
menu.add(cancelItem);
cancelItem.setEnabled(false);
cancelItem.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent event)
{
textReader.cancel(true);
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

});
pack();
}
private class ProgressData
{
public int number;
public String line;
}
private class TextReader extends SwingWorker<StringBuilder, ProgressData>
{
private File file;
private StringBuilder text = new StringBuilder();
public TextReader(File file)
{
this.file = file;
}
// Ponisza metoda jest wykonywana w wtku roboczym nie operuje na komponentach Swing
@Override
public StringBuilder doInBackground() throws IOException, InterruptedException
{
int lineNumber = 0;
try (Scanner in = new Scanner(new FileInputStream(file)))
{
while (in.hasNextLine())
{
String line = in.nextLine();
lineNumber++;
text.append(line);
text.append("\n");
ProgressData data = new ProgressData();
data.number = lineNumber;
data.line = line;
publish(data);
Thread.sleep(1); // Test operacji anulowania, nie ma potrzeby robienia tego w
// swoich programach
}
}
return text;
}
// Ponisze metody s wykonywane w wtku dystrybucji zdarze
@Override
public void process(List<ProgressData> data)
{
if (isCancelled()) return;
StringBuilder b = new StringBuilder();
statusLine.setText("" + data.get(data.size() - 1).number);
for (ProgressData d : data)
{
b.append(d.line);
b.append("\n");
}

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

823

824

Java. Podstawy
textArea.append(b.toString());
}
@Override
public void done()
{
try
{
StringBuilder result = get();
textArea.setText(result.toString());
statusLine.setText("Zakoczono");
}
catch (InterruptedException ex)
{
}
catch (CancellationException ex)
{
textArea.setText("");
statusLine.setText("Anulowano");
}
catch (ExecutionException ex)
{
statusLine.setText("" + ex.getCause());
}
cancelItem.setEnabled(false);
openItem.setEnabled(true);
}
};
}

Program ten demonstruje typowy wygld interfejsu uytkownika podczas wykonywania


zadania w tle:

Po kadym etapie pracy nastpuje aktualizacja interfejsu uytkownika w celu


pokazania postpu.

Po zakoczeniu pracy w interfejsie dokonywana jest ostateczna zmiana.

Dziki klasie SwingWorker zadanie to jest atwe do wykonania. Wystarczy przedefiniowa


metod doInBackground, aby wykonywaa czasochonne dziaania, i co jaki czas wywoywa
metod publish majc na celu pokazanie postpu. Metoda ta jest wykonywana w wtku roboczym. Metoda publish powoduje wykonanie metody process w wtku dystrybucji zdarze. Jej zadaniem jest obsuga danych dotyczcych postpu. Po zakoczeniu pracy w wtku
dystrybucji zdarze wywoywana jest metoda done pozwalajca zakoczy aktualizacj
interfejsu uytkownika.
Aby wykona jakie dziaania w wtku roboczym, naley utworzy obiekt klasy SwingWorker
(kady taki obiekt moe by uyty tylko jeden raz). Nastpnie naley wywoa metod execute.
Metod t z reguy wywouje si na rzecz wtku dystrybucji zdarze, ale nie jest to wymogiem.
Z zaoenia obiekt klasy SwingWorker powinien zwrci jaki wynik. Dlatego klasa SwingWor
ker<T, V> implementuje interfejs Future<T>. Wynik ten mona pobra za pomoc metody

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

825

get tego interfejsu. Poniewa metoda ta wcza blokad, dopki wynik nie jest dostpny, nie
naley wywoywa jej bezporednio po metodzie execute. Dobrym rozwizaniem jest wywo-

ywanie jej dopiero wwczas, gdy wiadomo, e praca zostaa zakoczona. Zazwyczaj metod
get wywouje si w metodzie done (wywoanie metody get nie jest konieczne czasami
wystarczy przetworzenie danych postpu).
Zarwno porednie dane postpu, jak i kocowy wynik mog by dowolnego typu. Typy te
s okrelone w klasie SwingWorker jako parametry typowe. Klasa SwingWorker<T, V> tworzy
wynik typu T i dane postpu typu V.
Do anulowania zadania w toku suy metoda cancel z interfejsu Future. Kiedy zadanie jest
anulowane, metoda get zgasza wyjtek CancellationException.
Jak ju wiemy, wywoanie w wtku roboczym metody publish spowoduje wywoanie metody
process na rzecz wtku dystrybucji zdarze. Aby zwikszy wydajno, wyniki zwrcone przez
kilka wywoa metody publish mona zgrupowa w jednym wywoaniu metody process.
Metoda process odbiera obiekt List<V> zawierajcy wszystkie wyniki porednie.
Uyjemy tej techniki do wczytywania pliku tekstowego. Okazuje si, e komponent JTextArea
jest niezbyt szybki. Dodawanie linii tekstu z duego pliku tekstowego (jak The Count of
Monte Cristo) zajmuje duo czasu.
Aby pokaza uytkownikowi, e co si dzieje, w pasku stanu bdziemy wywietla liczb
wczytanych linijek tekstu. Dlatego dane postpu skadaj si z aktualnej liczby linii tekstu
oraz aktualnej linii tekstu. Dane te pakujemy w prostej klasie wewntrznej:
private class ProgressData
{
public int number;
public String line;
}

Ostateczny wynik stanowi tekst, ktry zosta wczytany do obiektu typu StringBuilder.
W zwizku z tym klasa, ktrej potrzebujemy, to SwingWorker<StringBuilder, ProgressData).
Metoda doInBackground wczytuje dane z pliku wiersz po wierszu. Po kadym wierszu wywoujemy metod publish publikujc numer i zawarto aktualnej linii.
@Override public StringBuilder doInBackground() throws IOException, InterruptedException
{
int lineNumber = 0;
Scanner in = new Scanner(new FileInputStream(file));
while (in.hasNextLine())
{
String line = in.nextLine();
lineNumber++;
text.append(line);
text.append("\n");
ProgressData data = new ProgressData();
data.number = lineNumber;
data.line = line;
publish(data);
Thread.sleep(1); // Test operacji anulowania, nie ma potrzeby robienia tego w swoich programach.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

826

Java. Podstawy
}
return text;
}

Po kadej linii tekstu usypiamy wtek na jedn milisekund, aby mona byo spokojnie przetestowa anulowanie. Oczywicie w programach przeznaczonych do uytku nie naley tego
robi, aby ich nie spowalnia. Jeli postawimy przed tym wierszem symbol komentarza,
zauwaymy, e tekst ksiki wczytuje si do szybko i jest tylko kilka wikszych aktualizacji interfejsu uytkownika.
Aby program dziaa pynniej, pole tekstowe mona aktualizowa w wtku roboczym.
Nie jest to jednak moliwe w przypadku wszystkich komponentw Swing. Prezentujemy ogln technik, polegajc na aktualizacji wszystkich komponentw w wtku
dystrybucji zdarze.

Metoda process ignoruje wszystkie linie tekstu poza ostatni oraz czy wszystkie linie
w jednej aktualizacji obszaru tekstowego.
@Override public void process(List<ProgressData> data)
{
if (isCancelled()) return;
StringBuilder b = new StringBuilder();
statusLine.setText("" + data.get(data.size() - 1).number);
for (ProgressData d : data) { b.append(d.line); b.append("\n"); }
textArea.append(b.toString());
}

W metodzie done obszar tekstowy jest aktualizowany kompletnym tekstem, a polecenie Anuluj
zostaje wyczone.
Warto zwrci uwag na sposb uruchomienia obiektu klasy SwingWorker w suchaczu zdarze elementu menu Otwrz.
Ta prosta technika pozwala na wykonywanie czasochonnych zada przy zachowaniu wraliwoci interfejsu uytkownika.
javax.swing.SwingWorker<T, V> 6

abstract T doInBackground()

T metod naley przedefiniowa, aby wykonywaa zadanie w tle i zwracaa wynik


swojego dziaania.

void process(List<V> data)

T metod naley przedefiniowa, aby przetwarzaa porednie dane przetwarzania


w wtku dystrybucji zdarze.

void publish(V... data)

Przesya porednie dane postpu do wtku dystrybucji zdarze. Naley j wywoywa


w metodzie doInBackground.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia 14.

Wielowtkowo

827

void execute()

Planuje wykonanie obiektu klasy SwingWorker w wtku roboczym.

SwingWorker.StateValue getState()

Sprawdza stan obiektu SwingWorker PENDING, STARTED lub DONE.

14.11.3. Zasada jednego wtku


Kada aplikacja w Javie zaczyna si w metodzie main, ktra dziaa w wtku gwnym. W programach opartych na Swingu wtek ten yje jednak bardzo krtko. Rozplanowuje konstrukcj
interfejsu uytkownika w wtku dystrybucji zdarze i koczy dziaanie. Po utworzeniu interfejsu wtek dystrybucji zdarze przetwarza powiadomienia o zdarzeniach, takie jak wywoania
metod actionPerformed czy paintComponent. Inne wtki, jak ten, ktry wysya zdarzenia do
kolejki zdarze, dziaaj w tle, ale s niewidoczne dla programisty.
We wczeniejszej czci tego rozdziau wprowadzilimy zasad jednego wtku, mwic,
e na obiektach Swing naley operowa wycznie w wtku dystrybucji zdarze. Przeanalizujemy t zasad nieco bardziej szczegowo.
Od zasady jednego wtku jest kilka wyjtkw.

Suchaczy zdarze mona bezpiecznie dodawa i usuwa w kadym wtku.


Oczywicie metody suchaczy s wywoywane w wtku dystrybucji zdarze.

Niektre metody Swing s bezpieczne wtkowo. W dokumentacji API s one


oznaczone specjalnym zdaniem: This method is thread safe, although most Swing
methods are not (Metoda ta jest bezpieczna wtkowo, mimo e wikszo
metod biblioteki Swing nie jest). Najbardziej przydatne metody z tej grupy to:
JTextComponent.setText
JTextArea.insert
JTextArea.append
JTextArea.replaceRange
JComponent.repaint
JComponent.revalidate

Metody repaint uywalimy ju wielokrotnie, natomiast metoda revalidate jest


znacznie mniej popularna. Jej zadaniem jest wymuszenie uoenia komponentu
po zmianie zawartoci. W bibliotece AWT suy do tego metoda validate (aby wymusi
uoenie komponentu JFrame, konieczne jest wywoanie metody validate JFrame jest
komponentem, ale nie typu JComponent).

W przeszoci zasada jednego wtku bya mniej restrykcyjna. Kady wtek mg tworzy
komponenty, ustawia ich wasnoci i dodawa je do kontenerw, jeli aden z tych komponentw nie by realizowany. Komponent jest realizowany, kiedy moe odbiera zdarzenia rysowania lub walidacji. W zwizku z tym problem zaczyna si w chwili wywoania na
rzecz komponentu metody setVisible(true) lub pack (!) bd w momencie dodania go do
zrealizowanego kontenera.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

828

Java. Podstawy
Tamta wersja zasady jednego wtku bya bardzo dogodna. Pozwalaa na utworzenie GUI
w metodzie main, a nastpnie wywoanie metody setVisible(true) na rzecz ramki najwyszego poziomu. Nie byo potrzeby kopotliwego planowania obiektw implementujcych
interfejs Runnable na wtku dystrybucji zdarze.
Niestety niektrzy programici komponentw nie zwaali na misterno pierwotnej zasady
jednego wtku. Uruchamiali dziaania na rzecz wtku dystrybucji zdarze bez sprawdzenia,
czy komponent zosta zrealizowany. Na przykad wywoanie metod setSelectionStart lub
setSelectionEnd na rzecz komponentu JTextComponent spowoduje, e przesunicie karetki
zostanie wykonane w wtku dystrybucji zdarze, chocia komponent jest niewidoczny.
Problemy te mona by byo odnale i naprawi, ale projektanci Swinga wybrali atwiejsze
rozwizanie. Orzekli, e bezpieczny dostp do komponentw mona uzyska wycznie
w wtku dystrybucji zdarze. Dlatego interfejs uytkownika musi by konstruowany w wtku
dystrybucji zdarze przy uyciu metody EventQueue.invokeLater, ktr ogldalimy we
wszystkich przykadowych programach.
Oczywicie istnieje mnstwo programw, ktre wci dziaaj zgodnie ze star wersj zasady
jednego wtku, czyli inicjuj interfejs uytkownika w gwnym wtku. W aplikacjach tych
istnieje ryzyko, e inicjacja interfejsu spowoduje dziaania w wtku dystrybucji zdarze bdce
w konflikcie z dziaaniami w wtku gwnym. Jak pisalimy w rozdziale 7., nikt nie chce by
tym nieszczliwcem, ktremu si to przytrafi i ktry bdzie musia powici mnstwo
czasu na odnalezienie bdu. Dlatego najlepiej cile trzyma si zasady jednego wtku.
W tym miejscu koczy si pierwszy tom Core Java. Opisano w nim podstawy jzyka Java
oraz niektre fragmenty jego API, ktre s potrzebne w wikszoci projektw programistycznych. Mamy nadziej, e podobaa Ci si podr przez podstawowe zagadnienia zwizane z Jav i e udao Ci si tu znale przydatne wiadomoci. Dodatkowe informacje na
temat programowania sieciowego, zaawansowanego programowania AWT i Swing, bezpieczestwa aplikacji czy internacjonalizacji zostay zawarte w drugim tomie.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Sowa kluczowe
Javy
Sowo kluczowe

Opis

abstract

Abstrakcyjna klasa lub metoda

assert

Lokalizacja wewntrznych bdw programu

boolean

Typ logiczny

3.

break

Przerywa dziaanie instrukcji switch lub ptli

3.

byte

Omiobitowy typ cakowitoliczbowy

3.

case

Klauzula instrukcji switch

3.

catch

Klauzula bloku try przechwytujca wyjtek

char

Typ znaku Unicode

3.

class

Definicja klasy

4.

const

Nieuywane

continue

Przekazuje sterowanie na koniec ptli

3.

default

Domylna klauzula instrukcji switch

3.

do

Grna cz ptli do-while

3.

double

Liczby zmiennoprzecinkowe o podwjnej precyzji

3.

else

Klauzula else instrukcji if

3.

enum

Wyliczenie

3.

extends

Definicja klasy nadrzdnej innej klasy

4.

final

Staa, klasa lub metoda, ktrej nie mona przesoni

5.

finally

Zawsze wykonywana cz bloku try

11.

float

Zmiennoprzecinkowa liczba o pojedynczej precyzji

3.

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia
5.
11.

11.

830

Java. Podstawy

Sowo kluczowe

Opis

for

Rodzaj ptli

goto

Nieuywane

if

Instrukcja warunkowa

3.

implements

Definicja interfejsu (lub interfejsw) implementowanego przez klas

6.

import

Import pakietu

4.

instanceof

Sprawdzanie, czy obiekt jest egzemplarzem danej klasy

5.

int

32-bitowa liczba cakowita

3.

interface

Typ abstrakcyjny zawierajcy metody, ktre klasa moe


zaimplementowa

6.

long

64-bitowa liczba cakowita

3.

native

Metoda zaimplementowana przez hosta

new

Przydzielenie pamici dla nowego obiektu lub nowej tablicy

3.

null

Referencja null

3.

package

Pakiet, do ktrego naley klasa

4.

private

Cecha dostpna tylko dla metod okrelonej klasy

4.

protected

Cecha dostpna tylko dla metod okrelonej klasy, jej potomkw


i innych klas z tego samego pakietu

5.

public

Cecha dostpna dla wszystkich metod z wszystkich klas

4.

return

Zwraca warto z metody

3.

short

16-bitowa liczba cakowita

3.

static

Cecha waciwa tylko swojej klasie, nie jej obiektom

3.

strictfp

Stosowanie cisych regu dotyczcych oblicze na liczbach


zmiennoprzecinkowych

2.

super

Obiekt lub konstruktor nadklasy

5.

switch

Instrukcja wyboru

3.

synchronized

Metoda lub blok kodu, ktry jest niepodzielny dla wtku

this

Niejawny argument metody lub konstruktor tej klasy

throw

Zgoszenie wyjtku

11.

throws

Wyjtki, ktre metoda moe zgosi

11.

transient

Oznaczanie danych, ktre nie powinny by trwae

try

Blok kodu przechwytujcego wyjtki

void

Oznaczenie metody niezwracajcej adnej wartoci

volatile

Zapewnienie spjnego dostpu do pola przez wiele wtkw

while

Ptla

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Rozdzia
3.

11. (tom II)

14.
4.

1. (tom II)
11.
3.
14.
3.

Skorowidz
A
abstrakcja, 214
ActiveX, 26, 35, 540
adnotacja, 111, 640
@SafeVarargs, 643
@SuppressWarnings, 643, 648
adres URL, 527, 546
agregacja, 135
akceleratory, 439
akcesorium podgldu, 499
akcesory, 142
akcje, 355, 373
aktualizacje, updates, 39
aktualizowanie preferencji, 558
aktywno komponentu, 375
algorytm, 132, 718
binarySearch, 723
obliczania kodu mieszajcego, 226
QuickSort, 121, 721
znajdujcy najwikszy element, 718
algorytmy
sortujce, 720
w klasie Collections, 724
alokacja listy tablicowej, 235
analiza
funkcjonalnoci klasy, 252
MVC, 397
obiektw w czasie dziaania programu, 257
animacja piki, 736742
animowane gify, 546
anonimowe klasy wewntrzne, 289, 300, 363
API
Javy, 33
JNLP, 525
Logging, 591

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Preferences, 555, 595


String, 83
aplet, 28, 34, 53, 54
aplet, 511, 533
Jmol, 29
WelcomeApplet, 53
aplety
implementacja, 533
komunikacja, 547
konwersja programw, 536
obrazy, 546
pliki audio, 546
rodowisko dziaania, 547
uruchamianie, 535
aplikacja
ImageViewer, 51
Java Web Start, 519
WebStartCalculator, 528
aplikacje
graficzne, 50
serwerowe, 29
architektura, framework, 706
kolekcji, 706, 710
model-widok-kontroler, 396
argument, 61
ASCII, 65
asercje, 587
wyczanie, 588
zastosowania, 589, 590
asocjacja, 135
atak, 25
atrybut
classid, 540
codebase, 541
codetype, 540

832

Java. Podstawy

atrybuty
pozycjonujce, 540
znacznika applet, 537540
znacznika param, 541, 542
autoboxing, 32
automatyczna konwersja typw, 32
automatyczne opakowywanie, 241
AWT, Abstract Window Toolkit, 314

B
bariera cykliczna, 814
bariery, 814
bazowy katalog drzewa pakietu, 187
bezpieczestwo, 25, 35
typw, 639, 649
wtkw, 775
biaa ksiga Javy, 22
biblioteka, 33
AWT, 314, 387
fdlibm, 74
IFC, 314
Java2D, 332, 333
JFC, 314
kolekcji, 666, 707
refleksyjna, 247
STL, 666
Swing, 315, 815
biblioteki
struktur danych, 666
zabezpiecze, 25
bit, 729
blok, 98
inicjujcy, 175
try-catch, 250, 578, 585
try-finally, 578
blokada, 762, 769
jawna, 770
odczytu, 783
uczciwa, 764
wewntrzna, 770
zapisu, 784
bloki synchronizowane, 774
blokowanie po stronie klienta, 774, 775
bd
AssertionError, 587
pomyki o jeden, 719
ThreadDeath, 752
typu, 650
typu cannot read, 46
bdy
danych wejciowych, 564
kompilacji, 49, 153, 181, 629, 646
programisty, 568

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

przydzielania pamici, 25
urzdze, 564
w Eclipse, 49
w kodzie, 565
wejcia-wyjcia, 565
wewntrzne, 568
wykonawcze, 644
zabezpiecze, 25
zaokrglania, 64, 333

C
catch, 250
cechy
jzyka, 22, 33
komponentu, 393
certyfikat
bezpieczestwa, 524
niebezpieczny, 525
wasny, 524
chwytanie typu wieloznacznego, 655
ciao metody, 60
czasochonne zadania, 816
czcionki, 343
bezszeryfowe, 346
nazwy, 343
nazwy logiczne, 344
PostScript type 1, 345
styl, 345
TrueType, 345
wysoko, 346

D
dane, 195
binarne, 26
wejciowe, 89
wyjciowe, 91
debuger, 28, 621
debuger JSwat, 623
debugowanie, 609
debugowanie aplikacji z GUI, 614
definiowanie
klasy, 148
klasy oglnej, 630
kolorw, 340
suchacza, 356
staej klasowej, 69
wtku, 745
wyjtkw kontrolowanych, 567
zmiennej, 66, 68
zmiennej obiektowej, 138
zmiennej tablicowej, 117

Skorowidz
dekompilowanie pliku, 761
dekrementacja, 71
delegacja, 265
delegacja zdarze, 356
demony, 753
dezaktywacja elementw menu, 441
diagram
dziedziczenia klasy, 215, 534
dziedziczenia zdarze AWT, 387
hierarchii wyjtkw, 565
klas, 136
przepywu sterowania, 100106, 110
dodawanie
akcji do menu, 375
elementu do listy powizanej, 678
elementu do mapy, 702
ikony, 435
klasy do pakietu, 182
klauzuli throws, 96
komponentw, 401
dokumentacja, 194
API, 8587
JSR, 627
zaoe, 590
dopasowywanie typw, 659
dopenienie, 459
wewntrzne, 452
zewntrzne, 452
dostp
chroniony, 219
do apletu, 539
do elementw kolekcji, 672, 708
do elementw listy tablicowej, 236
do elementu tablicy, 117
do formatera, 781
do komponentw, 473
do pakietw, 518
do plikw lokalizacyjnych, 516
do pliku, 96
do pliku JNLP, 521
do pl, 155
do pl generycznych, 637
do prywatnych pl nadklasy, 202
do sekcji krytycznej, 762
do stanu obiektu, 289
do usugi, 526
do wartoci, 243
do wza drzewa, 555
do zasobw lokalnych, 525
do zmiennej warunkowej, 775
do zmiennych finalnych, 297
jednoczesny wtkw, 761
swobodny, 719, 723
wtkw do struktury danych, 757

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

drukowanie, 31
drukowanie informacji o klasie, 252
drzewo katalogw, 42
due liczby, big numbers, 62
dymki, tooltips, 446
dyrektywa
#include, 182
import, 90
dziaanie
kontrolera, 395
metody transfer, 761
dziedziczenia klasy Applet, 534
dziedziczenie, inheritance, 133, 199, 268
hierarchia, 206
klasy abstrakcyjne, 214
klasy finalne, 211
metody finalne, 211
ochrona dostpu, 219
polimorfizm, 207
pomidzy klasami par, 649
rzutowanie, 212
typw oglnych, 649
wizanie dynamiczne, 209
wielokrotne, 279
dzielenie
cakowitoliczbowe, 69
modulo, 69
zmiennoprzecinkowe, 69
dzienniki, 591
dzienniki rotacyjne, 599

E
Eclipse, 44, 47
edycja
kodu rdowego, 48
cieki dostpu, 39
edytor tekstowy
Emacs, 44
JEdit, 44
TextPad, 44
edytowalna lista rozwijalna, 423
EE, Enterprise Edition, 38
egzemplarz klasy, 132
elementy menu, 432
aktywowanie, 440
dezaktywowanie, 440
ikony, 435
pola wyboru, 436
przeczniki, 436
elementy tablicy, 116
eliminacja wywoa funkcji, 27
elipsa, 335

833

834

Java. Podstawy

etykiety, 459
HTML, 409
komponentw, 408
ewolucja Javy, 32

F
figury
2D, 332
geometryczne, 333
filtr
plikw, 496, 497, 505
rekordw, 600
firma
Oracle, 32, 34
Sun Fellow, 30
Sun Microsystems, 25, 34
format
binarny liczby, 63
JNLP, 519
Unicode, 26
XML, 556
formatery, 600
formatowanie
danych wyjciowych, 91
daty, 95
funkcja unexpected, 569
funkcje
czysto wirtualne, 216
matematyczne, 73
sieciowe, 24
skadowe, 60

G
GC, Garbage Collector, 702
generowanie
dokumentacji, 194
obiektw klas oglnych, 646
generyczne
klasy, 629
listy tablicowe, 233
generyczny kod tablicowy, 261
graficzny interfejs uytkownika, GUI, 28, 57, 313,
391, 614
grafika, 313
grupa, 754
przyciskw radiowych, 416
wtkw, 755
zada, 808
GTK, 316

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

harmonogram
wykonywania wtkw, 750
zada, 696
haso, 90, 410
hermetyzacja, 133, 155
hierarchia
dziedziczenia, 206
dziedziczenia interfejsu Type, 660
dziedziczenia klasy Component, 400
dziedziczenia klasy JFrame, 321
interfejsw, 278
wyjtkw, 566, 586
zdarze, 387
historia Javy, 30
HTML, 33

I
IDE, 39, 47
identyfikacja klas, 134
IFC, Internet Foundation Classes, 314
ikony, 435
ikony komunikatw, 475
implementacja
apletw, 533
ArrayList, 644
interfejsu, 271, 273
klasy Bank, 770
klasy oglnej, 629
kolejki, 667
import
klas, 180
statyczny, 182
indeks, 123
indeks argumentu, 95
informacje
o klasie, 252
o typach, 28
o typach czasu wykonywania, 248
o typach generycznych, 659
o typach obiektw, 248
o uruchomionym programie, 613
o zdarzeniach, 602
inicjalizacja
pl, 172
pl statycznych, 176
pl wartociami domylnymi, 171
tablic, 118
z podwjn klamr, 302
zmiennej obiektowej, 138
zmiennych, 68

Skorowidz
inkrementacja, 71
instalacja
bibliotek, 41
dokumentacji, 41
filtru, 600
JDK, 38, 39
programw, 42
instrukcja
break, 111114
break z etykiet, 112
case, 111
continue, 113
do-while, 104
for, 77
goto, 111
if, 100, 113
if-else, 100
if-else if, 102
import, 180, 182
lock, 762
return, 578
switch, 109111
try, 580, 581
while, 103
instrukcje
sterujce, 98
warunkowe, 98, 109, 113
zoone, 99
interfejs
Action, 373, 379, 433
ActionListener, 286, 300, 357, 364, 373, 389
AdjustmentListener, 389
AppletContext, 547
AutoCloseable, 580
BasicService, 527
BlockingDeque<E>, 793
BlockingQueue<E>, 792
ButtonModel, 397, 398, 417
Callable, 797
Callable<V>, 801
Cloneable, 282
Collection, 668672, 679, 707, 711
Collection<E>, 672
Comparable, 272, 279, 308, 634, 654, 689
Comparator<T>, 690, 693
Condition, 769771
Delayed, 792
Deque<E>, 695
Enumeration, 670, 727
ExecutorService, 803, 808
FileFilter, 497
Filter, 600
FocusListener, 389
Formattable, 92

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Future, 798
Future<V>, 801
GenericArrayType, 660, 664
InvocationHandler, 307, 311
ItemListener, 389
Iterable, 117, 669
Iterator, 669, 677, 680
Iterator<E>, 674
klasy, 146
LayoutManager, 469
LayoutManager2, 469
LinkedList<E>, 683
List, 684, 708, 711
List<E>, 682
ListIterator, 677, 708
ListIterator<E>, 683
Lock, 764, 769, 771, 783
KeyListener, 389
Map, 707
Map<K, V>, 700
MenuListener, 441
MouseListener, 381, 383, 389
MouseMotionListener, 381, 383, 389
MouseWheelListener, 389
nasuchu, 356
NavigableMap, 709
NavigableMap<K, V>, 716
NavigableSet, 709, 712
NavigableSet<E>, 694, 716
ParameterizedType, 660, 664
PersistenceService, 527
PersistentService, 528
Powered, 278
Queue, 666, 667
Queue<E>, 695
RandomAccess, 708, 721
Runnable, 797
ScheduledExecutorService, 807
Set, 708
Shape, 333
SortedMap, 709
SortedMap<K, V>, 716
SortedSet, 709, 712
SortedSet<E>, 693, 716
SwingConstants, 278, 408
Thread.UncaughtExceptionHandler, 754
TransferQueue, 788
TransferQueue<E>, 793
Type, 660
TypeVariable, 660, 664
WildcardType, 660, 664
WindowFocusListener, 389
WindowListener, 369, 372, 389
WindowStateListener, 372, 389

835

836

Java. Podstawy

interfejsu, 219, 265


hierarchia, 278
implementacja, 271, 273
metody, 278
sprzenie zwrotne, 286
zmienne, 277
interfejsy
architektury kolekcji, 707
kolekcyjne, 665, 707, 719
nasuchowe, 389
nasuchujce AWT, 389
przenone, 27
uytkownika, 34, 391
znacznikowe, 282
interlinia, 346
interpreter, 27, 184
iterator jako parametr, 727
iteratory, 669, 670

J
JAR, Java Archive, 512
Java look and feel, 315
Java Micro Edition, 23
Java Plug-in, 540
Java Runtime System, 26
Java Web Start, 511, 519
JavaBeans, 247
JavaFX, 317
jawna inicjalizacja pl, 172
JDK, Java Development Kit, 37
jednostki kodowe, 66, 81
jzyk
Algol, 165
C, 25
C#, 28, 34
C++, 23, 24
HTML, 33
J#, 28
J++, 28
Java, 22
JavaScript, 35
UML, 136
Visual Basic, 23, 137
jzyki
interpretowane, 34
obiektowe, 24
proceduralne, 24
JFC, Java Foundation Classes, 314
JIT, just-in-time compiler, 27
JNLP, Java Network Launch Protocol, 519
JRE, Java Runtime Environment, 38
JSR, Java Specification Requests, 627

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

K
kalendarz, 139
kalkulator, 403, 521
karta
HSB, 506
RGB, 506
Swatches, 506
katalog
bazowy drzewa pakietu, 187
bin, 39
com, 183
gutenberg, 820
src, 43
klas
coupling, 135
diagramy, 136
dziedziczenie, 133, 200
identyfikacja, 134
implementacja interfejsu, 271
komentarze, 191
konstruktory, 137, 152
metody, 133
metody prywatne, 157
metody statyczne, 160
nadklasy, 200
plik rdowy, 189
podklasy, 200
pola statyczne, 159
predefiniowanie, 137
projektowanie, 195
relacje, 135
rozszerzanie, 133
pola stae, 158
cieka, 187
klasa, 58, 132
ExampleFileView, 499
AbstractAction, 374, 377
AbstractButton, 419, 434436, 440
AbstractCollection, 672
AbstractList, 726
AbstractQueue, 668
AbstractSequentialList, 723
AbstractSet, 223
AccessibleObject, 257, 261
ActionMap, 376
AnonymousInnerClassTest, 301
Applet, 533, 537, 545, 549
AppletContext, 540, 548, 549
Array, 262
ArrayAlg, 663
ArrayBlockingQueue<E>, 791
ArrayDeque, 694
ArrayDeque<E>, 696

Skorowidz
ArrayDequeue, 668
ArrayList, 234236, 240, 628, 642, 674, 684
ArrayList<T>, 650
ArrayListTest, 238
Arrays, 118, 121, 123
AtomicInteger, 777
AWTEvent, 387
BallRunnable, 742
BasicButtonUI, 398
BasicService, 528, 531
bazowa Object, 133, 220
BigDecimal, 64, 114, 116
BigInteger, 114, 115
BigIntegerTest, 115
BitSet, 726, 729
BlockingQueueTest, 789
BorderFactory, 419, 421
BorderLayout, 401, 402
BounceFrame, 737
BuggyButtonTest, 622
ButtonFrame, 360
ButtonGroup, 417, 418
ButtonModel, 417, 418
ButtonUIListener, 398
Calendar, 140
CalendarTest, 145
CheckBoxTest, 415
CircularArrayQueue, 668
Class, 233, 248251, 255, 261, 658
Class<T>, 658, 663
CloneTest, 284
Collections, 679, 711, 715, 724
Color, 340, 342, 343
ColorAction, 360, 362
Component, 322, 325, 332, 343, 399, 408
ConcurrentHashMap, 794
ConcurrentHashMap<K, V> 5.0, 795
ConcurrentLinkedQueue<E>, 795
ConcurrentSkipListMap, 794
ConcurrentSkipListSet<E>, 795
Console, 90, 91
ConsoleHandler, 600, 607
Constructor, 250252, 256, 658
ConstructorTest, 177
Container, 362, 399
CopyOfTest, 263
CountDownLatch, 813
Cursor, 381
CyclicBarrier, 814
Date, 139, 140
DateFormatSymbols, 144, 148
DateInterval, 638
Dimension, 330
Double, 276

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

DrawTest, 337
Ellipse2D, 335
Ellipse2D.Double, 340
Employee, 148, 151, 163, 184, 639
EmployeeSortTest, 275
EmployeeTest, 149
Enum, 246
EnumMap, 704
EnumMap<K extends Enum<K>, V>, 706
EnumSet, 704
EnumSet<E extends Enum<E>>, 706
EnumTest, 246
EOFException, 570
EqualsTest, 230
Error, 565
EventHandler, 364, 365
EventObject, 363, 387
EventTracer, 616
Exception, 251, 565, 583
Exchanger, 814
Executors, 802
ExtendedService, 527
Field, 252, 256, 258, 261, 265
File, 497
FileContents, 531
FileFilter, 504
FileHandler, 600, 607
FileInputStream, 567
FileNameExtensionFilter, 505
FileOpenService, 526, 532
FileSaveService, 532
FileView, 497, 505
Filter, 609
FlowLayout, 400
Font, 345, 349
FontMetrics, 351
FontParamApplet, 541
FontRenderContext, 346
FontTest, 348
ForkJoinTest, 811
Formatter, 600, 609
Frame, 318, 326
FutureTask<V>, 802
FutureTest, 799
GenericReflectionTest, 661
Graphics, 332, 343, 350, 353
Graphics2D, 332, 333, 343, 351
GraphicsDevice, 325, 617
GraphicsEnvironment, 344, 620
GregorianCalendar, 139143, 146, 654
GridBagConstraints, 454, 458
GridLayout, 399, 405
GroupLayout, 459, 463, 466
Handler, 598, 607

837

838

Java. Podstawy

klasa
HashMap<K, V>, 701
HashSet, 684, 686
HashSet<E>, 687
Hashtable, 709, 726
IdentityHashMap, 705
IdentityHashMap<K, V>, 706
ImageIcon, 327, 351
ImagePreviewer, 499
ImageTest, 352
InnerClassTest, 292
InputEvent, 386
InputMap, 376
Integer, 242, 276, 308
Iterator, 290
JApplet, 533
JButton, 361, 397
JCheckBox, 415
JCheckBoxMenuItem, 436
JColorChooser, 505, 509
JComboBox, 425, 725
JComponent, 328, 347, 351, 379, 408, 419, 438
JDialog, 484, 488
JEditorPane, 412
JFileChooser, 495, 503
JFrame, 318, 321, 331, 434, 484
klasa JLabel, 408, 499
klasa JList, 425
JMenu, 433
JMenuBar, 432
JMenuItem, 434, 440
JOptionPane, 288, 474, 481, 484
JPanel, 330
JPasswordField, 406, 410
JPopupMenu, 437
JRadioButton, 418
JRadioButtonMenuItem, 436
JScrollPane, 413
JSlider, 426, 431, 640
JTextArea, 406, 410, 412
JTextComponent, 406
JTextField, 406, 408
JToolBar, 445, 447
KeyStroke, 375, 379
LayoutManager, 472
Line2D.Double, 340
LineBorder, 422
LineMetrics, 347, 350
LinkedBlockingQueue<E>, 792
LinkedHashMap, 702, 704
LinkedHashMap<K, V>, 705
LinkedHashSet, 702
LinkedHashSet<E>, 705
LinkedList, 290, 668, 675, 684, 694, 713

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

LinkedListQueue, 668
LinkedListTest, 681
LinkedTransferQueue, 788
ListIterator, 679
Lock, 762
Logger, 605
LogManager, 595
LogRecord, 609
LookAndFeelInfo, 368
Manager, 232
ManagerTest, 205
MapTest, 699
Math, 73, 74
MenuListener, 441
Method, 252, 265, 663
MethodTableTest, 266
Modifier, 252, 256
MouseEvent, 380, 386
MouseHandler, 383
MouseMotionHandler, 383
MouseMotionListener, 381
Object, 220
ObjectAnalyzer, 258
ObjectAnalyzerTest, 259
PackageTest, 183, 184
Pair, 303, 637, 641, 648, 655
Pair<T>, 659
PairTest1, 631
PairTest2, 635
PairTest3, 656
ParallelGroup, 468
ParamTest, 169
PasswordChooser, 490
Paths, 97
PersistenceService, 532
PersonTest, 217
PlafFrame, 368
Point2D, 335
Point2D.Double, 340
Preferences, 556, 560
PreferencesFrame, 558
PrintWriter, 97
PriorityBlockingQueue<E>, 792
PriorityQueue, 697
PriorityQueueTest, 697
Properties, 550555, 709, 728
PropertiesTest, 551
Proxy, 307, 311
ProxyTest, 309
Rectangle2D, 334, 335
Rectangle2D.Double, 334, 339
Rectangle2D.Float, 334, 339
RectangularShape, 335, 339
RecursiveTask<T>, 810

Skorowidz
ReentrantLock, 762764, 783
ReentrantReadWriteLock, 783
ReflectionTest, 253
ResourceBundle, 596
ResourceTest, 517
Robot, 617, 618
Runnable, 746
RuntimeException, 566568, 583
Scanner, 89, 90, 97
SequentialGroup, 468
ServiceManager, 531
SetTest, 686
ShuffleTest, 721
SimpleDateFormat, 781
SimpleFrame, 319
SimpleFrameTest, 318
Singleton, 645
SizedFrameTest, 324
SoftBevelBorder, 421, 422
SortedMap<K, V>, 701
SQLException, 576
Stack, 709, 729
StackTraceElement, 581, 583
StackTraceTest, 582
StaticInnerClassTest, 305
StaticTest, 162
StreamHandler, 598
StrictMath, 74
String, 77, 83, 86, 211
StringBuilder, 86, 88
SwingThreadTest, 818
SwingUtilities, 494
SwingWorker, 820, 824, 826
SwingWorkerTest, 821
SynchronousQueue, 815
System, 43, 554
SystemColor, 341
TalkingClock, 289, 295297
Thread, 647, 746, 749753
ThreadGroup, 754, 755
ThreadLocal, 781
ThreadLocal<T>, 782
ThreadPoolTest, 804
Throwable, 251, 565, 570, 581, 583
TimePrinter, 291295
Timer, 286288
TimerTest, 287
ToolBarTest, 447
Toolkit, 288, 323, 327, 386
TraceHandler, 307
TransferRunnable, 780
TreeMap<K, V>, 701
TreeSet, 688, 691

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

TreeSet<E>, 688, 694


TreeSetTest, 691
UIManager, 368
Vector, 234, 684, 709, 726
WeakHashMap, 702
WeakHashMap<K, V>, 705
Window, 322, 326
WindowAdapter, 370
WindowEvent, 372
klasy
abstrakcyjne, 214, 216, 279
adaptacyjne, adapter class, 369
anonimowe, 300
bazowe, 200, 279
blokad, 783
finalne, 211
generyczne, 234
graniczne, 636
kolekcyjne, 627, 672, 674, 709, 726
kontenerowe, 709
macierzyste, 200
modelowe, 397
niezmienne, 158
oglne, generic class, 629
osonowe, 241
pochodne, 200
podpisywane cyfrowo, 25
pomocnicze, 453, 781
potomne, 200
proxy, 306, 311
publiczne, 180
specjalne, 702
statyczne, 303
surowe, 237
szablonowe, 632
w architekturze kolekcji, 710
wewntrzne, 271, 289
anonimowe, 300
bezpieczestwo, 296
dostp do stanu obiektu, 289
dostp do zmiennych finalnych, 297
lokalne, 296
obsuga zdarze, 362
prawa dostpu, 296
referencja do klasy zewntrznej, 293
referencja do obiektu zewntrznego, 291
reguy skadniowe, 293
skadnia, 289
statyczne, 303
wyjtkw, 570
wyliczeniowe, 245
zagniedone, 290
zdarzeniowe AWT, 387
klasyfikacja wyjtkw, 565

839

840

Java. Podstawy

klauzula
catch, 572, 748
finally, 576, 578
throws, 96, 567
try, 572
klawiatura, 375
klawisze specjalne, 380
klonowanie obiektw, 280, 285
klucz URL, 528
klucze, 698
klucze nalece do wza, 560
kod
bajtowy, 26
bdu, 570
kod generyczny, 630
kod maszynowy, 26
kod mieszajcy, hash code, 225, 684, 687
kod oglny, 635
kod wyjcia, exit code, 60
kodowanie
Unicode, 65, 67
UTF-16, 66, 82
kolejka, queue, 666
ArrayBlockingQueue, 788
DelayQueue, 788
Deque, 694
LinkedBlockingQueue, 787
PriorityBlockingQueue, 788
Queue, 694
kolejki
blokujce, 786
dostpu, 472
priorytetowe, 696
synchroniczne, 815
kolejno
dostpu, 703
dostpu do komponentw, 473
ogranicze, 637
kolekcja, 117, 665
par, 698
wtkw, 754
kolekcje
bezpieczne wtkowo, 794, 796
ograniczone, 668
uporzdkowane, 677, 708
w bibliotece, 675
kolizja nazw, 180
kolizje, 685
kolor, 340
kolor ta, 341, 507
komentarze, 61
do klas, 191
do metod, 191
do pakietw, 194
do pl, 192

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

dokumentacyjne, 190
oglne, 192
komparator, 690
kompilacja
programu, 44
w czasie rzeczywistym, 26
kompilator, 25, 45, 776
czasu rzeczywistego, 34
javac, 188
JIT, 27, 212
komponenty
Swing, 391510
tekstowe, 411
kompresja ZIP, 512
komunikacja
midzy apletami, 540, 547
midzyprocesowa, 736
komunikat, 592
o bdzie, 46, 50, 569
o wyjtkach, 816
konektor UML, 136
konfiguracja
komponentw, 319
menedera dziennikw, 598
projektu, 48
konflikt metod, 648
konkatenacja, 78
konsola, 44
konstruktor, 137, 152
bezargumentowy, 172
domylny, 172
kopiujcy, 140
przeciony, 172
wirtualny, 250
konstruktory
klasy FileHandler, 607
klasy HashSet<E>, 687
klasy TreeMap<K, V>, 701
klasy TreeSet<E>, 694
kontekst
graficzny, 328
urzdzenia, 328
kontener, 330, 399
kontrola
dostpu, 290
nazw, 290
typw, 627
kontroler, controller, 394
konwersja
acucha na liczb, 242
pomidzy kolekcjami a tablicami, 718
programw na aplety, 536
tablic, 718
typw, 650
typw numerycznych, 74

Skorowidz
koczenie dziaania programu, 60
kopie
acucha, 80
obrazu, 352
kopiowanie
gbokie, 281
obiektw, 280
pytkie, 281
tablicy, 119
zmiennej tablicowej, 119
koszty uzyskania certyfikatu, 524
kowariantne typy zwrotne, 209, 639
kubeek, bucket, 685
kursory, 382
kwalifikator .this, 294

L
licencja GPL, 34
liczba
klikni, 381
parametrw, 244
liczby
cakowite, 62
zmiennoprzecinkowe, 64
linia
bazowa, 346, 347
dolna pisma, 346
grna pisma, 346
lista
ArrayList, 117
kluczy, 556, 698
modyfikowalna, 721
rozwijalna, combo box, 423
wtkw, 779
listy
cykliczne, 666, 668
dwukierunkowe, 25, 238, 674
powizane, 668, 674, 680
tablicowe, 234, 236, 684
surowe, 239
z typem, 240
lokalizacja, 143, 596
komunikatw, 596
pliku, 546
lokalne klasy wewntrzne, 289, 296

adowanie
klas, 307
pliku w osobnym wtku, 821
zasobw, 516

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

841

acuch
_blank, 548
dziedziczenia, 206
null, 81
prompt, 91
pusty, 81
testowy, 407
wyjtkw, 575
acuchy, 77
czenie, 78
modyfikowanie, 79
porwnywanie, 79
skadanie, 86
wspdzielenie, 79
zmienialne, mutable, 80
czenie narastajce, 27

M
makro assert, 588
manifest, 512
mapa, 697
akcji, 376
HashMap, 698
haszowa, 795
wejcia, 376
wasnoci, property map, 550, 553, 728
mapy klawiaturowe, 376
maska bitowa, 380
maszyna wirtualna, JVM, 26, 514
opcja -verbose, 612
opcja -Xlint, 612
opcja -Xprof, 614
ME, Micro Edition, 38
meneder
dziennikw, 595
zabezpiecze, 522
menu, 432
menu podrczne, pop-up menu, 437
metadane, 32
metoda, 133
accept, 504
acquire, 812
actionPerformed, 286, 291, 357, 368, 414, 433
add, 142, 237, 399, 433, 629, 677
addActionListener, 364
addAll, 629, 672, 682
addBall, 737, 742
addChangeListener, 426
addChoosableFileFilter, 504
addComponent, 467
addContainerGap, 468
addFirst, 683
addGap, 467

842

Java. Podstawy

metoda
addGroup, 467
addItem, 423, 426
addLast, 683
addPropertyChangeListener, 373
addSeparator, 434, 447
addSuppressed, 583
addWindowListener, 370
akcesora get, 141
and, 730
andNot, 730
append, 87, 413
appendCodePoint, 88
Arrays.hashCode, 227
Arrays.toString, 230
asList, 711
await, 766, 769, 783, 814
awaitUninterruptibly, 783
beep, 289
binarySearch, 123, 308, 722
BorderFactory, 421
brighter, 341
call, 801
cancel, 802
canRead, 532
canWrite, 532
cast, 658
ceiling, 694
charAt, 83
checkedCollection, 713
clear, 672, 730
clone, 280284
close, 179, 580, 600, 607, 762
codePointAt, 83
codePointCount, 84
compare, 276, 690, 693
compareTo, 83, 246, 272274, 299, 634, 653,
693, 720
Component.show, 320
config, 605
console, 91
contains, 672, 680, 686
containsAll, 672, 673
containsKey, 700
containsValue, 700
copy, 724
copyArea, 352, 354
copyOf, 123
countdown, 813
create, 365
createCompoundBorder, 422
createCustomCursor, 382, 386
createEmptyBorder, 421
createEtchedBorder, 421

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

createFont, 345
createLineBorder, 421
createLoweredBevelBorder, 421
createMatteBorder, 421
createParallelGroup, 467
createRaisedBevelBorder, 421
createScreenCapture, 618
createTitledBorder, 422
darker, 341
decrementAndGet, 777
metoda delay, 621
metoda delete, 88
deriveFont, 345, 350
destroy, 537
disjoint, 725
divide, 115
doInBackground, 824826
draw, 333, 340
drawImage, 353
drawString, 329, 341, 347, 351
elements, 728
endsWith, 83
ensureCapacity, 236
entering, 600, 605
entrySet, 700
equals, 79, 83, 124, 221, 242, 648, 705
equalsIgnoreCase, 83
execute, 827
exiting, 600, 605
fill, 340, 343, 724
fillMenu, 725
finalize, 179
fine, 605
finer, 605
finest, 605
firstKey, 701
floor, 694
flush, 600, 607
Font.createFont, 345
format, 609
formatMessage, 609
formatTo, 92
forName, 248, 251
frame.setUndecorated, 320
frequency, 725
get, 142, 236, 261, 264, 628, 682
getActionCommand, 363, 388, 417
getActionMap, 379
getActualTypeArguments, 664
getAllItems, 726
getAncestorOfClass, 494
getApplet, 540
getAppletContext, 547, 549
getAppletInfo, 545

Skorowidz
getApplets, 548, 549
getAudioClip, 547
getAutoCreateContainerGaps, 467
getAutoCreateGaps, 467
getAvailableFontFamilyNames, 344
getBackground, 343
getBounds, 664
getCause, 583
getCenterX, 339
getClass, 222, 248, 641
getClassName, 368, 584
getClickCount, 380, 386
getCodeBase, 528, 531
getColor, 343, 510
getColumns, 408
getComponentPopupMenu, 438
getConstructor, 658, 659
getConstructors, 252, 255
getContentPane, 327, 331
getDeclardeFields, 261
getDeclaredConstructor, 658
getDeclaredConstructors, 252, 256
getDeclaredField, 261
getDeclaredFields, 252, 255, 258
getDeclaredMethods, 252, 255
getDeclaringClass, 256
getDefaultScreenDevice, 621
getDefaultToolkit, 288, 323, 327
getDelay, 788, 792
getDescent, 350
getDescription, 505
getDocumentBase, 546
getDouble, 258, 610
getEnumConstants, 658
getExceptionTypes, 256
getExtendedState, 326
getFamily, 349
getField, 261
getFields, 252, 255, 261
getFileName, 583
getFilter, 607
getFirst, 637, 652, 683
getFirstDayOfWeek, 143
getFont, 350, 408
getFontMetrics, 347, 351
getFontName, 349
getFontRenderContext, 346, 351
getForeground, 343
getFormatter, 607
getGenericComponentType, 664
getGenericInterfaces, 663
getGenericParameterTypes, 663
getGenericReturnType, 663
getGenericSuperclass, 663

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

getHandlers, 606
getHead, 609
getHeight, 347
getHonorsVisibility, 467
getIcon, 409, 505
getIconImage, 326
getImage, 327, 547
getInheritsPopupMenu, 438
getInputMap, 376, 379
getInputStream, 526, 531
getInstalledLookAndFeels, 368
getKey, 701
getKeyStroke, 375
getLast, 683
getLeading, 350
getLength, 262, 264
getLevel, 606, 607
getLineMetrics, 347, 350
getLineNumber, 584
getLocalGraphicsEnvironment, 344
getLogger, 605
getLoggerName, 608
getLowerBounds, 664
getMaxX, 339
getMessage, 571, 608
getMethodName, 584
getMethods, 255
getMethodsi, 252
getMillis, 608
getMinX, 339
getModifiers, 252, 256
getModifiersEx, 381, 386
getModifiersExText, 386
getMonths, 148
getName, 150, 249, 349, 505, 664
getNewState, 372
getOldState, 372
getOutputStream, 526, 532
getOwnerType, 664
getPaint, 343
getParameter, 541, 545
getParameterInfo, 546
getParameters, 608
getParameterTypes, 256
getParent, 606
getPassword, 410
getPoint, 386
getPredefinedCursor, 381
getPreferredSize, 330
getProperties, 550, 554
getProperty, 553, 728
getProxyClass, 311
getRawType, 664
getResource, 516

843

844

Java. Podstawy

metoda
getResourceBundle, 608
getResourceBundleName, 608
getReturnType, 256
getRootPane, 494
getSalary, 214, 265
getScreenSize, 323, 327
getSelectedFile, 504
getSelectedItem, 423426
getSelectedObjects, 417
getSelection, 417, 418
getSequenceNumber, 609
getServiceNames, 531
getShortMonths, 148
getShortWeekdays, 144, 148
getSource, 388, 424
getSourceClassName, 608
getSourceMethodName, 608
getStackTrace, 581, 583
getState, 752
getStringBounds, 346
getSuperclass, 659
getSuppressed, 581
getTail, 609
getText, 406, 409
getThreadID, 609
getThrown, 608
getTitle, 322, 326
getTotalBalance, 764
getType, 252
getTypeDescription, 505
getTypeParameters, 663
getUpperBounds, 664
getUseParentHandlers, 607
getValue, 373, 379, 701
getWeekdays, 148
getWidth, 334, 335, 339, 346
getX, 339, 386
getY, 386
hashCode, 225, 684, 706
hasMoreElements, 670, 727
hasNext, 91, 669, 674, 677
hasNextDouble, 91
hasNextInt, 91
headMap, 712, 716
headSet, 712, 716
higher, 694
IconImage, 522
in.close, 580
incrementAndGet, 777
indexOf, 84
indexOfSubList, 724
info, 605
init, 536

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

initCause, 583
initialize, 782
initialValue, 781
insert, 88, 434
insertItemAt, 426
InsertItemAt, 424
insertSeparator, 434
interrupt, 746, 749
interrupted, 748, 749
intValue, 243
invoke, 265267, 311
invokeAll, 808, 810
invokeAndWait, 820
invokeAny, 808
invokeLater, 817, 820
isAbstract, 256
isAccessible, 261
isDispatchThread, 820
isDone, 777
isEditable, 406, 425
isEmpty, 672, 673
isEnabled, 373, 379
isFinal, 252, 256
isInterface, 256
isInterrupted, 746749
isJavaIdentifierPart, 67
isJavaIdentifierStart, 67
isLocationByPlatform, 323, 326
isLoggable, 600, 609
isNative, 256
isNativeMethod, 584
isPopupTrigger, 438
isPrivate, 252, 256
isProtected, 256
isProxyClass, 311, 312
isPublic, 252, 256
isResizable, 326
isSelected, 415, 436
isStatic, 256
isStrict, 257
isSynchronized, 257
isTraversable, 498, 505
isUndecorated, 326
isVisible, 325
isVolatile, 257
isWebBrowserSupported, 531
itemComparator, 720
iterator, 668, 672
join, 752, 810
JTextField, 406
keyPress, 621
keyRelease, 621
keySet, 700
KeyStroke, 379

Skorowidz
lastIndexOf, 84
lastIndexOfSubList, 724
lastKey, 701
layoutContainer, 472
length, 84, 730
linkSize, 466
listFiles, 497
listIterator, 677, 682
load, 554, 728
lock, 762, 764, 782
lockInterruptibly, 783
log, 593, 606
logp, 606
logrb, 606
lookup, 531
lower, 694
main, 59, 148, 162, 249
makeButton, 362
makePair, 644
Math.random, 122
Math.round, 75
menuCanceled, 441
menuDeselected, 441
menuSelected, 441
minimumLayoutSize, 472
mod, 115
modifiers, 256
mouseClicked, 380, 381
mouseDragged, 382
mouseEntered, 383
mouseExited, 383
mouseMove, 621
mouseMoved, 381, 382
mousePress, 621
mousePressed, 380, 381
mouseRelease, 621
mouseReleased, 380
move, 736
multiply, 115, 116
mutatora set, 142
newCondition, 765, 769
newFixedThreadPool, 803
newInstance, 249251, 262, 658
newProxyInstance, 307, 311
newScheduledThreadPool, 807
newSingleThreadScheduledExecutor, 807
next, 669, 679
nextDouble, 89, 91, 610
nextElement, 670, 727
nextInt, 89, 91
nextLine, 89, 91
node, 560
notify, 773
notifyAll, 773

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Object.clone, 282
Objects.hashCode, 227
offer, 787, 793
offerFirst, 793
offerLast, 793
offsetByCodePoints, 83
openFileDialog, 526, 532
openMultiFileDialog, 532
or, 730
ordinal, 247
pack, 330, 332
paintComponent, 328, 347, 475, 569
parse, 244
parseInt, 242, 243
peek, 729, 787
play, 546
poll, 787, 793, 809
pollFirst, 694, 793
pollLast, 694, 793
pop, 729
preferredLayoutSize, 472
previous, 677680
previousIndex, 680
print, 96
printBuddies, 651
printf, 92, 244
println, 60, 96, 229, 310
printStack, 251
printStackTrace, 251, 581, 611
process, 826
publish, 599, 607, 825
push, 729
put, 556, 787, 792
putFirst, 793
putIfAbsent, 794
putLast, 793
putValue, 373, 379
raiseSalary, 284
readConfiguration, 595
readLine, 91
readLock, 784
readPassword, 91
release, 812
remove, 434, 669673, 678, 682
removeAll, 672, 673, 681
removeAllItems, 426
removeEldestEntry, 705
removeFirst, 683
removeHandler, 607
removeItem, 424, 426
removeItemAt, 424, 426
removeLast, 683
removeLayoutComponent, 472
removePropertyChangeListener, 373

845

846

Java. Podstawy

metoda
repaint, 329, 332, 827
replace, 84, 796
replaceAll, 724
res.close, 580
resetChoosableFileFilters, 504
resize, 537
resume, 752
retainAll, 672, 717
revalidate, 407, 408
reverse, 724
reverseOrder, 722
rotate, 724
run, 647, 754
Runtime.addShutdownHook, 179
saveAsFileDialog, 532
saveFileDialog, 526
schedule, 807
scheduleAtFixedRate, 807
scheduleWithFixedDelay, 808
ServiceManager, 526
set, 142, 236, 261, 679, 682
setAccelerator, 440
setAccessible, 257, 261
setAccessory, 504
setAction, 434
setActionCommand, 363, 417, 419
setAutoCreateContainerGaps, 467
setAutoCreateGaps, 467
setBackground, 341, 343
setBorder, 419, 423
setBounds, 321, 322, 325, 468
setCharAt, 88
setColor, 343, 510, 625
setColumns, 408, 410, 413
setComponentPopupMenu, 438
setCursor, 386
setDaemon, 754
setDebugGraphicsOptions, 615
setDefaultButton, 494
setDefaultCloseOperation, 320, 536
setDefaultUncaughtExceptionHandler, 611
setDone, 777
setEditable, 406, 423, 425
setEnabled, 373, 379, 441
setExtendedState, 325, 326
setFileFilter, 497, 504
setFileSelectionMode, 503
setFileView, 499, 504
setFilter, 600, 607
setFirst, 652
setFont, 350, 408
setForeground, 341, 343
setFormatter, 601, 607

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

setFrameFromCenter, 337
setFrameFromDiagonal, 336
setHonorsVisibility, 467
setHorizontalGroup, 466
setHorizontalTextPosition, 435
setIcon, 409, 435
setIconImage, 321, 326
setInheritsPopupMenu, 438
setJMenuBar, 432, 434
setLabelTable, 428, 431, 640
setLayout, 399
setLevel, 606, 607
setLineWrap, 411, 413
setLocation, 321, 325
setLocationByPlatform, 322, 326
setLookAndFeel, 368
setMajorTickSpacing, 431
setMinorTickSpacing, 431
setMnemonic, 440
setModel, 424
setMultiSelectionEnabled, 503
setPaint, 340, 341, 343
setPaintLabels, 428, 431
setPaintTicks, 428, 431
setPaintTrack, 432
setParent, 606
setPriority, 753
setRect, 335
setResizable, 321, 326
setRows, 410, 413
setSecond, 638
setSelected, 414, 436
setSelectedFiles, 503
setSize, 325
setSnapToTicks, 432
setSource, 363
setText, 406, 407, 409
setTitle, 321, 326, 537
setToolTip, 446
setToolTipText, 447
setUncaughtExceptionHandler, 754
setUndecorated, 326
setUseParentHandlers, 607
setValue, 701
setVerticalGroup, 466
setVisible, 320, 325, 489, 537, 827
setWrapStyleWord, 413
severe, 605
show, 320, 437
showConfirmDialog, 474, 476, 482
showDialog, 490
showDocument, 531, 548, 549
showInputDialog, 475, 476, 483
showInternalConfirmDialog, 482

Skorowidz
showInternalInputDialog, 484
showInternalMessageDialog, 482
showMessageDialog, 288, 474, 481, 530
showOptionDialog, 474, 476
showSaveDialog, 495
showStatus, 548, 549
shuffle, 721, 722
shutdown, 803
shutdownNow, 804
signal, 769, 780
signalAll, 766769
size, 236, 672, 673
sleep, 737, 742
sort, 121, 274, 720
start, 288
startsWith, 84
stateChanged, 426
stop, 288, 752, 784
store, 550, 554, 728
subList, 716
subMap, 712, 716
submit, 803, 809
subSet, 712, 716
substring, 78, 84, 711
subtract, 115, 116
super.clone, 282
super.paintComponent, 330
suspend, 752, 785
swap, 168, 724
swapHelper, 656
SwingUtilities.updateComponentTreeUI, 366
synchronizedCollection, 713, 797
synchronizedList, 797
synchronizedMap, 713, 797
synchronizedSet, 797
synchronizedSortedMap, 797
synchronizedSortedSet, 797
System.exit, 60, 320
System.out.println, 89
System.runFinalizersOnExit, 179
systemNodeForPackage, 560
systemRoot, 560
tailMap, 712, 716
tailSet, 712, 716
take, 793
takeFirst, 793
takeLast, 793
text, 91
Thread.getAllStackTraces, 581
ThreadLocalRandom.current, 781
throwing, 594, 606
toArray, 237, 645, 672, 718
toBack, 326
toFront, 322, 326

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

847

toString, 88, 118, 228, 259, 310, 424, 584, 610


toUpperCase, 84
transfer, 756, 761, 770, 793
trim, 85, 407
trimToSize, 235, 236
tryLock, 782, 783
tryTransfer, 793
UIManager.setLookAndFeel, 366
uncaughtException, 754, 755
unlock, 762, 764
unmodifiableCollection, 713
unmodifiableList, 713
unmodifiableSet, 713
update, 240
userNodeForPackage, 560
userRoot, 560
validate, 407, 408
valueOf, 114, 243, 246
wait, 773
warning, 605
windowActivated, 369, 372
windowClosed, 369, 372
windowClosing, 369, 370, 372
windowDeactivated, 369, 372
windowDeiconified, 369, 372
windowIconified, 369, 372
windowOpened, 369, 372
windowStateChanged, 372
writeLock, 784
xor, 730
metody
abstrakcyjne, 215
akcesora, 155
fabryczne, 161, 704, 803
finalne, 211, 770
graficzne, 332
interfejsu
Action, 373
BlockingDeque<E>, 793
BlockingQueue<E>, 792
Collection, 672
Collection<E>, 672
Deque<E>, 695
Future<V>, 801
GenericArrayType, 664
Iterator<E>, 674
LinkedList<E>, 683
List<E>, 682
ListIterator<E>, 683
Map<K, V>, 700
NavigableSet<E>, 694
ParameterizedType, 664
Queue<E>, 695
SortedSet, 712

848

Java. Podstawy

metody
interfejsu
TypeVariable, 664
WildcardType, 664
WindowListener, 369, 372
klas wewntrznych, 289
klasy
AccessibleObject, 261
Applet, 537, 545, 546
Array, 264
Arrays, 123
BigDecimal, 116
BigInteger, 115
BitSet, 730
BorderFactory, 421
Class, 252, 255
Class<T>, 658
Collections, 712, 715, 722, 724
Component, 325
Console, 91
Constructor, 256
Date, 141
Employee, 151
Executors, 803
FileView, 498, 505
Font, 349
Frame, 326
Graphics, 350
Graphics2D, 351
GregorianCalendar, 147
GroupLayout, 466
Integer, 243
JComboBox, 425
JFileChooser, 503
JFrame, 321
JMenu, 433
JOptionPane, 481
JSlider, 431
JTextArea, 412
JTextComponent, 406
LayoutManager, 472
LineMetrics, 350
Logger, 605
Modifier, 256
Object, 773
Preferences, 560
Properties, 553
RectangularShape, 335, 339
Robot, 621
Scanner, 90
String, 83, 87
StringBuilder, 88
Thread, 749755

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

ThreadLocal<T>, 782
Throwable, 583
Timer, 288
Toolkit, 327
Window, 326
kolejek blokujcych, 787, 788
komentarze, 191
monitorowe, 775
o zmiennej liczbie parametrw, 244
odradzane, 141
oglne, 632
pomostowe, 638, 649
prywatne, 157
przecianie, 171
przesaniajce, 201
publiczne, 59
rejestrujce, 594
rodzime, 160
statyczne, 73, 160, 645
statyczne ze zmiennymi typowymi, 645
sygnatura, 171, 209
synchronizowane, 771
tworzce niemodyfikowalne widoki, 712
udostpniajce, 141
uoglnione, 671
wstawiane, 154
z parametrami typowymi, 632
zmieniajce warto elementu, 141
mieszanie wsprzdnych, 691
mnemoniki, 438
modalno, 485
model, 394, 395
pola tekstowego, 394
wskanikowy, 24
modu adujcy klasy, 588
modyfikacja
parametru obiektowego, 167
zbioru EnumSet, 704
modyfikator
dostpu, 58, 220
dostpu private, 186
dostpu public, 185
final, 158, 211, 777
volatile, 777
zdarzenia, 386
modyfikowanie elementw zbioru, 687
monitor, 775
motyw Ocean, 316
mutatory, 142
MVC, Model-View-Controller, 392
mysz, 380

Skorowidz

N
nadklasa, superclass, 200
nadtypy, 652, 654
NaN, 64, 70
narzdzia
graficzne, 317
wiersza polece, 44
narzdzie
appletviewer, 53, 535
ButtonTest, 618
ImageViewer, 51, 500
jar, 512, 514
Jar Bundler, 515
javac, 45
javadoc, 190195
javap, 294
jconsole, 595, 613, 779
jmap, 614
Matisse, 460, 461
OptionDialogTest, 477
ReflectionTest, 295, 296
Swing graphics debugger, 614
natywna biblioteka interfejsowa, 33
nawiasy
klamrowe, 59, 300
kwadratowe, 116
ostre, 632
puste, 234
nazwa
akcji, 377
klasy, 45, 58, 134, 197
konstruktora, 137
parametru, 174
pliku, 45
pliku dziennika, 598
rejestratora, 595
zasobu, 516
zmiennej, 67
zmiennej typowej, 630
nazwy
klas komponentw Swing, 318
klas proxy, 311
logiczne czcionek, 344
metod, 197
rodziny czcionek, 343
NetBeans, 38, 44, 459
niezaleno od architektury, 26
niezawodno, 24
niezmienialno acuchw, 79
niszczenie obiektw, 179
notacja wielbdzia, 58

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

849

O
obiekt, 24, 132
Action, 446
ActionEvent, 357
AssertionError, 587
BasicButtonUI, 398
builder, 86
ButtonGroup, 415
Callable, 803
ColorAction, 360
Comparator, 693
Console, 90
Date, 138
DefaultButtonModel, 398
ExecutorCompletionService, 808
FutureTask, 798
Graphics, 328, 341
GridLayout, 406
Handler, 595, 598, 608
Iterator, 672
JButton, 397
JPanel, 402
JRadioButton, 415
PaintEvent, 388
Path, 97
PrintWriter, 96, 97
Properties, 553
Runnable, 759, 803
Scanner, 96
System.out, 60
WeakReference, 702
obiektw, 133
autoboxing, 241
hermetyzacja, 133
klonowanie, 280
kopiowanie, 280
metody, 133
metody prywatne, 157
niszczenie, 179
polimorfizm, 204
porwnywanie, 221
skadowe, 133, 155
stan, 133, 134
tosamo, 134
waciwoci, 133
zachowanie, 133
obiekty
blokady, 762
funkcyjne, 690
klasy Class, 249
klasy Lock, 762
klasy oglnej, 646
nasuchujce zdarze, listener objects, 287, 371

850

Java. Podstawy

obiekty
obsugujce wywoanie, 307
opakowujce kolekcje, 711
proxy, 308
typu wyliczeniowego, 548
warunkw, 765
zdarze, event objects, 356
obliczanie kodu mieszajcego, 226
obramowanie, 419
obrazy, 351
obsuga
apletw, 533
bdw, 564
kliknicia przycisku, 357
nieprzechwyconych wyjtkw, 754
przycisku OK, 486
ramek, 325
wyjtkw, 249, 564, 646
zdarze, 329, 355
zdarze AWT, 389, 390
zdarze myszy, 380, 383
obszar
surogatw, surrogates area, 66
tekstowy, text area, 406, 410
ochrona bloku kodu, 762
odczyt plikw, 96
odmierzanie czasu, 782
odpakowywanie, 242
odradzane metody, 141
odwoanie, 193
odwzorowanie nazw czcionek, 345
ograniczenia
blokad wewntrznych, 771
nadtypw, 652, 653
typw generycznych, 240
widocznoci metody, 219
widokw, 714
zmiennych typowych, 633
okna dialogowe
modalne, 474
niemodalne, 474
okno, 369, 372
dialogowe
opcji, 474, 483
potwierdzenia, 482
przyjmowania danych, 484
typu O programie, 485
wyboru kolorw, 505
wyboru plikw, 495
z komunikatem, 482
z polem hasa, 489
dokumentacji API, 85
konsoli, 44
przegldarki apletw, 53
wyboru plikw, 498, 558

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

OOP, Object Oriented Programming, 132


opakowywanie, autoboxing, 241
opakowywanie wyjtkw, 648
opcja
Step Into, 623
Step Over, 623
Terminate, 625
opcje
debugera, 623
narzdzia jar, 513
operacje
niepodzielne, 760
opcjonalne, 714
zbiorcze, 717
operator
::, 202
[], 120
==, 705
dekrementacji, 71
inkrementacji, 71
instanceof, 213, 222, 278
new, 86, 137, 153, 276
przecinka, 77
operatory
arytmetyczne, 69
bitowe, 72
logiczne, 71
relacyjne, 71
opis
danych, 33
elementu, 692
klasy, 86, 192
metod, 87
struktury stron, 33
zmiennej, 192
optymalizacja, 27
osona obiektw, 241
ostrzeenie, 643
otwieranie menu, 438
overloading resolution, 171
oznaczenia relacji, 136

P
pakiet
com.horstmann.corejava, 183
com.mycompany.mylib, 588
com.mycompany.util, 518
com.sun.java, 366
domylny, 183
java.awt, 186
java.awt.event, 286, 388
java.lang, 83, 90
java.lang.reflect, 252, 660
java.math, 114

Skorowidz
java.sql, 181
java.util, 90, 181, 387
java.util.concurrent, 762, 771, 797
java.util.concurrent.atomic, 777
java.util.concurrent.locks, 783
javax.swing, 286, 319
javax.swing.event, 390
JDK, 38
org.omg.CORBA, 243
Swing, 314
pakiety, packages, 180
dodawanie klasy, 182
komentarze, 194
lokalizacyjne, 596
zasig, 185
panel
JPanel, 398
przewijany, scroll pane, 411, 413
z przyciskami, 359, 398
para klucz warto, 556, 561, 697
parametr, 61
anchor, 452
fill, 452
gridheight, 451, 452
gridwidth, 451, 452
gridx, 451, 452
gridy, 451, 452
parametry
jawne, 153
konfiguracyjne obiektu Handler, 598
acuchowe, 96
metod, 164
niejawne, 153, 174
obiektowe, 167
okrelajce cel, 549
typowe, 628, 641
wiersza polece, 120
pasek
menu, 432
narzdzi, toolbar, 444
ptla
do-while, 102
for, 98, 106108
for each, 32, 98, 117, 126
w stylu for each, 669
while, 101, 102
piaskownica, sandbox, 519, 522
piecztowanie pakietw, 186, 518
plik
AboutDialog.java, 487
ActionFrame.java, 377
AnonymousInnerClassTest.java, 301
ArrayListTest.java, 238
Ball.java, 739

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

BallComponent.java, 740
Bank.class, 761
Bank.java, 758, 767, 772
BigIntegerTest.java, 115
BlockingQueueTest.java, 789
BorderFrame.java, 420
Bounce.java, 737
BounceThread.java, 743
BuggyButtonTest.java, 622
ButtonFrame.java, 360
ButtonPanel.java, 481
Calculator.jnlp, 519
CalculatorFrame.java, 528
CalculatorPanel.java, 403
CalendarTest.java, 144
Chart.java, 543
CheckBoxTest.java, 414
CircleLayout.java, 469
CircleLayoutFrame.java, 472
CloneTest.java, 284
ColorChooserPanel.java, 508
ComboBoxFrame.java, 424
CompoundInterest.java, 125
ConstructorTest.java, 177
CopyOfTest.java, 263
DataExchangeFrame.java, 491
deskryptora, 519
DialogFrame.java, 486
doc-files, 191
DrawTest.java, 337
Employee.java, 151, 184, 205, 231, 275, 284
EmployeeSortTest.java, 275
EmployeeTest.java, 149, 151
en.properties, 596
EnumTest.java, 246
EqualsTest.java, 230
EventTracer.java, 615
FileIconView.java, 503
fontconfig.properties, 345
FontFrame.java, 454, 463
FontTest.java, 347
forkJoinTest.java, 810
FutureTest.java, 799
GBC.java, 456
GenericReflectionTest.java, 661
ImagePreviewer.java, 502
ImageTest.java, 352
ImageViewer.java, 51
ImageViewerFrame.java, 500
InnerClassTest.java, 292
InputTest.java, 89
Item.java, 692
javan.log, 597
javaws.jar, 526

851

852

Java. Podstawy

plik
jogging.properties, 594
LinkedListTest.java, 681
LoggingImageViewer.java, 602
LotteryArray.java, 128
LotteryDrawing.java, 121
LotteryOdds.java, 108
Manager.java, 206, 232
ManagerTest.java, 204
MANIFEST.MF, 512
manifestu
klasa gwna, 514
sekcja gwna, 512
wstawianie sekcji, 518
zmienianie zawartoci, 513
MapTest.java, 699
MenuFrame.java, 442
MethodTableTest.java, 266
MouseComponent.java, 383
MouseFrame.java, 383
NotHelloWorld.java, 330, 534
ObjectAnalyzerTest.java, 259
OptionDialogFrame.java, 477
overview.html, 194
PackageTest.java, 184
PairTest1.java, 631
PairTest2.java, 634
PairTest3.java, 656
ParamTest.java, 169
PasswordChooser.java, 492
Person.java, 217
PersonTest.java, 217
PlafFrame.java, 367
PreferencesTest.java, 557
PriorityQueueTest.java, 696
program.properties, 551
PropertiesTest.java, 551
ProxyTest.java, 309
RadioButtonFrame.java, 417
ReflectionTest.java, 253
ResourceTest.java, 517
Retirement.java, 103
Retirement2.java, 105
RobotTest.java, 618
rt.jar, 187
SetTest.java, 686
ShuffleTest.java, 721
Sieve.cpp, 731
Sieve.java, 731
SimpleFrameTest.java, 318
SizedFrameTest.java, 324
SliderFrame.java, 428
src.zip, 41, 42
StackTraceTest.java, 582

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

StaticInnerClassTest.java, 305
StaticTest.java, 162
Student.java, 218
swing.properties, 366
SwingThreadTest.java, 817
SwingWorkerTest.java, 821
System.java, 43
TalkingClock$TimePrinter.class, 294
TextComponentFrame.java, 411
ThreadPoolTest.java, 804
TimerTest.java, 287
ToolBarTest.java, 446
TransferRunnable.java, 759
TreeSetTest.java, 691
UnsynchBankTest.java, 757
Welcome.java, 45
WelcomeApplet.class, 53
WelcomeApplet.html, 53
WelcomeApplet.java, 55
pliki
.class, 514
.exe, 514
.java, 58
.jnlp, 519
audio, 546
cookie, 527
graficzne, 546
JAR, 187, 512, 514
obrazw, 515
podpisane cyfrowo, 523
tekstowe, 515
XML, 600
z danymi binarnymi, 515
zasobw, 516
rdowe, 151, 189
pobieranie
hasa, 90
waciwoci systemowych, 554
podklasa, subclass, 200
Properties, 726
Stack, 726
podklasa, subclass, 200
podkradanie pracy, 812
podacuchy, 78
podmenu, 432
podpakiety, 180
podpis cyfrowy, 26, 523
podpisywanie kodu, 523
podtyp typu granicznego, 634
podziaka, 427
pola
finalne, 211
hase, 406, 410
klasowe, 159

Skorowidz
niestatyczne, 159
prywatne, 155
publiczne, 155
statyczne, 159, 176
tekstowe, 394, 406
ulotne, 776
weight, 451
wyboru, 413
pole value, 243
polecenie
cmd, 41
dir, 46
jcontrol, 536
polimorfizm, 204, 207, 269
poczenie na poziomie gniazd, 24
pooenie przyciskw, 402
porwnywanie
elementw tablic, 225
acuchw, 79, 81
obiektw, 221, 689
obiektw osonowych, 242
w ptli, 107
w podklasach, 277
powiadamianie o zdarzeniach, 358
powizana tablica mieszajca, 703
poziom
FINE, 595, 597, 601
rejestracji obiektu Handler, 597
poziomy
rejestracji, 595
wanoci komunikatw, 592
pozycjonowanie
bezwzgldne, 468
ramki, 321
preferencje
uytkownika, 549, 555
wza, 561
priorytet
informacji, 592
wtkw, 750
wtku, 752, 753
operatorw, 76
procedura
obsugi bdw, 565
obsugi zdarze, 355
proces, 736
proces inliningu, 212
program, Patrz narzdzie
programowanie
interfejsw graficznych, 391
interfejsu uytkownika, 317
obiektowe, 24, 132
oglne, generic programming, 627
paskw narzdzi, 445

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

proceduralne, 133
sieciowe, 33
wielowtkowe, 28
programy
jednowtkowe, 736
refleksyjne, 247
wielowtkowe, 735
projektant formy, 398
projektowanie klas, 195
prostokt, 334
protokoy sieciowe, 24
prywatne pola, 155
przechwytywanie
strumienia bdw, 611
wielu typw wyjtkw, 574
wyjtkw, 250, 571, 747
przeciganie paska narzdzi, 445
przecianie
konstruktorw, 172
metod, 171
przedzia, 712
przegldarka
apletw, 535
HotJava, 31
pamici podrcznej, 521
przejmowanie blokady, 774
przekazywanie
obiektu, 138
przez warto, 167
wyjtkw, 586
przecznik, radio button, 413
przeczniki, 415
przeczniki w elementach menu, 436
przenono, 26, 70
przepyw sterowania, 98, 111, 298
przerywanie
dziaania ptli, 111
procesu adowania, 737
wtkw, 746
przesanianie metod, 202, 225, 647
przestrzenie numeracyjne, 66
przesuwanie iteratora, 671
przeszukiwanie liniowe, 723
przetwarzanie XML, 33
przycisk, 358, 397
domylny, 490
JButton, 398
OK, 486
pooenie, 402
rozmiar, 402
w rozkadzie brzegowym, 401
przyciski radiowe, 415
przywileje klasowe, 157

853

854

Java. Podstawy

publiczne metody
akcesora, 155
mutatora, 155
publiczne pola, 155
pule wtkw, 802804
punkt wstrzymania, 623, 624
pusta mapa wasnoci, 553

R
ramka, frame, 318
nadrzdna, 485, 490
wywietlajca tekst, 327
ramki
pozycjonowanie, 321
rozmiar, 323
warstwy, 327
wasnoci, 322
wywietlanie, 320
reaktywacja wtkw, 766
referencja
do elementw typu Object, 628
do klasy zewntrznej, 291, 293
do obiektu, 138
do parametru niejawnego, 203
null, 250, 565
refleksja, reflection, 199, 247, 270, 658
analiza
funkcjonalnoci klasy, 252
obiektw w czasie dziaania programu, 257
generyczny kod tablicowy, 261
rejestr, 556
rejestr zdarze, 615
rejestratory, logger, 591, 597
rejestrujcy obiekt poredni, 610
rekordy dziennika, 597
relacje
agregacja, 135
dziedziczenie, 136
zaleno, 135
reorganizacja tablicy mieszajcej, 686
repozytorium Preferences, 555
robot, 618
rodzaje
atakw, 25
modalnoci, 485
obramowa, 419
suwakw, 429
rozkad
brzegowy, 400, 402, 448
cigy, 448
GridBagLayout, 448453
grupowy, 459, 461
komponentw, 407
siatkowy, 402, 448

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

sprynowy, 449
SpringLayout, 449
rozmiar
apletu, 537
ekranu, 323
ikon, 522
interpretera, 23
pola tekstowego, 407
przyciskw, 402
ramki, 323
tablicy, 117
rozmieszczanie komponentw, 398
rozstrzyganie przeciania, 171, 209
rozszerzanie
klasy, 133, 216
klasy Throwable, 646
programw, 211
stylu, 317
rysowanie, 314
figur, 333, 337
na komponencie, 328
obrazu, 354
wykresu, 543
rzutowanie, casting, 75, 212, 637, 644, 718

S
sandbox, 519, 522
scalanie list, 681
SDK, Software Development Kit, 38
SE, Standard Edition, 38
semafory, 812
separator, 445
serializacja, 539
serwer pochodzenia, 523
serwer Tomcat, 519, 520
siatka, 450
sito Eratostenesa, 730
skaner, 89
skad tekstw, 346
skadanie acuchw, 86
skadnia
diamentowa, diamond syntax, 234
Javy, 23
klas wewntrznych, 289
wewntrznych klas anonimowych, 371
skadowe, 133, 155
skadowe chronione, 220
skrty klawiszowe, 439
sabe referencje, 702
sowo kluczowe, 829
abstract, 215
assert, 587
catch, 250
class, 58

Skorowidz
extends, 200, 634
final, 68, 158, 211, 299
implements, 273, 634
import, 180
instanceof, 213
interface, 272
package, 183, 186
private, 152, 158, 220
protected, 190, 219
public, 58, 152, 220
static, 69, 160, 304
strictfp, 70
super, 202
synchronized, 762, 769, 775
this, 154, 160, 174
throws, 569
try, 250
void, 60
volatile, 776
suchacz
akcji, 414, 416
przycisku, 359
z rdami zdarze, 373
zdarze, event listener, 356, 364, 388
sortowanie, 275, 299, 720
kluczy, 701
listy elementw, 720
tablicy, 121
specyfikacja, 59
specyfikacja wyjtku, 568
specyfikator
dostpu, 150
formatu, 92, 96
throws, 283, 569, 573
sprawdzanie
parametrw, 589
pl obiektu, 265
typw, 641
typw pl, 257
zakresu, 120
sprzenie zwrotne, 286
staa, 68
ACCELERATOR_KEY, 374
ACTION_COMMAND_KEY, 374
BorderLayout.SOUTH, 401
DEFAULT, 374
Double.NaN, 64
Double.NEGATIVE_INFINITY, 64
Double.POSITIVE_INFINITY, 64
LONG_DESCRIPTION, 374
MNEMONIC_KEY, 374
NAME, 374
SHORT_DESCRIPTION, 374
SMALL_ICON, 374

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

stae
interfejsu Action, 374
interfejsu SwingConstants, 409
klasowe, 69
klasy BorderLayout, 401
acuchowe, 80
matematyczne, 73
statyczne, 159
stan
obiektu, 133, 134
okna, 372
wtkw, 749, 751
standard
ECMA-262, 35
IEEE 754, 64
ISO/ANSI, 80
wyraania czasu, 139
status przerwania wtku, 746
statyczne
funkcje skadowe, 60
klasy wewntrzne, 303
sterta, heap, 120, 140, 696
STL, Standard Template Library, 666
stopie powiza midzy klasami, 135
stopy oprocentowania, 126
stos, stack, 120, 729
stos wywoa, 251, 582
stosowanie
blokady, 765
dziedziczenia, 269
refleksji, 270
warunkw, 769
wyjtkw, 584
struktura
katalogw, 43
ramki JFrame, 328
struktury danych, 665
strumie
ByteArrayInputStream, 526
ByteArrayOutputStream, 526
InputStream, 526
PrintStream, 526
wejciowy, 89
styl
GTK, 316
Metal, 315, 366
Nimbus, 317
Synth, 317
style
obramowa, 419
projektowania, 196
suwak, slider, 413, 426, 430
suwak z podziak, 427
SWT, 317

855

856

Java. Podstawy

sygnatura metody, 171, 209


symbole zastpcze, 65
symulacja banku, 756, 759, 768
synchronizacja, 756
synchronizatory, 812, 813
synchronizowane wtki, 763
system kolorw, 342
szablon
bitset, 729
vector, 235
szeroko kolumny, 407
szkielet rozgazienie-zczenie, 809, 812

cieka
dostpu, 39
klas, 187, 189
cieki
bezwzgldne, 97
wzgldne, 97
cisa kontrola typw, 62, 274
ledzenie
przepywu wykonywania, 593
stosu, 251, 581, 755
rodowisko dziaania apletu, 547
rodowisko programistyczne
Eclipse, 44, 47
NetBeans, 44

T
tabela metod, 210
tablic, 116
inicjowanie, 118
kopiowanie, 119
numerowanie, 116
przegldanie, 117
sortowanie, 121
tablica
accounts, 760
args, 120
arrayToFill, 673
par klucz warto, 556
referencji, 628
result, 123
trjktna, 129
tablice
anonimowe, 118
generyczne, 644
kopiowane przy zapisie, 796
mieszajce, 226, 428, 684, 703
postrzpione, 127

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

typw oglnych, 642


typw wieloznacznych, 642
wielowymiarowe, 124
tasowanie, 720
tasowanie elementw listy, 721
technologia Flash, 29
tekst, 406, 410
terminal, 35
test wydajnoci, 730
testowanie
apletu, 53
blokad, 782
mechanizmu wasnoci, 551
Tomcat, 519
tosamo obiektu, 134
translacja
metod oglnych, 637
poprzez wymazywanie typw, 648
typw oglnych, 639
wyrae generycznych, 637
tryb penoekranowy, 325
tworzenie
akceleratora, 439
apletw, 29, 53
dziennika, 601
egzemplarza klasy, 132
etykiety, 409
klas wyjtkw, 570
konstruktorw, 152
listy cyklicznej, 668
listy powizanej, 668
menu, 432
obiektw, 96, 132, 137, 171
obiektu proxy, 307
obiektu typu Class, 249
obramowa, 421
oglnych tablic, 643
okien dialogowych, 484
okna komunikatu, 477
osobnego wtku, 741, 745
plikw JAR, 512
pl wyboru, 413
przycisku, 358, 362
puli wtkw, 802
ramki, 318
robota, 617
suchacza akcji, 363
tablic, 129, 203
postrzpionych, 128
generycznych, 644
trjktnych, 129
z kolekcji, 718
widoku mapy, 698

Skorowidz
typ
boolean, 66
byte, 62
char, 65
Date, 92
double, 64
float, 63
graniczny, 634
int, 26, 62
Integer, 243
long, 63
MIME, 519, 520
osony, 265
parametryzowany, 650
short, 62
short int, 26
surowy, 636, 641, 650
wbudowany, 137
wieloznaczny, 629, 634
wieloznaczny z ograniczeniem nadtypw, 654
wyliczeniowy, 77
zwrotny, 639
typy
cakowite, 62
interfejsowe, 667
kolekcji, 675
komunikatw, 475
konwersji, 74
kursorw, 382
listowe, 651
oglne, 627, 658
parametryzowane, 627
podstawowe, 221
sparametryzowane, generic types, 32
surowe, 234
tablicowe, 221
wieloznaczne, 650, 653
bez ogranicze, 655
mechanizm chwytania, 656
wyliczeniowe, 245, 704
zmiennoprzecinkowe, 63
zwrotne kowariantne, 639

U
ukrywanie danych, 133
UML, Unified Modeling Language, 136
Unicode, 65
uruchamianie
apletu, 53, 535
aplikacji graficznej, 50
aplikacji Java Web Start, 519, 520
osobnego wtku, 741

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

programw w konsoli, 45
programu, 44, 49
uruchomienie kilku wtkw, 743
usugi
API, 526
JNLP, 526
ustawianie
preferencji, 557
cieki klas, 189
usuwanie
elementw, 670
elementw z kolekcji, 673
elementu z listy powizanej, 676
elementu z tablicy, 675
nieuytkw, garbage collecting, 22, 702
przedziau, 712
UTC, Coordinated Universal Time, 139
UTF-16, 66
utrata wyjtku, 579

V
varargs, 244

W
warstwa
implementacji, 666
interfejsw, 666
ramki, 327
wartoci
graniczne przedziau, 712
zwrotne okna potwierdzenia, 476
warto
NaN, 64, 70
null, 77, 81, 139, 172
skrtu, hash value, 227
warunek wstpny, 590
warunki, 765, 769
wtek, thread, 735, 815
dystrybucji zdarze, 319, 741, 785, 819, 828
roboczy, 824
sterowania, thread of control, 735
TransferRunnable, 785
wyliczeniowy, 791
zablokowany, 747
zamknity, 747
wtki
koczenie dziaania, 746
oczekujce, 766
niesynchronizowane, 763
priorytet, 752
stan

857

858

Java. Podstawy

wtki
stan
BLOCKED, 749
NEW, 749
RUNNABLE, 749
TERMINATED, 749
TIMED WAITING, 749
WAITING, 749
synchronizowane, 763
usunite z kolejki, 766
wywaszczanie, 760
zamienianie w demona, 753
zamknicie, 752
wczytywanie zasobw, 516
wejcie, 89
wejcie System.in, 96
wersje Javy, 32
wze, 556, 560
wizanie
dynamiczne, 204, 209211
statyczne, 209
widoczno metod, 211
widok, view, 394, 710
kolekcji, 715
listowy elementw, 716
mapy, 698
podprzedziau, 717
pola tekstowego, 394
widoki
kontrolowane, 714
niemodyfikowalne, 712
przedziaowe, 711
synchronizowane, 713
wielkie liczby, 114
wielko liter, 58
wielowtkowo, 28, 33, 735
wielozadaniowo, 735
wiersz polece, 44, 120
wizualne budowanie interfejsw, 51
wasne typy wyjtkw, 571
wasnoci
czcionki, 450, 454
interfejsw, 276
interfejsu ButtonModel, 397
klas proxy, 311
metody equals, 222
monitorw, 775
ramek, 322, 551
wtkw, 752
wasno, property, 322
waciwoci
list, 721
systemowe, 554

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

wczanie
asercji, 588
zegara, 293
wnioskowanie o typie, 632
wprowadzanie tekstu, 406
wskaniki, 25
do funkcji, 286
do metod, 264
do obiektw, 140
wspczynnik zapenienia tablicy, 686
wsprzdne
figur, 333336
kodowe znakw, 66, 81
siatki, 451
wstawianie komentarzy, 190
wybr
kolorw, 505
plikw, 495
wyciszanie wyjtkw, 586
wydajno, 27
wyduenie
dolne, 346
grne, 346
wyjtek
ArrayIndexOutOfBounds, 117
ArrayIndexOutOfBoundsException, 566, 816
ArrayStoreException, 642, 650
BadCastException, 658
Class.forName, 250
ClassCastException, 213, 262, 650, 714
CloneNotSupportedException, 283
ConcurrentModificationException, 679, 797
EmptyStackException, 584, 586
FileNotFoundException, 97, 528, 569, 648
IllegalAccessException, 257
IllegalMonitorStateException, 773
IllegalStateException, 670, 674, 683
InterruptedException, 737, 742, 748
IOException, 569, 572
NoSuchElementException, 674, 695
NullPointerException, 566, 586, 590
RuntimeException, 565, 566
ServletException, 575
ThreadDeath, 785
typu RuntimeException, 583
UnavailableServiceException, 526
UnsupportedOperationException, 711715
wyjtki
kontrolowane, 249, 567, 646
konwersji, 629
niekontrolowane, 250, 567, 647
typu IOException, 569
typu SQLException, 576
zabezpiecze, 554
wykonawcze, 565

Skorowidz
wyjtkw, 563
deklarowanie, 567
powtrne generowanie, 575
przechwytywanie, 250, 571
przechwytywanie wielu typw, 574
przekazywanie, 586
wasne typy, 571
zgaszanie, 569
wyjcie, 89
wyjcie System.out, 96
wykres, 543
wykres supkowy, 542
wyliczenia, 727
wyczanie
asercji, 588
dziedziczenia, 211
sprawdzania wyjtkw, 646
wymazywanie typw, 637648
wymiana danych, 489
wymuszanie rysowania, 329
wypenianie figur, 340
wyraenia generyczne, 637
wyrwnywanie etykiet i pl, 462
WYSIWYG, 395
wysyanie
rekordw, 597
zdarze, 319
wyszukiwanie binarne, 722
wycig, 756, 760
wywietlanie
elementw w przegldarce, 548
informacji, 327
komponentw, 614
obrazw, 52, 351
ramki, 320
rekordw dziennika, 604
tekstu, 327, 329
zasobu, 515
wywaszczanie wtku, 750, 760
wywoanie
dowolnych metod, 264
innego konstruktora, 174
przez nazw, 165
przez referencj, 164
przez warto, 164
setFirst(null), 655
wzorce
nazw plikw dziennika, 599
projektowe, 392
wzorzec
Composite, 393
Decorator, 393
MVC, 392396
Strategy, 393

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

859

X
XML, 33

Z
zachowanie obiektu, 133
zagniedanie
blokw instrukcji, 98
ptli, 113
zakleszczenie, deadlock, 766, 778
zaleno, 135
zamiana parametrw obiektowych, 168
zamykanie
aplikacji, 320
ramki, 320
wtkw, 752
zapenianie tablicy, 237
zapis
bdw w pliku, 611
danych w repozytorium, 556
do dziennika, 592594
plikw, 96
preferencji uytkownika, 549
zarzdca rozkadu, layout manager, 391, 400, 448
CircleLayout, 469
brzegowego, 400
cigego, 398
FlowLayout, 402
grupowego, 449
niestandardowy, 469
siatkowego, 402
zasada
jednego wtku, 816, 827
uczciwoci, 764
zamienialnoci, 207
zasig
blokowy, 98
pakietw, 185
zmiennych, 98
zasoby, resources, 515
zastosowanie
asercji, 589, 590
klas abstrakcyjnych, 279
klas wewntrznych, 294
kolejek priorytetowych, 696
parametrw Class<T>, 659
refleksji, 252
typw wyliczeniowych, 246
zawarto pl danych, 257
zawijanie wierszy, 411
zbir, set, 686, 697
HashSet, 684
TreeSet, 688691
uporzdkowany, 688

860

Java. Podstawy

zdarzenia, 355, 389


interfejs nasuchu, 356
myszy, 380, 438
niskiego poziomu, 388
okna, 370
semantyczne, 388
suchacz, 356
rdo, 356
zdarzenie, 287
FocusEvent, 388
KeyEvent, 388
MouseEvent, 388
MouseWheelEvent, 388
WindowEvent, 369, 388
zdjcie blokady, 766
zegar, 286, 293
zezwolenie, permit, 812
zgaszanie wyjtkw, 569
zintegrowane rodowisko programistyczne, IDE, 39, 47
zmiana
koloru, 374
koloru ta, 507
rozmiaru tablicy, 117
stanu okna, 372, 388
stylu, 366, 368
typu wyjtkw, 647
wasnoci czcionek, 454
zmienna, 66
zmienna rodowiskowa
CLASSPATH, 46, 189
Path, 40, 41
zmienne
atomowe, 777
finalne, 299, 777
finalne puste, 299
interfejsowe, 277
lokalne, 153
lokalne wtkw, 781
obiektowe, 137140, 216
parametryczne, 174
polimorficzne, 207
statyczne, 159
tablicowe, 116, 119
typowe, 630, 633, 643, 645
warunkowe, 765

znacznik
@author, 192
@deprecated, 193
@link, 194
@Override, 225
@param, 192
@see, 193
@since, 193
@version, 192
append, 599
applet, 54, 537
object, 540
param, 541, 542
znaczniki
dokumentacyjne, 190
polecenia printf, 93
znak
$, 67
/, 516, 560
ampersand, 634
konwersji, 92
koca pliku, 565
koca wiersza, 514
powrotu karetki, 60
rwnoci, 68
trzykropka, 244
znaki
dodatkowe, 66
echa, 410
konwersji Date i Time, 94, 95
konwersji polecenia printf, 93
nowego wiersza, 477
specjalne, 65
zniszczenie danych, 757
zoptymalizowane wywoywanie metod, 212
zwalnianie blokady, 764
zwracanie referencji, 157

rda zdarze biblioteki AWT, 389


rdo zdarze, event sources, 356, 389

danie zamknicia wtku, 746, 747

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Notatki

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

Ebookpoint.pl KOPIA DLA: Angelika Mlodzianowska angelinka90@o2.pl

You might also like