1. Typer av undantag
Alla undantag är indelade i 4 typer, som faktiskt är klasser som ärver varandra.
Throwableklass
Basklassen för alla undantag är Throwableklassen. Klassen Throwableinnehåller koden som skriver den aktuella anropsstacken (stackspårning av den aktuella metoden) till en array. Vi kommer att lära oss vad en stackspårning är lite senare.
Kastoperatören kan bara acceptera ett objekt som härrör från klassen . ThrowableOch även om du teoretiskt sett kan skriva kod som throw new Throwable();, brukar ingen göra detta. Huvudsyftet med Throwableklassen är att ha en ensamförälderklass för alla undantag.
Errorklass
Nästa undantagsklass är Errorklassen, som direkt ärver Throwableklassen. Java-maskinen skapar objekt av Errorklassen (och dess avkomlingar) när allvarliga problem har uppstått . Till exempel ett hårdvarufel, otillräckligt minne, etc.
Vanligtvis, som programmerare, finns det inget du kan göra i en situation där ett sådant fel (den typ som en Errorska kastas för) har inträffat i programmet: dessa fel är för allvarliga. Allt du kan göra är att meddela användaren att programmet kraschar och/eller skriva all känd information om felet till programloggen.
Exceptionklass
Klasserna Exceptionoch RuntimeExceptionär för vanliga fel som inträffar vid driften av många metoder. Målet med varje kastat undantag är att fångas av ett catchblock som vet hur det ska hanteras på rätt sätt.
När en metod av någon anledning inte kan slutföra sitt arbete, bör den omedelbart meddela anropsmetoden genom att kasta ett undantag av lämplig typ.
Med andra ord, om en variabel är lika med null, kommer metoden att kasta en NullPointerException. Om de felaktiga argumenten skickades till metoden kommer den att kasta en InvalidArgumentException. Om metoden av misstag delar med noll, kommer den att kasta en ArithmeticException.
RuntimeExceptionklass
RuntimeExceptionsär en delmängd av Exceptions. Vi skulle till och med kunna säga att det RuntimeExceptionär en lätt version av vanliga undantag ( Exception) — färre krav och begränsningar ställs på sådana undantag
Du kommer att lära dig skillnaden mellan Exceptionoch RuntimeExceptionsenare.
2. Throws: markerade undantag

Alla Java-undantag delas in i två kategorier: markerad och omarkerad .
Alla undantag som ärver RuntimeExceptioneller Erroranses vara okontrollerade undantag . Alla andra är markerade undantag .
Tjugo år efter att kontrollerade undantag infördes, ser nästan alla Java-programmerare på detta som en bugg. I populära moderna ramverk är 95 % av alla undantag avmarkerade. C#-språket, som nästan kopierade Java exakt, lade inte till markerade undantag .
Vad är den största skillnaden mellan markerade och omarkerade undantag?
Det finns ytterligare krav på kontrollerade undantag. Grovt sett är de dessa:
Krav 1
Om en metod ger ett markerat undantag måste den ange typen av undantag i sin signatur . På så sätt är varje metod som anropar den medveten om att detta "meningsfulla undantag" kan förekomma i den.
Ange markerade undantag efter metodparametrarna efter throwsnyckelordet (använd inte thrownyckelordet av misstag). Det ser ut ungefär så här:
type method (parameters) throws exception
Exempel:
| kontrollerat undantag | okontrollerat undantag |
|---|---|
|
|
I exemplet till höger ger vår kod ett omarkerat undantag - ingen ytterligare åtgärd krävs. I exemplet till vänster ger metoden ett markerat undantag, så throwsnyckelordet läggs till i metodsignaturen tillsammans med typen av undantag.
Om en metod förväntar sig att skapa flera markerade undantag måste alla anges efter throwsnyckelordet, avgränsade med kommatecken. Ordningen är inte viktig. Exempel:
public void calculate(int n) throws Exception, IOException
{
if (n == 0)
throw new Exception("n is null!");
if (n == 1)
throw new IOException("n is 1");
}
Krav 2
Om du anropar en metod som har kontrollerat undantag i sin signatur kan du inte bortse från att den kastar dem.
Du måste antingen fånga alla sådana undantag genom att lägga till catchblock för var och en, eller genom att lägga till dem i en throwssats för din metod.
Det är som om vi säger: " Dessa undantag är så viktiga att vi måste fånga dem. Och om vi inte vet hur vi ska hantera dem, då måste alla som kan anropa vår metod meddelas att sådana undantag kan förekomma i den.
Exempel:
Föreställ dig att vi skriver en metod för att skapa en värld befolkad av människor. Det initiala antalet personer skickas som ett argument. Så vi måste lägga till undantag om det är för få personer.
| Skapar jorden | Notera |
|---|---|
|
Metoden ger potentiellt två markerade undantag:
|
Detta metodanrop kan hanteras på tre sätt:
1. Fånga inga undantag
Detta görs oftast när metoden inte vet hur den ska hantera situationen på rätt sätt.
| Koda | Notera |
|---|---|
|
Anropsmetoden fångar inte upp undantagen och måste informera andra om dem: den lägger till dem i sin egen throwsklausul |
2. Fånga några av undantagen
Vi hanterar de fel vi kan hantera. Men de vi inte förstår, vi kastar upp dem till anropsmetoden. För att göra detta måste vi lägga till deras namn till throws-satsen:
| Koda | Notera |
|---|---|
|
Den som ringer fångar bara ett markerat undantag — LonelyWorldException. Det andra undantaget måste läggas till i sin signatur, vilket anger det efter throwsnyckelordet |
3. Fånga alla undantag
Om metoden inte ger undantag från anropsmetoden, är anropsmetoden alltid säker på att allt fungerade bra. Och det kommer inte att kunna vidta några åtgärder för att fixa en exceptionell situation.
| Koda | Notera |
|---|---|
|
Alla undantag fångas i denna metod. Den som ringer kommer att vara säker på att allt gick bra. |
3. Undantag för inpackning
Kontrollerade undantag verkade coolt i teorin, men visade sig vara en enorm frustration i praktiken.
Anta att du har en superpopulär metod i ditt projekt. Det kallas från hundratals platser i ditt program. Och du bestämmer dig för att lägga till ett nytt markerat undantag till det. Och det kan mycket väl vara så att detta kontrollerade undantag är riktigt viktigt och så speciellt att bara main()metoden vet vad den ska göra om den fångas.
Det betyder att du måste lägga till det markerade undantaget till throwsklausulen för varje metod som anropar din superpopulära metod . Samt i throwsklausulen av alla metoder som kallar dessa metoder. Och av metoderna som kallar dessa metoder.
Som ett resultat throwsfår klausulerna i hälften av metoderna i projektet ett nytt kontrollerat undantag. Och naturligtvis omfattas ditt projekt av tester, och nu kompileras inte testerna. Och nu måste du redigera kastsatserna i dina tester också.
Och sedan måste all din kod (alla ändringar i hundratals filer) granskas av andra programmerare. Och vid det här laget frågar vi oss själva varför vi gjorde så många blodiga förändringar i projektet? Arbetsdag(ar?) och trasiga tester - allt för att lägga till ett markerat undantag?
Och naturligtvis finns det fortfarande problem relaterade till arv och metodöverstyrning. Problemen som kommer från kontrollerade undantag är mycket större än nyttan. Summan av kardemumman är att nu är få människor som älskar dem och få människor använder dem.
Det finns dock fortfarande mycket kod (inklusive standard Java-bibliotekskod) som innehåller dessa markerade undantag. Vad ska man göra med dem? Vi kan inte ignorera dem, och vi vet inte hur vi ska hantera dem.
Java-programmerare föreslog att linda in kontrollerade undantag i RuntimeException. Med andra ord, fånga alla markerade undantag och skapa sedan omarkerade undantag (till exempel ) RuntimeExceptionoch kasta dem istället. Att göra det ser ut ungefär så här:
try
{
// Code where a checked exception might occur
}
catch(Exception exp)
{
throw new RuntimeException(exp);
}
Det är inte en särskilt snygg lösning, men det finns inget brottsligt här: undantaget var helt enkelt stoppat i en RuntimeException.
Om så önskas kan du enkelt hämta den därifrån. Exempel:
| Koda | Notera |
|---|---|
|
Få undantaget lagrat inuti RuntimeExceptionobjektet. Variabeln causekanske nullBestäm dess typ och konvertera den till en markerad undantagstyp. |
4. Fånga flera undantag
Programmerare hatar verkligen att duplicera kod. De kom till och med på en motsvarande utvecklingsprincip: Don't Repeat Yourself (DRY) . Men vid hantering av undantag finns det ofta tillfällen då ett tryblock följs av flera catchblock med samma kod.
Eller det kan finnas 3 catchblock med samma kod och ytterligare 2 catchblock med annan identisk kod. Detta är en standardsituation när ditt projekt hanterar undantag på ett ansvarsfullt sätt.
Från och med version 7 lades i Java-språket till möjligheten att specificera flera typer av undantag i ett enda catchblock. Det ser ut ungefär så här:
try
{
// Code where an exception might occur
}
catch (ExceptionType1 | ExceptionType2 | ExceptionType3 name)
{
// Exception handling code
}
Du kan ha hur många catchblock du vill. Ett enda catchblock kan dock inte specificera undantag som ärver varandra. Du kan med andra ord inte skriva catch ( Exception| RuntimeExceptione), eftersom RuntimeExceptionklassen ärver Exception.
5. Anpassade undantag
Du kan alltid skapa din egen undantagsklass. Du skapar helt enkelt en klass som ärver RuntimeExceptionklassen. Det kommer att se ut ungefär så här:
class ClassName extends RuntimeException
{
}
Vi kommer att diskutera detaljerna när du lär dig OOP, arv, konstruktörer och metodöverstyrning.
Men även om du bara har en enkel klass som denna (helt utan kod), kan du fortfarande kasta undantag baserat på den:
| Koda | Notera |
|---|---|
|
Kasta en omarkerad MyException . |
I Java Multithreading- uppdraget kommer vi att ta en djupdykning i att arbeta med våra egna anpassade undantag.
GO TO FULL VERSION