CodeGym /Java-blogg /Tilfeldig /Unntak: fangst og håndtering
John Squirrels
Nivå
San Francisco

Unntak: fangst og håndtering

Publisert i gruppen
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. Unntak: fangst og håndtering - 1 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 ".

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" - Throwableforeldreklassen. 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.
I alt har Java nesten 400 slike klasser! Hvorfor så mange? For å gjøre dem mer praktiske for programmerere å jobbe med. Tenk deg dette: du skriver et program, og mens det kjører genererer det et unntak som ser slik ut:

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, catchog finally. Unntak: fangst og håndtering - 2 Kode der programmereren tror et unntak kan forekomme, plasseres i tryblokken. 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 tryblokken. 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 FileNotFoundExceptionikke 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 catchblokken kjørte ikke, fordi vi "konfigurerte" denne blokken å fange ArithmeticException, og koden i tryblokken 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 tryblokken, vil koden som følger ikke bli utført. Utførelse av programmet "hopper" umiddelbart til catchblokken. 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 tryblokken prøver vi å dele med 0, noe som resulterer i en ArithmeticException. Følgelig tryvil 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 catchblokker. Hvis koden i tryblokken kanskje ikke gir én, men flere forskjellige typer unntak, kan du skrive en catchblokk 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 catchblokker. Hvis a FileNotFoundExceptionoppstår i tryblokken, vil den første catchblokken bli utført. Hvis en ArithmeticExceptionoppstår, vil den andre blokken bli utført. Du kunne skrive 50 catchblokker 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 tryblokk og legge til to catchblokker:

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 throwsi 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 throwsangir 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-catchblokk 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-finallyunntaket 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 finallyblokken bli utført i begge tilfeller. Hvis koden i tryblokken kjøres i sin helhet uten å gi noen unntak, finallyvil blokken kjøre til slutt. Hvis koden inne i tryblokken blir avbrutt av et unntak og programmet hopper til blokken catch, finallyvil blokken fortsatt kjøre etter koden inne i catchblokken. 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 finallyer 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!
Kommentarer
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION