CodeGym /Java blog /Véletlen /Kivételek: fogás és kezelés
John Squirrels
Szint
San Francisco

Kivételek: fogás és kezelés

Megjelent a csoportban
Szia! Utálom megemlíteni, de a programozó munkájának nagy része a hibák kezelése. Leggyakrabban a sajátját. Kiderült, hogy nincs olyan ember, aki ne hibázna. És nincsenek ilyen programok sem. Kivételek: fogás és kezelés - 1 Természetesen a hiba kezelésekor a legfontosabb az okának megértése. És sok minden okozhat hibát egy programban. Valamikor a Java készítői feltették maguknak a kérdést, hogy mit kell tenni a legvalószínűbb programozási hibákkal? Ezek teljes elkerülése nem reális, a programozók képesek olyan dolgokat írni, amiket el sem tudsz képzelni. :) Tehát egy mechanizmust kell adnunk a nyelvnek a hibákkal való munkavégzéshez. Más szóval, ha hiba van a programban, szükség van valamilyen szkriptre a következő lépésekhez. Pontosan mit kell tennie egy programnak hiba esetén? Ma ezzel a mechanizmussal fogunk megismerkedni. Ezt úgy hívják, hogy " kivételek a Java-ban ".

Mi a kivétel?

Kivételt képez a program futása közben fellépő rendkívüli, nem tervezett helyzet. Sok kivétel van. Például olyan kódot írt, amely szöveget olvas ki egy fájlból, és megjeleníti az első sort.

public class Main {

   public static void main(String[] args) throws IOException {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   }
}
De mi van, ha nincs ilyen fájl! A program kivételt generál: FileNotFoundException. Kimenet: Kivétel a "fő" szálban java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (A rendszer nem találja a megadott elérési utat) A Java nyelvben minden kivételt külön osztály képvisel. Mindezek a kivételosztályok egy közös „ősből” – a Throwableszülőosztályból – származnak. A kivételosztály neve általában tömören tükrözi, hogy miért történt a kivétel:
  • FileNotFoundException(a fájl nem található)

  • ArithmeticException(egy matematikai művelet végrehajtása során kivétel történt)

  • ArrayIndexOutOfBoundsException(az index túl van a tömb határain). Ez a kivétel például akkor fordul elő, ha egy olyan tömb 23. pozícióját próbálja megjeleníteni, amely csak 10 elemből áll.
Összességében a Java-nak közel 400 ilyen osztálya van! Miért olyan sok? Hogy kényelmesebb legyen velük dolgozni a programozók számára. Képzelje el ezt: írsz egy programot, és futás közben egy kivételt generál, amely így néz ki:

Exception in thread "main"
Uhhhh. :/ Ez nem sokat segít. Nem világos, hogy mit jelent a hiba, és honnan ered. Itt nincs hasznos információ. De a Java kivételosztályainak sokfélesége megadja a programozónak azt, ami a legfontosabb: a hiba típusát és valószínű okát (az osztály nevébe ágyazva). Egészen más dolog látni

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Azonnal világos, hogy mi lehet a probléma, és hol kell kezdeni az ásást a probléma megoldása érdekében! A kivételek, mint bármely osztály példányai, objektumok.

A kivételek elfogása és kezelése

A Java speciális kódblokkokat tartalmaz a kivételekkel: try, catchés finally. Kivételek: fogás és kezelés - 2 A kód, ahol a programozó úgy véli, hogy kivétel előfordulhat, a blokkba kerül try. Ez nem jelenti azt, hogy itt kivétel lesz. Ez azt jelenti, hogy itt előfordulhat, és a programozó tisztában van ezzel a lehetőséggel. A várt hibatípus a blokkba kerül catch. Ez tartalmazza az összes kódot is, amelyet kivétel esetén végre kell hajtani. Íme egy példa:

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {

       System.out.println("Error! File not found!");
   }
}
Kimenet: Hiba! Fájl nem található! A kódunkat két blokkba helyezzük. Az első blokkban arra számítunk, hogy "A fájl nem található" hiba léphet fel. Ez a tryblokk. A másodikban elmondjuk a programnak, hogy mit tegyen, ha hiba történik. És a konkrét hibatípus: FileNotFoundException. Ha egy másik kivételosztályt teszünk a blokk zárójelébe catch, akkor FileNotFoundExceptionnem lesz elkapva.

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (ArithmeticException e) {

       System.out.println("Error! File not found!");
   }
}
Kimenet: Kivétel a "main" szálban java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (A rendszer nem találja a megadott elérési utat) A catchblokkban lévő kód nem futott, mert "konfiguráltuk" ezt a blokkot elkapni ArithmeticException, és a tryblokkban lévő kód más típusút dobott: FileNotFoundException. Nem írtunk kódot a kezeléshez FileNotFoundException, így a program megjeleníti az alapértelmezett információkat FileNotFoundException. Itt három dologra kell figyelni. Első számú. Ha a blokk valamelyik sorában kivétel történik try, a következő kód nem kerül végrehajtásra. A program végrehajtása azonnal "ugrik" a catchmondatra. Például:

public static void main(String[] args) {
   try {
       System.out.println("Divide by zero");
       System.out.println(366/0);// This line of code will throw an exception

       System.out.println("This");
       System.out.println("code");
       System.out.println("will not");
       System.out.println("be");
       System.out.println("executed!");

   } catch (ArithmeticException e) {

       System.out.println("The program jumped to the catch block!");
       System.out.println("Error! You can't divide by zero!");
   }
}
Kimenet: osztás nullával A program a fogóblokkra ugrott! Hiba! Nem lehet nullával osztani! A blokk második sorában trymegpróbálunk 0-val osztani, ami egy ArithmeticException. Következésképpen a tryblokk 3-9. sorai nem kerülnek végrehajtásra. Mint mondtuk, a program azonnal megkezdi a catchblokk végrehajtását. Második. Több blokk is lehet catch. Ha a blokkban lévő kód nem egy, hanem több különböző típusú kivételt dobhat, mindegyikhez tryírhat egy blokkot.catch

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       System.out.println(366/0);
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
      
       System.out.println("Error! File not found!");
      
   } catch (ArithmeticException e) {

       System.out.println("Error! Division by 0!");
      
   }
}
Ebben a példában két blokkot írtunk catch. Ha FileNotFoundExceptiona blokkban a előfordul try, akkor az első catchblokk kerül végrehajtásra. Ha ArithmeticExceptionelőfordul, a második blokk végrehajtásra kerül. 50 blokkot írhat catch, ha akar. Természetesen jobb, ha nem írunk olyan kódot, amely 50 féle kivételt adhat. :) Harmadik. Honnan tudja, hogy a kód mely kivételeket okozhat? Nos, lehet, hogy néhányat kitalálsz, de lehetetlen, hogy mindent a fejedben tarts. A Java fordító tehát ismeri a leggyakoribb kivételeket és azokat a helyzeteket, ahol ezek előfordulhatnak. Például, ha olyan kódot ír, amelyről a fordító tudja, hogy kétféle kivételt okozhat, akkor a kód addig nem fordul le, amíg nem kezeli őket. Az alábbiakban erre láthatunk példákat. Most néhány szó a kivételkezelésről. A kivételek kezelésének két módja van. Az elsővel már találkoztunk: a metódus magát a kivételt is képes kezelni egy blokkban catch(). Van egy második lehetőség: a metódus újra feldobhatja a kivételt a hívási verembe. Az mit jelent? Például van egy osztályunk ugyanazzal a printFirstString()metódussal, amely beolvas egy fájlt és megjeleníti annak első sorát:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
Jelenleg a kódunk nem fordít le, mert vannak kezeletlen kivételek. Az 1. sorban adja meg a fájl elérési útját. A fordító tudja, hogy egy ilyen kód könnyen előállíthat egy FileNotFoundException. A 3. sorban olvassa el a szöveget a fájlból. Ez a folyamat könnyen (bemeneti/kimeneti hibát) eredményezhet IOException. Most a fordító azt mondja neked: "Haver, nem hagyom jóvá ezt a kódot, és nem fogom lefordítani addig, amíg meg nem mondod, mit tegyek, ha ezen kivételek valamelyike ​​előfordul. És minden bizonnyal megtörténhet az általad írt kód alapján. !" Ezt nem lehet megkerülni: mindkettőt kezelni kell! Az első kivételkezelési módszerről már tudunk: a kódunkat egy blokkba kell helyeznünk, tryés két catchblokkot kell hozzáadnunk:

