Undantagshantering

Undantagshantering är en programmeringsspråksmekanism  utformad för att beskriva ett programs reaktion på körtidsfel och andra möjliga problem ( undantag ) som kan uppstå under programexekveringen och leda till omöjligheten (meningslösheten) att ytterligare bearbeta programmet av dess grundläggande algoritm. På ryska används också en kortare form av termen: " undantagshantering ".

Undantag

Allmän uppfattning om en exceptionell situation

Under exekveringen av ett program kan situationer uppstå när tillståndet för externa data, in- och utdataenheter eller datorsystemet som helhet gör ytterligare beräkningar i enlighet med den underliggande algoritmen omöjliga eller meningslösa. Klassiska exempel på sådana situationer ges nedan.

Typer av undantag

Undantagssituationer som uppstår under programdrift kan delas in i två huvudtyper: synkron och asynkron, vars reaktionsprinciper skiljer sig avsevärt.

Vissa typer av undantag kan klassificeras som antingen synkrona eller asynkrona. Till exempel bör en dividera-med-noll-instruktion formellt resultera i ett synkront undantag, eftersom det logiskt sett inträffar precis när den givna instruktionen exekveras, men på vissa plattformar, på grund av djup pipelining , kan undantaget faktiskt visa sig vara asynkront.

Undantagshanterare

Allmän beskrivning

I avsaknad av en inbyggd mekanism för hantering av undantag för applikationer är det vanligaste svaret på alla undantag att omedelbart avbryta exekvering och uppmana användaren med ett meddelande om undantagets natur. Vi kan säga att i sådana fall blir operativsystemet den enda och universella undantagshanteraren. Till exempel , Dr. Watson , som samlar in information om ett obehandlat undantag och skickar det till en speciell Microsoft -server .

Det är möjligt att ignorera undantaget och fortsätta, men denna taktik är farlig eftersom den leder till felaktiga programresultat eller fel senare. Till exempel, genom att ignorera felet att läsa från datablockfilen, kommer programmet att få till sitt förfogande inte den data som det var tänkt att läsa, utan några andra. Det är omöjligt att förutsäga resultatet av deras användning.

Hantering av exceptionella situationer av programmet självt består i det faktum att när en exceptionell situation inträffar, överförs kontrollen till någon fördefinierad hanterare  - ett kodblock, en procedur, en funktion som utför de nödvändiga åtgärderna.

Det finns två fundamentalt olika mekanismer för hur undantagshanterare fungerar.

Det finns två alternativ för att koppla en undantagshanterare till ett program: strukturell och icke-strukturell undantagshantering.

Icke-strukturell undantagshantering

Icke-strukturell undantagshantering implementeras som en mekanism för att registrera funktioner eller kommandohanterare för varje möjlig typ av undantag. Programmeringsspråket eller dess systembibliotek förser programmeraren med minst två standardprocedurer: registrera en hanterare och avregistrera en hanterare. Att anropa den första "binder" hanteraren till ett specifikt undantag, att anropa den andra kopplar bort denna "bindning". Om ett undantag inträffar avbryts exekveringen av huvudprogramkoden omedelbart och exekveringen av hanteraren börjar. Efter avslutad hanterare överförs kontrollen antingen till någon förutbestämd punkt i programmet, eller tillbaka till den punkt där undantaget inträffade (beroende på den specificerade bearbetningsmetoden - med eller utan retur). Oavsett vilken del av programmet som körs för närvarande, besvaras ett särskilt undantag alltid av den senast registrerade hanteraren för det. På vissa språk förblir en registrerad hanterare giltig endast inom det aktuella kodblocket (procedurer, funktioner), då krävs inte avregistreringsproceduren. Nedan finns ett villkorligt fragment av programkoden med icke-strukturell undantagshantering:

SetHandler(ErrorDB, Goto ErrorDB) // En hanterare är inställd för undantaget "DB Error" - kommandot "GoTo Error DB". ... // Här är operatorerna för att arbeta med databasen JumpTo ClearErrorDB // Ovillkorligt hoppkommando - förbigå undantagshanteraren ErrorDB: // label - övergången kommer att ske här i händelse av ett databasfel enligt den installerade hanteraren ... // DB undantagshanterare Ta bortOshDB: // label - hopp kommer att ske här om den kontrollerade koden exekveras utan ett databasfel. ClearHandler (DB-fel) // Hanterare borttagen

