Java-koe tehtävä 1 Date: Sun, 09 Jan 2005 14:09:45 +0200 From: Olli Jokinen Tehtävä on arvosteltu kahdessa osassa (A ja B), joista molemmista sai 10pistettä. Molemmat osat on jaettu metodeihin, joiden pisteytys menee alla olevan listan mukaan. Erilaisista virheistä sakoitettiin, mutta kuitenkin niin, että osan (A tai B) pisteiden mennessä miinukselle, miinuspisteet eivät vaikuttaneet toisen osan arvosteluun. Arvostelussa käytettiin vähentävää laskutapaa, eli oletuksena jokaisesta kohdasta sai täydet pisteet ja pisteitä vähennettiin mahdollisten virheiden kohdalla. luokan/muuttujien/metodien määrittely 1p - kentät jotain muuta kuin private -2p - laskuri ei static -1p - tarina[]-taulukko final -1p public Tarina(String[] rivit) 2p - kopioitu rivit-taulukon viite, ei sisältöä -1p - tarinan rivejä ei ole kopioitu ollenkaan (ei viitettä tai sisältöä) -2p public int mikäOnTarinanKoodi() 1p public int mikäOnTarinanPituus() 1p - käytetty pituus-muuttujaa, ei otettu pituutta tarina-taulukon pituuden mukaan -1p public String mikäOnTarinanRivi(int i) 2p - virhekäsittely (+ selitykset) puuttuu -2p - väärät virhe-ehdot -1p - ei pääty jokaisessa haarassa returniin -1p public void tulostaTarina() 1p - väärä for-ehto -1p public Tarina koosta(Tarina toinen) 2p luokan/muuttujien/metodien määrittely 1p - ei ole määritelty aliluokaksi -1p public KommentoituTarina(String[] rivit) 2p - super(rivit) puuttuu -1p public void liitäKommentti(String kommentti, int rivi) 2p - virhetarkistus puuttuu -2p - väärät virhe-ehdot -1p public String mikäOnKommentti(int rivi) 2p - virhetarkistus puuttuu -2p - väärät virhe-ehdot -1p public void tulostaKommentit() 1p - ei null-tarkastusta -1p public KommentoituTarina koosta(KomentoituTarina toinen) 2p - luodaan turhia Tarina-olioita -2p - luotu palautettava tarina super.koosta()-metodilla -2p - käytetty super.koosta()-metodia apuna luomisessa -1p Muita virheitä: - B-kohdassa on viitattu yläluokan privaattikenttiin -2p - B-kohdassa koodattu yläluokan kalustoa uusiksi -1-5p - metodeihin otsikoihin on lisätty throws Exception (huom, metodit saavat silti heittää virhetilanteissa poikkeuksia) -1p - satunnaiset ohjelmointivirheet -1p/kpl Yleisin virhe tehtävässä oli Tarina-luokan konstruktorissa pelkän rivit-taulukon viitteen kopiointi. Yleisesti metodeista oli unohdettu myös parametrien tarkistus tai tarkistus oli virheellinen. Kannattaa olla tarkkana taulukon ala- ja ylärajan kanssa. Eli seuraavat tavat ovat oikein: if(i >= 0 && i < taulukko.pituus) { return taulukko[i]; } else { return null; } if(i < 0 || i >= taulukko.pituus) { return null; } else { return taulukko[i]; } Varsinkin alempaa tapaa käytettäessä yhtäsuuruusmerkki oli usein unohtunut. Myös metodien virheenkäsittelyyn kannattaa kiinnittää huomiota. Useissa papereissa esimerkiksi Stringin palauttavat metodit saattoivat käsitellä virheen vain tulostamalla System.outiin virheilmoituksen tai palauttamalla "Riviä ei löydy" tms String-olion. Jos metodin on tarkoitus palauttaa olio, on null-viitteen käyttäminen hyvä ja riittävä tapa ilmaista virhe. Myös poikkeuksia voi käyttää, mutta niitä ei tässä tehtävässä edelletetty. Tehtävä meni kuitenkin kokonaisuudessaan hyvin. Vastanneista selkeästi suurin osa sai 10 pistettä tai enemmän ja 20 pisteen suorituksiakin oli useita. Ja vielä esimerkkikoodit: /** * Java-ohjelmointi syksy 2004, * Helsingin yliopisto, Tietojenkäsittelytieteen laitos * Koetehtävä 1A (Tarina), esimerkkivastaus. * * @author Olli Jokinen */ public class Tarina { private static int koodi = 1; private final int TARINAN_KOODI; private String[] tarina; /** * luo Tarina-olion ja sen yksikäsitteisen koodinumeron */ public Tarina(String[] rivit) { this.TARINAN_KOODI = koodi; koodi++; // HUOM, kopioidaan taulukon sisältö, ei viitettä this.tarina = new String[rivit.length]; for(int i = 0; i < rivit.length; i++) { this.tarina[i] = rivit[i]; } } /** * palauttaa tarinan yksikäsitteisen koodinumeron */ public int mikäOnTarinanKoodi() { return this.TARINAN_KOODI; } /** * palauttaa tarinan rivien lukumäärän */ public int mikäOnTarinanPituus() { return this.tarina.length; } /** * palauttaa arvonaan tarinan rivin, jonka numero on i */ public String mikäOnTarinanRivi(int i) { if(i < 0 || i >= this.tarina.length) { // Virheellisen parametrin kohdalla palautetaan // null-viite. Hyväksyttävää (ja jopa toivottavaa, // koska mikään ei estä tarinan riviä olemasta null) // on aiheuttaa poikkeus virheellisen parametrin // kohdalla. return null; } return this.tarina[i]; } /** * tulostaa koko tarinan rivinumeroilla varustettuna */ public void tulostaTarina() { for(int i=0; i < this.tarina.length; i++) { // tässä 0 on mahdollinen rivinumero, rivinumerot // voivat alkaa myös numerosta 1 System.out.println((i) + ": " + this.tarina[i]); } } /** * palauttaa arvonaan uuden Tarina-olion, jossa tarina * muodostuu this-tarinasta, jonka perään on liitetty * parametrina annettu toinen tarina. */ public Tarina koosta(Tarina toinen) { // oletetaan, että toinen tarina ei ole null int tarinanPituus = this.mikäOnTarinanPituus() + toinen.mikäOnTarinanPituus(); String[] temp = new String[tarinanPituus]; for(int i = 0; i < this.mikäOnTarinanPituus(); i++) { temp[i] = this.mikäOnTarinanRivi(i); } for(int i = 0; i < toinen.mikäOnTarinanPituus(); i++) { temp[this.mikäOnTarinanPituus() + i] = toinen.mikäOnTarinanRivi(i); } return new Tarina(temp); } public static void main(String[] args) { // testataan luokkaa String[] testi = new String[]{"ensimmäinen", "toinen", "kolmas"}; Tarina testiTarina = new Tarina(testi); System.out.println("Tarinan koodi: "+testiTarina.mikäOnTarinanKoodi()); System.out.println("Tarinassa on rivejä: "+testiTarina.mikäOnTarinanPituus()); testiTarina.tulostaTarina(); String[] testi2 = new String[]{"neljäs", "viides"}; Tarina testiTarina2 = new Tarina(testi2); System.out.println("Tarinan koodi: "+testiTarina2.mikäOnTarinanKoodi()); testiTarina2.tulostaTarina(); Tarina yhdistelmäTarina = testiTarina.koosta(testiTarina2); System.out.println("Tarinan koodi: "+yhdistelmäTarina.mikäOnTarinanKoodi()); yhdistelmäTarina.tulostaTarina(); } } /** * Java-ohjelmointi syksy 2004, * Helsingin yliopisto, Tietojenkäsittelytieteen laitos * Koetehtävä 1B (KommentoituTarina), esimerkkivastaus. * * @author Olli Jokinen */ public class KommentoituTarina extends Tarina { private String[] kommentit; /** * luo KommentoituTarina-olion, jossa mitään riviä ei * vielä ole kommentoitu */ public KommentoituTarina(String[] rivit) { super(rivit); kommentit = new String[super.mikäOnTarinanPituus()]; } /** * liittää annetun kommentin tarinan annettuun riviin, * mahdollinen vanha kommentti korvataan tällä uudella */ public void liitäKommentti(String kommentti, int rivi) { if(rivi < 0 || rivi >= this.kommentit.length) { // Virheellinen rivinumero, ei tehdä mitään. // Voitaisiin heittää myös poikkeus. return; } this.kommentit[rivi] = kommentti; } /** * palauttaa arvonaan tarinan i:nnen rivin kommentin (tai * arvon null, jos riviin ei liity kommenttia) */ public String mikäOnKommentti(int rivi) { if(rivi < 0 || rivi >= this.kommentit.length) { // kts. Tarina-luokan mikäOnTarinanRivi(int i) return null; } return this.kommentit[rivi]; } /** * tulostaa rivinumeroin kaikki ei-tyhjät kommentit, * so. kommenttiluettelon */ public void tulostaKommentit() { for(int i = 0; i < this.kommentit.length; i++) { if(this.mikäOnKommentti(i) != null) { System.out.println(i + ": " + this.mikäOnKommentti(i)); } } } /** * palauttaa arvonaan uuden KommentoituTarina-olion, * jossa tarina muodostuu this-tarinasta, jonka perään * on liitetty parametrina annettu toinen tarina. Uusi * kommentoitu tarina saa tietenkin myös vanhojen * tarinoiden kommentit! */ public KommentoituTarina koosta(KommentoituTarina toinen) { // oletetaan, että toinen tarina ei ole null int tarinanPituus = this.mikäOnTarinanPituus() + toinen.mikäOnTarinanPituus(); String[] tarinat = new String[tarinanPituus]; for(int i = 0; i < this.mikäOnTarinanPituus(); i++) { tarinat[i] = this.mikäOnTarinanRivi(i); } for(int i = 0; i < toinen.mikäOnTarinanPituus(); i++) { tarinat[this.mikäOnTarinanPituus() + i] = toinen.mikäOnTarinanRivi(i); } KommentoituTarina palautettava = new KommentoituTarina(tarinat); for(int i = 0; i < this.mikäOnTarinanPituus(); i++) { // liitetään 1. tarinan kommentit palautettava.liitäKommentti(this.mikäOnKommentti(i), i); } for(int i = 0; i < toinen.mikäOnTarinanPituus(); i++) { // liitetään 2. tarinan kommentit palautettava.liitäKommentti(toinen.mikäOnKommentti(i), i + this.mikäOnTarinanPituus()); } return palautettava; } public static void main(String[] args) { // testataan luokka String[] testi = new String[]{"1. rivi", "2. rivi"}; KommentoituTarina testiTarina = new KommentoituTarina(testi); System.out.println("Tarinan koodi: "+testiTarina.mikäOnTarinanKoodi()); testiTarina.liitäKommentti("Superlatiivit eivät riitä kuvaamaan tätä riviä.", 0); testiTarina.liitäKommentti("Ai onko tässä kolmaskin rivi?", 2); // ei tee mitään testiTarina.tulostaKommentit(); String[] testi2 = new String[]{"3. rivi", "4. rivi", "5. rivi"}; KommentoituTarina testiTarina2 = new KommentoituTarina(testi2); System.out.println("Tarinan koodi: "+testiTarina2.mikäOnTarinanKoodi()); testiTarina2.liitäKommentti("Mitähän kirjoittaja hakee tällä?", 0); testiTarina2.liitäKommentti("Kirjoittaja näyttää menettäneen otteen tekstistä lopullisesti.", 1); testiTarina2.liitäKommentti("Hmm, onko tämä joku vitsi?", 2); testiTarina2.liitäKommentti("Tekotaiteellista, joutaa roskiin.", 2); testiTarina2.tulostaKommentit(); KommentoituTarina yhdistelmäTarina = testiTarina.koosta(testiTarina2); System.out.println("Tarinan koodi: "+yhdistelmäTarina.mikäOnTarinanKoodi()); yhdistelmäTarina.tulostaTarina(); yhdistelmäTarina.tulostaKommentit(); } }