Professional Documents
Culture Documents
:Ζ6Ζă$
Z\GDQLH9ΖΖ
Java
®
8ľ\ZDMSRSXODUQ\FK
QDU]ÛG]L-DY\
7ZµU]SURVWHRELHNW\-DY\
LSRQRZQLHXľ\ZDMNRGX
2EVĄXJXM]GDU]HQLD
LZ\MÇWNL
dr Barry Burd
DXWRUNVLÇľNLJava Programming
for Android Developers for Dummies
Tytuł oryginału: Java For Dummies, 7th Edition
ISBN: 978-83-283-5990-1
Original English language edition Copyright © 2017 by John Wiley & Sons, Inc., Hoboken, New Jersey.
All rights reserved including the right of reproduction in whole or in part in any form.
This translation published by arrangement with John Wiley & Sons, Inc.
Oryginalne angielskie wydanie © 2017 by John Wiley & Sons, Inc., Hoboken, New Jersey.
Wszelkie prawa, włączając prawo do reprodukcji całości lub części w jakiejkolwiek formie,
zarezerwowane. Tłumaczenie opublikowane na mocy porozumienia z John Wiley & Sons, Inc.
Wiley, the Wiley Publishing logo, For Dummies, Dla Bystrzaków, the Dummies Man logo,
Dummies.com, Making Everything Easier and related trade dress are trademarks or registered
trademarks of John Wiley and Sons, Inc. and/or its affiliates in the United States and/or other countries.
Used by permission. Java is a registered trademark of Oracle America, Inc. Android is a registered
trademark of Google, Inc. All other trademarks are the property of their respective owners.
Wiley, the Wiley Publishing logo, For Dummies, Dla Bystrzaków, the Dummies Man logo,
Dummies.com, Making Everything Easier i związana z tym szata graficzna są markami
handlowymi John Wiley and Sons, Inc. i/lub firm stowarzyszonych w Stanach Zjednoczonych
i/lub innych krajach. Wykorzystywane na podstawie licencji. Java jest zastrzeżonym znakiem
towarowym Oracle America, Inc. Android jest zastrzeżonym znakiem towarowym Google, Inc.
Wszystkie pozostałe znaki handlowe są własnością ich właścicieli.
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 the Publisher.
Autor oraz Helion SA dołożyli wszelkich starań, by zawarte w tej książce informacje były
kompletne i rzetelne. Nie biorą jednak żadnej odpowiedzialności ani za ich wykorzystanie,
ani za związane z tym ewentualne naruszenie praw patentowych lub autorskich. Autor oraz
Helion SA nie ponoszą również żadnej odpowiedzialności za ewentualne szkody wynikłe
z wykorzystania informacji zawartych w książce.
Drogi Czytelniku!
Jeżeli chcesz ocenić tę książkę, zajrzyj pod adres
http://dlabystrzakow.pl/user/opinie/javby7_ebook
Możesz tam wpisać swoje uwagi, spostrzeżenia, recenzję.
Helion SA
ul. Kościuszki 1c, 44-100 Gliwice
tel. 32 231 22 19, 32 230 98 63
e-mail: dlabystrzakow@dlabystrzakow.pl
WWW: http://dlabystrzakow.pl
acc7ba75380d5ea8e325402fae0d0e83
a
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Spis treści
O autorze .........................................................................................15
Wprowadzenie ................................................................................19
Spis treści 5
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
ROZDZIAŁ 3: Używanie podstawowych elementów .............................59
Mówimy w języku Java ............................................................................................. 60
Gramatyka i typowe nazwy ............................................................................... 60
Słowa w programie w języku Java ..................................................................... 62
Pierwsze czytanie kodu języka Java ........................................................................ 63
Poznawanie prostego programu w języku Java .................................................... 65
Klasa Javy ............................................................................................................. 65
Metody języka Java ............................................................................................. 66
Główna metoda programu ................................................................................ 68
Jak ostatecznie nakazać komputerowi wykonanie jakiejś pracy? ................. 69
Nawiasy klamrowe ............................................................................................. 71
A teraz kilka komentarzy ......................................................................................... 74
Dodawanie komentarzy do kodu ..................................................................... 75
Jaką wymówkę ma Barry? .................................................................................. 78
Wykorzystywanie komentarzy do eksperymentowania z kodem ................ 79
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
ROZDZIAŁ 5: Kontrolowanie przepływu programu
za pomocą instrukcji podejmowania decyzji .............119
Podejmowanie decyzji (instrukcja if w języku Java) ............................................120
Zgadnij liczbę .....................................................................................................120
Kontrolowanie naciśnięć klawiszy na klawiaturze ........................................121
Tworzenie losowości ........................................................................................124
Instrukcja if ........................................................................................................125
Podwójny znak równości .................................................................................126
Przygotuj się ......................................................................................................126
Wcięcia w instrukcji if .......................................................................................127
Bezelseność w Iflandii ......................................................................................128
Używanie bloków w JShell .....................................................................................130
Tworzenie warunków z porównaniami i operatorami logicznymi ...................131
Porównywanie liczb, porównywanie znaków ................................................131
Porównywanie obiektów .................................................................................132
Importowanie wszystkiego za jednym zamachem .......................................134
Operatory logiczne w języku Java ...................................................................135
Vive les nuls! ......................................................................................................137
(Warunki w nawiasach) ....................................................................................138
Budowanie gniazda ................................................................................................140
Wybór spośród wielu wariantów (instrukcja switch w języku Java) ..................142
Podstawowa instrukcja switch ........................................................................143
Przerwać czy nie przerwać ..............................................................................146
Ciągi znaków w instrukcji switch .....................................................................148
Spis treści 7
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
CZĘŚĆ III: PRACA W SZERSZEJ PERSPEKTYWIE
— PROGRAMOWANIE OBIEKTOWE ......................................... 169
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Korzystanie z podklas ............................................................................................229
Dopasowywanie typów ....................................................................................231
Druga część programu .....................................................................................231
Zastępowanie istniejących już metod
(zmiana sposobu wypłaty dla niektórych pracowników) ................................233
Adnotacja Javy ...................................................................................................235
Używanie metod z klas i podklas ....................................................................236
Spis treści 9
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Eksperymenty ze zmiennymi ................................................................................283
Umieszczenie zmiennej na swoim miejscu ...................................................283
Wskazywanie zmiennej, gdzie ma iść .............................................................286
Przekazywanie parametrów .................................................................................290
Przekazywanie przez wartość .........................................................................290
Zwracanie wyniku .............................................................................................292
Przekazywanie wartości przez referencję ......................................................292
Zwracanie obiektu z metody ...........................................................................294
Epilog ..................................................................................................................296
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wyrażenia lambda ............................................................................................342
Typologia wyrażeń lambda ..............................................................................345
Używanie strumieni i wyrażeń lambda ..........................................................346
Po co się tak męczyć? .......................................................................................351
Referencje metod .............................................................................................353
Spis treści 11
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
ROZDZIAŁ 15: Fantazyjne typy referencyjne .......................................409
Typy w języku Java ..................................................................................................409
Interfejsy w języku Java ..........................................................................................410
Dwa interfejsy ...................................................................................................411
Implementowanie interfejsów ........................................................................412
Składanie wszystkich elementów razem .......................................................414
Klasy abstrakcyjne ..................................................................................................416
Opieka nad swoim zwierzakiem .....................................................................419
Używanie tych wszystkich klas ........................................................................421
Spokojnie! Nie widzisz podwójnie! .......................................................................423
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
CZĘŚĆ V: DEKALOGI .................................................................. 455
Skorowidz .......................................................................465
Spis treści 13
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
14 Java dla bystrzaków
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
O autorze
B
arry Burd uzyskał tytuł magistra nauk komputerowych na uniwersytecie
Rutgers, a doktorat z matematyki zrobił na uniwersytecie stanu Illinois.
W czasie, gdy pracował jako asystent nauczający w Champaign-Urbana
w stanie Illinois, pięciokrotnie był wybierany do listy nauczycieli najlepiej ocenia-
nych przez studentów uniwersytetu.
Dr Burd mieszka w Madison w stanie New Jersey razem ze swoją żoną od n lat,
gdzie n > 35. W wolnym czasie uwielbia oddawać się pracoholizmowi.
O autorze 15
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
16 Java dla bystrzaków
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Podziękowania
od autora
Z całego serca dziękuję Paulowi Levesque za jego pracę nad wieloma moimi
książkami z tej serii.
Dziękuję Chadowi Darby i Becky Whitney za ich wysiłki włożone w pracę nad
książką.
Jeanne Boyarsky, Frank Greco, Chandra Guntur i Michael Redlich — dziękuję wam
za nieocenione porady w sprawach technicznych.
Podziękowania od autora 17
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
18 Java dla bystrzaków
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wprowadzenie
J
ava to świetna technologia. Używam jej od lat. Lubię Javę, ponieważ jest
uporządkowana. Niemal każdy jej element przestrzega prostych zasad. Cza-
sami te zasady mogą wydawać się onieśmielające, ale w tej książce pomogę Ci się
w niech rozeznać. Jeżeli zatem chcesz zacząć używać języka Java, ale nie lubisz
tradycyjnego stylu książek technicznych w miękkiej okładce, to usiądź wygodnie,
zrelaksuj się i zacznij czytać Javę dla bystrzaków.
Muszę być jednak szczery. Jeżeli nie poznasz ogólnych zasad języka, to napisanie
jakiegokolwiek programu będzie naprawdę trudne. I dotyczy to każdego języka
programowania, nie tylko Javy. Jeżeli wpisujesz tylko kod, nie rozumiejąc, jak on
działa, a potem okazuje się, że program nie funkcjonuje tak, jak powinien, to nie
masz szans go poprawić.
W tej książce staram się dzielić tworzenie programów w Javie na łatwe do zro-
zumienia części. Każda z takich części zajmować będzie (mniej więcej) jeden
rozdział. Możesz zatem przejść w dowolne miejsce w książce — rozdział 5., rozdział
10. albo dowolny inny. Możesz nawet zacząć od środka wybranego rozdziału.
Starałem się przygotować interesujące przykłady, nie wprowadzając zależności
pomiędzy poszczególnymi rozdziałami. Jeżeli w danym rozdziale korzystam z waż-
nej idei omawianej w innym rozdziale, to załączam też notatkę ułatwiającą zro-
zumienie koncepcji.
Jeżeli zżera Cię ciekawość, to śmiało przejdź do kolejnych rozdziałów. Zawsze masz
możliwość zajrzenia do wcześniejszych, jeżeli zaszłaby taka potrzeba.
Wprowadzenie 19
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Konwencje używane w tej książce
Niemal każda książka techniczna zaczyna się krótkim opisem stosowanych w niej
czcionek, dlatego Java dla bystrzaków nie będzie tu wyjątkiem. Poniżej przedstawiam
zatem listę czcionek, z jakich będę korzystał w tej książce:
Nowe pojęcia oraz adresy stron WWW (adresy URL) będą podawane kursywą.
Często zobaczysz też czcionkę o stałej szerokości znaków. Za jej pomocą będę
podawał kod języka Java, komunikaty pojawiające się na ekranie i tym podobne
rzeczy. Co więcej, jeżeli tekst do wpisania będzie bardzo długi, to będzie się pojawiał
w osobnym wierszu (lub wierszach) z czcionką o stałej szerokości znaków.
Jeżeli już wiesz, czym właściwie jest Java, i wiesz, że chcesz zacząć z niej korzystać,
to pomiń rozdział 1. i od razu przejdź do rozdziału 2. Naprawdę, wcale się nie obrażę.
Jeżeli już wiesz, jak uruchomić program w języku Java i nie interesuje Cię, jakie
mechanizmy działają w tle, aby taki program mógł pracować, to możesz pominąć
rozdział 2. i zacząć od rozdziału 3.
Jeżeli już piszesz w języku C (ale nie C++), to zacznij od rozdziałów 2., 3. i 4.,
natomiast możesz tylko krótko przejrzeć rozdziały 5. i 6.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeżeli chcesz pominąć paski boczne albo ikony z informacjami technicznymi, to
proszę, nie krępuj się. W tej książce możesz pomijać, co tylko zechcesz.
Głupie założenia
W książce przyjąłem kilka założeń na Twój, Czytelniku, temat. Jeżeli któreś z tych
założeń nie jest prawidłowe, to z pewnością nic się nie stanie. Jeżeli wszystkie
założenia będą niewłaściwe, to… i tak kup sobie tę książkę.
Wprowadzenie 21
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Podział treści w książce
Cała książka została podzielona na podsekcje, które zostały pogrupowane w sekcje,
a te z kolej składają się na rozdziały zebrane w ramach pięciu części. (Podczas
pisania książki całkiem dobrze poznajesz jej strukturę. Po wielu miesiącach pisania
Twoje sny wypełnione są najróżniejszymi sekcjami i rozdziałami). Poniżej
przedstawiam poszczególne części tej książki:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Część 4. Sprytne techniki Javy
Jeżeli spodoba Ci się język Java i zechcesz dowiedzieć się więcej, to koniecznie
musisz zajrzeć do tej części książki. Zawarte w niej rozdziały zostały poświęcone
szczegółom — rzeczom niewidocznym przy pierwszym czytaniu dostępnych
materiałów. Po przeczytaniu wcześniejszych części i napisaniu kilku własnych
programów możesz przejść do zaawansowanych tematów omawianych w 4. czę-
ści tej książki.
Część 5. Dekalogi
Dekalogi przypominają sklepik ze słodkościami dla języka Java. W tej części znaj-
dziesz listy — listy porad, jak unikać błędów, jak znajdować niezbędne zasoby oraz
wiele innych interesujących ciekawostek.
Oczywiście w samej książce nie zauważysz mojego kręcenia głową. Potrzeba nam
zatem sposobu na wyróżnienie takich pobocznych przemyśleń. W książce używam
do tego ikon. Gdy zobaczysz ikonę Wskazówka albo Zapamiętaj, to znak, że na
chwilę zboczymy z tematu.
Wprowadzenie 23
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
„Jeżeli nie pamiętasz, co znaczy to-i-to, zajrzyj do bla-bla-bla”
albo „Więcej informacji znajdziesz w bla-bla-bla”.
1
Materiały dodatkowe dostępne w języku angielskim. — przyp. red.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zaczynamy pracę
z językiem Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TEJ CZĘŚCI
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 1
Klasy w C++
M
ów, co chcesz, o komputerach. Jeśli o mnie chodzi, to uważam, że kom-
putery są dobre z dwóch prostych powodów:
Gdy komputery pracują, nie odczuwają oporów, stresu, nudy ani zmęczenia.
Komputery są naszymi elektronicznymi niewolnikami. Mój komputer pracuje
24 godziny na dobę, 7 dni w tygodniu, wykonując obliczenia dla Cosmology@Home
— rozproszonego projektu obliczeniowego, którego celem jest zbadanie modeli
opisujących wszechświat. Czy jest mi przykro z powodu ciężkiej pracy mojego
komputera? Czy komputer narzeka? Czy komputer zgłosi mnie do Państwowej
Inspekcji Pracy? Nie.
Mogę żądać, wydawać rozkazy komputerowi i strzelać z bicza. Czy czuję się
(lub powinienem się czuć) choć odrobinę winny? Ani trochę.
Komputery, a nie papier poruszają idee. Jeszcze nie tak dawno temu, chcąc
wysłać komuś wiadomość, trzeba było wynająć posłańca. Posłaniec wsiadł na
swojego konia i osobiście dostarczył wiadomość. Wiadomość była zapisana na
papierze, pergaminie, glinianej tabliczce lub jakimkolwiek innym materialnym
nośniku, który był wtedy dostępny.
Cały ten proces wydaje się teraz marnotrawstwem czasu, ale to tylko dlatego,
że Ty i ja siedzimy wygodnie w erze elektronicznej. Wiadomości są ideami, a fizyczne
rzeczy, takie jak atrament, papier i konie, mają niewiele lub nic wspólnego
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
z prawdziwymi ideami; są tylko ich tymczasowymi nosicielami (pomimo że ludzie
przez kilka stuleci używali ich w ten sposób). Niemniej jednak same idee istnieją
bez papieru, bez koni i bez posłańców.
Dobrą rzeczą w komputerach jest to, że skutecznie przenoszą idee. Przetwarzają
wyłącznie idee, kilka fotonów i trochę energii elektrycznej. Robią to bez żadnych
kłopotów i bez żadnego dodatkowego fizycznego bagażu.
Kiedy zaczynasz skutecznie radzić sobie z ideami, dzieje się coś bardzo ciekawego.
Nagle znikają wszystkie dodatkowe obciążenia. Zamiast używać papieru i atra-
mentu, tworzysz liczby i koncepcje. Bez dodatkowych obciążeń możesz znacznie
szybciej wykonywać swoje prace, które dodatkowo są o wiele bardziej złożone niż
kiedykolwiek wcześniej.
Biorąc pod uwagę obecny stan rzeczy, nie możesz napisać tych instrukcji w ję-
zyku polskim ani w żadnym innym języku naturalnym. Fantastyka naukowa jest
pełna historii o ludziach, którzy mówią proste rzeczy robotom i uzyskują katastro-
falne, nieoczekiwane rezultaty. Język polski i inne mu podobne języki z kilku
powodów nie nadają się do komunikacji z komputerami:
Polskie zdanie może zostać źle zinterpretowane. „Żuj jedną tabletkę trzy razy
dziennie, aż do zakończenia”.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Aby powiedzieć komputerowi, co ma robić, musisz użyć specjalnego języka składa-
jącego się ze zwięzłych, jednoznacznych instrukcji. Specjalny język tego rodzaju
nazywany jest językiem programowania komputera. Zestaw instrukcji napisanych
w takim języku nazywany jest programem. Gdy potraktujemy je jak wielki zbiór
instrukcji, to będziemy mogli nazywać je oprogramowaniem lub kodem. Oto jak
wygląda taki kod, gdy jest napisany w języku Java:
Aby przygotować się do imprezy, trzeba będzie upiec ciasto. Jestem leniwy, więc
użyję gotowej mieszanki do pieczenia ciasta. Zobaczmy… dodaj wodę do mieszanki,
a następnie dodaj masło i jajka — hej, zaczekaj! Właśnie spojrzałem na listę skład-
ników. Co to jest MSG? A co to jest glikol propylenowy? Te składniki są chyba uży-
wane w płynach przeciw zamarzaniu, prawda?
Jednak zmienię plany i zrobię ciasto samodzielnie. Oczywiście, jest trochę trud-
niej, ale w ten sposób otrzymuję dokładnie to, czego chcę.
Programy komputerowe działają w ten sam sposób. Możesz użyć cudzego pro-
gramu lub napisać własny. Jeśli korzystasz z cudzego programu, używasz wszyst-
kiego, co jest w nim zawarte. Kiedy piszesz własny program, możesz dostosować
program specjalnie do swoich potrzeb.
1
W kręgach zawodowych obowiązki twórcy oprogramowania są zazwyczaj szersze niż obowiązki
samego programisty. Ale w tej książce używam terminów programista i twórca oprogramowania
niemalże zamiennie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Według ostatnich szacunków liczba wierszy kodu pisanego każdego dnia przez
samych programistów w Stanach Zjednoczonych przekracza liczbę cząsteczek
metanu na planecie Jowisz.2 Zastanów się, co można zrobić przy użyciu kompu-
tera. Mając wystarczająco dużo czasu, możesz napisać własny program realizu-
jący wybrane zadanie. (Oczywiście „wystarczająco dużo czasu” może okazać się
bardzo długim okresem, ale nie o to chodzi. Wiele ciekawych i przydatnych
programów można napisać w ciągu kilku godzin lub nawet minut).
2
Sam to wymyśliłem.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
1986 — Bjarne Stroustrup (ponownie w AT&T Bell Labs) opracowuje C++.
W przeciwieństwie do swojego przodka (języka C) język C++ pozwala na
programowanie obiektowe. Ta nowa możliwość stanowi ogromny krok
naprzód (zobacz następny podrozdział).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Ponadto technologia Java zapewnia interaktywne możliwości wszystkim urządzeniom
Blu-ray i jest najpopularniejszym językiem programowania w Indeksie
Społeczności Programistycznej (Programming Community Index) TIOBE
(www.tiobe.com/index.php/content/paperinfo/tpci), na PYPL: indeks popularności
języków programowania (PopularitY of Programming Language Index)
(http://sites.google.com/site/pydatalog/pypl/PyPL-PopularitY-of-Programming-
Language) oraz na innych indeksach.
Jestem pod wrażeniem.
Nagle dzwoni telefon. Obudziłem się wyrwany z głębokiego snu. (Jasne, nie po-
dobał mi się sen o egzaminie z historii, ale jeszcze bardziej nie lubię się z nagła
obudzić). Po pierwsze upuszczam telefon na podłogę. Po jego niezdarnym pod-
niesieniu wydaję zrzędliwe: „Cześć, kto to?”. Głos odpowiada: „Jestem reporterem
z »New York Timesa«. Piszę artykuł o Javie i muszę wiedzieć wszystko o tym
języku programowania w pięciu lub mniej słowach. Możesz nam przybliżyć ten
temat?”.
Mój umysł jest zbyt zamglony. Nie mogę myśleć. Mówię więc pierwszą rzecz,
która przychodzi mi do głowy, a potem wracam spać.
Rano prawie nie pamiętam rozmowy z reporterem. Nie pamiętam nawet, w jaki
sposób odpowiedziałem na to pytanie. Czy powiedziałem reporterowi, gdzie może
sobie wsadzić ten artykuł o Javie?
Języki obiektowe
Java jest zorientowana obiektowo. Co to znaczy? W przeciwieństwie do języków
takich jak FORTRAN, które koncentrują się na wydawaniu komputerowi poleceń
„Zrób to, zrób tamto”, języki obiektowe skupiają się na danych. Oczywiście pro-
gramy zorientowane obiektowo nadal informują komputer, co ma robić, ale za-
czynają jednak od uporządkowania danych, a polecenia pojawiają się później.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Języki zorientowane obiektowo są lepsze od języków „Zrób to, zrób tamto”, po-
nieważ organizują dane w sposób, który pomaga ludziom zrobić z nimi różne
rzeczy. Aby zmodyfikować dane, możesz korzystać z tego, co już masz, zamiast
wyrzucać wszystko, co zrobiłeś, i zaczynać od nowa za każdym razem, gdy mu-
sisz zrobić coś nowego. Chociaż programiści komputerowi są zazwyczaj inteli-
gentnymi ludźmi, trochę czasu zajęło im rozgryzienie tego tematu. Pełna lekcja
historii znajduje się w ramce „Kręta droga z FORTRAN-a do Javy” (ale nie będę
mieć Ci za złe, jeśli jej nie przeczytasz).
Języki, które skupiają się przede wszystkim na danych, nazywane są językami pro-
gramowania obiektowego. Stanowią one doskonałe narzędzia programistyczne. Oto
lista powodów, dlaczego tak jest:
Jeżeli najpierw skupiasz się na danych, to znaczy, że jesteś dobrym programistą
komputerowym.
Możesz w kółko rozszerzać i ponownie wykorzystywać opisy danych. Kiedy
próbujesz nauczyć stare programy FORTRAN nowych sztuczek, pokazują one,
jak bardzo są kruche. Po prostu psują się.
W latach 70. XX wieku języki zorientowane obiektowo, takie jak na przykład SIMULA
i Smalltalk, wspominane były jedynie w artykułach hobbystycznego magazynu kompu-
terowego. Natomiast w międzyczasie języki oparte na starym modelu FORTRAN roz-
mnażały się jak króliki.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jednak w 1986 roku człowiek o nazwisku Bjarne Stroustrup stworzył język o nazwie
C++. Stał się on bardzo popularny, ponieważ zmieszał starą terminologię języka C
z ulepszoną strukturą obiektową. Wiele firm odwróciło się od starego stylu programo-
wania FORTRAN/C i przyjęło język C++ jako swój standard.
Ale język C++ miał pewną wadę. Używając go, można pominąć wszystkie funkcje
obiektowe i napisać program przy użyciu starego stylu programowania FORTRAN/C.
Podczas pisania programu księgowego w języku C++ możesz wybrać jedną z dróg:
Zacząć od wydawania bezpośrednich poleceń komendami „zrób to”, tworząc
matematyczny odpowiednik polecenia: „Wydrukuj listę kont z zaległościami
i zrób to szybko”.
Wybrać podejście zorientowane obiektowo i opisać najpierw, jak ma wyglądać
konto.
W 1995 roku James Gosling z firmy Sun Microsystems stworzył język o nazwie Java.
Tworząc Javę, Gosling zapożyczył wygląd języka C++, ale większość jego starych
funkcji „zrób to, zrób tamto” wrzucił do kosza. Następnie dodał funkcje, dzięki któ-
rym tworzenie obiektów było znacznie łatwiejsze. W sumie Gosling stworzył język,
którego filozofia obiektowa jest przejrzysta i czysta. Kiedy programujesz w Javie, nie
masz innego wyboru; musisz pracować z obiektami. I tak właśnie powinno być.
Ale same obiekty to nie wszystko. Chociaż domy różnią się nieco od siebie,
wszystkie mają tę samą listę wspólnych cech. Na przykład każdy dom ma cechę
zwaną kolorem elewacji, każdy z nich ma też cechę zwaną stylem szafek kuchen-
nych. W Twoim programie obiektowym potrzebna jest główna lista zawierająca
wszystkie te cechy, które może mieć obiekt domu. Ta główna lista cech nazywa
się klasą.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
A to dopiero! Programowanie obiektowe otrzymało błędną nazwę. Powinno być
nazywane „programowaniem za pomocą klas i obiektów”.
Analogia nie kończy się na listach cech. Istnieje jeszcze jedna ważna paralela
między planami a klasami. Rok po utworzeniu projektu możesz użyć go do bu-
dowy dziesięciu domów. To samo dotyczy klas i obiektów. Najpierw programista
pisze kod opisujący klasę. Po uruchomieniu programu komputer tworzy obiekty
na podstawie klasy (projektu).
Wyglądałoby to tak, jakby architekt tworzył długą listę instrukcji zamiast nary-
sować plan. Aby znaleźć instrukcje dotyczące budowania sypialni, musisz zmo-
dyfikować plan budowy, sortując listę kolejnych kroków. Co gorsza, poszczegól-
ne instrukcje mogą się znajdować na stronach 234, 394 – 410, 739, 10 i 2. Jeśli
budowlaniec musiałby rozszyfrować skomplikowane instrukcje innych ludzi, zada-
nie byłoby dziesięć razy trudniejsze.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Rozpoczęcie prac nad programem od przygotowania klasy jest podobne do roz-
poczęcia budowy domu od przygotowania projektu. Jeśli zdecydujesz się budo-
wać domy z trzema i czterema sypialniami, możesz zacząć od ogólnego projektu
zwanego planem domu, na którym znajdzie się rysunek parteru i piętra, ale nie
będzie rozrysowanych na piętrze ścian wewnętrznych. Następnie tworzysz dwa
projekty piętra — jeden dla domu z trzema sypialniami, a drugi dla domu z cztere-
ma sypialniami. (Nazywasz te nowe plany domem z trzema sypialniami i domem
z czterema sypialniami).
RYSUNEK 1.1.
Terminologia
w programowaniu
obiektowym
Oryginalna klasa domu nazywa się klasą nadrzędną klas domów trzy- i czteropoko-
jowych. Podążając tym tropem, można powiedzieć, że klasy domów z trzema i czte-
rema sypialniami są podklasami oryginalnej klasy domu. Innymi słowy, oryginalna
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
klasa domu nazywa się klasą bazową klas domów trzy- i czteroosobowych. Klasy
domów z trzema i czterema sypialniami są klasami potomnymi pierwotnej klasy
domu (patrz rysunek 1.1).
Nie trzeba dodawać, że Twoi koledzy budowlańcy są zazdrośni. Cały tłum ludzi
kręci się wokół Ciebie, aby usłyszeć o Twoich wspaniałych pomysłach. W tym
momencie dorzucasz jeszcze jedną rewelację: „Tworząc klasę z podklasami, mo-
żemy w przyszłości ponownie wykorzystać nasz projekt. Jeśli ktoś w przyszłości
zażyczy sobie projektu domu z pięcioma sypialniami, możemy rozszerzyć nasz
oryginalny plan domu, tworząc wersję z pięcioma sypialniami. Nigdy więcej nie
będziemy musieli wydawać pieniędzy na oryginalny plan domu”.
„Ale co się stanie, jeśli ktoś zechce inny projekt parteru? — pyta kolega z tylnego
rzędu. — Będzie trzeba wyrzucić oryginalny plan domu, czy zaczniemy poprawiać
gotowy, oryginalny projekt? To będzie sporo kosztować, prawda?”
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Teraz odłóż na chwilę tę książkę i rozejrzyj się po swoim pokoju. (Jeśli nie sie-
dzisz teraz w swoim pokoju, spróbuj sobie go wyobrazić).
W pokoju jest kilka krzeseł, a każde krzesło jest obiektem. Każdy z tych obiektów
jest przykładem tej eterycznej rzeczy zwanej klasą Chair. Tak to działa — klasa
jest opisem istoty krzesła, a każde krzesło jest obiektem.
Klasa nie jest jedynie zbiorem elementów. W pewnym sensie jest ona ideą pew-
nego rodzaju rzeczy. Kiedy mówię o klasie krzeseł z Twojego pokoju, mówię o tym,
że każde z nich ma nogi, siedzisko, kolor i tak dalej. Każde z krzeseł znajdujących
się w pokoju może mieć inny kolor, ale to nie ma znaczenia. Kiedy mówisz o klasie
rzeczy, skupiasz się na właściwościach, które ma każda z tych rzeczy.
A oto inny sposób myślenia o klasie. Wyobraź sobie tablicę wyświetlającą dane
Twoich trzech kont bankowych (patrz tabela 1.1).
Możesz myśleć o zbiorze nagłówków kolumn tabeli jak o klasie, a o każdym wierszu
tabeli jak o obiekcie. Nagłówki kolumn tabeli opisują klasę Account.
Zgodnie z nagłówkami kolumn tabeli każde konto ma numer konta, typ konta
i saldo. Definiując to w terminologii programowania zorientowanego obiektowo,
każdy obiekt klasy Account (tj. każda instancja klasy Account) ma numer konta,
typ konta i saldo konta. Tak więc dolny wiersz tabeli jest obiektem o numerze
konta 16-17238-13344-7, ma on typ Oszczędności i saldo 247,38. Po otworzeniu
nowego konta mielibyśmy kolejny obiekt, a tabela powiększyłaby się o dodatkowy
wiersz. Taki nowy obiekt byłby instancją tej samej klasy Account.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Co dalej?
Ten rozdział wypełniony jest ogólnymi opisami różnych rzeczy. Ogólny opis jest
dobry w momencie, gdy dopiero zaczynasz poznawać tę tematykę, ale tak na-
prawdę rozumiesz to wszystko po poznaniu dodatkowych szczegółów. Dlatego
w następnych kilku rozdziałach będę omawiał te szczegóły.
Proszę więc, przewróć tę stronę. Następny rozdział już się nie może doczekać, aż
go przeczytasz.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
40 CZĘŚĆ I Zaczynamy pracę z językiem Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Przygotowanie do pisania
i uruchamiania programów Javy
Rozdział 2
Wszystko
o oprogramowaniu
N
ajlepszym sposobem na poznanie języka Java jest pisanie w nim progra-
mów. Zajmując się tym językiem, piszesz, testujesz i uruchamiasz własne
programy. Ten rozdział jest wstępem do języka Java. Opisuje ogólną kon-
figurację oprogramowania — oprogramowania, które musisz mieć na swoim
komputerze niezależnie od tego, czy używasz systemu operacyjnego Windows,
Mac, Linux, czy prywatnego systemu operacyjnego Janusza. W tym rozdziale nie
znajdziesz konkretnych instrukcji instalacji dla systemu Windows, komputerów
Mac ani dla żadnego innego systemu.
Aby uzyskać instrukcje konfiguracji właściwe dla swojego systemu, odwiedź stronę
internetową tej książki (https://users.drew.edu/bburd/JavaForDummies/).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Skrócona instrukcja
Jeśli jesteś już doświadczonym weteranem komputerów i przetwarzania danych
(cokolwiek to znaczy) i dodatkowo brakuje Ci cierpliwości, aby uzyskać szczegó-
łowe instrukcje ze strony tej książki, to możesz spróbować zainstalować wyma-
gane oprogramowanie, postępując zgodnie z ogólnymi instrukcjami zawartymi
w tym podrozdziale. Instrukcje te działają na wielu komputerach, ale nie na
wszystkich. W tym podrozdziale nie ma żadnych szczegółowych kroków, żadnych
specjalnych rozwiązań typu „jeśli to, to zrób to” albo „to działa, ale lepiej będzie
zrobić coś innego”.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
IntelliJ IDEA
W badaniu Baeldung na temat środowisk IDE Javy (http://www.baeldung.com/
java-ides-2016) środowisko IntelliJ IDEA jest na drugim miejscu, z 43,6%
wszystkich programistów na pokładzie.
Gdy odwiedzisz stronę www.jetbrains.com/idea, możesz pobrać wersję
społecznościową Community Edition (która jest bezpłatna) lub wersję Ultimate
Edition (która nie jest już darmowa). Aby uruchomić przykłady zawarte w tej
książce, możesz użyć wersji społecznościowej. Tej wersji możesz też używać
do tworzenia komercyjnego oprogramowania!
NetBeans
Badanie Baeldung na temat wykorzystania środowisk IDE Javy
(http://www.baeldung.com/java-ides-2016) daje NetBeans zaledwie 5,9%.
Ale za to środowisko NetBeans jest oficjalnym środowiskiem Java firmy Oracle.
Jeśli witryna zaoferuje wybór pakietów do pobrania, wybierz pakiet Java SE.
Aby uzyskać własną kopię NetBeans, odwiedź dział plików do pobrania na
stronie https://netbeans.org/.
Środowisko NetBeans jest bezpłatne do użytku komercyjnego i niekomercyjnego.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
I to wszystko! Ale pamiętaj: nie każdy (komputerowy maniak czy też nie) może
bezproblemowo skorzystać z tych instrukcji. Oto inne rozwiązania:
1
Kontakt z autorem w języku angielskim — przyp. red.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Potrzebujesz kompilatora.
Kompilator pobiera napisany przez Ciebie kod Java i zamienia go na zbiór instrukcji
zwanych kodem bajtowym.
Ludzie nie są w stanie łatwo tworzyć ani odszyfrowywać instrukcji kodu bajtowego.
Jednak odpowiednie programy uruchamiane na komputerze mogą interpretować
i wykonywać instrukcje kodu bajtowego.
W sieci WWW znajdują się darmowe, dostępne do pobrania wersje każdego z tych
narzędzi:
Pozostała część tego rozdziału opisuje kompilatory, maszyny JVM i środowiska IDE.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Czym jest kompilator?
Kompilator przyjmuje napisany przez Ciebie kod Javy i zamienia go w zbiór instrukcji
zwanych kodem bajtowym.
— BARRY BURD, JAVA DLA BYSTRZAKÓW
Kod Javy pokazany na listingu 2.1 sprawdza, czy są wolne pokoje w małym hotelu
(w hotelu z pokojami o numerach od 1 do 99). Nie możesz uruchomić kodu z li-
stingu 2.1 bez dodawania kilku kolejnych wierszy. Ale tutaj w rozdziale 2. te do-
datkowe wiersze nie są ważne. Ważne natomiast jest to, że wpatrując się w kod,
mrużąc oczy i ignorując te wszystkie dziwne znaki interpunkcyjne, możesz zo-
baczyć, co ten kod próbuje zrobić:
Jeśli nie widzisz podobieństw między kodem pokazanym na listingu 2.1 a jego
polskim odpowiednikiem, nie martw się. Czytasz książkę Java dla bystrzaków i jak
większość ludzi możesz nauczyć się czytać i pisać kod znajdujący się na listingu
2.1. Kod pokazany na tym listingu nazywany jest kodem źródłowym Javy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
A oto drobny szczegół: komputery nie są istotami ludzkimi. Zwykle nie wykonują
instrukcji takich jak instrukcje pokazane na listingu 2.1. Oznacza to, że kompu-
tery nie przestrzegają instrukcji kodu źródłowego Javy. Zamiast tego stosują ta-
jemnicze instrukcje, takie jak te z listingu 2.2.
Listing 2.2. Kod z listingu 2.1 przetłumaczony na kod bajtowy języka Java
aload_0
iconst_1
putfield Hotel/roomNum I
goto 32
aload_0
getfield Hotel/guests [I
aload_0
getfield Hotel/roomNum I
iaload
ifne 26
getstatic java/lang/System/out Ljava/io/PrintStream;
new java/lang/StringBuilder
dup
ldc "Pokój "
invokespecial java/lang/StringBuilder/<init>(Ljava/lang/String;)V
aload_0
getfield Hotel/roomNum I
invokevirtual java/lang/StringBuilder/append(I)Ljava/lang/StringBuilder;
ldc " jest wolny."
invokevirtual
java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
invokevirtual java/lang/StringBuilder/toString()Ljava/lang/String;
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
iconst_0
invokestatic java/lang/System/exit(I)V
goto 32
aload_0
dup
getfield Hotel/roomNum I
iconst_1
iadd
putfield Hotel/roomNum I
aload_0
getfield Hotel/roomNum I
bipush 100
if_icmplt 5
getstatic java/lang/System/out Ljava/io/PrintStream;
ldc "Brak wolnych pokoi"
invokevirtual java/io/PrintStream/println(Ljava/lang/String;)V
return
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
rzędzia do jego uruchomienia) do swojego kodu źródłowego. Program ten jest
kompilatorem. Kompilator tłumaczy instrukcje kodu źródłowego na instrukcje
kodu bajtowego języka Java. Innymi słowy, kompilator pobiera kod, który można
napisać i zrozumieć (jak kod pokazany na listingu 2.1), i tłumaczy go na kod,
który komputer ma szansę wykonać (jak kod z listingu 2.2).
Możesz umieścić swój kod źródłowy w pliku o nazwie Hotel.java. Jeśli tak zrobisz,
to kompilator najprawdopodobniej umieści swój kod bajtowy Javy w innym pliku
o nazwie Hotel.class. Zwykle nie będziemy się przejmować przeglądaniem kodu
bajtowego znajdującego się w pliku Hotel.class. W rzeczywistości kompilator nie
przekodowuje pliku Hotel.class do postaci zwykłego tekstu, więc nie można zbadać
kodu bajtowego za pomocą zwykłego edytora. Jeśli spróbujesz otworzyć Hotel.class
za pomocą Notatnika, TextEdit, KWrite lub nawet Microsoft Word, zobaczysz
tylko kropki, zawijasy i inne dziwactwa. Aby utworzyć (zwizualizować) kod znaj-
dujący się na listingu 2.2, musiałem zastosować jeszcze jedno narzędzie do kon-
wersji mojego pliku Hotel.class. To narzędzie wyświetli tekstową wersję pliku kodu
bajtowego Java. Użyłem edytora Java Bytecode Ando Saabasa (www.cs.ioc.ee/
~ando/jbe).
Oto hipotetyczna sytuacja: jest rok 1992 (kilka lat przed upublicznieniem języka
Java) i uruchamiasz system operacyjny Linux na komputerze ze starym proce-
sorem Pentium. Twój znajomy używa Linuksa na komputerze z innym rodzajem
procesora — procesorem PowerPC. (W latach 90. Intel Corporation tworzył pro-
cesory Pentium, a IBM produkował procesory PowerPC).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 2.3 zawiera zestaw instrukcji wyświetlających słowa „Witaj, świecie!” na
ekranie komputera. Instrukcje działają na procesorze Pentium w systemie ope-
racyjnym Linux2.
2
Wykorzystuję tu instrukcje procesora firmy Intel z książki Linux Assembly HOWTO Konstantina
Boldysheva (http://tldp.org/HOWTO/Assembly-HOWTO/hello.html).
3
Wykorzystuję tu kod procesora PowerPC ze strony „PowerPC Assembly” Hollisa Blancharda
(www.ibm.com/developerworks/library/l-ppc). Hollis przeanalizował i recenzował dla mnie
podrozdział „Czym jest wirtualna maszyna Javy?”. Dziękuję, Hollis.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Instrukcje pokazane na listingu 2.3 działają bezproblemowo na procesorze klasy
Pentium. Ale te same instrukcje nic nie znaczą dla procesora PowerPC. Podobnie
jak instrukcje zawarte na listingu 2.4 działają dobrze na procesorze PowerPC, ale
są kompletnym bełkotem dla komputera z procesorem Pentium. Oprogramowa-
nie procesora PowerPC Twojego znajomego może więc nie być dostępne na Twoim
komputerze. A oprogramowanie Twojego komputera z procesorem Pentium Intela
może w ogóle nie działać na komputerze Twojego przyjaciela.
Teraz idź do domu swojego kuzyna. Komputer kuzyna ma procesor Pentium (tak
jak i Twój), ale jego komputer ma system Windows zamiast Linuksa. Co zrobi
komputer Twojego kuzyna, gdy podasz mu kod procesora Pentium, na przykład
taki jak na listingu 2.3? Zawoła: „To nie jest prawidłowa aplikacja Win32” lub
„Windows nie może otworzyć tego pliku”. Co za bałagan!
Kod bajtowy języka Java wprowadza porządek do tego całego chaosu. W przeci-
wieństwie do kodu z listingu 2.3 i 2.4 kod bajtowy Java nie jest specyficzny dla
jednego rodzaju procesora ani dla jednego systemu operacyjnego. Zamiast tego
każdy komputer może mieć wirtualną maszynę Javy, a instrukcje kodu bajtowego
języka Java są uruchamiane na dowolnej wirtualnej maszynie Javy naszego kom-
putera. Maszyna wirtualna JVM działająca na procesorze Pentium z systemem
Linux tłumaczy instrukcje kodu bajtowego Java na kod widoczny na listingu 2.3.
JVM działająca na platformie z procesorem PowerPC z Linuksem tłumaczy instruk-
cje kodu bajtowego Java na kod widoczny na listingu 2.4.
Jeśli napiszesz program w języku Java i skompilujesz ten program na kod bajtowy,
to JVM może uruchomić ten kod na Twoim komputerze i na komputerze Twojego
przyjaciela, a także na superkomputerze Twojej babci, i jeśli będziemy mieli szczę-
ście, to JVM uruchomi go także na telefonie komórkowym lub na tablecie.
Aby zobaczyć przykładowy kod bajtowy Javy, spójrz na listing 2.2. Ale pamiętaj:
nigdy nie musisz pisać ani odszyfrowywać kodu bajtowego Javy. Zapisywanie
kodu bajtowego jest zadaniem kompilatora. Odszyfrowywanie kodu bajtowego to
zadanie dla wirtualnej maszyny Javy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 2.1.
Wyimaginowane
spotkanie Rady
Bezpieczeństwa ONZ
(Oczywiście nie dogadujesz się z żadnym z nich. Zawsze jesteście serdeczni wo-
bec siebie, ale nigdy nie jesteście szczerzy. Czego niby oczekujesz? To przecież
polityka!) Wybitny przedstawiciel języka Java jest na podium. Przedstawiciel Javy
mówi kodem bajtowym i ani Ty, ani Twoi koledzy ambasadorzy (Mac i Linux) nie
rozumiecie słowa kodu bajtowego Java.
Ale każdy z was ma interpretera. Podczas gdy przedstawiciel Javy mówi, Twój
interpreter tłumaczy z kodu bajtowego Java na język Windows, natomiast inny
interpreter tłumaczy z kodu bajtowego Java na język Macintosha. Trzeci zaś
tłumaczy kod bajtowy Java na język zrozumiały dla Linuksa.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
komputer przez proces wykonania kolejnych instrukcji kodu bajtowego. Bit po
bicie pobiera ona kod bajtowy Twojego programu i wykonuje opisane w nim in-
strukcje. JVM interpretuje kod bajtowy dla systemu Windows, komputera Mac
lub komputera z systemem Linux albo dla dowolnego innego komputera, którego
używasz. Doskonale! Sprawia to, że programy Javy są bardziej przenośne niż
programy napisane w jakimkolwiek innym języku.
Numeracja wersji narzędzi Java może być myląca. Zamiast kolejnych numerów wersji
„Java 1”, „Java 2” i „Java 3” stosowana jest numeracja, która przypomina prawdziwy
tor przeszkód. W tabeli znajdującej się na końcu tej ramki przedstawiam sekwencję
kolejnych wersji Javy w poszczególnych latach. Każda z tych wersji ma kilka nazw.
Wersja produktu jest oficjalną nazwą używaną na całym świecie, a wersja programisty
to numer identyfikujący wersje, pozwalający programistom na ich rozróżnianie.
(W swoim żargonie programiści używają różnego rodzaju nazw dla różnych wersji
języka Java). Nazwa kodowa jest bardziej zabawną nazwą, która identyfikuje wersję
podczas jej tworzenia.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Java 2 Standard Edition 1.2, która do dziś wprowadza w błąd wszystkich. W tym cza-
sie każdy, kto używa terminu Java Development Kit, został poproszony o używanie
nazwy pakietu Software Development Kit (SDK).
W roku 2004 firma odeszła od nazw wersji platformy zaczynających się od cyfry 1,
natomiast w roku 2006 z nazwy platformy Java usunięto cyfry 2 i 0.
Zdecydowanie najbardziej znaczące zmiany dla programistów Javy pojawiły się w roku
2004. Wraz z wydaniem J2SE 5.0 osoby nadzorujące rozwój platformy Java wprowa-
dziły zmiany w języku, dodając nowe funkcje — takie jak typy generyczne, adnotacje
i rozbudowane instrukcje for. (Aby zobaczyć w działaniu adnotacje języka Java,
przejdź do rozdziałów 8., 9. i 16. Natomiast przykłady użycia rozszerzonych instruk-
cji for i typów generycznych znajdziesz w rozdziałach 11. i 12.).
1999
2001
2003
2005
2007
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Platforma Nazwa kodowa Funkcje
2008
2009
2010
2012
2013
2015
2016
Tworzenie oprogramowania
Wszystko to wydarzyło się już wcześniej i wszystko się jeszcze powtórzy.
— PETER PAN (J.M. BARRIE) I BATTLESTAR GALACTICA
(2003 – 2009, NBC UNIVERSAL)
RYSUNEK 2.2.
Tworzenie programu
w języku Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Najpierw piszesz program. Po napisaniu pierwszego szkicu programu wielokrot-
nie go kompilujesz, uruchamiasz i modyfikujesz. Przy odrobinie doświadczenia
kroki kompilacji i uruchamiania stają się bardzo łatwe. W wielu przypadkach jedno
kliknięcie myszą rozpoczyna kompilację lub uruchomienie.
Jednak pisanie pierwszej wersji programu i modyfikowanie jego kodu nie są zada-
niami na jedno kliknięcie. Tworzenie kodu wymaga czasu i koncentracji.
Nigdy nie zniechęcaj się, gdy pierwsza wersja Twojego kodu nie działa. Co więcej,
nie poddawaj się, nawet gdy Twój projekt nie działa po 25. poprawce. Przepisy-
wanie i zmienianie kodu jest jedną z najważniejszych rzeczy, które możesz robić
(może z wyjątkiem zapewnienia pokoju na świecie).
RYSUNEK 2.3.
Kto co robi
i z jakim kodem
Rysunek 2.3 zawiera zbyt wiele informacji jak na potrzeby większości ludzi. Jeśli
kliknę ikonę Uruchom, nie muszę pamiętać, że komputer uruchamia kod w moim
imieniu. I wcale mnie nie obchodzi, czy komputer uruchamia mój oryginalny kod
źródłowy Java, czy też jego wersję przerobioną na kod bajtowy. W tej książce
wielokrotnie piszę: „kiedy uruchamiasz kod Java” lub „kiedy komputer uruchamia
Twój program Javy”. Informacje z rysunku 2.3 nie są Ci potrzebne do szczęścia.
Rysunek 2.3 ma jedynie pomóc, jeśli luźne sformułowania przedstawione na ry-
sunku 2.2 wprowadzają Cię w błąd. Jeśli nie masz problemów z rysunkiem 2.2, to
możesz go zignorować.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Czym jest zintegrowane środowisko programistyczne?
„Zintegrowane środowisko programistyczne pomaga zarządzać kodem Java i zapewnia
wygodne sposoby pisania, kompilowania i uruchamiania kodu”.
— BARRY BURD, JAVA DLA BYSTRZAKÓW
RYSUNEK 2.4.
Opracowywanie kodu
bez wykorzystania
zintegrowanego
środowiska
programistycznego
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 2.5.
Opracowywanie kodu
w zintegrowanym
środowisku
programistycznym
Eclipse
RYSUNEK 2.6.
Używanie
konstruktora Swing
GUI typu „przeciągnij
i upuść” w IDE
NetBeans
Aby uruchomić program, możesz kliknąć przycisk znajdujący się na pasku na-
rzędzi lub wybrać z menu polecenie Run. Aby skompilować program, w wielu
przypadkach nie musisz robić zupełnie nic. (Nie musisz nawet wydawać polece-
nia. Niektóre środowiska IDE automatycznie kompilują Twój kod podczas jego
pisania).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
58 CZĘŚĆ I Zaczynamy pracę z językiem Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Dokumentowanie kodu
Rozdział 3
Używanie
podstawowych
elementów
„Bce мыcли, кoтopыe имeют oгpoмныe пocлeдcтвия вceгдa пpocты”.
(Wszystkie wielkie idee są proste)
— LEW TOŁSTOJ
T
en cytat dotyczy wszystkich możliwych rzeczy — życia, miłości albo pro-
gramowania komputerów. To właśnie dlatego w tym rozdziale zastosuję
podejście wielowarstwowe. Przedstawię w nim pierwsze szczegóły na temat
programowania w Javie. A dzięki tym szczegółom zauważysz prostotę tego języka.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Mówimy w języku Java
Spróbuj sobie wyobrazić, jak może wyglądać cały język polski. Co widzisz? Być
może po prostu słowa, słowa, słowa. (Podobną wizję miał Hamlet). Przyglądając się
językowi pod mikroskopem, zobaczymy tylko kolejne słowa. Taki obraz składa-
jący się z samych słów jest jak najbardziej w porządku, ale jeżeli trochę się cofniesz,
to zobaczysz dodatkowo dwie rzeczy:
gramatykę języka,
W pierwszej kategorii (gramatyka) znajdują się takie reguły jak „czasownik jest
zgodny z rzeczownikiem pod względem liczby i osoby”. Do drugiej kategorii
(wyrażenia, powiedzenia itp.) zaliczają się takie informacje jak „Juliusz Cezar był
słynnym rzymskim imperatorem, dlatego lepiej nie nadawaj swojemu synowi
imion Juliusz Cezar, chyba że chcesz, żeby obrywał codziennie po szkole”.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Specyfikację języka, dokumenty opisujące API oraz inne elementy dokumentacji
Javy możesz pobrać (albo po prostu je przejrzeć), przeglądając stronę http://
docs.oracle.com/javase/specs.
Pierwsza część języka, czyli jego specyfikacja, jest względnie niewielka. Nie oznacza
to, że nie zajmie Ci dużo czasu poznawanie poszczególnych reguł zapisanych
w specyfikacji języka. Mimo to musisz pamiętać, że inne języki mają dwa, trzy,
a nawet dziesięć razy więcej reguł.
Druga część języka Java, czyli jego API, jest tak wielka, że może być naprawdę
przytłaczająca. W API znajdziemy całe tysiące różnych nazw, a grupa ta zwiększa
się z każdym wydaniem nowszej wersji języka. Przerażające? Cóż, dobra wiadomość
jest taka, że z API nie trzeba zapamiętywać niczego. Absolutnie niczego. Nawet
kawałeczka. W dokumentacji zawsze możesz odszukać potrzebne Ci elementy
i zignorować wszystkie pozostałe. To, z czego będziesz korzystać, często samo zo-
stanie Ci w głowie. To, czego nie używasz, często ulegnie zapomnieniu (to całkiem
typowe dla ludzi i programistów).
Nikt nie wie wszystkiego na temat języka Java. Jeżeli jesteś programistą języka
Java często piszącym programy otwierające nowe okna, to doskonale wiesz, jak
używać klasy JFrame z API języka. Jeżeli jednak rzadko piszesz programy otwie-
rające nowe okna, a zachodzi taka potrzeba, musisz przejrzeć w dokumentacji
opis klasy JFrame. Sądzę, że gdybyśmy zabrali typowemu programiście dostęp do
dokumentacji API języka, to byłby w stanie używać nie więcej niż 2% zawartych
w niej nazw.
Styl używany w książkach z serii Dla bystrzaków może Ci się bardzo podobać, ale
niestety dokumentacja API języka Java napisana jest zupełnie inaczej. Doku-
mentacja API jest jednocześnie spójna i precyzyjna. Pewną pomoc przy rozszy-
frowywaniu języka i stylu tej dokumentacji znajdziesz na stronie WWW związanej
z tą książką (https://users.drew.edu/bburd/JavaForDummies/).
Jeżeli interesuje Cię praca w JCP, to możesz odwiedzić stronę www.jcp.org. A je-
żeli interesuje Cię projekt OpenJDK, to odwiedź stronę http://openjdk.java.net.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Ludzie pracujący w procesie JCP wcale nie ukrywają przed światem swoich prac
nad oficjalnym API Javy. Jeżeli masz na to ochotę, to możesz przejrzeć przygo-
towane przez nich programy. Po zainstalowaniu Javy na swoim komputerze znaj-
dziesz na dysku plik src.zip. Możesz otworzyć go w swoim ulubionym programie
obsługującym archiwa ZIP. Twoim oczom ukaże się wtedy cały kod API języka Java.
Poszczególne rodzaje słów w języku Java różnią się od siebie na podobnej zasa-
dzie, na jakiej różnią się też słowa w języku polskim. W zdaniu „Adam jest osobą”
słowo osoba jest bardzo podobne do słowa kluczowego języka Java. Niezależnie
od tego, kto będzie używał słowa osoba, będzie ono zawsze miało takie samo
znaczenie. (Oczywiście można sobie wymyślać najdziwniejsze wyjątki, ale lepiej
się powstrzymać).
Z kolei słowo Adam jest podobne do identyfikatora w języku Java, ponieważ Adam
jest imieniem określonej osoby. Takie słowa jak Adam, Bożydar albo Kowalski nie
mają w języku polskim przypisanego znaczenia. W zależności od kontekstu
opisują one określone osoby. A nazwami stają się w momencie, gdy zostaną
wybrane przez rodziców dla swoich nowo narodzonych dzieci.
Zastanówmy się teraz nad zdaniem: „Juliusz Cezar jest osobą”. Wymawiając to
zdanie, najprawdopodobniej masz na myśli koleżkę, który rządził Rzymem aż do
idów marcowych. Co prawda w języku polskim nazwa Juliusz Cezar nie jest po-
wiązana z niczym konkretnym, to jednak każdy, kto używa tych słów, niemal
zawsze odnosi się do tej samej osoby. Jeżeli język polski byłby językiem progra-
mowania, to nazwa Juliusz Cezar byłaby identyfikatorem API języka.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Identyfikatory. Identyfikator jest nazwą nadaną jakiemuś elementowi.
Znaczenie identyfikatorów może zmieniać się w różnych programach,
ale znaczenie niektórych z nich może zmieniać się częściej.
Identyfikatory tworzone przeze mnie i przez Ciebie. Jako programista języka Java
(nawet początkujący) tworzysz nowe nazwy dla klas i innych elementów,
które umieszczasz w swoim programie. Oczywiście możesz nadać pewnemu
elementowi nazwę Podstawa, a kolega pracujący w drugim pokoju może użyć tej
samej nazwy dla innego elementu. To zupełnie w porządku, ponieważ w języku
Java nazwa Podstawa nie ma żadnego odgórnie narzuconego znaczenia. W Twoim
programie nazwa Podstawa może oznaczać podstawowy kurs wymiany waluty,
natomiast u Twojego kolegi ta sama nazwa może dotyczyć podstawy
prostopadłościanu. Nie powstaną w ten sposób żadne konflikty, ponieważ
pracujecie nad dwoma różnymi programami.
Identyfikatory z API języka Java. Członkowie JCP przygotowali nazwy dla wielu
elementów i wrzucili całe tysiące różnych nazw do API języka. Całe API
dostarczane jest z każdą wersją języka Java, a zatem nazwy te dostępne są dla
każdego programisty tworzącego program w Javie. Przykładami takich nazw są:
String, Integer, JWindow, JButton, JTextField albo File.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Prawda jest taka, że odkrywanie nowego programu w języku Java jest niezwy-
kłym doświadczeniem. Najpierw gapisz się na program ze strachem. Potem
uruchamiasz go, żeby sprawdzić, co właściwie robi. Później przez pewien czas
wpatrujesz się w sam program i czytasz czyjeś wyjaśnienia poszczególnych ele-
mentów programu. Następnie jeszcze trochę gapisz się na kod programu i po-
nownie go uruchamiasz. Ostatecznie jakoś udaje Ci się porozumieć z programem.
(I nie wierz mądralom mówiącym, że nigdy nie musieli przechodzić przez te
etapy. Nawet doświadczeni programiści powoli i ostrożnie zaznajamiają się z no-
wymi projektami).
Nie musisz samodzielnie wpisywać kodu z listingu 3.1 (ani z żadnego innego li-
stingu z tej książki). Możesz pobrać wszystkie kody z serwera FTP wydawnictwa
(ftp://ftp.helion.pl/przyklady/javby7.zip).
RYSUNEK 3.1.
Do uruchomienia
programu z listingu
3.1 użyłem pakietu
Eclipse
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Poznawanie prostego programu
w języku Java
W tym podrozdziale wyjaśnię, przeanalizuję, rozłożę na części pierwsze i gene-
ralnie objaśnię elementy programu Javy przedstawionego wcześniej na listingu 3.1.
Klasa Javy
Java jest językiem obiektowym, dlatego Twoim głównym zadaniem jest przygo-
towanie opisu klas i obiektów. (Jeżeli wydaje Ci się to dziwne, to przeczytaj pod-
rozdział o programowaniu obiektowym z rozdziału 1.).
W czasie tych szczególnych dni, gdy czuję się wyjątkowo sentymentalnie, mówię
moim słuchaczom, że Java jest zdecydowanie bardziej obiektowym językiem niż
wiele innych tak zwanych języków zorientowanych obiektowo. Mówię tak dla-
tego, że w Javie nie można zrobić niczego bez uprzedniego utworzenia przy-
najmniej jednej klasy. Wyobraź sobie, że jako uczestnik Milionerów słyszysz, że
Hubert Urbański mówi: „A teraz czas na przerwę reklamową”, a Ty przerywasz
mu, mówiąc: „Przykro mi, Hubercie, ale nie możesz wydać żadnej instrukcji bez
uprzedniego umieszczenia jej w klasie”.
Kod z listingu 3.1 jest programem w języku Java i jako taki opisuje pewną klasę.
Sam napisałem ten program, dlatego mogę też wybrać nazwę dla swojej klasy.
W tym przypadku wybrałem nazwę Displayer, ponieważ program zajmuje się
wyświetlaniem wiersza tekstu na ekranie komputera. To właśnie dlatego w pierw-
szym wierszu listingu 3.1 znajdują się słowa class Displayer (rysunek 3.2).
RYSUNEK 3.2.
Program w Javie
jest klasą
Pierwsze dwa słowa w listingu 3.1, czyli public i class, są słowami kluczowymi
języka Java. (Zajrzyj do punktu „Słowa w programie w języku Java” w tym roz-
dziale). Nie ma znaczenia, kto pisze program, słowa public i class będą zawsze
używane w ten sam sposób. Z drugiej strony słowo Displayer z listingu 3.1 jest
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
identyfikatorem. Wymyśliłem sobie słowo Displayer, gdy pisałem ten rozdział.
W tym przypadku Displayer jest nazwą konkretnej klasy — dokładnie tej, którą
tworzę w ramach pisania programu.
W tej książce cały czas mówimy na temat klas, ale najlepszy opis klas języka Java
(oraz wyjaśnienie użycia słowa class na listingu 3.1) znajdziesz w rozdziale 7.
Słowo public oznacza, że inne klasy języka Java (klasy inne niż klasa Displayer z li-
stingu 3.1) mogą korzystać z funkcji zadeklarowanych na listingu 3.1. Więcej in-
formacji na temat znaczenia słowa public i sposobów używania go w progra-
mach Javy znajdziesz w rozdziałach 7. i 14.
Masz też zabieganego szefa, który każe Ci wykonywać tę pracę. Szef zmusza
Cię do pracy, wypowiadając słowa „naprawAlternator”. Oznacza to, że nakazuje
Ci wykonać pewną pracę, wypowiadając jej nazwę.
W tym scenariuszu użycie słowa metoda wcale nie byłoby wielkim nadużyciem.
Masz przecież swoją metodę naprawiania alternatora. Twój szef wywołuje tę
metodę, a Ty reagujesz na nią, wykonując operacje określone w liście instrukcji,
które są powiązane z daną metodą.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeżeli rozumiesz to wszystko (mam nadzieję, że tak jest), to możesz przejść do
czytania opisów metod języka Java. W języku tym metoda jest listą instrukcji do
wykonania. Każda metoda ma swoją nazwę, a poprzez użycie tej nazwy w swoim
programie nakazujesz komputerowi wykonanie instrukcji powiązanych z tą nazwą.
Jeszcze nigdy nie pisałem programu sterującego robotem tak, żeby naprawił al-
ternator. Jeżeli jednak zdarzyłoby mi się napisać taki program, to z całą pewno-
ścią znalazłaby się w nim metoda naprawAlternator. Lista instrukcji powiązana
z metodą naprawAlternator byłaby podobna do tej z listingu 3.2.
Nie analizuj zbyt dokładnie zawartości listingów 3.2 i 3.3. Cały znajdujący się na
nich kod jest zmyślony! Przygotowałem go tak, żeby wyglądał jak prawdziwy kod
języka Java, ale wcale nim nie jest. Najważniejsze w kodzie z listingów 3.2 i 3.3
jest to, że ilustruje on pewne reguły języka Java. Dlatego właśnie kod z tych dwóch
listingów należy traktować z przymrużeniem oka.
Gdzieś w moim programie (gdzieś poza listingiem 3.2) muszę umieścić jeszcze
instrukcję wywołującą moją metodę naprawAlternator. Taka instrukcja przybra-
łaby postać podobną do pokazanej na listingu 3.3.
Skoro wiesz już, czym jest i jak działa metoda, możemy zająć się bardzo przydatną
terminologią:
W niektórych przypadkach będę nazywał kod z listingu 3.2 metodą. Jeżeli będę
chciał się wyrażać bardziej profesjonalnie, to kod ten będę nazywał deklaracją
metody.
Deklaracja metody z listingu 3.2 składa się z dwóch części. Pierwsza część
(zawierająca słowo naprawAlternator, kończąca się tuż przed otwierającym
nawiasem klamrowym) nazywa się nagłówkiem metody. Pozostała część listingu 3.2
(umieszczona w nawiasach klamrowych) nazywana jest ciałem metody.
Pojęcie deklaracji metody odróżnia instrukcje z listingu 3.2 od instrukcji z listingu 3.3,
która nazywana jest wywołaniem metody.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Deklaracja metody informuje komputer, co ma się wydarzyć w przypadku wywo-
łania tej metody. Wywołanie metody (niezależny kawałek kodu) informuje kom-
puter, że wybrana metoda ma zostać wywołana. Zazwyczaj deklaracja metody i jej
wywołanie znajdują się w dwóch różnych częściach programu.
RYSUNEK 3.3.
Metoda main
Podobnie jak każda inna metoda języka Java, metoda main również podobna jest
do przepisu:
albo
W języku Java słowo main ma szczególne znaczenie. Nigdy nie tworzy się kodu,
który jawnie wywoływałby metodę main. Słowo main jest nazwą metody, która
jest wywoływana automatycznie w momencie uruchamiania programu.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
jednej instrukcji. Ta instrukcja nakazuje komputerowi wypisać na ekranie tekst
„Pokochasz język Java!”. To właśnie dlatego na rysunku 3.1 widoczne są słowa
„Pokochasz język Java!”.
RYSUNEK 3.4.
Instrukcja
w języku Java
Oczywiście w języku Java istnieją też inne rodzaje instrukcji. Wywołanie metody,
o którym mówiłem wcześniej w punkcie „Metody języka Java”, jest jednym z wielu
rodzajów instrukcji języka Java. Na listingu 3.3 można zobaczyć, jak wygląda wy-
wołanie metody. Na rysunku 3.4 również widoczne jest takie wywołanie metody:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Już to raz powiedziałem, ale warto powtórzyć. W nazwie System.out.println
przedostatni znak jest małą literą l (jak w słowie linia), a nie cyfrą 1 (słownie:
jeden). Jeżeli wpiszesz w tym miejscu cyfrę 1, to cały kod przestanie działać.
Jeżeli chcesz się dowiedzieć, co znaczą kropki w nazwach w języku Java, możesz
zajrzeć do rozdziału 7.
RYSUNEK 3.5.
Wywołanie metody
System.out.println
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Mówiąc coś takiego jak „deklaracja metody System.out.println jest ukryta głęboko
w API języka Java”, nie oddaję natury samego API. Oczywiście można ignorować
cały ten kod znajdujący się w API języka Java. Można ograniczyć się do zapamięta-
nia, że gdzieś w tym kodzie znajduje się deklaracja metody System.out.println.
Nie chcę jednak, żeby ktokolwiek odniósł wrażenie, że API języka jest czymś
magicznym. API jest po prostu zbiorem kodu języka Java. Instrukcje z API infor-
mujące komputer, co ma zrobić w przypadku wywołania metody System.out.
println, są bardzo podobne do tych z listingu 3.1.
W języku Java każda instrukcja (taka jak z wyróżnionego wiersza na rysunku 3.4)
musi zakończyć się znakiem średnika. Pozostałe wiesze z rysunku 3.4 nie kończą
się średnikami, ponieważ w tych wierszach kodu nie ma żadnych instrukcji. Na
przykład nagłówek metody (wiersz zawierający słowo main) nie nakazuje kom-
puterowi wykonania żadnej operacji. Nagłówek metody jest jedynie informacją
w stylu: „jeżeli kiedyś zaszłaby potrzeba wykonania metody main, to w kolejnych
wierszach znajdują się wszystkie dane na jej temat”.
Nawiasy klamrowe
Dawno temu, a może całkiem niedawno, nauczyciele z pewnością mówili Ci, jak
ważne jest stosowanie konspektów. Dzięki konspektom można łatwo posegre-
gować pomysły i idee. Dzięki nim łatwiej można zauważyć cały las, a nie tylko
same drzewa. A poza tym są pierwszym krokiem na drodze do Klubu Uporząd-
kowanych Ludzi. Co ciekawe, program języka Java można potraktować jak swego
rodzaju konspekt. Program z listingu 3.1 zaczyna się od nagłówka mówiącego
nam „tutaj znajduje się klasa o nazwie Displayer”. Po tym nagłówku widzimy
nagłówek niższego poziomu informujący nas, że „tutaj znajduje się metoda o na-
zwie main”.
Można zatem spytać: skoro program w języku Java jest podobny do konspektu, to
dlaczego w ogóle go nie przypomina? Gdzie podziały się te wszystkie liczebniki
rzymskie, wielkie litery i inne elementy? Na takie pytanie mogę odpowiedzieć
dwojako:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 3.4. Nawiasy klamrowe w klasie języka Java
public class Displayer {
public static void main(String args[]) {
System.out.println("Pokochasz język Java!");
}
}
Nigdy nie zapominaj o tym, że program języka Java jest przede wszystkim zwy-
czajnym konspektem.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 3.6.
Konspekt zmienia się
w kod języka Java
RYSUNEK 3.7.
Klasa jest większa od
metody, metoda jest
większa od instrukcji
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeżeli masz kod pobrany ze strony związanej z tą książką, to zaimportuj kod
z listingu 3.1 (znajduje się w folderze 03-01) do swojego IDE. Jeżeli nie masz
zamiaru pobierać tego kodu, to utwórz w IDE nowy projekt. W nowym projekcie
utwórz klasę o nazwie Displayer i użyj w niej kodu z listingu 3.1. Niezależnie od
tego, czy projekt będzie pobrany, czy utworzony na nowo, po jego uruchomieniu
na ekranie powinny pojawić się słowa „Pokochasz język Java!”.
Spróbuj uruchomić kod z listingu 3.1, zmieniając tekst „Pokochasz język Java!”
na tekst „Nigdy więcej fasoli!”. Co się stanie?
Spróbuj uruchomić kod z listingu 3.1, zmieniając słowo public (same małe litery)
na słowo Public (teraz zaczyna się od dużej litery). Co się stanie?
Spróbuj uruchomić kod z listingu 3.1, zmieniając słowo main (same małe litery)
na słowo Main (teraz zaczyna się od dużej litery). Co się stanie?
Spróbuj uruchomić kod z listingu 3.1, zmieniając słowo System (zaczyna się
od dużej litery) na słowo system (same małe litery). Co się stanie?
Spróbuj uruchomić kod z listingu 3.1, zmieniając słowo println na słowo print1n
(przy końcu umieść cyfrę 1). Co się stanie?
Spróbuj uruchomić kod z listingu 3.1, usuwając z niego średnik. Co się stanie?
Spróbuj uruchomić kod z listingu 3.1, zastępując tekst „Pokochasz język Java!”
tekstem „Używaj cudzysłowów prostych \", a nie drukarskich \u201D". Co się
stanie?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Pewnego dnia, gdy projekt był już niemalże na ukończeniu, Janek dostał w po-
czcie list od swojego ubezpieczenia zdrowotnego. Nie, wcale nie chodziło o jakąś
bardzo poważną chorobę, a jedynie o rutynową wizytę u lekarza. Na formularzu
podania o zwrot kosztów znajdowało się pole, w którym należało wpisać swoją
datę urodzenia, tak jakby ta mogła się zmienić od czasu, gdy Janek podpisywał
umowę ubezpieczenia. Wypełniając w pośpiechu formularz, Janek wpisał rok 2016
w dacie urodzenia, a teraz ubezpieczyciel odmówił przejęcia kosztów wizyty
u lekarza.
Pięć miesięcy później Janka bolało już ucho, ale po spędzeniu 800 godzin przy
telefonie udało mu się uzyskać wstępnie zapewnienie od ubezpieczyciela, że
przejmie on koszt wizyty u lekarza. Dumny z siebie Janek chciał teraz od razu
przystąpić do dalszych prac nad projektem. Ale czy pamiętał jeszcze, na jakim
etapie pozostawił prace w swoim projekcie?
Niestety nie. Janek wpatrywał się w napisany przez siebie kod, ale ten przypomi-
nał mu sen, którego nie da się zapamiętać po przebudzeniu. Teraz kod był cał-
kowicie niezrozumiały. Wcześniej napisał ponad milion wierszy kodu, ale żad-
nego z nich nie opatrzył nawet najmniejszym komentarzem. Nie pozostawił
sobie żadnych wskazówek opisujących swój sposób myślenia podczas pracy nad
całym projektem. Wielce sfrustrowany, Janek porzucił cały swój wielki projekt.
/**
* Klasa Displayer wyświetla tekst
* na ekranie komputera.
*
* @author Barry Burd
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
* @version 1.0 24/01/17
* @see java.lang.System
*/
public class Displayer {
/**
* Wykonywanie programu rozpoczyna
* się od metody main.
*
* @param args (Zobacz rozdział 11.)
*/
public static void main(String args[]) {
System.out.println("Kocham język Java!"); //Ja? Ty?
}
}
Komentarze na końcu wiersza. Widoczny na listingu 3.6 tekst //Ja? Ty? jest
komentarzem na końcu wiersza. Taki komentarz zaczyna się dwoma znakami
ukośnika i kończy się wraz z końcem danego wiersza. Tutaj również kompilator
zupełnie nie interesuje się tekstem znajdującym się w komentarzu na końcu wiersza.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Otóż istnieje pewien program o nazwie javadoc (jakżeby inaczej), który potrafi
wyszukać komentarze javadoc na listingu 3.6 i umieścić je na przyjemnie
wyglądającej stronie WWW. Przykład takiej strony możesz zobaczyć na rysunku 3.8.
RYSUNEK 3.8.
Strona javadoc
wygenerowana na
podstawie kodu
z listingu 3.6
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Komentarze javadoc do świetne rozwiązanie. Mają one kilka bardzo interesują-
cych cech:
Jedyną osobą, która musi przeglądać dany kawałek kodu Java, jest tworzący go
programista. Pozostałe osoby, które chcą jedynie korzystać z tego kodu, mogą
dowiedzieć się, jak on działa, czytając mówiącą o tym automatycznie
wygenerowaną stronę WWW.
Skoro takie osoby nie zaglądają do kodu Java, to nie mogą też wprowadzać do
niego zmian. (Oznacza to, że takie osoby nie mogą wprowadzić do naszego kodu
żadnych nowych błędów).
Skoro te osoby nie mogą przeglądać naszego kodu Java, to nie muszą też
rozgryzać sposobu jego działania. Wszystko, co muszą wiedzieć na jego temat,
mogą przeczytać na stronie WWW wygenerowanej z tego kodu.
Programista nie musi tworzyć dwóch osobnych plików, umieszczając kod języka
Java w jednym z nich, a dokumentację dla tego kodu w drugim. Wystarczy,
że przygotuje plik z kodem programu, umieszczając w nim również tekst
dokumentacji (w postaci komentarzy javadoc).
Trzy proste słowa: znaj swoją publiczność. Gdy piszesz skomplikowany kod dla
rzeczywistego rozwiązania, to Twoją publicznością są inni programiści, mene-
dżerowie IT oraz ludzie, który będą musieli rozszyfrowywać to, co przygotujesz.
Gdy piszę proste przykłady dla tej książki, to moją publicznością jesteś Ty —
początkujący programista języka Java. Zamiast czytać ewentualne komentarze,
powinieneś skupiać się na przeglądaniu w takim programie instrukcji, którymi
będzie zajmował się kompilator Javy. To właśnie dlatego umieszczam w książce
tak niewiele komentarzy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wykorzystywanie komentarzy
do eksperymentowania z kodem
Czasami można usłyszeć, jak programiści mówią o wykomentowaniu pewnych
części swojego kodu. Gdy piszesz dany program, a coś nie działa w nim prawi-
dłowo, to często pomocne okazuje się usunięcie pewnych elementów z kodu.
Chociażby po to, żeby sprawdzić, co się stanie po usunięciu takiego podejrzanego
wycinka kodu. Oczywiście efekty takiego usuwania kodu mogą Ci się wcale nie
spodobać, dlatego dobrze byłoby nie usuwać go z programu całkowicie. Rozwiąza-
niem w takiej sytuacji jest umieszczenie zwyczajnych instrukcji języka Java w ko-
mentarzu. Na przykład możesz zamienić instrukcję:
w komentarz:
System.out.println("Rodzice,");
System.out.println("ostrożnie");
/*
* Celowo wyświetlane w czterech osobnych wierszach
*/
System.out.println("wybierajcie");
System.out.println("swoje bitwy!");
/*
System.out.println("Rodzice,");
System.out.println("ostrożnie");
/*
* Celowo wyświetlane w czterech osobnych wierszach
*/
System.out.println("wybierajcie");
System.out.println("swoje bitwy!");
*/
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
wykomentowane, natomiast kolejny znak */ sprawi, że kompilator będzie zgła-
szał błędy. Nie da się zagnieżdżać w sobie tradycyjnych komentarzy. Z tego
właśnie powodu do eksperymentowania ze swoim kodem zalecam stosować ko-
mentarze na końcu wiersza.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Pisanie własnych
programów
w języku Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TEJ CZĘŚCI
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Nakazywanie zmiennym
przechowywania wartości
określonego typu
Rozdział 4
Jak najlepiej
wykorzystać zmienne
i ich wartości
P
rzedstawię poniżej pewną rozmowę między panem Van Dorenem a panem
Baraschem, która nigdy nie miała miejsca:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Niektóre aspekty przedstawionego powyżej dialogu można przedstawić w języku
Java za pomocą kilku wierszy kodu.
Zmieniając zmienną
Bez względu na to, w jaki sposób zdobędziesz swój milion złotych, możesz sko-
rzystać ze zmiennej, aby umieścić w niej swoje bogactwo. Na listingu 4.1 przed-
stawiam przykładowy kod.
Nie musisz ręcznie wpisywać kodu znajdującego się na listingu 4.1 (ani żadnego
innego znajdującego się w tej książce). Wszystkie przykładowe kody programów
zamieszczone w książce są dostępne na serwerze ftp wydawnictwa: ftp://ftp. he-
lion.pl/przyklady/javby7.zip.
Rysunek 4.1 przedstawia wynik działania kodu z listingu 4.1 przed i po wprowa-
dzeniu zmiany do zmiennej. Po wykonaniu pierwszego polecenia na listingu 4.1
zmienna amountInAccount przechowuje liczbę 50,22. Następnie po wykonaniu
drugiej instrukcji zmienna ta otrzyma nagle liczbę 1000050,22. Kiedy myślisz
o zmiennej, wyobraź sobie miejsce w pamięci komputera, gdzie tranzystory prze-
chowują liczbę 50,22, 1000050,22 lub dowolną inną. Możemy sobie wyobrazić, że
po lewej stronie rysunku 4.1 pudełko z liczbą 50,22 jest otoczone milionami po-
dobnych pudełek.
RYSUNEK 4.1.
Wartość zmiennej
przed i po zmianie
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Teraz zajmiemy się terminologią. Rzecz przechowywana w zmiennej jest warto-
ścią. Wartość zmiennej może się zmienić w trakcie trwania programu (na przy-
kład wtedy, gdy Jack daje Ci milion złotych). Wartość przechowywana w zmien-
nej niekoniecznie jest liczbą. (Na przykład można utworzyć zmienną, która
zawsze przechowuje literę). Rodzaj wartości przechowywanej w zmiennej jest
typem zmiennej.
Zanim zakończymy temat z listingu 4.1, musisz zauważyć jeszcze jedną ważną
rzecz. W tym listingu znajdują się zapisy 50.22 i 1000000.00. Każdy przy zdro-
wych zmysłach nazwałby je liczbami, ale w programie Java lepiej jest stosować
nazwę literały.
Wartość zmiennej może się zmieniać, natomiast wartość literału już nie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Instrukcje przypisania
Instrukcje takie jak te na listingu 4.1 nazywane są instrukcjami przypisania. In-
strukcja przypisania przypisuje do czegoś pewną wartość. W wielu przypadkach
tym czymś jest zmienna.
RYSUNEK 4.2.
Działanie pierwszego
wiersza kodu
przedstawionego
na listingu 4.1
Drugi wiersz kodu z listingu 4.1 jest nieco bardziej skomplikowany. Rysunek 4.3
ilustruje działanie drugiego wiersza tego kodu.
RYSUNEK 4.3.
Działanie drugiego
wiersza kodu
przedstawionego
na listingu 4.1
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
sobie poradzić. Kiedy myślisz, że komputer przechowuje literę J, komputer na-
prawdę zapisuje cyfry 01001010. Wszystko wewnątrz komputera to sekwencja
cyfr 0 i 1. Każdy komputerowy geek z pewnością już wie, że owe 0 i 1 są nazywane
bitami.
Jak się okazuje, sekwencja cyfr 01001010, która oznacza naszą literę J, może
również oznaczać liczbę 74. Ta sama sekwencja może również oznaczać liczbę
1,0369608636003646×10-43. Co więcej, jeśli te bity zostaną zinterpretowane jako
piksele na ekranie, to ta sama sekwencja może być użyta do wyświetlenia kropek
pokazanych na rysunku 4.4. Znaczenie ciągu cyfr 01001010 zależy od sposobu, w jaki
oprogramowanie będzie interpretować sekwencję 0 i 1.
RYSUNEK 4.4.
Ekstremalnie duże
zbliżenie ośmiu
czarno-białych pikseli
na ekranie
amountInAccount = 50.22;
amountInAccount = amountInAccount + 1000000.00;
System.out.print("Na swoim koncie masz");
System.out.print(amountInAccount);
System.out.println(" PLN.");
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 4.5.
Wynik działania
programu
z listingu 4.2
double amountInAccount;
W tej deklaracji zmiennej słowo double jest słowem kluczowym języka Java. In-
formuje ono komputer, jakiego rodzaju wartości zamierzasz przechowywać
w zmiennej amountInAccount. A dokładniej słowo double oznacza liczby z przedziału
od –1,8×10308 do 1,8×10308. (Są to gigantyczne liczby z 308 zerami przed przecin-
kiem dziesiętnym. Tylko najbogatsi ludzie świata mogą wypisywać czeki z 308
zerami. Druga z tych liczb to jeden przecinek osiem gazazzo-zillion-kaskilionów.
Liczba 1,8×10308 jest stałą, zdefiniowaną przez Międzynarodowe Biuro Miar i Wag,
i jest równa liczbie ekscentrycznych programistów siedzących między Sunnyvale
w Kalifornii a Galaktyką M31 Andromeda).
Jednak ważniejszym faktem niż szeroki zakres liczb związanych ze słowem klu-
czowym double jest to, że zmienna typu double może przechowywać liczby
zmiennoprzecinkowe. Po zadeklarowaniu zmiennej amountInAccount i przypisa-
niu jej typu double można w niej przechowywać wszystkie rodzaje liczb. Możesz na
przykład zapisać 50.22, 0.02398479 lub –3.0. Gdybym nie zadeklarował na li-
stingu 4.2, że amountInAccount jest typu double, nie byłbym w stanie zapisać w niej
liczby 50.22. Zamiast tego musiałbym przechowywać zwyczajną liczbę całkowitą
50, bez żadnych cyfr po przecinku.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Możesz zmienić kod znajdujący się na listingu 4.2 i zadeklarować, że zmienna
amountInAccount będzie teraz miała typ float.
float amountInAccount;
Z pewnością 32 bity wystarczą, aby zapisać niewielką liczbę, np. 50.22, prawda? Cóż,
tak i nie. Możesz łatwo zapisać liczbę 50.00 w zaledwie 32 bitach. Tak naprawdę to
możesz zapisać tę liczbę, używając jedynie 6 bitów. Jej rozmiar nie ma tak naprawdę
znaczenia, ponieważ ważna jest dokładność. W 64-bitowej zmiennej o typie double
używasz większości bitów do przechowywania cyfr spoza przecinka dziesiętnego.
Aby zapisać część .22 z liczby 50.22, potrzebujesz więcej niż jedynie 32 bity, które
otrzymujesz wraz z typem float.
Naprawdę uwierzysz w to, co właśnie przeczytałeś — że przechowywanie .22 zaj-
muje więcej niż 32 bity? Aby Cię przekonać, wprowadziłem kilka zmian w kodzie na
listingu 4.2. Przerobiłem zmienną amountInAccount na typ float. Następnie zmie-
niłem trzy pierwsze instrukcje wewnątrz metody main w następujący sposób:
float amountInAccount;
amountInAccount = 50.22F;
amountInAccount = amountInAccount + 1000000.00F;
(Aby zrozumieć, dlaczego użyłem litery F w liczbach 50.22F i 1000000.00F, spójrz na
tabelę 4.1 znajdującą się w dalszej części tego rozdziału). W efekcie otrzymałem taki
wynik:
Na swoim koncie masz 1000050.25 PLN.
Porównaj teraz nasz wynik z wynikiem działania programu z rysunku 4.5. Kiedy
zmieniam typ zmiennej double na typ float, Charles ma jeszcze dodatkowe trzy
grosze na swoim koncie. Poprzez tę zmianę wpłynąłem na dokładność setnych czę-
ści zmiennej amountInAccount. To źle.
Inny kłopot związany z wartościami typu float to już jedynie kosmetyka. Spójrz po-
nownie na liczby 50.22 i 1000000.00 znajdujące się na listingu 4.2. Prawa języka Java
mówią, że każda z tych liczb zajmuje po 64 bity. Jeśli zatem zadeklarujesz, że zmien-
na amountInAccount jest typu float, to pojawią się problemy. Będziesz miał kłopoty
z upchnięciem 64-bitowych literałów w małej 32-bitowej zmiennej amountInAccount.
Aby to zrekompensować, możesz zmienić w literałach typ double na typ float, do-
dając literę F do każdej liczby typu double. Niestety taka liczba z dodatkową literą F na
końcu wygląda po prostu śmiesznie.
float amountInAccount;
amountInAccount = 50.22F;
amountInAccount = amountInAccount + 1000000.00F;
Aby eksperymentować z liczbami, odwiedź stronę WWW pod adresem http://babbage.
cs.qc.cuny.edu/IEEE-754.old/Decimal.html. Strona ta pobiera dowolną wprowadzoną
liczbę dziesiętną i pokazuje, jaka będzie jej reprezentacja w 32-bitowej i 64-bitowej
postaci binarnej.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W wielu sytuacjach masz wybór. Możesz zadeklarować pewne wartości jako
wartości pojedynczej precyzji lub wartości podwójnej precyzji. Ale nie przejmuj
się wyborem między typem float a double. W większości programów użyj po
prostu typu double. Dzięki dzisiejszym wypasionym procesorom przestrzeń za-
oszczędzona przy użyciu typu float prawie nigdy nie jest warta utraty dokład-
ności. (Aby uzyskać więcej informacji, zobacz ramkę „Cyfry za przecinkiem
dziesiętnym”).
Wyświetlanie tekstu
W ostatnich trzech instrukcjach znajdujących się na listingu 4.2 używam cieka-
wej sztuczki formatowania tekstu. Jeśli chcesz wyświetlić kilka różnych ele-
mentów w jednym wierszu na ekranie, to umieszczasz te elementy w osobnych
instrukcjach. Wszystkie z wyjątkiem ostatniej instrukcji to wywołania metody
System.out.print. (Ostatnia instrukcja to wywołanie metody System.out.println).
Wywołanie metody System.out.print powoduje wyświetlenie pewnego tekstu, a na-
stępnie pozostawia kursor na końcu tego wiersza. Po uruchomieniu metody System.
out.print kursor nadal znajduje się na końcu tego samego wiersza, tak więc
następna instrukcja System.out.cokolwiek może kontynuować wypisywanie w tym
samym wierszu. Po kilku wywołaniach instrukcji print i następujących po nich
pojedynczym wywołaniu instrukcji println otrzymamy ładnie wyglądający wiersz
tekstu (patrz rysunek 4.5).
Uruchom kod z listingu 4.2, aby upewnić się, że działa poprawnie na Twoim na
komputerze. Następnie zobacz, co się stanie po wprowadzeniu następujących
zmian:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Dodaj w kodzie symbol waluty do liczby 50.22. Na przykład, jeśli mieszkasz
w Polsce, gdzie symbolem waluty jest PLN, zobacz, co się stanie, gdy zmienisz
pierwszą instrukcję przypisania na amountInAccount = 50.22 PLN.
W tym momencie każdy mądry facet zauważy, że żadna prawdziwa rodzina nie
ma dokładnie 2,3 dziecka. Oczywiście liczby całkowite są w tym świecie bardzo
ważne. Dlatego w języku Java można zadeklarować zmienną do przechowywania
tylko liczb całkowitych. Listing 4.3 przedstawia program, który używa zmiennych
całkowitych typu int.
Masz windę hotelową o nośności 800 kilogramów. W ten weekend w hotelu od-
bywa się spotkanie członków rodziny Kowalskich. W jednej gałęzi tej rodziny
pojawiły się dziesięcioraczki (dziesięcioro dzieci mających te same cechy fizycz-
ne). Każde z tych dziesięcioraczków waży dokładnie 80 kilogramów. W sobotę
rodzina ta zjadła duży obiad, a ponieważ zawierał on dodatkowe truskawkowe
ciastko, każdy z członków rodziny Kowalskich waży teraz 85 kilogramów. Zaraz
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
po obiedzie dokładnie w tym samym czasie wszyscy z dziesięciorga rodzeństwa
docierają do windy. (Dlaczego nie? W końcu myślą w podobny sposób). Pytanie
brzmi: ile osób z dziesięciorga rodzeństwa zmieści się w windzie?
Teraz proszę pamiętać, że jeśli całkowita waga ludzi znajdujących się w windzie
choć o dekagram przekroczy limit 800 kilogramów, lina windy pęknie, a wszystkie
te osoby spadną na dno, ponosząc nagłą (i kosztowną) śmierć.
RYSUNEK 4.6.
Ocal Kowalskich
Moja żona i ja pobraliśmy się 29 lutego, więc mamy rocznicę co cztery lata. Na-
pisz program ze zmienną o nazwie years. Na podstawie wartości tej zmiennej
program wyświetli liczbę rocznic, które już mieliśmy. Na przykład, jeśli wartość
years wynosi 4, program wyświetla zdanie Liczba rocznic: 1. Jeśli wartość years
wyniesie 7, program nadal wyświetli Liczba rocznic: 1. Jeśli jednak wartość
years będzie równa 8, teraz nasz program wyświetli zdanie: Liczba rocznic: 2.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
CZTERY SPOSOBY PRZECHOWYWANIA
LICZB CAŁKOWITYCH
W języku Java istnieją cztery typy liczb całkowitych: byte, short, int i long. W przeci-
wieństwie do skomplikowanej historii o dokładności typów float i double jedyną rze-
czą, która ma znaczenie podczas wybierania spośród typów liczb całkowitych, jest
rozmiar liczby, jaka ma się znaleźć w zmiennej. Jeśli chcesz używać liczb większych
niż 127, nie używaj typu byte. Aby przechowywać liczby większe niż 32767, nie uży-
waj typu short.
Jeśli dwie zmienne mają dwa zupełnie różne typy, nie można utworzyć obu tych
zmiennych w tej samej deklaracji. Na przykład, aby utworzyć zmienną typu int
o nazwie weightOfFred i zmienną typu double o nazwie amountInFredsAccount,
będziemy potrzebować dwóch oddzielnych deklaracji dla tych zmiennych.
Gdy połączysz sześć wierszy kodu znajdującego się na listingu 4.3 w jedną
deklarację, kod stanie się bardziej zwięzły. Czasem łatwiej jest przeczytać
zwięzły kod, a czasami nie. To jest Twoja decyzja jako programisty.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Eksperymentowanie z JShell
Programy przedstawione na listingach 4.2 i 4.3 zaczynają się od tego samego
starego, męczącego refrenu:
W każdym razie ponowne wpisanie tego szablonu kodu do okna edytora może
być denerwujące, zwłaszcza gdy Twoim celem jest przetestowanie efektu wyko-
nania kilku prostych instrukcji. Aby rozwiązać ten problem, w Javie 9 twórcy języka
opracowali nowe narzędzie. Nazywają je JShell.
Używając JShell, prawie nigdy nie wpisujesz już całego programu. Zamiast tego
piszesz instrukcję Java, a JShell reaguje na to, co wpiszesz. Potem wpisujesz ko-
lejną instrukcję, a JShell również na nią reaguje. Następnie wpisujesz trzecią in-
strukcję i tak dalej. Wystarczy jedna instrukcja, aby uzyskać odpowiedź z JShell.
JShell to tylko jeden z przykładów pętli REPL (Read Evaluate Print Loop — pętla
odczyt-wykonanie-wydruk) danego języka. Wiele języków programowania uży-
wa już pętli REPL, a od wersji 9 język Java także ma własną pętlę REPL.
Na rysunku 4.7 używam JShell, aby dowiedzieć się, jak Java reaguje na instrukcje
przypisania znajdujące się na listingach 4.2 i 4.3.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 4.7.
Intymna rozmowa
między mną a JShell
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Możesz poprosić JShell o zwrócenie wartości wyrażenia.
Nie musisz przypisywać wartości wyrażenia do zmiennej. Na przykład na rysunku
4.7 wpisuję kod:
elevatorWeightLimit / weightOfAPerson
JShell, odpowiadając na moje zapytanie, informuje mnie, że wynik dzielenia
elevatorWeightLimit / weightOfAPerson wynosi 9. JShell tworzy tymczasową
nazwę tej wartości. Na rysunku 4.7 nazwą tą jest $8. Zatem w następnym wierszu
tego kodu proszę o wartość $8 +1, a JShell daje mi odpowiedź 10.
Podczas korzystania z JShell nie musisz ponownie wpisywać już raz wpisanych
wierszy. Jeśli naciśniesz jeden raz klawisz strzałki w górę, JShell wyświetli po-
lecenie, które ostatnio wpisałeś. Jeżeli naciśniesz ten klawisz dwa razy, to JShell
wyświetli przedostatnie wpisane przez Ciebie polecenie. I tak dalej. Gdy JShell wy-
świetli polecenie, możesz użyć klawiszy strzałek w lewo i w prawo, aby przejść
do dowolnego znaku w środku polecenia. Możesz także modyfikować znaki w po-
leceniu. I wreszcie po naciśnięciu klawisza Enter JShell wykona nowo zmodyfi-
kowane polecenie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
nego tekstu. Użytkownik wpisuje coś, a komputer wyświetla odpowiedź pod
każdym wierszem danych wejściowych.
Techniki programowania GUI — w rozdziałach 9., 10., 14. i 16. opiszę niektóre
dobrze już znane techniki. W tym rozdziale również zamieściłem mały przykład
programu GUI. (Zobacz podrozdział „Cząsteczki i związki — typy referencyjne”
w dalszej części tego rozdziału).
Typy, których nie należy ignorować, to int, double, char i boolean. Poprzednie
podrozdziały dotyczyły typów int i double. W następnych dwóch podrozdziałach
zajmę się typami char i boolean.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
TABELA 4.1. Typy proste w języku Java
Typ znakowy
char 'A' Tysiące znaków, hieroglifów i symboli
Typ logiczny
boolean true true, false
Typ char
Kilkadziesiąt lat temu ludzie sądzili, że komputery istnieją tylko po to, by wyko-
nywać ogromne ilości obliczeń. Obecnie nikt tak już nie myśli. Jeśli nie byłeś w ko-
morze kriogenicznej przez ostatnie 20 lat, to zapewne już wiesz, że komputery
przechowują litery, symbole interpunkcyjne i jeszcze inne znaki.
W języku Java typ używany do przechowywania znaków nazywa się char. Na li-
stingu 4.4 przedstawiam prosty program, który wykorzystuje zmienne typu char,
natomiast wynik działania tego programu znajduje się na rysunku 4.8.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 4.8.
Ekscytujący przebieg
programu z listingu
4.4, który pojawia
się w widoku
konsoli Eclipse
Jeśli potrzebujesz pomocy przy uporządkowaniu takich pojęć jak przypisanie, de-
klaracja i inicjalizacja, przeczytaj, proszę, punkt „Łączenie deklaracji i inicjowanie
zmiennych”, który znajdziesz wcześniej w tym rozdziale.
to proszę, oprzyj się tej pokusie. Do zmiennej typu char nie można przypisać więcej
niż jednej litery naraz, a tym samym między apostrofami nie możne znajdować
się więcej niż jedna litera. Jeśli chcesz przechowywać całe wyrazy lub zdania (a nie
tylko pojedyncze litery), to musisz użyć typu o nazwie String.
Aby zapoznać się szerzej z typem String wykorzystywanym w języku Java, zobacz
podrozdział „Cząsteczki i związki — typy referencyjne”, który znajduje się w dal-
szej części tego rozdziału.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
pisywania słowa Witaj na ekranie, a zamiast tego pojawia się wyraz W i t a j,
sprawdź dokumentację tej metody, poszukując w niej wzmianek o używanym
zestawie znaków Unicode.
Typ boolean
Zmienna typu boolean może przechowywać jedną z dwóch wartości: true lub false.
Na listingu 4.5 zademonstrowałem użycie zmiennej typu boolean. Natomiast
rysunek 4.9 przedstawia wynik działania programu z listingu 4.5.
System.out.println(allTenOkay);
}
}
RYSUNEK 4.9.
Dziesięcioraczki
Kowalskich
ponownie atakują
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Na listingu 4.5 zmienna allTenOkay ma typ boolean. Aby uzyskać wartość zmien-
nej allTenOkay, program sprawdza, czy zmienna numberOfPeople jest większa lub
równa dziesięć. (Znaki >= oznaczają większe lub równe.)
W tym momencie opłaca się być wybrednym w kwestii terminologii. Każda część
programu Java, która ma wartość, jest wyrażeniem. Jeśli napiszesz:
weightOfAPerson = 150;
to liczba 150 jest wyrażeniem (wyrażenie, którego wartością jest liczba 150). Jeśli
napiszesz:
numberOfEggs = 2 + 2;
Na listingu 4.5 kod numerOfPeople >= 10 jest wyrażeniem. Wartość tego wyraże-
nia zależy od wartości przechowywanej w zmiennej numberOfPeople. Ale jak już
zapewne wiesz, mając na uwadze ciastko truskawkowe, które podano podczas
obiadu rodziny Kowalskich, wartość zmiennej numberOfPeople nie jest większa
lub równa dziesięć. W rezultacie wartość wyrażenia numberOfPeople >= 10 jest
fałszem. Tak więc w instrukcji z listingu 4.5, w której zmiennej allTenOkay jest
przypisywana wartość, zmienna ta otrzyma wartość false.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Program z listingu 4.6 wykorzystuje typy referencyjne. Natomiast na rysunku
4.10 możesz zobaczyć, co stanie się po uruchomieniu tego programu.
myFrame.setTitle(myTitle);
myFrame.setSize(300, 200);
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
}
}
RYSUNEK 4.10.
Pusta ramka
Typ String to inaczej ciąg znaków. To tak, jakby mieć kilka wartości typ char
połączonych w jednym wierszu. Skoro na listingu 4.6 nadajemy zmiennej
myTitle typ String, to następnie przypisanie do tej zmiennej tekstu „Pusta ram-
ka” jest całkowicie sensowne. Klasa String jest deklarowana w interfejsie API
języka Java.
Typ JFrame języka Java bardzo przypomina okienko. (Jedyna różnica polega na
tym, że nie nazywa się go okienkiem, tylko JFrame). Chciałem, żeby listing 4.6
był krótki i śliczny, dlatego postanowiłem nie umieszczać niczego innego w mo-
jej ramce — żadnych przycisków, pól tekstowych ani niczego innego.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Nawet z całkowicie pustą ramką kod zawarty na listingu 4.6 używa pewnych
sztuczek, które będę opisywał w dalszej części tej książki. Nie próbuj więc czytać
i interpretować każdego słowa znajdującego się w kodzie na listingu 4.6. Naj-
ważniejszą sprawą w tym listingu jest to, że program zawiera dwie deklaracje
zmiennych. Pisząc program, wymyśliłem dwie nazwy zmiennych: myTitle i myFrame.
Zgodnie z deklaracjami myTitle jest typu String, a myFrame jest typu JFrame.
Możesz poszukać opisu typów String i JFrame w dokumentacji API Javy. Ale zanim
to zrobisz, mogę Ci powiedzieć, co znajdziesz. Dowiesz się, że typy String i JFrame
są nazwami klas w języku Java. I to jest ta wielka wiadomość. Każda klasa jest
nazwą typu referencyjnego. Zmienną amountInAccount można zdefiniować jako typ
double, stosując taki zapis:
double amountInAccount;
lub taki:
JFrame myFrame;
lub ten:
Aby dowiedzieć się, czym jest klasa w języku Java, zajrzyj do podrozdziału doty-
czącego programowania obiektowego (OOP), znajdującego się w rozdziale 1.
Deklarując, że zmienna będzie typu int, możesz dość łatwo wyobrazić sobie, co
taka deklaracja będzie oznaczać. Oznacza ona, że gdzieś w pamięci komputera
rezerwowane jest miejsce przeznaczone do przechowywania wartości tej zmien-
nej. Takie miejsce w pamięci jest tylko pewną grupą bitów. Układ tych bitów sta-
nowi reprezentację określonej liczby całkowitej.
To wyjaśnienie jest dobre dla typów prostych, takich jak int lub double, ale co to
wszystko będzie oznaczać, gdy zadeklarujesz zmienną tak, aby miała typ refe-
rencyjny? Jak będzie wyglądać przypisanie zmiennej myFrame typu JFrame?
No cóż, co może oznaczać deklaracja, że wiersz dziękuję Ci, Boże, że jesteś jest
dziełem E.E. Cummingsa? Co będzie oznaczało napisanie takiej deklaracji?
EECummingsPoem ithankYouGod;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Oznacza to, że EECummingsPoem jest nazwą klasy, a ithankYouGod odnosi się do in-
stancji tej klasy. Innymi słowy, ithankYouGod jest obiektem należącym do klasy
EECummingsPoem.
Dzięki temu, że JFrame jest klasą, możesz tworzyć obiekty z tej klasy. (Jeśli mi
nie wierzysz, przeczytaj niektóre z akapitów opisujących klasy i obiekty, znaj-
dujących się w rozdziale 1.). Każdy obiekt (każda instancja klasy JFrame) jest
rzeczywistą ramką — oknem, które pojawia się na ekranie po uruchomieniu
kodu pokazanego na listingu 4.6. Deklarując zmienną myFrame z typem JFrame,
zastrzegasz sobie prawo użycia nazwy myFrame. Ta rezerwacja mówi komputero-
wi, że myFrame może odwoływać się do rzeczywistego obiektu typu JFrame. Innymi
słowy, myFrame może stać się pseudonimem dla jednego z okien, które pojawi się
na ekranie komputera. Rysunek 4.11 ilustruje taką sytuację.
RYSUNEK 4.11.
Zmienna myFrame
odnosi się do
instancji klasy JFrame
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Eksperymentuj z kodem znajdującym się na listingu 4.6, zmieniając kolejność
instrukcji znajdujących się w treści metody main. Które z tych zmian będą
w porządku, a które nie?
Deklaracja importu
Zawsze dobrze jest z góry ogłosić swoje zamiary. Rozważmy następujący wykład
z czasów szkolnych:
Oto kolejny przykład — typ Time (czas). Instancja klasy Time może mieć zmienną
hour (godzina, czyli liczba od 1 do 12), zmienną minutes (minuty, czyli liczba od 0 do 59)
i zmienną letter (litera, gdzie a oznacza przedpołudnie, a p oznacza popołudnie).
int hour;
int minutes;
char amOrPm;
Zauważ, że ta wielce potężna rzecz nazywana klasą API Java nie jest ani wielka, ani
potężna. Klasa to tylko zbiór deklaracji. Niektóre z tych deklaracji są deklaracjami
zmiennych. Niektóre z tych deklaracji zmiennych używają typów prostych, inne zaś
używają typów referencyjnych. Typy referencyjne pochodzą jednak z innych klas, a de-
klaracje tych klas zawierają zmienne. Taki łańcuszek trwa i trwa. Ostatecznie wszystko
w ten czy inny sposób sprowadza się do typów prostych.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Interesujące rzeczy, nieprawdaż? Teraz porównaj te akapity z wykładem, w którym
instruktor nie rozpoczyna od wprowadzenia do tematu:
Urodzony w Filadelfii Lionel Barrymore pojawił się w ponad 200 filmach, w tym
w firmie „Wspaniałe życie”, „Key Largo” i w „Dniu ślubu dr Kildare”. Ponadto
Barrymore (nie Ethel, John czy Drew) był pisarzem, kompozytorem i reżyserem.
Lionel Barrymore co roku użyczał w radiu swojego głosu Ebenezerowi
Scroogowi….
import javax.swing.JFrame;
myFrame.setTitle(myTitle);
myFrame.setSize(3200, 200);
myFrame.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Żaden podrozdział w tej książce nie jest w stanie przedstawić wszystkich infor-
macji na temat deklaracji importu. Aby rozpocząć rozwiązywanie niektórych sub-
telnych zagadek związanych z takimi deklaracjami, zajrzyj do rozdziałów 5., 9. i 10.
Język Java również korzysta ze znaku plusa. Możesz go używać do różnych celów.
Na przykład aby dodać dwie liczby, jak pokazałem to poniżej:
Możesz także użyć znaku plusa, aby skleić ze sobą dwie wartości typu String:
String startOfChapter =
"Jest trzecia rano. Śni mi się egzamin z"+
"historii, który oblałem w szkole średniej.";
System.out.println(startOfChapter);
To może być przydatne, ponieważ w Javie nie da się tworzyć ciągów znaków
znajdujących się w dwóch wierszach. Innymi słowy, poniższy kod nie zadziałałby:
String thisIsBadCode =
"Jest trzecia rano. Śni mi się egzamin z
historii, który oblałem w szkole średniej.";
System.out.println(thisIsBadCode);
Znaku dodawania możesz też użyć, aby połączyć liczby z wartościami typu
String:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Oczywiście dostępny jest również stary dobry znak odejmowania (ale nie dla
wartości typu String):
Użyj znaku gwiazdki (*), aby wykonać operację mnożenia, i znaku ukośnika (/),
aby wykonać operację dzielenia:
rate = 6.25;
hours = 35;
pay = rate * hours;
System.out.println(pay);
Kiedy dzielisz wartość typu int przez inną wartość typu int, otrzymujesz także
wartość typu int. Komputer nie zaokrągla wyniku, zamiast tego odcina resztę.
Jeśli umieścisz instrukcję System.out.println(11 / 4) w swoim programie, kompu-
ter wypisze na ekranie tylko cyfrę 2 zamiast pełnej liczby 2.75. Aby sobie z tym
poradzić, spraw, żeby jedna lub dwie liczby, które będziesz dzielić, były wartością
typu double. Jeśli umieścisz instrukcję System.out.println(11.0 / 4) w swoim
programie, komputer wypisze na ekranie liczbę 2.75.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
out.println("Z " + total + " centów dostajesz");
out.println(tenCents + " dziesięciocentówek");
out.println(fiveCents + " pięciocentówek");
out.println(twoCents + " dwucentówek");
out.println(cents + " centów");
}
}
Rysunek 4.12 przedstawia wynik działania kodu z listingu 4.7. Zaczynamy od 248
centów. Następnie:
tenCents = total / 10
dzielimy liczbę 248 przez 10, co daje w wyniku liczbę 24. Oznacza to, że całą
liczbę możemy podzielić na 24 dziesięciocentówki. W kolejnym kroku:
whatsLeft = total % 10
RYSUNEK 4.12.
Drobniaki składające
się na 2,48 dolara
Znajdź wartości poniższych wyrażeń, wpisując każde z nich w JShell (jeśli masz
problemy z uruchomieniem JShell, utwórz program Javy, który będzie wyświetlał
wartość każdego z tych wyrażeń):
5 / 4
5 / 4.0
5.0 / 4
5.0 / 4.0
"5" + "4"
5 + 4
" " + 5 + 4
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
DEKLARACJE IMPORTU — BRZYDKA PRAWDA
Zwróć uwagę na deklarację importu znajdującą się na początku listingu 4.7:
import static java.lang.System.out;
Oto pytanie: dlaczego w jednej deklaracji znajduje się słowo static, a w drugiej nie?
Szczerze mówiąc, wolałbym nie zadawać tego pytania!
Tak naprawdę, żeby poznać prawdę na temat słowa static, musisz przeczytać część
rozdziału 10. I tak naprawdę nie polecam przeskakiwania w tej chwili do tego roz-
działu, jeśli przyjmujesz lekarstwa na chorobę serca albo jeśli jesteś w ciąży czy też
karmisz piersią albo po prostu nie masz doświadczenia w programowaniu obiektowym.
Na razie możesz mieć pewność, że przeczytanie rozdziału 10. nie będzie trudne, je-
żeli wcześniej zapoznasz się z trzecią częścią tej książki. A kiedy musisz zdecydować,
czy użyć słowa static w deklaracji importu, pamiętaj o poniższych wskazówkach:
Zdecydowana większość deklaracji importu w programie Java nie używa słowa
static.
W tej książce nigdy nie używam deklaracji import static do importowania
czegokolwiek poza instrukcją System.out. (Nooo… prawie nigdy…)
Większość deklaracji importu nie używa słowa static, ponieważ większość z tych
deklaracji importuje klasy. Niestety, nazwa System.out nie jest nazwą klasy.
whatsLeft = whatsLeft % 5;
whatsLeft = whatsLeft % 2;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Tylko jeden z tych wierszy jest deklaracją. Pozostałe dwa to instrukcje przypisa-
nia. To dobrze, ponieważ daną zmienną możesz zadeklarować tylko raz (chyba
że utworzysz coś zwanego blokiem). Jeśli spróbujesz czegoś głupiego i w listingu
4.7 napiszesz taki kod:
Aby dowiedzieć się, czym jest blok, zajrzyj do rozdziału 5. Następnie, aby uzy-
skać rzetelne informacje na temat ponownego deklarowania zmiennych, zajrzyj
do rozdziału 10.
RYSUNEK 4.13.
Używanie
preinkrementacji
RYSUNEK 4.14.
Działanie programu
przestawionego na
rysunku 4.13
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Podwójny znak dodawania występuje pod dwoma nazwami, w zależności od te-
go, gdzie go umieściliśmy. Kiedy umieścisz znak ++ przed nazwą zmiennej, to
znak ten nazywany jest operatorem preinkrementacji (pre oznacza przed).
RYSUNEK 4.15.
Używanie
postinkrementacji
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Przy preinkrementacji pokazanej na rysunku 4.14 druga liczba to 29.
RYSUNEK 4.16.
Działanie programu
przestawionego na
rysunku 4.15
Zamiast stosować zapis ++numberOfBunnies, możesz osiągnąć ten sam efekt, pi-
sząc taką instrukcję: numberOfBunnies = numberOfBunnies + 1. W związku z tym
niektóre osoby uważają, że w języku Java operatory ++ i – mają na celu zmniej-
szenie liczby naciśnięć klawiszy, aby nasze biedne palce zanadto się nie napra-
cowały. Takie myślenie jest całkowicie błędne. Najważniejszym powodem uży-
wania operatorów ++ jest uniknięcie nieefektywnej i podatnej na błędy praktyki
zapisywania nazwy tej samej zmiennej, takiej jak na przykład numberOfBunnies,
dwukrotnie w tej samej instrukcji. Jeśli napiszesz numberOfBunnies tylko raz (tak
jak ma to miejsce przy użyciu operatorów ++ lub --), komputer nie będzie musiał
po raz kolejny dowiadywać się, co to jest numberOfBunnies. Ponadto gdy piszesz
numberOnBunnies tylko raz, masz tylko jedną szansę (zamiast dwóch) na niepo-
prawne wpisanie tej nazwy. W prostych wyrażeniach, takich jak numberOf
Bunnies++, te zalety nie mają większego znaczenia. Ale przy bardziej skompliko-
wanych wyrażeniach, takich jak na przykład inventoryItems[(quantityReceived--
*itemsPerBox+17)]++, wydajność i dokładność uzyskiwana przy użyciu operatorów
++ i –- są już znaczące.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
INSTRUKCJE I WYRAŻENIA
Działania pre- i postinkrementacyjne oraz operatory pre- i postdekrementujące
można opisać na dwa sposoby: w sposób zrozumiały dla każdego oraz w ten wła-
ściwy sposób. Sposób, w jaki wyjaśniam tę koncepcję w większości tego podrozdziału
(wykorzystując przy tym pojęcia przed i po), powinien być zrozumiały dla każdego.
Niestety sposób, w jaki wszyscy rozumieją tę koncepcję, nie jest właściwy. Kiedy wi-
dzisz ++ lub --, możesz myśleć w kategoriach następstwa czasowego. Jednak czasami
programista używa operatora ++ lub -- w bardzo zawiły sposób, a wtedy pojęcia
przed i po najzwyczajniej przestają działać. Jeśli więc jesteś w takiej trudnej sytuacji,
pomyśl o tych operatorach w kategoriach instrukcji i wyrażeń.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public class Main {
jshell> int i = 8
jshell> i++
jshell> i
jshell> i
jshell> i++
jshell> i
jshell> ++i
jshell> i
Operatory przypisania
Czytając poprzedni podrozdział, dotyczący operatorów zwiększających wartość
o 1, możesz się zastanawiać, czy dałoby się tak manipulować tymi operatorami,
aby dodawały 2 lub 5 albo na przykład 1 000 000. Czy można napisać number
OfBunnies++++ i nadal nazywać się programistą języka Java? Niestety nie. Jeśli
tego spróbujesz, to podczas kompilowania takiego kodu pojawi się komunikat
o błędzie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 4.8. Operatory przypisania
public class UseAssignmentOperators {
numberOfBunnies += 1;
System.out.println(numberOfBunnies);
numberOfBunnies += 5;
System.out.println(numberOfBunnies);
numberOfBunnies += numberExtra;
System.out.println(numberOfBunnies);
numberOfBunnies *= 2;
System.out.println(numberOfBunnies);
System.out.println(numberOfBunnies -= 7);
System.out.println(numberOfBunnies = 100);
}
}
RYSUNEK 4.17.
Wynik działania
programu
z listingu 4.8
Ostatnie dwa wiersze listingu 4.8 demonstrują specjalną funkcję operatorów przy-
pisania w języku Java. Takiego operatora można użyć jako części większej instrukcji
języka Java. W ostatnim wierszu na listingu 4.8 operator odejmuje 7 od zmiennej
numberOfBunnies, zmniejszając tym samym jej wartość z 172 do 165. Następnie
całe to przypisanie umieszczane jest wewnątrz instrukcji System.out.println,
dzięki czemu na ekranie wypisana zostanie wartość 165.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Co więcej, ostatni wiersz z listingu 4.8 pokazuje, że w ten sam sposób można
wykorzystać stary, dobry znak równości. To, co na początku tego rozdziału na-
zywałem instrukcją przypisania, jest tak naprawdę jednym z operatorów przy-
pisania, które opisuję w tym miejscu. Oznacza to, że przypisując wartość do
czegoś, możesz też wykorzystać to przypisanie jako część większej instrukcji.
Aby uzyskać pełniejsze wyjaśnienie tych rzeczy, zajrzyj do ramki „Instrukcje i wy-
rażenia” znajdującej się wcześniej w tym rozdziale.
i += 2;
i -= 5;
i *= 6;
System.out.println(i);
System.out.println(i += 3);
System.out.println(i /= 2);
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
118 CZĘŚĆ II Pisanie własnych programów w języku Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Zagnieżdżanie instrukcji
Rozdział 5
Kontrolowanie
przepływu programu
za pomocą instrukcji
podejmowania decyzji
P
rogram telewizyjny Dennis Rozrabiaka emitowano w telewizji CBS od 1959
do 1963 roku. Pamiętam jeden odcinek, w którym pan Wilson miał problem
z podjęciem ważnej decyzji. Myślę, że chodziło o zmianę pracy lub prze-
prowadzkę do nowego miasta. W każdym razie wciąż widzę ujęcia pana Wilsona
siedzącego przez całe popołudnie na swoim podwórku, popijającego lemoniadę
i wpatrującego się w dal. Oczywiście irytujący Dennis nieustannie przerywał
spokój i ciszę pana Wilsona. To sprawiło, że sytuacja była zabawna.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
To, co zrobiło na mnie największe wrażenie w tym odcinku (powód, dla którego
pamiętam to do dziś), to wytrwałość pana Wilsona w próbach podjęcia decyzji.
Nie zajmował się swoim codziennym sprawami, nie włóczył się po okolicy, a myśl
o decyzji nie nawiedzała go od czasu do czasu. Siedział cicho na swoim podwór-
ku, ostrożnie i logicznie tworząc swoją mentalną listę za i przeciw. Ilu ludzi fak-
tycznie podejmuje decyzje w ten sposób?
W tamtym czasie byłem jeszcze całkiem młody. I nigdy jeszcze nie stanąłem
przed koniecznością podjęcia wielkiej decyzji, która miałaby wpływ na moją ro-
dzinę i na mnie. Mimo to zastanawiałem się, jak wyglądałby proces podejmowa-
nia takiej decyzji. Czy siedzenie godzinami jak słup soli może pomóc? Czy po-
dejmowałbym decyzje poprzez staranne wyważenia i porównywanie wszelkich
możliwości? A może strzelałbym w ciemno, podejmowałbym ryzyko i działał pod
wpływem impulsu? Tylko czas to pokaże.
Podejmowanie decyzji
(instrukcja if w języku Java)
Kiedy piszesz programy komputerowe, nieustannie natykasz się na rozgałęzienia
dróg. Czy użytkownik wpisał poprawne hasło? Jeśli tak, pozwól użytkownikowi
pracować; jeśli nie, wyrzuć go z programu. Oznacza to, że język programowania
Java musi mieć sposoby na rozgałęzianie programu w jednym z dwóch kierun-
ków. Na szczęście znalazł się na to sposób: nazywa się on instrukcją if.
Zgadnij liczbę
Na listingu 5.1 pokazane zostało użycie instrukcji if. Dwa uruchomienia programu
z tego listingu przedstawiam na rysunku 5.1.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
if (inputNumber == randomNumber) {
out.println("**********");
out.println("*Wygrałeś.*");
out.println("**********");
} else {
out.println("Przegrałeś.");
out.print("Liczba losowa wynosiła ");
out.println(randomNumber + ".");
}
out.println("Dziękuję za grę.");
keyboard.close();
}
}
RYSUNEK 5.1.
Dwie serie gry
zgadywania
import java.util.Scanner;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Spośród wszystkich tych nazw zawartych w powyższych trzech wierszach kodu
sam wymyśliłem jedyne dwie nazwy — inputNumber i keyboard. Wszystkie pozo-
stałe nazwy są częścią języka Java. Jeżeli chciałbym być kreatywny, to napisał-
bym te wiersze w ten sposób:
import java.util.Scanner;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Gdy spodziewasz się, że użytkownik wpisze wartość typu int (pewną liczbę
całkowitą), użyj metody nextInt().
Jeśli oczekujesz, że użytkownik wpisze wartość typu double (liczbę zawierającą
miejsca po przecinku), użyj metody nextDouble(). Jeśli oczekujesz, że użytkownik
wpisze wartości typu true lub false, użyj metody nextBoolean(). Z kolei
w przypadku, gdy użytkownik powinien wpisać takie słowo jak Barry, Java lub Witaj,
użyj next().
W zależności od kraju stosowane są różne znaki oddzielające miejsca dziesiętne
liczby. W Stanach Zjednoczonych 10.5 (z kropką) oznacza liczbę dziesięć i pół, ale
w Polsce liczbę tę zapisuje się jako 10,5 (z przecinkiem). W języku perskim przecinek
dziesiętny wygląda jak ukośnik (ale znajduje się nieco niżej niż cyfry). System
operacyjny komputera przechowuje informacje o kraju, w którym mieszkasz,
a Java odczytuje te informacje, aby zdecydować, jak ma wyglądać liczba dziesięć
i pół. Jeśli uruchamiasz program zawierający wywołanie metody nextDouble(),
a Java odpowiada za pomocą wyrażenia InputMismatchException, sprawdź dane
wejściowe. Prawdopodobnie wprowadzona została liczba 10.5, podczas
gdy konwencje Twojego kraju wymagają zapisu 10,5 (lub innego sposobu
reprezentowania liczby dziesięć i pół). Aby uzyskać więcej informacji na ten temat,
zobacz ramkę „Gdzie mieszkasz na tej ziemi?” znajdującą się w rozdziale 8.
Aby zobaczyć przykład, w którym użytkownik wpisuje całe słowo, popatrz na
listing 5.3 znajdujący się w dalszej części tego rozdziału. Z kolei przykład, w którym
użytkownik wpisuje pojedynczy znak, znajduje się na listingu 6.4 w rozdziale 6.
Jeszcze inny przykład, w którym to program odczytuje cały wiersz tekstu (wszystko
za jednym zamachem), znajdziesz w rozdziale 8.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W rozdziale 13. pokazuję bardziej niezawodny sposób na dołączenie instrukcji
keyboard.close() do Twojego programu Java.
Gdy Twój program wywołuje funkcję System.out.println, to korzysta z ekranu
komputera. Dlaczego zatem nie trzeba wywoływać metody close po wszystkich
wywołaniach funkcji System.out.println? Odpowiedź na to pytanie nie jest
prosta. Na listingu 5.1 Twój własny kod łączy się z klawiaturą, wywołując funkcję
new Scanner(System.in). W związku z tym w dalszej części programu Twój kod
sprząta po sobie, wywołując metodę close. Natomiast dzięki zastosowaniu funkcji
System.out.println nasz program nie tworzy samodzielnie połączenia z ekranem.
(Zmienna out jest obiektem klasy PrintStream, ale nasz program nie musi
zawierać wywołania new PrintStream(), aby przygotować się na wywołanie funkcji
System.out.println). Zamiast tego wirtualna maszyna Javy łączy się z ekranem
w Twoim imieniu. Kod wirtualnej maszyny Javy (którego wcale nie musisz oglądać)
zawiera wywołanie new PrintStream(), przygotowujące do wywołania funkcji
System.out.println. Dzięki temu, że jest to dobrze zapisany fragment kodu,
wirtualna maszyna Javy samodzielnie, bez żadnego działania z Twojej strony,
wywoła metodę out.close().
Tworzenie losowości
Osiągnięcie prawdziwej losowości jest zaskakująco trudne. Matematyk Persi Diaco-
nis powiedział, że jeśli rzucisz monetą kilka razy, zawsze zaczynając od orła na
wierzchu, prawdopodobnie będziesz wyrzucał orły częściej niż reszkę. Jeśli rzu-
cisz kilka razy, zawsze zaczynając od reszki na wierzchu, prawdopodobnie wyrzu-
cisz reszkę częściej niż orła. Innymi słowy, rzucanie monetami nie jest do końca
sprawiedliwe1.
import java.util.Random;
int randomNumber = new Random().nextInt(10) + 1;
Jeszcze raz proszę przyjąć ten kod na wiarę. Dopóki nie zyskasz więcej doświad-
czenia w programowaniu w języku Java, nie martw się o to, co oznacza nowa in-
strukcja Random().nextInt. Po prostu skopiuj ten kod do własnych programów i baw
1
Diaconis, Persi. „The Search for Randomness”. Doroczne spotkanie American Association for
Advancement of Science. Seattle, 14 lutego 2004 r.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
się nim. A jeżeli nie masz zastosowania dla liczb od 1 do 10, to też nie problem.
Aby rzucić wirtualną kostką, napiszcie poniższą instrukcję:
Instrukcja if
U podstaw listingu 5.1 znajduje się instrukcja if. Instrukcja ta reprezentuje roz-
widlenie drogi (patrz rysunek 5.2). Komputer podąża jedną z dwóch dróg —
drogą, która wyświetli w wyniku działania napis Wygrałeś, lub drogą, która wy-
pisze słowo Przegrałeś. Komputer sam zadecyduje, którą drogę wybrać, spraw-
dzając uprzednio stan warunku, czyli jego prawdziwość lub fałszywość. Na li-
stingu 5.1 testowany jest warunek:
inputNumber == randomNumber
RYSUNEK 5.2.
Instrukcja if jest jak
rozwidlenie drogi
Czy wartość funkcji inputNumber jest równa wartości randomNumber? Gdy warunek
jest spełniony (prawdziwy), to komputer wykonuje czynności między warunkiem
a słowem else. Gdy warunek okaże się jednak fałszywy, komputer wykona in-
strukcje znajdujące się po słowie else. W obu przypadkach komputer wywoła też
ostatnią funkcję println, która wyświetla komunikat Dziękuję za grę.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Czasami gdy piszę o warunku, który jest sprawdzany, używam słowa wyrażenie
zamiast słowa warunek. Jest to w porządku, ponieważ każdy warunek jest wyra-
żeniem. Wyrażenie to coś, co ma wartość, i z pewnością każdy warunek również
ma wartość. Wartość warunku może być prawdą (true) lub fałszem (false).
(Więcej informacji o wyrażeniach i wartościach takich jak true i false znajdziesz
w rozdziale 4.).
Z drugiej strony, jeśli nigdy nie popełnisz błędu, używając pojedynczego znaku
równości w zapisie warunku, to znaczy, że jesteś niezwykłą osobą. Niedawno
prowadziłem zajęcia z wstępnego kursu Javy i obiecałem kursantom, że połknę
mój wskaźnik laserowy, jeśli podczas zajęć praktyczno-laboratoryjnych nikt nie
pomyli znaków przypisania i sprawdzania równości. To nie była pusta obietnica.
Wiedziałem, że nigdy nie będę musiał tego robić. Jak się okazało, nawet gdybym
zignorował pierwszych dziesięć razy, gdy ktoś użył pojedynczego znaku równo-
ści w zapisie warunku, to i tak nie musiałbym połykać wskaźnika laserowego.
W swojej karierze programisty każdy przynajmniej kilka razy błędnie używa
pojedynczego znaku równości.
Sztuką nie jest unikanie błędu z jednym znakiem równości; sztuką jest wyłapanie
tego błędu za każdym razem, gdy się go popełni.
Przygotuj się
Instrukcja if zawarta na listingu 5.1 ma dwie połówki: górną i dolną. Mam także
nazwy dla tych dwóch części instrukcji if. Nazywam je częścią if (górna połowa)
i częścią else (dolna połowa).
Część if pokazana na listingu 5.1 zdaje się składać z więcej niż tylko jednej in-
strukcji. Jest to możliwe przez umieszczenie trzech instrukcji z części if we-
wnątrz nawiasów klamrowych. Przy użyciu takich nawiasów klamrowych two-
rzymy blok. Blok jest zbiorem instrukcji zgrupowanych przez nawiasy klamrowe.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Cała ta sprawa z blokami i nawiasami klamrowymi dotyczy również części else.
Na listingu 5.1, gdy wartość inputNumber nie jest równa wartości randomNumber,
komputer wykonuje trzy wywołania instrukcji print/println. Aby przekonać
komputer, że wszystkie trzy wywołania znajdują się w klauzuli else, umiesz-
czam je w bloku. Oznacza to, że wszystkie trzy wywołania umieszczam w na-
wiasach klamrowych.
Ściśle mówiąc, listing 5.1 zawiera tylko jedną instrukcję między instrukcjami if
i else oraz tylko jedną instrukcję po instrukcji else. Sztuczka polega na tym, że
gdy umieścisz grupę instrukcji w nawiasach klamrowych, otrzymasz tym samym
blok, a taki blok pod każdym względem zachowuje się jak pojedyncza instrukcja.
I rzeczywiście, oficjalna dokumentacja języka Java wymienia bloki jako jeden
z wielu rodzajów instrukcji. Tak więc na listingu 5.1 blok wypisujący słowo Wygrałeś
oraz gwiazdki jest pojedynczą instrukcją, która zawiera w sobie trzy mniejsze
instrukcje.
Wcięcia w instrukcji if
Zauważ, że na listingu 5.1 wywołania funkcji print i println znajdujące się we-
wnątrz instrukcji if są wcięte. (Dotyczy to zarówno instrukcji wypisujących
Wygrałeś, jak i Przegrałeś. Wywołania funkcji print i println, które następują po
słowie else, również są częścią instrukcji if). Tak naprawdę nie musisz wpro-
wadzać wcięć w instrukcjach znajdujących się wewnątrz instrukcji if. Z punktu
widzenia kompilatora możesz napisać cały program w jednym wierszu lub roz-
mieścić wszystkie swoje instrukcje w artystycznym, nieco powyginanym zyg-
zaku. Problem polega na tym, że jeśli nie sformatujesz swoich instrukcji w od-
powiednio logiczny sposób, to ani Ty, ani nikt inny nie będziecie mogli
zrozumieć tego kodu. Na listingu 5.1 wcięcie instrukcji print i println pomaga
naszym oczom (i mózgowi) szybko dostrzec, że te instrukcje są podporządkowane
ogólnemu schematowi przepływu instrukcji if / else.
W małym programie kod bez wcięć lub z kiepsko wykonanymi wcięciami można
od biedy tolerować. Ale w skomplikowanym programie wcięcia, które nie idą w pa-
rze ze zgrabnym, logicznym wzorem, są prawdziwie wielkim koszmarem.
Kiedy piszesz instrukcje if, możesz ulec pokusie, aby wyrzucić przez okno te
wszystkie reguły dotyczące nawiasów klamrowych i korzystać wyłącznie z wcięć.
Sprawdza się to w innych językach programowania, takich jak Python i Haskell,
natomiast nie działa w Javie. Jeśli wcinasz trzy instrukcje po słowie else i zapo-
mnisz ująć je w nawiasy klamrowe, komputer „myśli”, że część else zawiera
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
tylko pierwszą z tych trzech instrukcji. Co gorsza, wcięcie wprowadza w błąd, bo
sugeruje, że część else zawiera wszystkie trzy instrukcje. To sprawia, że trudniej
jest wykryć, dlaczego nasz kod nie zachowuje się tak, jak powinien. Dlatego zawsze
zwracaj uwagę na nawiasy klamrowe!
Bezelseność w Iflandii
No dobra, tytuł tego podrozdziału sobie wymyśliłem. I co z tego? Chodzi o to, że
możesz utworzyć instrukcję if bez części else. Weźmy na przykład pokazany już
wcześniej kod z listingu 5.1. Może wolałbyś nie irytować użytkownika, gdy ten
przegrywa. Jak tego dokonać, pokazuje zmodyfikowany kod programu znajdu-
jący się na listingu 5.2 (natomiast rysunek 5.3 przedstawia wynik jego działania).
if (inputNumber == randomNumber) {
out.println("*Wygrałeś.*");
}
keyboard.close();
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 5.3.
Dwie serie gry
zgadywania
z listingu 5.2
Listing 5.2 prezentuje całkiem nowy pomysł. Dzięki deklaracji import dla obiektu
System.in mogę zredukować instrukcję new scanner(System.in) do nowej, krót-
szej formy Scanner(in). Dodanie tej deklaracji importu nie jest jednak warte wy-
siłku. W rzeczywistości wpisanie tej deklaracji wymaga więcej pracy niż przygo-
towanie programu bez niej. Niemniej jednak kod z listingu 5.2 pokazuje, że
można zaimportować obiekt System.in.
W rozdziale 4. kod na listingu 4.5 informuje nas, czy uda się zmieścić dziesięć
osób w windzie. Wynik działania tego kodu wygląda mniej więcej tak:
Fałsz
albo
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Używanie bloków w JShell
Rozdział 4. przedstawia interaktywne środowisko JShell dostępne w Javie 9.
Wpisujesz instrukcję, a JShell odpowiada natychmiast, wykonując tę instrukcję.
To jest dobre dla instrukcji prostych, ale co się stanie w momencie, gdy mamy
instrukcję wewnątrz bloku?
jshell>
Gdy wpiszesz instrukcję, która nie znajduje się w bloku, JShell pozwoli pominąć
średnik na końcu tej instrukcji.
Po wpisaniu instrukcji znajdującej się w bloku JShell (podobnie jak zwykła stara
Java przedstawiona na listingu 5.2) nie pozwala pominąć średnika.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wpisując blok w JShell, masz możliwość wpisania całego bloku w jednym wierszu,
bez żadnych podziałów na kolejne, na przykład w ten sposób:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zachowaj szczególną ostrożność, sprawdzając, czy dwie liczby są sobie równe
(symbolem ==), czy nie (symbolem !=). Po wykonaniu pewnych obliczeń i uzy-
skaniu dwóch wartości typu double lub typu float okazuje się, że obie liczby tylko
bardzo rzadko będą sobie równe. (Problem wiąże się z tymi nieznośnymi cyframi
po przecinku). Na przykład ekwiwalent 21 stopni Celsjusza na skali Fahrenheita
to 69,8. Gdy ręcznie obliczysz 9,0 / 5 * 21 + 32, to otrzymasz 69,8. A jednak
warunek 9,0 / 5 * 21 + 32 == 69,8 okazuje się być fałszywy. Dzieje się tak dla-
tego, że gdy komputer oblicza wartość 9,0 / 5 * 21 + 32, to otrzymuje liczbę
69.80000000000001, a nie 69,8.
Porównywanie obiektów
Kiedy zaczniesz pracować z obiektami, odkryjesz, że możesz użyć operatorów
porównania == i !=, aby porównać ze sobą obiekty. Na przykład przycisk widoczny
na ekranie komputera jest obiektem. Możesz zatem sprawdzić, czy to coś, co zo-
stało właśnie kliknięte myszą, jest konkretnym przyciskiem na ekranie. A do-
konasz tego za pomocą operatora równości Java:
if (e.getSource() == bCopy) {
clipboard.setText(which.getText());
RYSUNEK 5.4.
Wyniki użycia
operatora == i użycia
metody equals
w języku Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 5.3. Sprawdzanie hasła
import static java.lang.System.*;
import java.util.Scanner;
out.print("Podaj hasło?");
if (password == "wieloryb") {
out.println("Wpisane słowo jest zapisane");
out.println("w tym samym miejscu w pamięci");
out.println("co prawdziwe hasło.");
out.println("Musisz być hakerem.");
} else {
out.println("Wpisane słowo nie jest zapisane");
out.println("w tym samym miejscu w pamięci");
out.println("co prawdziwe hasło.");
out.println("Ale to żaden problem.");
}
out.println();
if (password.equals("swordfish")) {
out.println("Słowo, które wpisałeś, ma");
out.println("te same znaki co prawdziwe");
out.println("hasło. Możesz użyć naszego");
out.println("cennego systemu.");
} else {
out.println("Słowo, które wpisałeś, nie");
out.println("ma takich samych znaków jak");
out.println("prawdziwe hasło. Nie możesz");
out.println("użyć naszego cennego systemu.");
}
keyboard.close();
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Bardziej odpowiednia z tych dwóch technik wykorzystuje metodę equals języka
Java. Metoda ta wygląda śmiesznie, ponieważ po jej wywołaniu wstawiasz kropkę
po jednym ciągu znaków i umieszczasz drugi ciąg znaków w nawiasach. Ale nie-
stety w ten sposób musisz to zrobić.
if ("wieloryb".equals(password))
Porównując ciągi znaków, użyj metody equals, a nie podwójnego znaku równości.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Operatory logiczne w języku Java
Pan Spock byłby zadowolony: język Java ma wszystkie operatory, których po-
trzebujesz do miksowania i dopasowywania testów logicznych. Operatory te przed-
stawiono w tabeli 5.2.
if (
username != null && password != null &&
(
(username.equals("bburd") && password.equals("wieloryb")) ||
(username.equals("hritter") && password.equals("preakston"))
)
)
{
JOptionPane.showMessageDialog(null, "Jesteś zalogowany.");
} else {
JOptionPane.showMessageDialog(null, "Jesteś podejrzany.");
}
}
}
Na rysunku 5.5 pokazano kilka przebiegów programu z listingu 5.4. Gdy nazwą
użytkownika jest bburd, a hasło to wieloryb lub gdy nazwą użytkownika jest hrit-
ter, a hasło to preakston, użytkownik otrzymuje miłą wiadomość. W przeciwnym
razie użytkownik uznawany jest za włamywacza i dostaje nieprzyjemną wiado-
mość, na którą zasługuje.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 5.5.
Kilka przebiegów
kodu z listingu 5.4
znajdująca się na listingu 5.4 realizuje mniej więcej to samo zadanie, co instrukcja:
Pod koniec kodu znajdującego się na listingu 5.4 wprowadzam drobną zmianę
w sposobie wykorzystania klasy JOptionPane:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
JOptionPane.showMessageDialog(null, "Jesteś zalogowany.");
Nazwa JOptionPane została zdefiniowana w API Javy, podobnie jak tysiące innych
nazw. (Aby być bardziej szczegółowym, JOptionPane jest zdefiniowana wewnątrz
czegoś o nazwie javax.swing, a to coś z kolei jest zdefiniowane w API języka
Java). Aby w kodzie z listingu 5.4 użyć nazwy JOptionPane, muszę na początku
tego kodu zaimportować nazwę javax.swing.JOptionPane.
if (
username != null
if (
username nie jest niczym
albo w ten:
if (
username ma jakąkolwiek wartość
2
W języku rosyjskim „bystrzak” to „чaйник”, który interpretowany dosłownie oznacza „czajniczek”.
Tak więc po rosyjsku ta książka to Java dla czajniczków. Nigdy nie nazywano mnie „czajniczkiem”,
a nie jestem pewien, jak bym zareagował, gdyby ktoś mnie tak nazwał.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Aby dowiedzieć się, jak nazwa użytkownika może nie mieć żadnej wartości, zo-
bacz ostatni wiersz na rysunku 5.5. Po kliknięciu przycisku Anuluj w pierwszym
oknie dialogowym komputer przekaże wartość null do programu. Tak więc na
listingu 5.4 zmienna username otrzymuje wartość null. Porównanie username !=
null sprawdza, czy przypadkiem w pierwszym oknie dialogowym programu nie
kliknięto przycisku Anuluj. Z kolei porównanie password != null wykonuje kon-
trolę dla drugiego okna dialogowego programu. Widząc instrukcję if z listingu
5.4, możesz sobie wyobrazić, że widzisz następujące elementy:
if (
nie nacisnąłeś Anuluj w oknie dialogowym nazwy użytkownika i
nie nacisnąłeś Anuluj w oknie dialogowym hasła i
(
(wpisałeś bburd w oknie dialogowym nazwy użytkownika i
wpisałeś wieloryb w oknie hasła) lub
(wpisałeś hritter w oknie dialogowym nazwy użytkownika i
wpisałeś preakston w oknie dialogowym hasła)
)
)
Ostatnia para wartości null na listingu 5.4 różni się od pozostałych. W wywoła-
niu JOptionPane.showMessageDialog(null, "Jesteś zalogowany.") słowo null
oznacza „brak innych okien dialogowych”. W szczególności wywołanie funkcji
showMessageDialog mówi językowi Java, aby wyświetlił nowe okno dialogowe,
a słowo null wskazuje, że to nowe okno nie wywodzi się z żadnego istniejącego już
okna dialogowego. Tak czy inaczej Java nalega, abyś powiedział coś o pochodzeniu
nowo tworzonego okna dialogowego. (Z jakiegoś powodu Java nie nalega, abyś
określił pochodzenie okna showInputDialog. Ciekawostka!) W każdym razie na li-
stingu 5.4 całkiem przydatna jest możliwość wyświetlenia dowolnego okna dia-
logowego przy użyciu funkcji showMessageDialog.
(Warunki w nawiasach)
Miej oko na te nawiasy! Gdy łączysz warunki z operatorami logicznymi, lepiej
poświęcić trochę wysiłku na wpisanie niepotrzebnych nawiasów niż zepsuć sobie
wynik, używając ich zbyt mało. Weźmy na przykład wyrażenie:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Poprzez błędne odczytanie tego wyrażenia możesz dojść do wniosku, że wyraże-
nie jest nieprawdziwe. Chodzi o to, że można błędnie odczytać wyrażenie jako
(cośtam-cośtam) && 27 < 1. Jak widać, 27 < 1 jest nieprawdziwe, dlatego można
by wywnioskować, że całe wyrażenie jest fałszywe. Trzeba jednak pamiętać, że w
języku Java każdy operator && jest oceniany przed dowolnym operatorem ||. Tak
więc wyrażenie naprawdę pyta, czy 2 < 5 || (cośtam-cośtam). A skoro porównanie
2 < 5 jest prawdziwe, to całe wyrażenie również jest prawdziwe.
Aby zmienić wartość wyrażenia z wartości true na false, możesz umieścić dwa
pierwsze porównania wyrażenia w nawiasach, tak jak poniżej:
W Javie nie można łączyć porównań, tak jak robi się to w języku polskim. W ję-
zyku polskim możesz powiedzieć: „Będziemy mieć od trzech do dziesięciu osób
przy stole”. Ale w języku Java, jeśli napiszesz 3 <= ludzie <= 10, to otrzymasz
komunikat o błędzie. Musisz napisać coś w takim stylu: 3 <= ludzie && ludzie
<= 10.
Na listingu 5.4 warunek instrukcji if zawiera ponad tuzin nawiasów. Co się sta-
nie, jeśli pominiesz dwa z nich?
if (
username != null && password != null &&
// pominięto nawias otwierający
(username.equals("bburd") && password.equals("swordfish")) ||
(username.equals("hritter") && password.equals("preakston"))
// pominięto nawias zamykający
)
Java stara się odgadnąć Twoje życzenia, grupując wszystkie elementy znajdujące
się przed operatorem „lub” (operator ||):
if (
username != null && password != null &&
(username.equals("bburd") && password.equals("wieloryb"))
||
Gdy użytkownik kliknie przycisk Anuluj, to zmienna username będzie miała wartość
null. Java stwierdzi wtedy: „Dobra! Elementy przed operatorem || są fałszywe,
ale może elementy znajdujące się za nim są prawdziwe. Sprawdzę wszystko, co
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
zostało umieszczone za operatorem ||, aby dowiedzieć się, czy jest to prawdą”.
(Java często mówi do siebie. Psychiatrzy monitorują tę sytuację).
Budowanie gniazda
Widziałeś te urocze rosyjskie matrioszki? Otwórz jedną, a drugą znajdziesz w środku.
Otwórz drugą, a trzecia będzie w środku. To samo możesz zrobić z instrukcjami
języka Java. (I już jest zabawa!) Przykład takiego działania przedstawiam na li-
stingu 5.5.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 5.5. Zagnieżdżone instrukcje if
import static java.lang.System.out;
import java.util.Scanner;
if (username.equals("bburd")) {
out.print("Hasło: ");
String password = keyboard.next();
if (password.equals("wieloryb")) {
out.println("Jesteś zalogowany.");
} else {
out.println("Niepoprawne hasło");
}
} else {
out.println("Nieznany użytkownik");
}
keyboard.close();
}
}
Rysunek 5.6 przedstawia kilka przebiegów kodu pokazanego na listingu 5.5. Przede
wszystkim chodzi o to, że zalogowanie się wymaga przejścia przez dwa testy.
(Innymi słowy, muszą być spełnione dwa warunki). Pierwszy warunek sprawdza
poprawność nazwy użytkownika; drugi warunek sprawdza poprawność hasła.
Jeśli zaliczysz pierwszy test (test nazwy użytkownika), maszerujesz bezpośred-
nio do drugiej instrukcji if, która wykonuje drugi test (test hasła). Jeśli nie zaliczysz
pierwszego testu, nigdy nie przejdziesz do drugiego testu. Ogólny plan działania
przedstawiam na rysunku 5.7.
RYSUNEK 5.6.
Trzy przebiegi
programu
z listingu 5.5
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 5.7.
Nie próbuj jeść tym
widelcem
Zmodyfikuj program znajdujący się na listingu 5.4, tak aby po kliknięciu przy-
cisku Anuluj przed wpisaniem nazwy użytkownika lub hasła program odpowiedział
komunikatem „Brak informacji”.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
wybierania spośród wielu możliwych wariantów. To dla mnie wielka ulga, że
mogłem to z siebie wyrzucić!
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Kompletny program do wyświetlania tekstu piosenki „Al’s All Wet” pojawi się
dopiero w rozdziale 6. Na razie załóżmy, że mamy zmienną o nazwie verse. W tej
zmiennej może znaleźć się wartość 1, 2, 3 lub 4, w zależności od tego, którą
zwrotkę tej piosenki będziemy aktualnie wypisywać. Można by przygotować dużą,
niezdarną grupę instrukcji if sprawdzających każdą możliwą wartość zmiennej verse:
if (verse == 1) {
out.println("That's because he has no brain.");
}
if (verse == 2) {
out.println("That's because he is a pain.");
}
if (verse == 3) {
out.println("’Cause this is the last refrain.");
}
Ale takie podejście wydaje się jednak marnotrawstwem czasu. Dlaczego by nie
utworzyć instrukcji, która tylko raz sprawdza wartość zmiennej verse, a następ-
nie podejmuje działanie na podstawie znalezionej wartości? Na szczęście taka
instrukcja już istnieje. Nazywa się ona instrukcją switch. Na listingu 5.6 został
przedstawiony przykład zastosowania instrukcji switch.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Rysunek 5.8 przedstawia dwa przebiegi programu z listingu 5.6. (Natomiast ry-
sunek 5.9 ilustruje ogólną ideę programu). Po pierwsze użytkownik wpisuje
liczbę, na przykład 2. Następnie programu dociera do początku instrukcji switch.
Komputer sprawdza tutaj wartość zmiennej verse i gdy ustali, że wartość tej
zmiennej to 2, zaczyna sprawdzać każdy kolejny przypadek instrukcji switch. Je-
śli okaże się, że wartość 2 nie pasuje do górnego przypadku, to komputer przej-
dzie do sprawdzenia środkowego z trzech przypadków. Wartość umieszczona dla
środkowego przypadku (numer 2) odpowiada wartości zmiennej verse, a zatem
komputer wykona instrukcje, które znajdują się tuż po instrukcji case 2. Te in-
strukcje to:
RYSUNEK 5.8.
Dwa uruchomienia
kodu z listingu 5.6
RYSUNEK 5.9.
Duży widelec
w kodzie z listingu 5.6
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeśli nieznośny użytkownik poprosi o zwrotkę numer 6, komputer omija przy-
padki case 1, 2, 3 i przechodzi od razu do ustawień domyślnych, czyli wyświetli
tekst Brak takiej zwrotki. Proszę, spróbuj ponownie, a następnie wyjdzie z in-
strukcji switch. W kolejnym kroku zostanie wyświetlony tekst Ohhhhhhhh ....
Zwykle gdy używasz instrukcji switch, nie chcesz przechodzić dalej, więc stosu-
jesz instrukcję przerwania break na końcu sekcji case. Czasami jednak może się
zdarzyć, że takie przejście dalej jest tym, czego aktualnie potrzebujesz. Weźmy
na przykład naszą piosenkę „Al’s All Wet”. (Ten wspaniały tekst znajduje się
w ramce z nazwą utworu). Każda zwrotka tej piosenki dodaje nowe wiersze obok
wierszy z poprzednich zwrotek. Ta sytuacja (gromadzenie wierszy z kolejnych
zwrotek) domaga się zastosowania instrukcji switch z wykorzystaniem przejścia.
Realizację tego pomysłu przedstawiam na listingu 5.7.
switch (verse) {
case 3:
out.print("Last refain, ");
out.println("last refain,");
case 2:
out.print("He's a pain, ");
out.println("he's a pain,");
case 1:
out.print("Has no brain, ");
out.println("has no brain,");
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
}
keyboard.close();
}
}
RYSUNEK 5.10.
Czterokrotne
uruchomienie kodu
z listingu 5.7
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zauważ, co się dzieje, gdy użytkownik prosi o wers numer 6. Instrukcja switch
na listingu 5.7 nie ma przypadku case 6 i nie ma też bloku default. W takiej sy-
tuacji nie jest wykonywane żadne z poleceń znajdujących się w instrukcji switch.
Niemniej gdy użytkownik poprosi o wers 6., komputer wyświetli jakiś tekst, po-
nieważ instrukcje wypisujące teksty In the rain, in the rain oraz Ohhhhhhhh ...
znajdują się zaraz za instrukcją switch.
switch (verse) {
case "pierwsza":
out.println("That's because he has no brain.");
break;
case "druga":
out.println("That's because he is a pain.");
break;
case "trzecia":
out.println("'Cause this is the last refrain.");
break;
default:
out.println("Brak takiej zwrotki. Proszę, spróbuj ponownie.");
break;
}
out.println("Ohhhhhhhh... .");
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
keyboard.close();
}
}
RYSUNEK 5.11.
Wynik działania
programu
z listingu 5.8
Uczyń swój kod jeszcze lepszym! Niech użytkownik wprowadzi nazwę miesiąca,
ale także wprowadzi odpowiedź: tak lub nie w odpowiedzi na pytanie: Czy jest
to rok przestępny?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
150 CZĘŚĆ II Pisanie własnych programów w języku Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Zliczanie w pętli
Rozdział 6
Sterowanie
przepływem programu
za pomocą pętli
W
1966 firma produkująca szampon Head & Shoulders zapisała się na kar-
tach historii. W tylnej części butelki umieściła wskazówki dotyczące uży-
wania szamponu „SPIENIĆ-SPŁUKAĆ-POWTÓRZYĆ”. Nigdy wcześniej
nie było kompletnego zestawu wskazówek (do robienia czegokolwiek, nie mówiąc
już o myciu włosów), które zostały tak zwięźle streszczone. Ludzie z branży pisania
instrukcji uznali to za monumentalne osiągnięcie. W tamtym czasie takie wytycz-
ne kontrastowały z innymi. (Na przykład pierwsze zdanie na puszce z aerozolem
brzmiało: „Obróć tę puszkę tak, aby dysza była z dala od twojej twarzy” No ba!)
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Podstawową ideą jest to, że kiedy stosujesz się do wskazówek, nie wykonujesz
tylko jednej instrukcji po drugiej. W rzeczywistości ciągle zmieniasz swój sposób
postępowania. Podejmujesz decyzje („Jeśli WŁOSY SĄ SUCHE, to UŻYJ ODŻYWKI”)
albo przechodzisz do pętli („SPIENIĆ–SPŁUKAĆ, a następnie ponownie SPIENIĆ–
-SPŁUKAĆ”). W programowaniu komputerowym cały czas używasz podejmowania
decyzji i zapętlania. W tym rozdziale zajmiemy się pętlami stosowanymi w ję-
zyku Java.
int numGuesses = 0;
int randomNumber = new Random().nextInt(10) + 1;
out.print("Wygrywasz po");
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
out.println(numGuesses + " próbach.");
keyboard.close();
}
}
RYSUNEK 6.1.
Graj, aż zgadniesz
Spójrz na listing 6.1, a zobaczysz kod, który wykonuje całą tę pracę. U podstaw
kodu leży coś nazywane instrukcją while (zwana również pętlą while). Instrukcja
while sformułowana w języku polskim będzie brzmiała następująco:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 6.2.
Zabawa w kółeczku
że gdy program zaczyna działać, użytkownik jeszcze nie zgadywał. Później w pro-
gramie, zaraz po każdym wywołaniu instrukcji keyboard.nextInt, znajduje się in-
strukcja numGuesses++. I tak właśnie ma to działać — licznik jest inkrementowany,
gdy tylko użytkownik wprowadzi kolejną liczbę.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
w nawiasach klamrowych tej pętli. Następnie ponownie przeprowadza test (aby
sprawdzić, czy zmienna inputNumber nadal nie jest równa zmiennej randomNumber),
ale dopiero po pełnym wykonaniu wszystkich pięciu instrukcji w pętli.
Zmodyfikuj program z listingu 6.1 tak, aby losowo wygenerowana liczba była
liczbą z zakresu od 1 do 100. Dodatkowo, żeby ułatwić graczowi zabawę, niech
program poda wskazówkę za każdym razem, gdy gracz odgadnie nieprawidłowo.
Bardzo pomocne będą na przykład takie wskazówki jak wyświetlenie tekstu
Wypróbuj wyższą liczbę lub Wypróbuj niższą liczbę.
Na całe szczęście w tamtym czasie nie wiedziałeś jeszcze nic o pętlach i licznikach.
Gdybyś wskazał wszystkie te rzeczy swojemu nauczycielowi, wpadłbyś w dużo
większe tarapaty.
Tak czy inaczej życie pełne jest przykładów pętli zliczających. A programowanie
komputerowe jest odzwierciedleniem życia — a może to było odwrotnie? Kiedy
mówisz komputerowi, co ma robić, często mówisz mu, aby wypisał trzy wiersze,
obsłużył dziesięć kont, wybrał milion numerów telefonów lub cokolwiek innego.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Z powodu tego, że pętle zliczające są tak powszechne w programowaniu, ludzie
tworzący języki programowania opracowali instrukcje wyspecjalizowane do ob-
sługi tego rodzaju pętli. W języku Java instrukcja powtarzająca pewną liczbę razy
nazywana jest instrukcją pętli for. Na listingach 6.2 i 6.3 zilustrowano użycie
właśnie takiej instrukcji. I tak: listing 6.2 zawiera szokująco prosty przykład, na-
tomiast listing 6.3 pokazuje bardziej egzotyczne zastosowanie. Wybierz, co Ci bar-
dziej odpowiada.
out.println("Gotowe!");
}
}
RYSUNEK 6.3.
Zliczanie do 10
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Gdy zmienna count ma już wartość 2, instrukcja for sprawdza ponownie, aby
upewnić się, że wartość tej zmiennej jest mniejsza lub równa 10. (No tak, 2 jest
mniejsze niż 10). Ponieważ ten test zostaje zaliczony, instrukcja for powraca do
instrukcji umieszczonych w nawiasach klamrowych i wypisuje na ekranie tekst
Licznik pętli ma wartość 2. I wreszcie instrukcja for wykonuje tę ostatnią rzecz
znajdującą się w nawiasie — dodaje 1 do zmiennej count, zwiększając tym samym
jej wartość do 3.
I tak dalej. Cały ten proces powtarza się, aż po dziesięciu iteracjach zmienna
count w końcu osiągnie wartość 11. Gdy się tak stanie, sprawdzanie, czy wartość
zmiennej count jest mniejsza lub równa 10, kończy się niepowodzeniem i tym
samym wykonywanie pętli for zostaje zatrzymane. Komputer przeskakuje do
instrukcji znajdującej się bezpośrednio za pętlą for. W przypadku programu z li-
stingu 6.2 komputer wypisze na ekranie tekst Gotowe!. Cały ten proces przedsta-
wiam na rysunku 6.4.
RYSUNEK 6.4.
Działanie pętli for
zawarte
na listingu 6.2
Każdy z tych trzech znajdujących się w nawiasach elementów odgrywa swoją od-
rębną rolę:
Inicjalizacja jest wykonywana tylko raz, gdy program po raz pierwszy dotrze
do instrukcji for.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wyrażenie jest sprawdzane kilka razy (przed każdą iteracją).
Aktualizacja jest również wykonywana kilka razy (na końcu każdej iteracji).
Jeśli to pomoże, pomyśl o pętli tak, jakby jej zapis został rozłożony do takiej postaci:
int count = 1
for count <= 10 {
out.print("Licznik pętli ma wartość ");
out.print(count);
out.println(".");
count++;
}
W ten sposób nie da się napisać prawdziwej instrukcji pętli for. Pomimo tego jest
to właściwa kolejność wykonywania elementów tej instrukcji.
Jeśli deklarujesz zmienną w inicjalizacji pętli for, to nie możesz użyć tej zmien-
nej poza pętlą. Na przykład, jeżeli na listingu 6.2 spróbujesz umieścić instrukcję
out.println(count) po zakończeniu pętli for, to pojawi się komunikat o błędzie.
Wszystko, co można wykonać za pomocą pętli for, można również zrobić za pomocą
pętli while. Wybór pętli for jest kwestią stylu i wygody, a nie zaś konieczności.
Inicjalizacja instrukcji for może składać się z kilku części. Aktualizacja instrukcji
for także może składać się z kilku części. Aby dowiedzieć się, jak to zrobić,
skorzystaj z JShell i wprowadź poniższe wiersze lub umieść je w małym
programie języka Java:
import static java.lang.System.out
for (int i = 0, j = 10; i < j; i++, j--) {out.println(i + " " + j);}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Bez wcześniejszego uruchomienia poniższego kodu spróbuj przewidzieć,
co otrzymamy w wyniku jego działania:
for (int row = 0; row < 5; row++) {
for (int column = 0; column < 5; column++) {
System.out.print("*");
}
System.out.println();
}
Po dokonaniu tej prognozy uruchom kod, aby dowiedzieć się, czy Twoje
przewidywania były słuszne.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
out.println("Oh, why is Al all wet? Oh,");
out.print("Al's all wet 'cause ");
out.println("he's standing in the rain.");
out.println("Why is Al out in the rain?");
switch (verse) {
case 1:
out.println("That's because he has no brain.");
break;
case 2:
out.println("That's because he is a pain.");
break;
case 3:
out.println("'Cause this is the last refrain.");
break;
}
switch (verse) {
case 3:
out.println("Last refrain, last refrain,");
case 2:
out.println("He's a pain, he's a pain,");
case 1:
out.println("Has no brain, has no brain,");
}
Kod z listingu 6.3 jest interesujący, ponieważ łączy wiele pomysłów z rozdziałów
5. i 6. Na listingu 6.3 dwie instrukcje switch są zagnieżdżone w pętli for. Jedna
z tych instrukcji wykorzystuje polecenia break; natomiast druga wykorzystuje
efekt przejścia. Wartość zmiennej licznika przebiegów pętli for (verse) zmienia
się z 1 na 2, a następnie na 3, a zatem wykonywane są wszystkie przypadki w obu
instrukcjach switch. Gdy program zbliża się do końca, a wykonanie pętli for się za-
kończyło, ostatnie cztery instrukcje programu wypisują ostatnią zwrotkę piosenki.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
for ( ; ; ) działać będzie wiecznie, co przydaje się w sytuacji, w której taka pętla
będzie kontrolować jakąś poważną maszynę. Pisząc instrukcję for, zazwyczaj
zliczamy, ile razy należy powtórzyć pewien kod. Ale prawdę mówiąc, za pomocą
tej instrukcji możesz zrobić prawie każdy rodzaj powtórzenia.
while (true) {
System.out.print("Wprowadź liczbę całkowitą: ");
int i = keyboard.nextInt();
if (i == 0) {
break;
}
System.out.println(i);
}
System.out.println("Gotowe!");
keyboard.close();
Warunek tej pętli jest zawsze prawdziwy. To jak rozpoczęcie pętli takim wierszem:
while (1 + 1 == 2)
Gdyby nie instrukcja break, pętla działałaby w nieskończoność. Na szczęście po
wykonaniu instrukcji break Java przeskakuje do kodu znajdującego się bezpośrednio
za pętlą.
while (true) {
System.out.print("Wprowadź liczbę całkowitą: ");
int i = keyboard.nextInt();
if (i > 10) {
continue;
}
if (i == 0) {
break;
}
System.out.println(i);
}
System.out.println("Gotowe!");
keyboard.close();
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Powtarzaj, aż uzyskasz to,
czego chcesz (instrukcje do w języku Java)
Głupcy pędzą tam, gdzie aniołowie boją się stąpać.
— ALEXANDER POPE
Dzisiaj chcę być młody i głupi (a przynajmniej głupi). Spójrz ponownie na rysu-
nek 6.2 i zwróć uwagę na to, jak działa pętla while w języku Java. Gdy program
dochodzi do pętli while, komputer sprawdza, czy warunek tej pętli jest prawdzi-
wy. Jeśli nie jest prawdziwy, instrukcje w tej pętli nigdy nie będą wykonywane —
nawet jednokrotnie. Rzeczywiście możesz łatwo przygotować pętlę while, której
instrukcje nigdy nie zostaną wykonane (chociaż nie mogę wymyślić powodu, dla
którego kiedykolwiek chciałbyś to zrobić):
int twoPlusTwo = 2 + 2;
while (twoPlusTwo == 5) {
out.println("Żartujesz sobie?");
out.println("2 + 2 nie jest równe 5");
out.print("Wszyscy to wiedzą");
out.println(" 2 + 2 jest równe 3");
}
Pomimo tego głupiego przykładu twoPlusTwo instrukcja while okazuje się być
najbardziej wszechstronną konstrukcją pętli w języku Java. W szczególności pętla
while jest przydatna w sytuacjach, w których zanim coś zrobisz, musisz najpierw
pomyśleć. Na przykład: „Dopóki mam pieniądze na koncie, co miesiąc wypisuję
czek na hipotekę”. Gdy po raz pierwszy spotkasz się z tym stwierdzeniem, a Twoje
konto ma zerowe saldo, to z pewnością nie chcesz wypisywać czeku hipotecznego
— nawet jednego.
Ale czasami (nie zawsze) chcesz coś zrobić bez myślenia. Weźmy na przykład
sytuację, w której prosimy użytkownika o odpowiedź. Taka odpowiedź może być
sensowna, a może i nie. Jeśli tak nie jest, należy zapytać ponownie. Może po prostu
użytkownik pomylił się przy pisaniu, a może w ogóle nie zrozumiał pytania.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 6.5.
Dwa przebiegi
programu zawartego
na listingu 6.4
Aby napisać ten program, potrzebujesz takiej pętli — pętli, która wielokrotnie
będzie pytała użytkownika, czy plik powinien zostać usunięty. Pętla pyta, dopóki
użytkownik nie udzieli zrozumiałej odpowiedzi. Warto zauważyć, że nie musi
ona niczego sprawdzać, zanim zapyta użytkownika po raz pierwszy. Rzeczywi-
ście, zanim użytkownik udzieli pierwszej swojej odpowiedzi, pętla nie ma nic do
sprawdzenia. Jej działanie nie zaczyna się od zapisu „dopóki to i to jest prawdzi-
we, żądaj od użytkownika odpowiedzi na pytanie”. W tym przypadku pętla od razu
zaczyna działać, pobiera od użytkownika odpowiedź, i dopiero wtedy sprawdza,
czy otrzymana wartość ma jakikolwiek sens.
do {
out.print("Usunąć dowody? (t/n) ");
reply = keyboard.findWithinHorizon(".",0).charAt(0);
} while (reply != 't' && reply != 'n');
if (reply == 't') {
out.println("Dobra, usuwam... ");
evidence.delete();
out.println("Dowody zostały usunięte.");
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
} else {
out.println("Wybacz, stary. Tylko pytam.");
}
keyboard.close();
}
}
do {
out.print("Usunąć dowody? (t/n) ");
reply = keyboard.findWithinHorizon(".", 0).charAt(0);
} while (reply != 't' && reply != 'T' && reply != 'n' && reply!='N');
RYSUNEK 6.6.
Kręcimy się w pętli,
pętli do
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
moim dysku twardym w folderze o nazwie 06-04. Wewnątrz tego folderu mam
także plik o nazwie falszyweRachunki.txt. Jeśli będziesz miał z tym problem, dodaj
następujący kod do listingu 6.4, wpisując go od razu po instrukcji new File:
try {
out.println("Szukam " + evidence.getCanonicalPath());
} catch (java.io.IOException e) {
e.printStackTrace();
}
Po uruchomieniu tego kodu Java poinformuje Cię, gdzie powinien się znajdować
na twardym dysku plik falszyweRachunki.txt.
Ale w kodzie z listingu 6.4 nie jesteśmy zainteresowani odczytaniem kilku znaków.
Chcemy, żeby użytkownik wpisał tylko jedną literę — t lub n. W związku z tym
nie tworzymy zmiennej typu String, która będzie przechowywała odpowiedź użyt-
kownika. W tym przypadku użyjemy zmiennej typu char, która przechowuje tylko
jeden znak.
API języka Java nie definiuje metody nextChar, tak więc aby odczytać coś, co
nadawałoby się do przechowywania w zmiennej typu char, musimy improwizo-
wać. Na listingu 6.4 moja improwizacja wygląda następująco:
keyboard.findWithinHorizon(".", 0).charAt(0)
Zawsze gdy chcesz uzyskać pojedynczy znak, możesz użyć tego kodu dokładnie
w taki sposób, jak jest to przedstawione na listingu 6.4.
Zmienna typu String może przechowywać wiele znaków lub tylko jeden. Jednak
zmienna typu String przechowująca tylko jeden znak nie jest tym samym co
zmienna typu char. Bez względu na to, co umieścisz w zmiennej typu String,
zmienne typu String i zmienne typu char muszą być traktowane rozdzielnie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Obsługa plików w Javie
Na listingu 6.4 musimy zwrócić uwagę na instrukcje związane z obsługą plików.
W tych instrukcjach wykorzystywane są klasy, obiekty i metody. Wiele ważnych
szczegółów na ten temat znajduje się w innych rozdziałach, na przykład w roz-
działach 7. i 9. Mimo to nie zaszkodzi poruszyć tutaj kilku ważniejszych ele-
mentów.
Obiekt evidence, jako instancja klasy java.io.File, ma metodę delete. (Co mogę
więcej powiedzieć? To wszystko jest w dokumentacji interfejsu API Java). Kiedy
wywołujesz metodę evidence.delete, komputer pozbywa się tego pliku.
Oczywiście nie można pozbyć się czegoś, co nie istnieje. Gdy komputer wykonuje
instrukcję:
języka Java nie sprawdza, czy plik o nazwie falszyweRachunki.txt rzeczywiście ist-
nieje. Mamy kilka opcji, aby zmusić program do wykonania takiej kontroli. Naj-
prostszą jest wywołanie metody exists. Podczas wywołania evidence.exists()
metoda ta sprawdza folder, w którym Java spodziewa się znaleźć plik falszywe-
Rachunki.txt. Wywołanie tej metody zwraca wartość true (prawda), jeśli Java
znajdzie w folderze nasz plik falszyweRachunki.txt. W przeciwnym razie wywoła-
nie metody evidence.exists() zwróci wartość false (fałsz). Oto ulepszona wersja
kodu z listingu 6.4, zawierająca wywołanie metody exists:
import java.io.File;
import static java.lang.System.out;
import java.util.Scanner;
do {
out.print("Usunąć dowody? (t/n) ");
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
reply =
keyboard.findWithinHorizon(".", 0).charAt(0);
} while (reply != 't' && reply != 'n');
if (reply == 't') {
out.println("Dobra, usuwam...");
evidence.delete();
out.println("Dowody zostały usunięte.");
} else {
out.println("Wybacz, stary. Tylko pytam.");
}
keyboard.close();
}
}
}
do {
out.print("Usunąć dowody? (t/n) ");
char reply = keyboard.findWithinHorizon(".", 0).charAt(0);
} while (reply != 't' && reply != 'n');
if (reply == 't')
Oznacza to, że w kodzie z listingu 6.4 mamy związane ręce. Pierwsze rzeczywi-
ste użycie zmiennej reply programu znajduje się wewnątrz pętli. Ale aby ta
zmienna była także dostępna poza pętlą, musimy ją zadeklarować jeszcze przed
samą pętlą. W tej sytuacji najlepiej jest zadeklarować zmienną reply bez jej ini-
cjowania. Bardzo interesujące!
Wszystkie wersje języka Java mają trzy rodzaje pętli, które opisałem w tym rozdziale
(pętlę while, pętlę for i pętlę do ... while). Ale nowsze wersje Javy (a konkretnie
wersja Java 5 i późniejsze) mają jeszcze jeden rodzaj pętli, zwanej rozszerzoną pętlą
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
for (ang. enhanced for). Aby zapoznać się z rozszerzoną pętlą for w języku Java,
zajrzyj do rozdziału 11.
do {
out.println();
out.println("Spróbuj ponownie...");
out.print("Wpisz liczbę całkowitą od 1 do 10: ");
inputNumber = keyboard.nextInt();
numGuesses++;
} while (inputNumber != randomNumber);
out.print("Wygrywasz po ");
out.println(numGuesses + " próbach.");
Kod z listingu 6.1 używa pętli while, ale ten zmodyfikowany wykorzystuje pętlę do.
Czy ten zmodyfikowany kod działa poprawnie? Dlaczego tak lub dlaczego nie?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Praca w szerszej
perspektywie
— programowanie
obiektowe
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TEJ CZĘŚCI
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 7
Myślenie
w kategoriach
klas i obiektów
M
ówiono mi o tym wielokrotnie, że jako autor książek komputerowych nie
powinienem oczekiwać, że ludzie będą czytać wszystkie podrozdziały
i rozdziały w logicznym porządku. Ludzie skaczą, wybierając to, czego
aktualnie potrzebują, i pomijając to, czego nie chcą czytać. Mając to na uwadze,
zdaję sobie sprawę, że możesz pominąć rozdział 1. Jeśli tak było, nie czuj się winny.
Możesz to wszystko sobie zrekompensować w ciągu zaledwie 60 sekund, czytając
następujące informacje zaczerpnięte właśnie z rozdziału 1.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
No tak, możesz oczywiście pominąć ten 60-sekundowy akapit podsumowujący.
W takim przypadku spróbuj odzyskać choć część swoich strat. Możesz to zrobić,
czytając poniższe dwusłowowe podsumowanie rozdziału 1:
Klasy, obiekty.
Definiowanie klasy
(co to znaczy być kontem)
Co odróżnia jedno konto bankowe od drugiego? Jeśli zadasz to pytanie bankiero-
wi, usłyszysz długi bełkot marketingowy. Bankier opisuje stopy procentowe,
opłaty, kary — całą tę rutynę. Na szczęście dla Ciebie nie jestem tym w ogóle zain-
teresowany. Zamiast tego chcę wiedzieć, czym się różni moje konto od Twojego.
W końcu moje konto nazywa się Barry Burd, działalność gospodarcza jako Burd
Brain Consulting, a Twoje konto nazywa się na przykład Janusz Czytelnik, działalność
jako Janusz Ekspert Java. Na moim koncie saldo wynosi 24,02 USD. A na Twoim?
Gdy już się nad tym zastanowić, to dojdziemy do wniosku, że różnice między
jednym a drugim kontem można opisać jako wartości zmiennych. Może istnieje
zmienna o nazwie balance (saldo). Moja wartość zmiennej balance wynosi 24.02,
natomiast Twoja wartość tej zmiennej to 55.63. I pytanie brzmi: jak pisząc pro-
gram komputerowy do obsługi kont, mogę oddzielić moją zmienną balance od
Twojej zmiennej balance?
RYSUNEK 7.1.
Dwa obiekty
Jak na razie dobrze nam idzie. Jednak nadal nie rozwiązaliśmy pierwotnego pro-
blemu. Jak w swoim programie odniesiesz się do mojej zmiennej balance, a w jaki
sposób do swojej zmiennej balance? Cóż, masz już dwa obiekty, więc możesz mieć
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
też zmienne, które będą odwoływały się do tych dwóch obiektów. Utwórz jedną
zmienną o nazwie myAccount i drugą zmienną o nazwie yourAccount. Zmienna
myAccount związana jest z moim obiektem (moją instancją klasy Account) wraz ze
wszystkimi znajdującymi się w nim elementami. Aby odnieść się do mojej zmiennej
balance, napisz:
myAccount.balance
myAccount.name
myAccount.balance = 24.02;
out.println(yourAccount.name);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Deklaracje pól przedstawione na listingu 7.1 mają domyślny poziom dostępu, co
oznacza, że przed nazwą typu String nie dodałem żadnego innego słowa. Można
też skorzystać z innych poza domyślnym rodzajów dostępu, takich jak dostęp pu-
bliczny (public), chroniony (protected) oraz prywatny (private):
Później tego dnia starsza kobieta siedziała obok mnie na ławce w parku. Powie-
działa: „Konto ma swoją nazwę, adres i saldo”. Odpowiedziałem: „W porządku,
ale co ja mam z tym zrobić?”. Ona jednak wpatrywała się tylko we mnie, więc nic
nie zrobiłem z informacjami o jej koncie. Posiedzieliśmy sobie oboje i nie zrobi-
liśmy absolutnie nic.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public static void main(String args[]) {
Account myAccount;
Account yourAccount;
out.print(myAccount.name);
out.print(" (");
out.print(myAccount.address);
out.print(") ma ");
out.print(myAccount.balance);
out.println(" zł");
out.print(yourAccount.name);
out.print(" (");
out.print(yourAccount.address);
out.print(") ma ");
out.print(yourAccount.balance);
out.print(" zł");
}
}
W pewnym sensie pierwsze dwa wiersze metody main z listingu 7.2 są mylące.
Niektórzy ludzie czytają wiersz Account yourAccount tak, jakby oznaczał on, że
zmienna „yourAccount ma typ Account” lub że „zmienna yourAccount przecho-
wuje instancję klasy Account”. Tak naprawdę ten wiersz kodu oznacza coś innego.
Okazuje się, że zapis Account yourAccount oznacza: „Jeśli kiedyś zmienna yourAccount
będzie na coś wskazywać, to coś będzie instancją klasy Account”. Na czym zatem
polega różnica?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Technicznie rzecz biorąc, gdy język Java wykonuje instrukcję new Account(),
tworzony jest obiekt poprzez wywołanie konstruktora klasy Account. Więcej na ten
temat opowiem w rozdziale 9.
RYSUNEK 7.2.
Sytuacja przed i po
wywołaniu
konstruktora
Account myAccount;
Account yourAccount;
out.println(yourAccount.name);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeśli zmienna jest typu referencyjnego, to nie wystarczy tylko ją zadeklarować.
Obiekt nie powstanie, dopóki nie odwołamy się do konstruktora klasy i nie uży-
jemy słowa kluczowego new.
Inicjowanie zmiennej
W rozdziale 4. informuję, że zmienną typu pierwotnego można zainicjować w ra-
mach jej deklaracji.
RYSUNEK 7.3.
Uruchamianie kodu
programu
pokazanego na
listingach 7.1 i 7.2
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
klas. Oczywiście dwie klasy to nie to samo co tysiące klas, ale jest to już pierwszy
krok we właściwym kierunku.
Klasy publiczne
Pierwszy wiersz kodu z listingu 7.1 to:
Klasa Account jest klasą publiczną, co oznacza, że jest ona dostępna dla wszystkich
innych klas. Na przykład, jeśli w jakimś odległym zakątku cyberprzestrzeni na-
piszesz program ATMController, to ten program może zawierać instrukcję taką
jak myAccount.balance = 24.02 i korzystać z klasy Account zadeklarowanej na
listingu 7.1. (Oczywiście Twój program musi wiedzieć, gdzie w cyberprzestrzeni
zapisałem kod z listingu 7.1, ale to już całkiem inna historia).
Małym sekretem kodu z tego rozdziału jest to, że deklarowanie pewnych klas
jako publicznych sprawia, iż dobrze się czuję. Tak, programiści robią pewne
rzeczy, aby czuć się dobrze. Na listingu 7.1 moje dobre samopoczucie wynika z fak-
tu, że wielu innych programistów będzie mogło skorzystać z klasy Account. Kiedy
tworzę klasę, która odwzorowuje coś użytecznego, co można ładnie nazwać — na
przykład Account , Engine, Customer, BrainWave, Headache — lub klasę SevenLayerCake,
to deklaruję taką klasę jako publiczną.
Klasa UseAccount z listingu 7.2 również jest klasą publiczną. Jeżeli klasa zawiera
metodę main, to programiści Javy definiują ją jako publiczną, nie zastanawiając
się zbytnio nad tym, kto będzie jej używał. Nawet jeśli żadna inna klasa nie ko-
rzysta z mojej metody main, deklaruję klasę UseAccount jako publiczną. Większość
klas w tej książce zawiera metodę main, dlatego też zostały one zdefiniowane jako
klasy publiczne.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Kiedy deklarujesz klasę jako publiczną, musisz zadeklarować ją w pliku o takiej
samej nazwie jak nazwa klasy (ale z rozszerzeniem .java). Na przykład, jeśli za-
deklarujesz klasę publiczną public class MyImportantCode, musisz umieścić kod
tej klasy w pliku o nazwie MyImportantCode.java z dużymi literami M, I i C oraz ze
wszystkimi małymi literami. Ta reguła nazewnictwa plików ma ważne konse-
kwencje: jeśli Twój kod deklaruje dwie publiczne klasy, będzie musiał składać się
z co najmniej dwóch plików .java. Innymi słowy, nie można zadeklarować dwóch
klas publicznych w jednym pliku .java.
Więcej informacji na temat słowa public i innych słów tego rodzaju można zna-
leźć w rozdziale 14.
Organizacja ma nazwę (taką jak firma XYZ), roczny przychód (np. 100 000,00 zł)
i wartość typu boolean wskazującą, czy organizacja jest organizacją nastawioną
na zysk, czy też nie. Firmy produkujące i sprzedające jakieś produkty są na ogół
organizacjami nastawionymi na zysk; natomiast grupy, które udzielają na przykład
pomocy ofiarom klęsk żywiołowych, zazwyczaj nie są organizacjami skupiającymi
się na zarabianiu pieniędzy i na zysku.
Zadeklaruj własną klasę takiej organizacji Organization. Zadeklaruj także inną
klasę, która utworzy kilka organizacji i wyświetli informacje na ich temat.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W tabeli 7.1 każde konto ma trzy elementy — nazwę, adres i saldo. Tak to wyglą-
dało, zanim pojawiło się programowanie obiektowe. Ale programowanie zorien-
towane obiektowo wiązało się z dużą zmianą myślenia. W tego rodzaju oprogra-
mowaniu każde konto może mieć nazwę, adres, saldo i sposób ich wyświetlania.
No i dlaczego niby jest to taki dobry pomysł? Jest tak dobry, ponieważ powoduje
to, że dane są za siebie odpowiedzialne. Dzięki programowaniu zorientowanemu
obiektowo wszystkie funkcje powiązane z kontem są gromadzone wewnątrz
klasy Account. Wszystko, co musisz wiedzieć o ciągach znaków, znajduje się w pliku
String.java. Natomiast wszystko, co ma związek z liczbą lat (na przykład z dwie-
ma lub czterema cyframi), jest obsługiwane bezpośrednio w klasie Year. Dlatego
jeśli ktoś ma problemy z klasą Account lub z klasą Year, wie dokładnie, gdzie szukać
całego kodu. To wspaniale!
Wyobraź sobie ulepszoną wersję tabeli kont. W tej nowej tabeli każdy obiekt ma
wbudowaną funkcję, dzięki której każde konto wie, jak wyświetlić na ekranie
swoje dane. Każdy wiersz z tej tabeli ma własną kopię metody wyświetlającej display.
Oczywiście nie potrzeba wielkiej wyobraźni, aby zobrazować sobie taką tablicę
z danymi. Tak się składa, że mam już gotową tablicę, na którą możesz spojrzeć.
Tak, to jest tabela 7.2.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 7.3. Konto wyświetla się samo
import static java.lang.System.out;
myAccount.display();
System.out.println();
yourAccount.display();
}
}
Kod z listingów 7.3 i 7.4 po uruchomieniu działa dokładnie tak samo jak kod z li-
stingów 7.1 i 7.2. Wynik jego działania jest taki sam jak na znanym już rysunku 7.3.
Na listingu 7.3 klasa Account zawiera cztery elementy: nazwę, adres, saldo i metodę
wyświetlającą. Te elementy pasują do czterech kolumn pokazanych w tabeli 7.2.
Zatem każda instancja klasy Account ma nazwę, adres, saldo i metodę wyświetlania.
Sposób nazywania tych elementów jest przyjemnie jednolity. Aby odwołać się do
nazwy zapisanej w koncie myAccount, piszesz:
myAccount.name
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Natomiast aby wyświetlić na ekranie dane z konta myAccount, piszesz:
myAccount.display()
Słowo public służy mniej więcej temu samemu celowi, co słowo public
z listingu 7.1. Ogólnie rzecz biorąc, każdy kod może zawierać wywołanie metody
publicznej, nawet jeśli kod wywołujący i metoda publiczna należą do dwóch
różnych klas. W przykładzie z tego podrozdziału decyzja o upublicznieniu metody
display pozostaje już kwestią gustu. Zazwyczaj, kiedy tworzę metodę, która
będzie przydatna w wielu różnych aplikacjach, deklaruję, że ta metoda jest
publiczna (public).
Słowo void mówi językowi Java, że po wywołaniu metody display nie zwróci
ona niczego do kodu wywołującego. Aby zobaczyć metodę, która zwraca coś
do kodu wywołującego, zajrzyj do następnego podrozdziału.
Słowo display to nazwa metody. Każda metoda musi mieć nazwę. W przeciwnym
razie nie będziesz mógł jej wywołać.
Listing 7.3 zawiera deklarację metody display, natomiast na listingu 7.4 zostało
pokazane wywołanie tej metody. Chociaż listingi 7.3 i 7.4 zawierają różne klasy,
to w obu przypadkach wykorzystanie słowa public na listingu 7.3 jest opcjonal-
ne. Aby się dowiedzieć, dlaczego tak jest, zajrzyj do rozdziału 14.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wysyłanie wartości do i z metod
(obliczanie odsetek)
Pomyśl o wysłaniu kogoś do supermarketu, aby kupił chleb. Gdy to robisz, mówisz:
„Idź do supermarketu i kup trochę chleba”. (Wypróbuj to w domu. W mgnieniu
oka będziesz mieć świeży bochenek chleba!) Oczywiście, innym razem wysyłasz
tę samą osobę do supermarketu, aby kupiła banany. I mówisz: „Idź do super-
marketu i kup trochę bananów”. I po co to wszystko? Cóż, masz metodę i masz
pewne zmieniające się informacje, które przekazujesz do metody podczas jej
wywołania. Metoda nosi nazwę goToTheSupermarketAndBuySome (idź do super-
marketu i kup trochę). Zmienne informacje to w zależności od Twoich potrzeb
kulinarnych: bread (chleb) lub bananas (banany). W języku Java wywołania tych
metod wyglądałyby tak:
goToTheSupermarketAndBuySome(bread);
goToTheSupermarketAndBuySome(bananas);
A co się stanie, gdy Twój przyjaciel wróci z supermarketu? Powie: „Oto chleb,
który miałem kupić”. Spełniając Twoje życzenie, przyjaciel coś Ci zwraca. Wy-
konujesz wywołanie metody, a metoda zwraca informacje (lub w tym przypadku
bochenek chleba).
Taki zwrócony element nazywa się wartością zwracaną przez metodę. Ogólny typ
zwracanej rzeczy nazywany jest typem zwracanym metody. Bardziej konkretnie
przedstawiam to na listingach 7.5 i 7.6.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
out.print(balance);
out.print(" zł");
}
myAccount.display();
yourAccount.display();
RYSUNEK 7.4.
Wynik działania kodu
programu
pokazanego na
listingach 7.5 i 7.6
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W pierwszym wywołaniu saldo wynosi 24,02, a stopa procentowa wynosi
5,00. Pierwsze wywołanie metody myAccount.getInterest(5.00) odnosi się
do obiektu myAccount i wartości przechowywanych w polach obiektu myAccount
(patrz rysunek 7.5). Po wywołaniu tej metody wyrażenie balance * percentageRate /
100.00 oznacza wartość 24.02 * 5.00 / 100.00.
Nawiasem mówiąc, metoda main na listingu 7.6 zawiera dwa wywołania getInterest.
W pierwszym wywołaniu jako parametr przekazywany jest literał 5.00, nato-
miast w drugim wywołaniu w parametrze przekazywana jest zmienna yourInterest
Rate. Dlaczego jedno wywołanie używa literału, a drugie wykorzystuje zmien-
ną? Bez powodu. Chcę tylko pokazać, że możesz to zrobić w dowolny sposób.
RYSUNEK 7.5.
Moje konto
i Twoje konto
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 7.6.
Przekazywanie
wartości do metody
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
To samo dotyczy też drugiego wywołania metody getInterest. W dolnej części
listingu 7.6 wywołuję metodę getInterest i przekazuję do jej listy parametrów
zmienną yourInterestRate. Na całe szczęście zaledwie kilka wierszy wcześniej
zadeklarowałem, że zmienna yourInterestRate będzie miała typ double.
RYSUNEK 7.7.
Kolejność
wykonywania
programu z listingów
7.5 i 7.6
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zwracanie wartości z metody getInterest
Gdy wywoływana jest metoda getInterest, wykonuje ona jedyną instrukcję za-
wartą w swoim ciele: instrukcję return. Instrukcja return oblicza wartość wyrażenia
balance * percentageRate / 100.00. Jeśli saldo balance wynosi 24.02, a stopa
procentowa percentageRate wynosi 5.00, wartość wyrażenia wyniesie 1.201 — około
1,20 zł. (Ponieważ komputer posługuje się wyłącznie zerami i jedynkami, Java
oblicza tę wartość z minimalnym błędem. Jako wynik obliczeń otrzymamy wartość
1,2009999999999998. To jest coś, z czym musimy żyć).
W każdym razie po obliczeniu tej wartości Java wykonuje instrukcję return, która
zwraca wartość do miejsca w metodzie main, gdzie wywołano metodę getInterest.
W tym momencie wywołanie metody myAccount.getInterest(5.00) zmienia się
w wartość 1,2009999999999998. Samo wywołanie znajduje się wewnątrz in-
strukcji println:
out.println(myAccount.getInterest(5.00));
out.println(1.2009999999999998);
RYSUNEK 7.8.
Wywołanie metody
jest wyrażeniem
o pewnej wartości
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeśli metoda cokolwiek zwraca, to jej wywołanie będzie wyrażeniem o pewnej
wartości. Tę wartość można wypisać, przypisać do zmiennej, dodać do czegoś
innego lub wykonać dowolne inne działanie. Wywołanie metody możesz wyko-
rzystać tak samo jak i dowolny inny rodzaj wartości.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
prowadza niewielkie kwoty z milionów kont. Ludzie nie zauważają swoich nie-
wielkich strat, ale osoba realizująca ten przekręt zbiera wystarczająco dużo pie-
niędzy, aby szybko uciec na Barbados (z całym ładunkiem salami).
myAccount.balance = 24.02;
yourAccount.balance = 55.63;
RYSUNEK 7.9.
Liczby, które
wyglądają jak kwoty
podane w złotówkach
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Niedokładne liczby na rysunku 7.4 są wynikiem używania przez komputer wy-
łącznie zer i jedynek. Teoretyczny komputer, którego obwody umożliwiałyby
korzystanie z cyfr 0, 1, 2, 3, 4, 5, 6, 7, 8 i 9, nie cierpiałby z powodu tych samych
nieścisłości. Aby poprawić tę sytuację, język Java udostępnia specjalny sposób wy-
konywania obliczeń wolny od opisanych niedokładności samego komputera. In-
terfejs API Java definiuje klasę o nazwie BigDecimal — klasę, która pomija dziwne
komputerowe zera i jedynki, a zamiast nich do wykonywania obliczeń arytme-
tycznych używa zwykłych cyfr dziesiętnych. Więcej informacji na ten temat
można znaleźć na stronie internetowej tej książki (https://users.drew.edu/bburd/
JavaForDummies/).
Kod programu z listingu 7.7 używa przydatnej metody o nazwie printf. W wy-
wołaniu tej metody zawsze umieszczamy w nawiasach co najmniej dwa para-
metry:
Spójrz, proszę, na ostatnie wywołanie metody printf na listingu 7.7. Ciąg formatu-
jący z pierwszego parametru ma dwa symbole zastępcze dla liczb. Pierwszy symbol
zastępczy (%.2f) opisuje sposób wyświetlania zmiennej myInterest, natomiast
drugi symbol zastępczy (kolejne %.2f) opisuje sposób wyświetlania zmiennej your
Interest. Aby dowiedzieć się dokładniej, jak działają te ciągi formatujące, po-
patrz na rysunki od 7.10 do 7.14.
RYSUNEK 7.10.
Jak używać ciągu
formatującego
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 7.11.
Dodatkowe znaki na
wyświetlanie wartości
RYSUNEK 7.12.
Wyświetlanie wartości
bez określania
dokładnej liczby
znaków
RYSUNEK 7.13.
Określanie zbyt małej
liczby znaków, aby
wyświetlić wartość
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 7.14.
Wyświetlanie więcej
niż jednej wartości za
pomocą ciągu
formatującego
Oto „nieprogram” języka Java. Nie jest to prawdziwy program w języku Java, po-
nieważ ukryłem niektóre znaki w kodzie. Zastąpiłem je znakami podkreślenia (_):
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
out.printf("%s%_%s", ">>", 'x', "<<\n");
}
}
Zastąp znaki podkreślenia tak, aby program generował następujące dane wyj-
ściowe:
>>7<<
>> 7<<
>>7 <<
>>0000000007<<
>>+7<<
>>-7<<
>>(7)<<
>> 7.00000<<
>>CZEŚĆ<<
>>x<<
>>X<<
Ukrywanie szczegółów
za pomocą metod dostępu
Odłóż tę książkę i włóż na głowę kapelusz. Byłeś tak lojalnym czytelnikiem, że
zabieram Cię teraz na lunch!
Mam tylko jeden problem. Trochę brakuje mi gotówki. Czy w drodze na obiad
zatrzymalibyśmy się przy bankomacie i wzięlibyśmy kilka złotych? Ponadto mu-
simy skorzystać z Twojego konta. Moje konto jest trochę puste.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Dobre programowanie
Jeśli chodzi o dobre praktyki programowania komputerowego, jedno słowo wyróż-
nia się na tle innych: prostota. Kiedy piszesz skomplikowany kod, ostatnią rzeczą,
jakiej Ci potrzeba, są cudze zmienne o błędnych nazwach, nieczytelne rozwiąza-
nia problemów lub sprytne, ale dodawane w ostatniej minucie poprawki. Chcesz
czystego interfejsu, który pozwoli Ci rozwiązywać własne problemy, a nie pro-
blemy kogoś innego.
ale w ten sposób znalazłbyś się zbyt głęboko w strukturze wewnętrznej klasy Account.
W końcu ludzie używający bankomatu nie mogą programować zmiennych ma-
szyny. Nie mogą użyć klawiatury bankomatu, żeby wpisać instrukcję:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zamiast tego naciskają przyciski, które wykonują swoje zadania w uporządko-
wany sposób. I to właśnie w ten sposób programista może uzyskać bezpieczeństwo
i prostotę.
Aby uporządkować klasę Account z listingu 7.1, musimy zmienić ją, zakazując sto-
sowania takich instrukcji jak poniższa:
oraz ta:
out.print(yourAccount.balance);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 7.9. Wywołanie metod dostępu
import static java.lang.System.out;
myAccount.setName("Barry Burd");
myAccount.setAddress("222 Cyberspace Lane");
myAccount.setBalance(24.02);
yourAccount.setName("Janusz Czytelnik");
yourAccount.setAddress("ul. Kliencka 111");
yourAccount.setBalance(55.63);
out.print(myAccount.getName());
out.print(" (");
out.print(myAccount.getAddress());
out.print(") ma ");
out.print(myAccount.getBalance());
out.println(" zł");
out.print(yourAccount.getName());
out.print(" (");
out.print(yourAccount.getAddress());
out.print(") ma ");
out.print(yourAccount.getBalance());
out.print(" zł");
}
}
Wynik działania programu z listingów 7.8 i 7.9 w ogóle nie różni się od wyniku
działania programu z listingów 7.1 i 7.2. Efekt pracy obu tych programów za-
prezentowałem już wcześniej na rysunku 7.3. Duża różnica polega na tym, że na
listingu 7.8 klasa Account starannie wymusza kontrolowane użycie swoich pól
name, address i balance.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Programista tworzący klasę UseAccount nie może odwołać się do pola myAccount.name.
Zamiast tego musi wywołać metodę myAccount.setName lub metodę myAccount.
getName. Te metody są nazywane metodami dostępowymi, ponieważ zapewniają
dostęp do pola name klasy Account. (Właściwie termin metoda dostępowa nie jest
formalnie częścią języka programowania Java. Jest to termin, którego ludzie uży-
wają w przypadku metod robiących tego typu rzeczy). Aby jeszcze bardziej zgłę-
bić ten temat, powiem, że setName jest nazywana metodą ustalającą (ang. setter
method), a getName nazywa się metodą pobierającą (ang. getter method). Założę się,
że nie zapomnisz tej terminologii!
W przypadku wielu IDE nie musimy wpisywać własnych metod dostępu. Naj-
pierw wpisujemy deklarację pola, taką jak private String name. Następnie na pa-
sku menu IDE wybieramy pozycję: Source\Generate Getters and Setters lub pozycję
Code\Insert Code\Setter albo jakiś inny zestaw poleceń tego typu. Po dokonaniu
tych wszystkich wyborów IDE utworzy metody dostępu i doda je do kodu.
W metodach ustalających i pobierających nic nie jest święte. Wcale nie musisz
pisać tych metod, których nie masz zamiaru używać. Na przykład na listingu 7.8
mogę pominąć deklarację metody getAddress i nadal wszystko będzie działać.
Problem polega na tym, że w tej sytuacji nikt, kto chciałby użyć mojej klasy Account,
nie będzie mógł uzyskać z niej adresu istniejącego konta.
Kiedy tworzysz metodę służącą do ustalania wartości w polu balance, nie musisz
nazywać swojej metody setBalance. Możesz nazwać ją tunaFish lub jakkolwiek
inaczej. Problem polega na tym, że konwencja setNazwaPola (z małymi literami
set na początku i wielką literą rozpoczynającą część NazwaPola) jest uznaną kon-
wencją stylistyczną w światku programistów języka Java. Nie przestrzegając tej
konwencji, wprowadzasz zamieszanie innym programistom. Jeśli zintegrowane
środowisko programistyczne ma możliwość projektowania interfejsu graficznego
metodą przeciągnij i upuść, to może się okazać, że nie będziesz mógł skorzystać
z tej funkcji. (Aby dowiedzieć się więcej o projektowaniu GUI metodą przeciągnij
i upuść, zajrzyj do rozdziałów 2. i 16.).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Podczas wywołania metody ustalającej podajesz jej wartość typu zgodnego z typem
pola. Dlatego na listingu 7.9 wywołujemy metodę yourAccount.setBalance(55.63)
z parametrem typu double. Z drugiej strony, wywołując metodę pobierającą, za-
zwyczaj nie podajesz jej żadnych wartości. Dlatego na listingu 7.9 wywołujemy
metodę yourAccount.getBalance() z pustą listą parametrów. Czasami możesz
chcieć pobrać i ustalić wartość pola za pomocą pojedynczej instrukcji. Aby dodać
złotówkę do aktualnego salda konta (balance), napisz instrukcję yourAccount.
setBalance(yourAccount.getBalance() + 1,00).
myAccount.name = "";
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Własna klasa GUI Barry’ego
Być może nudzą Cię już nieciekawe programy tekstowe, które zaśmiecają strony
tej książki. Możesz chcieć czegoś choć trochę ciekawszego — czegoś z polami
tekstowymi i przyciskami. Mam dla Ciebie kilka takich przykładów!
RYSUNEK 7.15.
Kod z listingu 7.10
zaczyna pracę
RYSUNEK 7.16.
Użytkownik
wypełnił pole
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 7.17.
Użytkownik nacisnął
przycisk
Pierwszy wiesz
import com.allmycode.dummiesframe.DummiesFrame;
sprawia, że nazwa DummiesFrame jest dostępna dla reszty kodu na listingu.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Metoda może zwrócić dowolny typ Javy: String, int, double lub cokolwiek
innego (właściwie to nie jest żadna reguła, tylko okazja!).
Metoda calculate musi mieć tyle samo parametrów, ile jest wierszy w oknie
aplikacji.
Na listingu 7.10 znajduje się tylko jedno wywołanie metody addRow, więc okno
przedstawione na rysunku 7.15 ma tylko jeden wiersz (nie dotyczy to przycisku
Wyślij), a zatem metoda calculate ma tylko jeden parametr.
W momencie, gdy użytkownik zaczyna wpisywać tekst w polu tekstowym okna,
czerwony znak X zmienia się w zielony znacznik (patrz rysunek 7.16). Taki zielony
znacznik wskazuje, że użytkownik wprowadził już do pola tekstowego wartość
oczekiwanego typu (w tym przykładzie wartość typu String).
Używając mojej klasy DummiesFrame, możesz zbudować prostą aplikację GUI z zaled-
wie dziesięcioma wierszami kodu.
Klasa DummiesFrame nie jest częścią API Javy, więc abyś mógł uruchomić kod z li-
stingu 7.10, mój plik DummiesFrame.java musi być częścią Twojego projektu. Po
pobraniu kodu programu z serwera FTP wydawnictwa (ftp://ftp.helion.pl/przyklady/
javby7.zip) znajdziesz folder o nazwie 07-10 zawierający zarówno kod z listingu
7.10, jak i kod w pliku DummiesFrame.java. Natomiast jeśli utworzysz własny
projekt zawierający kod z listingu 7.10, musisz ręcznie dodać do niego mój plik
DummiesFrame.java. Sposób, w jaki to zrobisz, zależy od używanego przez Ciebie
środowiska IDE. Tak czy inaczej, moja klasa DummiesFrame znajduje się w pakiecie
o nazwie com.allmycode.dummiesframe, więc plik DummiesFrame.java musi znaj-
dować się w katalogu o nazwie dummiesframe, który znajduje się w innym kata-
logu o nazwie allmycode, który będzie jeszcze w innym katalogu o nazwie com.
Więcej informacji na temat pakietów znajduje się w rozdziałach 9. i 14.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
pliku, który grupuje wiele mniejszych plików .class. Nazywa się on plikiem typu
JAR i ma rozszerzenie .jar. W prawdziwej aplikacji, jeśli przygotowujesz swój
własny kod, aby inni ludzie mogli go wykorzystywać jako część własnych apli-
kacji, plik typu JAR jest zdecydowanie najlepszym rozwiązaniem.
Nasze okno programu, przedstawione na rysunku 7.18, będzie miało dwa wier-
sze, ponieważ kod z listingu 7.11 zawiera dwa wywołania metody addRow, a me-
toda calculate ma dwa parametry. Ponadto na listingu 7.11 wywoływana jest
metoda setButtonText obiektu frame. Dzięki temu na rysunku 7.18 tekst na przy-
cisku nie będzie już domyślnym słowem Submit.
RYSUNEK 7.18.
Popatrz! Kod
z listingu 7.11
faktycznie działa!
Listing 7.12 zawiera wersję GUI gry w zgadywanki pochodzącej z rozdziału 5.,
natomiast rysunek 7.19 przedstawia taką grę w akcji.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 7.12. Myślę o pewnej liczbie
import java.util.Random;
import com.allmycode.dummiesframe.DummiesFrame;
if (inputNumber == randomNumber) {
return "Wygrałeś.";
} else {
return "Przegrałeś. Liczba losowa wynosiła " + randomNumber + ".";
}
}
}
RYSUNEK 7.19.
Wygrałem
Na listingu 7.13 używam klasy Account z tego rozdziału razem z klasą DummiesFrame.
Takie same wyniki mogę uzyskać bez tworzenia instancji klasy Account, ale chcę
tu pokazać, jak klasy mogą ze sobą współpracować, tworząc kompletny program.
Wynik działania tego kodu znajduje się na rysunku 7.20.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public static String calculate(String name, String address,
double balance) {
myAccount.setName(name);
myAccount.setAddress(address);
myAccount.setBalance(balance);
return myAccount.getName() + " (" + myAccount.getAddress() +
") ma " + myAccount.getBalance() + " zł";
}
}
RYSUNEK 7.20.
Jestem bogaty
Okno powinno zawierać pola tekstowe dla typu żywności, wagi, ceny, liczby porcji
i liczby kalorii przypadających na porcję. Gdy użytkownik kliknie przycisk, w oknie
zostanie wyświetlona cena za 100 gramów produktu, cena przypadająca na porcję
i całkowita liczba kalorii w tym produkcie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
206 CZĘŚĆ III Praca w szerszej perspektywie — programowanie obiektowe
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Poprawianie kodu
Rozdział 8
Oszczędność
czasu i pieniędzy
— ponowne użycie
istniejącego kodu
D
awno, dawno temu żyła sobie piękna księżniczka. Kiedy księżniczka skoń-
czyła 25 lat (optymalny wiek sił witalnych, dobrego wyglądu i doskonałego
charakteru moralnego), jej miły, stary ojciec przyniósł jej prezent w uroczym
złotym pudełku. Pragnąc dowiedzieć się, co jest w pudełku, księżniczka zerwała
złoty papier ozdobny.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Nawet w miarę upływu czasu program komputerowy nigdy nie zawiódł. Przez lata
księżniczka zmieniała swoje potrzeby i oczekiwała teraz więcej od życia, stawiała
coraz większe wymagania, poszerzała swoją karierę, sięgała po coraz więcej za-
chcianek, żonglowała pragnieniami męża i dzieci, rozciągała budżet i szukała spo-
koju dla swojej duszy. Przez cały ten czas program pozostał jej stałym i wiernym
towarzyszem.
Kiedy księżniczka się zestarzała, wraz z nią zestarzał się też program. Pewnego
wieczoru, gdy siedziała przy domu, zadała programowi trudne pytanie: „Jak to
robisz?”. Zapytała: „Jak udaje ci się ciągle udzielać właściwych odpowiedzi, za
każdym razem, rok po roku?”.
Definiowanie klasy
(co oznacza bycie pracownikiem)
Czy nie byłoby miło, gdyby każdy program robił dokładnie to, czego byśmy sobie
życzyli? W idealnym świecie mógłbyś kupić program, od razu go uruchomić,
bezproblemowo używać go w nowych sytuacjach i łatwo aktualizować, gdy tylko
zmieniłyby się Twoje potrzeby. Niestety tego rodzaju oprogramowanie nie ist-
nieje. (Nic takiego nie istnieje). Prawda jest taka, że bez względu na to, co chcesz
zrobić, z pewnością znajdziesz oprogramowanie, które będzie realizować niektóre
z Twoich potrzeb, ale na pewno nie wszystkie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
funkcji, dodawać nowe funkcje i zastępować te, które nam nie odpowiadają.
A najlepsze jest to, że wprowadzanie takich zmian jest całkowicie czyste. Bez szar-
pania się i zagłębiania w kruchy kod innych ludzi. Zamiast tego tworzymy ładne,
uporządkowane dodatki i modyfikacje bez dotykania wewnętrznej logiki istnieją-
cego już kodu. To idealne rozwiązanie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zgodnie z listingiem 8.1 każdy pracownik ma siedem elementów. Dwa z nich są
dość proste: każdy pracownik ma nazwisko i stanowisko (na listingu 8.1 klasa
Employee ma pole name i pole jobTitle).
anEmployee.setName(aScanner.nextLine());
anEmployee.setJobTitle(aScanner.nextLine());
anEmployee.cutCheck(aScanner.nextDouble());
aScanner.nextLine();
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
GDZIE MIESZKASZ NA TEJ ZIEMI?
Separatory grupujące różnią się od siebie w zależności od kraju. Ma to duże zna-
czenie przy próbie odczytania wartości typu double za pomocą klasy Scanner. Aby
zobaczyć, co mam na myśli, proszę uważnie prześledzić poniższą sesję JShell.
jshell> import java.util.Scanner
jshell> keyboard.nextDouble()
1000.00
$4 ==> 1000.0
jshell> Locale.setDefault(Locale.FRANCE)
jshell> keyboard.nextDouble()
1000,00
$7 ==> 1000.0
jshell> keyboard.nextDouble()
1000.00
| java.util.InputMismatchException thrown:
| at Scanner.throwFor (Scanner.java:860)
| at Scanner.next (Scanner.java:1497)
| at Scanner.nextDouble (Scanner.java:2467)
| at (#8:1)
jshell>
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Domyślnie instancja klasy Scanner na Twoim komputerze chce, abyś wprowadził
liczby typu double w sposób, w jaki zwykle je wpisujesz w swoim kraju. Jeśli wpiszesz
liczby zgodnie z konwencją innego kraju, otrzymasz wyjątek InputMismatchException.
Tak więc kiedy uruchomisz kod na listingu 8.2, liczby w pliku EmployeeInfo.txt będą
musiały używać formatu Twojego kraju.
I tak na koniec, jeśli chcesz, aby dane wyjściowe wyglądały poprawnie w Twoim
kraju, możesz to zrobić za pomocą klasy Formatter, dostępnej w języku Java. Do
swojego kodu dodaj taką instrukcję:
out.print(
new java.util.Formatter().format(java.util.Locale.FRANCE, "%,.2f", 1000.00));
Wszystkie szczegóły na ten temat znajdziesz w dokumentacji interfejsu API (Application
Programming Interface) języka Java dla klasy Formatter, dostępnej pod adresem inter-
netowym https://docs.oracle.com/javase/8/docs/api/java/util/Formatter.html, a dla klasy
Locale pod adresem https://docs.oracle.com/javase/8/docs/api/java/util/Locale.html.
Jeśli chcesz uruchomić kod z listingu 8.2, na Twoim dysku powinien znajdować
się plik o nazwie EmployeeInfo.txt. Na szczęście pakiet, który pobrałeś z serwera
ftp (ftp://ftp.helion.pl/przyklady/javby7.zip), zawiera już plik EmployeeInfo.txt. Po-
brany materiał możesz zaimportować do dowolnego z trzech najpopularniejszych
środowisk IDE Java (Eclipse, NetBeans lub IntelliJ IDEA). Jeśli zaimportujesz do
środowiska Eclipse, uzyskasz projekt o nazwie 08-01. Projekt ten zazwyczaj
znajduje się na dysku twardym, w folderze /Użytkownicy/twoja-nazwa-użytkownika/
workspace/08-01. To właśnie w tym folderze znajdziesz też plik o nazwie Employee-
Info.txt.
Klasa DoPayroll z listingu 8.2 ma dwie metody. Jedną z nich jest metoda main,
która trzykrotnie wywołuje inną metodę — payOneEmployee. Za każdym razem
metoda payOneEmployee pobiera elementy z pliku EmployeeInfo.txt i przekazuje je
do metod klasy Employee.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Oto jak zmienna o nazwie anEmployee jest w programie wykorzystywana wielo-
krotnie i poddawana recyklingowi:
RYSUNEK 8.1.
Trzy wywołania
metody
PayOneEmployee
Użyj nowej klasy PlaceToLive, aby utworzyć aplikację GUI. Dodatkowo użyj mojej
klasy DummiesFrame (pochodzącej z rozdziału 7.). Aplikacja GUI powinna pobierać
informacje o adresie mieszkania i wyświetlać cenę tego lokalu w przeliczeniu na metr
kwadratowy (lub na stopę kwadratową) oraz cenę w przeliczeniu na liczbę sypialni.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Przygotowanie wypłaty
W kodzie z listingu 8.1 znajdują się trzy wywołania funkcji printf. Każde wywo-
łanie tej funkcji zawiera ciąg formatujący (taki jak "(%s) ***$") i zmienną (np.
jobTitle). Każdy ciąg formatujący ma symbol zastępczy (np. %s), który określa,
gdzie i jak wyświetlana jest wartość zmiennej.
RYSUNEK 8.2.
Każdy dostaje
wypłatę
Wróć do listingu 8.1 i zwróć uwagę na przecinek znajdujący się w symbolu za-
stępczym %,.2. Ten przecinek mówi programowi, aby używał separatorów grupo-
wania. Dlatego na rysunku 8.2 widać 5 000,00, 7 000,00 i 10 000,00 zamiast
5000.00, 7000.00 i 10000.00.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Większość tego rozdziału dotyczy ponownego użycia kodu. Jednak w kodzie z li-
stingu 8.2 ukrywa się pewna ważna idea, która nie jest bezpośrednio związana
z ponownym wykorzystaniem kodu. Inaczej niż miało to miejsce w przykładach
z poprzednich rozdziałów, kod z tego listingu odczytuje dane z pliku zapisanego
na dysku. W związku z tym w następnych punktach zrobimy małą przerwę, aby
przyjrzeć się plikom na dysku.
RYSUNEK 8.3.
Plik EmployeeInfo.txt
Chciałem jak najbardziej uprościć kod z listingu 8.2, dlatego proszę, wpisz znaki
z rysunku 8.3 i zakończ wpisywanie liczbą 10000.00, a następnie naciśnij na kla-
wiaturze klawisz Enter. (Spójrz ponownie na rysunek 8.3 i zauważ, że kursor znaj-
duje się na początku zupełnie nowego wiersza). Jeśli zapomnisz zakończyć na-
ciśnięciem klawisza Enter, to kod z listingu 8.2 ulegnie awarii podczas próby jego
uruchomienia.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Kopiowanie i wklejanie kodu
W niemal każdym języku programowania komputerowego odczytywanie danych
z pliku może sprawiać kłopoty. Wprowadzasz dodatkowe wiersze kodu, aby po-
wiedzieć komputerowi, co ma robić. Czasami możesz po prostu skopiować
i wkleić kod napisany przez innych ludzi. Na przykład możesz postępować zgodnie
ze wzorem przedstawionym na listingu 8.2:
/*
* Wzór listingu 8.2
*/
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
class NazwaTwojejKlasy {
public static void main(String args[]) throws IOException {
Scanner nazwaSkanera = new Scanner(new File("NazwaPliku"));
// Trochę kodu
nazwaSkanera.nextInt();
nazwaSkanera.nextDouble();
nazwaSkanera.next();
nazwaSkanera.nextLine();
// Trochę kodu
nazwaSkanera.close();
}
}
Dodaj dwie nowe deklaracje importu — jedną dla klasy java.io.File i drugą dla
klasy java.io.IOException.
Weź słowo, którego używasz do nazywania skanera. Użyj tego słowa w wywołaniu
metody close.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Czasami kopiowanie i wklejanie kodu może wpędzić Cię w kłopoty. Być może
chcesz napisać program, który nie będzie pasował do prostego wzorca z listingu
8.2. W takiej sytuacji musisz dopasować ten wzorzec do swoich potrzeb. Ale aby go
dostosować, musisz najpierw zrozumieć choć część idei stojących za tym wzorcem.
Czytanie z pliku
W poprzednich rozdziałach programy odczytują znaki pochodzące z klawiatury
komputera. Programy te używają takich rzeczy jak Scanner, System.in i nextDouble
— elementów zdefiniowanych w API języka Java. Program DoPayroll przedsta-
wiony na listingu 8.2 pokazuje nowe podejście do tych spraw. Zamiast odczyty-
wać znaki z klawiatury, program odczytuje znaki z pliku EmployeeInfo.txt, który
znajduje się na dysku twardym komputera.
Aby odczytać znaki z pliku, należy skorzystać z tych samych elementów, które
pozwalają czytać znaki z klawiatury. W tym celu możesz używać klasy Scanner,
metody nextDouble i innych pożytecznych gadżetów. Ale oprócz tych elementów
mamy jeszcze kilka dodatkowych przeszkód do przeskoczenia. Oto ich lista:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W tym miejscu terminologia czyni z igły widły. Oczywiście używam fraz nowy obiekt
File i nowa instancja klasy File, co tak naprawdę sprowadza się do wywołania new
File("EmployeeInfo.txt") oznaczającego plik na dysku twardym. Potem zostaje już
tylko przekazać wynik tego wywołania do nowo tworzonego obiektu klasy Scanner:
Scanner diskScanner = new Scanner(new File("EmployeeInfo.txt"));
I już możesz zapomnieć o wszystkim, co miało związek z klasą File. Od tego
momentu w Twoim kodzie zmienna diskScanner oznacza nazwę pliku
EmployeeInfo.txt na dysku twardym komputera. (Nazwa diskScanner oznacza plik
na dysku twardym, podobnie jak w poprzednich przykładach nazwa keyboard
oznacza te przyciski, które naciskasz każdego dnia).
Utworzenie nowego obiektu klasy File w kodzie na listingu 8.2 przypomina trochę
tworzenie nowego obiektu klasy Employee znajdujące się w dalszej części tego
samego listingu. Podobne jest też do tworzenia obiektu klasy Account w przykładach
z rozdziału 7. Jedyna różnica polega na tym, że klasy Employee i Account są
zdefiniowane w przykładach z tej książki, natomiast klasa File jest zdefiniowana
w interfejsie API Javy.
Łącząc plik znajdujący się na dysku twardym z nowym obiektem Scanner,
nie zapomnij o wpisaniu słów new File. Jeśli napiszesz new Scanner("Employee
Info.txt") bez new File, to kompilator nie będzie zgłaszał błędów. (Przed
uruchomieniem kodu nie otrzymasz żadnych ostrzeżeń ani komunikatów
o błędach). Ale po uruchomieniu programu nie uzyskasz niczego nawet
zbliżonego do wyników, które spodziewasz się zobaczyć.
Do klasy File musisz odwołać się przez jej pełną nazwę: java.io.File. Możesz
to zrobić za pomocą deklaracji importu, takiej jak ta pokazana na listingu 8.2.
Możesz też zaśmiecać swój kod instrukcjami takimi jak ta:
Scanner diskScanner = new Scanner(new java.io.File("EmployeeInfo.txt"));
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Więcej informacji na temat wyjątków w języku Java znajduje się w rozdziale 13.
W międzyczasie dodawaj klauzulę throws IOException do nagłówka dowolnej
metody, która stosuje wywołanie new Scanner(new File(….
No cóż, istnieje wiele sposobów, aby pozmieniać kod z listingu 8.2. Niektóre
rozwiązania nie potrzebują żadnego parametru. Ale sposób uporządkowania kodu
z tego przykładu sprawia, że mamy dwie oddzielne metody: metodę main i metodę
payOneEmployee. Tworzymy Scanner raz w metodzie main, a następnie używamy go
trzykrotnie — przy każdym wywołaniu metody payOneEmployee.
Wszystko, co zdefiniujemy wewnątrz metody, jest jak jej prywatny żart, znany
wyłącznie kodowi zawartemu w tej metodzie. Zmienna diskScanner zdefiniowana
jest wewnątrz metody main, tym samym nie jest automatycznie dostępna w meto-
dzie payOneEmployee. Aby metoda payOneEmployee mogła skorzystać z pliku na dysku,
przekazujemy do niej zmienną diskScanner podczas wywoływania jej z metody
main.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Kto przeniósł mój plik?
Po pobraniu kodu ze serwera ftp wydawnictwa Helion (ftp://ftp.helion.pl/przyklady/
javby7.zip) znajdziesz pliki o nazwach Employee.java i DoPayroll.java, zawierające
kod z listingów 8.1 i 8.2. Znajdziesz również plik o nazwie EmployeeInfo.txt. To
dobrze, ponieważ gdy kompilator Java nie będzie mógł znaleźć pliku EmployeeInfo.txt,
cały nasz projekt nie będzie działał poprawnie. Dostaniemy tylko wyjątek FileNot
FoundException, mówiący o tym, że nie znaleziono pliku.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Użytkownicy komputerów Macintosh i Linux mogą znaleźć pocieszenie w fakcie,
że ich separator ścieżek nie ma specjalnego znaczenia w ciągu języka Java. Na
komputerze Mac kod new File("/Users/bburd/workspace/08-01/EmployeeInfo.txt")
jest całkowicie poprawny. Jednak użytkownicy komputerów Mac i Linuksa nie
powinni zbyt szybko twierdzić o swojej wyższości. Takie instrukcje jak new
File("/Users/bburd/workspace… działają również w systemie Windows. W syste-
mie Windows możesz użyć prawego ukośnika (/) lub lewego ukośnika (\) jako
separatora nazw katalogów. Przy znaczku zachęty w wierszu poleceń, aby dostać
się do mojego katalogu domowego, mogę wpisać cd c:/users\bburd.
Jeśli już wiesz, gdzie Twój program Java będzie szukał plików, możesz przenieść
się z tego miejsca do wybranego przez siebie katalogu. Załóżmy, że kod z listingu
8.2 będzie szukał pliku EmployeeInfo.txt w katalogu o nazwie 08-01. W ramach
eksperymentu przejdź do katalogu 08-01 i utwórz nowy podkatalog o nazwie
dataFiles. Następnie przenieś plik EmployeeInfo.txt do nowo utworzonego katalogu.
Teraz, aby odczytać liczby i słowa z przeniesionego pliku, zmodyfikuj kod z listingu
8.2, wprowadzając do niego instrukcję new File("dataFiles\\EmployeeInfo.txt")
lub new File("dataFiles/EmployeeInfo.txt").
Zwróć, proszę, uwagę na mój staranny dobór słów: nextLine odczytuje wszystko
„aż do końca aktualnego wiersza”. Niestety, odczytywanie do końca aktualnego
wiersza nie zawsze będzie oznaczało to, co się nam wydaje. Łączenie ze sobą
wywołań takich metod jak nextInt, next Double i nextLine może wprowadzać spory
bałagan. Musisz krok po kroku sprawdzać, co robisz, i dokładnie kontrolować wy-
niki pracy swojego programu.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Teraz popatrz na rysunek 8.4:
RYSUNEK 8.4.
Wywołania metod
next Double
i nextLine
Spójrz ponownie na plik pokazany się na rysunku 8.3. Aby kod z tego przykładu
działał poprawnie, musimy umieścić znak podziału wiersza za ostatnią liczbą
w pliku — 10000,00. Jeśli tego znaku nie będzie, to ostatnie wywołanie nextLine
spowoduje błąd i przerwanie pracy programu. Komunikat o błędzie będzie
brzmiał: NoSuchElementException: No line found (wyjątek: brak takiego elementu:
nie znaleziono wiersza).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
tekst Barry Burd (bez znaku podziału wiersza). Oznacza to, że metoda nextLine
szuka znaku końca wiersza, a następnie go wyrzuca. Tak, to drobny szczegół.
I ten drobny szczegół prawie w ogóle nie sprawia nikomu żadnych problemów.
Jeśli coraz mniej rozumiesz ze sposobów użycia metod nextDouble i nextLine, nie
obwiniaj za to języka Java. Mieszanie wywołań odczytywania danych wejściowych
to delikatna sprawa, niezależnie od używanego języka programowania. A naprawdę
paskudne jest to, że każdy język programowania podchodzi do problemu w nieco
inny sposób. To, czego teraz dowiesz się o metodzie nextLine w języku Java,
w przyszłości może pomóc Ci zrozumieć inne problemy, pojawiające się w innych
językach programowania, takich jak C++ lub Visual Basic. Niestety nie przygotuje
Cię to na wszystkie możliwe niespodzianki. W każdym języku programowania
pojawiają się problemy właściwe dla tego języka. (Tak, to poważny problem. Ale
skoro wszyscy programiści komputerowi stają się kiedyś sławni i bogaci, to znaczy,
że ostatecznie się opłaca).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Definiowanie podklas (co to znaczy być
pracownikiem zatrudnionym w pełnym
lub niepełnym wymiarze godzin)
W zeszłym roku Twoja firma zapłaciła 10 milionów złotych za kupione oprogra-
mowanie. Cały program znajdował się w pliku Employee.class. Ludzie z firmy
Burd Brain Consulting (firmy, która stworzyła ten program) nie chcą, abyś
wiedział, co znajduje się we wnętrzu tego programu. (Chodzi o to, żeby nikt nie
ukradł ich pomysłów). Oznacza to, że nie masz pliku źródłowego, z którego po-
chodzi to oprogramowanie. (Innymi słowy, nie masz Employee.java). Możesz je-
dynie uruchomić kod bajtowy znajdujący się w pliku Employee.class. Możesz także
przeczytać dokumentację na stronie internetowej o nazwie Employee.html. Ale nie
będziesz w stanie zobaczyć instrukcji składających się na program Employee.java
i nie będziesz mógł nic zmienić w kodzie tego programu.
W ciągu roku Twoja firma nieco się rozrosła. Dzisiaj firma ta zatrudnia już dwa
rodzaje pracowników: w pełnym i niepełnym wymiarze godzin. Każdy pracownik
pełnoetatowy otrzymuje stałą, tygodniową pensję. (Jeśli ktoś pracuje w nocy
i w weekendy, to w zamian za ten monumentalny wysiłek otrzymuje solidny
uścisk dłoni). Natomiast każdy pracownik zatrudniony w niepełnym wymiarze
godzin pracuje za stawkę godzinową. Twoja firma potrąca pewną kwotę z każdej
wypłaty pełnoetatowego pracownika, aby pokryć cenę pakietu świadczeń firmy.
Pracownicy zatrudnieni w niepełnym wymiarze godzin nie otrzymują jednak ta-
kich świadczeń.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wyrzuć oprogramowanie o wartości 10 milionów złotych. Wyznacz kogoś
w swojej firmie do napisania tego programu od podstaw.
Innymi słowy, pożegnaj się z czasem i pieniędzmi.
RYSUNEK 8.5.
Drzewo
genealogiczne klasy
Employee
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Tworzenie podklasy
Na listingu 8.1 zdefiniowałem już klasę Employee. Mogę jej teraz użyć i rozszerzyć
jej definicję, tak aby utworzyć nowe, bardziej specjalistyczne klasy. Na listingu
8.3 zdefiniowałem nową klasę: FullTimeEmployee.
W kodzie z listingu 8.3 znajduje się magiczne słowo extends. Gdy jedna klasa
rozszerza istniejącą już klasę, klasa rozszerzająca automatycznie dziedziczy funk-
cjonalność zdefiniowaną w istniejącej już klasie. Oznacza to, że klasa FullTime
Employee dziedziczy pola name i jobTitle. Klasa ta dziedziczy również wszystkie
metody zadeklarowane w klasie Employee: setName, getName, setJobTitle, getJob
Title i cutCheck. Klasa FullTimeEmployee jest podklasą klasy Employee. Można też
powiedzieć, że klasa Employee jest superklasą klasy FullTimeEmployee. Możesz też
stosować terminologię pokrewieństwa: klasa FullTimeEmployee jest dzieckiem klasy
Employee, natomiast klasa Employee jest rodzicem klasy FullTimeEmployee.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wygląda to prawie (choć nie do końca) tak, jakby klasa FullTimeEmployee została
w pełni zdefiniowana przez kod z listingu 8.4.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Dlaczego tytuł listingu 8.4 mówi o fałszywym kodzie? (Czy kod powinien się ob-
razić?) Cóż, główna różnica między kodem z listingu 8.4 a sytuacją dziedziczenia
pokazaną na listingach 8.1 i 8.3 jest następująca: klasa potomna nie może bezpo-
średnio odwoływać się do prywatnych pól swojej klasy nadrzędnej. Aby zrobić coś
z polami prywatnymi klasy nadrzędnej, klasa potomna musi wywołać metody do-
stępowe klasy nadrzędnej. Wracając do listingu 8.3, wywołanie setName("Radek")
byłoby w porządku, ale już instrukcja name="Radek" nie byłaby OK. Jeśli wierzysz
we wszystko, co przeczytałeś na listingu 8.4, możesz odnieść wrażenie, że w klasie
FullTimeEmployee można zastosować instrukcję name="Radek". Niestety, nie można.
(O rany, taki drobniutki szczególik!)
Nie potrzebujesz pliku Employee.java na swoim dysku twardym, aby napisać kod
rozszerzający klasę Employee. Wszystko, czego będziesz potrzebować, to plik
Employee.class.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Korzystanie z podklas
Poprzedni podrozdział został poświęcony sposobom tworzenia podklas. Niestety
podane tam informacje są troszkę niekompletne. Tworzenie podklas jest bardzo
przydatne, ale nic Ci z tego nie przyjdzie, jeżeli nie napiszesz też kodu, który
będzie z tych klas korzystać. W tym podrozdziale omówię zatem kod wykorzy-
stujący podklasy.
Nadszedł czas, abyś zakwalifikował się jako osoba w typie F, osoba typu P lub
osoba typu T. (Jestem autorem tej książki, więc mogę sobie wymyślić dla Ciebie
kilka typów osobowości. Mogę nawet wskazać kogoś publicznie i powiedzieć:
„Patrz! On jest typem T!”).
Osobę typu T inspiruje coś, o czym piszę krótko w rozdziale 7.: osoba typu T
chce przetestować kod w podklasach FullTimeEmployee i PartTimeEmployee.
Testowanie kodu oznacza poddawanie go dokładnej kontroli — sprawdzanie
poprawności danych na wyjściu, gdy dane na wejściu są normalne, gdy dane
na wejściu mają nieoczekiwaną postać, a nawet gdy są one całkowicie nierealne.
Co więcej, osoba typu T chce użyć standardowego, łatwo rozpoznawalnego
schematu w kodzie testowym, tak aby inni programiści mogli szybko zrozumieć
wyniki testów. Osoba typu T tworzy testy JUnit, które używają podklas
FullTimeEmployee i PartTimeEmployee.
Listing 8.6 jest przeznaczony dla całego tłumu osób typu F, dlatego jest niewielki
i prosty oraz świetnie nadaje się do czytania do poduszki.
Jeśli jesteś osobą typu P lub typu T, odwiedź witrynę tej książki (https://users.
drew.edu/bburd/JavaForDummies/). Znajdziesz tam przykłady, które zadowolą czy-
telników z grup typu P i T.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Na listingu 8.6 przedstawiam podstawową wersję programu używającego pod-
klas FullTimeEmployee i PartTimeEmployee. Natomiast na rysunku 8.6 prezentuję
wynik działania tego programu.
ftEmployee.setName("Barry Burd");
ftEmployee.setJobTitle("Prezes");
ftEmployee.setWeeklySalary(5000.00);
ftEmployee.setBenefitDeduction(500.00);
ftEmployee.cutCheck(ftEmployee.findPaymentAmount());
System.out.println();
RYSUNEK 8.6.
Wynik działania kodu
programu
z listingu 8.6
Chcąc dobrze zrozumieć kod z listingu 8.6, musisz przyjrzeć się trzem klasom:
Employee, FullTimeEmployee i PartTimeEmployee. (Jeśli chcesz zobaczyć kod defi-
niujący te klasy, to spójrz na listingi 8.1, 8.3 i 8.5).
Pierwsza połowa kodu znajdującego się na listingu 8.6 dotyczy pracownika peł-
noetatowego. Ile metod możemy wykorzystać w zmiennej ftEmployee? Na przykład
za pośrednictwem tej zmiennej możemy wywołać metodę ftEmployee.setWeekly
Salary, gdyż ta zmienna jest typu FullTimeEmployee. Możemy także wywołać
metodę ftEmployee.setName, ponieważ klasa FullTimeEmployee rozszerza klasę
Employee.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Dopasowywanie typów
Spójrz, proszę, ponownie na pierwszą połowę kodu z listingu 8.6. Zwróć szcze-
gólną uwagę na ostatnią instrukcję — tę, w której pracownik zatrudniony w peł-
nym wymiarze godzin otrzymuje swoją wypłatę. Instrukcja ta tworzona jest
przez ciekawy, długi ciąg wartości oraz ich typów. Możesz się o tym przekonać,
czytając tę instrukcję od środka na zewnątrz:
RYSUNEK 8.7.
Dopasowanie
parametrów
Zawsze podawaj metodom dane tego typu, który pojawia się na liście parametrów
metody.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zamiast nich ma ona metodę setHourlyRate (ponownie patrz listing 8.5). To
dlatego w przedostatnim wierszu na listingu 8.6 pojawia się wywołanie metody
setHourlyRate.
Ostatni wiersz kodu z listingu 8.6 jest zdecydowanie najciekawszy. W tym wier-
szu kod przekazuje liczbę 10 (liczbę godzin przepracowanych przez pracownika)
jako parametr do metody findPaymentAmount. Proszę to porównać z wcześniej-
szym wywołaniem funkcji findPaymentAmount — wywołaniem przeznaczonym dla
pracownika pełnoetatowego, które znajduje się w pierwszej połowie kodu z li-
stingu 8.6. W obydwu podklasach — FullTimeEmployee i PartTimeEmployee —
znajdują się metody findPaymentAmount. Metody te różnią się jednak od siebie,
ponieważ mają one dwa różne rodzaje listy parametrów:
Można było się tego spodziewać. Obliczenie kwoty wynagrodzenia dla pracow-
nika zatrudnionego w niepełnym wymiarze godzin nie jest tym samym, co wy-
liczenie kwoty wynagrodzenia dla pracownika zatrudnionego w pełnym wymiarze
godzin. Wynagrodzenie pracownika pracującego w niepełnym wymiarze godzin
będzie się co tydzień zmieniać, w zależności od liczby godzin, jaką przepracował
w ciągu tygodnia. Natomiast wynagrodzenie pracownika pracującego w pełnym
wymiarze godzin pozostaje takie samo w każdym tygodniu jego pracy. W związku
z tym klasy FullTimeEmployee i PartTimeEmployee będą udostępniać metodę find
PaymentAmount, ale dla każdej z tych klas będzie ona działać zupełnie inaczej.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Utwórz nową klasę Organization_2.0, której każda instancja będzie mieć tylko
nazwę i roczną kwotę przychodów. Następnie utwórz dwie podklasy: klasę
ProfitMakingOrganization i klasę NonProfitOrganization. Organizacja
nastawiona na zysk (ProfitMakingOrganization) będzie płacić 10-procentowy
podatek od swoich przychodów, natomiast organizacja typu non profit
(NonProfitOrganization) płacić powinna tylko 2-procentowy podatek
od swoich przychodów.
Stwórz oddzielną klasę, która będzie tworzyć instancje klas ProfitMaking
Organization i NonProfitOrganization, a potem wyświetlać informacje o każdej
z tych instancji, w tym kwotę podatku, jaką płacić powinna każda z organizacji.
Możesz zagłębić się w kod klasy PartTimeEmployee, wprowadzić tam kilka zmian
i mieć nadzieję na poprawę sytuacji. (To nie jest najlepszy pomysł!)
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 8.7. Jeszcze jedna podklasa
public class PartTimeWithOver extends PartTimeEmployee {
@Override
public double findPaymentAmount(int hours) {
Rysunek 8.8 przedstawia zależność między kodem z listingu 8.7 a innymi frag-
mentami kodu w tym rozdziale. W szczególności klasa PartTimeWithOver jest pod-
klasą podklasy. W programowaniu zorientowanym obiektowo taki łańcuszek klas
nie jest niczym niezwykłym. Właściwie ten łańcuch podklas jest raczej krótki.
RYSUNEK 8.8.
Drzewo klas
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 8.9.
Metoda
findPaymentAmount
nie jest dziedziczona
Adnotacja Javy
Słowo @Override znajdujące się w kodzie na listingu 8.7 jest przykładem adnota-
cji. Adnotacja języka Java mówi komputerowi coś o kodzie. W szczególności ad-
notacja @Override (pokazana na listingu 8.7) mówi kompilatorowi Javy, aby po-
szukiwał typowego błędu w kodzie. Adnotacja ta mówi dokładnie: „Upewnij się,
że metoda bezpośrednio po tej adnotacji ma tę samą strukturę (tę samą nazwę,
te same parametry i tak dalej) co jedna z metod z klasy nadrzędnej. Jeśli coś się
nie zgadza, wyświetl komunikat o błędzie”.
zamiast int hours, tak jak na listingach 8.5 i 8.7, kompilator przypomni mi, że moja
nowa metoda findPaymentAmount nie zastąpi mi niczego, co jest na listingu 8.5.
Język Java definiuje również inne rodzaje adnotacji (takie jak na przykład
@Deprecated i @SuppressWarnings). O adnotacji @SuppressWarnings możesz przeczytać
więcej w rozdziale 9.
Adnotacje Javy są opcjonalne. Jeśli usuniesz słowo @Override z listingu 8.7, Twój
kod nadal będzie działał poprawnie, jednak adnotacja zapewni mu pewne dodatko-
we bezpieczeństwo. Dzięki adnotacji @Override kompilator sprawdza, czy robisz to,
co zamierzasz zrobić (chodzi tu o pokrywanie jednej z metod nadklasy). I kła-
niając się w stronę George’a Orwella, powiem, że niektóre typy adnotacji są
mniej opcjonalne niż inne. Niektóre adnotacje możesz pominąć w kodzie tylko
w przypadku, gdy jesteś gotów zastąpić adnotację sporą ilością dodatkowego
kodu Javy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Używanie metod z klas i podklas
Jeśli potrzebujesz dodatkowych objaśnień na temat pokrywania metody, spójrz
na kod znajdujący się na listingu 8.8. Na rysunku 8.10 przedstawiono wynik
działania tego programu.
ftEmployee.setName("Barry Burd");
ftEmployee.setJobTitle("Prezes");
ftEmployee.setWeeklySalary(5000.00);
ftEmployee.setBenefitDeduction(500.00);
ftEmployee.cutCheck(ftEmployee.findPaymentAmount());
ptEmployee.setName("Krzysztof Anatol");
ptEmployee.setJobTitle("Autor książek komputerowych");
ptEmployee.setHourlyRate(7.53);
ptEmployee.cutCheck(ptEmployee.findPaymentAmount(50));
ptoEmployee.setName("Stefan Nowak");
ptoEmployee.setJobTitle("Kierowca");
ptoEmployee.setHourlyRate(7.53);
ptoEmployee.cutCheck(ptoEmployee.findPaymentAmount(50));
}
}
RYSUNEK 8.10.
Wynik działania kodu
z listingu 8.8
Kod znajdujący się na listingu 8.8 wypisuje kwotę do wypłaty przeznaczoną dla
trzech pracowników. Pierwszy pracownik pracuje w pełnym wymiarze godzin,
drugi to pracownik zatrudniony w niepełnym wymiarze godzin, który nie otrzymał
jeszcze informacji na temat systemu wypłat za nadgodziny. Trzeci pracownik wie
o systemie płatności za nadgodziny i żąda uczciwego wynagrodzenia.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Przy pierwszym wywołaniu ftEmployee.findPaymentAmount zmienna ftEmployee
jest instancją klasy FullTimeEmployee. A zatem wywoływana jest metoda
z listingu 8.3.
Ten kod jest fantastyczny. Jest czysty, elegancki i wydajny. Pieniądze, które
oszczędzasz na oprogramowaniu, pozwalają Ci na płacenie każdemu pracowni-
kowi podwójnej stawki za godziny nadliczbowe. (Czy to zrobisz, czy zatrzymasz
pieniądze dla siebie, to już jest całkiem inna historia).
myThing.value = 7;
myThing2.value = 44;
myThing.display();
myThing2.display();
}
}
class MyThing {
int value;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
@Override
public void display() {
System.out.println("W klasie MySUBThing, wartość to " + value);
}
}
@Override
public void display() {
System.out.println("W klasie MyOTHERThing, wartość to " + value);
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Definiowanie konstruktorów
Rozdział 9
Konstruowanie
nowych obiektów
Pani Janina Burdzińska
Aleja Szkolna 121
Gdzieś, Podkarpackie
Droga Pani Burd,
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jednocześnie zapewniamy, że podejmujemy wszelkie niezbędne kroki, aby za-
pewnić bezpieczeństwo i dobre samopoczucie naszego lojalnego klienta. W razie
dalszych pytań prosimy o kontakt z naszym działem obsługi skarg. Kierownikiem
działu jest pan Bartosz Całka. Może się Pani z nim skontaktować, odwiedzając
stronę internetową naszej firmy.
Jeszcze raz dziękuję za troskę i mam nadzieję, że nadal będzie Pani wspierać
działalność ObjectsAndClasses.com.
Z poważaniem,
Stefan Kowalski
Ten, który nie mógł wsiąść do windy w rozdziale 4.
Definiowanie konstruktorów
(co to znaczy być temperaturą)
Oto instrukcja, która tworzy obiekt:
I tu nasuwa się jedno pytanie, które brzmi następująco: kiedy prosisz komputer
o utworzenie nowego obiektu, to czy możesz wpływać na to, co znajduje się w po-
lach tego obiektu? A jeśli interesuje Cię coś więcej niż tylko samo wypełnianie pól?
Być może masz całą listę zadań, które komputer powinien wykonać podczas two-
rzenia obiektu. Na przykład możesz chcieć, aby podczas tworzenia nowego obiektu
okna komputer wyrównał pozycje wszystkich przycisków znajdujących się w tym
oknie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Czym jest temperatura?
„Dzień dobry, witaj w „Wiadomościach obiektowych”. Lokalna temperatura w oko-
licy to przyjemne 23 stopnie Celsjusza”.
Każda temperatura składa się z dwóch rzeczy: liczby i skali temperatury. Liczba to
tylko wartość typu double, na przykład 32,0 lub 70,52. Ale jaka jest skala tem-
peratury? Czy jest to ciąg znaków, takich jak "Fahrenheit" lub "Celsjusz"? Nie do
końca, ponieważ niektóre ciągi znaków nie są skalami temperatury. Nie ma skali
temperatury "Quelploof", a program, który może wyświetlać temperaturę "73
stopnie Quelploofa", to zły program. W jaki sposób można ograniczyć skale tem-
peratury do niewielkiej liczby tych, z których korzystają ludzie? Jednym ze spo-
sobów w języku Java jest użycie typu enum.
Tworzenie skomplikowanego typu wyliczeniowego nie jest prostą sprawą, ale aby
utworzyć prosty typ wyliczeniowy enum, wystarczy napisać kilka słów wewnątrz
pary nawiasów klamrowych. Kod przedstawiony na listingu 9.1 definiuje właśnie
taki typ enum, nadając mu nazwę TempScale.
Na listingu 9.1 popisuję się swoją znajomością fizyki, wymieniając nie dwie, nie
cztery, ale dziewięć różnych skali temperatury. Niektóre komputery czytelników
mają problemy ze znakami specjalnymi w słowach RÉAUMUR i RØMER. Jeśli znajdziesz
się w takiej sytuacji, po prostu usuń z kodu słowa RÉAUMUR i RØMER. Obiecuję: nie
zepsuje to naszego przykładu.
Tworzysz wartości.
Podobnie jak 13 i 151 są wartościami typu int, tak wartości CELSIUS i FAHRENHEIT
są wartościami typu TempScale.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Możesz tworzyć zmienne korzystające z tych wartości.
Na listingu 9.2 deklaruję pola number i scale. Tak jak
double number;
deklaruje, że zmienna number jest typu double, tak
TempScale scale;
deklaruje, że zmienna scale jest typu TempScale.
„Być typu TempScale” oznacza, że zmienna może przyjmować takie wartości
jak CELSIUS, FAHRENHEIT, KELVIN i tak dalej. Zatem w kodzie przedstawionym
na listingu 9.2 mogę już przypisać zmiennej scale wartość FAHRENHEIT
(a dokładniej wartość TempScale.FAHRENHEIT).
Typ enum jest klasą języka Java w przebraniu. Dlatego też na listingu 9.1 znajduje
się cały plik poświęcony tylko jednej rzeczy, a mianowicie deklaracji typu enum
(typu TempScale). Podobnie jak deklaracja klasy, deklaracja typu enum musi być za-
pisana w osobnym pliku. Kod na listingu 9.1 znajduje się zatem w pliku o nazwie
TempScale.java.
public Temperature() {
number = 0.0;
scale = TempScale.FAHRENHEIT;
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
}
public void setNumber(double number) {
this.number = number;
}
public double getNumber() {
return number;
}
Kod pokazany na listingu 9.2 zawiera zwykłe metody ustalające i pobierające (me-
tody dostępu dla pól number i scale).
Poza tym kod z listingu 9.2 zawiera cztery inne elementy przypominające me-
tody. Każdy z tych metodopodobnych elementów nosi nazwę Temperature, która
jest taka sama jak nazwa klasy. Żaden z tych metodopodobnych tworów o nawie
Temperature nie ma podanego jakiegokolwiek typu wartości zwracanej — nawet
typu void, który jest informacją, że funkcja nie zwraca żadnej wartości.
Gdy komputer tworzy nowy obiekt, wykonuje tym samym instrukcje znajdujące
się wewnątrz konstruktora.
Możesz pominąć słowo public w pierwszych wierszach kodu z listingów 9.1 i 9.2.
Jeśli je pominiesz, to inne programy Javy mogą nie być w stanie skorzystać z funkcji
zdefiniowanych w typie TempScale i w klasie Temperature. (Nie martw się jednak
o programy pokazane w tym rozdziale: niezależnie od tego, czy słowo public pojawi
się w deklaracji tych typów, wszystkie te programy mogą korzystać z kodu po-
kazanego na listingach 9.1 i 9.2. Chcąc dowiedzieć się, które programy języka Java
mogą używać klas nieoznaczonych słowem public, zajrzyj do rozdziału 14.). Jeśli
użyjesz słowa public w pierwszym wierszu kodu z listingu 9.1, to kod ten będzie
się musiał znajdować w pliku o nazwie TempScale.java, zaczynającej się od dużej
litery T. Natomiast jeśli użyjesz słowa public w pierwszym wierszu w kodzie na li-
stingu 9.2, to kod ten będzie musiał znajdować się w pliku o nazwie Temperature.java,
także rozpoczynającej się dużą literą T. (Wprowadzenie do klas publicznych znaj-
dziesz w rozdziale 7.).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Co możesz zrobić z temperaturą?
Kod na listingu 9.3 praktycznie wykorzystuje niektóre idee przedstawione
w poprzednim podrozdziale. W tym kodzie wywołujemy konstruktory zadeklaro-
wane już wcześniej na listingu 9.2. Rysunek 9.1 pokazuje, co dzieje się po uru-
chomieniu całego tego programu.
RYSUNEK 9.1.
Uruchamianie kodu
z listingu 9.3
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
MAŁE OSZUSTWO:
TYPY ENUM I INSTRUKCJE SWITCH
Na listingach 9.2 i 9.3 pojawiają się długie nazwy, takie jak TempScale.FAHRENHEIT
lub TempScale.CELSIUS. Nazwy w rodzaju FAHRENHEIT i CELSIUS należą do mojego
typu TempScale (typ zdefiniowany został już wcześniej na listingu 9.1). Nazwy te nie
mają żadnego znaczenia poza moimi przykładami z typem TempScale. (Jeśli uważasz,
że jestem egoistyczny, rzucając uwagami w stylu: „nie mają znaczenia poza moimi
przykładami”, spróbuj usunąć zapis TempScale. z części TempScale.FAHRENHEIT
znajdującej się na listingu 9.2. Nagle Java powie Ci, że w Twoim kodzie jest błąd).
Java jest niezwykle dokładna, jeśli chodzi o nazwy typów i stosowanie kropek. Gdy
twórcy tego języka tworzyli typ enum, zdecydowali, że typy wyliczeniowe oraz in-
strukcje switch zasługują na specjalne traktowanie. Możemy zatem użyć wartości
enum, aby zdecydować, który z przypadków case ma zostać wykonany w ramach in-
strukcji switch. W takiej sytuacji nie używamy już nazwy typu wyliczeniowego w wy-
rażeniach case. Na przykład poprawny kod języka Java wygląda następująco:
TempScale scale = TempScale.RANKINE;
char letter;
switch (scale) {
case CELSIUS:
letter = 'C';
break;
case KELVIN:
letter = 'K';
break;
case RANKINE:
case RÉAUMUR:
case RØMER:
letter = 'R';
break;
default:
letter = 'X';
break;
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
ma dwa parametry: pierwszy typu double i drugi typu TempScale. Java zatwierdza
wywołanie tego konstruktora, ponieważ kod z listingu 9.2 zawiera pasującą de-
klarację. Oznacza to, że nagłówek:
także ma dwa parametry: pierwszy typu double i drugi typu TempScale. Jeśli na
listingu 9.3 wywołanie konstruktora Temperature nie miałoby pasującej deklaracji
na listingu 9.2, to program z listingu 9.3 roztrzaskałby się na kawałeczki. (Mó-
wiąc nieco łagodniej, Java wyświetliłaby błąd przy próbie skompilowania kodu
z listingu 9.3).
Nawiasem mówiąc, ta cała zabawa z wieloma parametrami wcale nie jest niczym
nowym. W rozdziale 6. wykorzystałem instrukcję keyboard.findWithinHorizon(".
", 0).charAt(0). W tym przypadku wywołanie metody findWithinHorizon(".",
0) ma dwa parametry: ciąg znaków i wartość typu int. Na szczęście dla mnie API
Javy zawiera deklarację metody findWithinHorizon — deklarację, której pierw-
szym parametrem jest ciąg znaków, a drugim parametrem jest wartość typu int.
z listingu 9.3, mówi sam do siebie: „Liczba 32.0 w nawiasach to wartość typu
double”. Jeden z konstruktorów Temperature z listingu 9.2 ma tylko jeden parametr
typu double. Nagłówek konstruktora wygląda tak:
this.number = number;
scale = TempScale.FAHRENHEIT;
W rezultacie otrzymamy nowy obiekt, którego pole number ma wartość 32.0, a pole
scale będzie miało wartość TempScale.FAHRENHEIT.
W dwóch wierszach kodu mamy dwie instrukcje, które ustawiają wartości dla
pól number i scale. Spójrz na drugą z tych instrukcji, która jest nieco łatwiejsza do
zrozumienia. Druga instrukcja ustala wartość pola scale nowego obiektu na
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
TempScale.FAHRENHEIT. Jak widzisz, lista parametrów konstruktora to (double
number), a zatem nie ma na niej wartości dla pola scale. Ktokolwiek przygotował
ten kod, musiał zdecydować, jakiej wartości użyć w polu scale. Programista
mógł wybrać wartość FAHRENHEIT lub CELSIUS, ale mógł także zdecydować się na
KELVIN, RANKINE lub dowolną inną mało znaną skalę wypisaną na listingu 9.1.
(Ten programista mieszka w New Jersey w Stanach Zjednoczonych, gdzie ludzie
często używają starej skali temperatury Fahrenheita).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 9.2.
Co oznacza
nazwa this.number
i nazwa number
out.printf("$%4.2f\n", myInterest);
Wymyślanie nazw jest prawdziwą sztuką, a nie nauką. Przeszedłem już przez wiele
różnych faz nazewnictwa. Wiele lat temu, kiedy potrzebowałem nowej nazwy para-
metru, wybrałem przekręconą pisownię oryginalnej nazwy zmiennej (nadawałem
parametrowi nazwę w rodzaju numbr lub nuhmber). Próbowałem także zmieniać
wielkość liter w nazwie zmiennej, aby uzyskać nazwę parametru (używałem nazw
parametrów, takich jak Number lub nUMBER). W rozdziale 8. wszystkim swoim para-
metrom nadaję nazwy składające się z odpowiadających im nazw zmiennych z przy-
rostkiem In (zmienna jobTitle powiązana jest z parametrem jobTitleIn). Żaden
z tych schematów nazewnictwa nie działa dobrze — nigdy nie mogę zapamiętać tych
dziwnych nowych nazw, które stworzyłem wcześniej. Dobrą wiadomością jest to, że
cały ten wysiłek wkładany w odpowiednie dopasowanie nazewnictwa parametrów
wcale nie jest potrzebny. Możesz nadać swojemu parametrowi taką samą nazwę jak
nazwa zmiennej. Aby je rozróżnić, należy po prostu użyć słowa kluczowego this.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Natomiast na listingu 9.3 zrywam z tą tradycją i rozpoczynam wywołanie metody
printf wraz ze zmienną, którą nazwałem format.
I to jest całkowicie w porządku, o ile moja zmienna format jest typu String. I rze-
czywiście, na listingu 9.3 pierwsza deklaracja zmiennej to:
W tej deklaracji zmiennej format należy zwrócić szczególną uwagę na słowo final.
Jest to słowo kluczowe języka Java, które wskazuje, że nie można zmienić wartości
zmiennej format. Jeśli dodam kolejne polecenie przypisania do kodu z listingu 9.3:
W kodzie przedstawionym na listingu 9.3 użycie słowa kluczowego final nie jest
absolutnie wymagane, ale zapewnia nam dodatkową ochronę. Kiedy inicjalizuję
zmienną format wartością "%5.2f degrees %s\n", zamierzam wielokrotnie używać
tego formatu w niezmienionej postaci. Dobrze wiem, że nie mam zamiaru zmie-
niać wartości zmiennej format. No cóż, oczywiście w programie składającym się
z 10 000 wierszy mogę się pomylić i gdzieś głęboko w kodzie spróbować przypisać
nową wartość do zmiennej format. Aby zapobiec przypadkowej zmianie wartości
zmiennej format, deklaruję, że wartość tej zmiennej jest ostateczna. Jest to po pro-
stu dobra, bezpieczna praktyka programowania.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
odpowiadającymi niektórym oficjalnym kodom lotnisk IATA. (Na przykład kod
londyńskiego lotniska Heathrow to LHR; kod międzynarodowego lotniska w Los
Angeles to LAX; zajrzyj pod adres https://www.iata.org/publications/Pages/code-
search.aspx, aby znaleźć stronę umożliwiającą wyszukiwanie bazy danych kodów
lotniczych).
Czasy przylotu i wylotu zapisuj przy użyciu klasy LocalTime. (Więcej informacji
na temat tej klasy można znaleźć na stronie dokumentacji zamieszczonej pod
adresem internetowym
https://docs.oracle.com/javase/8/docs/api/java/time/LocalTime.html). Aby utworzyć
obiekt LocalTime oznaczający godzinę 14:15 (zapisywaną również jako 2:15 PM),
napisz następującą instrukcję:
LocalTime twoFifteen = LocalTime.of(14, 15);
Natomiast aby utworzyć obiekt LocalTime ustawiony na aktualny czas (zgodnie
z zegarem systemowym komputera), napisz:
LocalTime currentTime = LocalTime.now();
Każdy z lotów ma swój numer, lotnisko odlotu i lotnisko przylotu. Jednak niektóre
loty mogą nie mieć czasów odlotów i przylotów. Utwórz konstruktory z parametrami
odlotu i przylotu, a także bez tych parametrów.
Utwórz osobną klasę, która będzie korzystać z nowej klasy AirplaneFlight.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
w nieco szerszym kontekście tego zagadnienia. Dlaczego nie przekazać poszczegól-
nym obiektom klasy Temperature odpowiedzialności za wyświetlanie własnych
danych? W końcu po opracowaniu metody display prawdopodobnie będziesz
chciał ją udostępnić także innym osobom, które używają klasy Temperature. A zatem
umieścimy tę metodę wewnątrz deklaracji klasy. W ten sposób każdy, kto chciałby
użyć tego kodu do wyświetlania temperatur, będzie miał łatwy dostęp do metody
display.
public TemperatureNice() {
super();
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
i scale. (W końcu TemperatureNice jest podklasą klasy Temperature, a kod klasy
Temperature definiuje pola number i scale). Jednak pola number i scale w klasie
Temperature są zadeklarowane jako prywatne, dlatego tylko kod znajdujący się
wewnątrz klasy Temperature może bezpośrednio korzystać z tych pól.
Gdy kod obiektu zawiera wywołanie jednej z jego własnych metod, nie musimy
już poprzedzać wywołania znakiem kropki. Na przykład w ostatniej instrukcji
znajdującej się na listingu 9.5 obiekt wywołuje własne metody za pomocą funkcji
getNumber() i getScale(), a nie za pomocą jakiśObiekt.getNumber() i innyObiekt.
getScale(). Jeśli zapis bez użycia kropki sprawia, że masz mdłości, możesz
zawsze poprawić tę sytuację poprzez dopisanie jeszcze jednego słowa kluczowego
this: po prostu w ostatnim wierszu kodu listingu 9.5 napiszemy: this.get
Number() i this.getScale().
Na listingu 9.5 znalazły się zatem cztery konstruktory. Każdy z nich ma nazwę
TemperatureNice oraz własną, unikatową listę parametrów. To była ta nudna
część. Interesujące jest to, że każdy konstruktor wywołuje coś nazwanego super,
co jest słowem kluczowym języka Java.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Na listingu 9.5 super oznacza konstruktor w klasie nadrzędnej:
super(number, scale);
z listingu 9.5, mówi sam do siebie: „Pola number i scale w nawiasach mają typy
double i TempScale. Tylko jeden z konstruktorów klasy Temperature na listingu 9.2
ma dwa parametry z typami double i TempScale”. Nagłówek konstruktora wygląda tak:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
temp = new TemperatureNice(2.73, TempScale.KELVIN);
temp.display();
}
}
Kod z listingu 9.6 jest bardzo podobny do kodu swojego kuzyna z listingu 9.3.
Natomiast różnice między nimi wyglądają następująco:
Kod z listingu 9.6 tworzy instancje klasy TemperatureNice. Oznacza to, że kod
z listingu 9.6 wywołuje konstruktory klasy TemperatureNice, a nie klasy
Temperature.
Wynik działania kodu z listingu 9.6 wygląda dokładnie tak samo, jak wynik
działania programu z listingu 9.3 — po prostu dociera on do linii mety w znacz-
nie bardziej elegancki sposób. (Wynik działania został pokazany wcześniej na
rysunku 9.1).
Domyślny konstruktor
Głównym przesłaniem z poprzedniego punktu jest to, że podklasy nie dziedziczą
konstruktorów. Co zatem dają nam wszystkie listingi programów z rozdziału 8.?
Na listingu 8.6 mamy instrukcję:
Oto co się tutaj dzieje. Kiedy tworzysz podklasę i nie umieszczasz w kodzie żad-
nych jawnych deklaracji konstruktora, Java tworzy za Ciebie jeden konstruktor.
Nazywa się on domyślnym konstruktorem. Jeśli tworzysz podklasę public Full
TimeEmployee, domyślny konstruktor wygląda jak ten z listingu 9.7.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Właśnie przeczytałeś o domyślnych konstruktorach, ale uważaj! Zwróć uwagę na
jedną rzecz, o której nie wspominałem podczas naszej rozmowy na temat kon-
struktorów domyślnych: wcale nie mówiłem, że zawsze dostajesz domyślny
konstruktor. Zwłaszcza jeśli utworzysz podklasę i sam zdefiniujesz konstruktory, to
Java nie doda już domyślnego konstruktora dla tej podklasy (a podklasa nie dzie-
dziczy też żadnych konstruktorów).
Co może z tego wynikać? Listing 9.8 jest kopią kodu z listingu 8.3, ale z doda-
nym jednym konstruktorem. Spójrz na tę zmodyfikowaną wersję kodu klasy
FullTimeEmployee.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
I co z tym będziemy robić dalej? Jeśli deklarujesz dowolne konstruktory, zade-
klaruj wszystkie te, których prawdopodobnie będziesz potrzebować. Weź kon-
struktor z listingu 9.7 i dodaj go do kodu znajdującego się na listingu 9.8. I w ten
sposób sprawisz, że wywołanie new FullTimeEmployee() zacznie ponownie działać.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
to obiekt temp zmieni swoją skalę temperatury na skalę Celsjusza i odpowiednio
przekształci wartość liczbową. Ten samo stanie się też w przypadku, gdy Java
wykona instrukcję:
temp.convertTo(TempScale.FAHRENHEIT);
gdzie zmienna temp przechowywać będzie temperaturę w skali Celsjusza.
Przykład z tego podrozdziału zawiera konstruktor, który robi coś więcej niż tylko
przypisywanie wartości do pól. Cały przykład znajduje się na listingach 9.9 i 9.10.
Wynik działania tego przykładowego kodu pokazano na rysunku 9.3.
@SuppressWarnings("numer seryjny")
public class SimpleFrame extends JFrame {
public SimpleFrame() {
setTitle("Nie naciskaj przycisku!");
setLayout(new FlowLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
add(new JButton("Panika"));
setSize(300, 100);
setVisible(true);
}
}
RYSUNEK 9.3.
Nie panikuj
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
PAKIETY I DEKLARACJE IMPORTU
Język Java udostępnia funkcję, która pozwala na grupowanie klas w grupy. Każde
takie zbiorowisko klas nazywa się pakietem. W świecie Javy programiści zwyczajowo
nadają tym pakietom długie, wypełnione kropkami nazwy. Na przykład mogę na-
zwać pakiet com.allmycode.utils.textUtils, ponieważ zarejestrowałem nazwę domeny
allmycode.com. API języka Java jest w rzeczywistości dużą kolekcją takich właśnie
pakietów. Interfejs API zawiera pakiety o takich nazwach jak java.lang, java.util,
java.awt i javax.swing.
Na przykład deklaracja
import java.util.Scanner;
jest poprawna, ponieważ java.util to nazwa pakietu będącego częścią API języka
Java, a Scanner to nazwa klasy w pakiecie java.util. Wykorzystującą znak kropki na-
zwę java.util.Scanner nazywa się w pełni kwalifikowaną nazwą klasy Scanner. Peł-
na nazwa klasy zawiera nazwę pakietu, w którym ta klasa jest zdefiniowana. (Mo-
żesz dowiedzieć się wszystkiego o pakiecie java.util i klasie Scanner, czytając
dokumentację API języka Java. Wskazówki dotyczące czytania dokumentacji znaj-
dziesz w rozdziale 3. i na stronie internetowej tej książki).
import javax.swing.*;
jest poprawna, ponieważ javax.swing jest nazwą pakietu tworzącego API języka Java,
a gwiazdka odnosi się do wszystkich klas istniejących w tym pakiecie. Z tą deklaracją
import umieszczoną na górze kodu programu Javy możemy użyć skróconych nazw
dla klas z pakietu javax.swing — na przykład nazw takich jak JFrame, JButton,
JMenuBar, JCheckBox i jeszcze wiele innych.
nie jest poprawną deklaracją import. Interfejs API Javy nie zawiera pakietu z jedno-
wyrazową nazwą javax. Możesz pomyśleć, że wiersz ten pozwala skrócić wszystkie na-
zwy zaczynające się od nazwy javax (nazwy takie jak na przykład javax.swing.JFrame
i javax.sound.midi), ale niestety nie tak działa deklaracja import. Ze względu na to,
że javax nie jest nazwą pakietu, wiersz kodu import javax.* po prostu będzie iry-
tować kompilator Javy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Podobnie jak jest w przypadku przykładów z klasą DummiesFrame, kod na listingach
9.9 i 9.10 również wyświetla okno na ekranie komputera. Ale w przeciwieństwie
do tych przykładów wszystkie wywołania metod pokazanych na listingach 9.9 i 9.10
odnoszą się do metod zdefiniowanych w standardowym interfejsie API Javy.
Kod na listingu 9.9 zawiera wiele nazw, które prawdopodobnie nie są Ci jeszcze
znane — nazwy te pochodzą z API języka Java. Kiedy po raz pierwszy zapozna-
łem się z językiem Java, głupio wierzyłem w to, że znajomość tego języka ozna-
cza konieczność zapamiętania tych wszystkich nazw. Jest wręcz przeciwnie: te
nazwy to tylko bagaż podręczny. Prawdziwa Java to sposób, w jaki język imple-
mentuje koncepcje obiektowe.
W każdym razie metoda main z listingu 9.10 składa się z tylko jednej instrukcji:
wywołania konstruktora klasy SimpleFrame. Zwróć, proszę, uwagę, że obiekt two-
rzony przez to wywołanie nie jest nawet przypisany do zmiennej. To jest zupełnie
w porządku, gdyż kod programu nie musi się później odwoływać do tego obiektu.
Aby uzyskać małą garść dodatkowych informacji na temat pakietu Java, zobacz
pobliską ramkę „Pakiety i deklaracje importu”. Natomiast w celu zdobycia szer-
szej wiedzy na temat pakietów Javy zajrzyj, proszę, do rozdziału 14.
W API Java to, co ludzie zwykle nazywają oknem, jest w rzeczywistości instancją
klasy javax.swing.JFrame.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
SetTitle — wywołanie metody setTitle umieszcza tekst na pasku tytułu ramki.
(Nowy obiekt SimpleFrame wywołuje własną metodę setTitle).
Add — nowy obiekt SimpleFrame wywołuje własną metodę add. Wywołanie tej
metody spowoduje umieszczenie przycisku na powierzchni obiektu (w tym
przypadku na powierzchni naszej ramki).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Adnotacja SuppressWarnings
W rozdziale 8. przedstawiłem już adnotacje — dodatkowy kod, który dostarcza
przydatnych informacji o naturze programu. W rozdziale 8. opisuję w szczególności
adnotację Override.
W tym rozdziale kod przedstawiony na listingu 9.9 wprowadza jeszcze inny ro-
dzaj adnotacji — SuppressWarnings (wycisz ostrzeżenia). Kiedy używasz adnotacji
SuppressWarnings, mówisz kompilatorowi Java, aby nie przypominał Ci, że w Twoim
programie znajduje się pewien wątpliwy kod. Na listingu 9.9 wiersz @Suppress
Warnings("serial") mówi kompilatorowi Java, aby nie przypominał Ci, że po-
minąłeś coś, co nazywa się polem serialVersionUID. Innymi słowy, adnotacja
SuppressWarnings mówi Javie, aby nie wyświetlała ostrzeżenia podobnego do po-
kazanego na rysunku 9.4.
RYSUNEK 9.4.
Bez adnotacji
SuppressWarnings
Java ostrzega
o brakującym
wywołaniu pola
serialVersionUID
jshell> frame.setVisible(true)
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Następnie w kodzie pokazanym na listingu 9.9 zmień instrukcję z
setLayout(new FlowLayout());
na
setLayout(new BorderLayout());
I co spowoduje ta zmiana po uruchomieniu programu?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Sprytne techniki
Javy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TEJ CZĘŚCI
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 10
Wprowadzanie
zmiennych i metod
tam, gdzie się znajdują
W
itam ponownie. Słuchasz stacji radiowej WWW, a ja jestem waszym go-
spodarzem, nazywam się Sam Burd. To kolejny początek wielkiego sezonu
rozgrywek baseballowych, a dziś stacja WWW przynosi wam relację na
żywo z meczu Hankees kontra Socks. W tym momencie oczekuję informacji o osta-
tecznym wyniku tych rozgrywek.
Jak pamiętasz, na początku tego meczu drużyna Socks wyglądała, jakby zamie-
rzała pozamiatać hankeesami boisko. Później drużyna Hankees zdobywała punkt
za punktem, wyciskając z socksów siódme poty. Ależ się działo! Cieszę się, że nie
musiałem być na ich miejscu.
W każdym razie gra toczyła się dalej, a drużyna Socks w końcu zebrała się w sobie.
Teraz socksi idą z hankeesami łeb w łeb. Za chwileczkę zobaczymy ostateczny
wynik, ale najpierw kilka informacji. Po zakończeniu tego meczu pozostań z nami,
aby zobaczyć też ważny mecz z Jersey. I nie zapomnij włączyć nas w przyszłym
tygodniu, kiedy drużyna Cleveland Gowns zmierzy się z Bermuda Shorts.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Okay, oto wynik końcowy. Który zespół jest lepszy? Który będzie cieszyć się ze
zwycięstwa? Zwycięzcą jest… och, rety, mamy remis!
Definiowanie klasy
(co to znaczy być graczem w baseball)
Jeśli o mnie chodzi, gracz w baseball ma imię i swoją średnią uderzeń. Kod na listingu
10.1 przekłada moje odczucia na ten temat do postaci programu w języku Java.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Metody pobierające dla pól name i average. W celu zdobycia dodatkowych
informacji na temat metod dostępu (czyli metod ustalających i pobierających)
wróć do rozdziału 7.
Klasa DecimalFormat Javy może być bardzo przydatna. Na przykład, aby wyświetlić
wartości 345 i –345 w formacie stosowanym powszechnie w księgowości, można
użyć następującego kodu:
345.00
(345.00)
Chcąc odkryć jeszcze kilka innych sztuczek z liczbami, odwiedź stronę Decimal-
Format zamieszczoną w dokumentacji API języka Java (pod adresem https://docs.
oracle.com/javase/8/docs/api/java/text/DecimalFormat.html).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Korzystanie z klasy Player
Na listingach 10.2 i 10.3 przedstawiam kod, który wykorzystuje klasę Player —
klasę zdefiniowaną już wcześniej na listingu 10.1.
@SuppressWarnings("serial")
public class TeamFrame extends JFrame {
setTitle("The Hankees");
setLayout(new GridLayout(9, 2, 20, 3));
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
hankeesData.close();
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wynik działania programu z listingów 10.1, 10.2 i 10.3 możesz zobaczyć na ry-
sunku 10.1.
RYSUNEK 10.1.
Czy postawiłbyś
pieniądze
na tych ludzi?
Chcąc uruchomić ten program, będziemy potrzebować pliku Hankees.txt. Ten plik
zawiera dane opisujące Twoich ulubionych graczy w baseball (patrz rysunek 10.2).
RYSUNEK 10.2.
Co za drużyna!
Musisz także zadbać o to, żeby plik Hankees.txt był w określonym miejscu na
dysku twardym Twojego komputera. Jeśli używasz Eclipse, to „określone miej-
sce” będzie katalogiem projektu w obszarze roboczym Eclipse. Z drugiej jednak
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
strony, jeśli używasz języka Java z wiersza poleceń, to „miejsce” może być ka-
talogiem zawierającym kod z listingu 10.3. Tak czy inaczej nie da się uniknąć
tego, że plik Hankees.txt musi znajdować się we właściwym miejscu na dysku
twardym. Jeśli nie masz tego pliku w tym miejscu, a następnie będziesz próbował
uruchomić przykład z tego podrozdziału, otrzymasz nieprzyjemny komunikat
FileNotFoundException (nie znaleziono pliku).
Możesz także pobrać materiały z witryny tej książki i uzyskać instrukcje otwie-
rania przykładów z niniejszej książki w swoim ulubionym środowisku IDE (Eclipse,
NetBeans lub IntelliJ IDEA). Po otwarciu projektu 10-01 z tego rozdziału plik
Hankees.txt będzie dokładnie tam, gdzie powinien być. Nie musisz się już martwić
o umieszczenie pliku tam, gdzie należy.
Aby kod z tego podrozdziału działał poprawnie, musisz umieścić znak końca
wiersza po ostatniej liczbie (.212) na rysunku 10.2. Szczegółowe informacje na
temat znaku końca wiersza znajdziesz w rozdziale 8.
Każda instancja klasy Player ma własne pola name i average. Każda instancja ma
również swój własny konstruktor Player i własne metody getName, getAverage
i getAverageString. Spójrz na rysunek 10.3 i pomyśl o klasie Player wraz z jej dzie-
więcioma wcieleniami.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 10.3.
Klasa i jej obiekty
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
kiety, a następnie wywołasz metodę setLayout), wywołanie setLayout zmieni
układ etykiet, odpowiednio uporządkowując je na ramce. W obu przypadkach działa
to tak samo dobrze.
Podczas projektowania ramki jedyną rzeczą, której nie wolno robić, jest naruszenie
poniższej sekwencji:
RYSUNEK 10.4.
Skurczona ramka
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
main radzi sobie z tym problemem, rzucając wyjątek IOException dalej (po prostu
ma własną klauzulę throws IOException). To właśnie w ten sposób klauzula throws
działa w programach Javy.
W tym miejscu w książce uważny czytelnik serii Dla bystrzaków może zadać jedno
lub dwa ważne pytania. „Skoro metoda main ma klauzulę throws, to ktoś inny
musi obsłużyć wyjątki z tej klauzuli. Ale kto wywołał metodę main? Kto zajmuje
się wyjątkiem IOException znajdującym się w klauzuli throws z listingu 10.3?”
Odpowiedź na te pytania brzmi następująco: To wirtualna maszyna Javy (lub
JVM, czyli mechanizm, który uruchamia cały kod Javy) wywołuje metodę main.
Oznacza to, że to JVM musi zająć się wyjątkiem IOException z listingu 10.3. Jeśli
program będzie miał problemy z odczytaniem pliku Hankees.txt, ostatecznie od-
powiedzialność za obsługę wyjątków spadnie na JVM. W takiej sytuacji JVM wy-
świetla komunikat o błędzie, a następnie kończy działanie programu. Jakież to
wygodne!
Kod z listingu 10.2 odczytuje plik o nazwie Hankees.txt. Usuń ten plik z dysku
twardego swojego komputera lub tymczasowo przenieś go do innego katalogu.
Następnie spróbuj uruchomić program znajdujący się na listingach od 10.1 do 10.3.
Jakie straszne rzeczy będą miały miejsce, kiedy to zrobisz?
Linia męskiej odzieży obejmuje koszule, spodnie, kurtki, płaszcze, krawaty i buty.
Utwórz typ wyliczeniowy enum, aby reprezentować sześć rodzajów tych
przedmiotów. Następnie utwórz klasę MensClothingItem. Każda instancja tej
klasy będzie zawierać rodzaj (jedną z sześciu wartości typu enum) i nazwę (np.
Nieformalny Letni Projekt # 7).
Napisz kod programu w taki sposób, aby wyświetlić ramkę (na przykład podobną
do ramki znajdującej się na rysunku 10.1). Ramka powinna mieć sześć wierszy,
tak aby opisać jedną kompletną męską szafę.
Utwórz typ enum reprezentujący kolory w talii kart do gry (Trefl, Karo, Kier i Pik).
Następnie utwórz klasę kart do gry PlayingCard. Każda karta do gry powinna
mieć numer (od 1 do 13) i kolor (suit). W schemacie numerowania 11 oznacza
waleta, 12 oznacza królową, a 13 oznacza króla. Napisz kod programu, który
tworzy kilka kart i wyświetla je na ekranie (w formacie tekstowym lub w ramce
podobnej do tej znajdującej się na rysunku 10.1).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Prace statyczne
(wyznaczanie średniej dla zespołu)
Rozmyślając o kodzie znajdującym się na listingach od 10.1 do 10.3, decydujesz,
że chcesz wyznaczyć ogólną średnią wydajności drużyny. To niezły pomysł!
Drużyna Hankees (na rysunku 10.1) ma średni wynik mniej więcej .106, więc
zespół potrzebuje intensywnego treningu. Podczas gdy gracze ćwiczą na boisku,
Ty musisz zmierzyć się z pewnym filozoficznym problemem.
Na listingach od 10.1 do 10.3 zdefiniowano trzy klasy: klasę Player i jeszcze dwie
inne klasy, które pomagają wyświetlać dane z klasy Player. Gdzie zatem w tym
gąszczu klas powinny znajdować się zmienne przechowujące średni wynik wy-
dajności drużyny?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 10.5.
Niektóre statyczne
i niestatyczne
pola i metody
static {
decFormat.setMaximumIntegerDigits(0);
decFormat.setMaximumFractionDigits(3);
decFormat.setMinimumFractionDigits(3);
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
playerCount (liczba graczy w zespole) nie powinno być związane z poszczegól-
nymi graczami, a zmuszanie każdego obiektu PlayerPlus do bieżącego śledzenia
swojej liczby byłoby po prostu głupie. („Wiem, ilu graczami jestem. Jestem tylko
jednym!”). Jeśli miałbyś dziewięć niezależnych pól playerCount, to każde z nich
mogłoby przechowywać liczbę 1 (co byłoby całkowicie bezużyteczne) lub miałbyś
dziewięć kopii licznika, co byłoby marnotrawstwem miejsca, a poza tym byłoby
to podatne na błędy. Poprzez zdefiniowanie pola playerCount jako statycznego
umieszczamy to pole w jednym miejscu — tym, w którym powinno się ono znaleźć.
Ogólnie rzecz biorąc, każde zadanie wspólne dla wszystkich instancji (i to dające
taki sam wynik dla każdej z tych instancji) powinno być zdefiniowane jako me-
toda statyczna.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
// TO JEST BARDZO ZŁY KOD:
public class PlayerPlus extends Player {
private static DecimalFormat decFormat = new DecimalFormat();
decFormat.setMaximumIntegerDigits(0); // Źle!
decFormat.setMaximumFractionDigits(3); // Źle!
decFormat.setfsMinimumFractionDigits(3); // Źle!
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 10.5. Sposób wykorzystania kodu pokazanego na listingu 10.4
import java.util.Scanner;
import java.io.File;
import java.io.IOException;
import javax.swing.JFrame;
import javax.swing.JLabel;
import java.awt.GridLayout;
@SuppressWarnings("serial")
public class TeamFrame extends JFrame {
addPlayerInfo(player);
}
add(new JLabel());
add(new JLabel(" ------"));
add(new JLabel("Średnia uderzeń drużyny:"));
add(new JLabel(PlayerPlus.findTeamAverageString()));
setTitle("The Hankees");
setLayout(new GridLayout(11, 2, 20, 3));
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);
hankeesData.close();
}
Aby uruchomić kod z listingu 10.5, potrzebujesz klasy z metodą main. Tutaj świetnie
sprawdzi się klasa ShowTeamFrame z listingu 10.3.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 10.6.
Wynik działania kodu
z listingu 10.5
Kiedy odwołujesz się do statycznego pola lub metody, możesz oszukiwać i uży-
wać nazwy obiektu zamiast nazwy klasy. Na przykład w kodzie zawartym na li-
stingu 10.5 po odpowiednim przestawieniu niektórych instrukcji można użyć
wyrażenia player.findTeamAverageString().
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Słowo kluczowe static to zeszłoroczny śnieg
W tym podrozdziale bardzo skupiamy się na temacie statycznych pól i metod, ale
elementy statyczne towarzyszą od początku tej książki. Na przykład w rozdziale 3.
przedstawiam metodę System.out.println. Nazwa System odnosi się do klasy, a out
to pole statyczne w tej klasie. Dlatego w rozdziale 4. i w kolejnych rozdziałach
używam słowa kluczowego static do zaimportowania pola out:
W Javie pola i metody statyczne pojawiają się niemal wszędzie. Kiedy zadeklaro-
wane zostały w kodzie przygotowanym przez kogoś innego, a Ty chcesz użyć ich
we własnym kodzie, to prawie nigdy nie musisz się nimi przejmować. Ale kiedy
deklarujesz własne pola lub metody i musisz samodzielnie zdecydować, czy mają
być statyczne, czy też nie, to konieczna jest chwila zastanowienia.
W tej książce dopiero na listingu 3.1 pierwszy raz poważnie używam słowa kluczo-
wego static. Wykorzystuję to słowo przy definiowaniu każdej metody main (a na
listingach w tej książce znajduje się wiele takich metod). Dlaczego zatem metoda
main musi być statyczna? Pamiętaj, że elementy niestatyczne należą do obiektów,
a nie do klas. Niestety podczas uruchamiania programu w Javie nie ma jeszcze
żadnych utworzonych obiektów. Dopiero instrukcje umieszczone w metodzie main
mogą zająć się tworzeniem pierwszych obiektów. Oznacza to, że jeżeli metoda
main nie byłaby statyczna, to mielibyśmy typowy problem jajka i kury.
Dziś już wiem, dlaczego tak często widywałem ten komunikat o błędzie. Gdybym
chciał, mogę nawet sprawić, że pojawi się on ponownie. A mimo to wciąż czuję
drżenie, widząc go na ekranie.
Zanim zrozumiesz, dlaczego pojawia się ten komunikat i jak rozwiązać wywołujący
go problem, musisz najpierw zaznajomić się z nową terminologią. Pola i metody,
które nie zostały zdefiniowane jako statyczne, nazywane są niestatycznymi. (Na-
prawdę zaskakujące, co?) Biorąc pod uwagę tę terminologię, istnieją co najmniej
dwa sposoby na pojawienie się tej przerażającej wiadomości:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W obu przypadkach wpadniesz w kłopoty. Bierzesz coś, co należy do obiektu (rzeczy
niestatycznej) i umieszczasz w miejscu, w którym nie widać żadnych obiektów.
Aby poznać smak drugiej sytuacji (z powyższej listy), wróć na chwilę do listingu
10.4. Gdy nikt nie będzie patrzył, po cichu usuń słowo static z deklaracji pola
decFormat (znajduje się w górnej części listingu). W ten sposób zmienisz pole
decFormat w pole niestatyczne. Nagle każdy gracz w drużynie będzie miał oddzielne
pole decFormat.
Cóż, wszystko jest po prostu cacy, dopóki komputer nie uruchomi metody findTeam
AverageString. Ta statyczna metoda zawiera w sobie cztery instrukcje decFor-
mat.cośTamCośTam. Po raz kolejny pojawia się pytanie, co właściwie taka instrukcja
ma oznaczać. Metoda findTeamAverageString nie należy do żadnej konkretnej in-
stancji. (Metoda jest statyczna, więc cała klasa PlayerPlus ma tylko jedną metodę
findTeamAverageString). A sposób, w jaki właśnie zmasakrowaliśmy kod, sprawił,
że stare wywołanie pola decFormat bez odniesienia do konkretnego obiektu nie
ma już najmniejszego sensu. Ponownie odwołujemy się z metody statycznej do
pola niestatycznego decFormat. Wstydź się, wstydź się, wstydź się!
Nie wiem jak Ty, ale ja zawsze chętnie poćwiczę sposoby wykorzystania metod
i zmiennych statycznych:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
odpowiedzialnych za opisanie zawartości kompletnej szafy męskiej. Ostatni wiersz
powinien przedstawić całkowity koszt zakupu ubrań znajdujących się w tej szafie.
Jaki będzie wynik działania poniższego kodu? Zapisz sobie kilka prognoz, a następnie
uruchom ten kod, aby sprawdzić, czy Twoje przewidywania się sprawdziły:
import static java.lang.System.out;
out.println();
holder1.value++;
holder2.value++;
MutableInteger.bigValue++;
out.println();
holder1.bigValue++;
out.println("bigValue w zmiennej holder1: " + holder1.bigValue);
out.println("bigValue w zmiennej holder2: " + holder2.bigValue);
}
}
class MutableInteger {
int value;
static int bigValue = 1_000_000;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Eksperymenty ze zmiennymi
Pewnego lata podczas moich studenckich dni często siedziałem na werandzie,
wałęsałem się i rozmawiałem z kimś, kogo właśnie poznałem. Myślę, że miała na
imię Janine. „Skąd jesteś?” — zapytałem, a ona odpowiedziała: „Mars”. Zatrzymała
się, by zobaczyć, czy zadam jakieś dodatkowe pytanie.
Jak się okazało, Janine faktycznie pochodziła z Marsa, z małego miasteczka po-
łożonego około 20 mil na północ od Pittsburgha w stanie Pensylwania. OK, więc
o co mi chodzi? O to, że znaczenie nazwy zależy od kontekstu. Jeśli znajdujesz
się teraz na północ od Pittsburgha i zadasz pytanie: „Jak dostać się stąd do Mar-
sa?”, możesz uzyskać rozsądną, spokojną odpowiedź. Ale jeśli zadasz to samo
pytanie, stojąc na rogu ulicy znajdującej się na Manhattanie, prawdopodobnie
wzbudzisz pewne podejrzenia. (No dobrze, znając Manhattan, ludzie prawdo-
podobnie po prostu Cię zignorują).
Mieszkaniec lokalny: Tu jest Mars, kolego. Jakiej konkretnej części Marsa szukasz?
Ty: Nie, nie mam na myśli Marsa w stanie Pensylwania. Mam na myśli planetę Mars.
Mieszkaniec lokalny: Och, planeta! Cóż, złap pociąg 8:19 na Przylądek Canaveral…
Nie, czekaj — to lokalny pociąg, jedzie przez Wirginię Zachodnią…
Zatem znaczenie nazwy zależy od tego, gdzie jej używamy. Chociaż większość
anglojęzycznych ludzi uważa, że Mars to miejsce o atmosferze z dwutlenku wę-
gla, to jednak niektórzy mieszkańcy Pensylwanii myślą o wszystkich zakupach,
które mogą zrobić w Marsie. Ludzie w Pensylwanii naprawdę mają dwa znaczenia
dla nazwy Mars. W języku Java nazwy te mogą wyglądać tak: Mars i planets.Mars.
class EnglishSpeakingWorld {
String mars = " czerwona planeta";
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
void visitPennsylvania() {
out.println("visitPA działa:");
out.println(mars);
out.println(this.mars);
}
}
RYSUNEK 10.7.
Wynik działania kodu
znajdującego się na
listingach 10.6 i 10.7
Innym sposobem opisania pola mars jest nazwanie go zmienną instancji, ponieważ
zmienna mars (której wartością jest "czerwona planeta") należy do instancji klasy
EnglishSpeakingWorld. Z drugiej strony pola statyczne (takie jak pole playerCount,
pola totalOfAverages i decFormat pokazane na listingu 10.4) można opisać jako
zmienne klasy. Na przykład pole playerCount (listing 10.4) jest zmienną klasy, po-
nieważ jedna kopia tej zmiennej należy do całej klasy PlayerPlus.
Teraz spójrz, proszę, na metodę main na listingu 10.7. Wewnątrz metody main
klasy GetGoing nie możesz napisać takiej instrukcji out.println(mars). Innymi
słowy, takie bezpośrednie odwołanie się do nieokreślonej zmiennej mars zdecy-
dowanie jest nie na miejscu. Zmienna mars, o której wspominam w poprzednim
akapicie, należy do obiektu EnglishSpeakingWorld, a nie do klasy GetGoing.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 10.8.
Wynik działania kodu
przedstawionego na
listingach 10.6 i 10.7
Teraz masz dwie zmienne, obie z nazwą mars. Jedna z tych zmiennych jest polem
o wartości "czerwona planeta". Natomiast druga zmienna jest lokalną zmienną
metody i ma wartość "rodzinne miasto Janine". Do której z tych dwóch zmiennych
będziesz się odwoływać, używając w kodzie słowa mars?
Co będzie, jeśli jesteś już w Pensylwanii i musisz odnieść się do tego 2-księżycowego
czerwonego obiektu dokładnie tak, jak kod znajdujący się wewnątrz metody
visitPennsylvania odwołuje się do pola o wartości "czerwona planeta"? Odpo-
wiedź: użyjesz zapisu this.mars. Słowo this wskazuje dowolny obiekt zawiera-
jący cały ten kod (a nie wybraną metodę wewnątrz kodu). Ten obiekt, instancja
klasy EnglishSpeakingWorld, ma duże, tłuste pole mars, a wartość tego pola to
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
"czerwona planeta". W ten sposób możesz zmusić kod do spojrzenia poza meto-
dę, w której się aktualnie znajdujmy — po prostu użyj słowa kluczowego this
języka Java.
Więcej informacji na temat tego słowa kluczowego this można znaleźć w roz-
dziale 9.
Widzisz więc, że nazwa, która działa dobrze w jednym miejscu, w innym miejscu
może być niezrozumiała i wcale nie działać. Ten problem został zilustrowany
w kodzie dostępnym na listingach 10.8 i 10.9 (to coś więcej niż tylko anegdota
o bankomatach).
class EnglishSpeakingWorld2 {
String mars;
void visitIdaho() {
out.println("działa metoda visitID:");
out.println(mars);
out.println(atomicCity);
}
void visitNewJersey() {
out.println("działa metoda visitNJ:");
out.println(mars);
//out.println(atomicCity); pojawia się błąd
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 10.9. Wywołanie kodu z listingu 10.8
public class GetGoing2 {
e.visitIdaho();
e.visitNewJersey();
}
}
Rysunek 10.9 przedstawia wynik działania kodu pokazanego na listingach 10.8 i 10.9.
Natomiast na rysunku 10.10 została przedstawiona struktura tego kodu. Kod
klasy EnglishSpeakingWorld2 definiuje dwie zmienne. Zmienna mars nie jest de-
klarowana wewnątrz metody, co czyni ją polem. Z kolei zmienna atomicCity jest
zmienną lokalną w metodzie visitIdaho.
RYSUNEK 10.9.
Wynik działania kodu
z listingów 10.8 i 10.9
RYSUNEK 10.10.
Struktura kodu
z listingów 10.8 i 10.9
Podczas czytania listingu 10.8 zwróć uwagę, gdzie poszczególne zmienne mogą
być użyte, a gdzie nie. Próba użycia zmiennej atomicCity wewnątrz metody visit
NewJersey skutkuje pojawieniem się komunikatu o błędzie. Dosłownie komunikat
mówi o tym, że nie można określić znaczenia symbolu (cannot resolve symbol).
Mówiąc obrazowo, wiadomość ta brzmi: „Hej, kolego, Atomic City jest w stanie
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Idaho, a nie w New Jersey”. Technicznie rzecz biorąc, komunikat ten informuje
nas, że zmienna lokalna atomicCity jest dostępna tylko w metodzie visitIdaho,
ponieważ to właśnie tam ją zadeklarowano.
W ten sposób wartość pola mars jest przekazywana z jednej metody do drugiej.
System.out.println(name);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Main2() {
System.out.println(name);
System.out.println(this.name);
}
}
class OtherClass {
OtherClass() {
String name = "Leonard";
System.out.println(name);
System.out.println(Main3.name);
}
}
Main4() {
String name = "Barry";
new YetAnotherClass(this);
}
}
class YetAnotherClass {
YetAnotherClass(Main4 whoCreatedMe) {
String name = "Leonard";
System.out.println(name);
// System.out.println(Main4.name); TAK NIE MOŻNA!
System.out.println(whoCreatedMe.name);
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Przekazywanie parametrów
Metoda może komunikować się z inną częścią programu w języku Java na kilka
różnych sposobów. Jednym z nich jest używanie jej listy parametrów. Korzysta-
jąc z listy parametrów, w momencie wywoływania metody przekazujesz do niej
aktualne informacje.
Listing 10.10 jest przykładem bardzo złego programu. Program powinien dodać
wartość 1 do zmiennej przechowującej liczbę mieszkańców miasteczka Smackover,
ale wcale tak nie działa. Spójrz, proszę, jeszcze raz na listing 10.10 i sprawdź,
dlaczego tak jest.
birth(smackoverARpop);
System.out.println(smackoverARpop);
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Przegląd ośmiu typów podstawowych języka Java znajduje się w rozdziale 4.
RYSUNEK 10.11.
Przekazywanie
wartości — jak to
wygląda pod maską
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zwracanie wyniku
Musisz rozwiązać problem, który wystąpił w kodzie przedstawionym na listingu
10.10. W końcu małe dziecko Kermongoos nie może przejść przez życie nieobjęte
programem śledzenia. Chcąc zarejestrować istnienie tego dziecka, musimy dodać
wartość 1 do wartości zmiennej smackoverARpop. Możemy to zrobić na wiele róż-
nych sposobów, a ten przedstawiony na listingu 10.11 nie należy do najprost-
szych. Mimo to sposób pokazany na tym listingu ma na celu zaprezentować pe-
wien pomysł: zwrócenie wartości z wywołania metody może całkiem dobrze
zastępować nam przekazywanie parametrów. Spójrz, proszę, jeszcze raz na li-
sting 10.11, aby zobaczyć, co mam tutaj na myśli.
smackoverARpop = birth(smackoverARpop);
System.out.println(smackoverARpop);
}
Kod z listingu 10.11 nie zawiera żadnych nowych funkcji (chyba że poprawne
działanie nazwiemy całkiem nową funkcją). Najważniejszą ideą pokazaną na li-
stingu 10.11 jest instrukcja return, która pojawia się również w rozdziale 7. Kod
z listingu 10.11 stanowi jednak ładny kontrast w stosunku do założeń zawartych
w kodzie na listingu 10.10, który musiał zostać odrzucony.
Gdy przekazujesz obiekt do metody, obiekt ten jest przekazywany przez referencję.
Dla Ciebie oznaczać to będzie, że wyrażenia w wywoływanej metodzie mogą zmie-
niać dowolne wartości przechowywane w zmiennych obiektu. Te zmiany wpływają
na wartości, które są widziane przez dowolny kod wywołujący metodę. Tę kwe-
stię zilustrowałem na listingach 10.12 i 10.13.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 10.12. Czym jest miasto?
class City {
int population;
}
Gdy uruchomisz kod znajdujący się na listingach 10.12 i 10.13, otrzymany wynik
wyniesie 2233. To dobrze, ponieważ ten kod zawiera w sobie takie rzeczy jak
operator ++ oraz słowo birth. Chodzi mi o to, że dodanie wartości 1 do zmiennej
aCity.population w metodzie birth faktycznie zmieni nam wartość zmiennej
smackoverAR.population, ponieważ jest ona już znana w metodzie main.
Na rysunku 10.12 widać tylko jedną instancję klasy City, w której znajduje się
zmienna population. Teraz miej oko na ten obiekt, jednocześnie czytając poniższą
sekwencję kroków:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 10.12.
Przekazywanie
wartości przez
referencję. Jak to
działa?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeśli uruchomisz kod z listingu 10.14, otrzymasz liczbę 2233. I dobrze. Kod działa
poprzez nakazanie metodzie doBirth utworzenia nowej instancji klasy City. W no-
wej instancji wartość population wyniesie 2233 (patrz rysunek 10.13).
RYSUNEK 10.13.
Metoda doBirth
tworzy instancję
klasy City
Po wykonaniu metody doBirth instancja klasy City jest zwracana do metody main.
Następnie w metodzie main zwrócona instancja (ta otrzymana od metody doBirth)
jest przypisywana do zmiennej smackoverAR (patrz rysunek 10.14). Teraz smackoverAR
przechowuje referencję zupełnie nowej instancji klasy City — instancji, której
populacja wynosi 2233.
RYSUNEK 10.14.
Nowa instancja klasy
City jest przypisana
do zmiennej
smackoverAR
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W kodzie pokazanym na listingu 10.14 zauważ zgodność typów w wywoływaniu
metody doBirth i w jej wartości zwracanej:
Metoda doBirth zwraca obiekt typu City. W metodzie main obiekt, do którego
referencję zwraca metoda doBirth, jest przypisany do zmiennej smackoverAR
i (zgadłeś) zmienna smackoverAR jest typu City.
Epilog
Dora Kermongoos i jej nowo narodzona córeczka są już bezpieczne, zdrowe i szczę-
śliwie odpoczywają w domu w Smackover w stanie Arkansas.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Wyszukiwanie
Rozdział 11
Używanie tablic
do żonglowania
wartościami
W
itamy w motelu Java! Żadnych wyniosłych boyów hotelowych, żadnych
drogich usług, żadnych głupich dowcipów. Po prostu czysty pokój dwu-
osobowy za dobrą cenę!
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Co dziwne, pokoje w tym motelu są ponumerowane od 0 do 9. Mogę powiedzieć,
że taka numeracja to pewien błąd — coś związanego z pierwotnym projektem
budynku. Ale prawda jest taka, że zaczynając odliczanie od zera, łatwiej będzie
nam opisać przykłady znajdujące się w tym rozdziale.
W każdym razie będziemy próbować śledzić liczbę gości w każdym z tych pokoi.
Z uwagi na fakt, że mamy dziesięć pokoi, możemy pomyśleć o zadeklarowaniu
dziesięciu zmiennych:
guestsInRoomNum0 = diskScanner.nextInt();
guestsInRoomNum1 = diskScanner.nextInt();
guestsInRoomNum2 = diskScanner.nextInt();
// …i tak dalej.
Ten lepszy sposób wymaga zastosowania tablicy. Tablica jest po prostu rzędem
wartości, podobnym do rzędu pokoi w jednopiętrowym motelu. Aby zobrazować
sobie taką tablicę, po prostu wyobraź sobie motel Java:
Jeśli możesz, zapomnij, że dwaj goście w pokoju numer 9 wkładają stosy banknotów
do dużej teczki. Zignoruj też fakt, że goście w pokoju numer 6 przez półtora dnia
nie odeszli od telewizora. Zamiast wszystkich tych szczegółów zobacz tylko liczby.
W każdym pokoju skoncentruj się na liczbie gości. (Jeżeli taka wizualizacja sprawia Ci
problemy, to spójrz na rysunek 11.1).
W terminologii z tego rozdziału cały rząd pokoi nazywany jest tablicą. Każdy
pokój w tej tablicy nazywany jest elementem tej tablicy (czasami używa się też
pojęcia komponent tablicy). Z każdym takim elementem powiązane są dwie liczby:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 11.1.
Szybkie
naszkicowanie pokoi
w motelu Java
Użycie w tym miejscu tablicy uchroni Cię przed tymi wszystkimi nonsensownymi
powtórzeniami w przykładowym kodzie pokazanym na początku tego podroz-
działu. Na przykład, aby zadeklarować tablicę z dziesięcioma wartościami, możesz
napisać jedną dość krótką instrukcję:
Jeśli jesteś szczególnie gadatliwy, możesz rozwinąć tę instrukcję, tak aby stała
się ona dwoma oddzielnymi instrukcjami:
int guests[];
guests = new int[10];
W obu tych fragmentach kodu zwróć uwagę na użycie liczby 10. Liczba ta mówi
komputerowi, aby tablica gości guests zawierała po dziesięć elementów. Każdy
element tablicy ma własną nazwę. Element początkowy tablicy nosi nazwę
guests[0], następny ma nazwę guests[1] i tak dalej. Ostatni z dziesięciu elementów
tej tablicy będzie nosił nazwę guests[9].
Podczas tworzenia tablicy zawsze określa się liczbę jej elementów. Indeksy ta-
blicy zaczynają się od 0 i kończą się liczbą o jeden mniejszą niż całkowita liczba
elementów tej tablicy.
Fragmenty, które tutaj pokazuję, dają nam dwie możliwości utworzenia tablicy.
Pierwszy sposób wykorzystuje jeden wiersz kodu. Drugi sposób będzie składał się
z dwu wierszy kodu. Jeśli wybierzesz wersję jednowierszową, to możesz umieścić
ten wiersz wewnątrz lub na zewnątrz metody. Wybór będzie już należał do Ciebie.
Z drugiej strony, jeśli użyjesz dwóch oddzielnych wierszy, drugi z nich, guests = new
int[10], powinien znajdować się wewnątrz metody.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W deklaracji tablicy możesz umieścić nawiasy kwadratowe przed lub po nazwie
zmiennej. Innymi słowy, możesz napisać int guests[] lub int[] guests. Kom-
puter utworzy tę samą zmienną guests bez względu na to, jakiej formy użyjesz.
int guests[];
guests = new int[10];
RYSUNEK 11.2.
Dwa kroki
w tworzeniu tablicy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Przechowywanie wartości
Po utworzeniu tablicy możesz już umieścić wartości, zapisując je do elementów
tablicy. Na przykład, jeśli będziesz chciał zapisać fakt, że w pokoju numer 6
znajduje się czterech gości, to aby umieścić wartość 4 w elemencie z indeksem 6,
musisz napisać instrukcję guests[6] = 4.
Nasz interes zaczyna już kiełkować. Duży autobus podjeżdża do motelu. Na boku
autobusu widnieje napis „Arka Noego”. Tym autobusem przyjeżdża 25 par, każda
z nich idzie, truchta, biegnie lub skacze do małego biura motelu. Niestety, tylko
10 par może pozostać w motelu Java, ale nie będzie to wielkim problemem, po-
nieważ możemy wysłać pozostałe 15 par dalej, pokazując im drogę do starego
hotelu C-Side.
W każdym razie, aby zarejestrować dziesięć par w motelu Java, umieszczasz parę
(dwójkę gości) w każdym z dziesięciu pokoi. Po utworzeniu tablicy możesz sko-
rzystać z indeksowania tej tablicy i napisać pętlę for, taką jak ta:
Ta pętla zastępuje dziesięć instrukcji przypisania. Zauważ, jak licznik pętli prze-
chodzi od liczby 0 do 9. Porównaj to z rysunkiem 11.2 i zapamiętaj, że indeksy
tablicy przechodzą od zera do liczby o jeden mniejszej niż całkowita liczba ele-
mentów tej tablicy.
Biorąc jednak pod uwagę, jak działa ten świat, Twoi goście nie zawsze będą
przychodzić równiutko w parach i niestety będziesz musiał wypełnić każdy pokój
inną liczbą gości. Prawdopodobnie informacje o pokojach i gościach przecho-
wujesz w bazie danych. Jeśli tak robisz, to i tak będziesz mógł wykorzystać pętlę,
przechodząc po elementach tablicy i pobierając liczbę gości w poszczególnych
pokojach motelu. Nasz kod wykonujący takie zadanie może wyglądać następująco:
Ze względu na fakt, że do tej pory książka ta nie poruszała tematu baz danych
(szerzej zagadnienie to będzie omawiane w rozdziale 17.), możemy teraz odczy-
tywać liczbę gości z pliku zawierającego zwykły tekst. Taki przykładowy plik o na-
zwie GuestList.txt pokazano na rysunku 11.3.
RYSUNEK 11.3.
Plik o nazwie
GuestList.txt
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Po utworzeniu pliku możemy wykorzystać klasę Scanner, aby pobrać z niego po-
szczególne wartości. Kod takiego programu został pokazany na listingu 11.1, na-
tomiast wynik jego działania można znaleźć na rysunku 11.4.
out.println("Pokój\tGości");
diskScanner.close();
}
}
RYSUNEK 11.4.
Wynik działania
programu
przedstawionego na
listingu 11.1
Kod przedstawiony na listingu 11.1 zawiera dwie pętle for — pierwsza pętla od-
czytuje liczbę gości, natomiast druga pętla wypisuje liczbę gości.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Każda tablica ma wbudowane pole podające jej długość. Długość tablicy stanowi
liczbę znajdujących się w niej elementów. To dlatego wypisując na listingu 11.1
wartość guests.length, otrzymasz liczbę 10.
\b znak backspace
\t znak poziomego tabulatora
\n znak przejścia do nowego wiersza
\f znak przejścia do nowej strony
\r znak powrotu karetki
\" znak podwójnego cudzysłowu (")
\' znak pojedynczego cudzysłowu (')
\\ znak lewego ukośnika (\)
Listing 11.2 przedstawia nową wersję kodu do wypełnienia tablicy. Wynik jego
działania jest taki sam jak wynik działania programu przedstawionego na listingu
11.1. (Chodzi o to, co widać na rysunku 11.4). Jedyna różnica między listingami
11.1 i 11.2 to pogrubiony tekst na listingu 11.2. Właśnie ten pogrubiony tekst jest
inicjalizatorem tablicy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 11.2. Użycie inicjalizatora tablicy
import static java.lang.System.out;
out.println("Pokój\tGości");
Materiał zawarty w tym punkcie dotyczy wersji Java 5.0 lub nowszej. Oznacza to,
że przykłady z tego podrozdziału nie będą działać ze starszymi wersjami Javy —
takimi jak 1.3, 1.4 i tak dalej. Więcej informacji o numeracji wersji Javy można
znaleźć w rozdziale 2.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
out.println("Pokój\tGoście");
Wyniki działania programów z listingów 11.1 i 11.3 są takie same. Zostały pokazane
na rysunku 11.4.
Rozszerzona pętla for w języku Java wymaga słowa ostrzeżenia. Za każdym razem
w pętli zmienna przechodząca przez zakres wartości przechowuje kopię wartości
z oryginalnego zakresu, ale nie wskazuje elementów samego zakresu.
out.println();
for (int numGuests : guests) {
out.print(numGuests + " ");
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zmienna numGuests przyjmuje wartości przechowywane w tablicy guests, ale in-
strukcja numGuests += 1 nie będzie zmieniać tych wartości. Wynik działania tego
kodu wygląda następująco:
1 1 1 1 1 1 1 1 1 1
0 0 0 0 0 0 0 0 0 0
Napisz program, który będzie w tablicy przechowywał pięć wartości typu double,
a następnie wyświetli średnią z wartości zawartych w tej tablicy.
Szukanie
Siedzisz za biurkiem w motelu Java. I popatrz! Zbliża się grupa pięciu osób. Ci
ludzie chcą wynająć pokój, więc potrzebujesz programu, który sprawdzi, czy mamy
jeszcze wolny pokój. Jeśli taki się znajdzie, to oprogramowanie zmodyfikuje plik
GuestList.txt (patrz rysunek 11.3), zastępując liczbę 0 liczbą 5. Na szczęście oprogra-
mowanie znajduje się już na naszym dysku twardym. Zostało one pokazane na
listingu 11.4.
roomNum = 0;
while (roomNum < 10 && guests[roomNum] != 0) {
roomNum++;
}
if (roomNum == 10) {
out.println("Przepraszam, ale nie ma wo nych miejsc");
} else {
out.print("Ile osób będzie w pokoju ");
out.print(roomNum);
out.print("? ");
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Scanner keyboard = new Scanner(System.in);
guests[roomNum] = keyboard.nextInt();
keyboard.close();
RYSUNEK 11.5.
Znaleziono
wolny pokój
RYSUNEK 11.6.
Wypełnienie
ostatniego
pustego pokoju
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 11.7.
Przepraszam, brak
wolnych pokoi
Na listingu 11.4 warunek roomNum < 10 && guests[roomNum] != 0 może być naprawdę
trudny do zrozumienia. Jeśli poprzestawiasz elementy tego warunku i napiszesz
tak: guests[roomNum] != 0 && roomNum < 10, możesz wpaść w niezłe kłopoty.
Szczegółowe informacje znajdziesz na stronie tej książki (https://users.drew.edu/
bburd/JavaForDummies/).
Zapisywanie do pliku
Kod z listingu 11.4 wykorzystuje sztuczki pokazane w innych rozdziałach i pod-
rozdziałach tej książki. Jedyną nowością w tym kodzie jest użycie klasy PrintStream
służącej do zapisywania danych do pliku na dysku. Pomyśl sobie o dowolnym
przykładzie pokazanym w tej książce, który wywołuje System.out.print, out.println
lub ich inne warianty. Co tak naprawdę dzieje się, gdy wywołujesz jedną z tych
metod?
Ta rzecz o nazwie System.out jest obiektem. Obiekt jest zdefiniowany w interfejsie API
Java. W rzeczywistości System.out jest instancją klasy o nazwie java.io.PrintStream
(lub po prostu PrintStream dla bliskich przyjaciół). Każdy obiekt utworzony
z klasy PrintStream ma metody o nazwie print i println. Podobnie jak każdy obiekt
Account (pokazany wcześniej na listingu 7.3) ma metodę display i podobnie jak
obiekt DecimalFormat na listingu 10.1 ma metodę format, tak obiekt klasy PrintStream
o nazwie out będzie miał metody print i println. Stosując w programie zapis
System.out.println, wywołujesz metodę, która należy do instancji klasy PrintStream.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
No dobrze i co z tego? No cóż, obiekt System.out zawsze dotyczy jakiegoś obszaru
tekstowego na ekranie komputera. Jeśli utworzysz własny obiekt klasy PrintStream
i sprawisz, że ten obiekt zostanie powiązany z plikiem na dysku twardym, to
powstały w ten sposób obiekt będzie reprezentował w programie ten plik. Gdy wy-
wołasz metodę print swojego obiektu, zapiszesz pewien tekst w pliku na dysku
twardym.
listOut.print(" ");
oznacza, że mówisz językowi Java, aby ten zapisał tekst do pliku GuestList.txt na
dysku twardym.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Scanner diskScanner = new Scanner(new File("GuestList.txt")); // Konstruowanie
for (roomNum = 0; roomNum < 10; roomNum++) {
guests[roomNum] = diskScanner.nextInt(); // Odczytywanie
}
diskScanner.close(); // Zamykanie
Wykonuję taki szybki taniec z wejściem i wyjściem, ponieważ mój program uży-
wa pliku GuestList.txt dwa razy — pierwszy raz do odczytania liczb i drugi raz do
ich zapisania. Jeśli nie będę tutaj ostrożny, dwa niezależne użycia pliku GuestList.txt
mogą ze sobą kolidować. Rozważmy następujący program:
guests[0] = diskScanner.nextInt();
listOut.print(5);
diskScanner.close();
listOut.close();
}
}
Aby uniknąć tej katastrofy, na listingu 11.4 starannie rozdzielam dwa zastosowa-
nia pliku GuestList.txt. W górnej części listingu konstruuję zmienną diskScanner,
następnie czytam dane z pliku GuestList.txt, a w następnej kolejności zamykam
obiekt diskScanner. Później, już pod koniec kodu, konstruuję zmienną listOut,
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
następnie zapisuję dane w nowym pliku GuestList.txt i na koniec zamykam obiekt
listOut. Wszystko będzie działać poprawnie, ponieważ zapisywanie jest tutaj
całkowicie oddzielone od odczytywania.
Zmienna keyboard z listingu 11.4 nie odnosi się do pliku GuestList.txt, dlatego nie
koliduje z innymi zmiennymi obsługującymi wejście lub wyjście. W związku z tym
stosowanie mojej zwyczajowej procedury — umieszczenia instrukcji keyboard = new
Scanner(System.in) na początku programu i umieszczenia instrukcji keyboard.close()
na końcu programu — nie spowoduje tutaj żadnych problemów. Mimo to, aby
poprawić czytelność kodu z listingu 11.4 i poprawić jego strukturę, konstruktor
zmiennej keyboard i wywołanie metody close umieszczam w pobliżu wywołania
metody keyboard.nextInt.
Użyj mojej klasy DummiesFrame (pochodzącej z rozdziału 7.), aby utworzyć pro-
gram z GUI bazujący na pomysłach z listingu 11.4. W Twoim programie ramka
powinna mieć tylko jeden wiersz na dane wejściowe. Jeśli pokój numer 3 jest
wolny, to etykieta w pierwszym wierszu ramki otrzyma tekst: Ile osób będzie
mieszkać w pokoju numer 3?. Jeśli użytkownik wpisze w tym wierszu liczbę 5, a na-
stępnie kliknie przycisk, to program zapisze podaną liczbę osób w pokoju numer 3.
Tablice obiektów
Motel Java został ponownie otwarty, teraz z ulepszonym oprogramowaniem do
rejestracji gości! Ludzie, którzy przygotowali pierwszą część tego rozdziału, drapią
się po głowie, szukając najlepszych sposobów na poprawienie swoich usług.
Teraz dzięki pomysłom z programowania obiektowego zaczęli rozmyślać na temat
klasy Room.
RYSUNEK 11.8.
Kolejna abstrakcyjna
prezentacja pokoi
w motelu Java
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 11.5 przedstawia kod opisujący klasę Room. Zgodnie z obietnicą każda in-
stancja klasy Room ma trzy pola: guests, rate oraz smoking. (Wartość false w polu
smoking typu boolean będzie oznaczała, że jest to pokój dla niepalących). Ponadto
cała klasa Room ma pole statyczne o nazwie currency. Na moim komputerze w Sta-
nach Zjednoczonych obiekt currency sprawia, że ceny za pokój wyglądają jak
kwoty w dolarach.
Kod z listingu 11.5 ma kilka interesujących i dziwnych zapisów, ale nie będę ich
tutaj opisywał, dopóki nie zobaczysz całego kodu w akcji. Dlatego w tym momencie
od razu przechodzę do kodu, który wywołuje kod z listingu 11.5. Po przeczytaniu
o tablicach pokoi (pokazanych na listingu 11.6) sprawdź mój opis tych dziwnych
elementów z listingu 11.5.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
trzy dni wolnego od pracy. Niech ta osoba usiądzie z Tobą i pociesza Cię przez
pełne 72 godziny, gdy będziesz powstrzymywać się od palenia. Gdy nikotyna opuści
Twoje ciało, możesz chwilowo być niepoczytalny, ale ostatecznie wyjdzie Ci to na
dobre. A Twój przyjaciel poczuje się jak prawdziwy bohater.
Room rooms[];
rooms = new Room[10];
out.println("Pokój\tGości\tCena\tDla palaczy?");
for (int roomNum = 0; roomNum < 10; roomNum++) {
out.print(roomNum);
out.print("\t");
rooms[roomNum].writeRoom();
}
diskScanner.close();
}
}
Mów, co chcesz, o kodzie z listingu 11.6. Jeżeli o mnie chodzi, to mogę wskazać
tylko jeden problem w całym tym listingu. Możesz spytać, o jaki problem chodzi?
Cóż, aby utworzyć tablicę obiektów — w przeciwieństwie do tablicy składającej
się z wartości pierwotnych — musisz zrobić trzy rzeczy: Utworzyć zmienną ta-
blicową, utworzyć tablicę, a następnie skonstruować każdy pojedynczy obiekt
z tej tablicy. Różni się to trochę od tworzenia tablicy z wartościami typu int lub
od tablicy zawierającej inne wartości typu pierwotnego. Kiedy tworzysz tablicę
wartości pierwotnych, musisz wykonać tylko dwie pierwsze z tych trzech rzeczy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 11.9.
Plik danych Room
RYSUNEK 11.10.
Uruchomienie kodu
z listingu 11.6
Aby zrozumieć to wszystko, zaglądaj na listing 11.6 i rysunek 11.11, czytając po-
niższe punkty:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 11.11.
Poszczególne kroki
w tworzeniu tablicy
obiektów
Choć technicznie rzecz biorąc, nie jest to uznawane za krok w procesie tworzenia
tablic, to jednak na koniec musisz wypełniać wartościami pola wszystkich
obiektów. Na przykład przy pierwszym obiegu pętli wywołanie metody readRoom
wygląda tak: rooms[1].readRoom(diskScanner). Oznacza to: „Wczytaj dane z pliku
RoomList.txt do pól obiektu rooms[1] (do pól guests, rate i smoking)”. Przy każdym
następnym obiegu pętli program będzie tworzyć nowy obiekt i wczytywać dane
do jego pól.
Możesz połączyć ze sobą pewne kroki, tak jak podczas tworzenia tablic wartości
pierwotnych. Na przykład możesz za jednym zamachem wykonać dwa pierwsze
kroki:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
DecimalFormat. Jednak na listingu 11.5 wyświetlam kwotę waluty, korzystając
przy tym z klasy NumberFormat z jej metodą getCurrencyInstance.
Oba listingi używają metody format. Na końcu tego procesu piszesz coś takiego,
jak instrukcja currency.format(rate)lub instrukcja decFormat.format(average),
a kompilator Javy wykona całą pracę za Ciebie.
Operator warunkowy
Kod z listingu 11.5 korzysta z interesującego gadżetu nazywanego operatorem wa-
runkowym. Ten operator przyjmuje trzy wyrażenia i zwraca wartość tylko jednego
z nich. To coś w stylu miniinstrukcji if. Kiedy używasz operatora warunkowego,
wygląda to mniej więcej tak:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
warunekDoPrzetestowania ? wyrażenie1: wyrażenie2
Komputer sprawdza tutaj, czy zmienna smoking ma wartość true. Jeśli tak jest, to
całe 3-częściowe wyrażenie zwróci pierwszy ciąg znaków — „tak”. Jeśli nie, to
całe wyrażenie zwróci drugi ciąg znaków — „nie”.
Jak uczyć się języka Java? Dokładnie w taki sam sposób, w jaki dostaniesz się na
Konkurs Chopinowski — Ćwiczyć! Ćwiczyć! I jeszcze raz ćwiczyć!
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wiersz do zastąpienia (lub -1, aby wyjść): 1
Wpisz nowy wiersz: Który poznawał obiekty i klasy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Argumenty wiersza poleceń
Dawno, dawno temu większość programistów korzystała z tekstowego interfejsu
programistycznego. Chcąc uruchomić program Displayer zaprezentowany w roz-
dziale 3., nie wybierali opcji Uruchom z menu w wyszukanym zintegrowanym
środowisku programistycznym. Zamiast tego wpisywali polecenia w prostym
oknie, zwykle z białym tekstem na czarnym tle. Na rysunku 11.12 przedstawiam
takie właśnie okno. W tym oknie wpisuję słowa java Displayer, a komputer od-
powiada wynikiem działania mojego programu, czyli wypisuje na ekranie tekst:
Pokochasz język Java!.
RYSUNEK 11.12.
Ale nuda!
RYSUNEK 11.13.
Po uruchomieniu
MakeRandom
NumsFile wpisujesz
dodatkowe
informacje
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Następne pytanie będzie brzmiało: „Skąd program Java wie, że za każdym razem
powinien pobierać dodatkowe informacje?”. Odkąd zacząłeś pracować z językiem
Java, widziałeś zapis String args[] w nagłówku każdej metody main. No cóż,
najwyższy czas, aby dowiedzieć się, o co w tym wszystkim chodzi. Parametr args[]
jest tablicą wartości typu String. Te wartości nazywane są argumentami wiersza
poleceń.
if (args.length < 2) {
System.out.println("Sposób użycia: MakeRandomNumsFile nazwa_pliku
liczba");
System.exit(1);
}
printOut.close();
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeśli dany program oczekuje pewnych argumentów w wierszu poleceń, to nie
będzie można uruchomić go w ten sam sposób jak większość innych programów
z tej książki. Sposób wpisywania argumentów wiersza poleceń do programu za-
leży od używanego środowiska IDE — Eclipse, NetBeans lub dowolnego innego.
Dlatego na stronie tej książki (https://users.drew.edu/bburd/JavaForDummies/) znaj-
dują się instrukcje dotyczące wpisywania argumentów w różnych środowiskach IDE.
Gdy kod na listingu 11.7 zaczyna działać, tablica args wypełniana jest wartościa-
mi. W przypadku pokazanym na rysunku 11.13 element tablicy args[0] automa-
tycznie przyjmuje wartość "MyNumberedFile.txt", a args[1] automatycznie otrzy-
muje wartość "5". A zatem instrukcje przypisania w programie mają następujące
znaczenie:
RYSUNEK 11.14.
Plik otrzymany
w wyniku działania
kodu z listingu 11.7
Gdy już uruchomimy kod z listingu 11.7, to gdzie na dysku twardym będzie moż-
na znaleźć nowy plik MyNumberedFile.txt? Odpowiedź zależy od wielu różnych
rzeczy, więc nie chcę się tutaj angażować w jedną konkretną odpowiedź. Jeśli
używasz IDE z programami podzielonymi na projekty, to nowy plik znajduje się
gdzieś w folderze projektu. Tak czy inaczej możesz zmienić kod z listingu 11.7 tak,
aby podać pełną nazwę ścieżki — nazwę taką jak "c:\\Users\\MojaNazwa\\
Documents\\MyNumberedFile.txt" lub "/Users/MojaNazwa/Documents/MyNumbered
File.txt".
Zauważ, że każdy argument wiersza poleceń (na listingu 11.7) jest wartością typu
String. Patrząc na argument args[1], nie widzisz liczby 5, ale ciąg znaków za-
wierający cyfrę — "5". Niestety, nie możesz użyć tej cyfry do wykonywania ob-
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
liczeń. Aby uzyskać wartość typu int z ciągu znaków "5", musisz zastosować
metodę parseInt. (Ponownie spójrz tu na listing 11.7).
Metoda parseInt została zdefiniowana wewnątrz klasy o nazwie Integer. Tak więc,
aby wywołać metodę parseInt, poprzedzasz nazwę parseInt słowem Integer. Klasa
Integer ma wszystkie rodzaje przydatnych metod do robienia różnych rzeczy
z wartościami typu int.
W Javie Integer jest nazwą klasy, a int jest nazwą typu podstawowego. Te dwie
rzeczy są ze sobą powiązane, ale nie są tym samym. Klasa Integer udostępnia
metody i inne narzędzia do obsługi wartości typu int.
Co robić w takiej sytuacji? Na listingu 11.7 sprawdzam wielkość tablicy args. Po-
równuję wartość pola args.length z liczbą 2. Jeśli tablica args zawiera mniej niż
dwa elementy, to wyświetlam komunikat na ekranie i wychodzę z programu.
Rysunek 11.15 przedstawia wynik działania tego kodu.
RYSUNEK 11.15.
Kod na listingu 11.7
informuje, w jaki
sposób należy go
uruchomić
Pomimo sprawdzenia wartości pola args.length na listingu 11.7 kod nadal nie
jest odporny na awarie. Jeśli użytkownik wpisze słowo pięć zamiast cyfry 5, to
program „przewróci się” z komunikatem o wyjątku NumberFormatException (nie-
prawidłowy format liczby). Drugi argument wiersza poleceń nie może być słowem.
Musi być liczbą (i to liczbą całkowitą). Mogę dodać instrukcje do listingu 11.7,
aby kod stał się bardziej kuloodporny; sprawdzenie wyjątku NumberFormatException
jest lepiej opisane w rozdziale 13.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Kiedy pracujesz z argumentami wiersza poleceń, możesz wprowadzić wartość
typu String zawierającą w sobie spację. Wystarczy ująć całą tę wartość w cudzy-
słów. Na przykład możesz uruchomić kod z listingu 11.7 z argumentami "Mój
wielki gruby plik.txt" 7.
Nasze omawianie tematu tablic dobiega końca. Zanim jednak rozstaniesz się
z tematem tablic, zastanów się, proszę, nad taką sprawą: tablica to szereg ele-
mentów, a nie każdy rodzaj takich elementów pasuje tylko do jednego wiersza.
Wróćmy do pierwszych przykładów z tego rozdziału — tych dotyczących mote-
lu. Pokoje motelowe, ponumerowane od 0 do 9, są umieszczone w jednej linii.
Ale co będzie, jeśli rozwiniesz swój interes? Kupisz duży hotel z 50 piętrami i ze
100 pokojami na każdym z tych pięter. Teraz nasze dane mają kształt prostokąta.
Masz 50 wierszy, a każdy wiersz zawiera 100 pozycji. Oczywiście, możesz myśleć
o pokojach tak, jakby były w jednym długim rzędzie, ale dlaczego miałbyś to robić?
Co powiesz na użycie dwuwymiarowej tablicy? Jest to kwadratowa tablica, w której
każdy element ma dwa indeksy: numer wiersza i numer kolumny. Niestety, w tej
książce nie mam miejsca, aby pokazać Ci dwuwymiarowe tablice (a poza tym nie
mogę sobie pozwolić na kupienie tak dużego hotelu). Ale jeśli odwiedzisz stronę tej
książki (https://users.drew.edu/bburd/JavaForDummies/), możesz się wiele na ten
temat dowiedzieć.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
324 CZĘŚĆ IV Sprytne techniki Javy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 12
Korzystanie z kolekcji
i strumieni (gdy tablice
nie są wystarczające)
R
ozdział 11. dotyczył tablic. Dzięki tablicy możesz zarządzać wieloma rzeczami
naraz. W programie zarządzania hotelem możesz kontrolować wszystkie
pokoje, tym samym szybko znaleźć liczbę osób w danym pokoju lub znaleźć
jeden z jeszcze wolnych pokoi.
Jednak tablice nie zawsze są dobrym wyjściem. W tym rozdziale dowiesz się, w ja-
kich sytuacjach tablice będą niewystarczające i jak kolekcje mogą uratować sytuację.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Poznawanie ograniczeń tablic
Tablice są bardzo ładne, ale mają poważne ograniczenia. Wyobraź sobie, że prze-
chowujesz nazwy klientów w określonej kolejności. Twój kod zawiera tablicę,
a tablica ma miejsce na 100 nazw:
I wszystko jest dobrze, dopóki pewnego dnia nie pojawi się klient o numerze 101.
Po uruchomieniu programu wprowadzasz dane dla tego klienta o numerze 101,
desperacko oczekując, że tablica ze 100 komponentami może się rozszerzyć, aby
dopasować się do Twoich rosnących potrzeb.
No i pech. Tablice się nie rozszerzają. Twój program ulega awarii z komunikatem
o wyjątku ArrayIndexOutOfBoundsException (indeks tablicy jest poza jej granicami).
Pewnego dnia pojawia się nowy klient. Jako że Twoi klienci są przechowywani w ta-
blicy w pewnym porządku (alfabetycznie według nazwiska, numerycznie według
numer ubezpieczenia społecznego lub jakkolwiek inaczej), chcesz wcisnąć tego
klienta na właściwą pozycję swojej tablicy. Problem polega na tym, że ten klient
powinien znaleźć się na samym początku tablicy, w elemencie z indeksem 7. Co
się wtedy dzieje?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
ter ma uczucia, prawdopodobnie lubi ten rodzaj pracy). Ale kiedy przemieszczasz
się po tych wszystkich nazwach, marnujesz czas, marnujesz energię i marnujesz
wszelkiego innego rodzaju zasoby.
API języka Java ma kilka klas znanych jako klasy kolekcji. Każda klasa kolekcji ma
metody do przechowywania zbiorów wartości, a metody te używają pewnych
sprytnych sztuczek. Dla Ciebie najważniejsze tutaj będzie to, że niektóre klasy
kolekcji radzą sobie z problemami poruszonymi w poprzednim podrozdziale tak
skutecznie, jak tylko jest to możliwe. Jeśli masz do czynienia z takimi proble-
mami podczas pisania kodu, możesz użyć tych klas kolekcji i wykorzystać ich
metody. Zamiast martwić się o klienta, którego nazwa powinna znaleźć się na
pozycji 7, możesz po prostu wywołać metodę add. Metoda wstawia nazwę na wy-
braną pozycję i samodzielnie radzi sobie ze wszelkimi efektami ubocznymi.
W najlepszych okolicznościach wstawianie jest bardzo wydajne, natomiast w naj-
gorszych możesz mieć pewność, że kod zrobi wszystko, co będzie w jego mocy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
while (diskScanner.hasNext()) {
people.add(diskScanner.nextLine());
}
people.remove(0);
people.add(2, "Jim Newton");
for (String name : people) {
out.println(name);
}
diskScanner.close();
}
}
RYSUNEK 12.1.
Kilka nazwisk w pliku
RYSUNEK 12.2.
Kod na listingu 12.1
zmienia
niektóre nazwy
Wywoływanie metody remove lub add sprawia, że dzieją się niezwykle interesu-
jące rzeczy. Zmienna o nazwie people przechowuje obiekt klasy ArrayList. Gdy
na rzecz tego obiektu wywołujesz metodę remove
people.remove(0);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Tym samym na liście pozostaje tylko osiem nazwisk, ale już następna instrukcja:
wstawia nowe nazwisko na pozycję numer 2. (Po usunięciu Barry’ego pozycja 2 jest
pozycją zajmowaną przez Harry’ego Spoonswaglera, więc Harry przesuwa się na
pozycję 3, a Jim Newton staje się człowiekiem numer 2).
Zauważ, że obiekt klasy ArrayList ma dwie różne metody add. Metoda, za po-
mocą której dodaliśmy Jima Newtona do listy, ma dwa parametry: numer pozycji
i wartość do dodania. Druga metoda add
people.add(diskScanner.nextLine());
Ostatnie kilka wierszy kodu z listingu 12.1 to rozszerzona pętla for. Podobnie jak
pętla z listingu 11.3, ta (listing 12.1) ma następującą postać:
Począwszy od wersji Java 5.0, każda klasa kolekcji jest typem generycznym. To
brzydko brzmiące słowo oznacza, że każda deklaracja kolekcji powinna zawierać
pewne elementy w nawiasach ostrych, takie jak <String>. To, co zostanie umiesz-
czone pomiędzy nawiasami < i >, mówi kompilatorowi Javy, jakie wartości może
przechowywać nowa kolekcja. Na przykład w kodzie pokazanym na listingu 12.1
słowa ArrayList<String> people oznaczają, że zmienna people jest zbiorem cią-
gów znaków. Oznacza to, że w liście people znajdują się obiekty typu String (nie
są to obiekty Room ani obiekty Account, ani nawet obiekty Employee; nic oprócz
obiektów typu String).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Typów generycznych nie można używać w żadnej innej wersji Javy wydanej
przed wersją Java 5.0, a dodatkowo kod z listingu 12.1 użyty we wcześniejszych
wersjach języka Java niż wersja Java 7 zrobi wielkie bum. Więcej informacji na
temat typów generycznych znajdziesz w pobliskiej ramce „Wszystko o typach
generycznych”. Natomiast aby zapoznać się z numeracją wersji języka Java, zaj-
rzyj do rozdziału 2.
people.add(new Room());
to kompilator odrzuci taki kod, ponieważ klasa Room (utworzona w rozdziale 11.)
nie jest typu String. (Takie odrzucenia zdarzają się nawet wtedy, gdy kompilator
ma dostęp do kodu klasy Room — kodu z rozdziału 11.). Natomiast instrukcja:
people.add("George Gow");
będzie już w porządku. Tekst "George Gow" ma typ String, dlatego kompilator
uśmiechnie się radośnie.
Użycie czegoś takiego jak <String> było nowością w Javie 5.0. W starej wersji języka
Java napiszesz to tak:
ArrayList people = new ArrayList();
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
ArrayList things = new ArrayList();
things.add(new Account());
Account myAccount = things.get(0);
//NIE UŻYWAJ TEGO. TO ZŁY KOD.
Wersja Java 5.0 wprowadziła typy generyczne. Jednak wkrótce po narodzinach Javy
5.0 programiści zauważyli, jak niezgrabny może być kod generyczny. W końcu moż-
na tworzyć typy generyczne wewnątrz typów generycznych. Obiekt typu ArrayList
może zawierać kilka tablic, z których każda może być tablicą typu ArrayList. Można
zatem napisać:
ArrayList<ArrayList<String>[]> mess = new ArrayList<ArrayList<String>[]>();
Wszystkie powtórzenia w tej deklaracji zmiennej mess przyprawiają mnie o ból gło-
wy! Aby uniknąć tego brzydkiego zapisu, do wersji Java 7 i nowszych wprowadzono
operator <>. Operator ten mówi programowi, aby ponownie używał wszelkich sza-
lenie skomplikowanych rzeczy, które umieściłeś w poprzedniej części deklaracji ge-
nerycznej. W tym przykładzie operator <> informuje kompilator Javy, aby ponownie
używał deklaracji <ArrayList<String>[]>, nawet jeśli deklaracja ta występuje tylko raz.
Oto jak wygląda uproszczony kod w Javie 7:
ArrayList<ArrayList<String>[]> mess = new ArrayList<>();
W Javie 7 i nowszych wersjach możesz napisać jedną z tych deklaracji zmiennej mess:
oryginalną, paskudną deklarację z dwoma wystąpieniami ArrayList<String>[] lub
uproszczoną deklarację z operatorem <> i tylko jednym wystąpieniem Array
List<String>[].
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Tak, usprawniony kod jest wciąż skomplikowany. Ale bez tego całego powtórzenia
ArrayList<String>[] będzie zapewne usprawniony i mniej kłopotliwy. Operator <>
w Javie 7 eliminuje prawdopodobieństwo niepoprawnego skopiowania wycinka ko-
du i wprowadzenia dużego błędu.
Klasy opakowujące
W rozdziale 4. zwracam uwagę na to, że Java korzysta z dwóch rodzajów typów:
typów pierwotnych i typów referencyjnych. (Nie czuj się winny, jeśli nie prze-
czytałeś tych podrozdziałów albo ich nie pamiętasz. Wszystko jest w porządku).
Takie typy jak int, double, char i boolean są typami pierwotnymi, natomiast takie
typy jak String, JFrame, ArrayList i Account to typy referencyjne.
ponieważ int jest typem pierwotnym. Jeśli więc chcesz przechowywać wartości
takie jak 3, 55 i 21 w tablicy ArrayList, co musisz zrobić? W takiej sytuacji w tablicy
ArrayList zamiast wartości int przechowuje się wartości typu Integer:
Klasa Integer ma wiele metod do obsługi wartości typu int, takich jak na przy-
kład parseInt. Klasa ta ma również pola takie jak MAX_VALUE i MIN_VALUE, które
oznaczają największe i najmniejsze wartości, jakie mogą mieć zmienne typu int.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Klasa Integer opakowuje typ podstawowy int, oferując przydatne metody i war-
tości. Ponadto można utworzyć instancję typu Integer, która opakowuje poje-
dynczą wartość typu int:
W tym wierszu kodu zmienna myInteger otrzymuje jedną liczbę całkowitą: 42.
Opakowanie typu int o wartości 42 w obiekt klasy Integer jest „czymś w rodzaju
użycia dużej ilości panierki do ukrycia brukselki. To sprawia, że wartość 42 jest
bardziej strawna dla wybrednych smakoszy, takich jak kolekcje”.
Oto program, który przechowuje pięć wartości typu Integer w tablicy ArrayList:
import java.util.ArrayList;
W kodzie tym zauważ wywołania takie jak list.add(85), które mają parametry
wartości int. W tym momencie mały Bartuś jest już bardzo podekscytowany i mó-
wi: „Patrz, mamo! Dodałem pierwotny typ int o wartości 85 do mojej tablicy
ArrayList!”. Nie, nie, Bartusiu. Naprawdę nie tak się to dzieje.
W tym kodzie zmienna list przechowuje wartości typu Integer, a nie wartości
typu int. Pierwotna wartość int jest bardzo podobna do instancji klasy Integer,
ale nie są one identyczne.
To wszystko, co się tutaj dzieje, nazywa się autoboksingiem. Przed wersją Java 5.0
trzeba było pisać tak:
list.add(new Integer(85));
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
jeśli chciało się dodać wartość typu Integer do listy ArrayList. Jednak wersje
Javy, począwszy od 5.0, mogą już automatycznie odpowiednio opakowywać warto-
ści typu int. Wartość typu int na liście parametrów zmienia się w typ Integer
w tablicy ArrayList. Funkcja autoboksingu w języka Java sprawia, że pisanie i czy-
tanie kodu programów jest znacznie łatwiejsze.
Kod z listingu 12.1 używa ogólnej metody hasNext. Metoda ta będzie zwracała
wartość true tak długo, jak długo na wejściu programu będzie coś do odczytania.
Gdy program odczyta ostatni wiersz tekstu Hugh R. DaReader (przedstawiony na
rysunku 12.1), kolejne wywołanie metody hasNext zwróci wartość false. W ten
sposób zakończy się wykonywanie pętli while, a komputer przejdzie do reszty
kodu z listingu 12.1.
Metoda hasNext jest bardzo przydatna; w rzeczywistości jest tak bardzo funkcjo-
nalna, że stanowi część większej koncepcji znanej jako iterator, a iteratory są
częścią wszystkich klas kolekcji w języku Java.
Korzystanie z iteratora
Iterator podaje nam po kolei wszystkie wartości z kolekcji. Chcąc uzyskać war-
tość z kolekcji, wywołujemy metodę next iteratora. Aby dowiedzieć się, czy ko-
lekcja zawiera więcej wartości, wywołujemy w iteratorze metodę hasNext. W ko-
dzie z listingu 12.2 zaprezentowano użycie iteratora do wyświetlania nazwisk.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public class ShowNames {
while (diskScanner.hasNext()) {
people.add(diskScanner.nextLine());
}
people.remove(0);
people.add(2, "Jim Newton");
diskScanner.close();
}
}
Możesz zastąpić rozszerzoną pętlę for znajdującą się na końcu listingu 12.1 po-
grubionym kodem widocznym na listingu 12.2. Kiedy to zrobisz, otrzymasz taki
sam wynik działania programu jak poprzednio. (Wynik ten przedstawiono na
rysunku 12.2). Na listingu 12.2 pierwszy pogrubiony wiersz kodu pobiera iterator
z kolekcji people. Natomiast drugi i trzeci wiersz wywołują odpowiednio metody
hasNext i next iteratora, tak aby odczytać wszystkie obiekty przechowywane
w kolekcji people — po jednym dla każdej iteracji pętli. Te trzy wiersze wyświetlają
wszystkie wartości kolekcji.
I co będzie tutaj lepszym rozwiązaniem? Rozszerzona pętla for czy też iterator?
Programiści Javy preferują w takim przypadku rozszerzoną pętlę for, ponieważ
pętla ta ma mniej bagażu — nie ma obiektu iterator, który trzeba przenieść z jed-
nego wiersza kodu do następnego. Jednak jak będziesz się mógł przekonać w dalszej
części tego rozdziału, nawet funkcja najbardziej ułatwiająca programowanie
również może zostać uaktualniona, usprawniona, ulepszona lub w inny sposób
przebudowana. I tak sposoby, na jakie możemy ulepszyć swój kod, nie mają
końca.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
TABELA 12.1. Niektóre klasy kolekcji
Każda klasa kolekcji ma swój własny zestaw metod (oprócz metod dziedziczo-
nych po klasie AbstractCollection, przodka wszystkich klas kolekcji).
Chcąc dowiedzieć się, które klasy kolekcji najlepiej odpowiadają Twoim potrzebom,
odwiedź strony dokumentacji Java API pod adresem http://docs.oracle.com/javase/
8/docs/api.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W celu napisania tego programu przydatna może okazać się metoda
compareToIgnoreCase z klasy String i metoda size z klasy ArrayList.
Więcej o tych metodach dowiesz się, odwiedzając strony internetowe
https://docs.oracle.com/javase/8/docs/api/java/lang/String.html#
compareToIgnoreCase-java.lang.String- i http://docs.oracle.com/javase/8/docs/
api/java/util/ArrayList.html#size--.
Programowanie funkcyjne
W latach od 1953 do 1957 John Backus i jeszcze inni ludzie opracowali język pro-
gramowania FORTRAN, który zdefiniował podstawowe ramy dla tysięcy języków
programowania XX wieku. Ze względu na swoją naturę ramy te stały się znane
jako programowanie imperatywne.
Kilka lat po powstaniu języka FORTRAN John McCarthy stworzył inny język o na-
zwie Lisp. W przeciwieństwie do języka FORTRAN podstawową strukturą języka
Lisp było programowanie funkcyjne. W czysto funkcyjnym programie unikasz pisania
„zrób to, a następnie zrób tamto”. Zamiast tego piszesz takie rzeczy jak „Oto,
w jaki sposób przekształcisz to w to, kiedy będziesz robił transformację”.
Z tego lub innego powodu programowanie imperatywne stało się dominującą me-
todą. W rezultacie język Java jest zasadniczo imperatywnym językiem programo-
wania. Jednak ostatnio programowanie funkcyjne okazało się potężnym i użytecz-
nym sposobem myślenia o kodzie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Aby pomóc Ci zrozumieć programowanie funkcyjne, rozpoczynam ten podroz-
dział od analogii. W dalszej jego części przedstawię kilka przykładów w języku Java.
Analogia, której tutaj używam do opisu programowania funkcyjnego, nie jest idealna.
Mój przyjaciel nazwał tę analogię rozciągnięciem, ponieważ pasuje ona do wielu
różnych metod programowania, a nie tylko do programowania funkcyjnego. Tak
czy inaczej myślę, że będzie tu pomocna.
RYSUNEK 12.3.
Imperatywne
segmenty
programowania
stanowią problem
RYSUNEK 12.4.
Elementy programu
imperatywnego nie
pasują idealnie do
wielordzeniowego
chipa procesora
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W programowaniu imperatywnym elementy Twojego kodu współdziałają ze so-
bą. Wszystkie elementy mogą na przykład aktualizować cenę akcji firmy Oracle
(symbol giełdowy: ORCL). Równoczesne aktualizacje to dość zagmatwana spra-
wa. To tak, jakby kilku chłopców z liceum pytało tę samą dziewczynę, czy pójdzie
na bal maturalny z którymś z nich — nic dobrego z tego nie wynika. Doświadczyłeś
tego samego zjawiska, jeśli kiedykolwiek kliknąłeś na stronie internetowej przy-
cisk Kup tylko po to, żeby dowiedzieć się, że przedmiot, który próbujesz kupić,
nie jest już dostępny. Ktoś inny dokonał zakupu, podczas gdy Ty wprowadzałeś
dane karty kredytowej. Zbyt wielu klientów chwyciło za ten sam towar w tym
samym czasie.
RYSUNEK 12.5.
Programowanie
funkcyjne dzieli
nasz problem
w inny sposób
RYSUNEK 12.6.
Elementy programu
funkcjonalnego
idealnie pasują do
wielordzeniowego
chipa procesora
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Rozwiązanie problemu w tradycyjny sposób
W rozdziale 11. używasz tablic do zarządzania motelem Java, ale teraz to przed-
sięwzięcie jest już za Tobą. Zrezygnowałeś z działalności hotelowej i powiedzia-
łeś ludziom, że zdecydowałeś się przejść dalej. Szczerze mówiąc, hotel tracił dużo
pieniędzy. Według sądu upadłościowego Stanów Zjednoczonych stary motel Java
znajduje się obecnie w rozdziale 11.
Po pozbyciu się biznesu hotelowego zająłeś się sprzedażą online. Obecnie pro-
wadzisz stronę internetową, która sprzedaje książki, płyty DVD i inne elementy
związane z różnymi treściami. (Książka Barry’ego Burda Java dla bystrzaków jest
obecnie najlepiej sprzedającą się pozycją, ale mniejsza o to).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 12.4. Wykorzystywanie klasy Sale
import java.text.NumberFormat;
import java.util.ArrayList;
fillTheList(sales);
double total = 0;
for (Sale sale : sales) {
if (sale.getItem().equals("DVD")) {
total += sale.getPrice();
}
}
System.out.println(currency.format(total));
}
Scenariusz przedstawiony na listingu 12.4 nie jest niczym niezwykłym. Masz ko-
lekcję elementów (na przykład kolekcję sprzedanych rzeczy). Przechodzisz przez
elementy tej kolekcji, znajdując przedmioty spełniające określone kryteria (na
przykład sprzedaż płyt DVD). Pobierasz określoną wartość (taką jak cena sprze-
daży) każdego elementu spełniającego Twoje kryteria, a następnie robisz coś
użytecznego z uzyskaną wartością (na przykład dodając ją do innej wartości).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Przejrzyj swoją listę klientów. Każdemu klientowi, który wykazał zainteresowanie
zakupem smartfona, wyślij e-mail o planach rabatowych w tym miesiącu.
Przejrzyj listę planet, które zostały odkryte. Dla każdej planety klasy M odczytaj
prawdopodobieństwo znalezienia na niej inteligentnego życia. Następnie znajdź
średnią wszystkich takich prawdopodobieństw.
Ten scenariusz jest tak powszechny, że warto znaleźć lepsze i jeszcze lepsze
sposoby radzenia sobie z tymi zagadnieniami. Jednym z takich sposobów jest
wykorzystanie niektórych funkcji programowania funkcyjnego w Javie.
Strumienie
W jednym z wcześniejszych punktów — „Korzystanie z iteratora” — wprowa-
dziłem pojęcie iteratora. Aby otrzymać wartości z kolekcji, używasz metody next
z iteratora. Język Java przenosi tę koncepcję o krok dalej, wprowadzając pojęcie
strumienia. Strumień jest jak iterator, z tą różnicą, że w strumieniu nie musisz
wywoływać metody next. Po utworzeniu strumień automatycznie zwraca warto-
ści z kolekcji. Chcąc pobrać wartości ze strumienia, nie musisz wywoływać jego
metody next. Co więcej, typowy strumień w ogóle nie ma metody next.
Jak zatem strumień działa jako część programu Java? Jak można utworzyć stru-
mień, który sam zwraca wartości? Skąd strumień będzie wiedział, kiedy zacząć
zwracać i gdzie ma odkładać zwracane wartości? Aby uzyskać odpowiedzi na te
i inne pytania, przeczytaj kilka następnych podrozdziałów.
Wyrażenia lambda
W latach 30. ubiegłego wieku matematyk Alonzo Church użył greckiej litery
lambda (λ), aby przedstawić pewną konstrukcję matematyczną tworzoną „w locie”1.
W ciągu następnych kilku dziesięcioleci idea ta trwała sobie spokojnie w czaso-
pismach matematycznych i informatycznych. Obecnie w języku Java termin wy-
rażenie lambda reprezentuje krótki fragment kodu, który służy zarówno jako de-
klaracja metody, jak i jej wywołanie, a to wszystko jest tworzone na bieżąco.
1
Wiele lat temu na University of Illinois brałem udział w wykładzie wygłoszonym przez Alonzo
Churcha. Był najbardziej skrupulatnym prezenterem na świecie. Każdy szczegół jego wykładu
został starannie zaplanowany i skrupulatnie wykonany. Wręczył papierowe kopie swoich notatek,
a ja spędziłem połowę wykładu, wpatrując się w te notatki i próbując odgadnąć, czy zostały
napisane odręcznie, czy na maszynie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Znaczenie takiego wyrażenia lambda prezentuję na rysunku 12.7.
RYSUNEK 12.7.
Czy sprzedawany
przedmiot
jest płytą DVD?
Wyrażenie lambda stanowi zwięzły sposób na definiowanie metody oraz jej wy-
woływanie bez nadawania takiej metodzie nazwy. Wyrażenie lambda (pokazane
na rysunku 12.7) robi (w przybliżeniu) to, co poniższy kod:
itemIsDVD(sale);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wyrażenie lambda z dwoma parametrami
Przyjrzyj się temu wyrażeniu lambda:
RYSUNEK 12.8.
Dodaj dwie ceny
sum(price1, price2);
display(sale);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wyrażenie lambda pobiera obiekty ze strumienia i dla każdego z tych obiektów
wywołuje metodę przypominającą metodę display. W nagłówku metody display
słowo void wskazuje, że metoda nie zwraca wartości. Wywołując metodę display
(lub używając równoważnego wyrażenia lambda), nie spodziewasz się uzyskania
jakiejkolwiek wartości. Zamiast tego oczekujesz, że kod zrobi coś w odpowiedzi
na to wywołanie (coś takiego jak na przykład wyświetlanie tekstu na ekranie
komputera).
Kategorie w tabeli 12.2 nie wykluczają się wzajemnie. Na przykład każdy Predykat
jest też Funkcją. (Każdy Predykat przyjmuje jeden parametr i zwraca wynik, który
musi być wartością typu boolean).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Używanie strumieni i wyrażeń lambda
Język Java ma fantazyjne metody, które optymalnie wykorzystują strumienie i wy-
rażenia lambda. Przy użyciu strumieni i wyrażeń lambda możemy przygotować
linię produkcyjną, która elegancko rozwiąże nasz problem sprzedażowy z tego roz-
działu. W przeciwieństwie do kodu z listingu 12.4 nowe rozwiązanie wykorzystuje
koncepcje programowania funkcyjnego.
Linia produkcyjna składa się z kilku metod. Każda metoda pobiera dane, prze-
kształca je w taki lub inny sposób i przekazuje swoje wyniki kolejnej metodzie.
Rysunek 12.9 ilustruje taką właśnie linię produkcyjną rozwiązującą nasz problem
sprzedaży internetowej.
RYSUNEK 12.9.
Linia produkcyjna
programowania
funkcyjnego
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Parametrem metody filter w języku Java jest Predykat — wyrażenie lambda,
którego wynikiem jest wartość typu boolean (patrz tabele 12.2 i 12.3). Metoda
filter (pokazana na rysunku 12.9) odrzuca elementy, które nie przeszły testu
wyrażenia lambda.
Dokładniej mówiąc, metoda map pobiera tutaj taką Funkcję jak ta z poniższej in-
strukcji:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
i stosuje ją do każdej wartości ze strumienia (patrz tabele 12.2 i 12.3). A zatem
metoda map na rysunku 12.9 pobiera przychodzący strumień obiektów sale i tworzy
wychodzący strumień wartości z pola price tych obiektów.
RYSUNEK 12.10.
Metoda reduce
dodaje dwie wartości
ze strumienia
wejściowego
RYSUNEK 12.11.
Metoda reduce
mnoży wartości ze
strumienia
przychodzącego
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Być może słyszałeś już o modelu programowania Google MapReduce. Podobieństwo
między nazwą tego modelu a nazwami metod map i reduce, pochodzącymi z języka
Java, nie jest tu przypadkowe.
fillTheList(sales);
System.out.println(currency.format(total));
}
Pogrubiony kod z listingu 12.5 jest jedną wielką instrukcją przypisania w języku
Java. Prawa strona tej instrukcji składa się z całej sekwencji wywołań metod.
Każde wywołanie metody zwraca obiekt, a każdy taki obiekt umieszczany jest
przed kropką w następnym wywołaniu metody. Tak właśnie tworzy się linię pro-
dukcyjną.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Metoda stream zwraca instancję klasy Stream (co za niespodzianka!), tak więc po
wywołaniu metody sales.stream() otrzymamy obiekt klasy Stream (rysunek 12.12).
RYSUNEK 12.12.
Pobieranie wszystkich
sprzedaży płyt DVD
jest wywołaniem metody filter w obiekcie klasy Stream (ponownie patrz rysu-
nek 12.12).
Tego schematu używamy też dalej. Metoda map obiektu klasy Stream zwraca kolejny
obiekt tej klasy — tym razem przechowujący same ceny (patrz rysunek 12.13). Do
tego strumienia z cenami stosujemy metodę reduce, która zwraca jedną wartość
typu double — sumę cen wszystkich sprzedanych płyt DVD (rysunek 12.14).
RYSUNEK 12.13.
Uzyskiwanie ceny
każdej
ze sprzedanych
płyt DVD
RYSUNEK 12.14.
Uzyskiwanie całej
kwoty sprzedaży
wszystkich płyt DVD
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Po co się tak męczyć?
Ciąg wywołań metod znajdujący się na listingu 12.5 realizuje wszystko, co osiąga
jedna pętla pokazana na listingu 12.4. Lecz kod na rysunku 12.14 używa pojęć
z programowania funkcyjnego. I o co tyle szumu? Czy kod z listingu 12.5 jest lepszy
od kodu z listingu 12.4?
Natomiast modyfikowanie kodu funkcyjnego jest bardzo łatwe. Jeśli chcemy sko-
rzystać z procesorów wielordzeniowych, zmieniamy tylko jedno słowo w kodzie na
listingu 12.5!
sales.parallelStream()
.filter((sale) -> sale.getItem().equals("DVD"))
.map((sale) -> sale.getPrice())
.reduce(0.0, (price1, price2) -> price1 + price2);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Ponadto niektóre problemy nie pozwalają na przetwarzanie równoległe. Wyobraź
sobie sytuację, w której cena przedmiotu zależy od całkowitej wysokości sprze-
daży podobnych przedmiotów. W takim przypadku nie można podzielić proble-
mu na cztery niezależnie działające rdzenie. Jeśli tego spróbujesz, to każdy rdzeń
będzie musiał wiedzieć, co robią pozostałe rdzenie. Tym samym tracisz zalety
wynikające z wykorzystywania czterech wątków.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Referencje metod
Spójrz, proszę, troszkę krytycznie na ostatnie wyrażenie lambda znajdujące się
na listingu 12.5:
To wyrażenie działa mniej więcej tak samo jak metoda sum. (W rzeczywistości
deklarację metody sum można znaleźć we wcześniejszym punkcie „Wyrażenia
lambda”). Jeśli wybierasz pomiędzy 3-wierszową metodą sum a wpisaniem
1-wierszowego wyrażenia lambda, prawdopodobnie wybierzesz wyrażenie lambda.
Ale co zrobisz, jeśli pojawi się trzecia opcja? Zamiast wpisywać własną metodę
sum, możesz odwołać się do istniejącej już metody sum. Korzystanie z istniejącej
metody jest najszybszą i najbezpieczniejszą rzeczą, jaką można zrobić.
Na szczęście klasa Double udostępnia już statyczną metodę sum. Dzięki temu nie
musimy tworzyć własnej. Jeśli uruchomimy następujący kod:
sales.stream()
.filter((sale) -> sale.getItem().equals("DVD"))
.map((sale) -> sale.getPrice())
.reduce(0.0, Double::sum);
Wyrażenie Double::sum odwołuje się do metody sum należącej do klasy Double ję-
zyka Java. Kiedy używasz takiej referencji metody Double::sum, robisz to samo, co
ostatnie wyrażenie lambda na listingu 12.5. I wszyscy są szczęśliwi.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
354 CZĘŚĆ IV Sprytne techniki Javy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 13
Wyglądaj dobrze,
gdy sprawy przybierają
nieoczekiwany obrót
9
września 1945 roku ćma wpada do jednego z przekaźników komputera Harvard
Mark II i wpływa na jego pracę. Staje się to pierwszym zarejestrowanym
przypadkiem prawdziwego robaka komputerowego.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Na podstawie swojego przeczucia pan Bright pisze mały program w języku FORTRAN
i próbuje go skompilować na swoim komputerze IBM 704. (IBM 704 pracuje we
własnym, specjalnie przygotowanym pokoju o powierzchni 200 metrów kwadrato-
wych. Zamiast tranzystorów pracują w nim lampy próżniowe. Maszyna ma ogrom-
ną pamięć RAM o wielkości 32 K. System operacyjny musi zostać załadowany
z taśmy przed uruchomieniem każdego programu, a czas pracy typowego programu
to od dwóch do czterech godzin). Po zwyczajowym czasie oczekiwania próba
skompilowania programu pana Brighta kończy się z pojedynczym błędem: braku-
jącym przecinkiem w jednej z instrukcji. Pan Bright poprawił błąd i program za-
działał jak marzenie.
Obsługa wyjątków
Robisz inwentaryzację. Oznacza to zliczanie kolejnych przedmiotów, kolejnych
pudełek i wpisywanie ilości różnych rzeczy w dziennikach, w małych przenośnych
gadżetach oraz w formularzach na komputerze. Szczególna część tego procesu po-
lega na inwentaryzowaniu liczby pudełek, które można znaleźć na regale z Du-
żymi Zakurzonymi Pudłami, Których Nikt Nigdy Nie Otwierał. Postanawiasz nie
łamać odwiecznego zwyczaju firmy i nie otwierasz żadnego z tych pudeł. Przypi-
sujesz za to wartość 3,25 zł każdemu z tych pudeł.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
aż tak, żeby odmawiać współpracy za każdym razem, gdy użytkownik wprowadzi
niewłaściwą wartość.
RYSUNEK 13.1.
Trzy kolejne
uruchomienia kodu
przedstawionego na
listingu 13.1
Cóż, oto co się dzieje. Język programowania Java ma mechanizm zwany obsługą
wyjątków. Dzięki obsłudze wyjątków program może wykryć, że coś właśnie idzie
nie tak jak trzeba, i zareagować na tę sytuację, tworząc nowy obiekt. W oficjalnej
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
terminologii mówi się, że program rzuca wyjątek. Ten nowy obiekt, instancja
klasy Exception, jest przekazywany jak gorący ziemniak z jednego kawałka kodu
do drugiego, dopóki jakiś fragment tego kodu nie zdecyduje się chwycić taki wy-
jątek. Po chwyceniu wyjątku program wykonuje jakiś kod obsługujący tę sytu-
ację, zakopuje wyjątek i przechodzi do następnej normalnej instrukcji, jakby się
nic nie wydarzyło. Proces ten przedstawiam na rysunku 13.2.
RYSUNEK 13.2.
Rzucanie,
przekazywanie
i chwytanie wyjątku
Cała sprawa odbywa się za pomocą kilku słów kluczowych Javy opisanych na po-
niższej liście:
try. Zawiera kod, który potencjalnie może utworzyć nowy obiekt wyjątku.
W zwykłym scenariuszu kod wewnątrz klauzuli try zawiera wywołania metod,
które mogą utworzyć jeden lub więcej wyjątków.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Dokumentacja API Javy dla metody parseInt mówi: „Rzuca NumberFormat
Exception, jeśli ciąg nie zawiera prawidłowej liczby całkowitej”. Od czasu do
czasu czytanie dokumentacji faktycznie się opłaca.
Jeśli uważasz się za bohatera, lepiej złap ten wyjątek, tak żeby pozostała część
kodu mogła kontynuować swoje normalne działanie. Kod z listingu 13.2 pokazu-
je, jak można chwytać wyjątek.
try {
int numBoxes = Integer.parseInt(numBoxesIn);
out.print("Ich wartość to ");
out.println(currency.format(numBoxes * boxPrice));
} catch (NumberFormatException e) {
out.println("To nie jest prawidłowa liczba.");
}
keyboard.close();
}
}
Rysunek 13.3 przedstawia trzy uruchomienia kodu z listingu 13.2. Gdy użytkow-
nik błędnie wpisze trzy zamiast liczby 3, program zachowa spokój, po czym
wyświetli zdanie: To nie jest prawidłowa liczba. Sztuczka polega na umieszcze-
niu wywołania metody Integer.parseInt wewnątrz klauzuli try. Jeśli zrobisz to
w ten sposób, to komputer będzie wypatrywał wyjątków podczas wykonywania
dowolnych poleceń znajdujących się wewnątrz klauzuli try. Gdy zostanie rzuco-
ny jakiś wyjątek, komputer wyskakuje z klauzuli try i przechodzi do znajdującej
się poniżej klauzuli catch. Na listingu 13.2 komputer przeskakuje bezpośrednio do
klauzuli catch (NumberFormatException e). Następnie wykonuje instrukcję println
zapisaną w tej klauzuli i wraca do normalnego trybu pracy. (Gdyby na listingu
13.2 znajdowały się jakieś instrukcje po zakończeniu klauzuli catch, to komputer
by je wykonał).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 13.3.
Trzy uruchomienia
kodu z listingu 13.2
Cała konstrukcja try-catch — wraz z klauzulami try i catch oraz całą resztą —
nazywa się instrukcją try. Czasami dla zwiększenia efektu nazywam ją instrukcją
try-catch.
Na listingu 13.2 w ogóle nie korzystam z parametru e klauzuli catch, ale z pew-
nością mógłbym, gdybym tylko chciał. Zapamiętaj: Rzucany wyjątek to obiekt —
instancja klasy NumberFormatException. Gdy wyjątek zostanie chwycony, kompu-
ter umieszcza go jako parametr w klauzuli catch. Innymi słowy, nazwa e prze-
chowuje informacje o wyjątku. Chcąc to wykorzystać, możesz wywołać niektóre
metody obiektu wyjątków.
} catch (NumberFormatException e) {
out.println("Komunikat: ***" + e.getMessage() + "***");
e.printStackTrace();
}
RYSUNEK 13.4.
Wywołanie metody
wyjątku obiektu
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Gdy jednocześnie stosujesz wywołania metod System.out.println i printStackTrace,
kolejność, w jakiej Java wyświetli informacje, nie jest przewidywalna. Na przy-
kład na rysunku 13.4 tekst Komunikat: ***For input string: "trzy"*** może po-
jawić się przed lub po informacji o stosie wywołań. Jeśli kolejność tego działania
ma dla Ciebie znaczenie, zamień instrukcję out.println("Komunikat: *** na in-
strukcję System.err.println("Komunikat: *** .
Typy wyjątków
Co jeszcze może pójść źle? Czy są inne rodzaje wyjątków — takich, które nie
pochodzą z klasy NumberFormatException? Oczywiście, że tak! Istnieje wiele róż-
nych typów wyjątków. Możesz nawet tworzyć swoje własne wyjątki. Chcesz spró-
bować? Jeśli tak, spójrz na listingi 13.3 i 13.4.
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
keyboard.close();
}
}
Kody z listingów 13.3 i 13.4 rozwiązują problem, który pojawił się wcześniej na
rysunku 13.3. Spójrz na ostatnie uruchomienie programu z rysunku 13.3. Użyt-
kownik podaje, że na półkach leży –25 pudełek, a komputer przyjmuje tę war-
tość bez mrugnięcia okiem. Prawda jest taka, że potrzebowałbyś czarnej dziury
(lub innego egzotycznego zjawiska odkształcania czasoprzestrzeni), aby mieć
ujemną liczbę pudełek na dowolnej półce w magazynie. Tak więc program powi-
nien się zdenerwować, jeśli użytkownik wprowadzi ujemną liczbę. I dokładnie tak
działa kod z listingu 13.4. Chcąc zobaczyć zdenerwowany kod, spójrz na rysunek 13.5.
RYSUNEK 13.5.
Trzy uruchomienia
kodu z listingów
13.3 i 13.4
Wróćmy jeszcze do listingu 13.4, gdzie rzucana jest nowa instancja wyjątku OutOf
RangeException. W takim przypadku klauzula catch (OutOfRangeException e)
chwyta tę rzuconą instancję. Kod tej klauzuli powtarza dane wprowadzone przez
użytkownika i wyświetla komunikat To niemożliwe!
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
ściach rzucenie wyjątku OutOfRangeException może spowodować, że program
pominie instrukcję keyboard.close(). Ale przecież gdy uruchomisz kod z listingu
13.4, to nie może się zdarzyć). Moim zdaniem zupełnie bezpiecznie możesz zi-
gnorować to ostrzeżenie.
Dla każdej klauzuli catch komputer zadaje sobie pytanie: „Czy wyjątek, który
został właśnie rzucony, jest instancją klasy na liście parametrów tej klauzuli?”.
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
throw new OutOfRangeException();
}
if (numBoxes > 1000) {
throw new NumberTooLargeException();
}
catch (NumberFormatException e) {
out.println("To nie jest prawidłowa liczba.");
}
catch (OutOfRangeException e) {
out.print(numBoxesIn);
out.println("? To niemożliwe!");
}
catch (Exception e) {
out.print("Coś poszło nie tak, ");
out.print("ale nie wiem ");
out.println("co to właściwie było.");
}
out.println("I to wszystko.");
keyboard.close();
}
}
Kod z listingu 13.6 opisuje sytuację, w której masz ograniczone miejsce na rega-
łach. Nie masz miejsca na więcej niż 1000 pudełek, ale raz na jakiś czas program
pyta, ile mamy pudeł, a ktoś wprowadza przypadkowo liczbę 100000. W takich
sytuacjach kod programu (listing 13.6) wykonuje szybką kontrolę, w wyniku
której każda liczba pudełek powyżej 1000 jest odrzucana jako nierealna.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
cie, gdy się natknie na tę klauzulę dla klasy OutOfRangeExceptioncatch, stwierdza:
„Dobra, znalazłem coś pasującego. Wykonam instrukcje w tej klauzuli catch”.
Aby nie musieć ciągle powtarzać całej tej historii, wprowadzam tu nową termi-
nologię. Mówię, że klauzula catch z parametrem OutOfRangeException pasuje do
rzuconego wyjątku NumberTooLargeException. Taką klauzulę catch nazywam pasującą
klauzulą catch.
RYSUNEK 13.6.
Cztery uruchomienia
kodu z listingu 13.6
RYSUNEK 13.7.
Nie rzucono wyjątku
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Użytkownik wprowadza coś, co nie jest liczbą całkowitą, jak słowo rybka.
Kod rzuca wyjątek NumberFormatException. Komputer pomija pozostałe
instrukcje w klauzuli try, po czym wykonuje instrukcje znajdujące się wewnątrz
pierwszej klauzuli catch — tej, której parametr jest typu NumberFormatException.
Następnie komputer pomija drugą i trzecią klauzulę catch i wykonuje kod,
który pojawia się zaraz za wszystkimi klauzulami (patrz rysunek 13.8).
RYSUNEK 13.8.
Zgłaszany jest wyjątek
NumberFormat
Exception
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 13.9.
Rzucany jest wyjątek
OutOfRange
Exception
RYSUNEK 13.10.
Zgłaszany jest wyjątek
NumberTooLarge
Exception
Dzieje się coś innego, coś nieprzewidywalnego. (Sam nie wiem co). Z moją
nieskończoną chęcią eksperymentowania sięgnąłem do klauzuli try (z listingu
13.6) i dodałem instrukcję, która zgłasza wyjątek IOException. I zrobiłem
to właściwie bez powodu — chciałem tylko zobaczyć, co się stanie.
Gdy kod rzucił wyjątek IOException, komputer pominął pozostałe instrukcje
w klauzuli try. A następnie pominął też instrukcje w pierwszej i drugiej klauzuli
catch. I kiedy osiągnął trzecią klauzulę catch, usłyszałem, jak komputer powiedział:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
„Hm! Wyjątek IOException to rodzaj klasy Exception. Znalazłem pasującą klauzulę
catch — tę z parametrem typu Exception. Wykonam znajdujące się w niej instrukcje”.
Komputer wykonał więc instrukcje w trzeciej klauzuli catch. A następnie wykonał
kod, który znajduje się zaraz za wszystkimi klauzulami catch (patrz rysunek 13.11).
RYSUNEK 13.11.
Rzucany jest wyjątek
IOException
Typ parametru klauzuli jest taki sam jak typ wyjątku, który został zgłoszony.
Jeśli lepsze dopasowanie pojawia się w dalszej części listy klauzul catch, to nie
jest ono brane pod uwagę. Wyobraź sobie, że do kodu z listingu 13.6 dodałeś
klauzulę catch z parametrem typu NumberTooLargeException i że umieściłeś tę
nową klauzulę za klauzulą z parametrem typu OutOfRangeException. W takiej sy-
tuacji kod w nowej klauzuli dla wyjątków NumberTooLargeException nigdy nie zo-
stanie wykonany, ponieważ klasa NumberTooLargeException jest podklasą klasy
OutOfRangeException. Tak właśnie to wszystko działa.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
try {
int numBoxes = Integer.parseInt(numBoxesIn);
if (numBoxes < 0) {
throw new OutOfRangeException();
}
if (numBoxes > 1000) {
throw new NumberTooLargeException();
}
out.print("Ich ilość to ");
out.println(currency.format(numBoxes * boxPrice));
}
catch (NumberFormatException | OutOfRangeException e) {
out.print(numBoxesIn);
out.println("? To niemożliwe!");
}
catch (Exception e) {
out.print("Coś poszło nie tak, ");
out.print("nie wiem ");
out.println("co to właściwie było.");
}
Nadmierna ostrożność
Czy jesteś jednym z tych obsesyjno-kompulsywnych typów? Lubisz łapać wszyst-
kie możliwe wyjątki, tak aby nie mogły spowodować awarii Twojego programu?
No to uważaj. Java nie pozwoli Ci stać się paranoikiem. Nie możesz łapać wyjątku,
jeśli w kodzie nie ma szans na jego rzucenie.
Przyjrzyj się poniższemu kodowi. Znajduje się w nim niewinna instrukcja i++
umieszczona w klauzuli try. No i świetnie. Ale poniżej klauzula catch twierdzi,
że przechwytuje wyjątek IOException:
// Zły kod!
try {
i++;
} catch (IOException e) {
e.printStackTrace();
}
Kogo ta klauzula catch próbuje nabrać? Taka instrukcja jak i++ nie wykonuje
żadnych operacji wejścia ani wyjścia. Kod wewnątrz klauzuli try w żaden sposób
nie może rzucić wyjątku IOException. Kompilator mówi zatem: „Hej, klauzulo
catch. Ogarnij się. I zejdź już z tego konia”. Mówiąc dokładniej, reprymenda od
kompilatora brzmi następująco:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
exception java.io.IOException is never thrown
in body of corresponding try statement
do {
out.print("Ile mamy pudełek? ");
String numBoxesIn = keyboard.next();
try {
int numBoxes = Integer.parseInt(numBoxesIn);
out.print("Ich wartość to ");
out.println(currency.format(numBoxes * boxPrice));
gotGoodInput = true;
} catch (NumberFormatException e) {
out.println();
out.println("To nie jest prawidłowa liczba.");
}
} while (!gotGoodInput);
out.println("I to wszystko.");
keyboard.close();
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 13.12.
Wynik działania kodu
z listingu 13.7
try {
while (true) {
dataOut.writeByte(dataIn.readByte());
}
} catch (EOFException e) {
numFilesCopied = 1;
}
Chcąc skopiować bajty z pliku dataIn do pliku dataOut, wystarczy wejść do pętli
while. Dzięki warunkowi o wartości true pętla while wydaje się nie mieć końca, ale
ostatecznie dotrzesz do końca pliku dataIn. W takim przypadku metoda readByte
rzuci wyjątek EOFException (koniec pliku). Rzucenie tego wyjątku wysyła komputer
z klauzuli try, poza pętlę while. Tam, w klauzuli catch, robisz wszystko, co chcesz
zrobić, a następnie kontynuujesz normalne działanie.
int i = 0;
do {
words[i] = keyboard.next();
} while (!words[i++].equals("Wyjście"));
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
for (int j = 0; j < 5; j++) {
System.out.println(words[j].length());
}
keyboard.close();
}
}
Na listingu 13.6 cena każdego pudełka oraz zbyt duża liczba pudełek to wartości
ustalone na stałe w kodzie. Popraw kod tak, aby użytkownik wprowadzał obie
te wartości. Pamiętaj jednak, że niektóre dane dla tych wartości nie będą miały
sensu. Na przykład ujemna liczba pudełek na pewno nie oznacza zbyt wielu.
Użyj instrukcji try-catch do obsługi niewłaściwych danych wprowadzanych
przez użytkownika.
Co powiesz na przerwę? Mała drzemka może dobrze Ci zrobić. Czy dziesięć sekund
jest w porządku? A może to za długo? Lepiej weźmy pięć sekund.
takeANap();
out.println("Ach, jakie to było odświeżające.");
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
static void takeANap() {
Thread.sleep(5000);
}
}
Strategia z listingu 13.8 nie jest taka zła. Chodzi o wywołanie metody sleep, która
jest zdefiniowana w API języka Java. Ta metoda sleep należy do klasy API Thread.
Kiedy wywołujesz metodę sleep, w jej parametrze podajesz liczbę milisekund.
A zatem wywołanie Thread.sleep(5000) oznacza pauzę na pięć sekund.
Problem polega na tym, że kod wewnątrz metody sleep może zgłosić wyjątek.
Ten rodzaj wyjątku jest instancją klasy InterruptedException. Kiedy będziesz
próbował skompilować kod taki jak pokazany na listingu 13.8, otrzymasz wiado-
mość taką jak ta:
Chcąc zrozumieć ogólnie wyjątki, nie musisz dokładnie wiedzieć, czym jest wy-
jątek InterruptedException. Musisz jedynie orientować się, że wywołanie metody
Thread.sleep może rzucić jeden z tych obiektów InterruptedException. Jeśli jesteś
naprawdę ciekawy, to wiedz, że wyjątek InterruptedException jest rzucany w mo-
mencie, gdy jakiś kod przerywa sen innego kodu. Wyobraź sobie, że masz dwa
fragmenty kodu uruchomione w tym samym czasie. Jeden fragment kodu wy-
wołuje metodę Thread.sleep, a drugi jednocześnie wywołuje metodę interrupt.
Wywołując metodę interrupt, drugi fragment kodu przerywa działanie metody
Thread.sleep z pierwszego kodu. Metoda Thread.sleep odpowiada, rzucając wyjątek
InterruptedException.
Pamiętaj, że w języku Java istnieją dwa rodzaje wyjątków. Są one nazywane wy-
jątkami sprawdzonymi i niesprawdzonymi:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Teraz gdy piszę, że w kodzie uwzględniono wyjątek, to co mam na myśli?
Jeśli jesteś zdezorientowany treścią tego wypunktowania, nic się nie martw, na-
stępne dwa listingi nieco rozjaśnią te sprawy.
takeANap();
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 13.13.
Pięciosekundowa
przerwa przed
wierszem
zawierającym „Ach”
try {
takeANap();
} catch (InterruptedException e) {
out.println("Hej, kto mnie obudził?");
}
Jeżeli chcesz zobaczyć wynik działania kodu z listingu 13.10, spójrz na rysunek
13.13. I tu ponownie rysunek 13.13 nie uchwycił prawdziwej istoty tego programu.
No, ale w porządku. Pamiętaj tylko, że na rysunku 13.13 komputer zatrzymuje się
na pięć sekund, zanim wyświetli tekst: Ach, jakie to było odświeżające.
Ważna część listingu 13.10 znajduje się w nagłówku metody takeANap. Nagłówek
ten kończy się klauzulą throws InterruptedException. Informuje tym samym, że
metoda takeANap może rzucić wyjątek InterruptedException, dzięki czemu pozbywa
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
się odpowiedzialności za niego. Klauzula throws tak naprawdę mówi tu: „Zdaję so-
bie sprawę, że instrukcja wewnątrz tej metody może wywołać wyjątek Interrupted
Exception, ale nie uwzględniam tego wyjątku w instrukcji try-catch. Kompi-
latorze Javy, nie musisz mnie z tego powodu strofować. Zamiast używać in-
strukcji try-catch, przekazuję całą odpowiedzialność za uwzględnienie tego wy-
jątku do metody main (metody, która wywołała metodę takeANap)”.
Chcąc lepiej zrozumieć klauzulę throws, wyobraź sobie grę w siatkówkę, w której
piłka jest wyjątkiem. Kiedy gracz drugiej drużyny serwuje, to znaczy, że rzuca
wyjątek. Piłka przekracza siatkę i trafia prosto do Ciebie. Jeśli odbijesz piłkę
z powrotem na drugą stronę siatki, to znaczy, że łapiesz wyjątek, natomiast jeśli
podasz piłkę innemu graczowi, to używasz klauzuli throws. W gruncie rzeczy
mówisz: „Hej, inny graczu, teraz ty zajmiesz się tym wyjątkiem”.
Instrukcja w metodzie może zgłosić wyjątek, który nie będzie zgodny z klauzulą
catch. Obejmuje to sytuacje, w których instrukcja rzucająca wyjątek nie znajduje
się nawet w bloku try. W takim przypadku wykonywanie programu wyskakuje
z metody, która zawiera problematyczną instrukcję, i przeskakuje z powrotem
do kodu wywołującego tę metodę.
Metoda może podawać w klauzuli throws więcej niż tylko jeden typ wyjątku. Użyj
przecinków, aby oddzielić nazwy typów wyjątków w taki sposób, jak przedstawiono
w poniższym przykładzie:
Interfejs API Javy ma setki typów wyjątków. Kilka z nich to podklasy klasy Runtime
Exception. Cokolwiek, co jest podklasą RuntimeException (lub podpodklasą, pod-
podpodklasą i tak dalej), jest wyjątkiem niesprawdzonym. Sprawdzony jest każdy
wyjątek, który nie jest potomkiem klasy RuntimeException. Niesprawdzone wy-
jątki obejmują rzeczy, które byłyby trudne do przewidzenia dla komputera. Takie
rzeczy obejmują wyjątek NumberFormatException (pochodzący z listingu 13.2, 13.4
i z innych listingów), wyjątek ArithmeticException, wyjątek IndexOutOfBounds
Exception, niesławny wyjątek NullPointerException i jeszcze wiele innych.
Znaczna część kodu programu pisanego w Javie jest podatna na powstawanie tych
wyjątków, ale zamknięcie kodu w klauzulach try (lub przerzucenie odpowiedzial-
ności za pomocą klauzul throws) jest całkowicie opcjonalne.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
uwzględniona za pomocą instrukcji try lub klauzuli throws. Wyjątki sprawdzane
przez Javę obejmują wyjątek InterruptedException (listingi 13.9 i 13.10), wyjątek
IOException, wyjątek SQLException oraz cały zbiór innych interesujących wyjątków.
int i = 0;
while (diskScanner.hasNextInt()) {
numerators[i] = diskScanner.nextInt();
denominators[i] = diskScanner.nextInt();
i++;
}
diskScanner.close();
}
}
Napraw nieuwzględniony wyjątek FileNotFoundException w taki sposób, aby
kod mógł zostać skompilowany. Następnie zauważ, że w zależności od wartości
znajdujących się w pliku numbers.txt podczas działania programu mogą zostać
rzucone pewne inne wyjątki. Dodaj jedną lub więcej instrukcji try-catch, aby
wyświetlić komunikaty o tych wyjątkach, nie pozwalając tym samym na awarię
programu.
Następnie dodaj instrukcje try-catch lub klauzule throws (lub kombinację tych
dwóch rzeczy), tak aby naprawić następujący uszkodzony kod:
// ZŁY KOD:
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.EOFException;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public class Main {
int numFilesCopied = 0;
try {
while (true) {
dataOutStrm.writeByte(dataInStrm.readByte());
}
} catch (EOFException e) {
numFilesCopied = 1;
}
}
}
Gdy uda Ci się skompilować ten kod, utwórz plik o nazwie input i uruchom
program, aby sprawdzić, czy utworzy on plik o nazwie output.
W każdym razie oba samochody nadal były zdolne do jazdy, a my byliśmy w samym
środku ruchliwego skrzyżowania. Nie chcąc spowodować korków, podjechaliśmy
do najbliższego krawężnika. Szukałem mojego prawa jazdy (na którym było moje
zdjęcie w bardzo młodej wersji), a następnie otworzyłem drzwi, aby wysiąść
z samochodu.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Na szczęście wszystko dobrze się skończyło. Byłem posiniaczony, ale nie poobi-
jany. Moi rodzice zapłacili za uszkodzenia samochodu, więc nie poniosłem żad-
nych konsekwencji finansowych tego zdarzenia. (Udało mi się przekazać dalej
mandat, umieszczając wyjątek RunARedLightException w klauzuli throws).
Dzięki temu wypadkowi mogę wyjaśnić, dlaczego w ten sposób myślę o meto-
dach obsługiwania wyjątków. W szczególności zastanawiam się tu nad pytaniem:
„Co się stanie, jeśli w czasie, gdy komputer zajmuje się obsługą jednego wyjątku,
zostanie zgłoszony kolejny wyjątek?”. W końcu instrukcje w klauzuli catch nie są
odporne na nieszczęścia.
Jedną z niezbyt wymagających rzeczy, które możesz tutaj zrobić, jest utworzenie
klauzuli finally. Podobnie jak klauzula catch, klauzula finally pojawia się po
klauzuli try. Duża różnica polega na tym, że instrukcje w klauzuli finally są wyko-
nywane niezależnie od tego, czy wyjątek został zgłoszony. Pomysł brzmi nastę-
pująco: „Bez względu na to, co się stanie, dobrze lub źle, wykonaj instrukcje
wewnątrz tej klauzuli finally”. Na listingu 13.11 zawarłem przykład.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zwykle gdy myślę o instrukcji try, myślę o tym, jak komputer zmaga się z nie-
przyjemną sytuacją. Takie zmagania odbywają się wewnątrz klauzuli catch, a na-
stępnie komputer przechodzi do dowolnych instrukcji znajdujących się po in-
strukcji try. Jeśli podczas wykonywania klauzuli catch coś pójdzie nie tak, całość
może wyglądać zupełnie inaczej.
RYSUNEK 13.14.
Wynik końcowy
działania
kodu programu
z listingu 13.11
Co ciekawe, na rysunku 13.14 nie zobaczysz słów: Nie zostanę wydrukowany., po-
nieważ wykonanie klauzuli catch zgłasza własny, nieprzechwycony wyjątek, tak
więc komputer nigdy nie przejdzie poza instrukcję try-catch-finally.
Komputer wraca zatem do miejsca w metodzie main, z którego wybrał się na wy-
cieczkę. Po powrocie do metody main informacja o wyjątku ArithmeticException
rzuconym w metodzie doSomething zaowocuje przejściem do klauzuli catch. Kom-
puter wypisze zdanie: Wyjątek złapany w metodzie main, a następnie skończy się
ten paskudny koszmar.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Instrukcja try i zasoby
Wyobraź sobie program, który pobiera dane wejściowe z dwóch różnych plików
lub z metody Scanner i z pliku znajdującego się na twardym dysku. Chcąc upewnić
się, że ładnie po sobie posprzątasz, umieszczasz wywołania metody close w klau-
zuli finally (patrz listing 13.12).
try {
scan1 = new Scanner(new File("File1.txt"));
scan2 = new Scanner(new File("File2.txt"));
// Rób przydatne rzeczy, ale także...
scan1.close();
scan1 = null;
} catch (IOException e) {
// Ups!
} finally {
scan1.close();
scan2.close();
System.out.println("Gotowe!");
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Teraz naprawdę znalazłeś się w kłopotliwym położeniu. Wewnątrz klauzuli
finally wartość scan1 wynosi null. Wywołanie funkcji scan1.close() zakończy
się niepowodzeniem, więc program rzuci wyjątek NullPointerException i prze-
stanie działać jeszcze przed osiągnięciem wywołania funkcji scan2.close(). W naj-
gorszych okolicznościach scan2 nie zostanie zamknięty, a program będzie nadal
blokował plik File2.txt i żaden inny program nie będzie mógł go użyć.
Gdy program korzysta z kilku zasobów (z wielu plików, z bazy danych i pliku lub
z czegokolwiek innego), konstrukcja instrukcji try staje się dość skomplikowana.
Możesz umieszczać instrukcje try w klauzulach catch i stosować wszelkiego ro-
dzaju zwariowane kombinacje. Jednak Java ma lepszy sposób na rozwiązanie tego
problemu: w Javie 7 (i nowszych wersjach) można utworzyć instrukcję try-z-zasobami.
Kod na listingu 13.13 pokazuje, jak to zrobić.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
pracy za pomocą klauzuli finally” pozbyłeś się ostrzeżeń, dopisując instrukcje
dataInStrm.close() i dataOutStrm.close() wewnątrz klauzuli finally. Zamiast
dodawać wywołania metody close, napraw ten problem za pomocą instrukcji
try-z-zasobami.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
384 CZĘŚĆ IV Sprytne techniki Javy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 14
Współdzielenie nazw
między częściami
programu w Javie
M ówiąc o prywatnych polach i metodach (i o tych rzeczach piszę w tym
właśnie rozdziale)…
Jem obiad z kolegami z pracy. „Mogą czytać twoje e-maile” — mówi jeden z kole-
gów. Inny dodaje: „Znają każdą odwiedzaną stronę. Wiedzą, jakie produkty kupu-
jesz, co jesz na obiad, co nosisz, co myślisz. Znają nawet twoje najgłębsze, najciem-
niejsze tajemnice. Wcale nie zdziwiłbym się, gdyby wiedzieli, kiedy umrzesz”.
Trzeci głos włącza się do dyskusji. „Dochodzi do tego, że nie można już wydmu-
chać nosa, żeby ktoś tego nie zarejestrował. Kilka tygodni temu odwiedziłem
stronę internetową, a ta życzyła mi wszystkiego najlepszego z okazji urodzin.
Skąd oni wiedzieli, że to ja, i skąd wiedzieli, że to były moje urodziny?”
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
„No tak — mówi pierwszy facet. — Mam przywieszkę na samochodzie, która
pozwala mi przejeżdżać przez punkty poboru opłat. Wyczuwa, że przejeżdżam
przez taki punkt, i automatycznie obciąża moją kartę kredytową. Co miesiąc dostaję
od firmy wykaz pokazujący, gdzie i kiedy byłem. Jestem zdumiony, że nie po-
dają jeszcze, kogo odwiedzałem i co tam robiłem”.
Myślę sobie cicho. Zastanawiam się, czy nie powiedzieć: „To przecież nonsens.
Osobiście schlebiałoby mi, gdyby mój pracodawca, rząd lub jakaś inna duża firma
tak bardzo się mną interesowały, żeby śledzić każdy mój ruch. Mam trudności ze
zwróceniem uwagi ludzi, kiedy naprawdę tego potrzebuję. A większość agencji,
które prowadzą wykazy wszystkich moich nawyków zakupowych i przeglądania,
nie potrafią nawet przeliterować mojego imienia, gdy wysyłają mi niechcianą
pocztę. »Witaj, chciałbym porozmawiać z Larrym Burgiem. Czy pan Burg jest
w domu?« Szpiegowanie ludzi jest tak naprawdę bardzo nudne. Mogę sobie wy-
obrazić nagłówek na pierwszej stronie gazety The Times: »Autor książki Java dla
bystrzaków nosi podkoszulek na lewą stronę!«. Wielka mi rzecz!”.
Myślę jeszcze przez kilka sekund, a potem mówię: „Chcą nas dorwać. Kamery tele-
wizyjne! Mówię wam, to będzie następne — kamery telewizyjne na całym świecie”.
Modyfikatory dostępu
Jeśli przeczytałeś książkę Java dla bystrzaków aż do tego miejsca, prawdopodobnie
wiesz już jedno: programowanie zorientowane obiektowo jest mistrzem w ukry-
waniu szczegółów. Programiści piszący jeden fragment kodu nie powinni majstro-
wać przy szczegółach w kodzie innego programisty. To nie jest kwestia bezpie-
czeństwa i tajemnicy. To kwestia modułowości. Ukrywając szczegóły, chronisz
zawiłości jednego fragmentu kodu przed modyfikowaniem i popsuciem przez in-
ny fragment kodu. Twój kod jest dostarczany w ładnych, dyskretnych, łatwych
w obsłudze kawałkach. Złożoność tego kodu redukowana jest do minimum. Robisz
mniej błędów, oszczędzasz pieniądze i pomagasz promować pokój na świecie.
W różnych miejscach książki Java dla bystrzaków znajdują się przykłady elementów,
które zostały zadeklarowane jako publiczne. Podobnie jak publiczna celebrytka, pole
zadeklarowane jako publiczne jest szeroko otwarte dla innych. Prawdopodobnie
wiele osób wie, jakiego rodzaju pasty do zębów używał Elvis, natomiast każdy pro-
gramista może skorzystać z publicznego pola, nawet takiego, które nie nazywa
się Elvis.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W języku Java słowa public i private nazywane są modyfikatorami dostępu. I bez
wątpienia widziałeś już pola i metody, w których deklaracjach nie ma żadnych
modyfikatorów dostępu. Mówi się, że tego rodzaju metoda lub pole ma dostęp
domyślny. Wiele przykładów w tej książce używa domyślnego dostępu, nie robiąc
przy tym dużego zamieszania. W większości rozdziałów jest to w porządku, ale nie
w tym. W tym rozdziale szczegółowo opiszę detale dotyczące domyślnego dostępu.
I będziesz mógł także dowiedzieć się o kolejnym modyfikatorze dostępu, który nie
był używany w żadnym innym z wcześniejszych rozdziałów. (Przynajmniej nie
pamiętam, bym go używał we wcześniejszych przykładach). Jest to modyfikator
dostępu chronionego — protected. Tak, ten rozdział omawia niektóre brudne
szczegóły dotyczące dostępu chronionego.
class MyClass {
int myField; //pole (element)
Uwierz mi, proszę, używanie na każdym kroku frazy metody i pola nie jest łatwą
sprawą. O wiele lepiej jest nadać tym rzeczom jedną nazwę i z niej właśnie ko-
rzystać. Dlatego zarówno metody, jak i pola są ogólnie nazywane elementami klasy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
lub
W Javie słowo kluczowe public ma dwa nieco różne znaczenia — jedno znaczenie
jest dla elementów klasy, a drugie jest przeznaczone dla klas. Większość tego
rozdziału dotyczy znaczenia słowa public (i innych podobnych słów kluczowych)
przeznaczonych dla elementów. Ostatnia część tego rozdziału (odpowiednio za-
tytułowana „Modyfikatory dostępu dla klas Javy”) dotyczy znaczenia w kontek-
ście klas.
Jeśli element klasy jest prywatny, to tylko instrukcje znajdujące się w tej samej
klasie mogą odwoływać się bezpośrednio do jego nazwy:
class SomeClass {
private int myField = 10;
}
class SomeOtherClass {
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Jeśli element klasy jest publiczny, to dowolny kod działający na tej samej maszynie
wirtualnej Javy może odwoływać się bezpośrednio do nazwy tego elementu.
class SomeClass {
public int myField = 10;
}
class SomeOtherClass {
//To działa:
System.out.println(someObject.myField);
}
}
RYSUNEK 14.1.
Kilka klas i ich
podklasy
Pól publicznych można łatwo użyć, ale jeszcze łatwiej można ich nadużyć. Naj-
lepszym sposobem projektowania kodu jest odpowiednie ograniczanie dostępu do
każdego pola. Jeśli pole nie musi być publiczne, spróbuj ustawić je jako prywatne.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 14.2.
Zakres kodu,
w którym można
stosować publiczne
pole lub metodę
(zacienione ramki)
RYSUNEK 14.3.
Zakres kodu,
w którym można
stosować prywatne
pole lub metodę
(zacienione ramki)
Jeśli inne klasy muszą uzyskać dostęp lub zmienić wartości tego pola, to dodaj
publiczne metody pobierania i ustalania wartości. I to ładnie prowadzi nas do
następnego akapitu…
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Umieszczanie rysunku w ramce
Chcąc wyjaśnić kwestię modyfikatorów dostępu, będziemy potrzebowali jednego
lub dwóch przykładów. W pierwszym przykładzie z tego rozdziału prawie wszystko
jest publiczne. Dzięki dostępowi publicznemu nie musisz się martwić, kto może
z czego korzystać.
Kod dla tego pierwszego przykładu składa się z kilku części. Pierwsza część,
która znajduje się na listingu 14.1, wyświetla ramkę ArtFrame. Na wierzchu tej
ramki znajduje się element typu Drawing. Jeśli wszystkie elementy są na swoim
miejscu, to uruchomienie kodu z listingu 14.1 spowoduje wyświetlenie okna po-
dobnego do pokazanego na rysunku 14.4.
class ShowFrame {
artFrame.setSize(200, 100);
artFrame.setVisible(true);
}
}
RYSUNEK 14.4.
Ramka typu ArtFrame
Kod z listingu 14.1 tworzy nową instancję klasy ArtFrame. Możesz podejrzewać, że
klasa ArtFrame jest podklasą klasy frame, i w tym przypadku tak właśnie jest.
W rozdziale 9. powiedziano już, że ramki w języku Java są domyślnie niewidoczne.
Tak więc na listingu 14.1 musimy wywołać metodę setVisible, aby instancja
ArtFrame pojawiła się na ekranie.
Zauważ teraz, że listing 14.1 zaczyna się od dwóch deklaracji importu. Pierwsza
deklaracja pozwala skrócić nazwę klasy Drawing z pakietu com.burdbrain.drawings.
Druga deklaracja pozwala skrócić nazwę klasy ArtFrame z pakietu com.burdbrain.
frames.
Jakiś detektyw może sobie pomyśleć: „Musiał napisać więcej kodu (którego tu-
taj nie widzę) i umieścić go w paczkach, które nazwał com.burdbrain.drawings
i com.burdbrain.frames”. I rzeczywiście tak jest! Masz rację. Aby listing 14.1 działał
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
poprawnie, musiałem utworzyć coś o nazwie Drawing i umieścić wszystkie moje
rysunki w pakiecie com.burdbrain.drawings. Potrzebowałem również klasy ArtFrame,
dlatego ją i wszystkie podobne klasy umieszczam w pakiecie com.burdbrain.frames.
No więc, jak naprawdę wygląda klasa Drawing? Cóż, jeśli tak bardzo chcesz wie-
dzieć, to spójrz na listing 14.2.
import java.awt.Graphics;
Kod klasy Drawing jest dość przejrzysty — zawiera ona kilka pól typu int i me-
todę paint. I to wszystko. No cóż, kiedy tworzę klasy, staram się, żeby były proste.
Tak czy inaczej oto kilka uwag na temat mojej klasy Drawing:
Klasa Drawing jest klasą publiczną. Klasa publiczna jest podatna na wtargnięcie
z zewnątrz. Ogólnie rzecz biorąc, unikam używania słowa kluczowego public
przed tworzoną klasą. Niestety na listingu 14.2 muszę zadeklarować moją klasę
Drawing, aby była publiczna. Jeśli tak nie zrobię, to klasy, które nie są zawarte
w pakiecie com.burdbrain.drawings, nie będą mogły korzystać ze wszystkich
dobrodziejstw zawartych w kodzie na listingu 14.2. W szczególności wiersz:
ArtFrame artFrame = new ArtFrame(new Drawing());
pochodzący z listingu 14.1 będzie nieprawidłowy, jeżeli klasa Drawing nie będzie
publiczna.
Więcej informacji na temat klas publicznych i niepublicznych znajdziesz
w podrozdziale „Modyfikatory dostępu dla klas Javy” w dalszej części tego rozdziału.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Kod ma metodę paint. Metoda ta wykorzystuje standardową sztuczkę języka
Java, aby wyświetlać na ekranie różne rzeczy. Parametr g (listing 14.2) nazywa
się buforem graficznym. Wszystko, co ma się pojawić na ekranie, musi zostać
narysowane na buforze graficznym, a potem bufor zostanie przeniesiony
na ekranie komputera.
Oto trochę więcej szczegółów: na listingu 14.2 metoda paint przyjmuje parametr
g. Ten parametr przechowuje instancję klasy java.awt.Graphics. Ponieważ
instancja klasy Graphics jest buforem, to rzeczy, które umieścisz w tym buforze,
zostaną ostatecznie wyświetlone na ekranie. Podobnie jak wszystkie instancje
klasy java.awt.Graphics, ten bufor też ma kilka metod rysowania — a jedną
z nich jest metoda drawOval. Kiedy wywołujesz metodę drawOval, podajesz pozycję
początkową (x pikseli od lewej krawędzi ramki i y pikseli od górnej części ramki).
Możesz także określić rozmiar owalu, podając liczbę pikseli w parametrach width
i height. Wywołanie metody drawOval umieszcza w buforze Graphics mały okrągły
element. Następnie ten bufor Graphics razem z tym małym okrągłym czymś
i innymi rzeczami zostanie wyświetlony na ekranie.
Struktura katalogów
Kod z listingu 14.2 należy do pakietu com.burdbrain.drawings. Umieszczając klasę
w takim w pakiecie, musisz utworzyć odpowiednią strukturę katalogów, która bę-
dzie odzwierciedlała nazwę pakietu.
Jeśli nie będziesz miał swojego kodu w odpowiednich katalogach, otrzymasz odpy-
chający i obrzydliwy komunikat NoClassDefFoundError (błąd, nie znaleziono defi-
nicji klasy). Uwierz mi, ten błąd nigdy nie jest przyjemny. Gdy go zobaczysz, nie
będziesz miał żadnych wskazówek, które pomogłyby Ci się dowiedzieć, gdzie
znajduje się brakująca klasa lub gdzie kompilator spodziewa się ją znaleźć. Jeśli
tylko zachowasz spokój, to wszystko na pewno uda Ci się ustalić. Jeśli jednak
wpadniesz w panikę, to będziesz się w tym grzebać godzinami. Jako doświadczo-
ny programista języka Java mam wiele nieprzyjemnych wspomnień związanych
z ohydnym błędem NoClassDefFoundError.
Tworzenie ramki
W pierwszych trzech listingach tego rozdziału przedstawiam wieloczęściowy
przykład. Ten podrozdział zawiera ostatni z trzech elementów tego przykładu.
Ostatni fragment kodu nie jest kluczowym elementem w poznawaniu modyfikato-
rów dostępu, co stanowi główny temat tego rozdziału. Jeśli więc chcesz pominąć
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 14.5.
Pliki i katalogi
w Twoim projekcie
wyjaśnienie listingu 14.3, możesz to zrobić bez utraty całego wątku przewodniego
tego rozdziału. Jednak z drugiej strony, jeśli chcesz się dowiedzieć coś więcej
o klasach Swing języka Java, to proszę, czytaj dalej.
import java.awt.Graphics;
import javax.swing.JFrame;
import com.burdbrain.drawings.Drawing;
Drawing;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
POSZUKIWANIE PLIKÓW
WE WŁAŚCIWYCH MIEJSCACH
Próbujesz skompilować program z listingu 14.1. Kompilator Java przeszukuje kod i tra-
fia na brakujące elementy. Najpierw jest to coś, co nazywa się ArtFrame. Potem
jeszcze jakieś dziwne Drawing. Listing 14.1 definiuje klasę o nazwie ShowFrame, a nie
o nazwach ArtFrame lub Drawing. Gdzie zatem kompilator ma szukać informacji o klasach
ArtFrame i Drawing?
Jeżeli się nad tym zastanowić, to problem ten może wydawać się zniechęcający. Czy
kompilator powinien poszukiwać na całym dysku twardym plików o nazwach Art-
Frame.java lub Drawing.class? Jak duży jest Twój nowy dysk twardy? 500 GB? 750 GB?
6 000 000 GB? A co z odniesieniami do plików na dyskach sieciowych? Przestrzeń do
przeszukania jest potencjalnie nieograniczona. Co się stanie, jeśli kompilator osta-
tecznie rozwiąże wszystkie te problemy? Spróbujesz uruchomić swój kod, a wirtu-
alna maszyna Javy (JVM) ponownie rozpocznie przeszukiwanie. (Chcąc uzyskać do-
datkowe informacje na temat wirtualnej maszyny Java, popatrz do rozdziału 2).
W ramach ograniczania tego problemu Java definiuje coś, co nazywa się zmienną
środowiskową CLASSPATH. Jest to lista miejsc, w których kompilator i JVM będą szu-
kały naszego kodu. Istnieje kilka sposobów ustalania wartości zmiennej CLASSPATH.
Niektórzy programiści tworzą nową zmienną CLASSPATH za każdym razem, gdy
uruchamiają program Javy. Inni tworzą ogólnosystemową zmienną CLASSPATH. (Jeśli
znasz zmienną PATH na komputerach z systemem Windows i Unix, pewnie już wiesz,
jak to działa). Tak czy inaczej kompilator i JVM potrzebują listy miejsc do szukania
kodu, a bez takiej listy narzędzia Java nigdzie nic nie znajdą. Nie doszukają się takich
klas jak ArtFrame czy Drawing i tym samym pojawi się komunikat cannot find
symbol message (nie można znaleźć symbolu) lub komunikat NoClassDefFoundError
(nie znaleziono definicji klasy) i będziesz bardzo niezadowolony.
Jedyną nową nazwą w kodzie z listingu 14.3 jest słowo paint. Metoda paint wy-
wołuje inną metodę paint — tę należącą do obiektu Drawing. Obiekt ArtFrame
tworzy ruchome okno na ekranie komputera. To, co jest rysowane wewnątrz tego
okna, zależy od obiektu Drawing przekazanego do konstruktora ArtFrame.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Tak właśnie działają obiekty klasy javax.swing.JFrame. Na listingu 14.3 metoda
paint jest wywoływana zza kulis. Następnie wywołuje ona metodę paint obiektu
Drawing, która zajmuje się rysowaniem owalu w ramce. W ten sposób otrzymu-
jesz to, co widać na rysunku 14.4.
Chcąc skorzystać z kodu znajdującego się na listingu 14.4, pamiętaj, proszę, aby
zmienić jeden z wierszy kodu z listingu 14.1. Zmieniasz wiersz do poniższej postaci:
Listing 14.4 definiuje podklasę oryginalnej klasy Drawing. W tej podklasie zastępu-
jesz pola width i height z klasy nadrzędnej oraz jej metodę paint. Otrzymana w ten
sposób ramka została pokazana na rysunku 14.6.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 14.6.
Kolejna ramka
artystyczna
Tak na marginesie, możesz zauważyć, że kod z listingu 14.4 nie zaczyna się od
deklaracji pakietu. Oznacza to, że cała kolekcja plików pochodzi z następujących
trzech pakietów:
Zawsze obecny pakiet bez nazwy. W Javie, gdy nie rozpoczynasz pliku deklaracją
pakietu, cały kod z tego pliku umieszczany jest w wielkim pakiecie bez nazwy.
Kody z listingów 14.1 i 14.4 znajdują się w tym samym pakiecie bez nazwy.
Dodatkowo powiem, że większość listingów z pierwszych trzynastu rozdziałów
tej książki znajduje się w tym samym pakiecie bez nazwy.
W tym momencie Twój projekt ma dwie klasy rysujące: oryginalną klasę Drawing
i nową klasę DrawingWide. Mimo ich wielkiego podobieństwa żyją one sobie w dwóch
niezależnych pakietach. To nie jest zaskakujące. Klasa Drawing, opracowana przez
Twoich przyjaciół z firmy Burd Brain Consulting, mieszka w pakiecie, którego
nazwa zaczyna się od com.burdbrain. Natomiast sam opracowałeś tę drugą klasę
DrawingWide, więc nie powinieneś umieszczać jej w pakiecie com.burdbrain.
Tak czy inaczej podklasa DrawingWide kompiluje się i działa zgodnie z naszym
planem. Idziesz do domu, promieniejąc z zadowolenia i mając pewność, że na-
pisałeś użyteczny, działający kod.
Domyślny dostęp
Jeśli czytasz te akapity po kolei, to wiesz, że ostatni z przykładów kończy się szczę-
śliwie. Kod z listingu 14.4 działa jak marzenie. Wszyscy są szczęśliwi, w tym i mój
wspaniały redaktor — Paul Levesque.
Ale poczekaj! Czy zastanawiasz się czasem, jak wyglądałoby życie, gdybyś nie
wybrał tej konkretnej ścieżki kariery, nie spotykał się z kimś lub nie przeczytał
tej książki serii Dla bystrzaków? W tym podrozdziale cofam nieco zegar, aby po-
kazać, co by się stało, gdyby na listingu 14.2 zostało pominięte jedno ze słów.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Radzenie sobie z różnymi wersjami programu może wywołać zawroty głowy,
więc zaczynam omawianie od opisu tego, co już mamy. Po pierwsze, mamy klasę
Drawing. W tej klasie pola nie są zadeklarowane jako publiczne i mają domyślny do-
stęp. Klasa Drawing mieszka w pakiecie com.burdbrain.drawings (patrz listing 14.5).
import java.awt.Graphics;
Problem polega na tym, że cała rzecz legnie w gruzach. Kod z listingu 14.6 nie
kompiluje się. Zamiast tego pojawiają się następujące komunikaty o błędach:
Kod nie kompiluje się, ponieważ do pola z domyślnym dostępem nie można się
odwoływać spoza jego pakietu. Nie może tego zrobić nawet podklasa klasy zawie-
rającej to pole. To samo dotyczy metod, które mają domyślny dostęp.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Pola i metody klasy nazywane są elementami klasy. A zasady dostępu do nich —
domyślne i wszystkie inne — dotyczą wszystkich elementów klas.
Zasady dostępu, które opisuję w tym rozdziale, nie dotyczą lokalnych zmiennych
metod. Dostęp do zmiennej lokalnej metody można uzyskać tylko w ramach jej
własnej metody.
W języku Java domyślnym dostępem elementu klasy jest dostęp z całego pakietu.
Element zadeklarowany bez poprzedzających go słów takich jak public, private
lub protected jest dostępny w pakiecie, w którym znajduje się jego klasa. Tę sytu-
ację przedstawiono na rysunkach 14.7 i 14.8.
RYSUNEK 14.7.
Pakiety podzielone na
hierarchie podklas
RYSUNEK 14.8.
Zakres kodu,
w którym można
zastosować domyślne
pole lub metodę
(zacienione miejsca
na rysunku)
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Nazwy pakietów ze wszystkimi ich kropkami i częściami mogą być nieco mylące.
Na przykład, kiedy piszemy program reagujący na kliknięcie przycisku, zwykle
importujemy klasy z dwóch oddzielnych pakietów. W jednym wierszu możemy
mieć instrukcję import java.awt.*;, a w kolejnym instrukcję import java.awt.
event.*;. Importowanie wszystkich klas z pakietu java.awt nie powoduje au-
tomatycznego importowania klas z pakietu java.awt.event.
Cóż, dzisiaj jest mój szczęśliwy dzień. Ktoś z firmy Burd Brain Consulting wysłał
mi podklasę klasy Drawing. Zasadniczo jej kod nie różni się od tego z listingu
14.6. Jedyna różnica polega na tym, że ta nowa klasa DrawingWideBB znajduje się
w pakiecie com.burdbrain.drawings. Kod pokazano na listingu 14.7. Chcąc uru-
chomić ten kod, muszę nieco zmodyfikować listing 14.1 za pomocą wiersza:
import java.awt.Graphics;
Kiedy uruchamiasz kod z listingu 14.7 wraz z klasą Drawing (na listingu 14.5),
wszystko działa dobrze. Powód? Drawing i DrawingWideBB są w tym samym pakiecie.
Spójrz troszkę wstecz na rysunek 14.8 i zauważ zacieniony obszar obejmujący
cały pakiet. Kod w klasie DrawingWideBB ma pełne prawo do używania pól x i y,
które są zdefiniowane z domyślnym dostępem w klasie Drawing, ponieważ Drawing
i DrawingWideBB znajdują się w tym samym pakiecie.
Chcąc użyć klasy DrawingWideBB w kodzie na listingu 14.7, należy wprowadzić dwie
zmiany w kodzie pierwotnym znajdującym się na listingu 14.1. Zmień pierwszą
deklarację importu na następującą:
import com.burdbrain.drawings.DrawingWideBB;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zmień także wywołanie konstruktora obiektu ArtFrame na new ArtFrame(newDrawing
WideBB()).
Dostęp chroniony
Kiedy po raz pierwszy poznałem język Java, myślałem, że słowo protected (chro-
niony) oznacza miłe i bezpieczne lub coś w tym rodzaju. „Wow, to pole jest chro-
nione. Trudno będzie się do niego dostać”. No cóż, to rozumowanie okazało się
być błędne. W języku Java element chroniony jest mniej ukryty, mniej bezpiecz-
ny i dostępny do użycia w większej ilości klas niż tylko w tej, w której jest zdefi-
niowany dostęp domyślny. Innymi słowy, dostęp chroniony jest bardziej dozwo-
lony niż dostęp domyślny. Dla mnie terminologia jest myląca. Ale tak to już jest.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 14.8. Pola chronione
package com.burdbrain.drawings;
import java.awt.Graphics;
Kod z listingu 14.8 definiuje klasę Drawing, a ten z listingu 14.9 definiuje klasę
DrawingWide, która jest podklasą klasy Drawing.
Porównaj teraz rysunki 14.8 i 14.9, przy czym zwróć uwagę na zacienione miej-
sce na rysunku 14.9. Podklasa może uzyskiwać dostęp do chronionego elementu
klasy, nawet jeśli ta podklasa należy do innego pakietu.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
RYSUNEK 14.9.
Zakres kodu,
w którym można
zastosować
chronione pole lub
metodę (zacienione
miejsca)
Dla elementów klasy dostęp prywatny jest najbardziej restrykcyjny, następny w ko-
lejności jest dostęp domyślny, a dalej dostęp chroniony i wreszcie dostęp publiczny.
import com.burdbrain.frames.ArtFrame;
class ShowFrameWideBB {
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Już spieszę wyjaśnić. Klasa ShowFrameWideBB z listingu 14.10 znajduje się w tym
samym pakiecie co klasa Drawing (pakiet com.burdbrain.drawings), ale nie jest ona
podklasą klasy Drawing.
Kod z listingu 14.10 ma metodę main, będącą częścią klasy, która znajduje się
w pakiecie com.burdbrain.drawings. W większości zintegrowanych środowisk pro-
gramistycznych (IDE) nie trzeba się szczególnie zastanawiać nad sposobem uru-
chomienia metody main zawartej w nazwanym pakiecie. Jeśli jednak uruchamiasz
programy z wiersza poleceń, to konieczne może okazać się wpisanie w pełni kwali-
fikowanej nazwy klasy. Na przykład, aby uruchomić kod z listingu 14.10, musisz
wpisać instrukcję java com.burdbrain.drawings.ShowFrameWideBB.
Na listingu 14.2 rysuję okrąg na ramce. Aby wypełnić okrąg kolorem zielonym,
użyj metod setColor i fillOval z klasy Graphics:
g.setColor(Color.GREEN)
g.fillOval(x, y, width, height);
Wartości takie jak Color.GREEN należą do klasy Color z pakietu java.awt.
Utwórz ramkę wyświetlającą sygnał drogowy z zielonymi, żółtymi i czerwonymi
światłami.
Opisująca książkę klasa Book ma tytuł (typ String) i autora (instancja klasy
Author). Z kolei klasa Author ma nazwisko (typ String) oraz tablicę ArrayList
przechowującą instancje klasy Book. Oddzielna klasa zawiera metodę main,
która tworzy kilka książek i kilku autorów. Metoda main wyświetla również
informacje o książkach i ich autorach.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Umieść każdą klasę we własnym pakiecie. Tam, gdzie jest to możliwe, ustaw pola
jako prywatne i udostępniaj publiczne metody pobierające i ustalające.
Klasa Item ma nazwę (String) i wykonawcę (instancja klasy Artist). Każda instancja
klasy Artist ma nazwisko (String) i tablicę ArrayList przechowującą elementy
typu Item.
Klasy Song i Album są podklasami klasy Item. Każda instancja klasy Song ma
określony gatunek (wartość enum o nazwie Genre). Wartości Genre to ROCK, POP,
BLUES i CLASSICAL. Każda instancja klasy Album ma tablicę ArrayList z utworami,
czyli instancjami klasy Song.
I wreszcie klasa Playlist ma tablicę ArrayList przechowującą elementy typu Item.
Utwórz te klasy. W osobnej klasie konstruuj instancje każdej klasy i wyświetlaj
informacje o tych instancjach na ekranie.
package com.allmycode.things;
import com.allyourcode.stuff.Stuff;
import com.allyourcode.stuff.morestuff.MoreStuff;
package com.allyourcode.stuff;
import com.allyourcode.stuff.morestuff.MoreStuff;
void aMethod() {
new MoreStuff().myMethod();
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
}
package com.allyourcode.stuff.morestuff;
import com.allmycode.things.Things;
package com.allmycode.things;
Klasa może być publiczna lub niepubliczna. Jeśli zobaczysz coś takiego:
class ShowFrame
Klasy publiczne
Jeśli klasa jest publiczna, możesz się do niej odwoływać z dowolnego miejsca
w kodzie. Oczywiście obowiązują pewne ograniczenia. Musisz przestrzegać wszyst-
kich zasad zawartych w punkcie „Struktura katalogów”. Musisz także prawidło-
wo odwoływać się do klas zawartych w pakietach. Na przykład na listingu 14.1
możesz pisać tak:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
import com.burdbrain.drawings.Drawing;
import com.burdbrain.frames.ArtFrame;
...
ArtFrame artFrame = new ArtFrame(new Drawing());
ale możesz to również zrobić bez deklaracji importu i teraz pisać tak:
com.burdbrain.frames.ArtFrame artFrame =
new com.burdbrain.frames.ArtFrame(new com.burdbrain.drawings.Drawing());
Tak czy inaczej Twój kod musi zawierać informację, że klasy ArtFrame i Drawing
znajdują się w nazwanych pakietach.
Klasy niepubliczne
Jeśli klasa jest niepubliczna, możesz odwołać się do niej tylko z kodu znajdującego
się w tym samym pakiecie.
package com.burdbrain.drawings;
import java.awt.Graphics;
class Drawing {
public int x = 40, y = 40, width = 40, height = 40;
package com.burdbrain.drawings;
Z uwagi na fakt, że oba fragmenty kodu znajdują się w tym samym pakiecie com.
burdbrain.drawings, dostęp klasy DrawingWideBB do niepublicznej klasy Drawing nie
stanowił tu żadnego problemu.
Ale potem próbowałem skompilować kod z listingu 14.3, a ten zaczyna się od
deklaracji:
package com.burdbrain.frames;
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Tego kodu nie ma w pakiecie com.burdbrain.drawings. I kiedy komputer dotarł do
wiersza:
Drawing drawing;
Rzeczy nigdy nie są tak proste, jakimi się wydają. Zasady, które opisuję w tym
rozdziale, dotyczą niemal każdej klasy pokazanej w tej książce. Niestety język
Java udostępnia fantazyjną funkcję zwaną klasami wewnętrznymi, a takie klasy
podlegają już zupełnie innym zasadom. Na szczęście typowy początkujący pro-
gramista ma niewielki kontakt z klasami wewnętrznymi. Jedyne klasy tego typu
znajdują się w rozdziale 15. tej książki (nie wspominam o klasach wewnętrznych
przebranych za typy enum). Na razie możesz żyć całkiem dobrze i szczęśliwie,
stosując się do zasad opisanych w tym podrozdziale.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 15
Fantazyjne typy
referencyjne
W
poprzednich rozdziałach mogłeś przeczytać o tym, co łączy pracowników
zatrudnionych w pełnym i niepełnym wymiarze godzin. Chodzi o to, że
klasy FullTimeEmployee i PartTimeEmployee mogą rozszerzać klasę Employee.
Dobrze jest o tym wiedzieć, jeżeli prowadzisz małą firmę, ale co w przypadku,
gdy nie prowadzisz żadnej firmy? A co zrobić w sytuacji, gdy opiekujesz się zwie-
rzętami domowymi?
W tym rozdziale opisano opiekę nad zwierzętami domowymi i inne palące tematy.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Interfejs API języka Java definiuje tysiące typów referencyjnych. Pisząc
program w tym języku, definiujesz nowe typy referencyjne.
Typ String jest typem referencyjnym, podobnie jak Scanner, JFrame, ArrayList
i File. Moja klasa DummiesFrame także jest typem referencyjnym. W rozdziale 7.
tworzysz własne typy referencyjne Employee, FullTimeEmployee i PartTimeEmployee.
Twój pierwszy program z tekstem Pokochasz język Java! ma metodę main
umieszczoną wewnątrz klasy, która też jest typem referencyjnym. Możesz nie
zdawać sobie z tego sprawy, ale każda tablica także jest typem referencyjnym.
Ale weź pod uwagę związek między redaktorem a autorem. Redaktor mówi: „Pod-
pisując tę umowę, zgadzasz się przesłać ukończony manuskrypt do 9 stycznia”.
Pomimo wszelkich wymówek, które autor podaje przed upływem terminu (i wierz
mi, autorzy mają naprawdę wiele wymówek), związek między redaktorem a au-
torem to pewne zobowiązanie. Autor zgadza się wziąć na siebie pewne obowiązki;
i musi je wypełnić, jeżeli chce nadal być autorem. (Nawiasem mówiąc, w tym
akapicie nie ma żadnego podtekstu — wcale a wcale).
Teraz pomyślmy o Barrym Burdzie. Kto taki? Barry Burd — ten facet, który pisze
książkę Java dla bystrzaków i jeszcze niektóre inne książki serii Dla bystrzaków.
Jest profesorem college’u, a także autorem. Chcielibyśmy odzwierciedlić tę sy-
tuację w programie w języku Java, jednak język ten nie obsługuje wielokrotnego
dziedziczenia. Nie możesz zmusić Barry’ego do jednoczesnego rozszerzenia klasy
profesorskiej Professor i klasy autorskiej Author.
Na szczęście dla Barry’ego język Java ma interfejsy. Interfejs jest rodzajem typu
referencyjnego. I rzeczywiście, kod tworzący interfejs wygląda bardzo podobnie
do kodu tworzącego klasę:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Interfejs jest bardzo podobny do klasy, a jednocześnie interfejs jest całkiem inny.
(Jakieś inne rewelacje? Krowa jest jak planeta, ale jest całkiem inna. Krowy muczą;
planety wiszą w kosmosie).
W każdym razie, kiedy czytasz słowo interface, możesz zacząć od myślenia o klasie.
Następnie zapamiętaj sobie, że:
Klasa może rozszerzać tylko jedną klasę nadrzędną, ale może implementować
wiele interfejsów.
Dwa interfejsy
Wyobraź sobie dwa różne rodzaje danych. Pierwszy to kolumna liczb, która po-
chodzi z tablicy. Drugi to tabela (z wierszami i kolumnami), która pochodzi z pliku
na dysku. Co te dwie rzeczy mogą mieć ze sobą wspólnego?
Nic wiem jak Ty, ale ja chciałbym wyświetlać oba rodzaje danych. Mogę zatem
napisać kod przygotowujący umowę. Umowa ta będzie miała taką treść: „Ktokol-
wiek podpisuje tę umowę, wyraża zgodę na stosowanie metody display”. W ko-
dzie pokazanym na listingu 15.1 deklaruję interfejs Displayable.
Poczekaj tylko jedną minutkę! Deklaracja metody display z listingu 15.1 ma na-
główek, ale nie ma ciała. Po deklaracji display() nie ma nawiasów klamrowych,
tylko samotny średnik. O co tutaj chodzi?
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Chcąc odpowiedzieć na to pytanie, pozwolę, by kod z listingu 15.1 mówił sam za
siebie. Jeśli kod z listingu mógłby przemówić, oto co by nam powiedział:
Interfejs Displayable jest jak umowa prawna. Interfejs ten nie mówi Ci, co ma
już implementująca go klasa. Zamiast tego interfejs Displayable informuje, co
klasa implementująca musi zadeklarować we własnym kodzie.
Tworzę więc kod zawierający drugą umowę. Druga umowa mówi nam: „Ktokol-
wiek podpisuje tę umowę, wyraża zgodę na stosowanie metody summarize”. Na
listingu 15.2 deklaruję interfejs Summarizable.
Metoda określona w deklaracji interfejsu może nie mieć własnego ciała. Metoda
bez ciała nazywa się metodą abstrakcyjną.
Implementowanie interfejsów
Kod z listingu 15.3 implementuje interfejsy Displayable i Summarizable oraz defi-
niuje ciała metod display i summarize.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 15.3. Implementowanie dwóch interfejsów
public class ColumnOfNumbers implements Displayable, Summarizable {
double numbers[];
@Override
public void display() {
for (double d : numbers) {
System.out.println(d);
}
}
@Override
public String summarize() {
double total = 0.0;
for (double d : numbers) {
total += d;
}
return Double.toString(total);
}
}
Kod pokazany na listingu 15.4 zawiera kolejną klasę, która implementuje inter-
fejsy Displayable i Summarizable.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public class Table implements Displayable, Summarizable {
Scanner diskFile;
ArrayList<String> lines = new ArrayList<>();
} catch (FileNotFoundException e) {
e.printStackTrace();
}
while (diskFile.hasNextLine()) {
lines.add(diskFile.nextLine());
}
}
@Override
public void display() {
for (String line : lines) {
System.out.println(line);
}
}
@Override
public String summarize() {
return lines.get(0);
}
}
displayMe(column);
summarizeMe(column);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Table table = new Table("MyTable.txt");
displayMe(table);
summarizeMe(table);
}
RYSUNEK 15.1.
Plik MyTable.txt
RYSUNEK 15.2.
Wynik działania
programu
z listingu 15.5
A teraz proszę podziwiać metodę displayMe z listingu 15.5. Jaki parametr przyj-
muje ta metoda? Czy będzie to typ ColumnOfNumbers? Otóż nie. A może taki typ jak
Table? Także nie.
Metoda displayMe nic nie wie o instancjach klasy ColumnOfNumbers, ani o instan-
cjach klasy Table. Ta metoda wie jednak o rzeczach, które implementują interfejs
Displayable, o czym informuje nas lista parametrów metody. Kiedy do metody
displayMe przekazujesz coś, co implementuje interfejs Displayable, to metoda już
wie, co może z tym zrobić. Metoda displayMe jest w stanie wywołać metodę display
swojego parametru, ponieważ parametr tego obiektu ma zagwarantowaną metodę
display.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
To samo dotyczy także metody summarizeMe, pochodzącej z listingu 15.5. Skąd
wiesz, że możesz wywołać summarizable.summarize() w ciele metody summarizeMe?
Możesz to zrobić, ponieważ parametr summarizable musi mieć metodę summarize().
Gwarantują to reguły dotyczące interfejsów Javy.
Następnie utwórz klasę DeletableTable jako podklasę klasy Table. Oprócz wszyst-
kich rzeczy, które robi ta klasa Table, klasa DeletableTable implementuje również
interfejs Deletable. Kiedy usuwasz (delete) tabelę, usuwasz także wszystkie jej
wiersze oprócz pierwszego wiersza (nagłówka tabeli). (Podpowiedź: jeśli metodę
remove listy lines będziesz wywoływać, zaczynając od pierwszego wiersza i prze-
chodząc do wiersza lines.size(), to nie będziesz zadowolony z otrzymanych wy-
ników. Wywołanie metody remove natychmiast zmodyfikuje listę i tym samym może
zepsuć pętlę).
Klasy abstrakcyjne
Czy istnieje takie coś, co możesz wymyślić, a będzie to dotyczyć zwierząt każ-
dego rodzaju? Jeśli jesteś biologiem, być może będziesz w stanie. Ale jeśli jesteś
programistą, możesz mieć niewiele do powiedzenia na ten temat. Jeśli mi nie
wierzysz, rozważ cudowną różnorodność życia na planecie Ziemia:1
Robak Pompeii żyje w podwodnej rurce. Temperatura przy głowie robaka wynosi
około 22 stopni Celsjusza. Ale na drugim końcu ciała robaka temperatura wody
wynosi zwykle 80 stopni Celsjusza. Jeśli znasz osobiście jednego z tych robaczków,
nie kupuj dla niego ciepłych skarpet.
1
Zajrzyj na strony smithsonianmag.com/science-nature/ethiopias-exotic-monkeys-147893502,
https://pl.wikipedia.org/wiki/Alvinella_pompejana, http://psychologytoday.com/blog/choke/
201207/how-humans-learn-lessons-the-sea-squirt oraz https://www.esa.int/Our_Activities/
Human_and_Robotic_Exploration/Research/Tiny_animals_survive_exposure_to_space.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
DWA RODZAJE METOD
W deklaracji interfejsu każda metoda bez ciała jest nazywana metodą abstrakcyjną.
Jeśli korzystasz z wersji Java 8 lub nowszej, możesz umieścić w deklaracji interfejsu
również metody z ciałem. Metoda z ciałem nazywana jest metodą domyślną. W kodzie
interfejsu każda deklaracja metody domyślnej zaczyna się od słowa kluczowego
default.
public interface MyInterface {
void method1();
Żachwy — tryskacz morski żyje przez część swojego życia jako zwierzę.
W pewnym momencie swojego cyklu życia przyczepia się na stałe do skały,
a następnie trawi własny mózg, skutecznie przekształcając się w roślinę.
Mały niesporczak może przetrwać 12 dni (a może nawet więcej) bez atmosfery
w próżni kosmosu. Nawet promieniowanie kosmiczne mu nie zaszkodzi. Właśnie
tym chcę być w następnym życiu — niesporczakiem.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
this.weight = weight;
this.sound = sound;
}
}
Podczas wpisywania kodu dla klasy Animal musiałem się zatrzymać i poprawić
kilka literówek. Błędy nie były tak naprawdę moją winą. Mój kot chodził tam i z po-
wrotem po klawiaturze komputera. I to doprowadziło mnie od tematu wszystkich
zwierząt do tematu zwierząt domowych.
Zwierzę domowe to zwierzę. I tak każde zwierzę domowe ma imię — jak Pikuś,
Mikuś lub Pysia. I każde zwierzę domowe ma także odpowiednią procedurę dba-
nia o nie.
W pierwszym wierszu listingu 15.7 słowo kluczowe abstract mówi Javie, że HousePet
jest klasą abstrakcyjną. Z uwagi na to, że HousePet jest klasą abstrakcyjną, może
mieć metodę abstrakcyjną. A na listingu 15.7 howToCareFor jest metodą abstrakcyjną,
która ma nagłówek, ale nie ma ciała. W deklaracji metody abstrakcyjnej nie ma
także nawiasów klamrowych. W miejscu, w którym normalnie pojawiałyby się
nawiasy klamrowe, jest tylko średnik.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Co się zatem stanie, gdy spróbujesz wywołać metodę howToCareFor? No cóż, tak
naprawdę nie ma możliwości wywołania metody howToCareFor z listingu 15.7.
W rzeczywistości nawet nie można utworzyć instancji klasy abstrakcyjnej, zade-
klarowanej na listingu 15.7. Poniższy kod jest nieprawidłowy:
Klasa abstrakcyjna nie ma własnego życia. Chcąc użyć klasy abstrakcyjnej, musisz
utworzyć zwykłą (nieabstrakcyjną) klasę, która będzie rozszerzać klasę abstrakcyj-
ną. W zwykłej klasie wszystkie metody mają swoje ciała. Dzięki temu wszystko
działa.
A zatem, aby użyć klasy HousePet z listingu 15.7, musisz utworzyć klasę rozszerza-
jącą klasę HousePet. Kod z listingu 15.8 rozszerza abstrakcyjną klasę HousePet i defi-
niuje ciało dla metody o nazwie howToCareFor.
@Override
public void howToCareFor() {
System.out.print(name + " wymaga ");
System.out.println(" " + walksPerDay + " spacerów dziennie.");
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Oprócz tego, że każdy pies ma imię, wagę i swój dźwięk (name, weight, sound),
codziennie chodzi też na spacer określoną liczbę razy. A teraz, dzięki treści me-
tody howToCareFor, już wiesz, co oznacza opieka nad psem: oznacza codzienne
chodzenie na spacer określoną liczbę razy. Dobrze, że metoda howToCareFor jest
abstrakcyjna w klasie HousePet. Niekoniecznie chcesz chodzić z jakimś innym
zwierzakiem.
Weźmy na przykład kota domowego. „Opieka” nad kotem może oznaczać, że nie
należy zbyt często zawracać mu głowy. A koty mają zupełnie inne cechy — ce-
chy, które nie dotyczą psów. Na przykład niektóre koty wychodzą na zewnątrz,
a inne nie. Możesz ustawić wartość walksPerDay na 0 dla kota siedzącego w domu,
ale to będzie trochę jak oszustwo. Zamiast tego każdy kot może mieć wartość
boolean reprezentującą status kota wychodzącego na zewnątrz lub siedzącego
w domu. Kod na listingu 15.9 przybliża nam ten temat.
@Override
public void howToCareFor() {
System.out.println(
name + (isOutdoor ? " wychodzi " : " nie wychodzi ") + " na dwór.");
}
}
Zarówno klasa Dog, jak i klasa Cat są podklasami klasy HousePet. Z powodu de-
klaracji metody abstrakcyjnej na listingu 15.7 obie te klasy muszą mieć metodę
howToCareFor. Natomiast metody howToCareFor w obu klasach są zupełnie inne,
jedna metoda korzysta z pola walksPerDay, a druga metoda używa pola isOutdoor.
A ponieważ metoda howToCareFor klasy HousePet jest abstrakcyjna, nie będzie miała
domyślnego zachowania. Klasy Dog i Cat muszą implementować własne metody
howToCareFor albo nie będą mogły rozszerzać klasy HousePet.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
musiałbym przygotować różne egzotyczne przykładowe programy. Dlatego w ni-
niejszym rozdziale upraszczam tę sprawę i piszę, że (a) klasa, która rozszerza
klasę abstrakcyjną, musi zapewniać treści dla metod abstrakcyjnych klasy abs-
trakcyjnej, oraz (b) klasa, która implementuje interfejs, musi zapewniać treści dla
metod abstrakcyjnych interfejsu. To nie do końca prawda, ale na razie wystarczy.
Jeśli mieszkasz w bardzo małym mieszkaniu, możesz nie mieć miejsca dla psa lub
kota. W takim przypadku kod z listingu 15.10 będzie jak najbardziej dla Ciebie.
@Override
public void howToCareFor() {
System.out.println(name + " musi mieć codzienne karmienie.");
}
}
Mógłbym tu kontynuować tworzenie podklas klasy HousePet. Wiele lat temu nasza
córka miała myszki domowe. Opieka nad myszkami oznaczała trzymanie kota
z dala od nich.
dog1.howToCareFor();
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
dog2.howToCareFor();
cat1.howToCareFor();
fish1.howToCareFor();
dog1.about();
dog2.about();
cat1.about();
fish1.about();
}
}
RYSUNEK 15.3.
Proszę nie głaskać
niesporczaka
Zauważ, jak kod z listingu 15.11 płynnie i bez wysiłku wywołuje wiele wersji me-
tody howToCareFor. W efekcie wywołań dog1.howToCareFor() i dog2.howToCareFor()
Java wykonuje metodę z listingu 15.8. Wywołanie cat1.howToCareFor() nakazuje
Javie wykonać metodę z listingu 15.9, a wywołanie fish1.howToCareFor() sprawia,
że Java wykonuje metodę z listingu 15.10 — to tak, jakby mieć dużą instrukcję if bez
pisania jej kodu. Kiedy dodajesz nową klasę dla myszki domowej, nie musisz po-
większać istniejącej już instrukcji if. Nie musisz, bo takiej instrukcji po prostu nie ma.
Zwróć też uwagę na to, że metoda about w abstrakcyjnej klasie HousePet dokład-
nie wie, który obiekt ją wywołał. Na przykład po wywołaniu dog1.about() z li-
stingu 15.11 ogólna metoda about klasy HousePet wie, że dźwięk wydawany przez
dog1 to Hau. I wszystko bardzo ładnie się układa.
Utwórz klasę abstrakcyjną o nazwie Shape. Klasa Shape ma pole size (typu int)
i abstrakcyjną metodę show. Rozszerz abstrakcyjną klasę Shape w dwóch innych
klasach: klasie Square i klasie Triangle. W treści definicji klas Square i Triangle
umieść kod, który będzie tworzyć tekstowe rysunki danego kształtu. Na przykład
klasa Square o rozmiarze 5 wyglądać będzie następująco:
---------
| |
| |
| |
---------
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
A klasa Triangle o wielkości 2 będzie wyglądać tak:
/\
/ \
----
Pierwszą rzeczą do zrobienia jest zapamiętanie, że nikt nie uczy się o obiekto-
wych pojęciach programowania bez dużej praktyki w pisaniu kodu. Jeśli czytasz
ten rozdział i jesteś zdezorientowany, może to być dobra rzecz. Oznacza to, że
zrozumiałeś na tyle, by wiedzieć, jak skomplikowane są te rzeczy. Im więcej kodu
napiszesz, tym wygodniej będzie Ci korzystać z klas, interfejsów i tych wszystkich
innych pomysłów.
Zarówno interfejsy, jak i klasy abstrakcyjne mają metody abstrakcyjne. Być może
zastanawiasz się, jak zdecydować, czy masz zadeklarować interfejs, czy też klasę
abstrakcyjną. W rzeczywistości możesz zapytać trzech profesjonalnych progra-
mistów, czym różnią się interfejsy i klasy abstrakcyjne. Jeśli to zrobisz, zapewne
uzyskasz pięć różnych odpowiedzi. (Tak, pięć odpowiedzi, nie trzy).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
TABELA 15.1. Używanie (lub nieużywanie) metod abstrakcyjnych
nie możesz sprawić, żeby klasa Dog rozszerzała również klasę Friend. Ale możesz
zmusić klasę Dog do implementowania interfejsu Befriendable. A następnie bę-
dziesz mógł sprawić, że ta sama klasa Dog zaimplementuje interfejs Trainable.
(Nawiasem mówiąc, próbowałem zmusić moją klasę Cat do implementacji inter-
fejsu Trainable, ale z jakiegoś powodu nigdy nie chciała działać).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
A jeśli chcesz jeszcze jaskrawszej różnicy między interfejsem a klasą abstrakcyjną,
mam dla Ciebie coś takiego: interfejs nie może zawierać żadnych niestatycznych
pól. Na przykład, jeśli klasa HousePet z listingu 15.7 byłaby interfejsem, to nie
mogłaby mieć pola name. Po prostu nie byłoby to dozwolone.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
426 CZĘŚĆ IV Sprytne techniki Javy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 16
Reagowanie
na naciśnięcia klawiszy
i kliknięcia myszą
P
od koniec lat 80. kupiłem moją pierwszą mysz. Zapłaciłem 100 dolarów, a po-
nieważ tak naprawdę nie potrzebowałem myszy, przed zakupem skonsulto-
wałem się z żoną. (W tym czasie mój komputer działał w hybrydowym śro-
dowisku tekstowo-okienkowym. Wszystko, co mogłem zrobić za pomocą tej myszy,
równie dobrze mogłem zrobić za pomocą klawisza Alt).
Teraz mamy XXI wiek. Za moich ostatnich dziesięć myszy nie zapłaciłem ani gro-
sza. Zwykłe modele po prostu same pojawiają się na moim biurku. W lokalnym
sklepie komputerowym na wyprzedaży było kilka egzotycznych modeli myszy.
Jedna kosztowała mnie całe 10 dolarów po uwzględnieniu rabatu wynoszącego
także 10 dolarów.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
No dalej… Naciśnij ten przycisk
W poprzednich rozdziałach tworzę okna, które niewiele robią. Typowe okno wy-
świetla pewne informacje, ale nie zawiera żadnych elementów interaktywnych.
Teraz nadszedł czas, aby to wszystko zmienić. Pierwszym przykładem z tego
rozdziału jest okno z przyciskiem. Kiedy użytkownik kliknie ten przycisk, to do
diaska, będzie się działo. Kod takiego projektu pokazano na listingu 16.1, a ko-
rzystającą z niego metodę main umieściłem na listingu 16.2.
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(textField);
add(button);
add(label);
button.addActionListener(this);
pack();
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
String textFieldText = textField.getText();
if (Integer.parseInt(textFieldText)==randomNumber) {
button.setEnabled(false);
textField.setText(textField.getText() + " Tak!");
textField.setEnabled(false);
} else {
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
textField.setText("");
textField.requestFocus();
}
numGuesses++;
String guessWord = (numGuesses == 1) ? " próba" : " prób";
label.setText(numGuesses + guessWord);
}
}
Jeśli liczba wpisana przez użytkownika nie jest taka sama jak tajna liczba,
to komputer pokaże liczbę dotychczasowych prób i użytkownik może zgadywać
dalej.
Jeśli liczba wpisana przez użytkownika jest taka sama jak tajna liczba,
to w polu tekstowym pojawi się słowo: Tak!. Tym samym gra się zakończy,
więc pole tekstowe i przycisk zostają wyłączone. Oba te elementy mają ten szary,
wyprany wygląd i żaden z nich nie będzie reagował na naciśnięcia klawiszy
lub kliknięcia myszą.
RYSUNEK 16.1.
Nieprawidłowa próba
RYSUNEK 16.2.
Prawidłowa próba
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Listing 16.1 używa fantazyjnego operatora do wybierania pojedynczej próby lub
liczby wielokrotnych prób. Jeśli nie znasz takiego użycia znaku zapytania i dwu-
kropka, zobacz rozdział 11.
Kod pokazany na listingu 16.1 został podzielony na trzy części, które dotyczą
obsługi zdarzenia kliknięcia przycisku:
Możesz dowiedzieć się wiele więcej o kodzie z listingu 16.1, usuwając niektóre
instrukcje i obserwując wyniki działania. Przy każdym sugerowanym usunięciu
sprawdź, czy IDE wyświetla komunikaty o błędach. Jeśli tego nie robi, to spróbuj
uruchomić program. Po zaobserwowaniu wyników działania programu umieść
element z powrotem i spróbuj następnego sugerowanego usunięcia:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Usuń wywołanie pack().
Wątki wykonania
Oto dobrze utrzymywany sekret: programy Javy są wielowątkowe (ang. multithre-
aded), co oznacza, że za każdym razem, gdy uruchamiasz program Javy, dzieje się
kilka rzeczy naraz. Jasne, że komputer wykonuje kod, który napisałeś, ale wykonuje
również inny kod (kod, którego nie napisałeś i którego nie widziałeś). Cały ten
kod jest wykonywany w tym samym czasie. Podczas gdy komputer wykonuje in-
strukcje z metody main jedna po drugiej, potrzebuje czasu, wymyka się na krótko
i wykonuje instrukcje z innych, niewidocznych metod. W przypadku większości
prostych programów Javy tymi innymi metodami są te, które są zdefiniowane
jako część wirtualnej maszyny Javy (JVM — Java Virtual Machine).
RYSUNEK 16.3.
Obsługa dwóch
wątków w Javie
if (buttonJustGotClicked()) {
object1.actionPerformed(infoAboutTheClick);
object2.actionPerformed(infoAboutTheClick);
object3.actionPerformed(infoAboutTheClick);
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Oczywiście za każdą odpowiedzią kryje się kolejne pytanie. W tej sytuacji na-
stępne pytanie brzmi: „Gdzie wątek obsługi zdarzenia znajduje wywołania me-
tod actionPerformed?”. I jest jeszcze jedno pytanie: „Co, jeśli nie chcemy, aby
wątek obsługi zdarzeń wywoływał niektóre metody actionPerformed czające się
w naszym kodzie?”.
button.addActionListener(this);
Fraza podaj swoje imię jest symbolem zastępczym. Jest to przestrzeń, w której
każda osoba umieszcza swoje imię:
Słowo kluczowe this działa w ten sam sposób. Znajduje się w kodzie definiują-
cym klasę GameFrame. Za każdym razem, gdy budowana jest instancja GameFrame,
wywołuje ona addActionListener(this). W tym wywołaniu słowo kluczowe this
oznacza tę samą instancję.
button.addActionListener(thisGameFrameInstance);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Wywołując button.addActionListener(this), instancja GameFrame mówi: „Dodaj
moją metodę _ actionPerformed do listy metod wywoływanych za każdym razem,
gdy przycisk zostanie kliknięty”. I rzeczywiście, instancja GameFrame ma metodę
actionPerformed, a musi ją mieć, ponieważ klasa GameFrame implementuje interfejs
ActionListener. Zabawne, jak to wszystko do siebie pasuje.
class IntegerHolder {
private int n;
IntegerHolder(int n) {
this.n = n;
}
void displayMyN() {
Displayer.display(this);
}
class Displayer {
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Każdy komponent w pakiecie javax.swing(JTextField, JButton lub cokolwiek
innego) ma metodę setEnabled. Kiedy wywołasz setEnabled(false), komponent
uzyskuje ten wiotki, szary, wyprany wygląd i nie może już otrzymywać kliknięć ani
naciśnięć klawiszy.
Możesz wykonać test, aby upewnić się, że obiekt, do którego odnosi się zmienna
przycisku, jest rzeczywiście tym, co zostało kliknięte. Po prostu napisz instrukcję
if (e.getSource() == button). Jeśli Twój kod ma dwa przyciski: button1 i button2,
a chcesz dowiedzieć się, który przycisk został właśnie kliknięty, możesz to prze-
testować, pisząc instrukcję if (e.getSource() == button1) and if (e.getSource()
== button2).
SerialVersionUID
Rozdział 9. wprowadził już adnotację SuppressWarnings, aby tym samym uniknąć
zajmowania się czymś zwanym serialVersionUID. SerialVersionUID to liczba,
która pomaga kompilatorowi Javy uniknąć konfliktów wersji podczas wysyłania
obiektu z jednego miejsca do drugiego. Na przykład możesz wysłać stan swojego
obiektu JFrame na ekran innego komputera. A następnie drugi komputer może
sprawdzić numer wersji ramki, aby upewnić się, że nie ma tam żadnej śmiesznej
rzeczy.
Kiedy więc w klasie serialVersionUID zawracać sobie będziesz głowę zmianą numeru
seryjnego? Jeśli wersja nr 1 jest już fajna, czy wersja nr 2 będzie jeszcze lepsza?
Odpowiedź jest skomplikowana, ale najważniejsze jest, aby nie zmieniać numeru
serialVersionUID, chyba że wprowadzisz zmiany w kodzie klasy, które będą ze
sobą niezgodne. Przez „niezgodne zmiany” rozumiem zmiany, które uniemożli-
wiają obsługę istniejącego kodu programu przez komputer odbierający w celu
obsługi nowo utworzonych obiektów.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Chcąc uzyskać więcej informacji na temat serialVersionUID i tego, co stanowi
niezgodną zmianę kodu, sprawdź, proszę, stronę:
http://docs.oracle.com/javase/8/docs/platform/serialization/spec/version.html
Każde główne środowisko Java IDE ma narzędzia wizualne, które pomagają za-
projektować interfejs GUI.
W tym rozdziale opisano funkcje środowiska Swing w Javie. Od roku 1998 Swing
jest podstawową platformą języka Java do tworzenia aplikacji GUI. Natomiast
pod koniec 2011 roku firma Oracle dodała do rdzenia Javy nowszą platformę —
JavaFX. JavaFX zapewnia bogatszy zestaw komponentów niż Swing, jednak w przy-
padku prostych aplikacji JavaFX jest trudniejsza w użyciu. Jeśli chcesz przeczytać
więcej o JavaFX, odwiedź stronę Oracle „Pierwsze kroki w JavaFX” dostępną pod
adresem http://docs.oracle.com/javafx/2/get_started/jfxpub-get_started.htm.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Reagowanie na rzeczy inne
niż kliknięcia przycisków
Teraz gdy już wiesz, jak zareagować na jeden rodzaj zdarzenia, reagowanie na
inne rodzaje zdarzeń będzie łatwiejsze. Za pomocą kodu znajdującego się na li-
stingach 16.3 i 16.4 wyświetlane jest okno, które umożliwia konwersję między
walutami amerykańską i polską. Kod w tym programie odpowiada na wiele rodza-
jów zdarzeń, a na rysunkach 16.4, 16.5 i 16.6 zostały pokazane niektóre z nich.
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
class MoneyFrame extends JFrame implements
KeyListener, ItemListener, MouseListener {
private static final long serialVersionUID = 1L;
public MoneyFrame() {
setLayout(new FlowLayout());
add(fromCurrencyLabel);
add(textField);
combo.addItem("UDS na PLN");
combo.addItem("PLN na USD");
add(label);
add(combo);
textField.addKeyListener(this);
combo.addItemListener(this);
label.addMouseListener(this);
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(300, 100);
setVisible(true);
}
void setTextOnLabel() {
String amountString = "";
String fromCurrency = "";
try {
double amount = Double.parseDouble(textField.getText());
if(combo.getSelectedItem().equals("USD na PLN")) {
amountString = " = " + currencyPL.format(amount * 3.86024);
fromCurrency = "$";
}
if(combo.getSelectedItem().equals("PLN na USD")) {
amountString = " = " + currencyUS.format(amount * 0.2591);
fromCurrency = "PLN";
}
} catch (NumberFormatException e) {
}
label.setText(amountString);
fromCurrencyLabel.setText(fromCurrency);
}
@Override
public void keyReleased(KeyEvent k) {
setTextOnLabel();
}
@Override
public void keyPressed(KeyEvent k) {
}
@Override
public void keyTyped(KeyEvent k) {
}
@Override
public void itemStateChanged(ItemEvent i) {
setTextOnLabel();
}
@Override
public void mouseEntered(MouseEvent m) {
label.setForeground(Color.red);
}
@Override
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public void mouseExited(MouseEvent m) {
label.setForeground(Color.black);
}
@Override
public void mouseClicked(MouseEvent m) {
}
@Override
public void mousePressed(MouseEvent m) {
}
@Override
public void mouseReleased(MouseEvent m) {
}
}
RYSUNEK 16.4.
Wymiana waluty
z amerykańskiej
na polską
RYSUNEK 16.5.
Użycie pola listy
rozwijanej
RYSUNEK 16.6.
Wymiana waluty
z polskiej
na amerykańską
W porządku, wprawdzie listing 16.3 jest może trochę przydługi, ale mimo to zarys
jego kodu nie jest taki zły. Oto jak to ogólnie wygląda:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Konstruktor z listingu 16.3 dodaje następujące cztery komponenty do nowego
okna MoneyFrame:
Pole listy rozwijanej. Na rysunku 16.4 w tym polu wyświetla się pole wyboru
USD na PLN. Na rysunku 16.5 użytkownik wybiera element z tego pola. Na rysunku
16.6 wybrana pozycja to PLN na USD.
W języku Java pole JComboBox (zwykle nazywane jest listą rozwijaną) może wy-
świetlać elementy dowolnego rodzaju. W kodzie na listingu 16.3 deklaracja:
konstruuje pole JComboBox, którego wpisy mają typ String. Wydaje się to być
rozsądne, ale jeśli Twoja aplikacja ma klasę Person, możesz zadeklarować JComboBox
<Person>peopleBox. W takiej sytuacji kompilator Javy musi wiedzieć, jak wyświetlić
każdy obiekt Person na liście rozwijanej. (To nie jest wielka sprawa. Kompilator
Javy dowiaduje się, jak wyświetlić daną osobę, szukając metody toString() w klasie
Person).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Na listingu 16.3, gdy użytkownik wybiera opcję USD na PLN lub opcję PLN na USD
na liście rozwijanej, wątek obsługi zdarzeń wywołuje metodę itemStateChanged.
Z kolei metoda itemStateChanged wywołuje setTextOnLabel i tak dalej.
Kod z listingu 16.3 zawiera kilka metod, które tak naprawdę nie są używane. Na
przykład, gdy implementujesz metodę MouseListener, Twój kod musi mieć własną
metodę mouseReleased. Potrzebujesz metody mouseReleased nie dlatego, że użyt-
kownik zrobi coś specjalnego (na przykład zwolni przycisk myszy), ale dlatego, że
złożyłeś obietnicę kompilatorowi Javy i musisz jej dotrzymać.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(textField);
add(button);
add(label);
button.addActionListener(new MyActionListener());
pack();
setVisible(true);
}
@Override
public void actionPerformed(ActionEvent e) {
String textFieldText = textField.getText();
if (Integer.parseInt(textFieldText) == randomNumber) {
button.setEnabled(false);
textField.setText(textField.getText() + " Tak!");
textField.setEnabled(false);
} else {
textField.setText("");
textField.requestFocus();
}
numGuesses++;
String guessWord = (numGuesses == 1) ? " próba" : " prób";
label.setText(numGuesses + guessWord);
}
}
}
Klasa MyActionListener z listingu 16.5 jest klasą wewnętrzną (ang. inner class). Klasa
wewnętrzna jest bardzo podobna do każdej innej klasy, z tą różnicą, że w kodzie
klasy wewnętrznej możesz odwoływać się do pól klasy otaczającej. Na przykład
kilka instrukcji w MyActionListener używa nazwy textField, a pole to jest zdefi-
niowane w otaczającej klasie GameFrame.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Zauważ także, że kod z listingu 16.5 używa tylko raz klasy MyActionListener.
(Jedynym zastosowaniem jest wywołanie button.addActionListener). Pytam więc,
czy naprawdę potrzebujesz nazwy czegoś, co jest używane tylko raz? Nie, Ty nie.
Możesz zastąpić całą definicję klasy wewnętrznej w wywołaniu metody list.add
ActionListener. Kiedy to zrobisz, otrzymasz anonimową klasę wewnętrzną (ang.
anonymous inner class). Listing 16.6 pokazuje, jak to wszystko działa.
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JTextField;
public GameFrame() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new FlowLayout());
add(textField);
add(button);
add(label);
button.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
String textFieldText = textField.getText();
if (Integer.parseInt(textFieldText) == randomNumber) {
button.setEnabled(false);
textField.setText(textField.getText() + " Tak!");
textField.setEnabled(false);
} else {
textField.setText("");
textField.requestFocus();
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
numGuesses++;
String guessWord = (numGuesses == 1) ? " próba" : " prób";
label.setText(numGuesses + guessWord);
}
});
pack();
setVisible(true);
}
}
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
444 CZĘŚĆ IV Sprytne techniki Javy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 17
Używanie
baz danych w Javie
G
dy uczę Javy profesjonalnych programistów, niemal zawsze słyszę to samo
stwierdzenie: „Nie potrzebujemy żadnych atrakcyjnych układów okienek.
Nie chcemy błyszczących interfejsów GUI. Potrzeba nam tylko dostępu do
bazy danych. Dlatego [zamknij się i] pokaż nam, jak pisać w Javie programy uży-
wające baz danych”.
1
Okazuje się, że w literaturze firmy Oracle nie ma żadnej wzmianki o tym, że skrót JDBC oznacza
Java Database Connectivity. Ale nie ma się czym przejmować. Nawet jeżeli nie jest to prawidłowe
rozwinięcie skrótu, to właściwie oddaje jego ideę. W świecie Javy JDBC z całą pewnością nie znaczy
Jasio Daje Babki Celinie.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Tworzenie baz danych i tabel
Istota JDBC zawarta jest w dwóch pakietach: java.sql i javax.sql, które znajdują się
w API języka Java. Przykłady w tym rozdziale będą korzystały z klas zawartych
w pakiecie java.sql. Pierwszy znajdziesz już na listingu 17.1.
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Nawet po pobraniu kopii pliku derby.jar Twoje IDE może nie wiedzieć, gdzie
umieściłeś ten plik na dysku twardym komputera. Zazwyczaj nie wystarczy
umieścić go w dobrze znanym katalogu. Zamiast tego musimy dokładnie po-
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
wiedzieć kompilatorowi Eclipse, IntelliJ IDEA lub NetBeans, gdzie może znaleźć
plik derby.jar. A robi się to tak:
Jeśli się trochę rozejrzysz, możesz znaleźć bezpośredni dowód na istnienie nowej
bazy danych. Za pomocą Eksploratora plików lub Findera możesz przejść na kom-
puterze do folderu projektu zawierającego kod z listingu 17.1. (Jeśli kod został
pobrany ze strony tej książki, zajrzyj do folderu 17-01). W tym folderze zoba-
czysz nowy podfolder o nazwie AccountDatabase. Tam właśnie znajduje się nowo
utworzona baza danych.
Niestety nie możesz zobaczyć, co jest w tej bazie danych, dopóki nie uruchomisz
jeszcze kilku innych programów. Tak więc czytaj dalej!
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
TAK WŁAŚCIWIE TO CZYJA TO BAZA DANYCH?
Bazy danych są dostępne w wielu kształtach i rozmiarach i pochodzą od wielu róż-
nych dostawców. W 2017 roku najlepszymi dostawcami baz danych były firmy
Oracle, Microsoft, IBM i SAP. Do popularnych baz danych o otwartych źródłach na-
leżą PostgreSQL i Oracle MySQL. Kod z listingu 17.1 (i z innych listingów tego roz-
działu) wykorzystuje otwartą źródłową bazę danych od fundacji Apache Software
Foundation, znaną jako Apache Derby.
Jeśli nie chcesz korzystać z bazy Apache Derby, musisz zastąpić ciąg znaków
CONNECTION w przykładach z tego rozdziału. To, jakiego innego ciągu użyjesz, zależy
od rodzaju używanego przez Ciebie systemu bazy danych i jeszcze wielu innych
czynników. Sprawdź, proszę, dokumentację swojego dostawcy bazy danych.
Nawiasem mówiąc, sterowniki baz danych są jak ludzie: niektóre są dość stare, a inne
są troszkę młodsze. Od stycznia 2017 roku najnowszą wersją JDBC jest wersja 4.2.
„Dość stary” sterownik bazy danych JDBC to taki, który został stworzony dla wersji
JDBC jeszcze przed wersją 4.0 (czyli około grudnia 2006 roku). Jeśli sterownik bazy
danych nie spełnia standardów JDBC 4.0, będziesz musiał dodać kilka dodatkowych
instrukcji do każdego z przykładów tego rozdziału, a możesz to zrobić w następujący
sposób:
final public String DRIVER =
"com.nazwaproducentabazydanych.nazwasystemubazydanych.byćmożecośjeszcze";
try {
Class.forName(DRIVER).newInstance();
} catch (InstantiationException |
IllegalAccessException |
ClassNotFoundException e) {
e.printStackTrace();
}
Jeśli znasz język SQL (Structured Query Language), to ciągi poleceń w wywołaniach
metody executeUpdate będą dla Ciebie zrozumiałe. Jeśli zaś nie znasz tego języka,
to wybierz kopię Pierwsze kroki z SQL autorstwa Thomasa Nielda (Helion). Tak czy
inaczej nie szukaj w tym rozdziale wyjaśnienia polecenia create table. Nie znaj-
dziesz go tutaj, ponieważ ten wielki ciąg znaków create table z listingu 17.1 nie
jest częścią języka Java. To polecenie jest tylko ciągiem znaków, które podajesz
do metody executeUpdate w kodzie Javy. Ten napisany w języku SQL ciąg znaków
zajmuje się tworzeniem w bazie danych nowej tabeli z trzema kolumnami (kolum-
ny dla nazwiska klienta NAME, dla jego adresu ADDRESS i salda jego konta BALANCE).
Tym właśnie zajmujesz się, pisząc programy używające bazy danych — piszesz
zwykłe polecenia SQL i otaczasz je instrukcjami języka Java.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Podłączanie i rozłączanie
Nie licząc wywołania metody executeUpdate, kod z listingu 17.1 powstał w zasadzie
metodą kopiuj i wklej. Oto podsumowanie tego, co oznacza każda z części kodu:
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Umieszczanie danych w tabeli
Jak każda inna konfiguracja tabelaryczna, tabela bazy danych ma kolumny i wier-
sze. Uruchamiając kod z listingu 17.1, otrzymasz pustą tabelę. Tabela ta ma trzy
kolumny (NAME, ADDRESS, BALANCE), ale nie ma wierszy. Aby dodać wiersze do ta-
beli, uruchom kod z listingu 17.2.
System.out.println("Dodano wiersze.");
} catch (SQLException e) {
e.printStackTrace();
}
}
}
Aby uzyskać najlepsze wyniki, umieść wszystkie listingi z tego rozdziału w tym
samym projekcie. W ten sposób nie musisz dodawać pliku derby.jar do kilku pro-
jektów. Możesz także liczyć na to, że folder AccountDatabase będzie łatwo dostęp-
ny dla programów z wszystkich czterech listingów tego rozdziału. Natomiast jeśli
pobierzesz przykłady tej książki przygotowane dla środowisk Eclipse, IntelliJ IDEA
lub NetBeans, cały kod z tego rozdziału znajdziesz w projekcie o nazwie 17-01.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Pobieranie danych
Po co Ci baza danych, jeśli nie można odczytać z niej informacji? W tym pod-
rozdziale zajmujemy się odczytywaniem bazy danych utworzonej już we wcze-
śniejszych podrozdziałach. Kod wysyłający zapytania przedstawiam na listingu 17.3.
W celu użycia bazy danych innej niż Apache Derby w każdym z przykładów tego
rozdziału zmień wartość zmiennej CONNECTION.
Wynik działania kodu z listingu 17.3 przedstawiam na rysunku 17.1. Kod wysyła
zapytanie do bazy danych, a następnie przechodzi przez poszczególne wiersze
tej bazy, wypisując dane każdego z nich.
RYSUNEK 17.1.
Pobieranie
danych z bazy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Kod z listingu 17.3 wywołuje metodę executeQuery i przekazuje jej treść polecenia
SQL. Osoby, które znają polecenia SQL, wiedzą, że to konkretne polecenie pobiera
wszystkie dane z tabeli ACCOUNTS (tabeli utworzonej już wcześniej na listingu 17.1).
Informuje, czy istnieje inny wiersz, zwracając wartość boolean — true lub false.
Niszczenie danych
To prawda. Wszystkie dobre rzeczy muszą się kiedyś skończyć. Pisząc to, odnoszę
się zarówno do treści tej książki, jak i do informacji zawartych w bazie danych
AccountDatabase z tego rozdziału.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Chcąc pozbyć się tabeli bazy danych utworzonej na listingu 17.1, uruchom kod
z listingu 17.4.
Uruchom program AddData (z listingu 17.2) dwa razy z rzędu bez modyfikowania
kodu programu. Jakie komunikaty o błędach zobaczysz? I dlaczego?
Utwórz tabelę zawierającą trzy kolumny: nazwę towaru, cenę i stawkę podatku.
Zapisz dane do kilku wierszy tabeli.
Pobierz dane z tabeli i wyświetl informacje z każdego wiersza w tej tabeli. Każdy
wypisany wiersz powinien zawierać nazwę towaru, a następnie cenę z dodanym
podatkiem. Na przykład, jeśli cena przedmiotu wynosi 10 zł, a stawka podatku
wynosi 0,05 (co oznacza 5%), wiersz wyniku powinien zawierać kwotę 10,50 zł.
Niech program w ostatnim wierszu wyświetla sumę opodatkowanych cen
wszystkich pozycji.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
454 CZĘŚĆ IV Sprytne techniki Javy
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Dekalogi
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TEJ CZĘŚCI
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 18
10 sposobów
unikania błędów
B
łędów nie popełniają tylko ci ludzie, którzy nic nie robią”. Tak powtarzał
jeden z moich profesorów na studiach. Nie pamiętam już jego nazwiska,
dlatego nie mogę mu odpowiednio podziękować. To chyba można nazwać
błędem.
Gdy używasz nazw z API języka Java, to muszą być one zapisywane dokładnie tak
samo, jak podaje to dokumentacja API.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Musisz też mieć pewność, że nazwy wymyślane przez Ciebie w całym programie
są zapisywane w dokładnie ten sam sposób. Jeżeli deklarujesz zmienną myAccount,
to nie możesz później odwoływać się do niej przy użyciu takich nazw jak MyAccount,
myaccount albo Myaccount.
switch (verse) {
case 3:
out.print("Last refain, ");
out.println("last refain,");
case 2:
out.print("He's a pain, ");
out.println("he's a pain,");
case 1:
out.print("Has no brain, ");
out.println("has no brain,");
}
if (inputNumber == randomNumber)
if (inputNumber = randomNumber)
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Dodawanie komponentów do GUI
Oto konstruktor ramki w programie Javy:
public SimpleFrame() {
JButton button = new JButton("Dziękuję wam...");
setTitle("...Katie Mohr i Paul Levesque");
setLayout(new FlowLayout());
add(button);
button.addActionListener(this);
setSize(300, 100);
setVisible(true);
}
Nie możesz nigdy zapomnieć o wywołaniu metody add. Bez tego wywołania cała
praca związana z tworzeniem przycisku pójdzie na marne, bo przycisk w ogóle
nie pojawi się na ekranie. Więcej informacji na ten temat znajdziesz w rozdziale 9.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Naprawianie odwołań
do niestatycznych elementów
Jeżeli spróbujesz skompilować ten kod, to otrzymasz komunikat o błędzie:
class WillNotWork {
String greeting = "Witaj";
Ten komunikat pojawia się dlatego, że metoda main jest statyczna, ale pole
greetings takie nie jest. Pełny opis sposobów wyszukiwania i naprawiania takich
problemów znajduje się w rozdziale 10.
class ListMyFiles {
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
public static void main(String args[]) {
File myFile = new File("/Users");
String dir[] = myFile.list();
Co się jednak stanie, jeżeli zmienimy ciąg znaków /Users na coś innego? Coś, co
na pewno nie będzie nazwą żadnego katalogu?
W tej sytuacji wywołanie new File zwraca jedynie null (specjalne słowo, które
w Javie oznacza nic), przez co zmienna myFile nie będzie miała żadnej wartości.
W dalszej części kodu zmienna dir również nie będzie miała żadnej wartości, dla-
tego próba iterowania w pętli po wartościach ze zmiennej dir zakończy się małą
katastrofą. Zostanie rzucony wyjątek NullPointerException, a cały program się po
prostu rozpadnie.
Chcąc uniknąć tego rodzaju katastrof, zawsze przeglądaj dokumentację API języka
Java. Jeżeli wywołujesz metodę, która może zwrócić null, zastosuj w programie
metody obsługi wyjątków.
Cały opis sposobów radzenia sobie z wyjątkami znajdziesz w rozdziale 13. Porady
dotyczące czytania dokumentacji API języka Java podaję w rozdziale 3. oraz na
stronie WWW związanej z tą książką (https://users.drew.edu/bburd/JavaForDummies/).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Więcej informacji na ten temat znajdziesz w rozdziale 14. oraz na stronie WWW
związanej z tą książką (https://users.drew.edu/bburd/JavaForDummies/).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
W TYM ROZDZIALE:
Rozdział 19
Dziesięć stron o Javie
N
ie ma co się dziwić popularności sieci WWW. Jest przecież zarówno przy-
datna, jak i dostarcza nam wiele zabawy. W tym rozdziale postaram się
tego dowieść, podając przykłady przydatnych i zabawnych stron WWW.
Na każdej z tych stron znajdują się informacje pozwalające na efektywniejsze
wykorzystanie języka Java. Dodatkowo, o ile mi wiadomo, żadna z tych stron nie
serwuje reklam, wyskakujących okienek i innych nieprzyjemnych rzeczy.
1
Strona w języku angielskim — przyp. red.
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Najważniejsze strony
Oficjalna strona firmy Oracle dotycząca języka Java to www.oracle.com/technetwork/
java.
Wyszukiwanie wiadomości,
recenzji i przykładowego kodu
Artykuły pisane przez ekspertów znajdziesz na stronach InfoQ (www.infoq.com)
oraz TheServerSide (www.theserverside.com). Na obu stronach zawsze znajdziesz
wiele interesujących materiałów.
Możesz też zadawać pytania na stronie Beginning Java Forum, której motto
brzmi: „żadne pytanie nie jest zbyt głupie” (coderanch.com/f/33/java).
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Skorowidz
EOFException, 371
A exception java.io.IOException is never
adnotacja thrown, 370
@Deprecated, 235 FileNotFoundException, 220, 377
@Override, 235 IndexOutOfBoundsException, 376
@SuppressWarnings, 235, 362, 434 InputMismatchException, 123, 211, 269
SuppressWarnings, 261 InterruptedException, 373, 377
anonymous inner class, Patrz: klasa IOException, 272, 377
wewnętrzna anonimowa komunikat, 357
API, 60, 61, 71 name has private access, 197
argument wiersza poleceń, 320, 322, 323 NoClassDefFoundError, 393, 395, 461
autoboksing, 333 non-static variable or method cannot be
referenced from a static context, 280
NoSuchElementException: No line found,
B 222
Backus John, 337 NullPointerException, 376, 382, 460
baza danych, 301, 445 NumberFormatException, 322, 357, 358,
sterownik, 446 360, 376
tabela SQLException, 377
pobieranie danych, 451 Unhandled exception type
tworzenie, 448 InterruptedException, 373
umieszczanie danych, 450 Variable 'whatsLeft' is already defined,
usuwanie, 452 111
tworzenie, 446 Board College, 31
źródło, 448 Bright Herbert, 355, 356
bit, 87
blok, 126, 127, 167
C
błąd, 273, 356
ArithmeticException, 376 Church Alonzo, 342
ArrayIndexOutOfBoundsException, 322,
326
cannot assign a value to final variable,
D
249 dane
cannot find symbol message, 395 baza, Patrz: baza danych
cannot resolve symbol, 287 opis, 33
Duplicate variable whatsLeft, 111
Skorowidz 465
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
dane
pobieranie z pliku, 214, 215, 216, 217, 223,
I
272, 309 IDE, 42, 43, 45, 56, 57
podział wiersza, 221, 222 identyfikator, 62, 63, 66
współdzielone, 352 inicjalizator, 277
zapisywanie do pliku, 308 inner class, Patrz: klasa wewnętrzna
deklaracja import, 110, 258 instrukcja, 69
Diaconis Persi, 124 break, 146, 161, 458
dostęp, 399, 403, Patrz też: poziom dostępu continue, 161
chroniony, 401, 404 do…while, 163
domyślny, 387, 399, 401 throws IOException, 216
modyfikator, Patrz: modyfikator dostępu for, 155, 156, 157, 160, 301
dysk twardy, 164 aktualizacja, 157, 158
dziecko, 226 inicjalizacja, 157, 158
dziedziczenie, 36, 226, 228, 252, 410 rozszerzona, 168, 304, 305, 329
if, 120, 125, 128, 139
else, 126, 127
E zagnieżdżanie, 141, 142
Eclipse, 42, 43, 45, 56, 447 przypisania, 86, 111
efekt uboczny, 345 Random, 124
enhanced for, Patrz: instrukcja for return, 188
rozszerzona switch, 161, 245, 458
etykieta, 439, 440 throws IOException, 218, 219, 272
event, Patrz: zdarzenie try, 360, 363, 375
event-handling code, Patrz: kod obsługi try-catch-finally, 380
zdarzeń try-z-zasobami, 382, 449
wcięcie, 127
while, 153, 154, 162
F IntelliJ IDEA, 43, 45, 56, 447
funkcja, 345 interfejs, 410, 423, 424, 425
ActionListener, 430
implementacja, 411, 412
G ItemListener, 439
Gosling James, 33, 330 KeyListener, 439
GUI, 97, 430, 435 MouseListener, 439, 440
tworzenie, 102 programowania aplikacji, Patrz: API
GUI Builder, 435 użytkownika graficzny, Patrz: GUI
GUI Designer, 435 interpreter, 51
iterator, 334, 342
H
J
hasło, 134, 135, 137, 142
Hopper Grace, 30 Java, 31, 33
specyfikacja, Patrz: specyfikacja
wersja, 52
wielkość liter, 66, 457
Java Community Proces, Patrz: JCP
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
Java Development Kit, Patrz: JDK grupowanie, 258
Java Runtime Environment, Patrz: JRE HashMap, 336
JavaFX, 435 HashSet, 336
JCP, 61, 62 instancja, 38, 104, 175
JDK, 42, 52 zmienna, 284, Patrz też: pole
język Integer, 322, 332, 333
naturalny, 60 InterruptedException, 373
obiektowy, 65 java.awt.Graphics, 393
programowania, 29 java.io.File, 166, 217, 218
C, 30, 33 java.io.IOException, 216
C#, 31 javax.swing.JFrame, 396
C++, 31, 33 JButton, 260, 434, 435
COBOL, 30 JDBC, 445, 448
FORTRAN, 30, 33, 337 JFrame, 61, 103, 259
Java, Patrz: Java JLabel, 270, 271, 433, 435
Lisp, 337 JTextField, 429, 433, 434, 435
obiektowego, 33 kolekcji, 327, 329, 335
SIMULA, 33 LinkedList, 336
Smalltalk, 33 nadrzędna, 36, 411
Visual Basic, 31 niepubliczna, 406, 407
SQL, 448 NumberFormat, 316
zorientowany obiektowo, 33, 34, 35, 36, NumberFormatException, 360
65, 209 opakowująca, 332
JRE, 52 potomna, 228
JShell, 94, 95, 96, 130 potomna, 37
JVM, 45, 48, 50, 51, 273, 431 PrintStream, 308
PriorityQueue, 336
publiczna, 178, 392, 406
K Queue, 336
klasa, 34, 35, 37, 65, 103, 171 rozszerzenie, 36
AbstractCollection, 336 RuntimeException, 376
abstrakcyjna, 418, 420, 422, 423, 424, 425 Scanner, 122, 334
instancja, 419 serialVersionUID, 434
rozszerzanie, 419, 420 SimpleFrame, 459
ArrayList, 327, 329, 330, 335 Stack, 336
bazowa, 37 Stream, 350
BigDecimal, 191 String, 102, 103
DecimalFormat, 267, 316 Thread, 373
Drawing, 391, 398, 400 Time, 105
DrawingWideBB, 400 wewnętrzna, 408, 440, 441
DriverManager, 449 anonimowa, 442
DummiesFrame, 97, 200, 201, 202, 203 Formatter, 211
element, 399 java.io.File, 216
EmbeddedDriver, 446 BufferedReader, 217
Exception, 358 Scanner, 217
FlowLayout, 260 klauzula, Patrz: słowo kluczowe
GridLayout, 271 catch pasująca, 365
Skorowidz 467
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
kod, 29 delete, 166
bajtowy, 45, 46, 47, 50 display, 182
obsługi zdarzeń, 430 domyślna, 417
tworzenie, 29 dostępowa, 210, 228
źródłowy, 46, 47 dostępu, 196, 198, 243, Patrz też: poziom
kolekcja, 329, 341, Patrz też: klasa kolekcji dostępu
komentarz, 75, 76 Double.parseDouble, 137
javadoc, 76, 78 equals, 132, 134
na końcu wiersza, 76 executeQuery, 452
tradycyjny, 76 executeUpdate, 447
kompilator, 45, 46, 48, 395 filter, 347, 350
komunikat, Patrz też: błąd format, 316
konspekt, 71 getConnection, 449
konstruktor, 240, 243, 276 getCurrencyInstance, 316
bezparametrowy, 459 getText, 433
deklarowanie, 252, 459 hasNext, 334
domyślny, 252, 254 hasNextDouble, 334
dziedziczenie, 252, 254 hasNextInt, 334
parametr, 244 Integer.parseInt, 137
ramki, 459 JOptionPane.showInputDialog, 136, 137
konsument, 345 main, 68, 70, 178, 210, 460
map, 347
mouseReleased, 440
L nagłówek, 71
liczba, 85 nazwa, 67
całkowita, 91, 92 next, 133
dokładność, 88 nextBoolean, 123
wyświetlanie, 190, 191, 193 nextDouble, 123, 217, 222, 223
lista rozwijana, 439 nextInt, 123
literał, 85, 134 nextLine, 221, 222, 223
wartość, 85 niestatyczna, 280
obiekt zwracany, 294
pack, 271, 272
M parametr, 244
maszyna wirtualna Java, Patrz: JVM przekazywanie przez referencję, 292,
McCarthy John, 337 293
metoda, 66, 67 przekazywanie przez wartość, 290,
abstrakcyjna, 417, 423 291
deklarowanie, 423 parseInt, 322, 357, 358, 359
actionPerformed, 430, 431, 433, 435 printf, 191, 193, 248
add, 259, 328, 329, 459 prywatna, 388
addActionListener, 432, 459 publiczna, 389, 390
bez ciała, 417 reduce, 347, 348, 351, 352
Character.toUpperCase, 99, 100 referencja, Patrz: referencja metody
close, 123, 216, 223, 309, 310, 381 remove, 328
definiowanie w ramach klasy, 179 requestFocus, 434
deklaracja, 67, 68 setDefaultCloseOperation, 259, 260
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
setEnabled, 434 logiczny, 139
setLayout, 259, 260 inkrementacji, 111
setSize, 259 logiczny, 135, 138
setText, 433 porównania, 131, 132, 458
setTitle, 259, 260 postdekrementacji, 113
setVisible, 259, 272 postinkrementacji, 112, 113
sleep, 373 predekrementacji, 113
statyczna, 274, 276, 280, 460 preinkrementacji, 112, 113
wywołanie, 279 przypisania, 115, 116
stream, 347, 350 warunkowy, 316
sum, 353 włączający, 139
System.out.print, 90 oprogramowanie, 29, Patrz też: kod
System.out.println, 69, 70, 90, 100, 110,
280
deklaracja, 70
P
wartość zwracana, 183, 186, 188, 189, pakiet, 258
292, 294 java.nio, 217
wywołanie, 67, 68 com.burdbrain.drawings, 391, 393, 397,
modyfikator dostępu, 387, 391, 406 398
dla elementu klasy, 388 com.burdbrain.frames, 392, 397
java.awt, 259
N java.sql, 446
javax.sql, 446
NetBeans, 43, 45, 56, 447 javax.swing, 259, 260, 429, 434
null, 138, 461 JRE, Patrz: JRE
nazwa, 392
org.apache.derby.jdbc, 446
O pętla
obiekt, 34, 35, 37, 65, 104, 171, 172 do…while, Patrz: instrukcja do…while
importowanie leniwe, 134 for, Patrz: instrukcja for
porównywanie, 132 REPL, 94
System.in, 134 while, Patrz: instrukcja while
System.out, 134, 308, 309 plik, 166
tworzenie, 240 danych, 302
obsługa derby.jar, 446, 447
wyjątków, 357, 358 JAR, 203
zdarzeń, 430, 431 lokalizacja, 447, 461
okno ścieżka, 321
dialogowe, 136, 137, 138 usuwanie, 162, 163, 164, 166, 310
wiersza poleceń, 319 wykrycie końca, 371
z przyciskiem, 428 zamykanie, 309
OOP, Patrz: programowanie obiektowe podklasa, 36, 226, 228, 229, 233, 236, 250,
OpenJDK, 61 251, 410
operator pole, 173
arytmetyczny, 107, 108 JComboBox, 439
binarny, 345 niestatyczne, 280, 425, 460
dekrementacji, 111 prywatne, 197, 199, 386, 388, 389
Skorowidz 469
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
pole default, 417
publiczne, 386, 389 double, 88, 186
serialVersionUID, 261, 434 extends, 226
statyczne, 274, 275, 276, 277, 280, 460 final, 249
tekstowe, 435, 439, 440 finally, 379, 380, 381, 382
powłoka, 319 implements, 413
poziom dostępu, 196, 403, Patrz też: dostęp, lista, 63
modyfikator dostępu private, 174, 197, 387, 399
chroniony, 174, 387, 401, 404 protected, 174, 387, 399, 401
domyślny, 174, 401 public, 65, 66, 174, 178, 179, 182, 243,
prywatny, 174, 197 387, 388, 392, 399
publiczny, 174 static, 110, 122, 274, 275, 277, 280
predykat, 345 this, 247, 248, 252, 285, 432
procesor, 48, 50 throw, 358
wielordzeniowy, 338, 351 throws, 358
program, 29, 65 try, 358, 359, 360, 363, 379
elementy, 65 void, 182
GUI, 97 Software Development Kit, Patrz: SDK
javadoc, 77 specyfikacja, 60, 61
tekstowy, 96 SQL polecenie, 452
programista, 30, 44 Stroustrup Bjarne, 31, 33
programowanie strumień, 342
funkcyjne, 337, 339, 345 pobieranie obiektu, 343, 345, 346
Google MapReduce, 349 równoległy, 351
imperatywne, 337, 339, 352 szeregowy, 351
obiektowe, 208 superklasa, 226
obiektowe, 31, 32, 180, 386 Swing, 435
przenośność, 50 instrukcja, 143, 144, 145, 146, 148
przetwarzanie równoległe, 352 break, 146
przycisk, 430, 435, 459 symbol zastępczy %s, 214
kliknięcie, 132
Ś
R
środowisko programistyczne zintegrowane,
ramka, 103, 105 Patrz: IDE
pakowanie, 271
referencja metody, 353
Ritchie Dennis, 30 T
rodzic, 226 tablica, 298
element, 298, 299, 301
S indeks, 298
inicjalizator, 303, 304, 315
SDK, 52 nazwa, 300
separator grupujący, 211, 215 obiektów, 313
słowo kluczowe, 62, 457 ograniczenia, 326
abstract, 418 tworzenie, 299, 300, 460
catch, 358, 359, 360, 363, 379 wielkość, 460
class, 65, 71
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
tekstu formatowanie, 85, 90 wątek obsługi zdarzeń, 431
terminal, 319 wielowątkowość, 431
testowanie, 421 wiersz poleceń, 319
typ, 85, 87 argument, Patrz: argument wiersza
boolean, 97, 98, 100 poleceń
byte, 93, 98 WindowBuilder, 435
char, 97, 98, 99, 165 wyjątek, Patrz też: błąd
Char, 98 niesprawdzony, 373, 376
dopasowywanie, 231 obsługa, Patrz: obsługa wyjątków
double, 88, 90, 97, 98 sprawdzony, 373, 376
dzielenie, 108 tworzenie, 361, 362
enum, 241, 245, 408 wyrażenie, 101, 126, 157
deklaracja, 242 lambda, 342, 343, 344, 345, 346, 353
float, 88, 90, 98, 132
generyczny, 329, 330
int, 91, 92, 93, 97, 98
Z
dzielenie, 92, 108 zdarzenie, 430, 436
wprowadzanie, 137 obsługa, Patrz: obsługa zdarzeń
Integer, 332, 333 zmienna, 84, 173
JAR, 203 deklaracja, 88, 92, 93, 111, 167
java.sql.ResultSet, 452 inicjowanie, 93, 167, 176, 177
JFrame, 102, 103 instancji, 284, Patrz też: pole
logiczny, 98 lokalna, 285, 287, 399
long, 93, 98 nazwa, 85
pierwotny, 97, 332, 409 środowiskowa
prosty, 97 CLASSPATH, 395
referencyjny, 101, 102, 103, 292, 332, 410, tablicowa, 313
423 typ, Patrz: typ
inicjowanie, 177 wartość, 85
short, 93, 98 początkowa, 93
String, 99, 102, 103, 134, 165, 439 znak
konkatenacja, 107 \', 303
porównywanie, 132 !, 135
Time, 105 !=, 131, 132, 154
znakowy, 98 ", 102, 220, 303
%, 108
U %s, 214
&&, 135, 139
ustawienia regionalne, 211, 215, 269 *, 76, 134
użytkownik */, 76
nazwa, 137, 142 *=, 116
uwierzytelnianie, 142 /, 221
/*, 76
/**, 76
W //, 76
wartość null, Patrz: null \\, 303
warunek, 125, 126, 138, 139 ||, 135, 139
Skorowidz 471
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
znak cudzysłów, Patrz: znak "
++, 111 \f, 303
+=, 116 gwiazdka, Patrz: znak *
<, 131 modyfikacji, 303
<=, 131 \n, 303
-=, 117 nawias klamrowy, 71
==, 126, 131, 132, 458 \r, 303
>, 131 średnik, 71
>=, 131 \t, 303
apostrof, 99 Unicode, 99
ASCII, 99 wieloznaczny, Patrz: znak *
\b, 303 znak ", 221
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
acc7ba75380d5ea8e325402fae0d0e83
a