Tarkastellaan tätä koodia:

public class Peli {
    public static void main(String[] args) {
        int pelihahmonX = 0;
        int pelihahmonY = 0;
        String pelihahmonNimi = "Waluigi";
        String pelihahmonKuva = "hahmo.png";

        piirraPelihahmo(pelihahmonX, pelihahmonY, pelihahmonNimi, pelihahmonKuva);
    }

    public void piirraPelihahmo(int hahmonX, int hahmonY, String nimi, String kuva) {
        piirraTeksti(nimi);
        piirraKuva(kuva, hahmonX, hahmonY);
    }
    // lisäksi piirraTeksti- ja piirraKuva-metodit
}

Ohjelmassa on aika paljon muuttujia, ja metodi piirraPelihahmo ottaa parametriksi neljä eri muuttujaa, mikä vaatii paljon kirjoittamista ja tekee koodista sekavaa. Vielä sekavampi on tilanne, jossa pelihahmoja on monta:

public class Peli {
    public static void main(String[] args) {
        int waluiginX = 0;
        int waluiginY = 0;
        String waluiginNimi = "Waluigi";
        String waluiginKuva = "waluigi.png";

        int peachinX = 200;
        int peachinY = 0;
        String peachinNimi = "Princess Peach";
        String peachinKuva = "princesspeach.png";

        int toadinX = 400;
        int toadinY = 50;
        String toadinNimi = "Toad";
        String toadinKuva = "toad.png";

        piirraPelihahmo(waluiginX, waluiginY, waluiginNimi, waluiginKuva);
        piirraPelihahmo(peachinX, peachinY, peachinNimi, peachinKuva);
        piirraPelihahmo(toadinX, toadinY, toadinNimi, toadinKuva);
    }
    // + samat metodit kuin viimeksi
}

Olisi paljon helpompaa, jos kaikki yksittäisen pelihahmon muuttujat saisi koottua yhdeksi muuttujaksi tähän tapaan:

public class Peli {
    public static void main(String[] args) {
        Pelihahmo waluigi = new Pelihahmo(0, 0, "Waluigi", "waluigi.png");
        Pelihahmo peach = new Pelihahmo(200, 0, "Princess Peach", "princesspeach.png");
        Pelihahmo toad = new Pelihahmo(400, 50, "Toad", "toad.png");

        piirraPelihahmo(waluigi);
        piirraPelihahmo(peach);
        piirraPelihahmo(toad);
    }

    public void piirraPelihahmo(Pelihahmo hahmo) {
        piirraTeksti(hahmo.nimi);
        piirraKuva(hahmo.kuva, hahmo.x, hahmo.y);
    }
    // samat piirraTeksti- ja piirraKuva-metodit
}

Näin voi tehdä olioiden ja luokan avulla!

Oliot ovat yhdistelmä muuttujia ja niihin liittyviä funktioita (metodeja). Luokassa määritellään tietynlaisen olion ominaisuudet (attribuutit/muuttujat) ja metodit.

Viime esimerkissä siis Pelihahmo on luokka, ja waluigi, peach ja toad ovat olioita.

Luokka on siis suunnitelma niistä ominaisuuksista, joita haluat tietyn tyyppisillä olioilla olevan. Pelihahmo-luokassa on määritelty, että Pelihahmo-oliolla on muuttujat x, y, nimi ja kuva. Luokan määrittelyn syntaksiin pääsemme kohta.

Olioiden sisältämiin muuttujiin pääsee käsiksi oliomuuttujan nimen kautta. Siis toad-olion nimen saa toad-muuttujasta näin: toad.nimi. toad.nimi-muuttujaa käytetään ihan samalla tavalla kuin toadinNimi-muuttujaakin.

Koodia voisi vielä selkeyttää se, jos piirraPelihahmo-metodia ei määriteltäisikään Peli-luokassa, vaan se liitettäisiin Pelihahmo-luokkaan. Annetaan metodille nimeksi piirra Pelihahmo-luokan nimen toistamisen välttämiseksi. Pelihahmo-luokkaan liitettyä piirra-metodia käytettäisiin näin:

public class Peli {
    public static void main(String[] args) {
        Pelihahmo waluigi = new Pelihahmo(0, 0, "Waluigi", "waluigi.png");
        Pelihahmo peach = new Pelihahmo(200, 0, "Princess Peach", "princesspeach.png");
        Pelihahmo toad = new Pelihahmo(400, 50, "Toad", "toad.png");
    
        waluigi.piirra();
        peach.piirra();
        toad.piirra();
    }
}

Oliot voivat sisältää minkä vain tyyppisiä muuttujia ja metodeja.

Esimerkki Kissa-luokasta ja siitä tehdyistä olioista:

luokka Kissa

Attribuutit:

  • nimi
  • ikä
  • omistaja
  • väri

Metodit:

  • annaNimi()
  • annaIkä()
  • sanoMiau()

olio tikru

Attribuutit:

  • nimi: "Tikru"
  • ikä: 3
  • omistaja: "Nea"
  • väri: "ruskea"

Metodit:

  • annaNimi()
    (palauttaa "Tikru")
  • annaIkä()
    (palauttaa 3)
  • sanoMiau()

olio mirri

Attribuutit:

  • nimi: "Mirri"
  • ikä: 5
  • omistaja: "Iida"
  • väri: "valkoinen"

Metodit:

  • annaNimi()
    (palauttaa "Mirri")
  • annaIkä()
    (palauttaa 5)
  • sanoMiau()

Esimerkki Koira-nimisen luokan käytöstä:

Koira musti = new Koira("Musti",5);
System.out.println("Koiran nimi: " + musti.nimi);
System.out.println("Koiran ikä: " + musti.ika);
musti.hauku();
musti.sanoNimesi();

Koodi tulostaa:

Koiran nimi: Musti
Koiran ikä: 5
Hau!
Olen Musti, hau!

Tällä oliolla on selvästikin attribuutit nimi ja ikä (ika). Lisäksi sillä on kaksi metodia nimeltä hauku() ja sanoNimesi(). On tärkeää tiedostaa, että olio ei ole lista, joka sisältää nimen ja iän, eikä myöskään merkkijono "Musti". Sen sijaan olio on yhdistelmä muuttujia ja funktioita. Olion metodit pystyvät käyttämään kyseisen olion attribuutteja, kuten sanoNimesi()-funktio tekee.

Konstruktori

Määritellään ihmistä kuvaava luokka Person:

class Person {
    public String name;
    public int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
}

Luokan määrittelyssä näkyy kaksi tämän luokan ominaisuutta, nimi ja ikä. Lisäksi luokkaan on määritelty erityinen metodi nimeltä konstruktori. Konstruktorin nimi on sama kuin luokan nimi ja sillä ei ole mitään palautusarvoa. Tämä metodi kutsutaan silloin kun luodaan uusi olio, ja usein siinä määritetään mitä luokasta tehdyn olion attribuutit (muuttujat) sisältävät. Esimerkiksi jos haluat, että Person-olion age-attribuutin alkuarvo on 0, voit laittaa kostruktoriin this.age = 0.

Konstruktori voi ottaa argumentteja tai olla ottamatta yhtään. Jos haluat antaa olion attribuuteille arvoja samalla kun olio luodaan, niin voin kirjoittaa konstruktorin, joka ottaa nämä arvot parametreiksi.

Konstruktorin sisällä näkyvä sana this viittaa siihen olioon, jonka attribuutteja muutetaan. Sitä pitää käyttää, jos funktiossa on joitain muita muuttujia tai parametrejä, jotka ovat saman nimisiä kuin olion attribuutit. Jos funktiossa ei ole tällaisia muuttujia, sitä ei ole pakko käyttää.

Esimerkkejä muunlaisista toimivista konstruktoreista:

public Person() {
    this.name = "Nimetön henkilö";
    this.age = 0;
}
public Person(String parametri, int toinen) {
    name = parametri;
    age = toinen;
}
public Person(String name) {
    this.name = name;
    age = 100;
}
public Person() {
    System.out.println("Luodaan uutta henkilöä...");
    name = "";
    age = 0;
}

Konstruktorin kutsuminen tapahtuu silloin, kun luodaan uusi olio. Uusi olio luodaan näin:

Person jenna = new Person("Jenna", 22);

Uutta oliota luodessa konstuktoria täytyy kutsua new-sanan kanssa. Tässä siis kutsutaan konstuktoria argumenteilla "Jenna" ja 22, jolloin luodaan Person-tyyppinen olio, jonka name-attribuutin arvo on "Jenna", ja age-attribuutin arvo on 22. Tämä olio tallennetaan muuttujaan jenna.

Getterit ja setterit

Usein olio-ohjelmoinnissa halutaan, että olion attribuutteja ei voi muokata olion omien metodien ulkopuolelta. Tämä saavutetaan vaihtamalla attribuuttien määre public määreeseen private, ja kirjoittamalla erikseen metodit, joilla attribuuttien arvoja pystyy tarkastelemaan tai muuttamaan. Tämä on erityisen hyödyllistä silloin, kun attribuuttien arvoksi ei saa laittaa mitä tahansa arvoa. Näitä metodeja sanotaan havainnointi- ja asetusmetodeiksi, tai gettereiksi ja settereiksi. Kirjoitetaan Person-luokan attribuuteille getterit ja setterit:

class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setName(String name) {
        this.name = name;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

Useimmiten getterien ja setterien nimien halutaan olevan muotoa [get/set][Attribuutin nimi], kuten ylläolevassa luokassa näkyy, mutta niiden nimien ei ole pakko olla tätä muotoa. Ne siis eroavat tässä konstruktorista, jonka nimi on pakko olla sama kuin luokan nimi. Gettereitä ja settereitä ei ole myöskään pakko tehdä. Ne ovat vain tavallisia metodeja, joita voit kirjoittaa tarpeen mukaan.