CodeGym /Java блог /Случаен /Изключения: улавяне и обработка
John Squirrels
Ниво
San Francisco

Изключения: улавяне и обработка

Публикувано в групата
здрасти Мразя да го споменавам, но огромна част от работата на програмиста се занимава с грешки. Най-често неговата or нейната собствена. Оказва се, че няма хора, които да не грешат. И такива програми също няма. Изключения: улавяне и обработка - 1 Разбира се, когато се занимавате с грешка, основното е да разберете причината за нея. И много неща могат да причинят грешки в програмата. В един момент създателите на Java се запитаха Howво трябва да се направи с най-вероятните програмни грешки? Напълното им избягване не е реалистично, програмистите са способни да пишат неща, които дори не можете да си представите. :) И така, трябва да дадем на езика механизъм за работа с грешки. С други думи, ако има грешка във вашата програма, имате нужда от няHowъв скрипт за това Howво да правите по-нататък. Какво точно трябва да направи една програма, когато възникне грешка? Днес ще се запознаем с този механизъм. Нарича се „ изключения в Java “.

Какво е изключение?

Изключение е изключителна, непланирана ситуация, която възниква, докато програмата работи. Има много изключения. Например, написали сте code, който чете текст от файл и показва първия ред.

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);
   }
}
Но Howво ще стане, ако няма такъв файл! Програмата ще генерира изключение: FileNotFoundException. Изход: Изключение в нишката "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системата не може да намери посочения път) В Java всяко изключение е представено от отделен клас. Всички тези класове изключения произлизат от общ "предшественик" - Throwableродителския клас. Името на клас изключение обикновено кратко отразява защо е възникнало изключението:
  • FileNotFoundException(файлът не е намерен)

  • ArithmeticException(възникна изключение при извършване на математическа операция)

  • ArrayIndexOutOfBoundsException(индексът е извън границите на масива). Например това изключение възниква, ако се опитате да покажете позиция 23 на масив, който има само 10 елемента.
Като цяло Java има почти 400 такива класа! Защо толкова много? За да ги направи по-удобни за работа на програмистите. Представете си следното: пишете програма и докато тя работи, тя генерира изключение, което изглежда така:

Exception in thread "main"
ъъъъъ :/ Това не помага много. Не е ясно Howво означава грешката or откъде идва. Тук няма полезна информация. Но голямото разнообразие от класове изключения в Java дава на програмиста най-важното: вида на грешката и вероятната й причина (вградена в името на класа). Съвсем друго е да се види

Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Веднага става ясно Howъв може да е проблемът и откъде да започнем да ровим, за да решим проблема! Изключенията, като екземплярите на всеки клас, са обекти.

Прихващане и обработка на изключения

Java има специални блокове code за работа с изключения: try, catchи finally. Изключения: улавяне и обработка - 2 Кодът, при който програмистът смята, че може да възникне изключение, се поставя в tryблока. Това не означава, че тук ще има изключение. Това означава, че може да се случи тук и програмистът е наясно с тази възможност. Типът грешка, който очаквате да възникне, се поставя в catchблока. Това също съдържа целия code, който трябва да бъде изпълнен, ако възникне изключение. Ето един пример:

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!");
   }
}
Изход: Грешка! Файлът не е намерен! Поставяме нашия code в два блока. В първия блок очакваме, че може да възникне грешка „Файлът не е намерен“. Това е tryблокът. Във втория казваме на програмата Howво да прави, ако възникне грешка. И конкретният тип грешка: FileNotFoundException. Ако поставим различен клас изключение в скобите на catchблока, тогава FileNotFoundExceptionняма да бъде уловен.

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!");
   }
}
Изход: Изключение в нишката "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Системата не може да намери посочения път) Кодът в catchблока не се изпълни, защото ние "конфигурирахме" този блок за улавяне ArithmeticExceptionи codeът в tryблока хвърли различен тип: FileNotFoundException. Не написахме code за обработка FileNotFoundException, така че програмата показва информацията по подразбиране за FileNotFoundException. Тук трябва да обърнете внимание на три неща. Номер едно. След като възникне изключение на някой ред в tryблока, codeът, който следва, няма да бъде изпълнен. Изпълнението на програмата веднага "скача" към catchблока. Например:

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!");
   }
}
Изход: Разделяне на нула Програмата скочи до блока catch! грешка! Не можеш да делиш на нула! На втория ред на tryблока се опитваме да разделим на 0, което води до ArithmeticException. Следователно, редове 3-9 на tryблока няма да бъдат изпълнени. Както казахме, програмата веднага започва да изпълнява catchблока. Номер две. Може да има няколко catchблока. Ако codeът в tryблока може да хвърли не едно, а няколко различни типа изключения, можете да напишете 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!");
      
   }
}
В този пример сме написали два catchблока. Ако FileNotFoundExceptionв блока се появи a, ще се изпълни tryпървият блок. catchАко ArithmeticExceptionвъзникне, вторият блок ще бъде изпълнен. Можете да напишете 50 catchблока, ако искате. Разбира се, по-добре е да не пишете code, който може да хвърли 50 различни вида изключения. :) Трето. Как да разберете кои изключения може да хвърли вашият code? Е, може да успеете да познаете някои от тях, но е невъзможно да запазите всичко в главата си. Следователно компилаторът на Java знае най-често срещаните изключения и ситуациите, в които те могат да възникнат. Например, ако пишете code, за който компилаторът знае, че може да хвърли два вида изключения, codeът ви няма да се компorра, докато не ги обработите. Ще видим примери за това по-долу. Сега няколко думи за обработката на изключения. Има 2 начина за обработка на изключенията. Вече се сблъскахме с първото: методът може да обработи самото изключение в catch()блок. Има втора опция: методът може да хвърли отново изключението в стека на повикванията. Какво означава това? Например, имаме клас със същия printFirstString()метод, който чете файл и показва първия му ред:

