Keele võimalused
Arhiivid, programne koodi uuring ja testimine.
Jar-arhiivid
Kuude ja aastatega koguneb programmilõike, millest ka uute rakenduste
koostamisel kasu on. Lühematel juhtudel saab need uue valmiskoodi sisse kopeerida, kuid
nõnda lähevad kapseldumisest saadavad hüved kaduma. Avastanud kord lähtefailis vea,
tuleb ka kõik muud lõigud läbi käia, kus sama koodijupiga tegemist on. Kui kõik
programmid kirjutatakse ühes kataloogis, siis pole suurt muret – klassi nime kaudu saab otse
ka koodile ligi. Vähegi suurema programmeerimise korral tuleb koodifaile aga nii hulganisti,
et neid üheskoos hoides läheks pilt väga kirjuks ning samuti ei õnnestuks eri ülesannete tarvis
loodud koodilõike lahus hoida.
Edaspidi mitmel pool kasutamist leidvate klasside failid võib kokku pakkida ühte
Jar-arhiivi, mida siis vajadusel mujalgi tarvitada annab. Näitena klass meetodiga nimega tühja
raami avamiseks:
C:\kodu\jaagup\0108\k1>type ArhiiviKlass.java
import java.awt.*;
public class ArhiiviKlass{
public static void avaRaam(String nimi){
Frame f=new Frame(nimi);
f.setSize(200, 200);
f.setVisible(true);
}
}
ning teine klass loodud meetodi katsetamiseks.
public class ArhiiviKasutaja{
public static void main(String argumendid[]){
ArhiiviKlass.avaRaam("Tervitusaken");
}
}
Traditsioonilisel moel kompileerides ja käivitades ongi tulemuseks avatud aken.
C:\kodu\jaagup\0108\k1>javac Arhiivi*.java
C:\kodu\jaagup\0108\k1>java ArhiiviKasutaja
Soovides aknaavamismeetodiga klass arhiivi paigutada, tuleb anda korraldus
C:\kodu\jaagup\0108\k1>jar cf ArhiiviKlassid.jar ArhiiviKlass.class
Selle tulemusena luuakse uus arhiiv nimega ArhiiviKlassid.jar. Võti c tähendab uue arhiivifaili
loomist (create), f – failinime. Kui sellenimeline arhiiv oleks olemas olnud, siis see kirjutataks
üle.
Nüüd võib kompileeritud klassi lahtipakitud kuju maha kustutada,
C:\kodu\jaagup\0108\k1>del ArhiiviKlass.class
sest andmeid saab ka failist lugeda.
C:\kodu\jaagup\0108\k1>java -cp ArhiiviKlassid.jar;. ArhiiviKasutaja
Semikoolon ning punkt arhiivi nime järel tähendab, et klasside otsinguteele pannakse lisaks
arhiivile ka jooksev kataloog. Muidu jääks käivitamiseks tarvilik ArhiiviKasutaja leidmata,
sest seda arhiivis pole.
C:\kodu\jaagup\0108\k1>java -cp ArhiiviKlassid.jar ArhiiviKasutaja
Exception in thread "main" java.lang.NoClassDefFoundError: ArhiiviKasutaja
Kui loodud arhiivi läheb mitmel pool vaja, siis võib selle paigutada kohta, kust
otsitav klass kogu virtuaalmasina piires üles leitakse. Kohaks on Java installeerimiskohast
alanev kataloogipuu jre\lib\ext.
Kui nüüd raamiloomiskäsku vajatakse
C:\kodu\jaagup\0108\k1>java ArhiiviKasutaja
, siis leitakse see vabalt ligi pääsetavast kataloogist paiknevast arhiivist välja ning tulemusena
võib jälle loodud raami näha.
Arhiivi võib panna ka käivitatava klassi.
C:\kodu\jaagup\0108\k1>jar cf ArhiiviKlassid.jar Arhiivi*.class
Nii võib vajaduse korral ka võõras masinas oma arhiivi sees paikneva programmi
välja kutsuda.
C:\kodu\jaagup\0108\k1>java -cp ArhiiviKlassid.jar ArhiiviKasutaja
Arhiivist on kasu ka veebilehe puhul. Atribuudiga archive saab määrata, millisest
failist andmed tõmmata. Sel juhul pääseb brauser hulga üksikute klasside kohale sikutamisest
ning lehe laadimine võib vähem aega võtta.
Arhiivirakend
Rakend arhiivist
import java.applet.Applet;
public class ArhiiviRakend extends Applet{
public ArhiiviRakend(){
ArhiiviKlass.avaRaam("Rakendi aken");
}
}
Paketid
Üle maailma luuakse Java klasse hulgaliselt ning üsna kindlasti satuvad mõnede
nimed nendest kokku. Mõistlikke arusaadavaid nimetusi on lihtsalt piiratud hulk. Selle tarvis
on Java keelde loodud objektidest/klassidest veel ühe taseme võrra kõrgem grupp: paketid.
Standardpakettidest oleme kindlasti kasutanud java.lang-i, java.io või java.awt vahendeid.
Selliseid klasside komplekte aga saab rahus ise juurde luua. Põhikomplekti kuuluvad paketid
on pandud algama nimega java, põhikomplekti laiendused sõnaga javax. Ülejäänud
pakkettide nime algusse aga soovitatakse paigutada neid välja töötava kompanii
veebiaadress. Pea igal pakette looval asutusel või isikul on oma väljund veebis ning juba
nimede jagamisel hoolitsetakse selle eest, et sama nime alla mitut omanikku ei satuks.
Domeeni sees aga tuleb loojatel juba isekeskis hoolitseda, et nimed kattuma ei hakkaks.
Nõnda panen pedagoogikaülikoolis loodud paketi nimeks veebiaadressi järgi ee.tpu.
Failisüsteemis paketi nimed kattuvad kataloogi nimega, nõnda tuleb siis loodud failid
paigutada kompileerimis/käivituskataloogi alamkataloogi ee\tpu.
C:\kodu\jaagup\0108\k1>type ee\tpu\Tervitaja.java
package ee.tpu;
public class Tervitaja{
public static void tervita(){
System.out.println("Tervist");
}
}
Faili algusse tuleb kirjutada, millise paketi klassiga tegemist on. Et paketist kasutatakse
sageli klasse mõne muu programmi töö tarbeks, siis ka siinses näites pole main-meetodit,
kust töö käima võiks minna. Nii nagu java.awt.Button'it võime oma töösse lisada, nii saab
ka vastloodud klassi oskusi oma hüvanguks kulutada.
C:\kodu\jaagup\0108\k1>type Paketikatse.java
import ee.tpu.*;
public class Paketikatse{
public static void main(String argumendid[]){
Tervitaja.tervita();
}
}
Faili alguses import-lause hoolitseb, et seal paketis asuvatele klassidele mugavalt ligi
pääseks. Programm läheb tööle nagu tavaline muugi Java rakendus.
C:\kodu\jaagup\0108\k1>javac Paketikatse.java
C:\kodu\jaagup\0108\k1>java Paketikatse
Tervist
Käivituva klassi võib ka paketi sisse panna.
C:\kodu\jaagup\0108\k1> type ee\tpu\Alustus.java
package ee.tpu;
public class Alustus{
public static void main(String argumendid[]){
Tervitaja.tervita();
}
}
Sellisel juhul saab paketile ligi pääsedes mugavalt seal paikneva programmi käivitada.
C:\kodu\jaagup\0108\k1>java ee.tpu.Alustus
Tervist
Paketi klassid võib lahedasti üheks arhiiviks kokku pakkida.
C:\kodu\jaagup\0108\k1>jar cf tekstipakett.jar ee\tpu\*.class
Sellisena piisab installeerimiseks vaid ühe faili sobivasse kohta kopeerimisest ning töö võibki
alata.
C:\kodu\jaagup\0108\k1>java -cp tekstipakett.jar ee.tpu.Alustus
Tervist
Kui tahta arhiivile üle kogu virtuaalmasina ligi pääseda, siis võib selle kopeerida
kättesaadavasse jre\lib\ext kataloogi nagu eelmiseski näites.
Soovides oma Java-programmi võõrasse masinasse paigutada, peab omanik enamasti
kopeerima sinna hulga faile ning lisaks teadma, millise klassi käivitamisel kogu lugu tööle
hakkab. Jar-faili mainfest-osas saab määrata, millise klassi main-meetodist programmi
käivitamist alustada tuleb.
C:\kodu\jaagup\0108\k1>type lisateave.txt
Main-Class: ee.tpu.Alustus
Kui arhiivi loomisel manifest tekstifailist lisada,
C:\kodu\jaagup\0108\k1>jar cmf lisateave.txt tekstipakett.jar ee\tpu\*.class
siis käivitamisel pannaksegi arhiiv niimoodi tööle, kuidas programmi kirjutaja seda soovinud
on.
C:\kodu\jaagup\0108\k1>java -jar tekstipakett.jar
Tervist
Kes on Jar-failile assotsiatsiooni loonud (või on see vaikimisi tehtud), et käivitamisel
lükatakse tööle java intepretaator jar-võtmega ning parameetriks antakse jar-arhiivi nimi, siis
tundubki, et tegemist on isekäivituva jar-failiga.
Erindid
Veidi seletusi erindite loomise ja kasutamise kohta.
Probleemist teada andmiseks võime soovitud kohas välja heita erindi. Järgnevad käsud
jäetakse täitmata kuni erind lendab virtuaalmasinast välja või püütakse kinni. Üldjuhul tuleb
meetodi päises näidata throws-teatega, kui meetodist võib erindeid välja tulla.
public class Erind5{
public static void main(String argumendid[]) throws Exception {
int vanus=8;
if(vanus<10)throw new Exception("Liiga noor");
System.out.println("Tere tulemast pikamaajooksule");
}
}
väljund
Exception in thread "main" java.lang.Exception: Liiga noor
at Erind5.main(Erind5.java:4)
vastab täiesti ootustele. Klassi Erind4 neljandal koodireal saadeti lendu erind ning
edaspidised käsud jäid täitmata.
Kui soovitakse heita erind, mille tekkimist ei pea deklareerima, siis tuleb kasutada
RuntimeExceptioni või selle alamklassi. Enamjaolt kuuluvad RuntimeExceptioni alla
eriolukorrad, mis võivad ette tulla väga paljudes kohtades (nt. jagamine nulliga, massiivi
piiride ületamine) ning mida deklareerides peaks peaks siis pea kõikide meetodite juurde
deklaratsioonid kirjutama. Nagu näha, siin kasutatakse RuntimeExceptioni ning main-
meetodis pole tarvis üles tähendada, et throws Exception või throws RuntimeException.
Samas kui sinna see siiski kirjutada, siis probleeme sellest ei tekiks.
public class Erind6{
public static void main(String argumendid[]) {
int vanus=8;
if(vanus<10)throw new RuntimeException("Liiga noor");
System.out.println("Tere tulemast pikamaajooksule");
}
}
Jällegi väljund vastavalt ootustele
Exception in thread "main" java.lang.RuntimeException: Liiga noor
at Erind6.main(Erind6.java:4)
, neljandal real avastati, et pikema jooksmise tarvis on vanust liialt vähe.
Omaloodud erind
Kui soovime selliseid eriolukordi teada anda ja nendele reageerida, milliseid
standardvahendites kirjas pole, siis võime luua oma erindialamklassi. Sellisel võime kergemini
reageerida vastavalt tekkinud probleemile. Ka erindi väljatrükil näidatakse, millisest klassist
erind pärit on.
class VanuseErind extends Exception{}
public class Erind7{
public static void main(String argumendid[]) throws VanuseErind{
int vanus=8;
if(vanus<10)throw new VanuseErind();
System.out.println("Tere tulemast pikamaajooksule");
}
}
/* väljund:
Exception in thread "main" VanuseErind
at Erind7.main(Erind7.java:4)
*/
Soovides erindi kinnipüüdjale anda selgituse probleemi kohta, on üheks võimaluseks
katta üle erindi meetod getMessage().
class VanuseErind2 extends Exception{
public String getMessage(){
return "Vanus ei sobinud";
}
}
public class Erind7a{
public static void main(String argumendid[]) throws Exception{
int vanus=8;
if(vanus<10)throw new VanuseErind2();
System.out.println("Tere tulemast pikamaajooksule");
}
}
Nõnda jõuab koodi sisse kirjutatud selgitus veateatena ekraanile või võidakse seda
muul moel veatöötluses arvestada.
Exception in thread "main" VanuseErind2: Vanus ei sobinud
at Erind7a.main(Erind7a.java:4)
Kõige viisakam võimalus programmi sees omaloodud erindile teadet kaasa panna on
luua erindile uus sõneparameetriga konstruktor mis omakorda ülemklassi vastava
konstruktori välja kutsub. Ülemklass hooliseb, et getMessage teate välja annaks. Et loodud
klassi veel omakorda ilusti laiendada annaks, tuleks siia ka parameetriteta konstruktor
kirjutada, mis ülemklassi parameetriteta konstruktori välja kutsuks. Põhjus selles, et vaikimisi
kutsutakse uue isendi loomisel alati välja ülemklassi parameetriteta konstruktor, kui parajasti
käivitatava konstruktori esimese käsuna pole välja kutsutud mõni muu konstruktor. Kui
juhtub aga, et ülemklassil parameetriteta konstruktorit pole ning mõnda muud ka välja ei
kutsuta, siis antakse veateade. Siinse näite VanuseErind3 juures on parameetriteta
konstruktori kirjutamisest loobutud, arvates et sellest erindist võib luua vaid programmeerija
määratud teatega isendeid ning et teateta alamklasse siia enam ei looda.
class VanuseErind3 extends Exception{
VanuseErind3(String teade){
super(teade);
}
}
class Erind7b{
static void main(String argumendid[]) throws Exception{
int vanus=8;
if(vanus<10)throw new VanuseErind3("Pole veel 10. aastane");
System.out.println("Tere tulemast pikamaajooksule");
}
}
/* väljund:
Exception in thread "main" VanuseErind3: Pole veel 10. aastane
at Erind7b.main(Erind7b.java:4)
*/
Lõpuplokk finally
Lisaks try-le ning cactch-i(de)le võib katsendile lisada finally-ploki, mis täidetakse
sõltumata sellest, kas uuritavas piirkonnas tekkis probleeme või kas neid töödeldi. Sinna
kirjutatakse enamasti käsud, mis tuleb alati täita. Näiteks voogude sulgemine või
andmebaasiühenduse katkestamine, mis tuleb ressursside vabastamiseks ikka läbi viia,
ükskõik, kas saabuvad andmed vastasid ootustele või mitte. Järgnevates näidetes saadetakse
lõpuplokis tervitused kõigile kohalolijatele sõltumata sellest kui noored või vanad nad
parajasti on. Samuti tegevus, mis oleks patt ära jätta.
class Erind8{
static void main(String argumendid[]) throws VanuseErind{
int vanus=8;
try{
if(vanus<10)throw new VanuseErind();
System.out.println("Tere tulemast pikamaajooksule");
} catch(VanuseErind v){
v.printStackTrace();
}
finally {
System.out.println("Tervitus igale kohalolijale");
}
}
}
Kõigepealt püüti catch-osas erind kinni ning trükiti selle andmed, edasi tervitati finally-plokis
kohalolijaid.
/* väljund:
VanuseErind
at Erind8.main(Erind8.java:5)
Tervitus igale kohalolijale
*/
Ka juhul, kui katsendiplokis püütakse return-i abil meetodist väljuda, ei pääse lõpuploki
käivitamisest.
public class Erind8a{
public static void main(String argumendid[]){
int vanus=78;
try{
if(vanus>70)return; //püütakse meetodist väljuda;
if(vanus<10)throw new VanuseErind();
System.out.println("Tere tulemast pikamaajooksule");
} catch(VanuseErind v){
v.printStackTrace();
}
finally {
System.out.println("Tervitus igale kohalolijale");
}
}
}
/* väljund:
Tervitus igale kohalolijale
*/
Kui kõik tingimused sobivad ning probleeme ei teki, ka siis täidetakse lõpuplokk.
class Erind8b{
static void main(String argumendid[]){
int vanus=27;
try{
if(vanus<10)throw new VanuseErind();
System.out.println("Tere tulemast pikamaajooksule");
} catch(VanuseErind v){
v.printStackTrace();
}
finally {
System.out.println("Tervitus igale kohalolijale");
}
}
}
/* väljund:
Tere tulemast pikamaajooksule
Tervitus igale kohalolijale
*/
Erindi võib pärast töötlemist edasi saata sarnaselt throw käsuga nagu algselgi juhul.
Sellist moodust läheb vaja, kui samale probleemile tuleb reageerida mitmes kohas.
public class Erind9{
public static void main(String argumendid[]) throws VanuseErind{
int vanus=8;
try{
if(vanus<10)throw new VanuseErind();
System.out.println("Tere tulemast pikamaajooksule");
} catch(VanuseErind v){
v.printStackTrace();
throw v;
}
}
}
/* väljund:
VanuseErind
at Erind9.main(Erind9.java:5)
Exception in thread "main" VanuseErind
at Erind9.main(Erind9.java:5)
*/
Ka siis ei pääse finally-ploki täitmisest.
public class Erind9a{
public static void main(String argumendid[]) throws VanuseErind{
int vanus=8;
try{
if(vanus<10)throw new VanuseErind();
System.out.println("Tere tulemast pikamaajooksule");
} catch(VanuseErind v){
v.printStackTrace();
throw v;
} finally {
System.out.println("Tervitus igale kohalolijale");
}
}
}
/* väljund:
VanuseErind
at Erind9a.main(Erind9a.java:5)
Tervitus igale kohalolijale
Exception in thread "main" VanuseErind
at Erind9a.main(Erind9a.java:5)
*/
Kloonimine
Objektide puhul rõhutatakse kolme eristatavat omadust: tüüp, andmed ja identiteet.
Tüübi alla kuuluvad klassi kirjeldamisel loodud käsklused ehk meetodid ning väljade ehk
muutujate loetelu. Andmed on igal objektil omad: olgugi et mõlemad isendid võivad olla
tüübist Punkt, nende x-i ja y-i väärtused on üldjuhul erinevad. Ning isegi, kui mõne punkti
koordinaadid peaksid teise punkti koordinaatidega kattuma, ei ole tegemist sama objektiga,
sest neil on erinev identiteet.
Soovides isendist luua koopiat, tuleb teha koopia kõigist tema väljadest. Üheks
võimaluseks on koostada koopia loomiseks eraldi käsklus. Seal luua konstruktori abil uus
sama tüüpi isend. Anda käskude ja parameetrite abil uuele isendile soovitud väärtused ning
siis returni abil tagastada osuti uuele objektile.
Vaeva vähendamiseks on loodud liides Cloneable, mille abil on lubatud objektidest
luua "mehhaaniline koopia". See tähendab, et uude koostatavasse objekti kantakse üle
lihtsalt kõik vana objekti väljade väärtused. Vastava toimingu läbiviijana saab kasutada
kõikide klasside ülemklassi Object protected-juurdepääsuga käsklust nimega clone, mis
loobki just uue objekti ja kopeerib sinna kõikide väljade väärtused.
class Kloon1 implements Cloneable{
String nimi;
int mass;
public Object clone(){
Object o=null;
try{
o=super.clone();
}catch(CloneNotSupportedException e){}
return o;
}
}
Et liides Cloneable määrab meetodi clone tagastustüübiks Object, siis tegelikuks
kasutamiseks tuleb uus eksemplar tüübimuundusega sobivasse tüüpi ehk algse isendiga
samasse tüüpi määrata.
Järgnevalt võibki näha, kuidas algsest eksemplarist kloon luuakse nõnda, et kõik
väärtused samaks jäävad, kuid nendele eraldi mälupesad eraldatakse. Ning kuna pärast
kloonimist on tegemist eraldi objektidega, siis massi muutmine ühe isendi juures ei muuda
teise isendi massi.
public class Kloon1Test{
public static void main(String[] argumendid){
Kloon1 k1=new Kloon1();
k1.nimi="Dolly";
k1.mass=80;
Kloon1 k2=(Kloon1)k1.clone();
k2.mass=90;
System.out.println(k1.mass+" "+k2.mass);
}
}
/*
D:\kodu\0312>java Kloon1Test
80 90
*/
Kui klassil on Cloneable-liidese kaudu kord juba kloonimine lubatud, siis edasi on
ka kõik vastava klassi alamklasside isendid kloonitavad. Et Kloon1 sai eelmises näites
kloonitavaks muudetud, siis vastav õigus kehtib ka Kloon3-e isendite kohta.
class Kloon3 extends Kloon1{
int vanus;
}
Ehkki nüüd kloonitakse isendit tüübist Kloon3, osatakse ülemklassi meetodite
väljakutse abil ikkagi kõikidest väljadest koopiad teha.
public class Kloon3Test{
public static void main(String[] argumendid){
Kloon3 k=new Kloon3();
k.mass=60;
k.vanus=2;
Kloon3 k2=(Kloon3)k.clone();
System.out.println(k2.mass+" "+k2.vanus);
}
}
/*
D:\kodu\0312>java Kloon3Test
60 2
*/
Süviti kloonimine.
Lihttüüpidega on asi selge: kui isendil olid väljad ning nendel väärtused, siis
kloonimise puhul loodi uus isend, kus iga välja jaoks oli loodud uus mälupesa. Välja
mälupesal paiknev väärtus ongi selle välja väärtus.
Struktuurtüüpidega on lugu keerulisem. Taoline väli võib viidata
andmebaasiühendusele, avatud aknale või näiteks failile kusagil kaugel Internetis. Kui isendil
ka on vastav väli küljes, siis välja kloonimine ei tee veel eraldi asetsevat objekti juurde.
Tulemuseks on lihtsalt mõlema isendi küljes paiknevad osutid, mis näitavad samale tegelikule
objektile. Kui nüüd ühe kloonitud isendi osuti kaudu taolise väljaspool asuva objekti
parameetreid muuta, siis muutus paistab mõlemast kloonimisega seotud isendist. Samuti nagu
ühest aknast puuoksa külge seotud pekitükk talvel lindude söötmiseks paistab ka teisest
aknast, sest tegemist on ikka ühe ja sama oksa ning pekiga. Ning kui pekk hakkab otsa
lõppema, siis paistab see samuti mõlemast aknast kätte.
Mõnikord aga soovitakse, et kloonimise järel oleksid ka kummastki objektist
viidatavad objektid teineteisest sõltumatud. Selleks tuleb ka alanevad objektid kloonida, mis
mõnikord on võimalik, teinekord mitte. Siin näites püütakse kirjeldada sugupuud. Et isik ning
vanemad on sama liiki ning vastav klass ise kloonitav, siis õnnestub vajadusel ka kogu
sugupuu andmed kloonida. Kloonimisel tehakse kõigepealt koopia enesest ning siis ka
kummastki esivanemast nende olemasolul.
if(isa!=null){
k.isa=(Kloon2)isa.clone();
}
Et esivanem on samuti tüübist Kloon2, siis ka tema kloonimisel minnakse omakorda
alanejaid esivanemaid kloonima.
Sarnaselt liigutakse alamelementide sisse ka väärtusi välja trükkides toString meetodi
abil.
class Kloon2 implements Cloneable{
String nimi;
Kloon2 isa;
Kloon2 ema;
public Object clone(){
Object o=null;
try{
o=super.clone();
Kloon2 k=(Kloon2)o;
if(isa!=null){
k.isa=(Kloon2)isa.clone();
}
if(ema!=null){
k.ema=(Kloon2)ema.clone();
}
}catch(CloneNotSupportedException e){}
return o;
}
public String toString(){
String s=nimi+"(";
if(isa!=null){ s+=isa.toString(); }
s+=", ";
if(ema!=null){ s+=ema.toString(); }
s+=")";
return s;
}
}
Testimisel luuakse kõigepealt väike sugupuu, kus peale Maasu leiduvad ka tema isa,
ema ning isaisa. Edasi sugupuu kloonitakse ning uue puu puhul määratakse isaisa nimeks
Puhvik.
public class Kloon2Test{
public static void main(String[] argumendid){
Kloon2 k1=new Kloon2();
k1.nimi="Maasu";
k1.isa=new Kloon2();
k1.isa.nimi="Punik";
k1.ema=new Kloon2();
k1.ema.nimi="Maara";
k1.isa.isa=new Kloon2();
k1.isa.isa.nimi="Puhvel";
System.out.println(k1);
Kloon2 k2=(Kloon2)k1.clone();
k2.isa.isa.nimi="Puhvik";
System.out.println(k1);
System.out.println(k2);
}
}
Kui süviti kloonimist poleks toimunud, siis oleks mõlemal kloonil isaisa objekt sama
ning isaisa nime muutmine ühe isendi kaudu muutnuks ta nime ka teise isendi poolt vaadates.
Et aga kloonimine toimus süviti, siis klooniti ka vanavanemad ning nimemuutus ühe muutuja
kaudu jättis algse vanaisa nime muutmata.
/*
D:\kodu\0312>java Kloon2Test
Maasu(Punik(Puhvel(, ), ), Maara(, ))
Maasu(Punik(Puhvel(, ), ), Maara(, ))
Maasu(Punik(Puhvik(, ), ), Maara(, ))
*/
Ülesandeid
* Koosta klass Punkt kahe koordinaadiga.
* Loo eksemplar, testi.
* Loo teine muutuja näitamaks samale eksemplarile. Muuda andmeid, testi väljatrükki
mõlema muutuja kaudu.
* Realiseeri Punktil liides Cloneable, kata üle meetod clone loomaks koopia.
* Loo muutuja, mis näitaks esialgse punkti koopiale. Testi väljade muutmist, trüki tulemused
ekraanile.
* Loo klass Kujund, mis sisaldab eneses nime ning Punktide nimistu. Klassile käsklused
punktide lisamiseks ja muutmisks. Testi.
* Muuda Kujund Kloonitavaks. Kloonitakse nimistu, kuid veel mitte üksikuid punkte
(nimistu käsk clone). Testi lisamist ja muutmist.
* Paranda koodi nõnda, et ka üksikud punktid kujundi nimistu sees kloonitaks.
Klasside uuring koodiga
Programmeerija üldjuhul teab, millised käsklused milliste parameetritega ta on oma
koodi kirjutanud. Või kui täpselt ei tea, siis vaatab koodist või dokumentatsioonist järele.
Kui aga pole lähtekoodi või dokumentatsiooni käepärast, samuti juhtudel, kui soovitakse
automaatselt statistikat teha või mõningaid käsklusi testida, aitab välja võimalus
programmikäskude abil olemasolevat klassi uurida. Järgnevas näites on võetud ette klass
String ning küsitud välja kõikide sealsete meetodite nimed.
import java.lang.reflect.Method;
public class Klassiuuring1{
public static void main(String[] argumendid){
String s="Tere";
Class c=s.getClass();
System.out.println("Klassi nimi: "+c.getName());
Method[] m=c.getMethods();
System.out.println("Meetodid:");
for(int i=0; ijavac -classpath junit.jar;. Minitest.java
C:\java\jaagup\testid>java -cp junit.jar;. Minitest
.
Time: 0,01
OK (1 test)
*/
Üldjuhul koostatakse testimoodulid konkreetsete moodulite, klasside,
alamprogrammide või muul viisil piiritletud tervikute kontrolliks. Enne testfunktsioonide
käivitamist võib mõnikord tarvilik olla rakenduse osa testimiseks ette valmistamine ning
pärast testimise lõppu taas seiskamine. Selle tarvist võib testmoodulis üle katta funktsioonid
setUp ning tearDown. Siis pole igas testkäskluses eraldi vaja nende toimetustega tegelda.
Järgnevas näites kontrollitakse bittväärtuste hoidlana toimiva java.util.BitSeti tööd.
BitSeti puhul saab iga elemendi puhul määrata, kas sealne bitt on püsti või mitte.
Algseadistamise juures määratakse bitt number 2 püstiseks. Edasiste funktsioonide juures
kontrollitakse, kas bitid paiknevad ettearvatult.
import junit.framework.*;
import java.util.BitSet;
public class Bitihulgatest extends TestCase{
BitSet b=new BitSet();
public void setUp(){
b.set(2, true);
System.out.println("Testi algus");
}
public void tearDown(){
System.out.println("Testi ots");
}
public void testPysti(){
assertTrue(b.get(2));
}
public void testPikali(){
assertFalse(b.get(1));
}
public static void main(String[] argumendid){
junit.textui.TestRunner.run(new TestSuite(Bitihulgatest.class));
}
}
/*
C:\java\jaagup\testid>javac -classpath junit.jar;. Bitihulgatest.java
C:\java\jaagup\testid>java -cp junit.jar;. Bitihulgatest
.Testi algus
Testi ots
.Testi algus
Testi ots
Time: 0,01
OK (2 tests)
*/
Testide kogum
Suurema rakenduse puhul on lihtsam kirjutada iga tervikliku osa test omaette faili.
Rakenduse täiskontrolli ajal tuleb aga kõik selle kohta käivad testid käivitada. Nõnda on
võimalik koostada testidest kogum ning see tervikuna käivitada.
import junit.framework.TestSuite;
public class TestiKogum{
public static void main(String[] argumendid){
TestSuite kogum=new TestSuite();
kogum.addTest(new TestSuite(Minitest.class));
kogum.addTest(new TestSuite(Bitihulgatest.class));
junit.textui.TestRunner.run(kogum);
}
}
/*
C:\java\jaagup\testid>javac -classpath junit.jar;. TestiKogum.java
C:\java\jaagup\testid>java -cp junit.jar;. TestiKogum
..Testi algus
Testi ots
.Testi algus
Testi ots
Time: 0,02
OK (3 tests)
*/
Ülesandeid
* Tutvu näidetega. Lae ja paki lahti JUnit. http://www.junit.org
* Lisa Minitesti testfunktsioon, mis kontrolliks, et nimes "Juku" on 4 tähte.
* Loo omaette testklass, mille initsialiseerimisel luuakse fail ning kirjutatakse sinna kümme
juhuslikku arvu; arvude summa jäetakse meelde. Lisa testfunktsioon kontrollimaks, kas failis
on ikka kümme rida ning teine jälgimaks, et arvude summa on muutumatu.
* Testi sulgemisel kustutatakse fail.
* Loo liides nimede lisamiseks, otsimiseks ja kustutamiseks.
* Loo liidest realiseeriv klass.
* Loo test klassi oskuste mitmekülgseks kontrolliks.
* Loo liidese põhjal klass, mis kasutaks faili asemel andmebaasi.
* Hoolitse, et loodud testid töötaksid ka uue klassi puhul.