Kurssin alkuosassa on jokainen ohjelma alkanut class -määrittelyllä. Siinä itse asiassa tehtiin uusi luokka, jolla oli haluttuja ominaisuuksia. Koska haluttiin luoda yksinään toimiva ohjelma, lisättiin luokkaan main -metodi, josta java -tulkki tiesi aloittaa ohjelman toteutuksen. Nyt luomme luokkia, joita käyttävät muut luokat hyväkseen. Main -metodia ei siis tarvita. Main -metodi on ensimmäisissä esimerkeissä kirjoitettu kunkin luokan viimeiseksi metodiksi, josta ohjelman toteutus alkaa (vrt. "Pääohjelma").
Luokka muodostetaan kirjoittamalla
class omena // luomme luokan omena
{
}
Olio-ohjelmoinnin lähtökohta on muodostaa malli ihmisen abstraktioista todellisessa elämästä. Jos meillä on siis mielleyhtymä omenasta, joka kasvaa omenapuussa tai joka tuodaan lentokoneella Ranskasta, jota syödään, jotka ovat punaisia, kypsiä, raakoja, makeita, kirpeitä, matoisia, rupisia, homeisia jne..., ei luokalla omena kannata ajatella olevan yhtään vähempää ominaisuuksia. Aluksi kannattaa keskittyä vain niistä oleellisimpiin. Ainahan voi periyttää omenaluokan aliluokaksi, jos jokin ominaisuus jäi puuttumaan. Ajattelun täytyy olla lähtöisin ihmisen omasta tavasta ajatella, ei koneen bittihirviömäisestä liturgiasta. Ihmisen ajattelumallista lähtöisin oleva luokka käyttäytyy kuten - ihminen (toivottavasti. Silloin sen kanssa on paljon mukavampi tulla toimeen.)
Otamme käyttöön luokalle omena muuttujat vari (nyt yritän kirjoittaa oikeaa javakoodia, joten luovun skandeista. Mitä tulikaan kirjoitettua edellisessä kappaleessa ihmisen abstraktioista!), maku ja matoinen. Näitä sanomme luokan omena jäsenmuuttujiksi.
Luotaessa olio luokasta omena, jäsenmuuttujista vari, maku ja matoinen tulee oliomuuttujia. Niiden ominaisuuksiin pääsee käsiksi kirjoittamalla olio.muuttuja ja jokaisella luokan omena oliolla on omat oliomuuttujansa. Nyt voimme kirjoittaa ensimmäisen olio-ohjelmamme. Samalla luovutaan Javalle varattujen nimien mustaamisesta.
Esimerkki 31. (esimerkki 31a. sama ohjelma ehkä aloittelijalle hieman selvemmin kirjoitettuna)
class omena // luomme luokan omena
{
String vari, maku; //luokan jäsenmuuttujat
boolean matoinen;
public static void main(String args[]) //tässä main -metodi ohjelman testaamista varten
{
omena omppu1=new omena(); // luodaan luokasta omena olio omppu1
omppu1.vari = "punainen"; //omppu1:llä on ominaisuus vari, maku ja matoinen
omppu1.maku = "hapan";
omppu1.matoinen = false;
// Tulostetaan omppu1:n ominaisuuksia
System.out.println("Omenamme väri on "+omppu1.vari);
System.out.println("Omenamme maku on "+omppu1.maku);
if (omppu1.matoinen)
System.out.println("Omenamme on kiusallisen matoinen");
else
System.out.println("Omenassamme ei ole matoja");
}
}
Java ei tunne varsinaisia vakioita, mutta ne voidaan toteuttaa laittamalla jäsenmuuttujan eteen sana final. Tällöin ei jäsenmuuttujan arvoa voida enää muuttaa ja siitä tulee vakio.
Esimerkiksi final float pii = 3.14;
Kirjoittamalla jäsenmuuttujan nimen eteen sana static, tapahtuu seuraavaa. Luotaessa luokasta uusi olio, ei tämä jäsenmuuttuja kopioidu uudelle oliolle vaan sen arvo on kaikilla luokasta luoduilla olioilla sama ja jos jokin oli muuttaa sitä, niin sen arvo muuttuu samalla kertaa kaikille samasta luokasta luodoille olioille. Tätä käytetään esimerkiksi ilmaisemaan luokasta luotujen olioiden määrää. Sen käytössä tulee olla hyvin varovainen, sillä sehän romuttaa koko olioajatelun perustan siitä, että kukin olio käyttäytyy itsenäisesti riippumatta muista olioista. (Luokkamuuttujaa sanotaan javassa usein instanssimuuttujaksi)
Esimerkiksi
static int luokastaLuotujenOlioidenMaara;
static final int luokastaLuotujenOlioiden Maksimimaara = 10;
Tulostusta varten lisäämme luokkaan omena metodin tulostaAttribuutit(), joka tulostaa oliomuuttujien vari, maku ja matoinen arvot. (Luokan muuttujia sanotaan usein luokan attribuuteiksi. Tämän takia tulostusmetodia kutsutaan tällä kertaa tulostaAttribuutit() -metodiksi.)
void tulostaAttribuutit()
{
System.out.println(" Väri: " +
vari + "/n Maku" +maku+ /n Matoisuus " + matoinen);
}
Käyttämällä tätä hyväksemme voimme kirjoittaa esimerkin E32, jossa otetaan käyttöön kaksi luokan omena1 oliota syysOmena ja kaneliOmena. Asetettuamme näille olioille ominaisuuksia ja tulostamme niiden ominaisuudet eli attribuutit.
Esimerkki E32
class omena1 // luomme luokan omena1
{
String vari, maku; // tässä luokan jäsenmuuttujat
boolean matoisuus;
void tulostaAttribuutit() //tässä luokan metodi tulostaAttribuutit
{
System.out.println(" Väri: " + vari + "\n Maku: " +maku+ "\n Matoisuus: " + matoisuus);
}
public static void main(String args[]) //tässä main -metodi ohjelman testaamista varten
{
omena1 kaneliOmena = new omena1(); // tässä uusi olio, joka kuuluu luokkaan omena1
omena1 syysOmena = new omena1(); // tässä samaan luokkaan kuuluva toinen olio
kaneliOmena.vari = "punainen"; // asetellaan olioiden muuttujia
kaneliOmena.maku = "makea";
kaneliOmena.matoisuus = false;
syysOmena.vari = "vihreä";
syysOmena.maku = "hapan";
syysOmena.matoisuus = true;
//Tulostus
System.out.println("kaneliomena:");
kaneliOmena.tulostaAttribuutit();
System.out.println("syysOmena:");
syysOmena.tulostaAttribuutit();
}
}
Seuraavaksi toteamme, että suora viittaus olion oliomuuttujiin voidaan korvata metodilla, jolla asetetaan halutut arvot. Tälläisiä metodeja olisivat
void asetaVari(String vari1)
{
vari=vari1;
}void asetaMaku(String maku1)
{
maku=maku1;
}void asetaMatoisuus(Boolean matoisuus1)
{
matoisuus=matoisuus1;
}
Sulkujen sisällä olevaa arvoa (esim vari1) sanotaan parametriksi. Parametrien tulee olla samaa tyyppiä metodikutsussa ja luokan metodissa. Seuraavassa esimerkki parametrien välityksestä metodille
Esimerkki 33
class omena
{
String vari;
...
void asetaVari(String vari1)
{
vari=vari1;
}
...
public static void main(..)
{ String syysVari="punainen";
int omenoidenMaara = 20;
omena omppu = new omena();
...
omppu.asetaVari(syysVari); //Oikea parametrikutsu, koska parametrin tyypit tässä ja metodissa samoja
omppu.asetaVari(omenoidenMaara); //Väärä parametrikutsu, koska parametrin tyypit tässä int ja metodissa String
...
}
}
Halutessa viitata luokan metodissa luokan omaan muuttujaan tai luokan toiseen metodiin, voidaan käyttää this muuttujaa. Tällöin muodostetaan osoite sen olion metodiin, jota oltiin parhaillaan käsittelemässä. Esimerkiksi
this.maku = maku1;
this.vari = vari1;
Parametrien välityksessä metodikutsusta metodille joudumme ensimmäisen kerran tutustumaan käsitteeseen osoite. Merkitään laatikoilla edellisen esimerkin 32 muuttujia syysVari ja omenoidenMaara seuraavasti
Metodille välitetään siis osoite siihen muistipaikkaan, jossa syysVari:n arvo sijaitsee. Osoite on metodissa nimeltään vari1.
Metodikutsussa esiintyviä parametreja sanotaan todellisiksi parametreiksi ja metodissa olevia parametreja muodollisiksi parametreiksi.
omppu.asetaVari(syysVari); | metodikutsun todellinen parametri syysVari |
void asetaVari(String vari1) | metodin muodollinen parametri vari1 |
Kutsuttaessa jonkin olion metodia, pinotaan metodikutsu ja siinä olevat todelliset parametrit metodian paluuarvon kanssa pinoksi. Pinon alimpana on kutsuttavan metodin osoite ja palautusarvo, seuraavana ensimmäinen todellinen parametri, toinen todellinen parametri jne.. . Metodi saa tämän pinon huipun osoitteen ja ottaa ylimmäin parametrin osoitteen ja antaa sen viimeisen muodollisen parametrin osoitteeksi. Tämä toistetaan muodollinen parametri kerrallaan, kunnes pinon pohjalla on enää metodin paluuarvo. Kun metoidin suoritus päättyy, siirtyy toiminta metodia kutsuneeseen lauseeseen siten, että metodin palautusarvo jää metodikutsupinon pohjalle. Kutsuva ohjelma voi käyttää tätä arvoa halutessaan. Metodikutsupinon muistipaikat vapautuvat lopuksi muuhun käyttöön. Oheinen kuva selventäkööt tilannetta.
// Kutsuvassa ohjelmassa kirjoitetaan
String p1;
int p2;
float p3;
boolean p4;
olio.metodi(p1,p2,p3,p4);
...
// luokassa, jossa metodi sijaitsee, kirjoitetaan
palautusarvo metodi(String m1, int m2, float m3, boolean m4);
Havaitaan, että pinon parametrien osoitus ei onnistu, jos todellisten parametrien tyypit eivät täsmää muodollisten parametrien kanssa.
Esimerkissä 33 on kokonaisuudessaan ohjelma, jossa asetetaan metodin avulla omenan vari.
Esimerkki 34
class omena3
{
String vari;
void asetaVari(String vari1)
{
vari=vari1;
}
public static void main(String args[])
{ String syysVari="punainen";
omena3 omppu = new omena3();
omppu.asetaVari(syysVari);
System.out.println("ompun väri on nyt "+omppu.vari);
omppu.asetaVari("Vihreä"); // Näinkin onnistuu
System.out.println("ompun väri on nyt "+omppu.vari);
}
}
Nyt olemmekin valmiit kirjoittamaan uudestaan esimerkin 32 (käyttämällä myös this -muuttujaa)
Esimerkki 35
class omena3 {
String vari, maku; //luokan muuttujat
boolean matoisuus;
void asetaVari(String vari1)
{
this.vari=vari1;
}
void asetaMaku(String maku1)
{
this.maku=maku1;
}
void asetaMatoisuus(boolean matoisuus1)
{
this.matoisuus=matoisuus1;
}
void tulostaAttribuutit()
{
System.out.println(" Vari: " + this.vari + "\n Maku " +this.maku+ "\n Matoisuus " + this.matoisuus);
}
//
public static void main(String args[]) //tassa main -metodi ohjelman testaamista varten
{
omena3 kaneliOmena = new omena3();
omena3 syysOmena = new omena3();
kaneliOmena.asetaVari("punainen"); //kaneliOmena:lla on ominaisuudet vari, maku ja matoisuus
kaneliOmena.asetaMaku ("hapan");
kaneliOmena.asetaMatoisuus(false);
syysOmena.asetaVari("Keltainen"); //syysOmena:lla on ominaisuudet vari, maku ja matoisuus
syysOmena.asetaMaku ("makea");
syysOmena.asetaMatoisuus(true);
// Tulostetaan omppu1:n ominaisuuksia
System.out.println("kaneliOmena ");
kaneliOmena.tulostaAttribuutit();
System.out.println("syysOmena");
syysOmena.tulostaAttribuutit();
}
}
Metodi voi paitsi tehdä jotain luokan jäsenmuuttujille, myös palauttaa jonkin objektin ominaisuuden kutsuvalle ohjelmalle. Meillä on ollut tälläisiä käytössä jo aikaisemmin. Esimerkiksi String luokan length() -metodi palautti merkkien määrän merkkijonossa.
Palautusarvon tyyppi ilmoitetaan metodia kirjoitettaessa esittelylauseessa. Esimerkiksi
public String kerroVari() {}
ilmoittaa, että metodi palauttaa kutsuvalle ohjelmalle String -tyyppisen arvon.
Kokonaisuudessaan esimerkki olisi seuraavanlainen. Palautettavan muuttujan arvo asetetaan return -lauseella seuraavasti.
Esimerkki
//Esimerkkiin 35 voidaan kirjoittaa metodi kerroVari(), joka
palauttaa kutsuvaan ohjelmaan olion muuttujan vari arvon
String kerroVari() //String kertoo palautusarvon
tyypin
{
return
this.vari; // voi olla myös return
(vari);
}
// Tätä käytetään kutsuvassa ohjelmassa seuraavasti
String apu = syysOmena.kerroVari(); // tai
System.out.println(syysOmena.kerroVari());
Kirjoittamalla metodiin
return this
palautetaan koko objektin osoite kutsuvalle ohjelmalle.
Mikäli muuttuja määritellään metodin sisällä, siitä tulee lokaali muuttuja, joka näkyy vain metodille itselleen. Siihen ei siis voi viitata muualla kuin metodin aikana.
Esimerkki
void tulostaAttribuutit()
{
int i; \\ tässä i on lokaali muuttuja eikä siihen voi viitata missään muussa kohdassa ohjelmaa
for (i=1; i<10;i++)
System.out.println("Miksi taas tulostetaan attribuutteja! OMENATHAN MÄTÄNEVÄT KÄSIIN");
System.out.println(" Vari: " + this.vari + "\n Maku " +this.maku+ "\n Matoisuus " + this.matoisuus);
}
luokka | olion mallikappale, joka kopioidaan jäsenmuuttujineen ja metodeineen olioksi luotaessa uusi olio. Ei voi käyttää sellaisenaan tiedon talletukseen |
olio | luokasta otettu kopio, jota voi käyttää ohjelmassa tiedon talletukseen. |
jäsenmuuttuja | Tiettyyn luokkaan kuuluva muuttuja, jota ei voida sinällään käyttää tiedon tallettamiseen |
vakio | jäsenmuuttuja jonka edessä oleva muunnin on final |
luokkamuuttuja | jäsenmuuttuja, jonka edessä oleva muunnin on static (usein kutsutaan instanssimuuttujaksi) |
oliomuuttuja | luokasta luodun olion jäsenmuuttujia
sanotaan oliomuuttujiksi, joita voi käyttää tiedon talletukseen (paitsi vakioita) |
metodi | luokkaan kuuluva menetelmä, jolla voidaan operoida luokan jäsenmuuttujia |
palautusarvo | metodikutsun palautusarvo. void tarkoittaa "ei palautusarvoa" |
lokaali muuttuja | metodin sisäinen muuttuja, joka ei näy metodin ulkopuolelle |
this -muuttuja | Sallii olion viitata metodissaan saman olion toiseen metodiin tai muuttujaan |