public static void printFirstString(String filePath) {

   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
В момента нашият code не се компorра, защото има необработени изключения. В ред 1 посочвате пътя до file. Компилаторът знае, че такъв code може лесно да създаде FileNotFoundException. В ред 3 четете текста от file. Този процес може лесно да доведе до IOException(грешка при въвеждане/извеждане). Сега компилаторът ви казва: „Пич, няма да одобря този code и няма да го компorрам, докато не ми кажете Howво трябва да направя, ако възникне едно от тези изключения. И те със сигурност могат да се случат въз основа на codeа, който сте написали !" Няма начин да го заобиколите: трябва да се справите и с двете! Вече знаем за първия метод за обработка на изключения: трябва да поставим нашия code в блок tryи да добавим два catchблока:

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();
   }
}
Но това не е единственият вариант. Можем просто да хвърлим изключението по-високо, instead of да пишем code за обработка на грешки вътре в метода. Това се прави с помощта на ключовата дума throwsв декларацията на метода:

public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
   BufferedReader reader = new BufferedReader(new FileReader(filePath));
   String firstString = reader.readLine();
   System.out.println(firstString);
}
След ключовата дума throwsние посочваме разделен със запетаи списък на всички видове изключения, които методът може да хвърли. Защо? Сега, ако някой иска да извика printFirstString()метода в програмата, той or тя (не вие) ще трябва да приложи обработка на изключения. Да предположим например, че някъде другаде в програмата един от вашите колеги е написал метод, който извиква вашия printFirstString()метод:

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");
}
Получаваме грешка! Този code няма да се компorра! Не сме написали code за обработка на изключения в printFirstString()метода. В резултат на това тази задача сега пада върху плещите на тези, които използват метода. С други думи, methodWrittenByYourColleague()методът вече има същите 2 опции: той трябва or да използва try-catchблок за обработка на двете изключения, or да ги хвърли отново.

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");
}
Във втория случай следващият метод в стека за извикване – този, който извиква methodWrittenByYourColleague()– ще трябва да обработва изключенията. Ето защо ние наричаме това „хвърляне or предаване на изключението нагоре“. Ако хвърлите изключения нагоре, като използвате ключовата дума throws, вашият code ще се компorра. В този момент компилаторът сякаш казва: „Добре, добре. Вашият code съдържа куп потенциални изключения, но аз ще го компorрам. Но ще се върнем към този разговор!“ И когато извикате всеки метод, който има необработени изключения, компилаторът изпълнява обещанието си и ви напомня за тях отново. Накрая ще поговорим за finallyблока (съжалявам за играта на думи). Това е последната част от try-catch-finallyтриумвирата за обработка на изключения..

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!");
   }
}
В този пример codeът вътре в finallyблока ще бъде изпълнен и в двата случая. Ако codeът в tryблока се изпълнява изцяло, без да се хвърлят ниHowви изключения, блокът finallyще се изпълнява накрая. Ако codeът вътре в tryблока бъде прекъснат от изключение и програмата прескочи до блока catch, finallyблокът пак ще се изпълнява след codeа вътре в catchблока. Защо е необходимо това? Основната му цел е да изпълни задължителен code: code, който трябва да бъде изпълнен независимо от обстоятелствата. Например, често освобождава някои ресурси, използвани от програмата. В нашия code отваряме поток, за да прочетем информация от file и да я предадем на BufferedReaderобекта. Трябва да затворим нашия четец и да освободим ресурсите. Това трябва да се направи без meaning Howво - когато програмата работи Howто трябва и когато хвърля изключение. Блокът finallyе много удобно място за това:

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();
       }
   }
}
Сега сме сигурни, че ще се погрижим за ресурсите, независимо Howво се случва, когато програмата работи. :) Това не е всичко, което трябва да знаете за изключенията. Обработката на грешки е супер важна тема в програмирането. На него са посветени много статии. В следващия урок ще разберем Howви видове изключения има и How да създадете свои собствени изключения. :) Ще се видим тогава!
Коментари
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION