Hej! Jag hatar att nämna det, men en stor del av en programmerares arbete är att hantera fel. Oftast hans eller hennes egen. Det visar sig att det inte finns några människor som inte gör misstag. Och det finns inga sådana program heller.
Naturligtvis, när man hanterar ett fel, är det viktigaste att förstå dess orsak. Och många saker kan orsaka buggar i ett program. Vid något tillfälle frågade Javas skapare sig själva vad som skulle göras med de mest troliga programmeringsfelen? Att helt undvika dem är inte realistiskt, programmerare kan skriva saker du inte ens kan föreställa dig. :) Så vi måste ge språket en mekanism för att arbeta med fel. Med andra ord, om det finns ett fel i ditt program behöver du något slags skript för vad du ska göra härnäst. Vad exakt ska ett program göra när ett fel uppstår? Idag kommer vi att bekanta oss med denna mekanism. Det kallas " undantag i Java ".
Kod där programmeraren tror att ett undantag kan inträffa placeras i

Vad är ett undantag?
Ett undantag är en exceptionell, oplanerad situation som inträffar medan ett program körs. Det finns många undantag. Till exempel skrev du kod som läser text från en fil och visar den första raden.
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);
}
}
Men tänk om det inte finns någon sådan fil! Programmet genererar ett undantag: FileNotFoundException
. Utdata: Undantag i tråden "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Systemet kan inte hitta den angivna sökvägen) I Java representeras varje undantag av en separat klass. Alla dessa undantagsklasser härrör från en gemensam "förfader" - Throwable
föräldraklassen. En undantagsklasss namn återspeglar vanligtvis kortfattat varför undantaget inträffade:
FileNotFoundException
(filen hittades inte)ArithmeticException
(ett undantag inträffade när en matematisk operation utfördes)ArrayIndexOutOfBoundsException
(indexet är bortom gränserna för arrayen). Detta undantag inträffar till exempel om du försöker visa position 23 i en array som bara har 10 element.
Exception in thread "main"
Åhhhh. :/ Det hjälper inte mycket. Det är inte klart vad felet betyder eller var det kom ifrån. Det finns ingen användbar information här. Men det stora utbudet av undantagsklasser i Java ger programmeraren det som är viktigast: typen av fel och dess troliga orsak (inbäddad i klassnamnet). Det är en helt annan sak att se
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Det är direkt klart vad problemet kan vara och var man ska börja gräva för att lösa problemet! Undantag, som instanser av alla klasser, är objekt.
Undantag för fångst och hantering
Java har speciella kodblock för att arbeta med undantag:try
, catch
och finally
. 
try
blocket. Det betyder inte att ett undantag kommer att inträffa här. Det betyder att det kan inträffa här, och programmeraren är medveten om denna möjlighet. Typen av fel som du förväntar dig ska uppstå placeras i catch
blocket. Denna innehåller också all kod som ska köras om ett undantag inträffar. Här är ett exempel:
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!");
}
}
Utdata: Fel! Filen hittades inte! Vi lägger vår kod i två block. I det första blocket förväntar vi oss att ett "File not found"-fel kan uppstå. Det här är try
blocket. I den andra berättar vi för programmet vad det ska göra om ett fel uppstår. Och den specifika feltypen: FileNotFoundException
. Om vi sätter en annan undantagsklass inom parentesen av blocket, catch
kommer vi FileNotFoundException
inte att fångas.
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!");
}
}
Utdata: Undantag i tråden "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Systemet kan inte hitta den angivna sökvägen) Koden i catch
blocket kördes inte, eftersom vi "konfigurerade" detta block att fånga ArithmeticException
, och koden i try
blocket gav en annan typ: FileNotFoundException
. Vi skrev ingen kod att hantera FileNotFoundException
, så programmet visar standardinformationen för FileNotFoundException
. Här måste du vara uppmärksam på tre saker. Nummer ett. När ett undantag inträffar på någon rad i try
blocket kommer koden som följer inte att exekveras. Utförande av programmet "hoppar" omedelbart till catch
blocket. Till exempel:
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!");
}
}
Utgång: Dividera med noll Programmet hoppade till fångstblocket! Fel! Du kan inte dividera med noll! På den andra raden i try
blocket försöker vi dividera med 0, vilket resulterar i en ArithmeticException
. Följaktligen kommer raderna 3-9 i try
blocket inte att exekveras. Som vi sa börjar programmet omedelbart exekvera blocket catch
. Nummer två. Det kan finnas flera catch
block. Om koden i try
blocket kanske inte ger ett, utan flera olika typer av undantag, kan du skriva ett catch
block för vart och ett av dem.
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!");
}
}
I det här exemplet har vi skrivit två catch
block. Om a FileNotFoundException
inträffar i blocket kommer try
det första blocket att exekveras. catch
Om en ArithmeticException
inträffar kommer det andra blocket att exekveras. Du kunde skriva 50 catch
block om du ville. Naturligtvis är det bättre att inte skriva kod som kan skapa 50 olika typer av undantag. :) Tredje. Hur vet du vilka undantag din kod kan ge? Tja, du kanske kan gissa några av dem, men det är omöjligt för dig att hålla allt i huvudet. Java-kompilatorn känner därför till de vanligaste undantagen och de situationer där de kan uppstå. Till exempel, om du skriver kod som kompilatorn vet kan ge två typer av undantag, kompileras inte din kod förrän du hanterar dem. Vi kommer att se exempel på detta nedan. Nu några ord om undantagshantering. Det finns två sätt att hantera undantag. Vi har redan stött på det första: metoden kan hantera själva undantaget i ett catch()
block. Det finns ett andra alternativ: metoden kan kasta om undantaget upp i anropsstacken. Vad betyder det? Till exempel har vi en klass med samma printFirstString()
metod, som läser en fil och visar dess första rad:
public static void printFirstString(String filePath) {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
För närvarande kompilerar inte vår kod eftersom den har obehandlade undantag. På rad 1 anger du sökvägen till filen. Kompilatorn vet att sådan kod lätt kan producera en FileNotFoundException
. På rad 3 läser du texten från filen. Denna process kan lätt resultera i ett IOException
(inmatnings-/utmatningsfel). Nu säger kompilatorn till dig, "Du, jag kommer inte att godkänna den här koden och jag kommer inte att kompilera den förrän du berättar för mig vad jag ska göra om ett av dessa undantag inträffar. Och de kan säkert hända baserat på koden du skrev !" Det finns inget sätt att komma runt det: du måste hantera båda! Vi känner redan till den första undantagshanteringsmetoden: vi måste lägga vår kod i ett try
block och lägga till två catch
block:
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();
}
}
Men detta är inte det enda alternativet. Vi kunde helt enkelt kasta undantaget högre istället för att skriva felhanteringskod inuti metoden. Detta görs med hjälp av nyckelordet throws
i metoddeklarationen:
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
Efter nyckelordet throws
anger vi en kommaseparerad lista över alla typer av undantag som metoden kan ge upphov till. Varför? Om någon nu vill anropa printFirstString()
metoden i programmet måste han eller hon (inte du) implementera undantagshantering. Anta till exempel att någon av dina kollegor någon annanstans i programmet skrev en metod som anropar din printFirstString()
metod:
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");
}
Vi får ett fel! Den här koden kommer inte att kompileras! Vi skrev inte undantagshanteringskod i printFirstString()
metoden. Som ett resultat faller denna uppgift nu på axlarna av dem som använder metoden. Med andra ord methodWrittenByYourColleague()
har metoden nu samma 2 alternativ: den måste antingen använda ett try-catch
block för att hantera båda undantagen, eller kasta om dem.
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");
}
I det andra fallet måste nästa metod i anropsstacken – den som anropar – methodWrittenByYourColleague()
hantera undantagen. Det är därför vi kallar detta "att kasta eller förpassa undantaget". Om du kastar undantag uppåt med nyckelordet throws
kompileras din kod. Vid det här laget verkar kompilatorn säga: "Okej, okej. Din kod innehåller en massa potentiella undantag, men jag kommer att kompilera den. Men vi återkommer till den här konversationen!" Och när du anropar någon metod som har obehandlade undantag, uppfyller kompilatorn sitt löfte och påminner dig om dem igen. Till sist ska vi prata om finally
blocket (förlåt för ordleken). Detta är den sista delen av try-catch-finally
triumviratet för undantagshantering..
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!");
}
}
finally
I det här exemplet kommer koden inuti blocket att exekveras i båda fallen. Om koden i try
blocket körs i sin helhet utan att göra några undantag, finally
kommer blocket att köras till slut. Om koden inuti try
blocket avbryts av ett undantag och programmet hoppar till blocket, catch
kommer finally
blocket fortfarande att köras efter koden inuti catch
blocket. Varför är detta nödvändigt? Dess huvudsakliga syfte är att exekvera obligatorisk kod: kod som måste utföras oavsett omständigheterna. Till exempel frigör det ofta vissa resurser som används av programmet. I vår kod öppnar vi en ström för att läsa information från filen och skicka den till objektet BufferedReader
. Vi måste stänga vår läsare och frigöra resurserna. Detta måste göras oavsett vad - när programmet fungerar som det ska och när det ger ett undantag. Blocket finally
är ett mycket bekvämt ställe att göra detta:
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();
}
}
}
Nu är vi säkra på att vi tar hand om resurserna, oavsett vad som händer när programmet körs. :) Det är inte allt du behöver veta om undantag. Felhantering är ett superviktigt ämne inom programmering. Många artiklar ägnas åt det. I nästa lektion tar vi reda på vilka typer av undantag som finns och hur du skapar dina egna undantag. :) Vi ses då!
GO TO FULL VERSION