Icke-strukturell bearbetning är praktiskt taget det enda alternativet för att hantera asynkrona undantag, men det är obekvämt för synkrona undantag: du måste ofta anropa kommandon för att installera / ta bort hanterare, det finns alltid en risk att bryta logiken i programmet genom att hoppa över registreringen eller avregistrering av hanteraren.

Strukturell undantagshantering

Strukturell undantagshantering kräver obligatoriskt stöd från programmeringsspråket  - närvaron av speciella syntaktiska konstruktioner. En sådan konstruktion innehåller ett block med kontrollerad kod och en undantagshanterare. Den mest allmänna formen av en sådan konstruktion (villkorlig):

StartBlock ... // Kontrollerad kod ... if (villkor) sedan CreateException Exception2 ... Hanterareundantag1 ... // Handlarkod för Undantag1 Hanterareundantag2 ... // Handlarkod för Undantag2 HandlerRaw ... // Kod för hantering av tidigare obehandlade undantag EndBlock

Här är "StartBlock" och "EndBlock" nyckelord som avgränsar blocket med kontrollerad kod, och "Handler" är början på blocket för att hantera motsvarande undantag. Om ett undantag inträffar inuti blocket, från början till den första hanteraren, kommer det att ske en övergång till hanteraren som skrivits för det, varefter hela blocket avslutas och exekveringen fortsätter med kommandot efter det. Vissa språk har inga speciella nyckelord för att begränsa ett block med kontrollerad kod, istället kan undantagshanterare byggas in i några eller alla syntaktiska konstruktioner som kombinerar flera uttalanden. Så, till exempel i Ada-språket, kan alla sammansatta uttalanden (början - slutet) innehålla en undantagshanterare.

En "RawHandler" är en undantagshanterare som inte matchar någon av de som beskrivs ovan i detta block. Undantagshanterare i verkligheten kan beskrivas på olika sätt (en hanterare för alla undantag, en hanterare för varje typ av undantag), men i princip fungerar de på samma sätt: när ett undantag inträffar finns den första motsvarande hanteraren i detta block, dess kod exekveras, varefter exekveringsblocket slutar. Undantag kan uppstå både som ett resultat av programmeringsfel och genom att uttryckligen generera dem med lämpligt kommando (i exemplet kommandot "CreateException"). Ur hanterarnas synvinkel skiljer sig sådana artificiellt skapade undantag inte från alla andra.

Undantagshanteringsblock kan upprepade gånger bygga in varandra, antingen explicit (textuellt) eller implicit (till exempel anropas en procedur i ett block som i sig har ett undantagshanteringsblock). Om ingen av hanterarna i det aktuella blocket kan hantera undantaget, slutar exekveringen av detta block omedelbart och kontrollen överförs till nästa lämpliga hanterare på en högre hierarkinivå. Detta fortsätter tills en hanterare hittas och hanterar undantaget, eller tills undantaget lämnar programmerarspecificerade hanterare och skickas till standardsystemhanteraren som kraschar programmet.

Ibland är det obekvämt att slutföra behandlingen av ett undantag i det aktuella blocket, det vill säga det är önskvärt att när ett undantag inträffar i det aktuella blocket, utför hanteraren några åtgärder, men undantaget fortsätter att behandlas på en högre nivå ( vanligtvis händer detta när hanteraren av detta block inte helt hanterar undantaget utan bara delvis). I sådana fall genereras ett nytt undantag i undantagshanteraren eller återupptas, med hjälp av ett speciellt kommando, som tidigare stött på. Hanterarkoden är inte skyddad i detta block, så ett undantag som slängs i den kommer att hanteras i block på högre nivå.

Block med garanterat slutförande

Förutom kontrollerade kodblock för undantagshantering, kan programmeringsspråk stödja garanterade kompletteringsblock. Deras användning visar sig vara bekväm när det i ett visst kodblock, oavsett om några fel har uppstått, är nödvändigt att utföra vissa åtgärder innan det slutförs. Det enklaste exemplet: om en procedur dynamiskt skapar något lokalt objekt i minnet måste objektet förstöras (för att undvika minnesläckor) innan det avslutas innan det har skapats, oavsett om fel uppstod efter att det skapades eller inte. Denna funktion implementeras av kodblock i formen:

StartBlock ... // Huvudkod Komplettering ... // Slutkod EndBlock

Satserna (huvudkoden) som är inneslutna mellan nyckelorden "StartBlock" och "End" exekveras sekventiellt. Om inga undantag görs under exekveringen, exekveras sedan satserna mellan nyckelorden "End" och "EndBlock" (avslutningskod). Om ett undantag (något) inträffar under exekvering av huvudkoden, exekveras exitkoden omedelbart, varefter hela blocket är färdigt, och undantaget som har uppstått fortsätter att existera och fortplantas tills det fångas upp av något undantag på högre nivå hanteringsblock.

