Ha a program természetes végrehajtása során probléma lép fel, akkor egy kivétel keletkezik. Vannak osztályok amelyeknek használata esetén kötelező a kivételt kezelni, ezek az ellenőrzött kivételek.
A kivételekkel két dolgot tehetünk: elkapjuk, vagy eldobjuk (továbbengedjük).
Ha egy metódusban hiba keletkezik, akkor létrehoz egy objektumot, amelyet átad a futtatókörnyezetnek. Ez a kivételobjektum tartalmazza a hiba leírását, annak típusát, a program állapotát. Ennek az objektumnak a létrehozása a kivételdobás.
Kivétel | Leírás |
---|---|
ellenőrzött | Ha egy metódus kivételt dob, a fordító hibát jelez, ha nincs try-catch kód. |
futási idejű (ellenőrizetlen) | Nem kötelező catch blokk A java.lang.RuntimeException osztályból származik. |
A try utasítással megpróbálunk végrehajtani egy kódot. Ha kód kivételt dob, akkor azt a catch utasítással elkapjuk. A catch utáni részben pedig kezeljük.
try { //kód amit megpróbálunk végrehajtani }catch(Exception e){ //mit teszünk, ha kivétel keletkezett }
Az Exception osztály egy általános kivételosztály. Minden kivétel ebből származik. Az e objektum keletkezik bármilyen kivétel esetén, ami Exception típusú lesz. Ez azt is jelenti, hogy minden kivételt elkap.
Az Exception helyett megadhatunk konkrét kivételeket is. Az IOException például fájlba írás során használjuk. Ha ilyen kivételeket szeretnénk elkapni, az e objektumot tegyük IOException típusúvá:
try { //fájlba író kód amit megpróbálunk végrehajtani }catch(IOException e){ //mit teszünk, ha a fájlba írás sikertelen }
Konkrét példa:
try { FileWriter fw = new FileWriter("adat.txt", true); fw.write("bármi\n"); fw.close(); }catch(IOException e){ System.err.println("Hiba! A fájlbaírás sikertelen!"); System.err.println(e.getMessage()); }
Egymás után több kivételt is elkaphatunk:
try { ... kód ... }catch (IOException e){ e.printStackTrace(); }catch (GeneralSecurityException e){ e.printStackTrace(); }catch (ClassNotFoundException e){ e.printStackTrace(); }
Nézzük meg egy FileNotFoundException esetén mit kapunk ha csak simán kiíratjuk a ex tartalmát:
try { File file = new File("adat.txt"); Scanner scanner = new Scanner(file); }catch(FileNotFoundException ex) { System.err.println(ex); }
java.io.FileNotFoundException: adat.txt (Nincs ilyen fájl vagy könyvtár)
Most nézzük meg, mit kapunk, ha az egész hívásverem tartalmát kiíratjuk:
try { File file = new File("adat.txt"); Scanner scanner = new Scanner(file); }catch(FileNotFoundException ex) { ex.printStackTrace(); }
java.io.FileNotFoundException: adat.txt (Nincs ilyen fájl vagy könyvtár) at java.base/java.io.FileInputStream.open0(Native Method) at java.base/java.io.FileInputStream.open(FileInputStream.java:211) at java.base/java.io.FileInputStream.<init>(FileInputStream.java:153) at java.base/java.util.Scanner.<init>(Scanner.java:639) at haromszog.Haromszog.beolvas(Haromszog.java:12) at haromszog.Haromszog.main(Haromszog.java:35)
import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; class App { static Scanner scanner; public static void main(String args[]) throws Exception { try { File file = new File("adat.txt"); scanner = new Scanner(file); }catch(FileNotFoundException ex) { System.err.println(ex.getMessage()); } } }
A metódus záradékában írjuk le milyen kivételeket nem szeretnénk kezelni. Ezeket a kivételeket eldobjuk.
Az IO kivételek eldobása a throws utasítással:
class Program { public void writeFile() throws IOException { } }
Teljes metódus:
class FileHandler { public void writeFile() throws IOException { FileWriter fw = new FileWriter("adat.txt", true); fw.write("bármi\n"); fw.close(); } }
Az eredeti writeFile() metódust átnvezem tryWriteFile() metódusra. Készítek egy writeFile() metódust, ahol kezeljük a kivételt. A tryWriteFile() metódusban csak tovább dobom a kivételt, nem kezelem.
import java.io.FileWriter; import java.io.IOException; class FileHandler { public void writeFile() { try { tryWriteFile(); } catch (IOException e) { System.err.println("Hiba! A fájlbaírás sikertelen!"); System.err.println(e.getMessage()); } } public void tryWriteFile() throws IOException { FileWriter fw = new FileWriter("adat.txt", true); fw.write("bármi\n"); fw.close(); } } class App { public static void main(String args[]) throws Exception { new FileHandler().writeFile(); } }
Ellenőrzött kivétel készítése:
class WrongNumberException extends Exception { WrongNumberException(String msg) { super(msg); } } class App { static int kerSzam() { return -9; } public static void main(String args[]) throws Exception { int szam = kerSzam(); if(szam<=0) throw new WrongNumberException("Hiba! Nem megfelelő szám!"); else System.out.println("A szam: " + szam); } }
class WrongNumberException extends Exception { WrongNumberException(String msg) { super(msg); } } class Calculate { private int kerSzam() { return -9; } public void calc() throws WrongNumberException { int szam = kerSzam(); if(szam<=0) throw new WrongNumberException("Hiba! Nem megfelelő szám!"); else System.out.println("A szam: " + szam); } } class App { public static void main(String args[]) throws Exception { new Calculate().calc(); } }
class WrongNumberException extends Exception { private int num; WrongNumberException(int num) { this.num = num; } @Override public String toString() { String msg; if (num == 0) { msg = "Error! Zero number!"; }else if(num < 0) { msg = "Error! Negative number!"; }else { msg = "Error! Other error!"; } return msg; } } class App { static int kerSzam() { return -9; } public static void main(String args[]) throws Exception { int szam = kerSzam(); if(szam<=0) throw new WrongNumberException(szam); else System.out.println("A szam: " + szam); } }
class WrongNumberException extends RuntimeException { WrongNumberException(String msg) { super(msg); } } class Calculate { private int kerSzam() { return -9; } public void calc() { int szam = kerSzam(); if(szam<=0) throw new WrongNumberException("Hiba! Nem megfelelő szám!"); else System.out.println("A szam: " + szam); } } class App { public static void main(String args[]) throws Exception { new Calculate().calc(); } }
A finally blokkban mondjuk meg, hogy mi az az utasítás, amelyet mindenképpen végre kell hajtani a try blokk után, még akkor is, ha a try blokk kivétellel ér véget. Jellemzően, ilyenek lehetnek az erőforrások lezárása. A következő példában egy fájt nyitunk meg írásra, és szeretnénk, ha mindenképpen le lenne zárva.
import java.io.FileWriter; import java.io.IOException; public class Program01 { public static void writeFile() { FileWriter fw = null; try { fw = new FileWriter("adat.txt", true); fw.write("alam"); } catch(IOException ex) { System.err.println("Hiba a fájlbaírás során!"); } finally { try { fw.close(); } catch(IOException ex) { System.err.println("Hiba a fájl bezárása során!"); } } } public static void main (String args[]) { writeFile(); } }
A kódunk sokkal szebb, ha a kivételkezelést elválasztjuk a lényegi kódtól:
import java.io.FileWriter; import java.io.IOException; public class Program01 { public static void writeFile() { FileWriter fw = null; try { fw = tryFajlbaIr(); } catch(IOException ex) { System.err.println("Hiba a fájlbaírás során!"); } finally { try { fw.close(); } catch(IOException ex) { System.err.println("Hiba a fájl bezárása során!"); } } } public static FileWriter tryWriteFile() throws IOException { FileWriter fw = new FileWriter("adat.txt", true); fw.write("alma"); return fw; } public static void main (String args[]) { writeFile(); } }
A kód lényegi része a tryWriteFile() metódusban valósítható meg, így az átlátható. A hibakezeléssel pedig a writeFile() metódusban foglalkozunk.
Esetleg leválaszthatjuk a fájl megnyitását és bezárását is:
import java.io.FileWriter; import java.io.IOException; public class Program01 { public static FileWriter openFile() { FileWriter fw = null; try { fw = new FileWriter("adat.txt", true); } catch(IOException ex) { System.err.println("Hiba a fájl megnyitása során!"); } return fw; } public static void writeFile(FileWriter fw) { try { tryWriteFile(fw); }catch(IOException ex) { System.err.println("Hiba a fájlba írás során!"); } } public static void tryWriteFile(FileWriter fw) throws IOException { fw.write("alma"); } public static void closeFile(FileWriter fw) { try { fw.close(); } catch(IOException ex) { System.err.println("Hiba fájl bezárása során!"); } } public static void main (String args[]) { FileWriter fw = openFile(); writeFile(fw); closeFile(fw); } }
Figyeljük meg a tryWriteFile() metódust, amely most sokkal átláthatóbb.
Ha egy metódust nem valósítottunk meg, akkor java.lang.UnsupportedOperationException kivételt szokás dobni:
throw new UnsupportedOperationException();
A try() zárójelében megnyitott erőforrások automatikusan lezárásra kerülnek, függetlenül attól, hogyan záródott be; sikeresen vagy sikertelenül.
Az automatikusa lezárás csak olyan erőforrásokon lehetséges, amin alkalmazva lett a AutoCloseable vagy a Closeable interfész.
import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class Filehandler { public void readFile() { File file = new File("adat.txt"); try(Scanner sc = new Scanner(file)){ while(sc.hasNext()) { String line = sc.nextLine(); System.out.println(line); } }catch(FileNotFoundException e) { System.err.println("Hiba! A fájl nem található"); System.err.println(e.getMessage()); } } }
import java.io.File; import java.io.FileNotFoundException; import java.util.Scanner; public class Filehandler { public void readFile() { try { tryReadFile(); } catch (FileNotFoundException e) { System.err.println("Hiba! A fájl nem található"); System.err.println(e.getMessage()); } } public void tryReadFile() throws FileNotFoundException { File file = new File("adat.txt"); try(Scanner sc = new Scanner(file)){ while(sc.hasNext()) { String line = sc.nextLine(); System.out.println(line); } } } }
A bekért adatokat mindig ellenőrizzük le, mert lehet nem tudunk majd vele számolni, műveleteket végezni.
Legyen például egy háromszög területszámítás, ahol be kell kérnünk a háromszög alapját és magasságát. A bekérést megtehetjük a Scanner, a System vagy más osztállyal is. A buktató az lehet, ha a felhasználó nem számot ír, vagy nem jó formában írja.
A következő kód egy háromszög alapjának bekérése System osztállyal:
System.out.print("Alap: "); String alapStr = System.console().readLine(); int alap = Integer.parseInt(alapStr);
Ez mindaddig jól működik, amíg a felhasználó számot ír. Ha felhasználó nem egész számot ír be, akkor a program kivételt dob. A fordítás kimenetében látszik, hogy a kivétel neve „NumberFormatException”. Ebből tudhatjuk, hogy ezt kell kezelnünk.
A kivétel kezelése:
System.out.print("Alap: "); String alapStr = System.console().readLine(); int alap; try{ alap = Integer.parseInt(alapStr); }catch(NumberFormatException ex){ System.err.println("Csak egész szám adható meg!"); }
Az alap bekérése kezd egészen sok sorból állni. Célszerű külön függvénybe tenni:
class Program01 { private static int beAlap(){ int alap=0; boolean szamOk = false; do{ System.out.print("Alap: "); String alapStr = System.console().readLine(); try{ alap = Integer.parseInt(alapStr); szamOk = true; }catch(NumberFormatException ex){ System.err.println("Számot kértem!"); } }while(!szamOk); return alap; } public static void main(String args[]) { int alap = beAlap(); } }
Viszont ha a másik számot is ugyanígy kérjük be, akkor általánosíthatjuk a függvényünket:
class Program01 { private static int beEgeszSzam(String bekerFelirat){ int szam=0; boolean szamOk = false; do{ System.out.print(bekerFelirat); String szamStr = System.console().readLine(); try{ szam = Integer.parseInt(szamStr); szamOk = true; }catch(NumberFormatException ex){ System.err.println("Egész számot kértem!"); } }while(!szamOk); return szam; } public static void main(String args[]) { int alap = beEgeszSzam("Alap: "); int magassag = beEgeszSzam("Magasság: "); } }
A végleges változat:
class TulKisSzamException extends Exception { TulKisSzamException(String uzenet) { super(uzenet); } } class Haromszog { public static double beValosSzam(String bekerFelirat){ double valosSzam=0; boolean szamOk = false; do{ System.out.print(bekerFelirat); String szamStr = System.console().readLine(); try{ valosSzam = Double.parseDouble(szamStr); szamOk = true; }catch(NumberFormatException ex){ System.err.println("Valós számot kértem!"); } }while(!szamOk); return valosSzam; } public static double szamolTerulet(double alap, double magassag) throws TulKisSzamException{ if(alap<1 || magassag<1){ throw new TulKisSzamException("Túl kis szám"); } double terulet = 0; terulet = (alap * magassag) / 2.0; return terulet; } } class Program01 { private static double trySzamol(double alap, double magassag){ Haromszog haromszog = new Haromszog(); double terulet = 0; try { terulet = haromszog.szamolTerulet(alap, magassag); }catch(TulKisSzamException ex){ System.err.println("Túl kis szám!"); } return terulet; } public static void main(String args[]){ Haromszog haromszog = new Haromszog(); double alap = haromszog.beValosSzam("Alap: "); double magassag = haromszog.beValosSzam("Magasság: "); double terulet = trySzamol(alap, magassag); System.out.printf("Terület: %.2f\n", terulet); } }