Podstawy programowania w języku Java

Transkrypt

Podstawy programowania w języku Java
Podstawy programowania w języku Java
mgr inż. Piotr Kowalski
18 stycznia 2013
Spis treści
1 Podstawy architektury komputera
1.1 Części składowe komputera . . . . .
1.1.1 Rzecz o rejestrach . . . . . .
1.1.2 Rzecz o pamięci . . . . . . .
1.1.3 Rzecz o programowaniu . . .
1.2 Języki programowania . . . . . . . .
1.2.1 Od kodu do programu . . . .
1.3 Funkcje systemu operacyjnego . . . .
1.3.1 Jądro systemu operacyjnego .
1.3.2 BIOS i uruchomienie stacji .
1.3.3 Systemy plików . . . . . . . .
1.3.4 Funkcja hashująca w systemie
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
. . . .
plików
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
5
5
6
6
7
8
9
9
9
10
10
11
2 Algorytmy i struktury danych
2.1 Struktury statyczne . . . . .
2.1.1 Tablice . . . . . . . .
2.1.2 Rekordy . . . . . . . .
2.1.3 Obiekty . . . . . . . .
2.2 Struktury dynamiczne . . . .
2.2.1 Stos . . . . . . . . . .
2.2.2 Kolejka . . . . . . . .
2.2.3 Lista . . . . . . . . . .
2.2.4 Drzewo binarne . . . .
2.2.5 Graf . . . . . . . . . .
2.3 Porównanie typów struktur .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
12
12
12
12
13
13
13
13
13
13
14
14
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
3 Wstęp - instalacja zintegrowanego
nego dla Javy
3.1 Czym jest wirtualna maszyna Javy
technologii . . . . . . . . . . . . . .
3.2 Czym jest IDE . . . . . . . . . . .
4 Podstawy języka Java
4.1 Zmienne w Javie. Pierwsze starcie
4.1.1 Typy prymitywne . . . . .
4.1.2 Typy wyliczeniowe . . . . .
4.1.3 Typy tablicowe . . . . . . .
4.1.4 Klasy . . . . . . . . . . . .
1
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
środowiska programistycz15
oraz najważniejsze składowe
. . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . .
15
16
.
.
.
.
.
18
18
19
19
20
20
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
SPIS TREŚCI
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
21
21
22
22
22
23
23
24
25
26
28
29
29
30
32
32
5 Tworzenie modeli w Javie
5.1 Tworzenie nowej klasy . . . . . . . . . . . . . . . . . .
5.2 Dziedziczenie i polimorfizm . . . . . . . . . . . . . . .
5.2.1 Interfejsy . . . . . . . . . . . . . . . . . . . . .
5.2.2 Modyfikator ’abstract’ i adnotacja ’@Override’
5.3 Modyfikatory dostępu . . . . . . . . . . . . . . . . . .
5.3.1 Modyfikator ’final’ . . . . . . . . . . . . . . . .
5.4 Słowo kluczowe ’this’ oraz o konstruktorach . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
33
33
34
35
36
37
38
39
6 Korzystanie z pakietów podstawowych języka Java
6.1 Kontenery . . . . . . . . . . . . . . . . . . . . . . . .
6.1.1 Kolekcje . . . . . . . . . . . . . . . . . . . . .
6.1.2 Iteratory . . . . . . . . . . . . . . . . . . . .
6.1.3 Tablice asocjacyjne . . . . . . . . . . . . . . .
6.1.4 Przeszukiwanie i sortowanie kontenerów . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
41
41
41
42
42
42
7 Tworzenie aplikacji z interfejsem graficznym Swing
7.1 Elementy pakietu Swing . . . . . . . . . . . . . . . . .
7.1.1 Uruchamianie okna głównego aplikacji . . . . .
7.1.2 Swing - menu . . . . . . . . . . . . . . . . . . .
7.1.3 Swing - kontenery . . . . . . . . . . . . . . . .
7.1.4 Swing - kontrolki . . . . . . . . . . . . . . . . .
7.2 Okienka dialogowe . . . . . . . . . . . . . . . . . . . .
7.2.1 Przykładowe użycia okienek dialogowych . . .
7.3 Wydarzenia . . . . . . . . . . . . . . . . . . . . . . . .
7.3.1 Akcja wykonana . . . . . . . . . . . . . . . . .
7.4 Właściwości okna . . . . . . . . . . . . . . . . . . . . .
7.5 Nasłuchiwacze . . . . . . . . . . . . . . . . . . . . . .
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
43
43
44
45
45
46
46
46
46
46
46
46
4.2
4.3
4.4
4.5
4.1.5 I jeszcze o zmiennych . . . .
4.1.6 Słowa zarezerwowane . . . . .
4.1.7 Znaki niepisane . . . . . . . .
4.1.8 Rzutowanie . . . . . . . . . .
4.1.9 Typ specjalny void . . . . . .
Podstawowe operacje w języku Java
4.2.1 Instrukcje warunkowe . . . .
4.2.2 Instrukcje pętli . . . . . . . .
4.2.3 Funkcje . . . . . . . . . . . .
Operatory . . . . . . . . . . . . . . .
4.3.1 Literały . . . . . . . . . . . .
Obsługa błędów w języku Java . . .
4.4.1 Drzewo hierarchii błędów . .
4.4.2 Komendy obsługi błędów . .
Inne reguły w języka Java . . . . . .
4.5.1 Nazewnictwo w Javie . . . . .
2
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
A Zadania laboratoryjne
47
B Zadania projektowe
50
SPIS TREŚCI
3
C Zadania do samodzielnego rozwiązania
52
C.1 Zadanie z prostych algorytmów . . . . . . . . . . . . . . . . . . . 52
C.2 Zadania z modelowania w języku Java . . . . . . . . . . . . . . . 52
C.3 Zadania z prostych aplikacji graficznych . . . . . . . . . . . . . . 54
SPIS TREŚCI
4
Wersje
numer
0.1
0.2
data
10.10.2012
13.11.2012
autor
Piotr Kowalski
Piotr Kowalski
0.3
27.11.2012
Piotr Kowalski
0.4
18.01.2012
Piotr Kowalski
opis
Prace nad pierwszym rozdziałem
Dołączone dziedziczenie, modyfikatory
dostępu oraz obsługa błędów
Dołączone konstruktory oraz liczne zadanka i projekty laboratoryjne.
Dołączone zaczątki kolejnych rozdziałów z: Kontenerów oraz Swing.
Rozdział 1
Podstawy architektury
komputera
1.1
Części składowe komputera
Do głównych elementów komputera należą
ˆ Płyta główna
ˆ Pamięć RAM
ˆ Dysk twardy
ˆ Karta graficzna
ˆ Inne elementy
Szczególną uwagę przykuwa płyta główna jako komponent odpowiedzialny
za wykonywanie obliczeń i działanie programu. Głównymi elementami wchodzącymi w skład płyty głównej są:
Procesor jest główną jednostką obliczeniową całego systemu komputerowego,
przeprowadza wszystkie operacje w komputerze.
Magistrala danych jest kanałem do przesyłania danych pomiędzy komponentami systemu. Jest jak ”rura” łącząca wiele wejść i wyjść. W danej chwili
jej pracy, jedno urządzenie działa w trybie nadawania a drugie w odbioru,
reszta pozostaje bierna czekając na swoją kolej. Zazwyczaj jednym z tych
czynnych urządzeń jest procesor.
Rejestry są najszybszym typem pamięci (i najdroższym) i to od ich wykorzystania przez procesor w głównej mierze zależy szybkość oprogramowania.
Rozmiar rejestrów zależy od tzw. architektury procesora. Pierwsze procesory były konstruowane w architekturze 16 bitowej co oznaczało że procesor operował na danych o rozmiarze do 16 bitów, zatem był wyposażony
w rejestry o pojemności 16 bitów. Obecnie dominują procesowy 32 bitowe,
część rynku jest jednakże już opanowywana przez procesory 64 bitowe.
5
ROZDZIAŁ 1. PODSTAWY ARCHITEKTURY KOMPUTERA
6
Sterownik przerwań , ponieważ komputer pracuje równolegle nad być może
wieloma zadaniami, może się okazać że w trakcie pracy nad jednym z nich,
jedno z urządzeń zgłosi procesorowi żądanie by ten sprawdził jego stan
i wykonał pewne operacje. By proces ten był nadzorowany i odpowiednio kolejkowany (które urządzenie jako kolejne powinno być obsłużone)
wprowadzony został sterownik przerwań. Sterownik przerwań jest mikrokontrolerem który nadzoruje zgłaszane ”przerwania” (tu: żądania przerwania bieżącego zadania przez procesor), ustala ich priorytet i delikatnie
”szturcha” procesor gdy nadejdzie przerwanie. Przykładem może być wciśnięcie przycisku na klawiaturze, które spowoduje zgłoszenie przerwania
celem odczytania znaku z bufora klawiatury. Innym rodzajem przerwania
są np. mrugania diody LED na przerwanie zgłaszane przez dysk twardy.
Zegar jest urządzeniem, które odmierza pewien krótki okres czasu w komputerze i może być wykorzystane zarówno do przerwań systemowych, jak dla
innych celów. Zbudowane jest na zasadzie drgającej struktury krystalicznej.
1.1.1
Rzecz o rejestrach
Rejestry są podstawową pamięcią komputera. To na danych w nich zawartych
są wykonywane wszystkie obliczenia. Rejestrów w architekturze rodziny procesorów Intel32 występuje ich około 20-tu. Do najważniejszych należą:
EAX / AX jest rejestrem nazywanym Akumulatorem. Jego nazwa pochodzi
od tego, że to najczęściej w nim umieszczane są wyniki pracy procesora
(akumuluje wyniki).
EBX / BX jest rejestrem adresowania. W tym rejestrze składowane są adresy
w pamięci RAM w chwili gdy procesor stara się je odczytać lub zapisać.
ECX / CX rejestr odliczający. Jest rejestrem ładowanym pewną daną przy
okazji wykonywania wszelakich pętli w trakcie obliczeń. Jest ładowany
pewną wartością, która co wykonanie pętli jest zmniejszana automatycznie o 1. Gdy osiągnie wartość 0 pętla jest zatrzymywana. W tej formie
rozwiązania pętli można dostrzec przewagą (i przyczynę) osobnego tworzenia pętli FOR.
CS, DS rejestry obszaru pamięci. Po załadowaniu programu do pamięci RAM,
obszar pamięci gdzie znajduje się kod programu jest oznaczany w rejestrze CS (CODE SEGMENT), zaś dla danych ma adres ich składowania
znajduje się w rejestrze DS (DATA SEGMENT)
REJESTR FLAGOWY jest specjalnym rejestrem procesora, gdzie składowane są nie dane wyjściowe procesora, ale błędy które zostały zgłoszone
w trakcie obliczeń.
1.1.2
Rzecz o pamięci
W kwestii pamięci procesora, wyróżnioną role pełni pamięć RAM. Dysk twardy
nie pełni żadnej innej funkcji poza funkcją składowania danych. Wszelkie operacje procesora na danych z dysku, muszą być poprzedzone przekopiowaniem
ROZDZIAŁ 1. PODSTAWY ARCHITEKTURY KOMPUTERA
7
tychże danych do pamięci RAM procesora. Pamięć RAM po załadowaniu systemu operacyjnego jest dzielona na obszar do użytku oraz na stos procesora.
Stos procesora jest miejscem gdzie mogą być składowane dane. Zgodnie z zasadą stosu rzeczy odłożone później są zdejmowane wcześniej. Stos jest bardzo
często wykorzystywanym obszarem pamięci.
Przykłady użycia stosu
ˆ Tymczasowe zachowanie stanu rejestrów, np gdy potrzeba rejestrów do
kolejnych obliczeń a nie można poświęcić posiadanych danych.
ˆ Wykonanie przerwania - w chwili pojawienia się przerwania wszystkie dane
nad którymi pracował procesor muszę być zachowane tak by po zakończeniu przerwania procesor mógł kontynuować pracę. Na stos są wtedy zrzucane wszystkie używane rejestry włącznie z flagowym oraz wskaźnikiem
do punktu w kodzie gdzie nastąpiło przerwanie.
ˆ Wywołanie funkcji - w chwili wywołania funkcji podobnie jak przy przerwaniu, wszystkie dane zostają zapisane na stosie.
ˆ Przekazanie parametrów do funkcji - Często lista parametrów funkcji jest
bardzo długa. Wtedy nie jest możliwym przekazać ich przez rejestry (
przekazywanie przez rejestry jest nawet nie wskazane). Jedyną możliwością
jest odłożyć je wtedy przez stos tak by nowa funkcja mogła go łatwo
odzyskać ze stosu.
1.1.3
Rzecz o programowaniu
W programowaniu występuje mnóstwo głównych reguł. Jednakże najważniejsze
reguły informatyki to:
ˆ Niezależność poziomów programowania
ˆ Zgodność wstecz
Poziomy programowania
Napisanie programu jest z reguły rzeczą trudną. Wymaga dokładnego przemyślenia sytuacji, z którą program ma sobie radzić, zapobiec dopuszczenia do błędu
programu oraz wykonania optymalnej ilości operacji by kod był szybki. Nie jest
jednak nigdy tak, że jeden programista pisze cały program. Częściej programiści dzielą program na małe części i każdy realizuje jego część. Nigdy nie jest
również tak, że program raz napisany nie zostanie już nigdy poprawiony. Ważną
zasadą jest więc właśnie to by programy mogły korzystać z siebie nawzajem
w sposób bezpieczny i niezmienny pomimo oczywistych poprawek lub nieznajomości innego programu. Prostym przykładem jest np. zainstalowanie nowego
sterownika do drukarki. Pomimo, że program sterownika działa zupełnie inaczej,
programy wyżej powinny mieć możliwość wywoływać DRUKUJ bez względu na
to jak dany sterownik realizuje tą funkcję. Jest to tzw. niezależność poziomów
programowania.
ROZDZIAŁ 1. PODSTAWY ARCHITEKTURY KOMPUTERA
8
Interfejsy
Interfejsem będziemy nazywać dowolny zestaw czynności udostępnianych przez
program lub aplikację. Tak naprawdę zaś interfejs jest fikcyjnym programem,
który ma zapewniać to, że inne programy będą mogły się do pewnej funkcjonalności odwoływać pomimo, że programista mógł zupełnie zmienić samą funkcję.
Korzystanie z interfejsów zapewnia jednak to, że programy mogą ze sobą bezpiecznie współpracować, mimo iż byłyby modyfikowane.
Zgodność wstecz
Niektórzy nazywają największą zmorą programistów. Przyczyny jej stosowania
są czysto ekonomiczne. Zachowanie zgodności wstecz to nic innego jak zapewnienie starszym programom możliwości pracy. I również w tym punkcie nieocenione
okazują się być interfejsy, które potrafią poprawić to co robią stare programy i
w miarę możliwości zastosować usprawnienia. Przykładem jest np. to że nowe
wersje MS-WORDa potrafią otwierać pliki z poprzedniej wersji programu np z
roku 97. Pomimo to, programy te niechętnie pozwalają zapisywać w starych formatach, które są mniej wydajne lub wręcz nie pozwalają na pewne dodatkowe
funkcje. Z tej samej przyczyny architektura systemu MS-Windows nie zmieniła
się zbytnio od roku 1994.
1.2
Języki programowania
Pomiędzy językiem który rozumie procesor komputerowy, a językiem ludzkim
jest olbrzymia przepaść. Nie jest możliwym aby przeskoczyć te różnice i ani
procesor nie zacznie ”słuchać po ludzku” ani programista nie napisze programu
za pomocą ciągu zer i jedynek. Muszą istnieć zatem narzędzia, specjalne translatory które tłumaczyły by nawzajem te języki. Jako, że różne są potrzeby i
wymagania wyróżnia się 5 generacji języków.
Języki procesora - I Generacja - jest językiem komend procesora, jego
składnia jest różna dla różnych procesorów i jest bardzo zorientowana na
stronę techniczną procesora. Dominuje tu poprawianie programu poprzez
lepsze realizowanie poleceń procesora.
Języki komend procesora - II Generacja - Asemblery - jest to język dyrektyw wydawanych procesorowi. Nie zależy on od rodzaju procesora. W
programach tych poprawianie realizuje się poprzez lepsze zarządzanie pamięcią. W asemblerach pisze się głównie sterowniki dla sprzętu.
Języki proceduralne - III Generacja - podstawowa generacje języków programowania. Jest to najniższa generacja języków niezależnych sprzętowo
tzn. ten sam kod programu działa tak samo na różnych komputerach,
choćby były zupełnie inaczej zbudowane. Słowo proceduralne w nazwie
określa, że programista dokładnie określa zadanie, które wykonać ma program. Przykładowe języki programowania to C, C++ ,C#, D, Pascal,
Java, Ada95.
Języki deklaratywne - IV Generacja - języki programowania z tej grupy są
bardzo przyjazne użytkownikowi. W przeciwieństwie do proceduralnych,
ROZDZIAŁ 1. PODSTAWY ARCHITEKTURY KOMPUTERA
9
tu program automatycznie stara się zrozumieć co miał na myśli użytkownik by uzupełnić jego kod. Przykładami takich języków są : SQL, HTML,
XML, BASH, PYTHON, PERL, PHP , AWK.
Języki inteligentne - V Generacja - dopiero rozbudowywana gałąź samouczących się programów. Rzadko stosowana.
Języki I i II generacji nazywa się językami niskiego poziomu - jako, że pozwalają
tworzyć oprogramowanie zależne od sprzętu, języki wyższych generacji nazywane są językami wysokiego poziomu, jako niezależnym sprzętowo.
1.2.1
Od kodu do programu
Ze względu na to, że języki V generacji są wielką innowacją, a języki II generacji
są niewydajne w tworzeniu aplikacji, programiści skupiają się na pracy w językach III i IV generacji. Programy stworzone w językach III generacji nazywamy
kodem, zaś programy stworzone w IV generacji nazywamy skryptami. Pomiędzy
nimi zachodzi istotna różnica.
Z tą różnicą związane są dwa pojęcia
Kompilator - program komputerowy, którego zadaniem jest tłumaczenie kodu
na języki coraz to niższego poziomu, aż do utworzenia kodu maszynowego.
Jest to postępowanie, któremu podlegają języki 3 poziomu.
Interpreter - program komputerowy, którego zadaniem jest wykonanie ciągu
poleceń umieszczonych w programie języka IV generacji, zwanym skryptem. Jest to opis ciągu wywołań kolejnych programów niższego poziomu,
celem uzyskania określone efektu.
Z racji istnienia takiej struktury rzeczy, zauważyć możemy, że jeden kod wysokiego poziomu, może różnie działać ze względu na miejsce jego kompilacji. Oznacza
to również, że skompilowany program przeniesiony na inną stację komputerową
może działać błędnie lub w ogóle. Z drugiej strony mamy jednak zapewnienie,
że na poziomie logicznym kod wykonany na jednej i drugiej stacji da takie same
wyniki.
1.3
1.3.1
Funkcje systemu operacyjnego
Jądro systemu operacyjnego
W komputerze niezwykle istotną funkcję pełni system operacyjny (ozn. OS).
Pełni on role interfejsu pomiędzy programami, a architekturą komputera i urządzeniami doń podłączonymi. System operacyjny składa się z:
ˆ Jądra systemu
ˆ Aplikacji systemu
ˆ Sterowników
Aplikacjami systemu są wszystkie programy zainstalowane na systemie by zapewnić jego podstawową działalność. Wszystko co komputer może wykonać dla
ROZDZIAŁ 1. PODSTAWY ARCHITEKTURY KOMPUTERA
10
innych programów w systemie jest jednakże zdefiniowane w jądrze systemu operacyjnego. Nie jest możliwym by jakikolwiek program znajdujący się powyżej
poziomu jądra systemu był w stanie bezpośrednio wywołać aplikacje z poziomu
poniżej. Jądro systemu świadczy zestaw metod i jeśli jakieś zastosowanie jest
zgodne z zasadami pracy systemu komputerowego, to jest przesyłane poniżej.
Najczęściej jądro służy do przekazania poleceń wywołania do poszczególnych
sterowników. Sterowniki mają szczególną pozycję w systemie operacyjnym i dlatego ich instalacja musi być bardzo odpowiedzialna. Taka struktura systemu ma
natomiast wiele zalet w kwestii bezpieczeństwa. W ten sposób programy, wywoływane osobno nie mogą mieć na siebie wzajemnego wpływu, gdyż wszystkie
operacje są nadzorowane przez jądro systemu.
1.3.2
BIOS i uruchomienie stacji
BIOS jest specyficznym rodzajem oprogramowania. Jest to oprogramowanie typu ROM tzn. jedynie do odczytu (choć od tego by był to czysty ROM, ostatnio
się odchodziło to obserwuje się tego negatywne skutki), związane z poszczególnymi elementami stacji komputerowej. Jedną z głównych rol BIOSu jest uruchamianie stacji. Kolejne etapy uruchamiania stacji (orientacyjne):
1. Start BIOSu płyty głównej
2. Poszukiwanie na kolejnych pozycjach systemów operacyjnych
3. Start jądra systemu
4. Start podstawowych usług systemu - montowanie systemów plików
5. Start trybu graficznego (lub nie :P)
6. Wyświetlenie okna logowania
1.3.3
Systemy plików
Nie rozłącznym pojęciem dla każdego OS-u jest system plików. Jest to sposób
zapamiętywania danych na dysku twardym. Sposób taki, musi być wydajny byśmy mogli zapisać jak najwięcej danych oraz bezpieczny - byśmy nie stracili
danych. Jak łatwo przypuszczać ma się albo jedno albo drugie. Najczęściej całość
wygląda następująco.
DYSK FIZYCZNY - stanowi górny limit dla składowania danych.
PARTYCJA - obszar zapamiętania poszczególnych danych.
SEKTORY - obszar umieszczenia danych
KLASTRY - jednostka pojemności pamięci. Pojedyncza dana zapisana na dysku jest w klastrze.
Dysk fizyczny, ma w swoim nagłówku listę wszystkich partycji na niej utworzonych (TABLICA PARTYCJI).Oprócz tego znajduje się tu również MBR
(MasterBootRecord) bardzo ważny element wskazujący położenie ewentualnego
programu ładującego system operacyjny. Partycja zaczyna się z kolei elementem TOC (TABLE OF CONTENT) - tj tablicy zawierania. Są tu umieszczone
ROZDZIAŁ 1. PODSTAWY ARCHITEKTURY KOMPUTERA
11
wszystkie pliki na dysku, a dokładniej lokalizacja ich początku. Pliki znajdują
się na dysku w postaci rozproszonej. W TOC znajduje się położenie pierwszego
kawałka pliku, na końcu pierwszego kawałka znajduje się położenie następnego
kawałka itd. Systemami plików stosowanymi obecnie są: NTFS dla windows,
EXT3/4 dla linux. Oprócz tego historyczne formaty to FAT16, FAT32 i VFAT
(obecnie na pendrive’ach).
1.3.4
Funkcja hashująca w systemie plików
Stosowanie tablicy plików jest dobrym rozwiązaniem w kwestii zapisu plików,
jednak zapis nie jest jedyną główną operacją w systemie plików. Inną przykładową funkcją systemu plików jest wyszukiwanie pliku o pewnej cesze, powiedzmy
nazwie. Prosta tablica plików nie jest wygodnym do poszukiwania rozwiązaniem.
Trzeba by przejrzeć średnio połowę, a w najgorszym wypadku wszystkie zapisy
tablicy, których może być bardzo dużo. Potrzebna jest subtelniejsza struktura,
która jednak nie spowoduje opóźnień w dostępie do plików.
Najprostszym rozwiązaniem byłoby podzielenie jedne dużej tablicy na kilka (a raczej kilka-dużo np kilka tysięcy) mniejszych tablic zawierających tylko
wybrane rekordy. Więc można by przykładowo podzielić tekst ze względu na
pierwszą literę w nazwie pliku. To podzieli zespół nazw na około 27 grup, co
skróci przeszukiwanie 27 razy ...
Błąd. Przyśpieszenie wcale nie będzie nawet tak średnio dobre. Problemem
jest liczność poszczególnych grup. Jak często trafiają się nam słowa na literę Y
a jak często na literę K. Podział, który powinniśmy zastosować powinien zapewnić nam możliwie równomierne rozłożenie składu tych grup. Zatem potrzebne
jest przyporządkowanie odpowiednich grup odpowiednim nazwom. Są to bardzo
skomplikowane w wypracowaniu, ale proste w użyciu funkcje nazywane fachowo
hashującymi.
Rozdział 2
Algorytmy i struktury
danych
Napisanie programu nie jest niczym trudnym. Prawdziwą trudnością programistyczną jest zrobienie tego przy minimalnych nakładach. Programy optymalizuje
się ze względu na dwie ich cechy:
Ilość operacji - mierzona w taktach procesora;
Zużycie pamięci - mierzona w ilości wykorzystanej pamięci
W zależności od stawianych sobie wymagań wykorzystuje się różne struktury i
różne metody prowadzenia obliczeń. Postaram się teraz omówić kilka najprostszych struktur.
2.1
Struktury statyczne
Pierwszym typem struktur są struktury statyczne. Wszędzie w programowaniu
określenie statyczne będzie się odnosiło do tworów, które są tworzone w chwili
uruchomienia kodu (tzn. już w momencie uruchomienia programu, a zanim zostanie wykonane jakiekolwiek polecenie te struktury otrzymują swój przydział
w pamięci).
2.1.1
Tablice
Tablice są najprostszym sposobem łączenia wielu danych. Pozwalają one połączyć jednakowe obiekty w jedną większą strukturę. Cechą naturalną tablicy jest
jej długość i rodzaj zawieranych danych.
2.1.2
Rekordy
Rekordy są sposobem na generowanie bardziej złożonych typów danych poprzez
łączenie określonej liczby elementów prostszych. Najlepszym przykładem jest
data, która jest typem złożonym z liczby odpowiadającej dniu, liczby odpowiadającej miesiącowi oraz liczby roku. Cechą szczególną rekordów jest to, że
potrafią łączyć składowe różnych typów w przeciwieństwie do tablic.
12
ROZDZIAŁ 2. ALGORYTMY I STRUKTURY DANYCH
2.1.3
13
Obiekty
Obiekty w najprostszym ujęciu są pewnym uogólnieniem rekordów, ale że jest
to temat obszerny jeszcze zdąży być omówiony.
2.2
2.2.1
Struktury dynamiczne
Stos
Często fachowo pieszczotliwie nazywany LIFO (Last In - First Out). Informatycy żartują, że ta struktura danych została stworzona przez Jezusa (”Pierwsi
będą ostatnimi, a ostatni pierwszymi”). Jako struktura ma następującą postać.
By utworzyć stos potrzebne są dwa twory. Twór o nazwie stos, oraz twory o
nazwie elementy stosu. Element stosu jest swego rodzaju otoczką, która otoczy element odkładany na stos. Ponadto każdy element stosu wskazuje element,
który znajduje się na stosie poniżej jego, lub jeśli jest ostatni nie pokazywać
niczego. Twór stosu jest zarządcą struktury. Pokazuje zawsze wierzchołek stosu
oraz wie jak zrealizować dwie główne operacje stosu.
PUSH - odłożenie kolejnego elementu na stos.
POP - zdjęcie elementu z wierzchołka stosu.
Operacjami, które są bardzo trudnymi do wykonania na stosie to usuwanie z
wnętrza stosu oraz np. sortowanie.
2.2.2
Kolejka
Kolejka jest z kolei pieszczotliwie nazywana FIFO (First In - First Out). Podobnie jak stos, struktura ta składa się z elementów kolejki i samej kolejki.
Elementy kolejki pokazują element je poprzedzający (”ta pani stała przede tu
przede mną”), zaś sama kolejka musi tym razem pamiętać i swój początek i swój
koniec. Operacje identyczne jak dla stosu.
2.2.3
Lista
Zwana też niekiedy listą dwukierunkową. W tej strukturze zmianie ulegają elementy listy, które pamiętają i poprzednika i następnika. Użycie takiej ulepszonej
kolejki pozwala łatwo z niej usuwać elementy.
2.2.4
Drzewo binarne
Z goła odmiennymi strukturami są drzewa. Drzewa są strukturami o zupełnie odmiennym podejściu niż listy, kolejki i stosy. Drzewa składają się z tworu
drzewa oraz tzw. węzłów (ang. node) (którym odpowiadają rozgałęzienia). Drzewo pamięta swoją podstawę tzw. korzeń (ang. root). Węzły są otoczką dla danych. Dodatkową informacją jest położenie kolejnych węzłów. Jeden węzeł może
wskazywać wiele innych węzłów (maksymalnie pewną określoną ilość - najczęściej dwa). Jeśli węzeł nie wskazuje żadnych kolejnych węzłów to nazywamy go
liściem (ang. leaf). Drzewa, a w szczególności drzewa binarne są najlepszą strukturą do przechowywania uporządkowanych struktur danych. W takim układzie
ROZDZIAŁ 2. ALGORYTMY I STRUKTURY DANYCH
14
wyszukiwanie, usuwanie, dołączanie i wiele innych operacji może być wykonanych rekordowo szybko.
2.2.5
Graf
Tak po prawdzie wszystkie struktury danych są z punktu widzenia matematyki
pewnymi grafami. Dla ogólnych grafów najczęściej tworzy się tablicę z posiadanych danych oraz inną strukturę do oznaczenia krawędzi i ich ewentualnych
wag.
2.3
Porównanie typów struktur
Struktury dynamiczne są najczęściej stosowanymi w programowaniu. Struktury statyczne co prawda wśród swoich zalet mają prostotę oraz maksymalną
szybkość przy tworzeniu i dostępie, jednakże mają znacznie poważniejsze wady. Absorbują i nigdy nie zwalniają dużej ilości miejsca, jak również że przy ich
tworzeniu może nie istnieć dostatecznie długa przestrzeń pamięci by ją utworzyć.
Rozdział 3
Wstęp - instalacja
zintegrowanego środowiska
programistycznego dla Javy
W części pierwszej skupimy się na przygotowaniu komputera jako stanowiska
pracy programisty. W przypadku programowania w technologii Java (nazywamy to technologia, gdyż myślimy łącznie o języku, narzędziach, jak i sposobie
myślenia), może być to wykonane niemalże na każdym ciut bardziej zaawansowanym urządzeniu elektronicznym, telefon, tablet, netbook aż w końcu nawet i
największych komputerach. W odróżnieniu od poprzedzających ją technologii,
raz napisany program będzie mógł działać na każdym urządzeniu. Technologie
takie jak C/C++, Ada95, Pascal czy inne generowały kod maszynowy, który
pozwalał programom działać na urządzeniach o podobnej specyfikacji. Często
aby zaadaptować ten sam program potrzebne były poprawki jego kodu. W przypadku Javy, ten problem został niemal doszczętnie wyeliminowany.
3.1
Czym jest wirtualna maszyna Javy oraz najważniejsze składowe technologii
Podstawowym problemem kompilacji1 programów, jest to że w pewien sposób umieszcza ona w kodzie maszynowym2 konstrukcję komputera na którym
została przeprowadzona. W przypadku Javy rozwiązano to poprzez specjalny
program instalowany w urządzeniu, który udaje przed programem napisanym
w Javie, cały komputer. Sprawia to że cały kod można łatwo przenosić pomiędzy urządzeniami. Tym programem jest wirtualna maszyna Javy (JVM - Java
Virtual Machine). Jej instalacja jest niezbędna do tego by na danym urządzeniu
dział jakikolwiek program w tej technologii.
Wirtualna maszyna jest obecnie jedynie częścią większej całości. Do tego
by uruchamiały się programy potrzeba również wielu standardowych bibliotek.
Wirtualna maszyna razem wspólnie z tymi bibliotekami jest udostępniania jako
1 proces zamiany kodu programu podanego przez programistę na jego binarny odpowiednik
dla procesora
2 kod po kompilacji, kod binarny rozumiany przez komputer
15
ROZDZIAŁ 3. WSTĘP - INSTALACJA ZINTEGROWANEGO ŚRODOWISKA PROGRAMISTYCZNEGO
środowisko uruchomieniowe java (JRE - Java Runtime Environment). W przypadku programistów potrzeba jeszcze znacznie więcej bibliotek, podobnie jak
dużo dokumentacji i innych narzędzi. Wszystko wspólnie można pobrać jako
Zestaw Narzędzi Programisty Java (JDK - Java Development Kit). JDK występują w różnych wersjach. Zalecane jest zawsze używanie najnowszych, jako najbezpieczniejszych i najaktualniejszych. JDK powinna być dołączona do systemu
przy instalacji IDE (o którym zaraz). Jeśli nie została, należy ją doinstalować
ręcznie.
3.2
Czym jest IDE
O tym jak dużo jest czasami pracy aby z kodu napisanego przez programistę
zrobić kod rozumiany przez komputer, wystarczy zapytać starszych programistów. Tymczasem prawda jest taka, że ten proces jest dość prosty, a jedynie
bardzo męczący w prowadzeniu. Zgodniej z zasadą co nudne należy automatyzować wkrótce powstały narzędzia, które miały przejąć od programisty zadanie
pilnowanie tego. Programy te są rozwijane po dziś dzień i większość programistów nie wyobraża sobie pracy bez nich. Główne aspekty z procesu tworzenia
kodu przez nie monitorowane to
ˆ Proces kompilacji kodu
ˆ Weryfikacja składni kodu
ˆ Zarządzanie bibliotekami
ˆ Zarządzenia konfiguracją
ˆ Nadzór wersji kodu
ˆ Zarządzanie testami
ˆ Pobieranie brakujących bibliotek
ˆ Generowanie prostych i monotonnych fragmentów kodu
ˆ Przeszukiwanie kodu źródłowego
ˆ Reorganizacja nazw w kodzie (tzw. refaktoring)
ˆ Podpowiadanie i kolorowanie składni
ˆ Generowanie i przedstawianie dokumentacji
Wszystkie narzędzia o których tu wspominam są nazywane IDE czyli zintegrowanymi środowiskami programistycznymi (ang. Integrated Development Environment).
W przypadku języka Java na rynku dominują dwa najsilniejsze programy
(oba darmowe). Jednym z nich jest Eclipse, stanowiący wolne oprogramowanie
będące własnością Fundacji Eclipse. Drugim programem jest Netbeans, darmowa aplikacja stanowiąca własność amerykańskiego giganta na rynku oprogramowania czyli firmy Oracle (firmy, który wykupiła firmę Sun - twórców języka
Java). Zasadniczo osoby, które są z zawodu programistami i tworzą duże projekty, wybierają za swoją platformę Eclipse, jako prościej konfigurowalny do
ROZDZIAŁ 3. WSTĘP - INSTALACJA ZINTEGROWANEGO ŚRODOWISKA PROGRAMISTYCZNEGO
ich potrzeb. Osoby o mniejszych potrzebach programowania z reguły wybierają
Netbeans, z uwagi na dużą ilość wbudowanych narzędzi. O ile kod java może
być bezproblemowo przenoszony pomiędzy platformami Eclipse i Netbeans, o
tyle konfiguracja projektów i algorytmu budowania różnią się na tyle, że z reguły nie udaje się przenieść projektu pomiędzy tymi platformami. Oczywiści
skompilowane kody przenoszą się bez problemu.
Z uwagi na początkujący charakter czytelnika, w dalszej części zakładamy, że
czytelnik posiada na swoim komputerze zainstalowany zarówno zestaw narzędzi
Java czyli jdk jak i Netbeans IDE. W takim układzie możemy spokojnie przejść
do nauki podstaw tego prostego języka jakim jest Java.
Rozdział 4
Podstawy języka Java
Aby nie programować na kartce papieru zacznijmy od razu od utworzenia nowego projektu. Wybierzmy z menu plik okna Netbeansa, nowy projekt typu Java
Application i nazywamy go HelloWorld. Automatycznie zostanie nam wygenerowana klasa HelloWorld oraz w niej funkcja main(String[]). Aby nasz projekt
coś napisał musimy wywołać metodę println() z klasy System dla jej elementu
out tj.
System.out.println("Hello World!!"); // tak wypisujemy tekst na ekranie
Co musimy umieścić w kodzie funkcji main(String[]):
public static void main(String[] args) {
System.out.println("Hello World!!"); // tak wypisujemy tekst na ekranie
}
Aby mieć lepsze zrozumienie co tak naprawdę się stało możemy zrobić to wolniej
public static void main(String[] args) {
String hello = "Hello World!!"; // tak tworzymy zmienną o typie tekstu
System.out.println(hello); // tak wypisujemy zmienną tekstową
// a to oczywiście był znak komentarza
}
Program utworzył nową zmienną o nazwie hello i wstawił tam ciąg znaków Hello
World!!, a następnie wypisał stan tej zmiennej.
4.1
Zmienne w Javie. Pierwsze starcie
Programowanie jest niczym innym jak opracowywaniem pewnego zbioru danych.
Dane te muszą znaleźć się najpierw w pamięci komputera. To co wyróżniło Javę
w porównaniu do jej poprzedników to sposób w jaki można modelować bardzo
skomplikowane dane. Tak czy inaczej musimy poznać podstawowe typu danych
aby umieć z nimi pracować.
W Javie typy danych można podzielić na podstawowe 4 grupy
1. Prymitywne
2. Wyliczeniowe
18
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
19
3. Tablicowe
4. Klasy
Zacznijmy od najprostszych
4.1.1
Typy prymitywne
Typu prymitywne są podwalinami wszystkich pozostałych typów danych. Pozwalają one gromadzić najprostsze informacje w pamięci komputera. Jest 8 typów prymitywnych. Pierwsze 4 czyli ’byte’, ’short’, ’int’ oraz ’long’ są typami
które służą do przechowywania w pamięci komputera liczb całkowitych. Dwa
kolejne czyli ’float’ i ’double’ służą do przechowywania w nich liczb zmiennoprzecinkowych czyli przybliżeń liczb rzeczywistych. Typ do składowania danej
zmiennej wybiera programista i musi kierować się wartościami liczba jakie będą
tam składane. Najczęściej wybiera się typy int oraz double. Ostatnie dwa typy to ’char’, czyli znak alfanumeryczny oraz ’boolean’ czyli typ prawda-fałsz.
Poniżej przykład zadeklarowania (zgłoszenia do użycia) każdego z powyżej wymienionych typów.
/* typy całkowite */
byte calkowity1bajt;
short calkowity2bajty;
int calkowity4bajty;
long calkowiry8bajtow;
/* typy zmiennoprzecinkowe */
float zmiennoprzecinkowa4bajty;
double zmiennoprzecinkowa8bajtow;
/* inne zmienne */
char znak;
boolean logiczny;
W przypadku Javy, wszystkie nieprzypisane wartości prymitywne są od razu
inicjowane pewną wartością. Dla liczb są to zera, fałsz dla typu logicznego oraz
znak pusty dla typu znakowego.
4.1.2
Typy wyliczeniowe
Typ wyliczeniowy tworzy się poprzez stworzenie ciągu określeń, które wypełniają jego sens. Np.
enum PoraRoku { WIOSNA, LATO, JESIEN, ZIMA};
Przyjmuje się, że wartości stałe nazywa się pisząc wszystko dużymi literami.
Odróżnia to je od tworzonych zmiennych, do nazwania których używa się mały
liter. Jeśli nazwa zmiennej ma łączyć w sobie kilka słów, przyjmuje się że poza
pierwszym z nich, wszystkie rozpoczynamy z wielkiej litery. Później dowiemy
się, że wszystkie pierwsze litery piszemy z wielkiej litery np. w nazwach klas.
Jak używa się typu wyliczeniowego? Bardzo prosto
enum PoraRoku { WIOSNA, LATO, JESIEN, ZIMA};
/**
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
20
* @param args the command line arguments
*/
public static void main(String[] args) {
/* użycie typu wyliczeniowego */
PoraRoku pora = PoraRoku.WIOSNA;
}
Typy wyliczeniowe mają jeszcze inne możliwości ale na razie je przemilczymy.
4.1.3
Typy tablicowe
Tworzenie bardziej skomplikowanych danych jest możliwe na dwa sposoby. Pierwszym z nich jest łączenie kilku cech i określanie ich wspólną nazwą np. przez
adres zamieszkania danej osoby można rozumieć zgrupowanie w pamięci komputera nazwy miasta, nazwy ulicy i numeru domu/mieszkania. Ten typ doprowadzi
nas do pojęcia klasy. Tymczasem na razie interesuje nas drugi z typów generowania nowych typów. Gdy nauczyciel wchodzi do klasy i mówi, że ”ma wyniki
klasówki” to odnosi się do do nich jak do pojedynczego tworu. Wynik klasówki
składa się z grupy elementów tego samego typu. Nauczyciel sprawdzał je biorąc klasówkę numer 1, potem klasówkę numer 2 i tak kolejno. Tworzyć większe
typy danych można zatem również grupując twory podobnego kształtu w tablicę tychże tworów. Do tworzenia tablic używa się operatora[] umieszczając przy
typie który ma stać się bazą tablicy.
int[] tablicaLiczbCalkowitych = {1,2,3,4,5};
char[] tekst = {’s’,’ł’,’o’,’w’,’o’};
boolean[] prawyIFalsze = { true, false, false, true};
double[] liczby = { 3.14, 2.78};
PoraRoku[] ulubionePoryRoku = {PoraRoku.LATO,PoraRoku.JESIEN};
tablicaLiczbCalkowitych[2]=3; // a tak uzyskujemy dostęp do
//któregoś elementu tablicy
Uwaga nie ma przeciwwskazań aby również elementy klas łączyć w tablice.
4.1.4
Klasy
Jeżeli chodzi o klasy w javie to potrafią one opisać niemal wszystko. Wszystkie typy danych, które nie są typami prymitywnymi okazują się być obiektami.
Obiekty to twory pasujące do pewnego wzorca czyli klasy. Łatwo zrozumieć
różnice pomiędzy obiektami, a klasami. Klasą jest człowiek, natomiast palcem
wskazana osoba jest stworzona na podstawie klasy człowieka i to czyni ją obiektem człowieka.
Klasą są np. tworzone przez nas przed chwilą tablice. Najpowszechniej stosowaną klasą jest klasa String czyli rozbudowana tablica znaków alfanumerycznych. W prostych słowach, ’String’ może reprezentować słowo, zdanie lub całe
strony tekstu.
To co jest potrzebne nam w pierwszej fazie kursu to jedynie wiedza o tym
jak stworzyć najprostszą klasę. Przykładowa klasa odpowiadająca Adresowi Zamieszkania wyglądała by jak poniżej:
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
21
static class AdresZamieszkania{
String miasto;
String ulica;
int numerDomu;
int numerMieszkania;
}
Znaczenie słowa static na razie przemilczymy.
Aby użyć naszej funkcji wewnątrz funkcji ’main’ musimy
AdresZamieszkania adres = new AdresZamieszkania();
adres.miasto="Łódź";
adres.ulica="Piotrkowska";
adres.numerDomu=1;
adres.numerMieszkania=2;
Słowo ’new’ jest używane do tworzenia nowych obiektów o danym typie. Potem
widzimy, że dzięki operatorowi kropki mogliśmy nadać wartości zmiennym w
pamięci.
4.1.5
I jeszcze o zmiennych
Omówmy jeszcze inne istniejące umowy dotyczące zmiennych w Javie. Nazwa
zmiennej może składać się z dużych i małych liter, cyfr i znaków podkreślenia.
Nazwa nie może składać się zacząć się od cyfry, a rozmiar liter ma znaczenie
(tzw. CASE SENSITIVE). Obowiązuje jednak umowa która obowiązuje mówi,
że
1. Nazwa zmiennej ma być znacząca - czyli odpowiadać jej treści lub zastosowaniu
2. Powinna zaczynać się od małej litery
3. Nie powinna zawierać znaku podkreślenia
4. Jeśli składa się z kilku słów języka codziennego to pierwszą literę nowego
słowa (prócz pierwszego z tych słów) wyróżnia się dużą literą
Zatem dla zmiennej typu int przechowującej ilość wykonań licznika pętli deklaracja powinna wyglądać następująco:
int iloscWykonanLicznikaPetli;
W tym dodam jeszcze uwagę. Każda linia wykonywalna (czyli prawie każda linia
kodu prócz kilku wyjątków) musi być zakończona znakiem ’;’.
4.1.6
Słowa zarezerwowane
Jest też lista słów tzw. kluczowych lub zarezerwowanych, które nie mogą być
użyte jako nazwy zmiennych. Oto ich lista
abstract
assert
boolean
continue
default
do
for
if
goto
new
package
private
switch
synchronized
this
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
break
byte
case
catch
char
class
const
4.1.7
double
else
enum
extends
final
finally
float
implements
import
instanceof
int
interface
long
native
protected
public
return
short
static
strictfp
super
22
throw
throws
transient
try
void
volatile
while
Znaki niepisane
Problemem przy wypisywaniu jest gdy próbujemy wykonać coś co nie ma znaku literowego na klawiaturze. Wśród całej gamy znaków są bowiem znaki tzw.
niepisane, które nie mają symbolu. Przykładem jest np. znak nowej linii. Język Java rozwiązuje ten problem definiując te znaki z użyciem znaku ”\” jako
symbolu specjalnego mającego zapewnić odmienną interpretacje. I tak:
\n - oznacza znak nowej linii
\t - oznacza znak tabulacji.
\v - tabulacja pionowa
\a - alarm
\f - nowa strona
\b - backspace
\” - do obsługi klamer w wypisywaniu
\\ - do obsługi znaku \w wypisywaniu
4.1.8
Rzutowanie
Język Java podobnie do języka C/C++ pozwala w łatwy sposób rzutować zmienne na inne. Nie robi tego jednak tak często i chętnie jak swój poprzednik i wymaga by wprost pisać, że nastąpi rzutowanie. Nie każde rzutowanie jest poprawne
(np. bitowo nie należy rzutować floata na int), a gdy się je stosuje czasem mimo,
że nie jest to wymagane, warto zaznaczyć że miało miejsce rzutowanie. Przykład
prostego rzutowania:
char literka = ’a’;
int noLiterki = (int) literka;
4.1.9
Typ specjalny void
Spośród wszystkich typów typ void jest bardzo wyjątkowy. W wielu językach
rozróżnia się procedury i funkcje. Przyjmuje się, że funkcja musi coś zwrócić,
zaś procedura tego nie robi. W programowaniu jednak przyjmuje się zasadę, że
jeśli pewne działanie nie musi niczego zwracać, to niech przynajmniej zwróci
jakiś kod błędu lub jego braku. Dlatego funkcje w Javie zawsze coś muszą zwracać. Najbezpieczniej by był to typ int, jednak z czasem by wyróżnić ten typ
utworzono specjalny typ, który nie zawiera żadnych informacji nazwany typem
void.
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
4.2
23
Podstawowe operacje w języku Java
Mając do dyspozycji podstawowe typy danych bylibyśmy w stanie napisać najprostsze programy o ile tylko znalibyśmy komendy jakie możemy wykonywać.
Najogólniej pisząc to co można robić w każdym z języków programowania to są
3 rzeczy
ˆ Sprawdzać, przy pomocy instrukcji warunkowych
ˆ Powtarzać, przy użyciu pętli
ˆ Wykonywać zwarte bloki kodu, przy pomocy napisanych funkcji.
Omówmy je sobie kolejno:
4.2.1
Instrukcje warunkowe
Instrukcje warunkowe pozwalają określić przypadki w jakich określony kod ma
zostać wykonany. Podstawową instrukcja tutaj jest ’if’. Blok if wygląda następująco
if (/*warunek logiczny*/) {
// ciąg instrukcji
}
W odróżnieniu od C++ w Javie warunkiem w if może być wyłączanie ciąg
instrukcji kończący się wartością logiczna czyli zmienną typu boolean. Warunek
if może mieć również dwie poniższe postaci
if (/*warunek logiczny*/) {
// ciąg instrukcji
} else {
// alternatywny ciąg instrukcji
}
lub
if (/*warunek logiczny*/) {
// ciąg instrukcji
} else if (/* inny przypadek */) {
// inny ciąg instrukcji
} else {
// alternatywny ciąg instrukcji
}
Drugim typem instrukcji warunkowej jest instrukcja ’switch’. W przypadku
Javy instrukcja ta może jedynie działać dla zmiennych typu prymitywnego oraz
kilku innych nielicznych jak np. String czy typ wyliczeniowy.
int zmienna = 5;
switch(zmienna){
case 2:
System.out.println("Zmienna wynosi 2");
break;
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
24
case 3:
case 4:
System.out.println("Zmienna wynosi 3 lub 4");
break;
case 5:
System.out.println("Zmienna wynosi 5");
case 6:
System.out.println("Zmienna wynosi 5 lub 6");
break;
default:
System.out.println("Brak inne dopasowania - wybrano domyślne");
}
Jak widać składa się ona z bloku poprzedzonego założeniem przełącznika (switch)
dla zmiennej o identyfikatorze zmienna. Potem wymienione są przypadki (case)
w których podane są wartości które tego przypadku dotyczą. Przetwarzane są
instrukcje do czasu końca bloku lub wystąpieniu instrukcji przerywającej ’break’.
4.2.2
Instrukcje pętli
Drugą główną klasą komend są instrukcje pętli, służące do powtarzania określonej czynności aż do spełnienia pewnego warunku.
Najprostszym przykładem pętli w Javie jest pętla while o następującej konstrukcji
while(warunek_logiczny){
Ciąg instrukcji;
}
Ciąg instrukcji jest wykonywany aż do momentu gdy warunek logiczny stanie się
fałszywy. Należy stosować ten typ pętli umiejętnie gdyż jest ona potencjalnie
nieskończona. W tym typie pętli, gdy warunek logiczny nie jest spełniony za
pierwszym razem, blok instrukcji w pętli nie zostanie ani razu wykonany.
Innym typem prostej pętli jest pętla do-while postaci:
do {
Ciąg instrukcji;
}while(warunek_logiczny);
Która różni się od poprzedniczki tylko tym, że warunek jest sprawdzany po raz
pierwszy po wykonaniu pierwszej pętli.
Innym typem pętli jest pętla for, która jest najbogatszą z zaimplementowanych w znanych językach i posiada olbrzymie możliwości. Jej postać to:
for (Inicjalizacja;Warunek;Inkrementacja){
Ciąg instrukcji;
}
Inicjalizacja jest polem, gdzie tworzone są np. liczniki dla pętli. Warunek jest jak
można by się spodziewać warunkiem na wyjście, przy czym szczególnie polecane
są warunki logiczne z > lub <. Pole inkrementacji (przyrostu) jest polem gdzie
opisuje się jak zmieniać się powinny pewne elementy np licznik. Przyjrzyjmy się
przykładowi, kodu sumującego elementy tablicy za pomocą pętli for:
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
25
int[] tablica = {1,2,3,4,5,6,7,8,9};
int n = 9;
int sum =0;
for (int i=0; i < n; i++) {
Ciąg instrukcji;
}
Uwaga! Istnieje jeszcze jedna wersja pętli. Jest to pętla foreach, podobna do
for ale z uwagi na to że jej konstrukcja, nie jest ani niezbędna do nauki podstaw
oraz wymagana jest wiedza z zakresu kolekcji Java zostanie ona omówiona w
dalszym miejscu lub pominięta w całości tego dokumentu.
W pętlach można wykonać pewne dodatkowe polecenia.
break - powoduje przerwanie wykonania bieżącej pętli i wyjście z niej. Należy
uważać, gdyż pozwala to na opuszczenie jedynie najbardziej wewnętrznej
pętli.
continue - jest ciekawszą instrukcją. Powoduje natychmiastowe przerwanie bieżącej pętli i przejście do następnej pętli. Jest to zwłaszcza wymagane gdy
pewna operacja (np. dzielenie przez 0) powinna być pominięta.
4.2.3
Funkcje
Choć to instrukcje warunkowe i pętle zapewniają wszystko co potrzebne w nowoczesnym programowaniu to jednak przez większość czasu programista pisze i
używa funkcji. Do czego służą nam funkcje
ˆ Grupują często powtarzający się fragment kodu
ˆ Grupują część instrukcji, która stanowią jedną spójną logicznie całość
W przypadku grupowania często powtarzalnego kodu, łączenie ich w jedną funkcję pozwala nam zaoszczędzić miejsca w pamięci kosztem nieznacznie
większej liczby operacji do wykonania.
Istotniejsza dla programisty jest druga zasada. Pozwala ona bowiem stworzyć elegancki abstrakcyjny model wszystkiego. Często jest tak, że wykonując
jakąś czynność nie myślimy o podstawowych czynnościach które się na nie składają. Gdy prosimy o zgięcie ręki kogoś. To nie myślimy wcale o mięśniach które
po kolej mu zginać by to się stało. Te działania są przed nami ukryte za kotarą
pewnego poziomu abstrakcji. Utrzymywanie kodu na jednym poziomie abstrakcji jest jednym z zadań programisty.
Do rzeczy, czyli jak w Javie tworzymy funkcje. Otóż funkcje w Javie wolno
nam tworzyć w obrębie jakieś klasy. Na początku będzie to nasza klasa główna.
Każda funkcja musi mieć podany typ zwracany i musi być zwrócony dokładnie
jeden. Funkcja może też przyjąć dowolną, ale określoną ilość danych wejściowych.
void funkcja(int a, float b, String c){
instrukcje;
}
funkcja może być poprzedzona słowem kluczowym static lub nie. Słowo kluczowe static określa czy dana część klasy należy bardziej do jej obiektów czy też
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
26
do samej klasy. Na przykład: każdy obiekt typu samolot posiada funkcję latania,
ale opisanie zasady latania tyczy się samej klasy samolotów a każdego z nich z
osobna. Słowem ’static’ wyróżnia się te funkcje, do użycia których nie potrzeba
obiektów tego typu (nie potrzebujemy mieć samolotu pod ręką by mówić o tym
w jaki sposób on lata, albo ile dany rodzaj samolotu pali).
4.3
Operatory
Specjalną grupę możliwości w programowaniu otwierane są przez tak zwane
operatory. Najogólniej można by powiedzieć, że operatory są szczególnym przypadkiem funkcji, działają powiem na operandach i produkują wynik. I faktycznie
w C++ można było je dokładnie w tenże sposób traktować. W przypadku Javy
jedna ważna rzecz zmieniła się. W języku Java nie można definiować nowych
operatorów. Postaramy się teraz omówić najważniejszej operatory występujące
w Javie.
Operator przypisania = jest najważniejszym operatorem w językach programowania. Jest prawostronnie łączny i jego lewej stronie musi zawsze występować zmienna lub referencja. Jest to jego jedyna funkcja w Javie, nie
można go przeciążyć i jest domyślnie generowany dla wszystkich klas.
Operatory arytmetyczne jest również cała gama operatorów matematycznych, które służą prostym operacjom na liczbach. Często są też w literaturze mylnie nazywane matematycznymi
Operatory logiczne służące do porównań oraz do operacji na wyrażeniach
logicznych
Operatory rzutowania służące do zamiany zmiennej na inną
Operator trójargumentowy służący do szybkiej inicjalizacji warunkowej
Operatory obiektowe operatory służące do pracy z obiektami
W przypadku Javy operatory są dostępne niemal wyłącznie dla typów prymitywnych. Jedynym wyjątkiem jest klasa String, gdzie działa operator dodawania
pełniąc rolę konkatenacji.
Lista operatorów:
= - przypisanie,
+ - dodawania,
- - odejmowanie,
* - mnożenie,
/ - dzielenie,
% - dzielenie modulo ,
++ - zwiększenie o 1,
-- - zmniejszenie o 1,
+=,-=,*=,/=,%=, jak wyżej ale zapisanie wyniku następuje
do zmiennej podanej po lewej stronie operatora
> - większe od,
>= - większe lub równe od,
< - mniejsze od,
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
27
<= - mniejsze lub równe od,
== - równe,
!= - nierówne,
! - zaprzeczenie logiczne,
&& - i logiczne leniwe ,
|| - lub logiczne leniwe,
& - i logiczne lub i bitowe ,
| - lub logiczne leniwe lub lub bitowe,
~ - negacja bitowa,
^ - alternatywa wykluczająca bitowa,
&=,|=,^= - ja wyżej ale z przypisaniem,
<< - bitowe przesunięcie w lewo,
>> - bitowe przesunięcie w prawo ze znakiem,
>>> - bitowe przesunięcie w prawo bez znaku
<<=,>>=, >>>= - jak wyżej ale z przypisaniem
new operator wywołania konstruktora
. - operator dostępu
(cast) - operator rzutowania
? : - operator potrójny
Kilka faktów dotyczących powyższych operatorów
ˆ Operatory ++ oraz −− w odróżnieniu od C++, występują jedynie w prawostronnej wersji (operand stoi po prawo). Służą do zwiększania/zmniejszania
wartości liczby całkowitej o 1.
ˆ Operatory leniwe logiczne wyróżniają się tym od od zwykłych, że nie wartościują niepotrzebnie wyrażenia. Np. gdy pierwsze w ’lub’ jest prawdziwe
lub fałszywe dla ’i’. Pozwala to uniknąć potencjalnie wielu błędów
String s;
...
if (s==null || s.isEmpty()){...}
przy lub zwykłym dla ’s’ pustego uzyskany by był błąd dostępu
ˆ Operatory bitowe operują na bitowej postaci liczby. Działają po pozycjach
wg zasady że 1 - oznacza prawdę a 0 fałsz.
ˆ Operatory przesunięcia za swój prawy argument przyjmują liczbę typu
całkowite o ilości miejsc przesunięcia. Operator << powoduje przesunięcie
w lewo przy jednoczesnym dopełnieniu zerami z prawej strony. Operator
>>> działa podobnie z zaznaczeniem dopełniania zerami od strony prawej. Operator >> jest przesunięciem ze znakiem gdyż dla liczb dodatnich
(0 na pierwszym bicie) uzupełnia je od lewej zerami, zaś dla ujemnych (1
na pierwszym bicie) uzupełnia je jedynkami.
ˆ Operator rzutowania pozwala zmieniać jeden typ prymitywny na drugi.
Np
int i = (char) ’c’;
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
28
W tej zasadzie działania operator ten działa jedynie dla typów prymitywnych. Dla obiektowych jest to operator reinterpretacji referencji. Chodzi
tu o patrzenie na otrzymaną referencję typu ogólnego jako na referencje
typu szczególnego
KlasaOgolna ko = new KlasaSzczegolna(); // ko ma cechy klasy ogolnej
KlasaSzczegolna ks = (KlasaSzczegolna) ko; //ks ma juz spowrotem
//wszystkie cechy obiektu
KlasaOgolna ko2 = new KlasaOgolna();
KlasaSzczegolna ks2 = (KlasaSzczegolna) ko2; // błąd rzutowania
ˆ Operator trójargumentowy służy do szybkiej inicjalizacji zmiennych. Jego
konstrukcja to
warunek_logiczny?wartosc1:wartosc2
wartosc1 jest nadawana zmiennej przy warunku prawdziwym, zaś wartosc2
w przypadku przeciwnym.
boolean czyMniejsza = true;
int i = czyMniejsza?3:5; //zwraca 3 gdy ma być mniejsza,
//5 w przeciwnym wypadku
ˆ Operator new służy do sygnalizacji użycia konstruktora czyli tworzenia
nowej zmiennej w pamięci
String text = new String("Jakis tekst");
Operatora tego używa się wyłącznie do tworzenia obiektów, w szczególności tablic, nigdy do typów prymitywnych.
// tablica 4 elementowa
int[] tab = new int[4];
int[] tab2 = new int[]{3,4,5};
String[] str = new String[5];
String[] str2 = new String[]{new String("raz"),
new String("dwa")};
ˆ Operator . służy do wywołania zasobów dla dowolnego obiektu. Jedynie
typy prymitywne nie posiadają dostępu przez ten operator. Operator ten
może być również wywoływany na rzecz klasy i wtedy oferuje zasoby posiadane przez klasę.
4.3.1
Literały
Istnieją również inne specjalne znaki w kodzie, które operatorami co prawda nie
są, ale ich funkcje są w pewien sposób szczególne. Nazywa się je często literałami.
Odpowiadają one potrzebom takim jak:
ˆ Podać liczbę w sposób ósemkowy, szesnastkowy.
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
29
ˆ Podać liczbę całkowitą długą (long)
ˆ Podać liczbę typu float
ˆ Podać liczbę z notacji wykładniczej
Aby zapisać liczbę w sposób ósemkowy wystarczy dopisać zero na początku
liczby, natomiast dla szesnastkowego należy dodać literał 0x lub 0X
int i = 010; // odpowiada 8
int j = 0x0F; //odpowiada 15
Aby zapisać liczbę długą używa się specjalnego postfixowego literału. Podobnie
by zaznaczyć że liczba jest typu float lub dla double (tu jest niepotrzebne, ale
czasami dla przejrzystości)
long i = 16666L;
float f = 3.14f;
double d = 3.1415d;
Aby zapisać liczbę w notacji wykładniczej piszemy:
float f = 1e-10f;
Wielkość liter w literałach nie ma znaczenia.
4.4
Obsługa błędów w języku Java
Oczekuje się, że napisany przez programistę program będzie niezawodny. Tymczasem zdarzają się sytuacje, gdy nie z winy samego programu, ten nie może
być poprawnie wykonany. Np. program ma sprawdzić w internecie notowania
jakiś walut. Tymczasem nie działa sieć internet. Zauważyliśmy, już że programy
(funkcje) kończą się wraz z wykonaniem instrukcji zakończenia i zwrotu ’return’.
Jednak funkcja która miała za zadanie odszukać wycenę danej waluty, nie spełniła swojej funkcji w skutek przeszkód. Zwrócenie jakieś przypadkowej wartości
nie rozwiązuje naszego problemu. Zależy nam bowiem na informacji, że nasza
funkcja nie ukończyła swojego działania w sposób planowany.
W przypadku języka Java problem ten rozwiązano w sposób następujący.
Istnieje specjalna podgrupa obiektów, które nazywane są wyjątkami. Nadrzędna wśród nich jest ”java.lang.Exception”. Najpierw wyjaśnijmy sobie jakie są
zasady tworzenia wyjątków, a potem jak w kodzie przebiega obsługiwanie rzeczy
z nimi związanych.
4.4.1
Drzewo hierarchii błędów
Dowiedzieliśmy się właśnie, że do obsługi błędów w Javie używa jest klasa
Exception z pakietu java.lang. Gdybyśmy jednak używali tylko tej klasy szybko
byśmy zgubili się w tym co one tak naprawdę oznaczają i co gorsza nie byłoby
nam łatwo ich rozróżniać.
Postaramy się omówić zasadę tworzenie drzewa hierarchii błędów na podstawie przykładu. Kupiliśmy używany samochód, a te jak wiadomo się psują.
Oznacza to, że dopuszczamy możliwość błędu dotyczącego całego samochodu.
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
30
class SamochodException extends Exception {...}
W samochodzie może się jednak zepsuć wiele rzeczy i od rodzaju tej awarii
może zależeć np. rodzaj mechanika, do którego się udamy. Np awaria silnika czy
uszkodzenie lakieru
class AwariaSilnikaException extends SamochodException {...}
class UszkodzenieLakieruException extends SamochodException {...}
Z kolei rodzajem awarii silnika może być problem z zapłonem silnika. Z kolei
rodzajem awarii zapłonu może być zniszczona świeca itd. Przykładem oddającym sens takiego hierarchicznego układania wyjątków zostaje dostrzeżony gdy
pojawiłby się wyjątek Nieudało Się Uruchomić Samochodu Exception. Gdyby
był to jeden z wielu wyjątków typu Exception to od razu udawalibyśmy się do
mechanika. Tymczasem przy pojawieniu się tego wyjątku każdy by po prostu
spróbował ponownie uruchomić silnik, i dopiero po wielu nieudanych próbach
udałby się do mechanika z problemem.
4.4.2
Komendy obsługi błędów
W Javie występuje kilka komend pozwalających nam na obsługę błędów.
throw jest komendą służącą do uruchomienia sytuacji wyjątkowej określonego
typu.
Samochod s = new Samochod();
s.odpalSilnik();
if (s.czySilnikWylaczony) {
throw new NieUruchomionoSilnikaException("Silnik nie pracuje "+
+"pomimo, że próbowano go uruchomić");
}
W chwili wykonania komendy ’throw’ przerywana jest dotychczasowa praca i zgłaszany jest problem. Problem przerywa działania kolejnych funkcji,
aż do odnalezienia obszaru, który rozwiązuje jego problem. Jeśli takiego
nie znajdzie, program przerywa całkowicie swoje działanie.
try jest komendą tworzącą blok obejmującą ’wadliwy kod’. Nie występuje samotnie. Zawsze musi być przy niej co najmniej jeden blok obsługi, lub
blok zamykający.
catch jest komendą tworzącą blok obsługi. Musi być poprzedzone blokiem obejmującym lub innym blokiem obsługi.
Samochod s = new Samochod();
try {
s.uruchomSilnik();
} catch(KluczykNiePasujeDoStacyjkiException ex){...}
catch(BrakPaliwaException ex){...}
catch(AwariaRozrusznikaException ex{...}
catch(SamochodException ex){...}
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
31
Jeśli występuje wiele bloków obsługi powinny być one poukładane w kolejności od najbardziej szczegółowych do najbardziej ogólnych. Wykonany
zostanie bowiem pierwszy blok do którego pasuje rozpatrzony problem.
Jeśli napisalibyśmy kod
Samochod s = new Samochod();
try {
s.uruchomSilnik();
} catch(SamochodException ex){...}
catch(KluczykNiePasujeDoStacyjkiException ex){...}
catch(BrakPaliwaException ex){...}
catch(AwariaRozrusznikaException ex{...}
To zawsze wykonywałby się jedynie blok dla SamochodException, gdyż
np. KlyczykNiePasujeDoStacyjkiException jest podtypem SamochodException.
finally jest komendą tworzącą blok zamykający. Jest to fragment kodu, który
wykonuje się zawsze, nie zależnie od tego czy ’wadliwy kod’ spowodował
błąd czy też nie. Stosujemy go głównie do tego by przed zgłoszeniem wyjątku uprzątnąć pewne rzeczy. Blok występuje po ostatnim bloku obsługi,
lub bezpośrednio po bloku obejmującym
Samochod s = new Samochod();
try {
s.uruchomSilnik();
}
catch(KluczykNiePasujeDoStacyjkiException ex){...}
catch(BrakPaliwaException ex){...}
catch(AwariaRozrusznikaException ex{...}
catch(SamochodException ex){...}
finally {
s.zaciagnijHamulec();
s.wysiadź();
s.zamknijSamochod();
}
throws W przypadku kiedy dany błąd może wystąpić w funkcji dołącza się do
niej informacje, o tym że dany wyjątek może być powodowany.
void uruchomSilnik() throws KluczykNiePasujeDoStacyjkiException, BrakPaliwaException,
Informacja ta występuje wtedy, gdy z danym błędem nie radzi sobie (np.
ponieważ nie powinna tego robić) w jej wnętrzu funkcja.
class Elektryk {
void napraw(Samochod s) throws AwariaSilnikaException{
try {
s.uruchomAuto();
} catch(PadniętyAkumulatorException ex){
ROZDZIAŁ 4. PODSTAWY JĘZYKA JAVA
32
wymień(s,new Akumulator());
}
}
} //class Elektryk
Widać, że o ile elektryk umie naprawić Padnięty Akumulator o tyle nie
poradzi sobie z uszkodzony silnikiem i pracę nad jego naprawą trzeba
przekazać komuś innemu.
Na tym kończymy omawianie wyjątków w Javie. Zauważamy jeszcze jedną
rzecz. Wszystkie klasy błędów, zwykło się zakończać słowem Exception. Poprawia to czytelność kodu i upewnia nas, że dany obiekt jest rodzajem wyjątku.
4.5
Inne reguły w języka Java
W tej części wymienimy inne ważne elementy dotyczące podstaw języka Java
4.5.1
Nazewnictwo w Javie
Wiemy już, że nazwy własne w Javie mogą być napisane z użyciem dużych jak
i małych liter. Przyjmuje się następujące umowy:
ˆ Wszystkie nazwy zmiennych zaczynają się małą literą.
Rozdział 5
Tworzenie modeli w Javie
Po poznaniu podstawy programowania w Java, następnym krokiem jest zrozumienia modelowania obiektowego. Czym zajmuje się modelowanie obiektowe?
Otóż, jak widać w świecie rzeczywistym, występują różne twory różnych typów
np. psy, koty, ludzie, kamienie, itd. Twory te posiadają pewne cechy, które należą
do 1 z 3 grup: charakterystyk, umiejętności i relacji. Przykładem charakterystyki
jest waga człowieka. Każdy człowiek ma jakąś wagę, która jest dokładnie niemu
przypisana. Przykładem umiejętności np. chodzenie. Przykładem relacji jest dla
człowieka bycie ssakiem i posiadanie cech ssakowi przypisanym. Modelowanie
stara się rozpoznać te 3 grupy cech i oddać zasadę ich pracy za pomocą pól,
funkcji oraz dziedziczeń. Sprowadza się to do utworzenia nowe rodzaju danych,
które mają przybliżyć złożoność konstrukcji danego tworu. Wszelkie abstrakty
istniejące w świecie są oddawane za pomocą typu danych jakim jest klasa, natomiast twory pasujące do danego abstraktu są oddawane za pomocą obiektu
pewnej klasy.
Mówiąc na przykładach, jeśli chcielibyśmy w programie stworzyć Reksia,
potrzebujemy najpierw opisać abstrakt psa:
ˆ Jakie są jego cechy? (Jak się wabi, kiedy się urodził, kto jest jego opiekunem, czy był szczepiony)
ˆ Jakie są jego umiejętności? (Czy aportuje, je, chodzi, szczeka)
ˆ Gdzie znajduje się jego miejsce w hierarchii (Pies - Psowate - Ssaki Zwierzęta)
. A w dalszej kolejności należy podać cechy jakie posiada Reksio w kwestii bycia
psem.
5.1
Tworzenie nowej klasy
Dowiedzieliśmy się już, że celem zamodelowania nowego obiektu należy utworzyć
nową klasę. Do tej pory używaliśmy do tworzenia klasy konstrukcji statycznej
która pozwalała nam tworzyć klasę w klasie. Po prawdzie używamy metody,
której sensu i działania jeszcze nie rozumiemy i która jest daleka od tej najprostszej. Otóż, nową klasę tworzymy w nowym pliku. Klikając PPM na paczce
oprogramowania w oknie widoku projektu i wybierając nową ”java class”. Tam
33
ROZDZIAŁ 5. TWORZENIE MODELI W JAVIE
34
podajemy nazwę klasy i klasa zostaje automatycznie wygenerowana i pojawia
się w widoku projektu. Dla ustalenia uwagi przyjmijmy, że tworzymy nową klasę
procesor
package modelowanie;
/**
*
* @author Piotr Kowalski
*/
public class Procesor {
}
W tak stworzonym pliku możemy zamodelować dowolny istniejący obiekt. Z
elementów które znamy wymienić i przypomnieć warto:
pola oddające charakter cechy lub składowej elementu. np taktowanie procesora jako int, nazwa procesora jako String czy podobne
pola statyczne pola dotyczące procesorów ogólnie, jak np data wypuszczenia
procesora na rynek
metody funkcje, która dany obiekt potrafi wykonywać. Np. każdy procesor
umie dodawać dwie liczby
int add(int a,int b)
metody statyczne funkcje, do użycia których nie potrzebny jest obiekt procesora np. przeliczyć taktowanie na ilość operacji na sekundę.
Teraz powiemy sobie o kolejnych możliwościach:
5.2
Dziedziczenie i polimorfizm
Okazuje się, że dziedziczenie jest już czymś co zdążyliśmy po części pokazać
przy okazji pokazania obsługi błędów. Powiedzmy sobie teraz jeszcze raz i ogólniej. W przyrodzie istnieją w sposób naturalny związki pomiędzy obiektami
różnych rodzajów. Można się doszukiwać w tym użycia matematycznej zasady
abstrakcji, lub zwykłego nazywania podobieństw. I tak człowiek jest ssakiem,
ssak zwierzęciem, zwierzę istotą żywą. Oznacza to że człowiekowi przypisane
są wszystkie cechy zarówno ssaka co i zwierzęcia co i istoty żywej. Podobnie
wszystko co potrafi abstrakcyjne zwierzę, potrafi i człowiek (np. jeść czy pić).
Języki programowania starają się oddać naturę tego związku.
Chcąc napisać że dany typ (czyli klasa) dziedziczy inny typ piszemy
class A extends B {}
Oznacza to, że wszystko co cechuje klasę B wyląduje w klasie A. Okazuje się jednak, że dziedziczenie w ten sposób może być wykonane tylko jeden raz. Ponadto
pisząc
class A {}
ROZDZIAŁ 5. TWORZENIE MODELI W JAVIE
35
tak naprawdę piszemy
class A extends Object {}
Czyli wszystkie klasy (prócz Object) w którymś momencie dziedziczą klasę Object, nadrzędną wszystkich klas. W efekcie okazuje się, że klasy tworzą strukturę
drzewa skierowanego z Objectem jako korzeniem.
Możliwość dziedziczenia jedynie po jednej klasie sprawia nam duży kłopot
przy tworzeniu ciekawszych typów. Często przywoływanym jest przykład amfibii, która jednocześnie jest samochodem co statkiem. Jako że nie może odziedziczyć jednocześnie po statku i samochodzie to oznacza, że należy wykonać modelowanie w inny sposób. Twórcy języka Java, chcieli nas w ten sposób ustrzec
przed problemem podwójnego odziedziczenia pewnych rzeczy. I samochód i statek dziedziczą prawdopodobnie pojazd. Czy w takim układzie amfibia powinna
mieć te pola pochodzące od pojazdu podwójnie? Czy jeśli będą pojedynczo to
funkcje samochodu i amfibii będą dalej dobrze działać? Jak w takim układzie
uzyskiwać do nich dostęp? Skąd programista ma wiedzieć czy taki konflikt się
pojawi?
Okazuje się Java pozwala na dziedziczeniu wielu klas jednak jedynie na specjalnych zasadach. Okazuje się, że jedynie specjalna część klas może zostać odziedziczona dodatkowo. Klasy te nazywane są interfejsami.
5.2.1
Interfejsy
Interfejsy stanowią specjalną podrodzinę klas. Nałożone są na nie specjalne i
duże ograniczenia, które w efekcie pozwalają uniknąć problemów wielokrotnego
dziedziczenia. Definiuje się również w specjalny sposób. Przy tworzeniu nowego
interfejsu w Netbeans zamiast new Java Class wybieramy new Java Interface.
/**
*
* @author Piotr Kowalski
*/
public interface Interfejs {
}
Omówmy cechy, które posiadają interfejsy
ˆ Interfejs może dziedziczyć tylko inny interfejs, niewolno nam jednak w tej
implementacji się zapętlić.
ˆ Wszystkie pola w interfejsach są traktowane jako stałe i statyczne.
ˆ Wszystkie metody muszą być poprzedzone przez modyfikator ’abstract’ i
nie posiadać wnętrza (sama definicja)
Aby użyć interfejsu w tworzeniu nowej klasy wykonujemy dziedziczenie innym
słowem kluczowym
class A implements InterfejsB {}
Często w takim układzie nazywa się te dwa typy dziedziczenia rozszerzaniem
i implementacją. Aby lepiej zrozumieć zasadę działania interfejsów przytoczmy
przykład. Przyjmijmy, że
ROZDZIAŁ 5. TWORZENIE MODELI W JAVIE
36
class Syn extends Ojciec{}
czyli Ojciec przekazał synowi całą swoją wiedzę i umiejętności. Nikt nie odbierze
ojcu jego prawa ojcowskiego. Jedna Syn mógł otrzymać również umiejętności
pochodzące skądinąd. Np. jazdy na rowerze nauczył go wujek. I tak
class Syn extends Ojciec implements Wujek,Szkoła,Studia {}
Syn otrzymuje dodatkowe umiejętności. Przykład choć nie oddaje klasycznie
charakteru dziedziczenia, wprawnie oddaje rolę interfejsu. Chodzi w skrócie o
to by przekazać informację o tym, że pewne obiekty potrafią wykonywać określone czynności. Weźmy np. umiejętność grania. Na czym można grać. Np na
instrumentach muzycznych. Ale grać można również na komputerze czy czasami na czyiś nerwach. Instrument i komputer dziedziczą co prawda po wspólnej
klasie przedmiot, jednakże nie na każdym przedmiocie można grać.
interface IMoznaGracNa {
abstract public void grajNa();
}
class InstrumentMuzyczny implements IMoznaGracNa {
@Override
public void grajNa(){}
}
class Komputer implements IMoznaGracNa {
@Override
public void grajNa(){}
}
W efekcie czego i klasa InstrumentMuzyczny i Komputer pozwalają na granie.
Całkiem nową zalecą modą jest rozpoczynanie nazwy interfejsu od wielkiej litery
I. Moda ta pochodzi z języka C# ale zdaje się mieć coraz więcej popleczników.
Pozostaje zatem wyjaśnić czym jest ’abstract’ i ’@Override’.
5.2.2
Modyfikator ’abstract’ i adnotacja ’@Override’
Słowo kluczowe ’abstract’ jest narzędziem do realizacji pojęcia tzw. polimorfizmu czyli wielu postaci kodu. Jaka jest ogólna idea ukryta za tajemniczym polimorfizmem. Wyobraźmy sobie, że mamy gromadę różnych zwierzątek. Wszystkie te zwierzątka doskonale rozumieją polecenie ’idź tam’. Zauważmy jednak,
że ’idź tam’ papugi, węża, psa i małpki znacząco się będzie różnić. Pomimo,
że to ’idź tam’ jest jednakowe w użyciu to każde z zwierzątek wykona je na
swój typowy sposób, papuga poleci, wąż popełźnie, pies poczłapie na czterech
łapkach, a małpka na dwóch.
Tłumacząc na to na języki programowania. Wszystkie klasy Papuga, Wąż,
Pies, Małpa posiadają metodę ’idź tam’. Jednak każde z tych metod jest wykonywana przez ciut inny ciąg instrukcji czyli jest innym kodem. Skoro instrukcja
’idź tam’ używa się tak samo to jest jedna jednak wykonuje w wielu postaciach
(poli-morfizm wiele postaci).
Cel c = new Cel();
Pies pies = new Pies();
Papuga papuga =new Papuga();
ROZDZIAŁ 5. TWORZENIE MODELI W JAVIE
37
Waz waz = new Waz();
Malpa malpa = new Malpa();
pies.idzTam(c);
papuga.idzTam(c);
waz.idzTam(c);
malpa.idzTam(c);
Czyli widać, że pod jedną nazwą może ukrywać się wiele różnych działań. Prawdziwa moc tej konstrukcji, ujawnia się dopiero gdy użyjemy rzutowań. Obiekty
bowiem same pamiętają na czym polega ich funkcja
...
Zwierzeta[] gromadaZwierzat = new Zwierzeta[]{pies,papuga,waz,malpa};
for (int i =0; i< gromadaZwierzat.length;i++){
gromadaZwierzat[i].idzTam(c);
}
Do realizacji tego wykorzystujemy właśnie duet słowa kluczowe ’abstract’ i
adnotację ’@Override’. Aby zrealizować powyższe wystarczy, że klasa Zwierzeta
zasygnalizuje, że ona i jej klasy potomne będzie używać takiej funkcji jak idzTam
abstract class Zwierzeta {
abstract void idzTam(Cel c);
}
class Pies extends Zwierzeta{
@Override
void idzTam(Cel c){/*idzie na 4 łapach*/};
}
class Papuga extends Zwierzeta{
@Override
void idzTam(Cel c){/*leci*/};
}
class Waz extends Zwierzeta{
@Override
void idzTam(Cel c){/*pelza*/};
}
class Malpa extends Zwierzeta{
@Override
void idzTam(Cel c){/*idzie na 2 nogach*/};
}
Dodatkowo klasa zwykła, która posiada co najmniej jedną metodę abstrakcyjną musi być poprzedzona słowem abstract. Klasę taką nazywa się abstrakcyjną (chyba że mamy do czynienia z interfejsem, który zawsze jest abstrakcyjny)
i nie można utworzyć obiektu takiej klasy.
5.3
Modyfikatory dostępu
Poznaliśmy już słowo kluczowe ’abstract’ które zmodyfikowało funkcje na funkcję polimorficzną. Poznamy jeszcze tzw. modyfikatory dostępu.
ROZDZIAŁ 5. TWORZENIE MODELI W JAVIE
38
Jaka jest idea zasięgu dostępu. Domyślnie jeśli klasa posiada jakąś funkcję
czy zmienną bez modyfikacji to w każdym miejscu w danym pakiecie oprogramowania można uzyskać do niej dostęp. Nazywane jest to dostępem pakietowym.
A ten może być zmodyfikowany przez jeden z 3 poniższych modyfikatorów
public - dostęp publiczny - poprzedzając klasę, funkcję czy zmienną czyni ją
widzialną w dowolnym oprogramowaniu Javy, która uzyskuje do niego
dostęp
private - dostęp prywatny - poprzedzając klasę, funkcję czy zmienna czyni ją
niewidzialną dla dowolnego spoza danego pliku. Służy do ukrywania tego
co nie powinno być zobaczone przez nikogo
protected - dostęp chroniony - działa tak jak private z tą jedyną różnicą, że
wszystkie klasy dziedziczące daną również mogą uzyskać do niej dostęp.
Poznanie wszelkich wyników kombinacji poszczególnych modyfikatorów zajmuje dużo czasu. Dlatego w tym miejscu wymieńmy jedynie kilka reguł
ˆ Wszystkie pola w klasie powinny być protected, a w szczególnych przypadkach private.
ˆ Klasy są z reguły prywatne i potrzeba klas prywatnych i chronionych zdarza się w praktyce niezwykle rzadko. Klasy takie są klasami wewnętrznymi
ˆ Można stworzyć klasę nieabstrakcyjną, której obiektów nie będzie można tworzyć. Wystarczy przy wszystkich konstruktorach dać modyfikator
protected lub private
ˆ Metody podane w interfejsach są z reguły publiczne. Nigdy prywatne.
Niemal nigdy chronione.
ˆ Klasa, która może być odziedziczona powinna mieć co najmniej jeden konstruktor co najmniej chroniony
Modyfikatory dostępu bez problemu łączą się ze słowem kluczowym ’static’.
5.3.1
Modyfikator ’final’
Z pewnych względów może nam zależeć aby pewne fragmenty kodu były niezmienne niezależnie od tego co się z nimi potem ma dziać. W języku Java do
wymuszania niezmienności używane jest specjalne( i po raz pierwszy pojawiające się właśnie w tym języku) słowo final. W zależności od tego co modyfikujemy
użyciem tego słowa kluczowego otrzymamy różne efekty.
Zmienne finalne oraz zmienne statyczne finalne
Słowo final jest często (nie do końca słusznie) traktowane jako nowy sposób
tworzenia stałych na zasadach modyfikatora const z języka C++. O ile w przypadku typów prymitywnych działanie to ma właśnie takie zastosowanie o tyle
w przypadku reszty czyli typów referencyjnych zastosowanie jest zupełnie inne. Operator final w zasadzie wyklucza jedynie możliwość wywołania operatora
przypisania na obiekcie który jest nim zmodyfikowany. Oznacza to że posiadając
finalną referencje do tablicy, jej elementy nie są chronione przed jakimikolwiek
ROZDZIAŁ 5. TWORZENIE MODELI W JAVIE
39
zmianami, a jedynie uniemożliwione jest wstawienie innego obiektu danego typu pod tą samą referencję. Identyczna zasada dotyczy się zmiennych, które są
dodatkowe modyfikowane poprzez słowo kluczowe ’static’.
Puste zmienne finalne
Możliwe jest tworzenie zmiennych finalnych bez natychmiastowego określania
ich wartości. Zmienne te oczekiwać będą na ustawienie na wartość początkową w późniejszym fragmencie kodu i dopiero tą przypisaną wartość/referencję
potraktują jako niezmienną.
Finalne argumenty
Jeśli argument w funkcji jest podany jako finalny, to podobnie jak przy definicji zmiennej, nie jest możliwe wywołanie na nim operatora przypisania. W
zakresie działania funkcji wartość nie może być zmieniona, natomiast referencja
przestawiona na inny obiekt.
Metody finalne
W związku z wymaganiami projektowymi, możliwe jest tworzenie metod (funkcji) będących finalnymi. Oznacza to, że żadna z klas dziedziczących tę metodę
nie może zmienić jej kodu. Wyobraźmy sobie, że napisaliśmy funkcję czyHasłoPrawdziwe, która sprawdza czy użytkownik podał prawdziwe hasło. Jeśli metoda
ta nie byłaby finalna, inny użytkownik mógł odziedziczyć naszą klasę i napisać
nową funkcję np. taką jak poniżej
@Override
boolean czyHasloPrawdziwe(Uzytkownicy user, String hasło){
return true;
}
W który to sposób uzyskał by dostęp do zasobów bez podania poprawnego
hasła.
Ponadto funkcje, które wykonują się bardzo krótko (z reguły jednolinijkowe)
oznacza się jako finalne, aby przyśpieszyć ich wykonywanie.
Warto też odnotować, że wszystkie funkcje prywatne są również finalne. Nie
przynosi żadnego efektu łączenie tych dwóch słów kluczowych.
Klasy finalne
W odniesieniu do problemu, który motywuje istnienie metod finalnych, można
pójść krok dalej i zabronić nie nadpisania funkcji, ale w jej dziedziczenia w ogóle.
Klasy finalne nie mogą zostać odziedziczone.
5.4
Słowo kluczowe ’this’ oraz o konstruktorach
W końcu działu o tworzeniu modeli w języku Java opowiemy sobie o metodach konstruowania obiektów. Zacznijmy jednak od tajemniczej referencji this.
Słowa kluczowego this możemy używać w dowolnej niestatycznej metodzie aby
ROZDZIAŁ 5. TWORZENIE MODELI W JAVIE
40
odwoływać się do obiektu na rzecz którego wywoływana jest funkcja. Jest ona
przydatna, głównie po to by nie mylić nazw.
Do rzeczy o konstruktorach. Obiekty z punktu widzenia komputera są tylko
obszarami w pamięci. Mogą być zatem zajmowane i zwalniane. Okres od zajęcia
do zwolnienia nazywa się czasem istnienia obiektu. Obiekty są zwalniane automatycznie przez jvm, gdy program zgubi wszystkie referencje do niej. Tworzenie
ich jednak występuje jedynie na życzenie programisty.
Konstruktor ze swojej pisowni przypomina zwykłą funkcję, jednak różnice
w jego pisowni są dość znaczne.
ˆ Konstruktor nie zwraca żadnego typu. Nawet typu ’void’
ˆ Nazywa się tak jak klasa którą ma tworzyć. Jest to zatem jedyna funkcja
pisana z dużej litery.
ˆ Nie używamy ’return’ w jego wnętrzu.
public NowaKlasa(){
}
Java w odróżnieniu od C++ pozwala nam używać innych konstruktorów.
Spełnione muszą być jednak pewne istotne warunki:
ˆ Użycie konstruktora musi wystąpić w pierwszej linijce kodu
ˆ nie wolno wywoływać w tej linijce żadnych funkcji
public NowaKlasa(Strins s){
this.nazwa = s;
}
public NowaKlasa(){
this("domyslna"); // wywolanie innego konstruktora
}
Musi to być pierwsza linijka kodu z uwagi na to jak w Javie tworzone są
obiekty
1. Tworzone są pola i metody nadklasy po której dziedziczymy
2. Tworzone są pola i metody klasy
3. Rozpoczyna się kod konstruktora
Dwie pierwsze czynności są robione automatycznie. Więc gdy chcemy użyć innego konstruktora, musimy wyraźnie od razu napisać by to tamten konstruktor
zajął się generowaniem nadklasy oraz pól i metod. Możemy wywołać dowolny
konstruktor tej klasy używając funkcji ’this(parametry konstruktora)’ lub też
wykorzystać konstruktor nadklasy pisząc ’super(parametry konstruktora)’.
By stworzyć nowy obiekt należy nazwę i parametry konstruktora poprzedzić
słowem kluczowym ’new’, która zainicjuje proces tworzenia referencji.
Rozdział 6
Korzystanie z pakietów
podstawowych języka Java
6.1
Kontenery
Kontenerami w Javie nazywamy klasy i interfejsy pomagające programiście
w gromadzeniu danych w dynamicznych prostych strukturach danych. Są one
znacznie częściej stosowane z uwagi na możliwość zmieniającego się ich rozmiaru. W tej części omówimy stosowanie najprostszych z tych konstruktów. Do
najprostszych zastosowań używać będziemy niemal zawsze albo kolekcji albo
tablic asocjacyjnych. Ogólnie omawiając różnicę pomiędzy tymi dwoma typami
polega ona tym, że kolekcje grupują elementy, a tablice asocjacyjne ich pary.
Przykładami kolekcji są ciągi, zbiory a relacje i grafy przykładami tablic asocjacyjnych. W przypadku programowania dochodzi do tego jeszcze mnóstwo
aspektów związanych z wydajnością w czasie dostępu co oszczędności pamięci.
6.1.1
Kolekcje
Wśród podstawowych kolekcji oferowanych przez Javę należy wyróżnić stosy,
listy, kolejki oraz drzewa. Stos jest zdecydowanie najprostszą ze struktur dynamicznych, posiada jedynie 5 prostych operacji
Push umieszcza nowy element na stosie
Pop pobiera element z wierzchołka
Peek odczytuje wartość z wierzchołka bez jej zdejmowania
Empty sprawdzające czy stos jest pusty
Search poszukujące w stosie danego elementu i zwracające ”głębokość” jego
położenia na stosie. Warto dodać, że poszukiwanie w stosie jest operacją
mało wydajną.
Aby użyć stosu w Javie, załóżmy że posiadamy następują klasę:
class A {
public A(String n){
41
ROZDZIAŁ 6. KORZYSTANIE Z PAKIETÓW PODSTAWOWYCH JĘZYKA JAVA42
this.name=n;
}
@Override
public String toString() {
return "A{" + "name=" + name + ’}’;
}
private String name;
}
Wtedy
public static void main(String[] args) {
Stack<A> stos = new Stack<A>();
A a = new A("A"),
b = new A("B");
stos.push(a);
stos.push(b);
System.out.println("Peek
"+stos.peek());
System.out.println("Search a
"+stos.search(b));
System.out.println("Search b
"+stos.search(a));
System.out.println("Pop once
"+stos.pop());
System.out.println("Czy pusty "+(stos.empty()?"TAK":"NIE"));
System.out.println("Pop second "+stos.pop());
System.out.println("Czy pusty "+(stos.empty()?"TAK":"NIE"));
}
Spowoduje wyprodukowanie następującego wyniku
Peek
Search a
Search b
Pop once
Czy pusty
Pop second
Czy pusty
A{name=B}
1
2
A{name=B}
NIE
A{name=A}
TAK
Stos podobnie jak inne kolekcje jest tzw. typem ogólnym. Charakterystyczne
dla typów ogólnych jest to, że należy podać nazwę klasy w nawiasie ¡¿.
Drugim typem danych jest kolejka.
6.1.2
Iteratory
6.1.3
Tablice asocjacyjne
6.1.4
Przeszukiwanie i sortowanie kontenerów
Rozdział 7
Tworzenie aplikacji z
interfejsem graficznym
Swing
Język Java pozwala na tworzenie prostych aplikacji okienkowych. Najczęstszym
pakietem umożliwiającym tworzenie prostych widoków, jest tzw. Swing. Platforma Netbeans posiada wbudowany edytor graficzny, pozwalający na łatwe i przyjemne tworzenie klas pakietu Swing i dostosowywanie ich do potrzeb działania
aplikacji. Jest to tak zwany budowniczy interfejsu użytkownika (ang. Graphical
User Interface Builder lub gui builder). W tym rozdziale omówimy podstawy
związane z tworzeniem aplikacji wielo-okienkowej.
7.1
Elementy pakietu Swing
Aby rozpocząć pracę z budowniczym okienek, najlepiej utworzyć sobie nowy
projekt. Do nowego projektu dodajmy sobie klasę JFrame Form z kategorii
Swing Gui Forms. Ta klasa odpowiada nam za okno programu. Po utworzeniu
nowej klasy i nazwaniu jej dla przykładu: OknoGlowne.java zostaniemy powitani
przez widok okna budowniczego.
Pierwszym najważniejszym miejscem jakie powinniśmy zauważyć, jest przełącznik Source-Design. Okno Swing jest zwykłą klasą Javy, a zatem Budowniczy
Okien jest programem generującym kod Javy w oparciu o nasze wytyczne. W
czasie gdy włączony jest przełącznik Design, działa Budowniczy okien i on zarządza generowaniem kodu dla okna. Przełączając się na Source widzimy bezpośrednio wygenerowany kod Java. Z uwagi na to, że Budowniczy Okien wygenerował sporą część klasy i gdybyśmy zaczęli ją zmieniać to Budowniczy utraciłby
zdolność dalszego kontrolowania klasy, część linijek jest wyszarzana i nie można ich edytować. Oznacza to, że pogląd źródeł ma na celu raczej możliwość
dodawania własnych rzeczy do klasy, niż też zmieniania tych generowanych.
Drugim ważnym miejscem Budowniczego Okien jest pasek podręcznych rzeczy pojawiający się z reguły po prawej stronie. Zawiera on wszystkie ważne komponenty Swing, potrzebne do konstruowania nawet dość skomplikowanych okien.
Szczegółowo do działania poszczególnych komponentów dojdziemy za chwilę.
43
ROZDZIAŁ 7. TWORZENIE APLIKACJI Z INTERFEJSEM GRAFICZNYM SWING44
7.1.1
Uruchamianie okna głównego aplikacji
W chwili utworzenia okna JFrame tworzona jest również do niego metoda mająca na celu wyświetlenie go użytkownikowi. Nie jest to już proces tak prosty jak
wcześniejsze, wymaga bowiem pracy na tzw. wątkach. Oznacza to, że możliwe
jest posiada kilkunastu okien i każde z nich będzie pracować zupełnie niezależnie od pozostałych. Sprawia jednak, że komunikowanie się okien staje się trochę
uciążliwe.
Nowo utworzony projekt standardowo posiada klasę wyposażoną w funkcję main, która służy do uruchamiania całości projektu. Nowo-tworzone okna
również wyposażone są w taką metodę, a zatem aby okno uruchamiało wraz
ze startem projektu funkcja main powinna zostać usunięta z klasy okna, a jej
wnętrze wklejone do głównej metody main. Powinno to wyglądać mniej więcej
w taki sposób:
public class Okna {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new OknoGlowne().setVisible(true);
}
});}
}
Jest to niebanalna konstrukcja mająca następujące działanie.
1. Fragment
new Runnable() {
@Override
public void run() {
new OknoGlowne().setVisible(true);
}
tworzy specjalną klasę, której zadaniem jest uruchomienie okna. Takie klasy są nazywane z reguły Runnerami. Automatycznie wygenerowany kod
nie oddaje z pełni istoty uruchamiaczy okien, ale do ich tematu powrócimy jeszcze przy tworzeniu pod-okienek. Tworzona jest tutaj tzw. klasa
anonimowa tzn. że nie posiada ona swojej nazwy ani konstruktora. Aby
stworzyć taką klasę należy wskazać klasę po której dziedziczy oraz uzupełnić resztę jej wnętrza. W tym wypadku klasa anonimowa pochodzi od
interfejsu wszystkich uruchamiaczy czyli Runnable. Interfejs ten posiada
jedną jedyną metodę run czyli uruchom. Zadaniem naszej uruchamiacza
jest stworzenie nowego okna ( new OknoGlowne()) oraz wyświetlenia go
(.setVisible(true));
2. Fragment
ROZDZIAŁ 7. TWORZENIE APLIKACJI Z INTERFEJSEM GRAFICZNYM SWING45
java.awt.EventQueue.invokeLater( \cdot );
odpowiada natomiast za tworzenie nowych wątków. Domyślnie oznacza
on ”Przekaż uruchamiacz do uruchomienia w nowym wątku w najbliższej
wolnej chwili”. Parametr, który otrzymuje funkcja invokeLater musi być
typu Runnable.
W powyższy sposób można tworzyć dowolną ilość okien. Nie jest jednak
banalnym aby nauczyć tak utworzone okna komunikować się ze sobą. Jak to
zrobić umówimy w sekcji tworzenia nowych okienek.
7.1.2
Swing - menu
Jednym z pierwszych rzeczy jakie zapragniemy dodać do naszego okna jest główny pasek menu. Rzeczy związane z jego tworzeniem znajdziemy przy działającym Budowniczym Okien po prawej stronie w oknie palety. Należy odszukać
Pasek Menu czyli Menu Bar. Każde okno może posiadać jeden pasek menu przypięty do siebie. Domyślnie pasek ten posiada dwa elementu rozwijalne. Plik/File
oraz Edycja/Edit. Można je wykorzystać do swoich potrzeb, zmienić lub usunąć. Aby dodać pasek menu należy przeciągnąć go z palety do lewego górnego
narożnika okna w którym chcemy go umieścić.
Do paska menu, można dodawać wyłącznie elementy typu menu. Analogicznie jak poprzednio polega to na przesunięciu ich z palety i upuszczeniu na Pasku
Menu.
Do danego menu, można dodawać inne menu, elementy menu (menu item),
elementy będące polami do zaznaczenia (menu checkbox), elementy będące polami wyboru jednokrotnego (menu radiobox) lub separatorami czyli liniami rozdzielającymi.
Każdy z tych elementem kliknięty PPM w Budowniczym Okien pozwala na
edytowanie wielu posiadanych przez siebie właściwości. Pierwszą nam niezbędną
jest zmiana nazwy menu lub elementu z menu. Inna, ciut ważniejszą gdy już
zaczniemy dopisać rzeczy do kodu wygenerowanego, będzie zmiana nazwy danej
zmiennej.
W dalszej części zajmiemy się jednak najważniejszymi czynnościami jakie
można przypisać elementom menu, jakimi są opisanie ich wydarzeń.
7.1.3
Swing - kontenery
Obszar we wnętrzu okna można podzielić na obszary, które posłużą nam do
przechowywania w swoim wnętrzu innych elementów Swing.
Zakładki
Najprostszym kontenerem jest kontener zakładek. Aby stworzyć w obszarze
okna zakładki należy przeciągnąć i odpowiednio rozszerzyć obszar zakładek/
Tabbed Pane. Aby utworzyć zakładkę należy do niego przeciągnąć Panel. Każda z zakładek może posiadać swoją unikatową nazwę. Zakładki są niezwykle
skuteczną metodą radzenia sobie z nadmiarem okien. Pisanie nowych okien wymaga tworzenia ich runnerów oraz opisywania dokładnej komunikacji między
nimi. Tymczasem gdy kilka okien połączyć w jedno okno z zakładkami, całość
staje znacznie przyjemniejsza w opisywaniu.
ROZDZIAŁ 7. TWORZENIE APLIKACJI Z INTERFEJSEM GRAFICZNYM SWING46
7.1.4
Swing - kontrolki
Tym co naprawdę wypełnia powierzchnie okna i kontenerów są kontrolki. Kontrolki są dla okna tym co elementy menu dla samego menu. To one odpowiadają
za akcje, wprowadzenia i wyprowadzenia tekstów czy wszystkie inne modyfikacje.
7.2
Okienka dialogowe
W Javie występuje szereg bardzo prostych okienek, których użycie pozwala na
przyśpieszenie działania programu. Są to okienka:
1. Potwierdzenia z opcjami tak,nie, cofnij
2. Wprowadzenia, celem podania jakiejś danej
3. Wiadomości, wyświetlenia wiadomości
4. Ogólne okno opcji
7.2.1
Przykładowe użycia okienek dialogowych
Dla przykładu jeśli chcemy wywołać okienko upewniające się czy program powinien się zakończyć:
int n = JOptionPane.showConfirmDialog(this,"Czy zakończyć działanie programu ?" ,
"Wyjść?", JOptionPane.OK_CANCEL_OPTION, JOptionPane.QUESTION_MESSAGE);
if (n == JOptionPane.OK_OPTION)
System.exit(0);
7.3
7.3.1
Wydarzenia
Akcja wykonana
Najczęściej stosowanym wydarzeniem jest wykonanie akcji. Aby opisać co się
dzieje gdy przycisk zostanie naciśnięty, menu item wybrany albo tekst wypełniony. Aby dodać wykonanie jakiś czynności dla danego obiektu należy kliknąć
PPM na nim i wybrać Events -¿ Action Performed. Doprowadzi to do przełączenia do widoku kodu i wnętrza automatycznie wygenerowanej funkcji której
zadaniem jest spełnić wszystkie czynności niezbędne do wykonania całej akcji
wywołanej przy wciśnięciu przycisku.
7.4
Właściwości okna
7.5
Nasłuchiwacze
Dodatek A
Zadania laboratoryjne
Laboratorium 1. Rozwiązywanie równania kwadratowego.
Napisać program służący do rozwiązywania równania kwadratowego. Program powinien działać w interfejsie terminalowym i wczytywać od użytkownika
współczynniki wielomianu stopnia drugiego. Na polecenie użytkownika, powinna
istnieć możliwość wykonania następujących akcji:
ˆ Wprowadzenia nowego wielomianu stopnia 2
ˆ Policzenia i wypisania wyróżnika wielomianu
ˆ Policzenia i wypisania miejsc zerowych
ˆ Wyjścia z programu
Należy utworzyć klasę Równanie Kwadratowe implementującą dwie funkcje
ˆ double[] rozwiaz()
ˆ double wyroznik()
Które odpowiednio wyznaczają wektor rozwiązań oraz wyróżnik równania.
Laboratorium 2. Rozwiązywanie równania trzeciego stopnia.
Rozszerzyć poprzedni program o możliwość rozwiązywania równań trzeciego
stopnia. W tym celu należy zaprojektować odpowiednia hierarchię klas i cech.
Należy utworzyć abstrakcyjną klasę Równanie Wielomianowe, które będzie posiadało swój stopień oraz listę swoich współczynników. Należy stworzyć jeszcze
interfejs Rozwiazywalne Metodą Wyznacznikową posiadający funkcje
double[] rozwiaz()
double wyroznik()
W reszcie należy najpierw poprawić klasę Równanie kwadratowe tak by to
dziedziczyło klasę abstrakcyjną oraz interfejs, oraz napisać klasę równania trzeciego stopnia. Na sam koniec należy przystosować program do możliwości odczytu Równań kwadratowych.
Dodatkowo! Można przystosować program do wczytywania dowolnego wielomianu, a w wypadku próby użycia metody wyznacznikowej dla nieodpowiedniego
typu wielomianu, zgłaszać błąd. Nie można rozwiązać metodą wyznaczników,
47
DODATEK A. ZADANIA LABORATORYJNE
48
lub w wypadku równania 4 rzędu - Nie utworzona funkcjonalność. Można kontynuować rozwój programu o rozwiązywanie równanie liniowego i sprawdzanie
równania bez zmiennych (wielomian stopnia 0 równy zeru).
Laboratorium 3. Figury płaskie.
Napisać program pozwalający na utworzenie różnych figur płaskich
ˆ Koło
ˆ Trójkąt
ˆ Czworokąt
ˆ Prostokąt
ˆ Romb
ˆ Kwadrat
Figury mają posiadać umiejętność obliczenia swojego pola i obwodu. Należy zaprojektować odpowiednia strukturę klas. Ponadto należy utworzyć klasę punktu na płaszczyźnie oraz wektora. Opracować interfejs przesuwalne i umożliwić
wszystkim obiektom przesuwanie się o wektor. Ponadto należy klasy wyposażyć
w odpowiednie implementacje funkcji abstrakcyjnej, która odpowiada na pytanie
czy wskazany punkt należy do danej figury.
Laboratorium 4. Rzut kostką n-ścienną
Napisać program okienkowy w Java Swing pozwalający na wygenerowanie
rzutu dowolną kością n-ścienną. Program powinien korzystać z klasy Kostka,
która posiada w sobie wszystkie elementy losowości, a interfejs graficzny jedynie służy do ich wywołania. Aplikacja powinna zawierać w sobie pasek menu,
umożliwiający jej zamknięcie, przycisk losowania, odpowiednie etykiety oraz pole
tekstowe do podania przez użytkownika ilości ścian. Dane podawane przez użytkownika powinny być zabezpieczone przed podaniem danej złego formatu (innego
niż liczba całkowita) oraz spoza zakresu ( zero lub mniej).
Laboratorium 5. Stos
Napisać własną obsługę stosu i wyposażyć tak zdefiniowana klasę w odpowiednie funkcje jak
Push włóż na stos
Pop pobierz z wierzchołka stosu
Top odczytaj wartość z wierzchołka stosu, bez jej zdejmowania
isEmpty czy stos jest pusty
Contains czy stos zawiera element o danym kluczu
Remove usuń element o wskazanym kluczu ze stosu
Laboratorium 6. Bank - 2 zajęcia
Napisać aplikację okienkową polegającą na zarządzaniu kontami bankowymi.
Aplikacja ma pozwalać na
DODATEK A. ZADANIA LABORATORYJNE
ˆ tworzenie użytkowników
ˆ zamykanie kont użytkowników
ˆ realizowanie wpłat i wypłat
ˆ realizowanie przelewów pomiędzy kontami użytkowników
ˆ dane powinny być zapisywane w plikach tekstowych w języku xml
Obowiązuje zasada 1 użytkownik = 1 konto.
49
Dodatek B
Zadania projektowe
Projekt 1. Napisać program obliczający liczbę π jako granicę ciągu. Ciąg może
być dowolnie wybrany z tych które w granicy posiadają liczbę π. Np. wiadomo,
że
∞
X
1
π2
=
(B.1)
2
6
i
i=0
Powyższe można przekształcić do utworzenia ciągu zbieżnego do liczby. Do stworzenia w ramach zadania jest klasa Ciąg Liczbowy posiadająca funkcję zwracającą wartość ciągu dla podanego miejsca w ciągu. Następnie w ciele funkcji main
napisać algorytm obliczający wartość liczby pi jako granicy ciągu, przy ustalonym parametrze dokładności.
Każdy projekt musi wykorzystywać inną zależność związaną z liczbą π.
Projekt 2. Rozszerzyć zadanie laboratoryjne o jedną z figur płaskich takich jak:
1. Kwadrat
2. Prostokąt
3. Romb
4. Trapez
5. Elipsa
6. Deltoid
7. Sześciokąt
Klasa ma zostać wyposażona w własne implementacje dziedziczonych metod (z
wyłączeniem przesun w szczególnych przypadkach) i posiadać co najmniej 4 metody jej konstruowania (4 różne konstruktory). Dla przykładu dla czworokąta
alternatywną metodą konstruowania jest podanie punktu oraz dwóch wektorów o
różnych kierunkach.
Projekt 3. Napisać bibliotekę realizującą algorytmy sortowania
ˆ bąbelkowego
ˆ przez wstawianie
50
DODATEK B. ZADANIA PROJEKTOWE
51
ˆ przez wybór
Oraz zaprezentować jej działanie z użyciem testów JUnit
Projekt 4. Napisać bibliotekę realizującą algorytmy sortowania
ˆ szybkiego
ˆ przez łączenie
Oraz zaprezentować jej działanie z użyciem testów JUnit
Projekt 5. Napisać program odszukujący minimum funkcji unimodalnej (posiadającej pojedynczy punkt minimum w zadanym przedziale) metodami
ˆ dychotomii
ˆ Fibonacci
Dodatek C
Zadania do samodzielnego
rozwiązania
C.1
Zadanie z prostych algorytmów
Zadanie C.1. Napisz program, którego zadaniem będzie zapytać użytkownika
o imię, a potem ładnie się z nim przywitać.
Zadanie C.2. Napisać program, którego zadaniem jest porównać dwie liczby
rzeczywiste, oraz porównać moduły tych liczb i wypisać, która z liczb pod którym
względem jest większa.
Zadanie C.3. Napisz program, którego zadaniem będzie wczytać linijkę tekstu
z pojedynczą operacją matematyczną +,-,*,/. Program ma policzyć wynik i wypisać go na ekranie. Zamiana wczytanego ciągu znaków na wartość ma odbyć
się z pomocą funkcji (napisanej przez programistę)
double compute(String text){}
Zadanie C.4. Napisać program, którego zadaniem jest sprawdzić czy liczba
podana przez użytkownika jest liczbą pierwszą. Jeśli nie jest, program ma wypisać
jej wszystkie dzielnik właściwe. Do wykonania powyższego należy napisać dwie
funkcje
boolean isPrime(int number){}
int[] findDivisors(int number){}
Zadanie C.5. Napisać program do liczenia liczby silnia oraz symbolu newtona
Zadanie C.6. Napisać implementacje sita Eratostenesa poszukiwania liczb pierwszych
Zadanie C.7. Napisać program do obliczania potęgi liczby naturalnej bez używania operatora mnożenia. Wskazówka. Przypomnieć sobie wzory z szeregów.
C.2
Zadania z modelowania w języku Java
Zadanie C.8. Napisać program, którego zadaniem jest realizacja podstawowych
operacji na liczbach zespolonych. W szczególności należy uzupełnić klasę ComplexNumber o postaci
52
DODATEK C. ZADANIA DO SAMODZIELNEGO ROZWIĄZANIA
53
static class ComplexNumber {
double real;
double imaginary;
/**
* Dodaje dwie liczby zespolone
* @param left lewy operand
* @param right prawy operand
* @return wynik dodawania
*/
static ComplexNumber add(ComplexNumber left, ComplexNumber right) {
return null;
}
/**
* Odejmuje dwie liczby zespolone
* @param left lewy operand
* @param right prawy operand
* @return wynik odejmowania
*/
static ComplexNumber subtract(ComplexNumber left, ComplexNumber right) {
return null;
}
/**
* Mnoży dwie liczby zespolone
* @param left lewy operand
* @param right prawy operand
* @return wynik mnożenia
*/
static ComplexNumber multiply(ComplexNumber left, ComplexNumber right) {
return null;
}
/**
* Dzieli dwie liczby zespolone
* @param left lewy operand
* @param right prawy operand
* @return wynik dzielenia
*/
static ComplexNumber divide(ComplexNumber left, ComplexNumber right) {
return null;
}
/**
* Wypisuje liczbę zespoloną
* @return liczba zespolona w postaci (a+bi)
*/
String print() {
return null;
}
}
DODATEK C. ZADANIA DO SAMODZIELNEGO ROZWIĄZANIA
54
Programista ma zapewnić użytkownikowi swobodę wyboru operacji oraz podania
liczb zespolonych. Program powinien kończyć się po podaniu specjalnej komendy
kończącej.
Wskazówka. Dodawania dwóch liczb będzie miało postać:
ComplexNumber c1,c2;
...
ComplexNumber c3 = ComplexNumber.add(c1,c2);
Zadanie C.9. Napisać program pozwalający na utworzenie wielu różnych pojazdów (samolot, pociąg, samochód, motocykl, rower), które mogą się przemieszczać
pomiędzy punktami A i B na płaszczyźnie. Pojazd posiada jaką swoje prywatne
składowe:
ˆ prędkość
ˆ bieżącą pozycje (klasy punkt)
Pojazdy posiadają funkcję jedź(), która powoduje przesunięcie się o odległość
zgodną z ich prędkością w kierunku ustalonego celu, lub osiągnięcie go jeśli jest
on w zasięgu. Funkcja jedź jest wywoływana cyklicznie i z równą częstością dla
każdego pojazdu umieszczonego na trasie. Na zakończenie cyklu wszystkie pojazdy podają przebytą odległość oraz czy osiągnęły cel.
C.3
Zadania z prostych aplikacji graficznych
Zadanie C.10. Napisać prosty program do przeliczania walut wg podanego kursu
Zadanie C.11. Napisać program do gry w wisielca. Słownik słów powinien być
podany poprzez plik tekstowy, i do gry powinno być losowane dowolne słowo ze
słownika. Gracz może wykonać dwie akcje
ˆ Szukaj spółgłoski
ˆ Zgadnij hasło
Szukanie spółgłoski oraz nieudane zgadywanie hasła ma powodować wyczerpywanie się możliwości do błędu gracza.
Spis rysunków
55
Spis tabel
56
Bibliografia
[1] Bruce Eckel. Thinking in Java. Helion, Gliwice, 4 edition, 2006.
[2] Jerzy Grębosz. Symfonia C++ standard, volume 1. Edition 2000, Kraków,
3 edition, 2008.
57

Podobne dokumenty