Den grundläggande skillnaden mellan ett block med garanterat slutförande och bearbetning är att det inte hanterar undantaget, utan endast garanterar exekvering av en viss uppsättning operationer innan bearbetningsmekanismen aktiveras. Det är lätt att se att ett block med garanterat slutförande enkelt implementeras med hjälp av den vanliga strukturerade bearbetningsmekanismen (för detta räcker det med att lägga kommandot för att kasta ett undantag omedelbart före slutförandet av det kontrollerade blocket och skriva hanterarkoden korrekt) , men närvaron av en separat konstruktion gör att du kan göra koden mer transparent och skyddar mot oavsiktliga fel. .

Flerspråkigt stöd

De flesta moderna programmeringsspråk som Ada , C++ , D , Delphi , Objective-C , Java , JavaScript , Eiffel , OCaml , Ruby , Python , Common Lisp , SML , PHP , alla .NET- plattformsspråk etc. har inbyggt stöd strukturell undantagshantering . På dessa språk, när ett språkstödt undantag inträffar, rullas samtalsstacken upp till den första undantagshanteraren av lämplig typ, och kontrollen överförs till hanteraren.

Förutom mindre syntaxskillnader finns det bara ett par undantagshanteringsalternativ. I de vanligaste av dem genereras ett undantag av en speciell operatör ( throweller raise), och undantaget i sig, ur programmets synvinkel, är något slags dataobjekt . Det vill säga, genereringen av ett undantag består av två steg: att skapa ett undantagsobjekt och att höja ett undantag med detta objekt som en parameter . Samtidigt medför inte konstruktionen av ett sådant föremål i sig ett undantag. På vissa språk kan ett undantagsobjekt vara ett objekt av vilken datatyp som helst (inklusive en sträng, ett nummer, en pekare och så vidare), på andra bara en fördefinierad undantagstyp (oftast har det namnet Exception) och ev. , dess härledda typer (typer -barn, om språket stöder objektfunktioner).

Omfattningen av hanterarna börjar med ett speciellt nyckelord tryeller bara en språkmarkör för blockstart (till exempel begin) och slutar före beskrivningen av hanterarna ( catch, except, resque). Det kan finnas flera hanterare, en efter en, och var och en kan ange vilken typ av undantag den hanterar. Som regel görs inget val av den mest lämpliga hanteraren och den första hanteraren som är typkompatibel med undantaget exekveras. Därför är ordningen på hanterarna viktig: om en hanterare som är kompatibel med många eller alla typer av undantag visas i texten före specifika hanterare för specifika typer, kommer specifika hanterare inte att användas alls.

Vissa språk tillåter också ett speciellt block ( else) som exekveras om inget undantag har kastats i motsvarande omfattning. Vanligare är möjligheten att garanterat slutförande av ett kodblock ( finally, ensure). Ett anmärkningsvärt undantag är C++, där det inte finns någon sådan konstruktion. Istället används ett automatiskt anrop till objektförstörare . Samtidigt finns det icke-standardiserade C++-tillägg som också stöder funktionalitet finally(till exempel i MFC ).

I allmänhet kan undantagshantering se ut så här (på något abstrakt språk):

prova { line = console . readLine (); if ( rad . längd () == 0 ) throw new EmptyLineException ( "Raden som läses från konsolen är tom!" ); konsol . printLine ( "Hej %s!" % rad ); } catch ( EmptyLineException undantag ) { console . printLine ( "Hej!" ); } catch ( Undantag undantag ) { konsol . printLine ( "Fel: " + undantag . meddelande ()); } annat { konsol . printLine ( "Programmet körde utan undantag" ); } finally { konsol . printLine ( "Programmet avslutas" ); }

Vissa språk kan bara ha en hanterare som hanterar olika typer av undantag på egen hand.

Fördelar och nackdelar

Fördelarna med att använda undantag är särskilt märkbara när man utvecklar bibliotek med procedurer och programvarukomponenter som är inriktade på massanvändning. I sådana fall vet utvecklaren ofta inte exakt hur undantaget ska hanteras (när man skriver en universell procedur för att läsa från en fil är det omöjligt att förutse reaktionen på ett fel i förväg, eftersom denna reaktion beror på att programmet använder proceduren), men han behöver inte detta - det räcker med att kasta ett undantag A vars hanterare tillhandahålls för att implementera av användaren av komponenten eller proceduren. Det enda alternativet till undantag i sådana fall är att returnera felkoder, som tvingas skickas längs kedjan mellan flera nivåer i programmet tills de kommer till platsen för bearbetning, vilket gör att koden blir rörig och dess förståelighet minskar. Att använda undantag för felkontroll förbättrar kodläsbarheten genom att separera felhanteringen från själva algoritmen och gör det lättare att programmera och använda komponenter från tredje part. Och felhantering kan centraliseras i .

Tyvärr är implementeringen av undantagshanteringsmekanismen mycket språkberoende, och även kompilatorer av samma språk på samma plattform kan ha betydande skillnader. Detta tillåter inte att undantag överförs transparent mellan delar av ett program som är skrivna på olika språk; till exempel är bibliotek som stöder undantag i allmänhet olämpliga för användning i program på andra språk än de som de är designade för, och ännu mer på språk som inte stöder en mekanism för hantering av undantag. Detta tillstånd begränsar avsevärt möjligheten att använda undantag, till exempel i UNIX och dess kloner och under Windows, eftersom de flesta systemprogramvaran och lågnivåbiblioteken i dessa system är skrivna på C-språket, vilket inte stöder undantag. Följaktligen, för att arbeta med API:et för sådana system som använder undantag, måste man skriva wrapper-bibliotek vars funktioner skulle analysera returkoderna för API-funktioner och, om nödvändigt, skulle generera undantag.

Undantagsstöd komplicerar språket och kompilatorn. Det minskar också hastigheten på programmet, eftersom kostnaden för att hantera ett undantag vanligtvis är högre än kostnaden för att hantera en felkod. På hastighetskritiska platser i ett program rekommenderas det därför inte att ta upp och hantera undantag, även om det bör noteras att i applikationsprogrammering är det mycket sällsynta fall där skillnaden i hastigheten för bearbetning av undantag och returkoder verkligen är betydande. .

Att implementera undantag korrekt kan vara svårt på språk med automatiska destructor -anrop . När ett undantag inträffar i ett block är det nödvändigt att automatiskt anropa destruktörerna för objekt som skapats i detta block, men bara de som ännu inte har raderats på vanligt sätt. Dessutom kommer kravet att avbryta den aktuella operationen när ett undantag inträffar i konflikt med kravet på obligatorisk automatisk radering på språk med autodestruktörer: om ett undantag inträffar i förstöraren, kommer kompilatorn antingen att tvingas ta bort ett ofullständigt frigjort objekt , eller så förblir objektet existerande, det vill säga en minnesläcka inträffar . Som ett resultat av detta är det helt enkelt förbjudet att skapa ouppfångade undantag i destruktörer i vissa fall.

Joel Spolsky menar att kod utformad för att hantera undantag förlorar sin linjäritet och förutsägbarhet. Om i klassisk kod utgångar från ett block, procedur eller funktion bara hittas där programmeraren uttryckligen angav dem, kan i kod med undantag (potentiellt) ett undantag förekomma i vilken sats som helst och det är omöjligt att ta reda på exakt var undantag kan inträffa genom att analysera själva koden. I kod som är designad för undantag är det omöjligt att förutsäga var kodblocket kommer att avslutas, och varje påstående måste betraktas som potentiellt den sista i blocket, som ett resultat av detta ökar kodkomplexiteten och tillförlitligheten minskar. [ett]

Dessutom, i komplexa program, finns det stora "högar" av operatörer try ... finallyoch try ... catch( try ... except), om du inte använder aspekter.

Markerade undantag

Vissa problem med enkel undantagshantering

Inledningsvis (till exempel i C++) fanns det ingen formell disciplin för att beskriva, generera och hantera undantag: vilket undantag som helst kan tas upp var som helst i programmet, och om det inte finns någon hanterare för det i anropsstacken är programexekveringen avbröts onormalt. Om en funktion (särskilt en biblioteksfunktion) ger undantag, måste programmet som använder den fånga upp alla för att vara stabila. När ett av de möjliga undantagen av någon anledning inte hanteras, kommer programmet att krascha oväntat.

Sådana effekter kan bekämpas genom organisatoriska åtgärder: beskriva möjliga undantag som förekommer i biblioteksmoduler i lämplig dokumentation. Men samtidigt finns det alltid en chans att hoppa över den nödvändiga hanteraren på grund av ett oavsiktligt fel eller inkonsekvens av dokumentationen med koden (vilket inte alls är ovanligt). För att helt eliminera förlusten av undantagshantering måste du specifikt lägga till en "varannan" undantagshanteringsgren till dina hanterare (som garanterat kommer att fånga upp alla, även tidigare okända undantag), men den här vägen ut är inte alltid optimal. Att dölja alla möjliga undantag kan dessutom leda till en situation där allvarliga och svåra att hitta buggar döljs.

Markerad undantagsmekanism

Senare introducerade ett antal språk, som Java, kontrollerade undantag . Kärnan i denna mekanism är att lägga till följande regler och begränsningar för språket:

  • Beskrivningen av en funktion (eller klassmetod) listar uttryckligen alla typer av undantag som den kan skapa.
  • En funktion som anropar en funktion eller metod med deklarerade undantag måste antingen innehålla en hanterare för vart och ett av dessa undantag, eller i sin tur ange denna typ som genereras av den i dess beskrivning.
  • Kompilatorn kontrollerar förekomsten av en hanterare i funktionskroppen eller en undantagspost i dess rubrik. Reaktionen på förekomsten av ett odeklarerat och ett obehandlat undantag kan vara olika. Till exempel, i Java, om kompilatorn upptäcker möjligheten till ett undantag som inte beskrivs i funktionshuvudet och inte bearbetas i det, anses programmet vara felaktigt och kompileras inte. I C++ gör förekomsten av ett odeklarerat och obehandlat undantag i en funktion att programmet avslutas omedelbart; Samtidigt indikerar frånvaron av en lista över deklarerade undantag i en funktion möjligheten till eventuella undantag och standardordningen för deras hantering med extern kod.

Externt (i Java-språket) ser implementeringen av denna metod ut så här:

int getVarValue ( String varName ) kastar SQLException { ... // metodkod, som eventuellt innehåller anrop som kan ge ett SQLException } // Kompileringsfel - inget undantag deklarerades eller fångades int eval1 ( String expression ) { ... int a = prev + getVarValue ( "abc" ); ... } // Korrekt - ett undantag har deklarerats och kommer att skickas på int eval2 ( String expression ) throws SQLException { ... int a = prev + getVarValue ( "abc" ); ... } // Korrekt - undantaget fångas inuti metoden och går inte utanför int eval3 ( String expression ) { ... try { int a = prev + getVarValue ( "abc" ); } catch ( SQLException ex ) { // Hantera undantaget } ... }

Här deklareras getVarValue-metoden att kasta ett SQLException. Därför måste varje metod som använder den antingen fånga undantaget eller förklara att det kastas. I det här exemplet kommer metoden eval1 att resultera i ett kompileringsfel eftersom den anropar metoden getVarValue men inte fångar upp eller deklarerar undantaget. Metoden eval2 deklarerar undantaget, och metoden eval3 fångar och hanterar det, som båda är korrekta för att hantera undantaget som gettVarValue-metoden ger.

Fördelar och nackdelar

Markerade undantag minskar antalet situationer där ett undantag som kunde ha hanterats orsakade ett kritiskt fel i programmet, eftersom kompilatorn håller reda på förekomsten av hanterare . Detta är särskilt användbart för kodändringar där en metod som tidigare inte kunde skapa ett undantag av X-typ börjar göra det; kompilatorn kommer automatiskt att spåra alla instanser av dess användning och leta efter lämpliga hanterare.

En annan användbar egenskap hos kontrollerade undantag är att de bidrar till en meningsfull skrivning av hanterare: programmeraren ser tydligt den fullständiga och korrekta listan över undantag som kan inträffa på en given plats i programmet, och kan istället skriva en meningsfull hanterare för var och en av dem. att skapa en "för säkerhets skull" En gemensam hanterare för alla undantag som reagerar lika på alla onormala situationer.

Markerade undantag har också nackdelar.

  • De tvingar dig att skapa undantagshanterare som programmeraren i princip inte kan hantera, såsom I/O-fel i en webbapplikation. Detta leder till uppkomsten av "dumma" hanterare som inte gör någonting eller duplicerar den systemkritiska felhanteraren (till exempel visar undantagsanropsstacken) och som ett resultat bara skräpar ner koden.
  • Det blir omöjligt att lägga till ett nytt markerat undantag till en metod som beskrivs i ett bibliotek , eftersom detta bryter bakåtkompatibiliteten . (Detta gäller även för icke-biblioteksmetoder, men i det här fallet är problemet mindre betydande, eftersom all kod så småningom blir tillgänglig och kan återvinnas).

På grund av dessa brister förbigås denna mekanism ofta när man använder markerade undantag. Till exempel deklarerar många bibliotek att alla metoder ger någon allmän klass av undantag (till exempel Exception), och hanterare skapas endast för denna typ av undantag. Resultatet är att kompilatorn tvingar dig att skriva undantagshanterare även där de objektivt sett inte behövs, och det blir omöjligt att avgöra, utan att läsa källorna, vilka underklasser av de deklarerade undantagen som kastas av metoden för att hänga olika hanterare på dem. Ett mer korrekt tillvägagångssätt är att fånga upp nya undantag inuti metoden, genererade av den anropade koden, och, om nödvändigt, skicka undantaget vidare - "linda" det till ett undantag som redan returnerats av metoden. Till exempel, om en metod ändras så att den börjar komma åt databasen istället för filsystemet, kan den själv fånga SQLExceptionoch kasta en nyskapad istället IOException, vilket anger det ursprungliga undantaget som orsak. Det rekommenderas vanligtvis att initialt deklarera exakt de undantag som anropskoden måste hantera. Säg, om metoden hämtar indata, är det tillrådligt att den deklarerar IOException, och om den fungerar med SQL-frågor, bör den, oavsett felets natur, deklarera SQLException. I vilket fall som helst måste uppsättningen av undantag som skapas av en metod noggrant övervägas. Om det behövs är det vettigt att skapa dina egna undantagsklasser, som härrör från Exception eller andra lämpliga markerade undantag.

Undantag som inte kräver kontroll

Det är omöjligt att göra alla undantag kontrollerbara i allmänhet, eftersom vissa exceptionella situationer till sin natur är sådana att deras förekomst är möjlig på vilken eller nästan vilken plats som helst i programmet, och programmeraren kan inte förhindra dem. Samtidigt är det meningslöst att ange sådana undantag i funktionsbeskrivningen, eftersom detta skulle behöva göras för varje funktion utan att göra programmet tydligare. I grund och botten är dessa undantag relaterade till en av två typer:

  • Undantag, som är allvarliga fel som "teoretiskt" inte bör förekomma, och som normalt inte ska hanteras av programmet. Sådana fel kan uppstå både i den externa miljön i förhållande till programmet och inuti det . Ett exempel på en sådan situation skulle vara ett körtidsfel i ett Java-program. Det är potentiellt möjligt med exekvering av vilket kommando som helst; med de mest sällsynta undantagen kan ett applikationsprogram inte ha en meningsfull hanterare för ett sådant fel - trots allt, om exekveringsmiljön inte fungerar korrekt, vilket indikeras av själva undantaget, finns det ingen garanti för att hanteraren också kommer att vara utförs korrekt.
  • Undantag vid körning, vanligtvis förknippade med programmeringsfel. Sådana undantag uppstår på grund av logiska fel av utvecklaren eller otillräckliga kontroller i koden. Till exempel innebär ett fel vid åtkomst till en oinitierad (null)-pekare vanligtvis att programmeraren antingen missade variabelinitiering någonstans, eller inte kontrollerade om minnet faktiskt allokerades vid tilldelning av dynamiskt minne. Både den första och den andra kräver korrigering av programkoden, och inte skapandet av hanterare.

Det är ologiskt och obekvämt att ta bort sådana fel ur undantagshanteringssystemet, om så bara för att de ibland fortfarande fångas upp och bearbetas. I system med kontrollerade undantag tas därför vissa undantagstyper bort från kontrollmekanismen och fungerar på traditionellt sätt. I Java är dessa undantagsklasser som ärvts från java.lang.Error - fatal errors och java.lang.RuntimeException - runtime-fel, vanligtvis relaterade till kodningsfel eller otillräckliga kontroller i programkoden (dåligt argument, åtkomst med nollreferens, exit out of bounds of the array , felaktig monitorstatus, etc.).

Gränsen mellan ett "korrigerbart" och ett "dödligt" fel är mycket godtycklig. Till exempel är ett I/O-fel i ett skrivbordsprogram vanligtvis "korrigerbart" och det är möjligt att informera användaren om det och fortsätta köra programmet. I ett webbskript är det också "dödligt" - om det hände hände något dåligt med exekveringsmiljön och du måste sluta med att visa ett meddelande.

Se även

Anteckningar

  1. 13 - Joel om programvara . Hämtad 7 oktober 2012. Arkiverad från originalet 22 oktober 2012.

Länkar