Hei! Jeg hater å nevne det, men en stor del av en programmerers arbeid er å håndtere feil. Oftest hans eller hennes egen. Det viser seg at det ikke er noen som ikke gjør feil. Og det finnes heller ingen slike programmer.
Selvfølgelig, når du arbeider med en feil, er det viktigste å forstå årsaken. Og mange ting kan forårsake feil i et program. På et tidspunkt spurte Javas skapere seg selv hva som skulle gjøres med de mest sannsynlige programmeringsfeilene? Å helt unngå dem er ikke realistisk, programmerere er i stand til å skrive ting du ikke engang kan forestille deg. :) Så vi må gi språket en mekanisme for å jobbe med feil. Med andre ord, hvis det er en feil i programmet ditt, trenger du et slags skript for hva du skal gjøre videre. Hva skal et program gjøre når det oppstår en feil? I dag vil vi bli kjent med denne mekanismen. Det kalles " unntak i Java ".
Kode der programmereren tror et unntak kan forekomme, plasseres i

Hva er et unntak?
Et unntak er en eksepsjonell, ikke-planlagt situasjon som oppstår mens et program kjører. Det er mange unntak. For eksempel skrev du kode som leser tekst fra en fil, og viser den første linjen.
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 hva om det ikke finnes en slik fil! Programmet vil generere et unntak: FileNotFoundException
. Utdata: Unntak i tråden "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Systemet finner ikke den angitte banen) I Java er hvert unntak representert av en egen klasse. Alle disse unntaksklassene stammer fra en felles "stamfar" - Throwable
foreldreklassen. Navnet på en unntaksklasse gjenspeiler vanligvis kort hvorfor unntaket skjedde:
FileNotFoundException
(filen ble ikke funnet)ArithmeticException
(et unntak oppsto under utførelse av en matematisk operasjon)ArrayIndexOutOfBoundsException
(indeksen er utenfor matrisens grenser). Dette unntaket oppstår for eksempel hvis du prøver å vise posisjon 23 i en matrise som bare har 10 elementer.
Exception in thread "main"
Åhhhh. :/ Det hjelper lite. Det er ikke klart hva feilen betyr eller hvor den kom fra. Det er ingen nyttig informasjon her. Men det store utvalget av unntaksklasser i Java gir programmereren det som betyr mest: typen feil og dens sannsynlige årsak (innebygd i klassenavnet). Det er en helt annen ting å se
Exception in thread "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (The system cannot find the specified path)
Det er umiddelbart klart hva problemet kan være og hvor du skal begynne å grave for å løse problemet! Unntak, som forekomster av alle klasser, er objekter.
Unntak for fangst og håndtering
Java har spesielle kodeblokker for å arbeide med unntak:try
, catch
og finally
. 
try
blokken. Det betyr ikke at det vil forekomme unntak her. Det betyr at det kan forekomme her, og programmereren er klar over denne muligheten. Typen feil du forventer skal oppstå plasseres i blokken catch
. Denne inneholder også all koden som skal kjøres hvis et unntak oppstår. Her er et eksempel:
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!");
}
}
Utgang: Feil! Fil ikke funnet! Vi legger koden vår i to blokker. I den første blokken forventer vi at en "File not found"-feil kan oppstå. Dette er try
blokken. I den andre forteller vi programmet hva det skal gjøre hvis det oppstår en feil. Og den spesifikke feiltypen: FileNotFoundException
. Hvis vi setter en annen unntaksklasse i parentesen til blokken catch
, blir den FileNotFoundException
ikke fanget opp.
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: Unntak i tråden "main" java.io.FileNotFoundException: C:\Users\Username\Desktop\test.txt (Systemet finner ikke den angitte banen) Koden i catch
blokken kjørte ikke, fordi vi "konfigurerte" denne blokken å fange ArithmeticException
, og koden i try
blokken kastet en annen type: FileNotFoundException
. Vi skrev ingen kode for å håndtere FileNotFoundException
, så programmet viser standardinformasjonen for FileNotFoundException
. Her må du være oppmerksom på tre ting. Nummer en. Når et unntak oppstår på en linje i try
blokken, vil koden som følger ikke bli utført. Utførelse av programmet "hopper" umiddelbart til catch
blokken. For eksempel:
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!");
}
}
Utgang: Del med null Programmet hoppet til fangstblokken! Feil! Du kan ikke dele på null! På den andre linjen i try
blokken prøver vi å dele med 0, noe som resulterer i en ArithmeticException
. Følgelig try
vil ikke linjene 3-9 i blokken bli utført. Som vi sa, begynner programmet umiddelbart å utføre blokken catch
. Nummer to. Det kan være flere catch
blokker. Hvis koden i try
blokken kanskje ikke gir én, men flere forskjellige typer unntak, kan du skrive en catch
blokk for hver 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 dette eksemplet har vi skrevet to catch
blokker. Hvis a FileNotFoundException
oppstår i try
blokken, vil den første catch
blokken bli utført. Hvis en ArithmeticException
oppstår, vil den andre blokken bli utført. Du kunne skrive 50 catch
blokker hvis du ville. Selvfølgelig er det bedre å ikke skrive kode som kan gi 50 forskjellige typer unntak. :) Tredje. Hvordan vet du hvilke unntak koden din kan gi? Vel, du kan kanskje gjette noen av dem, men det er umulig for deg å holde alt i hodet. Java-kompilatoren kjenner derfor de vanligste unntakene og situasjonene der de kan forekomme. For eksempel, hvis du skriver kode som kompilatoren vet kan gi to typer unntak, kompileres ikke koden din før du håndterer dem. Vi ser eksempler på dette nedenfor. Nå noen ord om unntakshåndtering. Det er 2 måter å håndtere unntak på. Vi har allerede møtt den første: metoden kan håndtere unntaket selv i en catch()
blokk. Det er et annet alternativ: metoden kan kaste unntaket opp i anropsstakken på nytt. Hva betyr det? For eksempel har vi en klasse med samme printFirstString()
metode, som leser en fil og viser dens første linje:
public static void printFirstString(String filePath) {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
For øyeblikket kompilerer ikke koden vår, fordi den har uhåndterte unntak. I linje 1 angir du banen til filen. Kompilatoren vet at slik kode lett kan produsere en FileNotFoundException
. På linje 3 leser du teksten fra filen. Denne prosessen kan lett resultere i en IOException
(inndata-/utdatafeil). Nå sier kompilatoren til deg: "Dude, jeg vil ikke godkjenne denne koden og jeg vil ikke kompilere den før du forteller meg hva jeg skal gjøre hvis ett av disse unntakene inntreffer. Og de kan sikkert skje basert på koden du skrev !" Det er ingen måte å komme utenom det: du må håndtere begge deler! Vi vet allerede om den første unntakshåndteringsmetoden: vi må legge koden vår i en try
blokk og legge til to catch
blokker:
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 dette er ikke det eneste alternativet. Vi kunne ganske enkelt kastet unntaket høyere i stedet for å skrive feilhåndteringskode inne i metoden. Dette gjøres ved å bruke nøkkelordet throws
i metodedeklarasjonen:
public static void printFirstString(String filePath) throws FileNotFoundException, IOException {
BufferedReader reader = new BufferedReader(new FileReader(filePath));
String firstString = reader.readLine();
System.out.println(firstString);
}
Etter nøkkelordet throws
angir vi en kommadelt liste over alle typer unntak som metoden kan gi. Hvorfor? Nå, hvis noen ønsker å kalle printFirstString()
metoden i programmet, må han eller hun (ikke du) implementere unntakshåndtering. Anta for eksempel at et annet sted i programmet skrev en av kollegene dine en metode som kaller printFirstString()
metoden din:
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 en feil! Denne koden vil ikke kompilere! Vi skrev ikke unntakshåndteringskode i printFirstString()
metoden. Som et resultat av dette faller denne oppgaven nå på skuldrene til de som bruker metoden. Med andre ord methodWrittenByYourColleague()
har metoden nå de samme 2 alternativene: den må enten bruke en try-catch
blokk for å håndtere begge unntakene, eller kaste dem på nytt.
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 andre tilfellet må den neste metoden i anropsstakken – den som ringer methodWrittenByYourColleague()
– håndtere unntakene. Det er derfor vi kaller dette «kaste eller gi unntaket opp». Hvis du kaster unntak oppover med nøkkelordet throws
, vil koden din kompileres. På dette tidspunktet ser det ut til at kompilatoren sier: "Ok, ok. Koden din inneholder en haug med potensielle unntak, men jeg skal kompilere den. Men vi kommer tilbake til denne samtalen!" Og når du kaller en metode som har uhåndterte unntak, oppfyller kompilatoren løftet og minner deg om dem igjen. Til slutt skal vi snakke om blokken finally
(beklager ordspillet). Dette er siste del av try-catch-finally
unntaket som håndterer triumviratet..
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!");
}
}
I dette eksemplet vil koden inne i finally
blokken bli utført i begge tilfeller. Hvis koden i try
blokken kjøres i sin helhet uten å gi noen unntak, finally
vil blokken kjøre til slutt. Hvis koden inne i try
blokken blir avbrutt av et unntak og programmet hopper til blokken catch
, finally
vil blokken fortsatt kjøre etter koden inne i catch
blokken. Hvorfor er dette nødvendig? Hovedformålet er å utføre obligatorisk kode: kode som må utføres uavhengig av omstendighetene. For eksempel frigjør det ofte noen ressurser som brukes av programmet. I koden vår åpner vi en strøm for å lese informasjon fra filen og sende den til objektet BufferedReader
. Vi må lukke leseren vår og frigjøre ressursene. Dette må gjøres uansett - når programmet fungerer som det skal, og når det gir et unntak. Blokken finally
er et veldig praktisk sted å gjøre dette:
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();
}
}
}
Nå er vi sikre på at vi tar vare på ressursene, uavhengig av hva som skjer når programmet kjører. :) Det er ikke alt du trenger å vite om unntak. Feilhåndtering er et superviktig tema i programmering. Mange artikler er viet til det. I neste leksjon finner vi ut hvilke typer unntak som finnes og hvordan du kan lage dine egne unntak. :) Ser deg da!
GO TO FULL VERSION