public static void printFirstString(String filePath) {

   try {
       BufferedReader reader = new BufferedReader(new FileReader(filePath));
       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error, file not found!");
       e.printStackTrace();
   } catch (IOException e) {
       System.out.println("File input/output error!");
       e.printStackTrace();
   }
}
De nem ez az egyetlen lehetőség. Egyszerűen magasabbra dobhatjuk a kivételt ahelyett, hogy hibakezelő kódot írnánk a metódusba. throwsEz a metódus deklarációjában található kulcsszó használatával történik :

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
A kulcsszó után throwsegy vesszővel elválasztott listát jelezünk az összes kivételtípusról, amelyet a metódus dobhat. Miért? Ha valaki meg akarja hívni a printFirstString()metódust a programban, akkor neki (nem neked) kell végrehajtania a kivételkezelést. Tegyük fel például, hogy a program más részében az egyik kollégája írt egy metódust, amely meghívja az Ön printFirstString()metódusát:

public static void yourColleagueMethod() {

   // Your colleague's method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
Hibát kapunk! Ez a kód nem fordítható le! Nem írtunk kivételkezelő kódot a metódusban printFirstString(). Ennek eredményeként ez a feladat most a módszert alkalmazók vállára hárul. Más szavakkal, a methodWrittenByYourColleague()metódusnak most ugyanaz a 2 lehetősége van: vagy blokkot kell használnia try-catchmindkét kivétel kezelésére, vagy újra kell dobnia őket.

public static void yourColleagueMethod() throws FileNotFoundException, IOException {
   // The method does something

   //...and then calls your printFirstString() method with the file it needs
   printFirstString("C:\\Users\\Henry\\Desktop\\testFile.txt");
}
A második esetben a hívásverem következő metódusának – a hívónak methodWrittenByYourColleague()– kell kezelnie a kivételeket. Ezért hívjuk ezt "a kivétel feldobásának vagy átengedésének". Ha a kivételeket felfelé dobja a kulcsszó használatával throws, a kód lefordításra kerül. Ezen a ponton úgy tűnik, hogy a fordító azt mondja: "Rendben, oké. A kódod egy csomó lehetséges kivételt tartalmaz, de lefordítom. De visszatérünk ehhez a beszélgetéshez!" És ha olyan metódust hívunk meg, amelyben vannak kezeletlen kivételek, a fordító beváltja ígéretét, és újra emlékeztet rájuk. Végül a blokkról beszélünk finally(elnézést a szójátékért). Ez a try-catch-finallykivételkezelési triumvirátus utolsó része..

public static void main(String[] args) throws IOException {
   try {
       BufferedReader reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       System.out.println("Error! File not found!");
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
   }
}
Ebben a példában a finallyblokkon belüli kód mindkét esetben végrehajtásra kerül. Ha a blokkban lévő kód tryteljes egészében lefut, kivételek nélkül, akkor a finallyblokk a végén lefut. Ha a tryblokkon belüli kódot egy kivétel megszakítja és a program a blokkra ugrik catch, a finallyblokk továbbra is a catchblokkon belüli kód után fut. Miért van erre szükség? Fő célja a kötelező kód végrehajtása: olyan kód, amelyet a körülményektől függetlenül végre kell hajtani. Például gyakran felszabadít néhány, a program által használt erőforrást. A kódunkban megnyitunk egy adatfolyamot, hogy információt olvassunk a fájlból és továbbítsuk az objektumnak BufferedReader. Le kell zárnunk olvasónkat, és fel kell szabadítani a forrásokat. Ezt mindentől függetlenül meg kell tenni – amikor a program megfelelően működik, és amikor kivételt dob. A finallyblokk nagyon kényelmes hely erre:

public static void main(String[] args) throws IOException {

   BufferedReader reader = null;
   try {
       reader = new BufferedReader(new FileReader("C:\\Users\\Username\\Desktop\\test.txt"));

       String firstString = reader.readLine();
       System.out.println(firstString);
   } catch (FileNotFoundException e) {
       e.printStackTrace();
   } finally {
       System.out.println ("And here's the finally block!");
       if (reader != null) {
           reader.close();
       }
   }
}
Most már biztosak vagyunk abban, hogy gondoskodni fogunk az erőforrásokról, függetlenül attól, hogy mi történik a program futása közben. :) Nem csak ennyit kell tudni a kivételekről. A hibakezelés rendkívül fontos téma a programozásban. Nagyon sok cikket szentelnek neki. A következő leckében megtudjuk, milyen típusú kivételek léteznek, és hogyan hozhat létre saját kivételeket. :) Majd találkozunk!
Hozzászólások
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION