Java:Veahaldus

Allikas: Kursused
Mine navigeerimisribale Mine otsikasti

Programmeerija peab koodi kirjutades arvestama võimalike eriolukordadega, mis võivad programmi käivitamisel tekkida. Töökindel programm on selline, mis suudab erinevate olukordadega toime tulla ilma, et jookseks kokku. Näiteks kui küsida massiivist negatiivse või liiga suure indeksiga elementi.

Töökindluse tõstmiseks on võimalik proovida kõik võimalikke vigu vältida ehk tegeleda ennetava tööga. Lisaks sellele pakub Java võimaluse tegeleda vigadega nende tekkimise hetkel (mingil märral nagu tulekahju kustutamine).

Vigade ennetamine

Mõni koodilõik vajab teatud tingimuste täitmist selleks, et korrektselt töötada. Kui vajalikud eeltingimused on täidetud, saab kood töötada ilma vigadeta.

Näiteks: <source lang="java"> if(i < 0 || i >= A.length) {

   // Do something to handle the
   // out-of-range index, i

} else{

   System.out.println(A[i]);

} </source>

Käsu System.out.println(A[i]); eeltingimuseks on i >= 0 && i < A.length.

Töökindel programm eeldab, et igal sammul tuleb kontrollida kõikide eeltingimuste täitmist. Eeltingimuste täitmist võib kontrollida if-lausega või jälgides, et tingimused on juba täidetud eelneva koodiga. Näiteks:

<source lang="java"> int index; Scanner s = new Scanner(System.in); while (true) {

   //  read next token until it's int
   while (!s.hasNextInt()) s.next();
   index = s.nextInt();
   if (index >= 0 && index < A.length)
       break;
   System.out.println("Your answer must be >= 0 and < " + A.length);

} // At this point, we can be absolutely sure that the value // of index is in the legal range of indices for the array A. System.out.println("A[" + index + "] is " + A[index]); </source>

Sellisel lähenemisel on aga puudused:

  • Teatud tingimustes on väga keeruline või isegi võimatu tuvastada kõik võimalikud probleemid, mis võivad juhtuda.
  • Kõikide võimalike probleemide ennetamise puhul võib lihtne kood muutuda väga keeruliseks ja mahukaks.

Java (nagu ka Python) pakub võimalust tegeleda vigadega mõistlikul kujul sellel hetkel, kui need tekivad (ehk siis enne, kui need programmi töö täitsa seisma panevad).

Erindid

Erindid (ingl Exception) tähistavad erilisi või mingil määral ootamatuid olukordi võrreldes programmi tavapärase tööga. Kui programm peab internetist lugema valuutakursid, siis erind on olukord, kui internet ei tööta või kui valuutakursside veebileht ei tööta jne. Ehk siis programmi tavapärane töö on see, et loetakse andmed veeilehelt ja kuvatakse need kasutajale. Kõiksugu probleemid lähevad erindite alla.

Erind on olemuselt laiem kui viga (ingl error). Kui viga tähistab üldiselt olukorda, kus midagi on läinud katki, siis erinditega saab märku anda olukorrast, mis on lihtsalt teistsugune võrreldes tavapärasega, aga pole tingimata viga. Erindeid saab kasutada programmivoo juhtimiseks (natuke sarnane if-lausele).

Kui programmi töös tekib erind, siis öeldakse selle kohta, et programm viskab erindi (ingl throws exception). Kui see juhtub, pannakse programmivoo käitamine seisma ning on oht, et programmitöö lõpetatakse täielikult. Tekkinud (visatud) erindeid on võimalik kinni püüda. Kinnipüütud erindi korral on võimalik programmil veel probleemi lahendada (sõltuvalt olukorrast). Näiteks anda kasutajale viisakas teade, miks programm oma töö lõpetab.

Erindi tekkimisel visatakse tegelikult objekti, mis hoiab endas kõiksugu vajalikku infot. Näiteks on seal objektis kirjas veateade (mis viga tekkis).


Erindi objektid jagunevad kaheks vastavalt sellele millisest peaklassist nad pärinevad. Error objektist pärinevad erindid tähendavad üldjuhul tõsist probleemi, mille korral programm ei saa jätkata. Exception objektist pärinevad erindid on üldjuhul mõeldud kinnipüüdmiseks. Kuigi Exception alamtüübid võivad ka kanda nime "viga", siis üldiselt need vead on lihtsamad, mille korral saab programm oma tööd jätkata.

Erindite püüdmine

Erindeid saab püüda try-catch plokiga. try plokk sisaldab käske, mida "proovitakse" käivitada. Kui nende käivitamine õnnestub, siis midagi erilist ei juhtu - kõik read pannakse järgemööda käima. Kui aga try plokis tekib mõne käsu käivitamisel viga (visatakse erind), on võimalik see visatud erind kinni püüda catch lauseosaga.

Näide: <source lang="java"> try {

   double determinant = M[0][0]*M[1][1] - M[0][1]*M[1][0];
   System.out.println("The determinant of M is " + determinant);

} catch ( ArrayIndexOutOfBoundsException e ) {

   System.out.println("M is the wrong size to have a determinant");

}

</source>

Eelnevas koodinäites üritatakse arvutada 2x2 suurusega maatriksi determinanti. Kui arvutumisel tekib ArrayIndexOutOfBoundsException, siis see püütakse kinni ja näidatakse kasutajale vastavat teadet. See erind tekib juhul, kui üritatakse massiivist võtta liiga suure või väikese indeksiga elementi (ehk siis indeksi väärtus pole massiivi piirides). Kui arvutamine õnnestub, siis käivitatakse väljaprintimise käsk. Õnnestunud try ploki käivitamise korral jääb catch plokk käivitamata.

try plokile võib järgneda mitu catch plokki. Näiteks: <source lang="java"> try {

   double determinant = M[0][0]*M[1][1] - 
           M[0][1]*M[1][0];
   System.out.println("The determinant of M is " + 
           determinant);

} catch ( ArrayIndexOutOfBoundsException e ) {

   System.out.println("M is the wrong size to have a determinant.");

} catch ( NullPointerException e ) {

   System.out.println("Programming error! M doesn't 
           exist. " + e.getMessage());

}

</source>

Eelnevas koodis on programm valmis kinni püüdma kaks erinevat viga. Massiivi indeksist me eelnevalt rääkisime. NullPointerException erind tekib juhul, kui üritatakse väärtustamata objektiga (ehk siis objekti pole mälus olemas) midagi teha. Antud näite puhul võiks see olla analoogne kontrolliga if (M == null) .... Ehk siis kui M väärtus on null, ei saa sellega ühtegi massiivi operatsiooni (näiteks konkreetse elemendi võtmine) teha. Vastavalt sellele, mis viga/erind tekib, pannakse vastav püüdmise plokk käima. Käivituda saab vaid üks catch plokk, seega see, millise tüübiga viga kõigepealt tekib. Kuna vea korral lõpetatakse try ploki käivitamine, siis rohkem vigu sealt tekkida ei saa.

try-catch süntaks

Süntaks on järgmine:

try {
    [käsud]
}
[valikulised catch osad]
[valikuline finally osa]

Süntaks catch osa kohta:

catch ([erindi-klassi-nimi] [muutuja-nimi]) {
    [käsud]
}

Süntaks finally osa kohta:

finally {
    [käsud]
}

Finally ploki osa

finally plokk pannakse alati käima, sõltumata sellest, kas catch plokk pandi konkreetse try-catch konstruktsiooniga käima või mitte.

finally plokki kasutatakse teatud puhastamise ja korrastamise teostamiseks.