Java on vahvasti oliopohjainen kieli. Javaa ei voi kirjoittaa joutumatta käsittelemään luokkia ja olioita, ja siten niiden toimintalogiikan täysi ymmärtäminen on ensiarvoisen tärkeää. Luokan ja olion lisäksi tärkeimpiä käsitteitä ovat niiden rakennusosat eli metodit ja attribuutit.
Olio (object) on jonkin luokan ilmentymä. Luokka
(class) määrittelee, mitä siitä muodostetut ilmentymät eli
instanssit voivat tehdä ja millaisia ominaisuuksia niillä on. Luokat
voidaan mieltää Platonin ideaopin mukaisiksi ideoiksi, joiden
ruumiillistumia ovat oliot.
Olio-ohjelmoinnissa yhdistetään ohjelmassa käsiteltävä tieto siihen, millaisia operaatioita sille voidaan suorittaa. Ihmisen ajattelun kannalta on usein helpompaa tarkastella ohjelmaa yhteistyötä tekevien olioiden kuin puhtaiden alkeistyyppien ja funktioiden, perinteisten ohjelmoinnin välineiden, muodostamina järjestelminä. Oliopohjainen ajattelu mahdollistaa laajojen asiakokonaisuuksien hahmottamisen yksityiskohdat piilottavien abstraktioiden avulla, ja on siten tärkeä suunnitteluväline. [1]
Luokan määrittely voi sisältää attribuutteja ja metodeja. Attribuutit määrittelevät, mitä olio voi tietää; ne ovat muuttujia, ja muodostavat siten tavan jolla yksi olio voi poiketa toisesta. Metodit kertovat, mitä toimintoja oliolla on ja miten ne suoritetaan.
Metodi on luokan sisällä määritelty nimetty koodilohko, jota voidaan kutsua muualta koodista luokan instanssin kautta. Metodille voidaan määrittää parametreja, jotka on annettava aina metodia kutsuessa. Niiden avulla metodia kutsuva koodi voi välittää metodille tietoa, jonka perusteella se voi toimia.
Attribuutti on luokan sisällä (muttei kuitenkaan metodissa) määritelty muuttuja. Kuten tavallisten muuttujien kohdalla, attribuuteilla voi olla oletusarvo, mutta sellaista ei ole pakko asettaa.
Javassa kaikilla ilmauksilla on hyvin määritelty tyyppi, ja
sellainen pitää määritellä myös attribuuteille, metodin parametreille ja
metodien paluuarvoille. Tyyppi on joko alkeistyyppi (esimerkiksi
totuusarvo tai kokonaisluku) tai luokka, jonka edustaja arvon tulee olla.
Metodien tapauksessa tyyppi voi olla myös tyhjä
(void), jolloin metodi ei palauta mitään.
class Tervehtija {
String tervehdys = "Hei";
void tervehdi(String nimi) {
System.out.println(this.tervehdys + ", " + nimi + "!");
}
}
Esimerkissä määritellään luokka Tervehtija, jonka
instanssit tietävät jonkin tervehdyksen ja osaavat tulostaa konsoliin
tervehdyksen kun niiden tervehdi-nimistä metodia kutsutaan
tervehdittävän nimen kanssa. Instanssin attribuuttia
käsitellään instanssiin itseensä viittaavan this-avainsanan
avulla.
Tervehtija tervehtija = new Tervehtija();
tervehtija.tervehdi("Teemu Teekkari");
Esimerkissä esitellään muuttuja tervehtija, luodaan uusi
Tervehtija-instanssi new-avainsanalla ja
sijoitetaan instanssi muuttujaan. Tämän jälkeen sen käsketään tervehtiä
Teemu Teekkaria.
Luokka tai olio ei välttämättä halua paljastaa kaikkia tietojaan muille, eikä muita vastaavasti välttämättä kiinnosta kaikki tämä tieto. Yleisesti luokan sisällä halutaan olla myös varmoja siitä, ettei attribuutin sisältö muutu yllättäen: mikäli attribuutti on julkinen, mikä tahansa taho jolla on pääsy instanssiin voi vapaasti muuttaa sen sisältöä. Vastaavasti julkista staattista attribuuttia voidaan muuttaa mistä tahansa käsin.
Ongelmia voi ilmetä myös silloin, kun luokan implementaatiota halutaan muuttaa. Kaikkia julkisia attribuutteja ja metodeja on voitu käyttää muualla sijaitsevassa koodissa, joten niiden poistaminen tai nimen vaihtaminen ei ole turvallista.
Tällaisten ongelmien vähentämiseksi Javassa on
näkyvyysmääreet, joilla voidaan määrittää mille tahoille
attribuutit ja metodit näkyvät. Tärkeimmät ja useimmin käytetyt ovat
public, joka antaa pääsyn kaikille sekä private,
joka estää käsittelyn muualta kuin luokasta itsestään.
Hyvänä käytäntönä pidetään sitä, että attribuutit ja metodit ovat vain niin julkisia kuin on ohjelman toiminnan kannalta välttämätöntä. [2] Tällöin luokkien sisäiset implementaatiodetaljit eivät ole ulkoa päin hämmästeltävissä ja luokkaa käyttävä koodi voi olla varma siitä, ettei voi sotkea luokan tai olion toimintaa tekemällä asioita, joita ei ole tarkoitus tehdä.
Näkyvyysmääreiden avulla voidaan esimerkiksi piilottaa attribuutti ulkoisilta tahoilta, mutta mahdollistaa sen lukeminen julkiseksi määritellyn saantimetodin avulla. Vastaavasti voidaan sallia myös muokkaaminen asetusmetodilla.
class Koira {
private String nimi;
public String annaNimi() {
return this.nimi;
}
public void asetaNimi(String nimi) {
this.nimi = nimi;
}
}
Mikäli Koiraa myöhemmin halutaan muokata siten, että sillä
on sekä etu- että sukunimi, voidaan saanti- ja asetusmetodeita muokata
ottamaan asia huomioon: jos muualla koodissa olisi käsitelty suoraan
nimi-attribuuttia, muutoksien tekeminen olisi vaikeampaa.
Jos olion luomistapahtuman yhteyteen tarvitaan jonkinlaista logiikkaa tai ulkopuolista tietoa, voidaan luokalle määritellä luontimetodi (constructor). Se on aina saman niminen kuin luokka itse, eikä sille julisteta paluuarvoa.
class Koira {
private String nimi;
public Koira(String nimi) {
this.nimi = nimi;
}
public String annaNimi() {
return this.nimi;
}
}
Esimerkissä määritellään Koira, jonka instanssia luodessa
on pakko antaa myös Koiran nimi. Ei siis epähuomiossa voida
luoda uutta Koiraa, jolla ei ole nimeä.
Sekä metodi että attribuutti voidaan julistaa luokkakohtaisiksi eli staattisiksi, jolloin niitä käytetään viittaamalla luokkaan eikä yksittäiseen siitä muodostettuun olioon. Tällöin attribuutin taltioima tieto on kaikille luokan instansseille yhteistä. Metodista taas pääsee käsiksi vain luokan muihin staattisiin metodeihin ja attribuutteihin.
class Koira {
private String nimi;
private static String vari = "ruskea";
public static String annaVari() {
return Koira.vari;
}
}
Esimerkissä määritellään Koiralle staattinen attribuutti
vari ja sen arvo sekä staattinen metodi, joka palauttaa
varin. Ei-staattista attribuuttia nimi ei voida
käsitellä annaVari-metodista.
Usein on mielekästä muodostaa luokista erikoistuneempia versioita, jotka osaavat samat asiat kuin alkuperäinen mutta tekevät ehkä jotakin ylimääräistä tai jotakin jo olemassa ollutta eri tavalla. Tällöin voidaan käyttää perintää (inheritance).
Yläluokan perivä lapsiluokka saa käyttöönsä kaikki yläluokan metodit ja
ominaisuudet, joita ei ole piilotettu yläluokassa asettamalla ne
yksityisiksi. Alaluokan instanssi voi siis toimia myös yläluokkansa tyypin
edustajana: esimerkiksi Koira on myös Elain. Jos
yläluokkaa ei erikseen ilmoiteta, peritään Javan
Object-luokka. Kaikki oliot ovat siis Objectin
edustajia.
class Elain {
private String nimi;
public Elain(String nimi) {
this.nimi = nimi;
}
public String annaNimi() {
return this.nimi;
}
}
class Koira extends Elain {
public void hauku() {
System.out.println("Woof!");
}
}
Esimerkissä määritellään Elain ja sen perivä
Koira. Kuten Elain, Koira tietää
nimensä ja osaa antaa sen, mutta osaa lisäksi haukkua.
On helppo erehtyä käyttämään perintää myös silloin, kun halutaan käyttää jossakin toisessa luokassa määriteltyjä ominaisuuksia hyödyksi luokassa, joka ei käsitteellisesti tai toiminnallisesti ole perityn luokan alatyyppi. Tällöin tulisi kuitenkin välttää perintää ja pyrkiä ratkaisemaan ongelma muulla tavalla. [3]
Jotta Java voisi suorittaa luodun ohjelman, täytyy sen tietää mistä
aloittaa. Koska ohjelmakoodia ei voi sijoittaa luokkien ulkopuolelle,
pitää ohjelman suorittavankin koodin olla luokan sisällä. Tarvitaan
pääohjelmametodi, joka Javassa on muotoa public static void
main(String[] args). Argumentiksi tulee merkkijonotaulukko, jonka
sisältönä ovat ohjelmaa kutsuttaessa annetut komentoriviargumentit.
class Koira {
public void hauku() {
System.out.println("Woof!");
}
public static void main(String[] args) {
Koira k = new Koira();
k.hauku();
}
}
Esimerkissä määritellään Koira ja pääohjelmametodi, joka
luo uuden Koiran ja käskee sen haukkua. Koira
siis muodostaa nyt valmiin ohjelman, jonka Java-virtuaalikone osaa
suorittaa.
[1]: John Paul Fullerton, "Benefits of Object-Oriented Design", 1998, Texas A&M University, USA, haettu 20.9.2008
[2]: "Controlling Access to Members of a Class", 2008, Sun Microsystems, USA, haettu 20.9.2008
[3]: Paul John Rajlich, "Object Composition vs. Inheritance", 1998, "Object Oriented Design", University of Illinois, USA, haettu 20.9.2008