здрасти Мразя да го споменавам, но огромна част от работата на програмиста се занимава с грешки. Най-често неговата or нейната собствена. Оказва се, че няма хора, които да не грешат. И такива програми също няма.
Разбира се, когато се занимавате с грешка, основното е да разберете причината за нея. И много неща могат да причинят грешки в програмата. В един момент създателите на 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 елемента.
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
. 
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 да създадете свои собствени изключения. :) Ще се видим тогава!
GO TO FULL VERSION