View
36
Download
0
Category
Preview:
DESCRIPTION
SWING. JAVADOC. UML-KAAVIOT. SÄIKEET JA SWING. 2D-GRAFIIKKA. SOVELMAT. TIETOVIRRAT. JAVA-API. PAKKAUKSET. POLYMORFISMI. SILMUKAT. MERKKIJONOT. TAULUKOT. MUUTTUJAT. SUUNNITTELUSTA. POIKKEUKSET. Informaatioverkostot: Studio 1 Luentomateriaali. 5 11 17 23. 40 45 50 60. - PowerPoint PPT Presentation
Citation preview
1
Informaatioverkostot: Studio 1Informaatioverkostot: Studio 1
LuentomateriaaliLuentomateriaaliJanne Käki
2006
MUUTTUJAT
TAULUKOT
MERKKIJONOT
SILMUKAT
POLYMORFISMI
POIKKEUKSET
JAVA-API
TIETOVIRRAT
SWING
SOVELMAT
2D-GRAFIIKKA
SÄIKEET JA SWING
UML-KAAVIOT
JAVADOC
PAKKAUKSET
SUUNNITTELUSTA
5
1
1
1
7
2
3
2
8
3
3
3
5
3
7
4
0
4
5
5
0
6
0
6
3
6
4
6
6
7
0
2
Muutama perusasiaMuutama perusasia Tietokone tekee juuri (ja vain) sen, mitä käsketään. Tietokone ymmärtää vain syntaksia (sanojen kirjoitusasua), ei
semantiikkaa (sanojen merkitystä). Muuttujat ja metodit voi tietokoneen puolesta nimetä (melkein)
”ihan miten vaan”, olennaista on vain, että samasta muuttujasta tai metodista käytetään johdonmukaisesti samaa nimeä.
Java-kielessä on kuitenkin joitakin kymmeniä avainsanoja (public, private, int, void, true, null, return, . . .), jotka on varattu tiettyyn käyttöön. Näitä ei siis voi valita muuttujien nimiksi.
Se, että muuttujat ja metodit nimetään fiksusti ja käyttötarkoitusta kuvaavasti, on tärkeää nimenomaan (ja vain) koodia lukevan ihmisen kannalta.
Ohjelman suoritus on normaalisti (virheettömänä) täysin näkymätöntä.
Siksi tarvitaan tulostuksia, jotta saisimme käsityksen siitä, mitä ohjelmassa tapahtuu – mitä paremmat tulostukset, sitä parempi käsitys.
3
SijoituslauseSijoituslause
int luku = (5 + 4) * 8 - 7;
laskuri = laskuri + 1;
1. Selvitetään oikeanpuoleisen lausekkeen arvo.
2. Määritellään uusi kokonaislukutyyppinen muuttuja nimeltä luku.
3. Sijoitetaan kohdassa 1 laskettu arvo kohdassa 2 määriteltyyn muuttujaan.
1. Lasketaan yhteen laskurin nykyinen arvo ja yksi.
2. Sijoitetaan tämä arvo laskurin uudeksi arvoksi.
(Koska laskurimuuttuja on jo entuudestaan olemassa, sitä ei siis ole syytä määritellä uudelleen, joten emme käytä sanaa int.)
sijoitu
soperaattori (
ei
yhtäsuuruusmerkki!)
4
Palauttaminen vs. tulostaminenPalauttaminen vs. tulostaminenMetodi, joka palauttaa tekstin ”böö”:
public String palautaBöö() {
return ”böö”;
}
Metodi, joka tulostaa tekstin ”böö”:
public void tulostaBöö() {
System.out.println(”böö”);
}
paluuarvon tyyppi
”ei palauta mitään”
bööString bööTeksti = palautaBöö();
System.out.println(bööTeksti);
antaa palautettavan arvon käyttöön sille, joka metodia kutsui
tuottaa tekstin näytölle
5
OliokohtaisetOliokohtaiset LuokkakohtaisetLuokkakohtaiset
(static)(static)
Attrib
uu
titA
ttribu
utit
Luokan yksittäisen ilmentymän eli olion ominaisuudet.Määrittelevät olion tilan.Yleensä yksityisiä, eli vain oliolla itsellään on suora pääsy käsiksi attribuuttien arvoihin.
Ominaisuuksia, jotka ovat kaikkien luokasta luotujen olioiden yhteisessä käytössä.Usein julkisia, eli kuka tahansa näkee nämä. Yleensä lisäksi vakioita, eli niille asetetaan tietty arvo, jota ei jälkeenpäin pysty muuttamaan.
Meto
dit
Meto
dit
Luokan yksittäisen ilmentymän eli olion toiminnot, ”mitä olio osaa”.Antavat mahdollisuuden saada tietoja olion tilasta ja muuttaa sitä.(Olion metodien tehtävänä on siis tarjota ”rajapinta”, jonka avulla ulkopuolinenkin voi kysyä ja jopa muokata olion attribuuttien arvoja.)
Metodeja, jotka eivät kuulu millekään yksittäiselle oliolle. (Niinpä sana this ei merkitse tällaisessa metodissa mitään.)Pääohjelmametodi on aina tällainen. Jos siellä halutaan testata jotakin yksittäisen olion metodia, tällainen olio pitää ensin luoda ja laittaa talteen johonkin muuttujaan.
6
Tiedon abstraktiotasojaTiedon abstraktiotasoja
bititnollia tai ykkösiä
muistipaikatjoukko bittejä, joilla on yksi osoite
muuttujatnimetty tietyntyyppisen tiedon muistipaikka
oliotnimetty joukko muuttujia (ja operaatioita)
kokoelmatnimetty joukko olioita
Java
n f
oku
s
MUUTTUJAT
7
Erilaisia muuttujiaErilaisia muuttujia
public class Mursu {
private String nimi;private int nopeus;
public Mursu(String nimi, int nopeus) {this.nimi = nimi;this.nopeus = nopeus;
}
public double liiku(double aika) {double matka = aika * this.nopeus;System.out.println(nimi + ” lyllersi ” + matka + ”
km.”);return matka;
} }
attribuutitmäärittävät olion tilan eli ominaisuuksien arvot
parametritantavat metodille sen suorituksen kannalta tarpeelliset lähtötiedot
apumuuttujatmahdollistavat jonkin arvon muistamisen niin kauan kuin sitä tarvitaan
MUUTTUJAT
8
MuuttujistaMuuttujista• Muuttujan määrittely (eli esittely): nimetään tietolokero ja
kerrotaan, minkä tyyppistä tietoa se voi sisältää. String brutaaliSolvaus; int vihreidenPingviinienLukumaara;
• Muuttujan alustus: sijoitetaan muuttujaan (sen historian ensimmäinen) arvo.
brutaaliSolvaus = ”hähä mogari”; Voidaan tehdä myös suoraan määrittelyn yhteydessä:
String brutaalimpiSolvaus = ”luetaan sitä API:a”;
• Muuttujan arvon lukeminen: käytetään muuttujaa missä tahansa muualla kuin sijoituslauseen vasemmalla puolella.
System.out.println(brutaalimpiSolvaus); if (vihreidenPingviinienLukumaara > 1) { Ei saa tehdä alustamattomalle muuttujalle, muuten kääntäjää alkaa pelottaa.
(”Variable might not have been initialized.”) Attribuuteilla on aina tietty alkuarvo automaattisesti (numeromuuttujilla
nolla, booleaneilla false, oliomuuttujilla null), joten niiden alustaminen ei ole välttämätöntä ennen muuttujan käyttämistä – yleensä toki suositeltavaa.
MUUTTUJAT
9
TietotyypeistäTietotyypeistä
alk
eistyyp
it
kokonaisluku
byte
short
int
long
desimaaliluku (liukuluku)float
double
totuusarvo boolean
merkki char
viittau
styypit
merkkijono String
alkeistyyppien kääreet Integer, Double, Boolean, Character, ...
taulukot int[ ], double[ ], Object[ ], Karttaruutu[ ][ ], ...
muut oliotyypit Object, Random, HashSet, Pelotteluesine, ...
olio
ita
MUUTTUJAT
10
Viittaustyyppiset muuttujatViittaustyyppiset muuttujat
• Olio on kuin ilmapallo.• Oliomuuttujat ovat viittauksia – kuin
naruja, joiden päässä se ilmapallo on.• Sama olioilmapallo voi olla useamman
narun päässä. Siihen voidaan vaikuttaa (eli sen metodeita kutsua) kaikkia naruja pitkin, ja kaikki kutsut muokkaavat yhtä ja samaa oliota.
• Kun oliomuuttuja lakkaa olemasta tai saa uuden arvon, yksi naru katkeaa.
• Kun olio ei ole enää yhdenkään narun päässä kiinni, se karkaa stratosfääriin (eli Javan automaattinen roskienkerääjä tulee ja syö sen).
MUUTTUJAT
11
Klassinen for-lauseKlassinen for-lause
for (int i = 0; i < 3; i++) {
...
}
alustus-lause
onkoehtotosi?
suoritettavatlauseet
kasvatus-lause
kyllä
ei
for-lauseen jälkeinen elämä
SILMUKAT
12
for (int i = 0; i < 3; i++) {
...
}
{
int i = 0;
while (i < 3) {
...
i++;
}
}
Tällainen for-lause...
...vastaa while-lauseella toteutettua rakennetta
Mikä merkityson ulommillaaaltosuluilla?
SILMUKAT
13
Iteroiva for-lauseIteroiva for-lause
• Käy läpi kaikki tiettyyn kokoelmaan sisältyvät oliot.• Kukin kokoelman olio sijoitetaan siis vuorollaan for-lauseen
määrittelemään muuttujaan ja siihen sovelletaan aaltosulkujen sisällä olevia lauseita.
• Läpikäyntijärjestys riippuu kokoelmasta. Listoilla se on määrätty, joukoilla (Set) yleensä määrittelemätön, mutta ei kuitenkaan aidosti satunnainen.
• Kokoelman sisältöä ei saa muuttaa (lisätä tai poistaa olioita) kesken iteroinnin, muuten seuraa virhe nimeltä ConcurrentModificationException.
public void tulostaNimet(Set<Olento> olennot) {
for (Olento o : olennot) {
System.out.println(o.annaNimi());
}
}
SILMUKAT
14
public void tulostaNimet(Set<Olento> olennot) { String nimet = ””; for (Olento o : olennot) { nimet += o.annaNimi() + ”, ”; } System.out.println(nimet);}
Arska, Pena, Mats, Rauski,Tulostaa:Miten pääsemme eroon ylimääräisestä pilkusta?
public void tulostaNimet(Set<Olento> olennot) { String nimet = ””; int otuslaskuri = 0; for (Olento o : olennot) { nimet += o.annaNimi(); if (otuslaskuri < olennot.size()-1) { nimet += ”, ”; } otuslaskuri++; } System.out.println(nimet);}
SILMUKAT
15
IteraattoriIteraattori
for (Olio o : kokoelma) {
System.out.println(o);
}
Iterator<Olio> iter = kokoelma.iterator();
while (iter.hasNext()) {
Olio o = iter.next();
System.out.println(o);
}
Vanha tuttu iteroiva for-looppi...
...toimii pinnan alla itse asiassa iteraattoriolion avulla.Sama looppi hieman toisin kirjoitettuna:
Kokoelman on toteutettava rajapintaIterable<T>, joka määrittelee metodinpublic Iterator<T> iterator().
Myös Iterator<T> on itse asiassa rajapinta.Iteraattorin metodi next() palauttaa japoistaa – iteraattorista, ei iteroitavastakokoelmasta – järjestyksessä seuraavanelementin. Metodi hasNext() kertoo,vieläkö elementtejä on jäljellä.
SILMUKAT
16
IteraattoriIteraattori
Iteroimisessa on vaaransa. Metodi next() voi heittää poikkeuksia... NoSuchElementException, jos elementtejä ei enää ole. Vältettävissä
huolellisella hasNext()-metodin käytöllä. ConcurrentModificationException, jos iteroitava kokoelma on muuttunut
iteraattorin luomisen jälkeen. Uusia elementtejä ei siis voi kesken iteroinnin lisätä, ellei iterointia tämän jälkeen keskeytä.Elementtien poistaminen on luvallista iteroinnin lomassa vain iteraattori-olion metodilla remove(), joka poistaa viimeisimmän next()-metodin palauttaman elementin, myös iteroitavasta kokoelmasta.
Iteraattori siis antaa luvan myös elementtien poistamiseen kokoelmasta (periaatteessa – oma iteraattoriluokka voidaan kuitenkin toteuttaa myös niin, ettei remove()-metodi tee mitään). Aina ulkopuoliselle käyttäjälle ei haluta jättää tällaista mahdollisuutta, jolloin voi olla syytä harkita toisenlaisen rajapinnan tarjoamista kokoelman läpikäyntiin.
SILMUKAT
17
TaulukkoTaulukko
0 1 2 3 4 5 6
9 14 2 7 2 0 22int[] taulu = { 9, 14, 2, 7, 2, 0, 22 };
taulu[0] = 1;taulu[2] = 100;taulu[taulu.length-1] = 8;
taulu = new int[5];
0 1 2 3 4 5 6
1 14 100 7 2 0 8
0 1 2 3 4
0 0 0 0 0
Taulukkomuuttujan määrittely ja alustaminen ns. taulukon alustajalla:
Taulukon indeksointi ja sen alkioiden arvojen asettaminen:
Uuden, tyhjän taulukon luonti new-operaattorilla:
Taulukotkin ovat olioita. Huomaa, että taulukon koko on taulukko-olion, ei taulukkomuuttujan ominaisuus. (Samaan muuttujaan voi sijoittaa minkä kokoisen taulukon tahansa.)Taulukon tietotyyppi on sen sijaan myös taulukkomuuttujan ominaisuus.
TAULUKOT
18
public void kauhistuHirviolaumaa(Olento[] hirviot) {
for (int i = 0; i < hirviot.length; i++) {
hirviot[i].pelottele(this);
}
}
Laskurilla varustettu for-silmukka on kuin luotu taulukon läpikäymiseen, esimerkiksi seuraavassa fiktiivisessä Olento-luokan metodissa:
Muista, ettätaulukon indeksitalkavat nollasta ja
päättyvät yhtäpienempään kuintaulukon koko.
hirviot[i] viittaavuorollaan aina yhteen
taulukon alkioon,siis Olento-tyyppiseenolioon (ellei kyseinen
alkio ole null).
this viittaaaina siihen olioon, jonka
metodista on kyse.Sitä voi käyttää myös
sellaisenaan, esimerkiksiparametrina jollekintoiselle metodille.
TAULUKOT
19
int[] tauluA;
int[] tauluB = null;
int[] tauluC = new int[5];
Pelotteluesine[] tauluD = new Pelotteluesine[1000];
tauluA[0] = 49;
tauluB[0] = 88;
tauluC[5] = 31;
tauluC[2] = 4.86;
tauluA = new double[10];
tauluB = tauluA;
tauluD[666].annaPelottavuus();
Inspired by: http://www.cs.helsinki.fi/u/wikla/Ohjelmointi/Sisalto/3/Taulukot.html#2
Jos määrittelemme seuraavasti
ja sen jälkeen yritämme seuraavia asioita, mikään niistä ei onnistu. Miksi?
Variable tauluA might not have been initialized.java.lang.NullPointerException
java.lang.ArrayIndexOutOfBoundsExceptionPossible loss of precision. Found: double. Required: int.
java.lang.NullPointerException
Variable tauluA might not have been initialized.
Incompatible types. Found: double[ ]. Required: int[ ].
TAULUKOT
20
Kaksiulotteinen taulukkoKaksiulotteinen taulukkoeli taulukoita taulukossaeli taulukoita taulukossa
0
1
2
3
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
0 1 2 3 4
Olento[][] otusruudukko = new Olento[4][5];
otusruudukko[2][1] = new Olento(”Rauski”, 100);
Luodaan 2D-taulukko eli matriisi, jossa on neljä riviä ja viisi saraketta:
Itse asiassa loimme yhden nelipaikkaisen taulukon, jonka alkioiden tietotyyppi on Olento[]. Se sisältää siis neljä yksiulotteista, viisipaikkaista olentotaulukkoa. Nämä taulukot ovat aluksi tyhjiä, eli niiden jokainen alkio on null.
Voimme sijoittaa ruudukkoon uusia arvoja kertomalla, mihin alitaulukkoon ja mihin lokeroon siellä kyseinen arvo sijoitetaan:
TAULUKOT
21
0
1
2
3
0 1 2 3 4
0 1 2
0
0 1 2 3
Olento[][] otusruudukko = new Olento[4][];
2D-taulukon rivien ei välttämättä tarvitse olla samanpituisia – jokainen alitaulukkohan on yksilöllinen olio, jonka pituus voi olla mitä vain:
otusruudukko[0] = new Olento[5];
otusruudukko[1] = new Olento[3];
otusruudukko[2] = new Olento[1];
otusruudukko[3] = new Olento[4];
otusruudukko[3][3] = new Olento(”Rauski”, 100);
Huomaa tyhjät jälkimmäiset hakasulut. Tässä luotiin tyhjä nelipaikkainen taulukko, johon voi sijoittaa Olento[]-taulukoita.
Näitä alitaulukkoja ei kuitenkaan vielä ole olemassa, ne on luotava nyt erikseen.
TAULUKOT
22
String[][] ruudut = new String[5][5];
for (int i = 0; i < ruudut.length; i++) {
for (int j = 0; j < ruudut[i].length; j++) {
ruudut[i][j] = i + ”, ” + j;
}
}
for-lauseita voi käyttää myös sisäkkäin.
Kaksiulotteisen taulukon läpikäynnissä tämä on usein tarpeen:
0, 0 0, 1 0, 2 0, 3 0, 4
1, 0 1, 1 1, 2 1, 3 1, 4
2, 0 2, 1 2, 2 2, 3 2, 4
3, 0 3, 1 3, 2 3, 3 3, 4
4, 0 4, 1 4, 2 4, 3 4, 4
TAULUKOT
23
Merkkijonojen vertailuMerkkijonojen vertailu
String a = "kaikki on mahdollista";
String b = "kaikki on mahdollista";
System.out.println(a == b);
b = new String("kaikki on mahdollista");
System.out.println(a == b);
System.out.println(a.equals(b));
System.out.println(a == b.intern());
true
false
true
trueEsimerkit: Ville Sundberg
MERKKIJONOT
24
EI NÄINEI NÄIN
VAAN VAAN NÄINNÄIN
if (nimi == ”Rauski”) {
if (nimi.equals(”Rauski”)) {
MERKKIJONOT
if (nimi.equalsIgnoreCase(”RAUSKI”)) {TAI JOS TAI JOS KIRJAIN-KIRJAIN-
KOOLLA EI OLE KOOLLA EI OLE MERKITYSTÄMERKITYSTÄ
25
Merkkijonon paloitteluMerkkijonon paloittelu
String lause = ”Kivet on tosi jänniä.”;
String[] sanat = lause.split(” ”);
sanat[0] sanat[1] sanat[2] sanat[3]
”Kivet” ”on” ”tosi” ”jänniä.”
MERKKIJONOT
26
Merkkijonon tulkinta luvuksiMerkkijonon tulkinta luvuksi
Integer.parseInt(String merkkijono)Integer.parseInt(String merkkijono) Integer-luokan staattinen metodi yrittää tulkita merkkijonon kokonaisluvuksi heittää poikkeuksen NumberFormatException, jos
merkkijono ei esitä puhdasta kokonaislukua (varauduttava aina tähän mahdollisuuteen)
Double.parseDouble(String merkkijono)Double.parseDouble(String merkkijono) Double-luokan staattinen metodi, muuten sama kuin
yllä mutta tulkitsee merkkijonoja desimaaliluvuiksi (desimaali-erottimena oletusarvoisesti piste)
MERKKIJONOT
27
String-luokassa lisäksi mm. metodit: String substring(int alku, int loppu)
palauttaa osamerkkijonon väliltä alku … loppu-1
int indexOf(String osamerkkijono) palauttaa indeksin, josta etsittävä osamerkkijono (ensimmäinen
löytynyt) alkaa jos osamerkkijonoa ei löydy, palauttaa arvon –1
String trim() palauttaa merkkijonon kopion, jonka alusta ja lopusta on poistettu
mahdolliset välilyönnit
String toLowerCase() palauttaa merkkijonon kopion, jonka kaikki ISOT KIRJAIMET on
muutettu pieniksi kirjaimiksi (toUpperCase() tekee saman toisinpäin)
String toString() :D Luokka String on arvo-
keskeinen: String-olion metodit eivät muuta kysei-sen olion tilaa mitenkään. Jos metodi tekee muutoksia, se palauttaa kopion alku-peräisestä – uuden olion.
MERKKIJONOT
28
Esineprivate String nimi
private double paino
public Esine(String nimi, double paino)
Object
public Object()
Pelotteluesineprivate int pelottavuus
public Pelotteluesine(String nimi,double paino, int pelottavuus)o
min
aisu
uksie
n m
äärä
public Pelotteluesine(String nimi, double paino, int pelottavuus) {
super(nimi, paino);
this.pelottavuus = pelottavuus;
}
public Esine(String nimi, double paino) {
super();
this.nimi = nimi; this.paino = paino;
}
public Object() {
...
}
Halutaan luoda uusi Pelotteluesine...
...mutta ennen pelotteluesineen oman pelottavuus-ominaisuuden alustusta on varmistettava, että perustana on oikeaoppinen Esine. Siksi ihan aluksi kutsutaan Esine-luokan luontimetodia...
...jonka aluksi puolestaan kutsutaan vielä Object-luokan konstruktoria. (Tässä super();ia ei olisi pakko kirjoittaa, koska nyt yläluokan luontimetodille ei tarvitse antaa parametreja. Esineelle sen sijaan täytyi.)
POLYMORFISMI
Kun luodaan uutta Pelotteluesinettä, on sille ensin rakennettava yläluokkien määrittelemä perusta – alustettava ne ominaisuudet, jotka sille kuuluvat 1) Objectina ja 2) Esineenä.
29
Metodin kuormittaminenMetodin kuormittaminen(overloading)(overloading)
• Samannimisestä metodista on määritelty samassa luokassa (tai samassa yli- ja aliluokkien jatkumossa) useita versioita, joista valitaan suoritettavaksi yksi sen perusteella, mitä parametreja metodikutsussa on annettu.
• Metodikutsu sidotaan suoritettavaan metodiin metodin nimen sekä parametrien tyyppien ja järjestyksen perusteella. (Sen sijaan esimerkiksi parametrimuuttujien nimillä ei ole mitään merkitystä.Ei myöskään sillä, minkä tyyppisiä arvoja metodin eri versiot palauttavat.)
• Metodikutsujen on oltava yksiselitteisiä, eli ei saa olla epäselvää, mikä vaihtoehtoisista tietynnimisen metodin versioista nyt suoritetaan:
public void metodi(Object o, String s) { ...}
public void metodi(String s, Object o) { ...}
metodi(”nuuh”, ”nuuh”);
metodi(”nuuh”, (Object) ”nuuh”);
POLYMORFISMI
30
Metodin korvaaminenMetodin korvaaminen(overriding)(overriding)
• Aliluokka määrittelee yliluokassa määritellyn metodin toteutuksen kokonaan uudelleen. Korvaavan metodin puumerkki on täsmälleen sama kuin alkuperäisen, eli metodeilla on sama nimi, samanlainen parametrilista (tyypit ja järjestys) ja sama paluuarvon tyyppi.
• Voidaan merkitä aliluokkaan kirjoittamalla metodin yläpuolelle erityinen @Override-tägi. Ei pakollista, mutta parantaa luettavuutta.
• Metodin uusi versio EI saa– muuttaa paluuarvon tyyppiä,– rajata näkyvyyttä alkuperäistä suppeammaksi (laajentaa sen sijaan saa),– heittää sellaisia poikkeuksia joita alkuperäinen ei määritellyt heittävänsä
(sen sijaan uusi versio voi mainiosti olla heittämättä joitakin poikkeuksia joita alkuperäinen metodi heitti).
• Kun luokan A metodi x() on korvattu aliluokassa B metodilla x(), niin alkuperäistä metodia voi kutsua luokan B oliolle vain olio itse notaatiolla super.x(). Muut kutsut johtavat aina luokassa B määritellyn metodin suorittamiseen.
POLYMORFISMI
31
:D
:D
Otus eka = new Otus();
Otus toka = new PäheäOtus();
PäheäOtus kolmas = new PäheäOtus();
toka
kolmas
Metodien staattinen ja Metodien staattinen ja dynaaminen sidontadynaaminen sidonta
Olkoon PäheäOtus luokan Otus aliluokka. Otus-luokassa on määritelty tylsä metodi eksistoi(), jonka PäheäOtus on korvannut uudella päheämmällä versiolla. Lisäksi PäheäOtus-luokassa on täysin uusi, ennen näkemätön metodi kelaaSunLaatuas().
PäheälleOtukselle, joka on sijoitettu Otus-tyyppiseen muuttujaan, voidaan kutsua ainoastaan Otus-luokan metodeja. Sanotaan, että kyseisen olion staattinen tyyppi on Otus. Vaikka olio siis osaisi myös kelata laatuaan, sitä ei voi tuon muuttujan kautta käskeä niin tekemään. Sopivalla tyyppimuunnoksella staattinen tyyppi voidaan kuitenkin saattaa sellaiseksi, että tuokin metodi on käytössä.
Sen sijaan muuttujan staattinen tyyppi Otus antaa toki meille luvan kutsua oliollemme metodia eksistoi(). Tällöin metodikutsu sidotaan kuitenkin aina olion todelliseen, dynaamiseen tyyppiin, joka tässä tapauksessa on PäheäOtus. Hämmästykseksemme tylsässä Otus-laatikossa piileksinyt olio alkaakin siis eksistoida hyvin päheästi, kun käskytämme sitä tuollaisella metodikutsulla. Mitenkään emme pysty käskemään tuota oliota käyttäytymään siten kuin puhdas Otus käyttäytyisi, ellei se sitten itse päätä suorittaa tuota metodia kutsulla super.eksistoi().
Staattinen tyyppi siis sanelee sen, mitkä metodikutsut muuttujaan sijoitetulle oliolle ovat ylipäänsä luvallisia. Dynaaminen tyyppi (joka ei olion elinaikana muutu) taas määrää, mitä koodia oliolle tehtyjen metodikutsujen seurauksena todella suoritetaan.
POLYMORFISMI
:)
eka
32
RajapinnatRajapinnatRajapinta eli liittymä (engl. interface) on luokka, joka määrittelee ”olion käyttöliittymän” (tai jonkin osa-alueen siitä).
Rajapinta toisin sanoen määrittelee yhden tai useamman metodin, jotka tarjoavat tietyn toiminnallisuuden. Ja tarkemmin sanoen se vain määrittelee nämä metodit, ei toteuta niitä (eikä ota kantaa siihen, millaisten teknisten yksityiskohtien avulla niiden toiminnallisuus toteutetaan).
Rajapinta on toisin sanoen kuin abstrakti luokka, joka sisältää vain abstrakteja metodeja (siis metodeja ilman toteutusta, eli tavallaan vaatimuksia aliluokille tietynlaisten metodien toteuttamisesta).
Mutta siinä missä Java-luokalla voi olla aina vain yksi suoranainen yläluokka (abstrakti tai ei), voi sama luokka toteuttaa yhden, kaksi, kolme tai miljoona rajapintaa. Toteuttaminen tarkoittaa sitä, että luokkaan on laadittu käytännön toteutus kaikille näissä rajapinnoissa määritellyille metodeille ja lisäksi toteuttami-sesta on kerrottu avainsanan implements avulla.
1. Rajapintaluokka määrittelee tietyn toiminnallisuuden.
2. Konkreettiset luokat toteuttavat rajapinnan, kukin omalla tavallaan.
3. Toinen luokka voi käyttää kaikkia näitä luokkia ”rajapinnan edustajina”, yhteisten ominaisuuksien pohjalta.
POLYMORFISMI
33
PoikkeuksetPoikkeuksetpublic Paaryna varastaPaaryna() throws KaameaPoikkeus {
if (vartija.onVahdissa() && !vartija.nukkuu()) {
KaameaPoikkeus poikkeus = new KaameaPoikkeus("Kiinni jäit, hähä!"); throw poikkeus;
} else {
return paarynapuu.annaPaaryna();
}}
public void eleleVaarallistaElamaa() {
try {
// VOI HEITTÄÄ POIKKEUKSEN: Paaryna saalis = this.varastaPaaryna(); this.syo(saalis);
} catch (KaameaPoikkeus poikkeus) {
System.out.println(poikkeus); this.karsiRangaistus();
} finally {
this.jatkaElamaaEntiseenTapaan();
}}
Tähän asti olemme tottuneet siihen, että metodin paluuarvo (true tai false, olio tai null) riittää kertomaan, onnistuiko metodi tehtävässään vai ei.
Mutta millainen paluuarvo pystyisi ilmaisemaan, että jäimme kiinni varastaessamme päärynää?
Javan poikkeukset (engl. exceptions) ovat ikään kuin vaihtoehtoja metodien normaaleille paluuarvoille. Tyypillisesti ne kertovat, että metodissa tapahtui odottamaton virhe.
Poikkeusten yhteydessä ei puhuta palauttamisesta, vaan ”heittämisestä”.Monet Javan valmiit poikkeukset (ja tyypillisesti kaikki itse laaditut) täytyy käsitellä. Toisin sanoen jos on olemassa vaara (todellinen tai edes teoreettinen), että tietyn metodin kutsusta aiheutuu poikkeus, tämän poikkeuksen mahdollisuuteen tulee varautua ns. try-catch-rakenteen avulla.
Try-osioon sijoitetaan se metodikutsu, josta potentiaalisesti voi aiheutua poikkeus, sekä kaikki se mitä onnistuneesta yrityksestä suoraan seuraa.
Catch-osiossa puolestaan yleensä reagoidaan jollain tapaa yrityksen epäonnistumiseen.
Finally-osio ei ole pakollinen. Se suoritetaan sekä onnistuneen että epäonnistuneen kokeilun lopuksi.
POIKKEUKSET
34
Erilaisia poikkeusluokkiaErilaisia poikkeusluokkia
Throwable
RuntimeException
Error
ExceptionTarvitsee käsitellä, paitsi...
...näitä ei tarvitse käsitellä.
Ei tarvitse käsitellä.
Kaikkien poikkeus-luokkien yläluokka. (Nimestään huolimatta ei siis rajapinta.)
POIKKEUKSET
35
Keskeisimmät Java-API:n pakkauksetKeskeisimmät Java-API:n pakkauksetAPI = Application Programming Interface eli sovellusohjelmointirajapinta (!)
pakkaus (engl. package) = tapa koota samaan asiaan liittyviä luokkia yhteen
java.lang• pari aivan keskeistä luokkaa, kuten
Object, String ja Math• alkeistyyppien kääreluokat (Integer,
Double, Boolean, Character, ...)• useimmat poikkeus- (Exception) ja
virheluokat (Error)• aina käytössä, ei tarvitse importoida!
java.util• kokoelmarajapinnat (Collection, Set,
List, Map) ja niiden toteutukset (HashSet, Vector, ArrayList, HashMap, ...)
• kirjastoluokat Arrays ja Collections• välineitä päiväysten ja kellonaikojen
käsittelyyn: Date, Calendar, TimeZone
• pari muuta hyödyllistä: Random, Timer, Scanner
java.io• välineet näppäimistön ja tiedostojen
lukemiseen (erilaiset InputStream- ja Reader-luokat) sekä näytölle ja tiedostoon kirjoittamiseen (OutputStream- ja Writer-luokat)
java.net• välineet verkkoyhteyksien
muodostamiseen
java.awt• AWT-luokkakirjasto (Abstract Windowing
Toolkit), Javan vanhempi kalusto graafisten käyttöliittymien toteutukseen
javax.swing• Swing-luokkakirjasto, uudemmat välineet
graafisten käyttöliittymien tekemiseen
JAVA-API
36
Luetaan sitä API:a!Luetaan sitä API:a!Mutta miten?Mutta miten?
1. Etsi API:sta oikea luokka haluamasi asian tekemiseen.2. Etsi luokasta metodi, jolla sen saa tekemään tuon asian.3. Selvitä, miten kyseistä metodia käytetään.
Java-ohjelmoinnissa monien ongelmien ratkaisu käy seuraavaan tapaan:
• Varmista, että olet tekemisissä oikean luokan kanssa (tsekkaa myös, missä pakkauksessa se on). Esimerkiksi java.util.List ja java.awt.List ovat kaksi täysin erilaista luokkaa, jotka menevät helposti sekaisin.
• Katso, mistä tutkimasi luokka periytyy. Yläluokista voi päätellä paljon siitä, mitä luokka osaa omien metodiensa lisäksi tehdä. Luokan omassa Method Summary -listassa ei mainita yläluokilta perittyjä metodeja, mutta luokalla on luonnollisesti myös ne käytettävissään. (Ne luetellaan lyhyesti tuon listan alapuolella.)
• Joistakin metodeista on tarjolla useampi kuormitettu (engl. overloaded) versio, eli samanniminen metodi erilaisilla parametrivaihtoehdoilla. Osa näistä on suora-viivaisempia käyttää, toiset tarjoavat mahdollisuuden hyvinkin monimutkaisiin säätöihin. Valitse oikea metodi tarpeesi mukaan, säästyt turhalta säätämiseltä. :)
API:n lukemisessa on kuitenkin muutama sudenkuoppa, joita kannattaa varoa:
JAVA-API
37
TietovirratTietovirrat
Tavuvirta Merkkivirta
SyöttöSyöttö
(input)(input)java.io.java.io.
InputStreamInputStream java.io.Readerjava.io.Reader
TulostusTulostus
(output)(output)java.io.java.io.
OutputStreamOutputStream java.io.Writerjava.io.Writer
TIETOVIRRAT
38
Näppäimistön lukeminenNäppäimistön lukeminen
InputStream(System.in)
InputStreamReader
BufferedReader
näppäim
istö
01001110
br
11101011
öm
mene avantoon
katso olentoa 4
tavut
merkit
merkkijonot
TIETOVIRRAT
39
BufferedReader lukija = new BufferedReader( new InputStreamReader(System.in));
IOException (tietovirtahepo) vaanii kaikissa tietovirroissa
InputStream(System.in)
InputStreamReader
BufferedReader
TIETOVIRRAT
40
Graafisen Java-ohjelman osatGraafisen Java-ohjelman osat
näkyvät osat näkymättömät osat
Komponentit Säiliöt AsettelijatTapahtumat
ja kuuntelijatMuut
apuluokat
pakkaus javax.swing javax.swing java.awtjava.awt.eventjavax.swing.event
java.awtjavax.swing.border
tärkeitäesimerkki-
luokkia
JButtonJLabelJTextFieldJMenuItemJComboBox... ja monta muuta
JFrameJAppletJDialog
JPanelJScrollPaneJSplitPane
FlowLayoutBorderLayoutGridBagLayoutGridLayoutBoxLayoutCardLayout
ActionEventActionListenerMouseEventMouseListenerChangeEventChangeListener... ja monia muita
ColorDimensionFontGridBagConstraintsInsetsBorder (Swing)
käyttö-tarkoitus
pähkinän-kuoressa
Käyttöliittymän näkyvät rakennuspalikat, vuoro-vaikutuksen välineet, ”affordanssit”.Esittävät tietoa käyttä-jän ymmärtämässä muodossa sekä tarjoa-vat mahdollisuuksia ohjelman tilan muutta-miseen.
Komponentteja, jotka sisältävät toisia kompo-nentteja.Ylimmän tason (top level) säiliöt tulevat toimeen omillaan, eli sellainen muodostaa käyttöliittymän rungon.Alemman tason säiliöitä (kuten JPanel) voi lisätä myös toisiin säiliöihin.
Määrittävät, miten tietyn säiliön sisältö (eli siihen lisättävät kompo-nentit) asetellaan suhteessa säiliöön ja toisiinsa.Se, miten (ts. minkä-laisin parametrein) komponentit lisätään säiliöön, riippuu käyte-tyn asettelijan tyypistä.
Vuorovaikutus-mekanismien perusta.Käyttäessään jotakin komponenttia käyttäjä aiheuttaa tapahtuman, jonka komponentti lähettää tuntemilleen kyseisen tapahtuma-tyypin kuuntelijoille. Nämä reagoivat siihen suorittamalla jonkin sopivan toiminnon.
Kuvaavat abstraktilla tasolla komponenttien tiettyjä näkyviä ominaisuuksia, kuten väriä, kokoa, kehystä tai asettelua.Annetaan tyypillisesti parametrina sopivalle komponenttiolion metodille, kun halutaan asettaa komponentille kyseinen ominaisuus.
top level
SWING
41
Tapahtumapohjainen Tapahtumapohjainen ohjelmointiohjelmointi
Tapahtuman-Tapahtuman-käsittelysäiekäsittelysäie
(event (event dispatching dispatching
thread)thread)
käyttöliittymäkäyttöliittymän alustusn alustus
tapahtuman tapahtuman odottelu…odottelu…
ohjelman ohjelman käynnistykäynnistyss
TapahtumTapahtumaa
käyttäjä tekee käyttäjä tekee jotain…jotain…
Tapahtuman Tapahtuman kuuntelija(t)kuuntelija(t)
tapahtumaantapahtumaanreagointireagointi(”ohjelma(”ohjelma
tekee jotain”)tekee jotain”)
ohjelman ohjelman sulkeminesulkeminenn
TyöläissäiTyöläissäiee
(worker (worker thread)thread)
SWING
42
GridBagConstraintsGridBagConstraints
GridBagConstraints c =GridBagConstraints c =
new GridBagConstraints( new GridBagConstraints(
0, 0, 0, 0,
1, 1, 1, 1,
0.0, 0.0, 0.0, 0.0,
GridBagConstraints.CENTER, GridBagConstraints.CENTER,
GridBagConstraints.BOTH, GridBagConstraints.BOTH,
new Insets(5, 2, 5, 2), new Insets(5, 2, 5, 2),
0, 0); 0, 0);
gridx ja gridy:komponentin sijainti-koordinaatit ”gridissä”(vasemmalta ylhäältä luettuna)
gridwidth ja gridheight:komponentin leveys ja korkeus(riveissä ja sarakkeissa)
weightx ja weighty:komponentin suhteellinenpainoarvo (0.0–1.0)säiliötä täytettäessä
anchor:komponentin sijainti solunsisällä (CENTER, NORTH,EAST, SOUTHWEST, jne.)
fill:kuinka komponentti täyttääsolunsa (HORIZONTAL,VERTICAL, BOTH tai NONE)
insets:montako pikseliätyhjää tilaakomponentinympärille jätetään(ylös, vasemmalle,alas, oikealle)
ipadx ja ipady:paljonko tyhjää reunusta(”paddingia”) kompo-nentin sisään jätetäänsen minimikoossa
SWING
43
GridBagLayoutGridBagLayout
1. Luodaan paneeli.
2. Asetetaan paneelin asettelijaksi uusi GridBagLayout.
3. Luodaan käyttöliittymä-komponentti.
4. Luodaan GridBagConstraints-olio, jolle asetetaan sopivat attribuuttien arvot.
5. Lisätään komponentti paneeliin GridBagConstraints:ia käyttäen.
6. Palataan tarvittaessa kohtaan 3.
JPanel paneeli = new JPanel(JPanel paneeli = new JPanel( new GridBagLayout()); new GridBagLayout());
JTextField kenttä = new JTextField();JTextField kenttä = new JTextField();
GridBagConstraints c =GridBagConstraints c = new GridBagConstraints( new GridBagConstraints( 0, 0, 0, 0, 1, 1, 1, 1, 0.0, 0.0, 0.0, 0.0, GridBagConstraints.CENTER, GridBagConstraints.CENTER, GridBagConstraints.NONE, GridBagConstraints.NONE, new Insets(5, 2, 5, 2), new Insets(5, 2, 5, 2), 0, 0); 0, 0);
paneeli.add(kenttä, c);paneeli.add(kenttä, c);
SWING
44
GridBagLayout, toinen tapaGridBagLayout, toinen tapa
1. Luodaan paneeli.
2. Asetetaan paneelin asettelijaksi uusi GridBagLayout.
3. Luodaan GridBagConstraints-olio oletusarvoisilla attribuuteilla.
4. Luodaan käyttöliittymä-komponentti.
5. Muutetaan GridBag-Constraints:in attribuuttien arvoja, mikäli tarpeen.
6. Lisätään komponentti paneeliin GridBagConstraints:ia käyttäen.
7. Palataan tarvittaessa kohtaan 4.
JPanel paneeli = new JPanel(JPanel paneeli = new JPanel( new GridBagLayout()); new GridBagLayout());
GridBagConstraints c =GridBagConstraints c = new GridBagConstraints(); new GridBagConstraints();
JTextField kenttä = new JTextField();JTextField kenttä = new JTextField();
c.gridx = 0;c.gridx = 0;c.gridy = 0;c.gridy = 0;c.anchor = GridBagConstraints.SOUTH;c.anchor = GridBagConstraints.SOUTH;c.insets = new Insets(5, 2, 5, 2);c.insets = new Insets(5, 2, 5, 2);
paneeli.add(kenttä, c);paneeli.add(kenttä, c);
JButton namiska = new JButton(”pl”);JButton namiska = new JButton(”pl”);
c.gridy = 1;c.gridy = 1;
paneeli.add(namiska, c);paneeli.add(namiska, c);
SWING
45
Sovelmaextends JApplet
• ei main-metodia, ei (välttämättä) luontimetodiakaan• public void init() - selain kutsuu automaattisesti - alustaa esimerkiksi käyttöliittymän• lisäksi: start(), stop(), destroy()
Sovellusextends JFrame
• public static void main(String[] args), joka tyypillisesti vain luo kehysluokan ilmentymän• luontimetodi
SOVELMAT
46
Sovelman ajaminenSovelman ajaminen
<html><html>
<head><head> <title>Kolee appletti</title> <title>Kolee appletti</title></head></head>
<body><body> <table width=”100%” height=”100%” border=”0”> <table width=”100%” height=”100%” border=”0”> <tr> <tr> <td align=”center” valign=”middle”> <td align=”center” valign=”middle”>
<applet code=”KoleeAppletti.class” width=”400” height=”300”><applet code=”KoleeAppletti.class” width=”400” height=”300”> </applet> </applet>
</td></td> </tr> </tr> </table> </table></body></body>
</html></html>
• appletviewer-ohjelmassa tai selaimessa, selaimien ongelmana sovelmakoodin jääminen välimuistiin (muutokset eivät näy ilman selaimen uudelleenkäynnistystä)• Molemmat tarvitsevat HTML-sivun, jolle sovelma sijoitetaan• Tulostukset ja virheilmoitukset tulevat joko terminaaliin (appletviewerillä) tai ns. Java-konsoliin (selaimella)
SOVELMAT
47
AppletviewerAppletviewer
• Unix-terminaalissa ja Windowsin komentorivillä:
appletviewer sivuJokaSisaltaaAppletin.html
• XEmacsissa:
SOVELMAT
48
Java-konsoliJava-konsoli(Windowsissa)(Windowsissa)
SOVELMAT
49
Sovelma vs. sovellusSovelma vs. sovellus
• Sovelmalla kiinteä ikkunan koko, sovelluksella joustava• Sovelma ei saa kirjoittaa mihinkään tiedostoon, sovellus saa
– ns. servletit (palvelinsovelmat eli ”palvelmat”) saavat temmeltää jossain määrin vapaammin
• Jotkin asiat on tehtävä sovelmissa hieman monimutkaisemmin, esimerkkinä kuvan lataaminen javax.swing.ImageIcon-olioksi:
ImageIcon kuva = new ImageIcon(”mursu.png”);
ImageIcon kuva = null;try { kuva = new ImageIcon(this.getImage( new java.net.URL(this.getCodeBase(), ”mursu.png”)));} catch (java.net.MalformedURLException e) { e.printStackTrace();}
sovellus
sovelma
SOVELMAT
50
Swing-komponentin piirtäminenSwing-komponentin piirtäminen
komponentti.paint(Graphics g)komponentti.paint(Graphics g)
paintComponent(Graphics g)paintComponent(Graphics g)
paintBorder(Graphics g)paintBorder(Graphics g)
paintChildren(Graphics g)paintChildren(Graphics g)
PiirtojärjestelmäPiirtojärjestelmä
komponentti.repaint()komponentti.repaint()
korvaa tämä tarvittaessa korvaa tämä tarvittaessa omalla toteutuksellaomalla toteutuksella
kutsu tätä tarvittaessakutsu tätä tarvittaessa
• Jokainen komponentti vastaa siitä, Jokainen komponentti vastaa siitä, mitenmiten se piirretään se piirretään ruudulleruudulle
• Piirtojärjestelmä vastaa siitä, Piirtojärjestelmä vastaa siitä, milloinmilloin tämä tapahtuu tämä tapahtuu
2D-GRAFIIKKA
51
GrafiikkakontekstiGrafiikkakonteksti
java.awt.Graphicsjava.awt.Graphics
java.awt.Graphics2Djava.awt.Graphics2D
Mahdollistaa piirtämisen tiettyyn kontekstiin, esimerkiksi Mahdollistaa piirtämisen tiettyyn kontekstiin, esimerkiksi näytöllä olevaan paneeliin tai koneen muistissa olevaan kuvaan.näytöllä olevaan paneeliin tai koneen muistissa olevaan kuvaan.
paintComponentpaintComponent-metodi saa tällaisen piirtojärjestelmältä -metodi saa tällaisen piirtojärjestelmältä valmiina.valmiina.
Sisältää metodeja, joiden avulla voidaan piirtää mm. viivoja, Sisältää metodeja, joiden avulla voidaan piirtää mm. viivoja, ovaaleja, monikulmioita, tekstiä sekä muistiin ladattuja kuvia.ovaaleja, monikulmioita, tekstiä sekä muistiin ladattuja kuvia.
Edellisen aliluokka, jossa uusina ominaisuuksina mm. erityisten Edellisen aliluokka, jossa uusina ominaisuuksina mm. erityisten kuvio-olioiden piirtäminen sekä affiinit kuvio-olioiden piirtäminen sekä affiinit koordinaatistonmuunnokset.koordinaatistonmuunnokset.
Jokainen Jokainen GraphicsGraphics-olio on yleensä pohjimmiltaan myös -olio on yleensä pohjimmiltaan myös Graphics2DGraphics2D-olio.-olio.
2D-GRAFIIKKA
52
Piirtäminen…Piirtäminen…
xx
yy(width-1, (width-1, height-1)height-1)
… … perustuu komponentinperustuu komponentin omaan koordinaatistoon: omaan koordinaatistoon:
Myös komponentin rajojen Myös komponentin rajojen ulkopuolelle voi ”piirtää”, mutta ulkopuolelle voi ”piirtää”, mutta nämä osat eivät näy ruudulla.nämä osat eivät näy ruudulla.
… … tapahtuu tapahtuu GraphicsGraphics-luokan-luokan metodien avulla: metodien avulla:
public void paintComponent(Graphics g) {public void paintComponent(Graphics g) {
super.paintComponent(g);super.paintComponent(g);
g.setColor(this.koleeVäri);g.setColor(this.koleeVäri); g.drawOval(-20, -40, 100, 50); g.drawOval(-20, -40, 100, 50);
g.fillRect(80, 35, 20, 200);g.fillRect(80, 35, 20, 200);
g.setColor(this.getForeground());g.setColor(this.getForeground()); g.setFont(this.päheeFontti); g.setFont(this.päheeFontti); g.drawString(”hähä”, 10, 150); g.drawString(”hähä”, 10, 150);
g.drawImage(this.kuva, 100, 100, null);g.drawImage(this.kuva, 100, 100, null);
}}
(0, 0)(0, 0)
hähähähä
2D-GRAFIIKKA
53
KaksoispuskurointiKaksoispuskurointi
Jos komponentti piirretään osissa, piirtämistä ei kannata tehdä suoraan Jos komponentti piirretään osissa, piirtämistä ei kannata tehdä suoraan näytölle, vaan koneen muistissa näkymättömissä olevaan kuvaan näytölle, vaan koneen muistissa näkymättömissä olevaan kuvaan (“bufferiin”). Valmis kuva piirretään sitten ruudulla näkyvään (“bufferiin”). Valmis kuva piirretään sitten ruudulla näkyvään komponenttiin yhtenä kokonaisuutena, jolloin vältetään kuvan välkkyminen komponenttiin yhtenä kokonaisuutena, jolloin vältetään kuvan välkkyminen ja eri osien eriaikainen piirtyminen.ja eri osien eriaikainen piirtyminen.
Swing-komponentit ovat kuitenkin oletusarvoisesti valmiiksi Swing-komponentit ovat kuitenkin oletusarvoisesti valmiiksi kaksoispuskuroitujakaksoispuskuroituja..On siis turvallista piirtää suoraan niihin.On siis turvallista piirtää suoraan niihin.
java.awt.Imagejava.awt.Imagejavax.swing.JPaneljavax.swing.JPanel
2D-GRAFIIKKA
54
Kuvan piirtäminenKuvan piirtäminen
g.drawImage(Image img, int x, int y, ImageObserver obs);g.drawImage(Image img, int x, int y, ImageObserver obs);
Mistä se kuva Mistä se kuva saadaan?saadaan?
Mitäs laitan Mitäs laitan koordinaateiksi?koordinaateiksi?
Mikä kumma tää Mikä kumma tää nyt sit on?nyt sit on?
Helpoin tapa:Helpoin tapa:
1.1. Luo Luo ImageIconImageIcon-olio -olio haluamastasi haluamastasi kuvatiedostosta.kuvatiedostosta.
2.2. Kysy Kysy ImageImage-olio -olio siltä.siltä.
On myös muita, On myös muita, vaikeampia vaikeampia tapoja.tapoja.
Joku, joka odottelee Joku, joka odottelee (tarvittaessa) kuvan (tarvittaessa) kuvan latautumista latautumista muistiin.muistiin.
Jos käytät Jos käytät ImageIconImageIcon:ia :ia kuvan hakemiseen, kuvan hakemiseen, tämä voi hyvin olla tämä voi hyvin olla nullnull..
Komponentin sen Komponentin sen pisteen, johon pisteen, johon haluat sijoittaa haluat sijoittaa kuvan vasemman kuvan vasemman yläkulman.yläkulman.
Saavat olla myös Saavat olla myös negatiivisia.negatiivisia.
2D-GRAFIIKKA
55
Kuvan piirtäminen Kuvan piirtäminen Graphics2DGraphics2D:llä:llä
g2.drawImage(Image img,g2.drawImage(Image img, AffineTransform xform, AffineTransform xform, ImageObserver obs); ImageObserver obs);
Mahdollistaa ns. affiinien muunnosten tekemisen piirrettävälle Mahdollistaa ns. affiinien muunnosten tekemisen piirrettävälle kuvalle.kuvalle.Kuvaa voi esimerkiksi pyöritellä vapaasti.Kuvaa voi esimerkiksi pyöritellä vapaasti.
Muunnokset esitetään 3 x 3 -matriisina, Muunnokset esitetään 3 x 3 -matriisina, java.awt.geom.AffineTransformjava.awt.geom.AffineTransform ..
Ohjelmoijan ei kuitenkaan välttämättä tarvitse juuri vaivata Ohjelmoijan ei kuitenkaan välttämättä tarvitse juuri vaivata päätään matriisialgebralla – riittää, että tietää mitä haluaa (siirtää, päätään matriisialgebralla – riittää, että tietää mitä haluaa (siirtää, kääntää, skaalata, vääntää) ja kuinka paljon :)kääntää, skaalata, vääntää) ja kuinka paljon :)
2D-GRAFIIKKA
56
Geometriset muunnoksetGeometriset muunnokset
Siirto (translation)Siirto (translation)
Kierto (rotation)Kierto (rotation)
Viistoutus (shearing)Viistoutus (shearing)
Skaalaus (scaling)Skaalaus (scaling)
Peilaus (mirroring)Peilaus (mirroring)
2D-GRAFIIKKA
57
MVC-arkkitehtuurimalliMVC-arkkitehtuurimalli
MODELMODEL
VIEWVIEW
CONTROLLERCONTROLLER
käyttäjän toimenpiteet käyttäjän toimenpiteet muuttavat ohjelman tilaamuuttavat ohjelman tilaa
ohjelman tila ohjelman tila visualisoidaan visualisoidaan
käyttäjällekäyttäjälle
käyttäjä reagoi näkemäänsä käyttäjä reagoi näkemäänsä käyttöliittymän välitykselläkäyttöliittymän välityksellä
2D-GRAFIIKKA
58
Graafinen vuorovaikutteinen peliGraafinen vuorovaikutteinen peli
dsfadsfa
xx
yy
VIEWVIEW
MODELMODEL
CONTROLLERCONTROLLER
käyttäjän toimenpiteet käyttäjän toimenpiteet muuttavat ohjelman tilaamuuttavat ohjelman tilaa
ohjelman tila ohjelman tila visualisoidaan visualisoidaan
käyttäjällekäyttäjälle
käyttäjä reagoi näkemäänsä käyttäjä reagoi näkemäänsä käyttöliittymän välitykselläkäyttöliittymän välityksellä
2D-GRAFIIKKA
59
dsfadsfa
xx
yy
Pelimaailman malli: Pelimaailman malli: paikat, olennot, paikat, olennot, esineetesineet
VIEWVIEWMODELMODEL CONTROLLERCONTROLLER
Pelimaailman (tai sen Pelimaailman (tai sen tietyn osan) tietyn osan) visualisaatiovisualisaatio
Muutoksen Muutoksen aiheuttaja, aiheuttaja, ”pelimoottori””pelimoottori”
Kuuntelee pelaajanKuuntelee pelaajanantamia komentoja jaantamia komentoja ja
reagoi niihinreagoi niihinPiirtää pelin grafiikanPiirtää pelin grafiikan(ja toistaa musiikin, ääni-(ja toistaa musiikin, ääni-
tehosteet, yms.)tehosteet, yms.)
Muistaa kuka missäkin on,Muistaa kuka missäkin on,mitä tekemässä, millaisessamitä tekemässä, millaisessakunnossa, ja niin edelleenkunnossa, ja niin edelleen
Pitää yllä pelaajastaPitää yllä pelaajastariippumattomiariippumattomiapelitapahtumiapelitapahtumia
2D-GRAFIIKKA
60
Säikeet ja SwingSäikeet ja Swing
Swingissä on ”yhden säikeen sääntö”: realisoituneita käyttöliittymäkomponentteja tulee käsitellä ainoastaan yhdestä säikeestä, Swingin tapahtumankäsittelijäsäikeestä (event dispatching thread).
Tapahtumankäsittelijäsäie on siis se, joka suorittaa kuuntelijoiden sopivien metodien kutsumisen vastaavien tapahtumien yhteydessä sekä kaikkien Swing-komponenttien piirtämisen ruudulle.
Realisoitunut tarkoittaa sitä, että komponentti on tehty näkyväksi ruudulla. Ylimmän tason säiliöille (kuten JFrame) tämän tekee jokin metodikutsuista setVisible(true), show() tai pack().
Alemman tason komponentit realisoituvat, kun ne lisätään näkyvään säiliöön tai ne sisältävä säiliö tulee näkyväksi.
Toisin sanoen vielä näkymättömän käyttöliittymän alustus voi periaatteessa tapahtua missä säikeessä tahansa, näkyvien komponenttien tilan tutkiminen ja muuttaminen sen sijaan vain tapahtumankäsittelysäikeessä. Yleensä on tyylikkäintä rajata kaikki GUI-toiminta tuon säikeen vastuulle. Joskus raskaiden uusien käyttöliittymäosakokonaisuuksien alustus voidaan kuitenkin tehdä omassa säikeessään, jottei tapahtumankäsittely hidastu tarpeettomasti.
SÄIKEET JA SWING
61
Entäs sit ku oikeesti tarviin Entäs sit ku oikeesti tarviin Swingissä muitaki säikeitä? Vai Swingissä muitaki säikeitä? Vai
tarviinks?tarviinks?Case 1: Ajasta riippuvat tapahtumat ja javax.swing.Timer Usein haluamme Swing-ohjelmaan myös käyttäjän tekemisistä riippumatonta
toimintaa, esimerkiksi tietyin aikavälein toistuvia tapahtumia. Tällaiseen oma säie olisi luonteva ratkaisu, mutta hankala, koska se ei saisi käsitellä GUI-komponentteja suoraan.
Toimiva ratkaisu on käyttää Swingin Timer-luokkaa. Se siirtää vastuun ajastetuista tapahtumista suoraan tapahtumankäsittelijäsäikeelle, joka toteuttaa ne uusina ActionEvent-tapahtumina määrätylle kuuntelijalle. Vähän kuin tapahtuman käynnistykseen olisi olemassa oma nappi, jota joku kävisi klikkaamassa vaikkapa sekunnin välein.
SÄIKEET JA SWING
62
Entäs sit ku oikeesti tarviin Entäs sit ku oikeesti tarviin Swingissä muitaki säikeitä? Vai Swingissä muitaki säikeitä? Vai
tarviinks?tarviinks?Case 2: Työläissäie ja SwingUtilities.invokeLater()-
metodi Raskaita ja aikaavieviä työtehtäviä, kuten vaativaa laskentaa tai suurten
oliokokonaisuuksien alustusta, ei kannata suorittaa tapahtumankäsittelysäikeessä, koska tämä näkyisi suoraan käyttäjälle vuorovaikutuksen hidastumisena. Nämä kannattaa delegoida erityisille työläissäikeille, jotka rouskuttelevat omaa urakkaansa huomaamattomasti taustalla.
Usein työläissäikeenkin on tarpeen saada aikaan jotakin näkyvää, esimerkiksi ilmoittaa työnsä tuloksista käyttöliittymän kautta. Tätä se ei kuitenkaan saa tehdä suoraan, vaan työläissäikeen pitäisi jotenkin saada vihjattua tapahtumankäsittelysäikeelle, että tämän olisi aika tehdä jotakin.
Ratkaisu tähän kommunikaatio-ongelmaan on SwingUtilities-luokan metodi invokeLater(Runnable r), joka ottaa parametrinaan jotakin suoritettavaa (siis olion, jolla on metodi run()) ja siirtää sen suoritettavaksi tapahtumankäsittelysäikeessä ”myöhemmin”, käytännössä hyvinkin pian. run()-metodissa on tyypillisesti koodia, joka yhdistää työläissäikeen työn tulokset ja graafisten käyttöliittymäkomponenttien käsittelyn sopivalla tavalla.
SÄIKEET JA SWING
63
Olento
- nimi : String- elinvoima : int- sijainti : Karttaruutu
+ Olento(nimi : String, elinvoima : int) : Olento+ annaNimi() : String+ annaElinvoima() : int+ muutaElinvoimaa(muutos : int) : void# kuole() : void + annaSijainti() : Karttaruutu+ asetaSijainti(ruutu : Karttaruutu) : void+ teeSiirto() : void
Basiliski
- BASILISKIN_KATSE : Pelotteluesine
+ Basiliski(nimi : String) : Basiliski+ teeSiirto() : void+ olentoSaapunut(r : Karttaruutu, o : Olento) : void+ olentoPoistunut(r : Karttaruutu, o : Olento) : void
<<interface>>Ruuduntarkkailija
+ olentoSaapunut(r : Karttaruutu, o : Olento) : void+ olentoPoistunut(r : Karttaruutu, o : Olento) : void
UML pähkinänkuoressaUML pähkinänkuoressa (Unified Modeling Language)(Unified Modeling Language)
Karttaruutu- nimi : String- olennot : List<Olento>- ruuduntarkkailijat : List<Ruuduntarkkailija>
+ Karttaruutu(nimi : String) : Karttaruutu+ annaNimi() : String+ lisaaOlento(o : Olento) : boolean+ poistaOlento(o : Olento) : boolean+ sisaltaaOlennon(o : Olento) : boolean+ lisaaRuuduntarkkailija(rt : Ruuduntarkkailija) : void+ poistaRuuduntarkkailija(rt : Ruuduntarkkailija) : void
perintä (extends)
rajapinnan toteuttaminen (implements)
yksisuuntainen viittaus: Karttaruutu-luokan ilmentymillä saattaa olla attribuuttinaan viittauksia Ruuduntarkkailijoihin
kaksisuuntainen viittaus: olento tuntee sijaintinsa ja karttaruutu siinä sijaitsevat olennot; olennolla on vain (enintään) yksi sijainti, mutta karttaruudussa voi olla useita olentoja
* 0 .. 1
*
*
kursiivi: abstrakti luokka tai metodi
alleviivaus: staattinen attribuutti tai metodi
ISOT_KIRJAIMET: vakioarvo (final)
- private
# protected
+ public
attribuutit
metodit
rajapinnan metodit
UML-KAAVIOT
64
Javadoc-kommentointiJavadoc-kommentointi/** * Luokka, joka kuvaa maailmaa. Maailma koostuu kaksiulotteiseen taulukkoon * järjestetyistä Karttaruutu-olioista. * * @author Veijo Vesisika */public class Maailma {
/** * Ilmansuunta pohjoinen, lukuarvoltaan 0. */ public static final int POHJOINEN = 0;
/** * Metodi, jonka avulla voi selvittää tässä maailmassa tietyissä * koordinaateissa sijaitsevan karttaruudun. * * @param x karttaruudun x-koordinaatti * @param y karttaruudun y-koordinaatti * * @return annetuissa koordinaateissa sijaitseva karttaruutu tai null, * jos paikassa ei ole karttaruutua * * @throws PahaPoikkeus jos koordinaatit osoittavat maailman ulkopuolelle */ public Karttaruutu annaKarttaruutu(int x, int y) throws PahaPoikkeus {
...
}
JAVADOC
65
On hyvä tapa Javadoc-kommentoida:• luokat
käyttötarkoitus sanallisesti mahdollisesti myös versionumero (@version) ja tekijän nimi (@author) sekä viittaukset
(@see) muihin luokkiin, joihin on hyvä tutustua tätä luokkaa käytettäessä
• attribuutit (ainakin public- ja protected-tyyppiset) käyttötarkoitus sanallisesti
• metodit (ainakin public- ja protected-tyyppiset) käyttötarkoitus sanallisesti parametrien selitykset (@param) kuvaus siitä, mitä metodi voi palauttaa (@return) kuvaukset metodin mahdollisesti heittämistä poikkeuksista ja niihin johtavista tilanteista
(@throws)
Javadoc-kommenteista voidaan muodostaa itse laadituille luokille automaattisesti vastaavanlaiset dokumentaatiosivut kuin mitä Java-API:sta löytyy Javan valmiille luokille.
Tämä tapahtuu esimerkiksi komennolla:
javadoc -d docs -link ”http://java.sun.com/j2se/1.5.0/docs/api/” *.java
Huomaa pieni mutta tärkeä notaatioero Javadocin ja muiden useampirivisten kommenttien välillä:
/** /* * Tämä on Javadocia. Käytä tätä * Tämä on ”tavallinen” kommentti, jollaisia * vain sille varatuissa paikoissa. * voi laittaa myös metodien koodin sekaan. */ */
JAVADOC
66
PakkauksetPakkaukset Pieni Java-projekti: kaikki luokat kiltisti samassa hakemistossa,
ei ongelmaa. Iso Java-projekti: luokkien määrän kasvaessa kovin suureksi
yksi kansio ei enää riitä. Ongelma ihmiselle, ei tietokoneelle. Muista ihmisen
tiedonkäsittelyn rajat, enintään 4±2 hahmotettavaa yksikköä kerrallaan tietoisessa tarkastelussa.
Luokkakokonaisuus pysyy hallittavana jakamalla se osakokonaisuuksiin, pakkauksiin (engl. package).
Yhdessä pakkauksessa yhteen asiaan liittyvät luokat. Toimintalogiikan malli omassa pakkauksessaan, käyttöliittymä omassaan, jne. Nämä voidaan puolestaan edelleen jakaa useaan erikoistuneeseen pakkaukseen.
Käytännössä saman pakkauksen luokat sijoitetaan aina samaan hakemistoon.
PAKKAUKSET
67
Egoboosti- ja brändäysprefiksit.
(Huomaa päinvastainen logiikka kuin www-palvelinten osoitteissa: maa organisaatio osasto jne.)
+ Asettavat ohjelmiston suurempaan kontekstiin, luovat tunnistettavuutta.
- Syventävät (muutenkin jo syvää) hakemistohierarkiaa.
Pakkausten nimeäminen ja Pakkausten nimeäminen ja nimihierarkiatnimihierarkiat
java.awt.event
javax.swing.border
org.w3c.dom
org.omg.CORBA.portable
fi.tkk.inf.studio1.turnaus.labyrintti
virallinen Java (by Sun Microsystems)
järjestöt (W3C, OMG, ...)
Robottiturnaussoftan labyrinttien esittämiseen käytetyt luokat voisivat sijaita tällaisessa pakkauksessa.
Käytännössäkin luokat sijaitsisivat tuollaisen hakemistopolun päässä, esimerkiksi:
C:\Javaproggikset\fi\tkk\inf\studio1\turnaus\labyrintti\Labyrinttiruutu.java
PAKKAUKSET
68
Pakkausten käyttö 1Pakkausten käyttö 1
Pakkaus, johon luokka sijoittuu, määritellään:
1. Avainsanalla package aivan luokan lähdekoodin alussa:
package fi.tkk.inf.studio1.turnaus.labyrintti;
import java.util.ArrayList;
public class Labyrinttiruutu {
2. Tallentamalla luokkatiedosto oikeaan paikkaan hakemistohierarkiassa.
Pakkaus rajaa luokkien näkyvyyttä niin, että vain samaan pakkaukseen kuuluvat luokat nähdään suoraan – muiden pakkausten luokat on importoitava kuten Javan valmiit luokatkin.
PAKKAUKSET
69
Pakkausten käyttö 2Pakkausten käyttö 2 Eclipsessä pakkausten käyttö on helppoa ja visuaalista.
Uusia pakkauksia luodaan samasta valikosta kuin uusia luokkiakin. Oletuksena kaikille projekteille luodaan oletuspakkaus (default package), mutta Eclipse ei arvosta, jos käytät sitä.
Luokkia voi myöhemmin siirtää pakkauksesta toiseen Refactor-valikon Move-toiminnolla. Tämä päivittää automaattisesti kaikki viittaukset kyseiseen luokkaan ja lisää tarvittavat importit.
Komentoriviympäristössä pakkaukset tuovat hieman enemmän haastetta. Kääntäminen ja ajaminen vaativat nyt ensin ns. classpathin määrittelemisen.
set CLASSPATH=path1;path2;path3 (Windows) setenv CLASSPATH path1:path2:path3 (Unix) Näissä path1 jne. ovat hakemistopolkuja, joista (ja joiden alta) luokkia
etsitään. Usein riittää nykyiseen hakemistoon osoittava polku eli pelkkä piste. Se on suhteellinen polku, eli muuttuu valitun hakemiston mukaan.
Lisäksi luokkia ajaessa täytyy kertoa, mistä pakkauksesta (määritellyn classpath-hakemiston alta) kyseinen luokka löytyy:
esim. java fi.tkk.inf.studio1.turnaus.Turnaus &
PAKKAUKSET
70
Oliopohjaisessa suunnittelussa Oliopohjaisessa suunnittelussa mietittävää...mietittävää...
• Käytänkö Javan valmista luokkaa sellaisenaan vai kirjoitanko sille aliluokan?– Mieti, tarvitseeko luokan ilmentymien osata jotain erityistä, jota valmiista luokasta ei löydy, vai
riittääkö, että luot valmiista luokasta olion tietyillä ominaisuuksilla. Usein Swingin säiliöille kannattaa laatia omia aliluokkia, muille Swing-komponenteille tämä taas on harvemmin tarpeen.
• Korkea koheesio eli (luokkien sisäinen) yhtenäisyys: GOOD!– Yhden luokan vastuut muodostavat selkeän kokonaisuuden. Mitään olennaista ei ole piilotettu
jonnekin muualle, eikä toisaalta mukana ole mitään kovin epärelevanttiakaan.
• Korkea luokkien välinen kytkeytyneisyys: BAD!– Jokainen luokkien välinen riippuvuussuhde tekee kokonaisuudesta vaikeammin hallittavan ja
muutettavan. Tietysti luokkien välinen yhteistyö on välttämätöntä kaikissa vähänkin monimutkaisemmissa ohjelmissa, mutta kytkentöjen kannattaa olla selkeitä ja niitä kannattaa olla mahdollisimman vähän.
– Harmittomin kytkennän muoto on Swingissä yleensä tapahtumankuuntelu: luokan A olio rekisteröityy tiettyä rajapintaa C edustavana kuuntelijana luokan B oliolle ja odottaa, että B ilmoittaa A:lle uusista tätä kiinnostavista tapahtumista.
– Kun B joutuu kutsumaan suoraan luokan A metodia, kytkentä on vahvempi ja vähemmän joustava.
• Käyttöliittymä on hyvä pitää erillään varsinaisesta sovelluslogiikasta, ”ongelmadomainin mallinnuksesta”.
SUUNNITTELUSTA
71
SuunnittelumallitSuunnittelumallit(design patterns)(design patterns)
Hyväksi havaittuja konsepteja siitä, millainen olioyhteisö soveltuu tietynlaisen ongelman ratkaisuun. (Kuvailevat yleensä muutamia oliota, joilla selkeä vastuunjako. Eivät sinänsä ota kantaa siihen, miten olioita kuvaavat luokat toteutetaan, eivätkä riipu tietystä ohjelmointikielestä.) MVC (Model-View-Controller): sovelluksen datamalli, sen esittäminen
käyttäjälle sekä datamallin muokkaaminen on jaettu eri osien vastuulle. Observer: yksi olio ilmoittautuu tarkkailemaan muutoksia toisen olion
tilassa. Tarkkailtava (observable) olio ilmoittaa, kun muutoksia tapahtuu. Singleton: luokka, josta voidaan luoda vain yksi olio. Luontimetodi
piilotettu, ilmentymän luonti (ja luonnin jälkeen tuon ainoan ilmentymän hakeminen) tapahtuu jollakin staattisella metodilla, kuten getInstance().
Factory: luokka, jonka metodien avulla voidaan luoda usean muun luokan ilmentymiä. Paluuarvon tyyppi voi olla jokin rajapinta, jolloin käyttäjä voi luoda erilaisia olioita välittämättä niiden todellisesta luokasta.
Composite: suurempi kokonaisuus rakennetaan tietynlaisista rakennus-palikkaolioista, jotka jälleen voivat edustaa jotakin ”palikkarajapintaa” (ja näin ollen olla todelliselta luokaltaan hyvin erilaisia keskenään).
ja onhan näitä vielä muitakin
SUUNNITTELUSTA
72
TulostusvirratTulostusvirrat
OutputStream
OutputStreamWriter
Writer
BufferedWriter
PrintWriter
FileWriter
FilterOutputStream
FileOutputStream
perintä
aggregaatio(ohuessa päässä oleva luokka toimii salmiakkikuviopään luokan rakenteellisena osana)
tavuvirrat merkkivirrat
Korkeimman tason virroissa ns. decorator-suunnittelumalli. Virrat ovat perusmerkkivirta Writerin aliluokkia (eli lupaavat saman toiminnallisuuden), mutta niiden toiminta perustuu johonkin toiseen, matalamman tason merkkivirta-olioon. Ne lisäävät sen ympärille uutta toiminnallisuutta, ”koristeita”. Nämä virrat luodaan siis aina jonkin olemassa olevan virran pohjalle.
SUUNNITTELUSTA
73
SovelluskehyksetSovelluskehykset(software frameworks)(software frameworks)
Valmiiden luokkien (ja lopulta pakkausten) muodostamia kokonaisuuksia, joiden varaan voi rakentaa uusia ohjelmistoja, sekä käyttämällä valmiita komponentteja sellaisenaan että laatimalla niille tarpeen mukaan omia aliluokkia.
Tutuin esimerkki sovelluskehyksestä: Swing. Usein sovelluskehysten luokkakokonaisuudet on suunniteltu hyvin, ja
niissä nähdään monien suunnittelumallien soveltamista käytäntöön: MVC: jokaisen hiemankin monimutkaisemman Swing-komponentin
perustana nämä kolme osaa. Observer: tapahtumankuuntelijat ja kuunneltavat komponentit. Singleton: tietyt Swing-sovelluksen hallintaan ja asetuksiin käytetyt luokat,
joilla on aina vain yksi ilmentymä. Factory: esimerkiksi erilaisten reunusten (border) luonti. Composite: komponenttien lisääminen säiliöihin asettelijoiden avulla.
SUUNNITTELUSTA
74
liikenne ______ jono
para _____ laku
psyki _____ buutti
pelottelu _____ ellistäminen
runsauden _____ kuono
kusi ___ assistentti
75
liikenne ______ jono
para _____ laku
psyki _____ buutti
pelottelu _____ ellistäminen
runsauden _____ kuono
kusi ___ assistentti
MERKKI
METRI
ATRI
MOGARI
ESINE
SARVI
TUNTI
Recommended