- Leren
- Informatica & ICT
- Programmeren
- Java programmeren: klassen, overerving, collecties en streams
- Excepties en bestanden in Java
Excepties en bestanden in Java
try, catch en finally. Werp je eigen excepties op en ontwerp exceptieklassen die precies aangeven wat er misging. Lees en schrijf vervolgens tekstbestanden met aandacht voor paden, tekencoderingen en regeleinden, sluit elk bestand veilig met try-with-resources en bouw een programma dat omgaat met ontbrekende bestanden en ongeldige gegevens.BeginnerLes 9 van 11 in Cursus Java programmeren: klassen, overerving, collecties en streams
Wat je leert
- Herken een exceptie wanneer een programma vastloopt, lees de stacktrace om te vinden waar en waarom dat gebeurde en plaats elke exceptie in de klassenhiërarchie van Java als gecontroleerd of ongecontroleerd.
- Lees een exceptie en de bijbehorende stacktraceLeg uit wat er gebeurt wanneer een instructie een exceptie opwerpt, noem veelvoorkomende uitvoeringsexcepties en hun oorzaken en gebruik een stacktrace om het probleem te lokaliseren.
- Deel excepties in als gecontroleerd of ongecontroleerdPlaats elke exceptie in de hiërarchie onder Throwable, bepaal of die gecontroleerd of ongecontroleerd is en pas de regel toe dat gecontroleerde excepties moeten worden opgevangen of gedeclareerd.
- Lees een exceptie en de bijbehorende stacktrace
- Vang excepties op die een programma kan verwachten, herstel de uitvoering op een zinvolle manier, handel meerdere soorten in de juiste volgorde af en voer opruimcode uit ongeacht wat er gebeurt.
- Vang een exceptie op en herstel de uitvoeringSchrijf een try-catch-instructie, volg hoe de uitvoering erdoorheen verloopt en herstel de uitvoering na ongeldige invoer in plaats van het programma te laten vastlopen.
- Handel meerdere exceptietypen in de juiste volgorde afGebruik meerdere catch-clausules of een meervoudige catch-clausule, orden die van specifiek naar algemeen en bepaal waar een try-blok moet beginnen en eindigen.
- Voer opruimcode uit met finallyVoeg een finally-blok toe, volg wanneer het wordt uitgevoerd op elk pad door een try-instructie en voorkom fouten waardoor finally problemen verbergt.
- Vang een exceptie op en herstel de uitvoering
- Signaleer problemen vanuit je eigen methoden met throw, declareer gecontroleerde excepties met throws en maak exceptieklassen die aanroepers precies vertellen wat er misging.
- Werp excepties op en declareer die met throwsValideer argumenten en de toestand van objecten door standaardexcepties op te werpen, declareer gecontroleerde excepties in methodekoppen en bepaal waar een exceptie moet worden afgehandeld.
- Maak je eigen exceptieklassen en behoud de oorzaakSchrijf je eigen gecontroleerde of ongecontroleerde exceptieklassen, werp die op voordat je de toestand van een object wijzigt en pak een exceptie van een lager niveau in zonder die te verliezen.
- Werp excepties op en declareer die met throws
- Laat een programma naar het juiste bestand verwijzen, begrijp hoe tekst als bytes en regels wordt opgeslagen en zorg dat elk geopend bestand met try-with-resources wordt gesloten.
- Werk met paden, tekencoderingen en regeleindenStel paden naar bestanden samen, voorspel waar Java bij een relatief pad zoekt en leg uit hoe tekencoderingen, regeleinden en getalnotaties tekstbestanden beïnvloeden.
- Sluit systeembronnen automatisch met try-with-resourcesGebruik try-with-resources om bestanden en andere systeembronnen te openen en sluiten, voorspel de sluitvolgorde en leg uit hoe excepties behouden blijven.
- Werk met paden, tekencoderingen en regeleinden
- Lees tekstbestanden regel voor regel en zet de regels om in gegevens, schrijf resultaten naar bestanden of voeg die achteraan toe en combineer alles in een programma dat omgaat met ontbrekende bestanden en ongeldige records.
- Lees een tekstbestand en zet de regels om in gegevensLees een bestand met Scanner, Files.readAllLines of BufferedReader, splits elke regel en zet de delen om, en sla regels over of meld die als ze niet aan de verwachte opmaak voldoen.
- Schrijf tekstbestanden en voeg tekst achteraan toeSchrijf tekst naar een nieuw of bestaand bestand, voeg tekst achteraan toe zonder de bestaande inhoud te wissen en zorg dat alles wat je schrijft het bestand ook daadwerkelijk bereikt.
- Bouw een robuust programma voor bestandsverwerkingCombineer lezen, parsen, excepties en schrijven in één programma dat bij fouten op bestandsniveau netjes stopt, ongeldige records overslaat en meldt, en gedeeltelijke uitvoer voorkomt.
- Lees een tekstbestand en zet de regels om in gegevens
Excepties en bestanden in Java: uitleg
1Begrijp wat excepties zijn en hoe Java die indeelt
Een programma kan met succes compileren en toch mislukken als het een lege array, ongeldige tekst of een ontbrekend bestand krijgt. Het exceptieobject en de bijbehorende stacktrace geven aanwijzingen over die fout. Lees de stacktrace terug via de actieve methodeaanroepen en gebruik daarna de klassenhiërarchie om te bepalen of Java tijdens het compileren een beslissing over de afhandeling vereist. Zo leer je onderscheid maken tussen programmeerfouten die je moet herstellen en verwachte problemen waarvan je programma kan herstellen.
1.1 Zoek de instructie die mislukt
Een exceptie is een object dat een probleem beschrijft dat een actief programma onderbreekt. Het opwerpen ervan stopt de huidige instructie. Java slaat de resterende normale werkzaamheden over totdat een passende exceptieafhandelaar wordt gevonden; zonder zo’n afhandelaar wordt de exceptie teruggegeven via de methoden die de betreffende methode hebben aangeroepen.
Herken het soort probleem
Een ontbrekende puntkomma is een compilatiefout: het programma kan niet compileren. Een exceptie treedt op wanneer gecompileerde code wordt uitgevoerd met gegevens die een probleem veroorzaken.
| Exceptie | Typische aanleiding |
|---|---|
ArithmeticException | Geheeltallige deling door nul |
ArrayIndexOutOfBoundsException | a[3] als de array lengte 3 heeft |
IndexOutOfBoundsException | list.get(list.size()) |
NullPointerException | Een methode aanroepen via null |
NumberFormatException | Integer.parseInt("12a") |
InputMismatchException | Scanner.nextInt() komt “ten” tegen |
ClassCastException | Een object expliciet converteren naar een klasse waartoe het niet behoort |
Array-indexen beginnen bij 0, dus bij lengte 3 zijn de geldige indexen 0, 1 en 2. Voor deling met zwevendekommagetallen gelden andere regels: 10.0 / 0 levert Infinity op; er wordt geen exceptie voor geheeltallige deling opgeworpen.
Lees een stacktrace vanaf de bovenkant
Hier staat een volledige klasse, zonder lege regels vóór de eerste regel:
public class Average {
static int average(int[] values) {
int sum = 0;
for (int value : values) sum += value;
return sum / values.length;
}
public static void main(String[] args) {
int[] values = new int[0];
System.out.println(average(values));
}
}
De array is leeg, dus de lengte is 0. De deling op regel 5 mislukt. De stacktrace is:
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Average.average(Average.java:5)
at Average.main(Average.java:9)
- Lees op de eerste regel de exceptieklasse en de melding.
- Lees het bovenste
at-frame voor de plaats waar het probleem is vastgesteld. - Lees naar beneden voor de aanroepers:
mainriepaverageaan op regel 9. - Als de bovenste frames bij de bibliotheek van Java horen, zoek dan het eerste frame dat je eigen klasse noemt. Bij een mislukte
Integer.parseIntkunnen bibliotheekframes vóór de aanroep in je code staan.
De stacktrace volgt de aanroepstack, de keten van actieve methodeaanroepen. Die vertelt je waar je moet zoeken, maar de ongeldige waarde kan eerder zijn ontstaan. Hier stelt de deling het probleem vast; de aanroeper gaf de lege array door. Bepaal of lege invoer moet worden afgewezen of afgehandeld voordat je een gemiddelde berekent.
Bekijk het exceptieobject
e.getMessage() geeft de melding terug en kan null teruggeven. e.toString() bevat de klassenaam en, indien aanwezig, de melding. e.printStackTrace() schrijft de stacktrace naar de standaardfoutstroom. Een onderdeel Caused by: toont een eerdere exceptie die in een wrapper is bewaard.
Door de JVM gegenereerde meldingen van NullPointerException geven normaal gesproken aan welke expressie null was. Een expliciet aangemaakte exceptie kan daarentegen een eigen melding of geen melding hebben. Volg de referentie terug naar de plaats waar die een object had moeten krijgen.
Een niet-afgehandelde exceptie die main verlaat, beëindigt de hoofdthread; daarmee eindigt een programma met één thread. Herstel programmeerfouten zoals verkeerde indexen in de code. Bereid herstel voor bij verwachte externe problemen, zoals een ontbrekend bestand of ongeldige gebruikersinvoer.
Belangrijkste punten
- Een exceptie onderbreekt de normale uitvoering; overgeslagen instructies worden niet automatisch hervat.
- Lees de exceptieklasse en de melding en volg daarna de stackframes van de vastgestelde fout naar de aanroepers.
- Bibliotheekframes kunnen vóór het eerste frame in je eigen code staan.
- De mislukte instructie kan een ongeldige waarde aan het licht brengen die eerder is ontstaan.
- Herstel programmeerfouten en handel verwachte invoer- of bestandsproblemen af.
1.2 Gecontroleerde en ongecontroleerde excepties
Exceptieklassen gebruiken de overervingsregels die je al kent. Een opgeworpen object van een subklasse behoort ook tot de superklasse. Dat verklaart zowel de exceptiecategorieën van Java als hoe een passende afhandelaar wordt gevonden.
Volg de hiërarchie
Alleen objecten van Throwable of de subklassen daarvan kunnen worden opgeworpen. Daaronder staan twee hoofdtakken:
Throwable
Error
OutOfMemoryError
StackOverflowError
Exception
RuntimeException
IllegalArgumentException
NumberFormatException
NullPointerException
IndexOutOfBoundsException
ArrayIndexOutOfBoundsException
IOException
FileNotFoundException
NoSuchFileException
Error beschrijft ernstige fouten van de uitvoeringsomgeving. Een gewone applicatie moet niet proberen te herstellen van een uitgeputte heap of een stack die door eindeloze recursie is uitgeput. Exception beschrijft problemen die een applicatie kan afhandelen, inclusief de tak RuntimeException.
| Categorie | Klassen | Eis van de compiler |
|---|---|---|
| Ongecontroleerd | RuntimeException, Error en hun subklassen | Opvangen of declareren is optioneel |
| Gecontroleerd | Alle andere subklassen van Throwable | Opvangen of declareren als de code ze kan opwerpen |
Beide soorten worden tijdens de uitvoering opgeworpen. Het woord “gecontroleerd” beschrijft dat de compiler je afhandeling controleert, niet dat de fout op een eerder moment optreedt. Ongecontroleerd betekent ook niet onschadelijk: een niet-afgehandelde null-referentie kan een programma stoppen.
Pas opvangen of declareren toe
Een bestand lezen kan ook in correcte code mislukken. Een hulpmethode kan de beslissing aan de aanroeper overlaten:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
class Names {
static List<String> load(Path file) throws IOException {
return Files.readAllLines(file);
}
}
Files.readAllLines kan de gecontroleerde IOException opwerpen. De declaratie geeft die mogelijkheid door aan de aanroeper, die de exceptie ook moet opvangen of declareren. Als je throws IOException verwijdert zonder een afhandelaar toe te voegen, compileert de hulpmethode niet. Een methode die Integer.parseInt gebruikt, heeft geen vergelijkbare verplichting van de compiler omdat NumberFormatException ongecontroleerd is.
Gecontroleerde fouten beschrijven vaak omstandigheden waarop een aanroeper zich kan voorbereiden, zoals een bestand dat verdwijnt. Uitvoeringsfouten wijzen vaak op programmeerfouten of ongeldige argumenten. Dat zijn nuttige redenen voor het ontwerp, maar de keten van superklassen bepaalt de categorie. Een eigen klasse die RuntimeException uitbreidt, blijft ongecontroleerd, ook als een aanroeper kan herstellen.
Koppel afhandelaars via overerving
catch (IOException e) kan een FileNotFoundException of NoSuchFileException afhandelen. catch (Exception e) vangt ook uitvoeringsexcepties op; die kan een programmeerfout veranderen in een misleidende invoermelding. catch (Throwable e) bereikt ook de tak Error en is zelden geschikt voor normaal herstel.
Om een onbekende klasse in te delen, volg je de overerving omhoog in de API-documentatie. InputMismatchException breidt NoSuchElementException uit, dat RuntimeException uitbreidt, dus is die ongecontroleerd. InterruptedException breidt Exception uit buiten de uitvoeringstak en is dus gecontroleerd. Een ongecontroleerd type declareren in een throws-clausule is toegestaan, maar verandert de categorie niet.
Belangrijkste punten
RuntimeException,Erroren al hun subklassen zijn ongecontroleerd.- Andere subklassen van
Throwablezijn gecontroleerd; code die ze kan opwerpen moet ze opvangen of declareren. - Zowel gecontroleerde als ongecontroleerde excepties treden tijdens de uitvoering op.
- Een catch voor een superklasse komt ook overeen met de subklassen daarvan.
- Een brede
Exception-afhandelaar kan programmeerfouten verbergen. - Deel een onbekende exceptie in aan de hand van de keten van superklassen.
2Handel excepties af met try, catch en finally
De afhandeling van een fout verandert welke instructies daarna worden uitgevoerd. Een nuttige afhandelaar moet bij de exceptie passen en het programma gereedmaken om verder te gaan, terwijl opruimcode ook moet worden uitgevoerd op paden die voortijdig worden verlaten. Volg conversies en lussen stap voor stap, kies specifieke afhandelaars vóór algemene en plaats het beschermde blok rond de werkzaamheden die samen moeten mislukken. Volg daarna hoe finally zich gedraagt wanneer een exceptie wordt doorgegeven of een methode terugkeert.
2.1 Herstel na een mislukte conversie
Een try-blok markeert werkzaamheden die kunnen mislukken. Een passend catch-blok geeft het programma een nuttige volgende actie, zoals om een andere waarde vragen. De exceptie afhandelen probeert de mislukte instructie niet vanzelf opnieuw.
Volg de paden
System.out.println("A");
try {
System.out.println("B");
int value = Integer.parseInt("4x");
System.out.println("C");
} catch (NumberFormatException e) {
System.out.println("D: " + e.getMessage());
}
System.out.println("E");
De uitvoer is A, B, D: For input string: "4x" en E, elk op een eigen regel. De conversie mislukt, dus slaat Java C over en gaat naar de passende afhandelaar. Wanneer die afhandelaar normaal eindigt, gaat de uitvoering na de volledige instructie verder. Als de conversie slaagt, wordt de afhandelaar overgeslagen. Als het blok een ArithmeticException opwerpt, past deze afhandelaar niet; die exceptie wordt doorgegeven.
Probeer opnieuw met nieuwe invoer
Lees een hele regel voordat je die omzet. De onderstaande lus accepteert een geheel getal en handelt het einde van de toetsenbordinvoer af door te melden dat geen waarde is opgegeven:
import java.util.Scanner;
class WholeNumbers {
static int readWholeNumber(Scanner in) {
while (in.hasNextLine()) {
String line = in.nextLine();
try {
return Integer.parseInt(line.trim());
} catch (NumberFormatException e) {
System.out.println("Please enter a whole number.");
}
}
throw new IllegalStateException("No number supplied");
}
}
Bij de invoer “twenty” en “20” geeft de afhandelaar één keer een melding weer, leest de lus een nieuwe regel en retourneert de methode 20. trim() verwijdert omringende witruimte: Integer.parseInt(" 20") mislukt anders. De conversie accepteert een optioneel teken en cijfers die een geheel getal binnen het bereik voorstellen.
Een scanner die met tokens werkt, heeft een andere oplossing nodig. Wanneer nextInt() “ten” tegenkomt, laat de bijbehorende InputMismatchException die token ongelezen. Opvangen en opnieuw nextInt() aanroepen herhaalt de fout eindeloos. Verbruik de ongeldige token met next() of sla de regel over met nextLine() voordat je het opnieuw probeert. hasNextInt() kan een token vóór de conversie controleren.
Houd de herstelde toestand bruikbaar
Een variabele die binnen try is gedeclareerd, bestaat alleen in dat blok. Als latere code het resultaat nodig heeft, declareer die dan erbuiten en laat elk pad dat doorgaat een geldige waarde toewijzen:
int quantity;
try {
quantity = Integer.parseInt("bad");
} catch (NumberFormatException e) {
quantity = 0;
}
System.out.println(quantity);
Nul is hier alleen geschikt als het programma die waarde als een geldig alternatief definieert. Als quantity in de afhandelaar geen waarde krijgt, veroorzaakt dat een compilatiefout omdat de variabele niet op alle paden zeker een waarde heeft gekregen.
Vang het specifieke type op dat je kunt afhandelen. Een lege afhandelaar laat het probleem ongemerkt verloren gaan; een brede catch (Exception e) kan een null-referentiefout als een ongeldig getal aanduiden. Bij herstel kun je opnieuw om invoer vragen, één element overslaan, een geldige standaardwaarde kiezen, een fout melden of een betekenisvolle exceptie doorgeven. Controleer bij eenvoudige voorspelbare grenzen eerst: index >= 0 && index < list.size() voorkomt zowel negatieve als te grote lijstindexen.
Belangrijkste punten
- Een passende afhandelaar wordt uitgevoerd nadat de rest van het mislukte
try-blok is overgeslagen. - Een lus moet nieuwe invoer lezen voordat die het opnieuw probeert.
Scanner.nextInt()laat een token van het verkeerde type ongelezen.- Elk pad dat doorgaat, moet variabelen die later worden gebruikt een zinvolle beginwaarde geven.
2.2 Kies afhandelaars en de werkzaamheden die ze beschermen
Een bestand kan ontbreken, onleesbaar zijn of vol onjuist gevormde records staan. Die fouten vragen om verschillende reacties. Met meerdere afhandelaars kan één try ze onderscheiden, terwijl de plaats van het blok bepaalt hoeveel werk stopt als er iets mislukt.
Zet specifieke afhandelaars voorop
Java toetst afhandelaars van boven naar beneden en voert de eerste passende afhandelaar uit. Daarna worden geen andere afhandelaars voor dezelfde exceptie uitgevoerd.
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
class FirstCount {
static void show(Path file) {
try {
int count = Integer.parseInt(Files.readString(file).trim());
System.out.println(count);
} catch (NoSuchFileException e) {
System.out.println("File not found: " + file.toAbsolutePath());
} catch (IOException e) {
System.out.println("Could not read: " + file.toAbsolutePath());
} catch (NumberFormatException e) {
System.out.println("The file must contain one whole number.");
}
}
}
NoSuchFileException breidt IOException uit en moet dus eerst komen. Als je de volgorde omdraait, wordt de specifieke afhandelaar onbereikbaar en compileert de code niet. De conversie-exceptie is niet verwant aan IOException, dus die twee afhandelaars mogen in beide volgordes staan.
Een afhandelaar voor een gecontroleerde exceptie moet ook relevant zijn voor de beschermde werkzaamheden. IOException opvangen rond alleen int count = 5; wordt afgewezen omdat dat blok de exceptie niet kan opwerpen. Deze beperking geldt niet op dezelfde manier voor ongecontroleerde typen of de brede typen Exception en Throwable.
Deel een afhandelaar als de reactie hetzelfde is
Een meervoudige catch-clausule verbindt niet-verwante exceptietypen met |:
try {
String[] parts = "Ana".split(",");
int age = Integer.parseInt(parts[1]);
System.out.println(age);
} catch (NumberFormatException | ArrayIndexOutOfBoundsException e) {
System.out.println("Invalid record: " + e.getMessage());
}
Hier veroorzaakt het ontbrekende tweede veld de indexexceptie. Een aanwezig veld dat geen getal bevat, zou de conversie-exceptie veroorzaken en beide krijgen dezelfde reactie. De parameter van de meervoudige catch-clausule is impliciet final. Alternatieven kunnen geen subklasse en de bijbehorende superklasse zijn: FileNotFoundException | IOException is ongeldig omdat IOException beide al omvat.
Bepaal wat één fout moet overslaan
Bekijk de teksten “3”, “x” en “5”. Met een try rond de hele lus stopt de verwerking bij “x” en is de som tot dan toe 3. Zet de try binnen de lus om voor elk element afzonderlijk te herstellen:
int sum = 0;
for (String text : new String[] {"3", "x", "5"}) {
try {
sum += Integer.parseInt(text);
} catch (NumberFormatException e) {
System.out.println("Skipped: " + text);
}
}
System.out.println(sum);
Dit geeft Skipped: x en daarna 8 weer. De lus gaat verder omdat de lusbody de fout van dat record heeft afgehandeld. Bij geneste afhandelaars wordt vanuit het binnenste beschermde blok naar buiten gezocht; als een binnenste afhandelaar niet past, krijgt een buitenste de kans. Kies de kleinste werkeenheid die in haar geheel moet mislukken: één record voor ongeldige gegevens, de bestandsbewerking voor een leesfout.
Belangrijkste punten
- Java voert voor elke exceptie hoogstens één passende afhandelaar uit.
- Vang een subklasse op vóór de superklasse.
- Een meervoudige catch-clausule deelt een reactie tussen niet-verwante exceptietypen.
- Een meervoudige catch-clausule kan een superklasse niet combineren met de subklasse daarvan.
- Plaats herstel op recordniveau binnen de lus om door te gaan met latere records.
2.3 Voer opruimcode uit voordat de uitvoering verdergaat
Sommige werkzaamheden moeten gebeuren ongeacht of een bewerking slaagt of mislukt. Een finally-blok wordt uitgevoerd wanneer de uitvoering de bijbehorende try-instructie verlaat, ook wanneer een exceptie wordt doorgegeven of een methode terugkeert.
Volg een exceptie zonder passende afhandelaar
try {
System.out.println("1");
int[] values = new int[2];
values[2] = 7;
System.out.println("2");
} catch (NumberFormatException e) {
System.out.println("3");
} finally {
System.out.println("4");
}
System.out.println("5");
Alleen 1 en 4 worden weergegeven voordat de ArrayIndexOutOfBoundsException wordt doorgegeven. Index 2 valt buiten de array met twee elementen. De afhandelaar past niet, maar finally wordt toch uitgevoerd. De instructie die 5 weergeeft, staat buiten het opruimblok en wordt daarom door de doorgegeven exceptie overgeslagen.
Als de beschermde werkzaamheden in plaats daarvan een passende NumberFormatException opwerpen na het weergeven van 1, geeft de afhandelaar 3 weer, geeft de opruimcode 4 weer en gaat de uitvoering verder naar 5. Als alles slaagt, wordt geen afhandelaar uitgevoerd, maar wel de opruimcode.
Retourwaarden worden vóór het opruimen voorbereid
class Cleanup {
static int result() {
try {
return 1;
} finally {
System.out.println("cleanup");
}
}
}
Eerst wordt de retourexpressie geëvalueerd. Java geeft daarna cleanup weer en de aanroeper ontvangt 1. Een break of continue die een beschermd codegebied verlaat, voert ook de bijbehorende opruimcode uit voordat de overdracht van de uitvoering plaatsvindt.
Houd return buiten finally. Een return 2; daarin zou de wachtende retourwaarde vervangen en zelfs een wachtende exceptie kunnen laten vervallen. Een exceptie die door de opruimcode wordt opgeworpen, kan op dezelfde manier de oorspronkelijke fout vervangen. Opruimcode moet geen tweede concurrerend resultaat opleveren. De compileroptie -Xlint:finally waarschuwt wanneer een finally-blok niet normaal kan eindigen.
Sluit een object alleen als het is geopend
Het oudere handmatige patroon heeft een variabele nodig die zichtbaar is voor het opruimblok:
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Scanner;
class ManualRead {
static void firstLine(Path file) throws IOException {
Scanner sc = null;
try {
sc = new Scanner(file, StandardCharsets.UTF_8);
if (sc.hasNextLine()) System.out.println(sc.nextLine());
if (sc.ioException() != null) throw sc.ioException();
} finally {
if (sc != null) sc.close();
}
}
}
Als het openen mislukt, is sc nog steeds null, zodat de controle voorkomt dat de opruimcode een nieuwe null-referentie-exceptie opwerpt. Voor objecten die kunnen worden gesloten, neemt try-with-resources dit beheerwerk weg en handelt het concurrerende fouten veiliger af.
finally biedt geen garantie als de machine stopt. System.exit beëindigt de JVM zonder deze opruimcode uit te voeren; ook een crash of stroomuitval verhindert de uitvoering. Een oneindige lus die het blok nooit verlaat, bereikt de bijbehorende finally evenmin.
Belangrijkste punten
finallywordt uitgevoerd bij normale voltooiing, opgevangen en niet-opgevangen excepties en overdrachten van de uitvoering zoalsreturn.- Een retourexpressie wordt geëvalueerd voordat de opruimcode wordt uitgevoerd.
- Code na een
try-instructie wordt overgeslagen wanneer een exceptie wordt doorgegeven;finallywordt nog wel bereikt. - Een return of opgeworpen exceptie in
finallykan het oorspronkelijke resultaat verbergen. - Try-with-resources is de gebruikelijke keuze om systeembronnen voor bestanden te sluiten.
- Beëindiging van de JVM of een blok dat nooit eindigt, kan het uitvoeren van opruimcode verhinderen.
3Werp excepties op en ontwerp je eigen excepties
Een ongeldig verzoek is het gemakkelijkst te onderzoeken voordat het een object verandert. Je methoden kunnen ongeldige argumenten of toestanden afwijzen met standaardexcepties en domeinproblemen zichtbaar maken met eigen klassen. De methodedeclaratie informeert aanroepers over gecontroleerde fouten; overerving beperkt wat een overschrijvende methode kan toevoegen. Als je een fout van een lager niveau omzet in een nuttigere fout, behoud dan de oorzaak zodat de aanroeper zowel context als de oorspronkelijke informatie voor het opsporen van de fout krijgt.
3.1 Signaleer ongeldige argumenten en toestanden
Je methoden kunnen ongeldige verzoeken afwijzen voordat ze een object wijzigen. Kies een exceptie die het probleem aanduidt en de aanroeper laat reageren.
Werp direct een object op
class Percentage {
private int value;
public void setPercentage(int value) {
if (value < 0 || value > 100) {
throw new IllegalArgumentException(
"percentage must be 0 to 100, was " + value);
}
this.value = value;
}
}
De aanroep setPercentage(120) werpt een exceptie op met de melding percentage must be 0 to 100, was 120. De toewijzing is niet uitgevoerd, dus het veld behoudt zijn vorige waarde. Dit is vroegtijdig afbreken: meld het ongeldige verzoek op de plaats waar je het kunt uitleggen. Dezelfde controle in een constructor voorkomt dat een ongeldig object wordt teruggegeven.
throw heeft een exceptieobject nodig, zoals in throw new IllegalArgumentException("bad quantity");. Een klassenaam alleen is geen object. Een gewone instructie direct na een onvoorwaardelijke throw in hetzelfde blok is onbereikbaar.
| Probleem | Standaardexceptie |
|---|---|
| Argument heeft een ongeldige waarde | IllegalArgumentException |
| Object heeft de verkeerde toestand voor de aanroep | IllegalStateException |
Vereiste objectreferentie is null | NullPointerException |
| Klasse biedt de bewerking niet aan | UnsupportedOperationException |
| Positie valt buiten een collectie | IndexOutOfBoundsException |
Objects.requireNonNull(name, "name must not be null") uit java.util.Objects is een handige controle op null. Een al verzonden bestelling die een extra artikel afwijst, heeft een toestandsprobleem; een negatief aantal artikelen is een argumentprobleem.
Declareer wat de methode kan verlaten
Een hulpmethode die een bestand leest, kan de bijbehorende gecontroleerde fout declareren:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
class NameLoader {
static List<String> loadNames(Path file) throws IOException {
return Files.readAllLines(file);
}
}
throw is een uitvoerbare instructie. throws hoort bij de methodedeclaratie en vertelt aanroepers welke gecontroleerde fouten ze moeten afhandelen. Het vangt een fout niet op en laat de methode na een fout ook niet doorgaan. Elke aanroeper moet IOException opvangen of op zijn beurt declareren. Een catch kan hetzelfde object ook opnieuw opwerpen met throw e;.
Vang een exceptie op waar een nuttige reactie mogelijk is. Een hulpmethode kan de gebruiker meestal niet vragen een ander bestand te kiezen, maar main kan dat wel. “fout” weergeven en een lege lijst teruggeven zou een ontbrekend bestand verwarren met een daadwerkelijk leeg bestand. Bij een klein experiment mag main throws IOException declareren; een afgerond programma handelt verwachte fouten af met nuttige meldingen. Ongecontroleerde typen mogen ter documentatie worden gedeclareerd, ook al vereist de compiler dat niet.
Respecteer het overgeërfde contract
Als een interface read() throws IOException declareert, mag een overschrijvende methode IOException, een specifiekere gecontroleerde subklasse of geen gecontroleerde exceptie declareren. De methode mag de declaratie niet verbreden tot Exception. Ongecontroleerde typen declareren mag wel. Een overschrijving van Object.toString() kan geen IOException toevoegen, omdat de overgeërfde methode geen gecontroleerde excepties declareert. Aanroepen via het interfacetype moeten de gedeclareerde IOException nog steeds afhandelen, ook wanneer een implementatie geen exceptie declareert.
Belangrijkste punten
- Gebruik
throwmet een exceptieobject om een ongeldige bewerking te stoppen. - Valideer voordat je velden of collecties wijzigt.
throwsdeclareert een mogelijke fout; het handelt die niet af.- Vang de fout op waar zinvol herstel mogelijk is; geef die anders door aan de aanroeper.
- Overschrijvende methoden kunnen geen nieuwe of bredere gecontroleerde excepties toevoegen.
3.2 Beschrijf domeinfouten en behoud de oorzaken
Een standaardexceptie is vaak voldoende. Maak een eigen klasse als aanroepers een specifiek probleem van het programma moeten herkennen, zoals onvoldoende voorraad of een onjuist gevormd gegevensrecord, en er anders op moeten reageren.
Kies de superklasse bewust
Door Exception uit te breiden maak je een gecontroleerde exceptie. Door RuntimeException uit te breiden maak je een ongecontroleerde exceptie. Kies het gecontroleerde ontwerp wanneer aanroepers rekening moeten houden met een domeinfout waarvan herstel mogelijk is; kies het ongecontroleerde ontwerp bij verkeerd gebruik of een programmeerfout.
class OutOfStockException extends Exception {
public OutOfStockException(String message) {
super(message);
}
}
super(message) slaat de melding op via de overgeërfde exceptiefunctionaliteit, zodat getMessage() die teruggeeft. De klassenaam eindigt volgens afspraak op Exception. Extra velden en getters kunnen gestructureerde details beschikbaar maken, zoals het gevraagde en het beschikbare aantal.
Laat de voorraad na een fout ongewijzigd
import java.util.HashMap;
class Inventory {
private final HashMap<String, Integer> stock = new HashMap<>();
Inventory() {
stock.put("pens", 12);
}
void remove(String item, int quantity) throws OutOfStockException {
if (quantity <= 0) {
throw new IllegalArgumentException("quantity must be positive");
}
int available = stock.getOrDefault(item, 0);
if (quantity > available) {
throw new OutOfStockException("Requested " + quantity
+ " of " + item + ", only " + available + " in stock");
}
stock.put(item, available - quantity);
}
}
Als je begint met 12 pennen, blijven er na het verwijderen van 5 nog 7 over. Een later verzoek om 10 werpt een exceptie op en er blijven 7 over, omdat de bijwerking na alle controles komt. De aanroeper moet de gecontroleerde voorraadexceptie afhandelen of declareren; de exceptie voor een ongeldig aantal is ongecontroleerd. Een programma dat een afzonderlijke reactie op een onbekend artikel nodig heeft, kan een verwante UnknownItemException gebruiken, met beide domeintypen onder een basisklasse InventoryException.
Pak in zonder het oorspronkelijke probleem te verliezen
Een parser kan een conversiefout van een lager niveau omzetten in een fout op recordniveau:
class InvalidRecordException extends Exception {
public InvalidRecordException(String message) {
super(message);
}
public InvalidRecordException(String message, Throwable cause) {
super(message, cause);
}
}
Binnen de parsemethode kan een afhandelaar dit doen:
catch (NumberFormatException e) {
throw new InvalidRecordException(
"Line 3: quantity is not a number: x", e);
}
De wrapper geeft de aanroeper een betekenisvolle categorie en een regelnummer. De methode getCause() geeft de oorspronkelijke NumberFormatException terug en een weergegeven stacktrace behoudt een onderdeel Caused by:. Als je een nieuwe exceptie maakt met alleen de oude melding, gaan het oorspronkelijke object en de bijbehorende stacktrace verloren.
java.io.UncheckedIOException is een kant-en-klare ongecontroleerde wrapper speciaal voor IOException. De constructoren accepteren de oorzaak, met een optionele melding. De wrapper kan een bestandsfout doorgeven via een overschrijvende methode die geen gecontroleerde declaratie mag toevoegen. Inpakken verandert het type dat de aanroeper ziet; het herstelt het bestandsprobleem niet. Gebruik voor gewone beslissingen in een lus voorwaarden en break, zonder excepties te bedenken voor het normale verloop.
Belangrijkste punten
- Met eigen klassen kunnen aanroepers betekenisvolle domeinfouten herkennen.
- Breid
Exceptionuit voor een gecontroleerde klasse ofRuntimeExceptionvoor een ongecontroleerde klasse. - Roep
super(message)aan om een nuttige foutmelding te behouden. - Valideer het hele verzoek voordat je de toestand bijwerkt.
- Geef de oorspronkelijke exceptie als oorzaak door wanneer je die inpakt.
UncheckedIOExceptiongeeft eenIOExceptiondoor als ongecontroleerde fout.
4Bepaal bestandslocaties en sluit systeembronnen veilig
Een tekstbestand moet correct worden gevonden, gedecodeerd en gesloten. Een relatief pad hangt af van de werkmap, terwijl bytes alleen met de juiste codering leesbare tekens worden. Bij tekst met getallen en regeleinden moet je eigen keuzes maken voor uitwisselbaarheid. Zodra een bestand open is, geeft try-with-resources het bij elke manier van verlaten vrij en houdt het de hoofdfout zichtbaar als het sluiten ook mislukt. Door deze keuzes kun je latere code voor lezen en schrijven beter vertrouwen.
4.1 Laat tekstbestanden op verschillende computers werken
Een bestand slaat bytes op, terwijl je programma met tekens en getallen werkt. Voor een geslaagde leesbewerking zijn de juiste locatie, de juiste tekencodering en een duidelijke afspraak voor regels en tekst met getallen nodig.
Zoek de werkelijke locatie
Een relatief pad begint vanuit de werkmap van het programma. Het begint niet vanuit de map met de broncode. Een absoluut pad begint bij de hoofdmap van het bestandssysteem, zoals /home/ana/data/scores.txt, of bij de hoofdmap van een schijf op Windows.
Path file = Path.of("data", "scores.txt");
System.out.println(System.getProperty("user.dir"));
System.out.println(file.toAbsolutePath());
System.out.println(file.getFileName());
Importeer java.nio.file.Path. Als je deze Path aanmaakt, wordt niets geopend en wordt niet gecontroleerd of iets bestaat. toAbsolutePath() laat zien waar Java zal zoeken en getFileName() geeft scores.txt. Een IDE gebruikt vaak de projectmap als werkmap. Schuine strepen in "data/scores.txt" werken op Windows, Linux en macOS. In een Windows-padliteral met backslashes moeten die worden verdubbeld, zoals in "C:\\data\\scores.txt". Ook de oudere java.io.File stelt een locatie voor; zet de vormen om met toPath() of toFile().
Koppel bytes aan tekens
UTF-8 codeert elk Unicode-teken. Latijnse basistekens nemen één byte in; andere twee tot vier. De tekst “café” bevat vier tekens maar vijf UTF-8-bytes omdat “é” er twee inneemt. Als je die bytes als ISO-8859-1 leest, kan “café” ontstaan. Gebruik de codering die bij het schrijven is gebruikt.
De tekstmethoden van Files gebruiken standaard UTF-8. Klassen zoals FileWriter en een scanner die zonder tekenset is geopend, gebruiken de standaardtekenset, die vanaf Java 18 UTF-8 is; bij oudere installaties kan die anders zijn. Met StandardCharsets.UTF_8 uit java.nio.charset maak je de keuze expliciet. Ongeldige bytes die door Files.readAllLines worden gelezen, leiden tot MalformedInputException, een subklasse van IOException.
Lees regels zonder achtergebleven regeleinden
| Conventie | Escapesequentie in een Java-string |
|---|---|
| Regeleinde van Linux en macOS | \n |
| Gebruikelijk regeleinde van Windows | \r\n |
Met een afsluiting na elke regel hebben “a” en “b” vier bytes nodig met LF-regeleinden en zes met CRLF-regeleinden. BufferedReader.readLine(), Files.readAllLines en Scanner.nextLine() verwijderen herkende regeleinden. Als je een volledige string splitst met split("\n"), blijft de Windows-\r staan, zodat het omzetten van "12\r" met parseInt mislukt. Gebruik bij voorkeur methoden die regels lezen of verwijder de omringende witruimte. System.lineSeparator() en opmaakspecificatie %n produceren het regeleinde van het huidige systeem.
Houd bestanden met getallen consistent
Integer.parseInt accepteert tekst met een geheel getal zonder decimaalteken. Double.parseDouble("3.5") accepteert een punt; Double.parseDouble("3,5") mislukt. Deze parseermethoden negeren de taal- en regio-instelling. Numerieke methoden van Scanner en gewone decimale opmaak volgen die instelling wel, zodat Duitse of Nederlandse instellingen “3,5” kunnen verwachten en “3,50” kunnen produceren. Gebruik scanner.useLocale(Locale.ROOT) en String.format(Locale.ROOT, "%.2f", value) voor gegevens met een decimale punt; importeer java.util.Locale.
Ontbrekende locaties leiden vaak tot NoSuchFileException bij Files of Scanner(Path), terwijl oudere constructoren met een bestandsnaam FileNotFoundException kunnen geven. Ontbrekende toestemming kan AccessDeniedException opleveren. Dit zijn gecontroleerde invoer- en uitvoerfouten: een correcte padexpressie alleen kan ze niet voorkomen.
Belangrijkste punten
- Relatieve paden worden bepaald vanuit de werkmap.
- Een
Pathaanmaken opent of controleert het bestand niet. - Gebruik dezelfde codering voor lezen en schrijven; kies UTF-8 expliciet waar standaardinstellingen kunnen verschillen.
- Methoden die regels lezen verwijderen de regeleinden LF, CRLF en CR.
Double.parseDoublegebruikt een punt; voor numerieke methoden die van taal- en regio-instellingen afhangen, moet je een instelling kiezen.- Bij het openen en lezen van bestanden moet je nog steeds gecontroleerde excepties afhandelen.
4.2 Laat Java elke geopende systeembron sluiten
Een open bestand gebruikt een systeembron van het besturingssysteem. Sluiten geeft die vrij en schrijft de nog aanwezige uitvoer van een bestandsschrijver door. Als je vergeet te sluiten, kun je de limiet voor open bestanden bereiken, gebufferde tekst verliezen of een ander programma verhinderen het bestand te wijzigen.
Declareer systeembronnen in de try-kop
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
class Lines {
static void show(Path file) throws IOException {
try (BufferedReader in =
Files.newBufferedReader(file, StandardCharsets.UTF_8)) {
String line;
while ((line = in.readLine()) != null) {
System.out.println(line);
}
}
}
}
De kop opent de lezer. De body gebruikt die en Java sluit de lezer wanneer de body normaal eindigt, terugkeert of een exceptie opwerpt. Een afzonderlijke finally is niet nodig. Hier is geen afhandelaar, dus wordt elke gecontroleerde fout nog steeds via throws IOException aan de aanroeper doorgegeven.
Een systeembron moet AutoCloseable implementeren. Die interface declareert close() en staat Exception toe; de subinterface Closeable beperkt de gedeclareerde gecontroleerde fout tot IOException. Bestandslezers, bestandsschrijvers en Scanner voldoen daaraan. Een String niet. Variabelen in de kop zijn impliciet final en beschikbaar in de body, maar niet in de catch- of finally-blokken van die instructie. Java 9 en later staan ook een bestaande final of effectief final variabele toe: try (in) { ... }.
Sluit in omgekeerde volgorde
Scheid systeembronnen met puntkomma’s:
try (BufferedReader in = Files.newBufferedReader(source);
java.io.BufferedWriter out = Files.newBufferedWriter(target)) {
String line;
while ((line = in.readLine()) != null) {
out.write(line);
out.newLine();
}
}
Dit is een blok binnen een methode die IOException declareert, met verschillende paden voor source en target. Java opent eerst in, daarna out, en sluit out vóór in. Als het openen van out mislukt, wordt de al geopende lezer toch gesloten. Als je een schrijver opent op het invoerpad, wordt die invoer afgekapt. Daarom moeten de paden voor deze directe kopie verschillen.
Systeembronnen worden gesloten vóór de catch- en finally-clausules van dezelfde instructie. Die afhandelaars kunnen fouten ontvangen die ontstaan bij het openen, gebruiken of sluiten van de systeembronnen.
Houd de hoofdfout zichtbaar
Stel dat de body een recordexceptie opwerpt en dat het sluiten van de lezer daarna een IOException opwerpt. Try-with-resources geeft de exceptie uit de body door en voegt de sluitfout toe als onderdrukte exceptie. getSuppressed() haalt die op en de stacktrace geeft een onderdeel Suppressed: weer. De oorspronkelijke fout uit de body blijft zichtbaar. Als de body slaagt en alleen het sluiten mislukt, wordt de sluitexceptie normaal doorgegeven.
Bij meerdere fouten tijdens het sluiten probeert Java de overige systeembronnen nog steeds te sluiten. Een gewone finally { resource.close(); } kan daarentegen een wachtende fout uit de body vervangen door een opruimfout.
Gebruik dit patroon voor bestandssysteembronnen die je code zelf beheert. Als je een scanner op System.in sluit, sluit je ook de toetsenbordinvoer van het programma, waardoor later lezen niet meer mogelijk is. Laat het beheer van gedeelde standaardinvoer op programmaniveau in plaats van die in een hulpmethode te sluiten.
Belangrijkste punten
- Try-with-resources sluit succesvol geopende systeembronnen bij zowel normaal als uitzonderlijk verlaten.
- Alleen
AutoCloseable-objecten komen als systeembron in aanmerking. - Systeembronnen openen van links naar rechts en sluiten van rechts naar links.
- Het sluiten gebeurt vóór de catch- en finally-blokken van dezelfde instructie.
- Een fout uit de body blijft de hoofdfout; latere sluitfouten worden onderdrukt.
- Automatisch sluiten vervangt de afhandeling volgens de opvangen-of-declarerenregel niet.
- Een scanner sluiten sluit ook de bijbehorende invoerbron als die kan worden gesloten.
5Lees en schrijf tekstbestanden
Bestandsverwerking verbindt externe bytes met de objecten en resultaten die je programma gebruikt. Lees regels met een methode die past bij de omvang van de invoer, valideer velden vóór de conversie en kies bewust of je bij het schrijven vervangt of achteraan toevoegt. Een volledig rapport vereist afzonderlijke reacties op een ongeldig record en een onbruikbaar bestand. Stel het resultaat samen voordat je het opslaat en gebruik daarna tijdelijke uitvoer en een ondersteunde atomaire verplaatsing wanneer een bestaand doelbestand moet worden beschermd.
5.1 Lees regels en zet die om in objecten
Lezen levert tekst op. Parsen geeft die tekst betekenis: een aantal, een leeftijd of een object. Houd die twee bewerkingen gescheiden, zodat een ongeldig record niet lijkt op een mislukte bestandslezing.
Kies hoeveel je in het geheugen bewaart
| Methode | Resultaat en gebruik |
|---|---|
Files.readAllLines(path) | Een lijst met alle regels; handig voor kleine bestanden |
Files.readString(path) | Eén string met behoud van regeleinden |
BufferedReader.readLine() | Eén regel tegelijk; null geeft het einde aan |
Scanner.hasNextLine() / nextLine() | Handig lezen per regel |
Importeer java.nio.file.Files en Path, plus de typen voor lezers of collecties die je gebruikt. Tekstmethoden van Files gebruiken standaard UTF-8. Een BufferedReader hoort in try-with-resources en meldt leesfouten met IOException. Door één regel tegelijk te lezen voorkom je dat de hele invoer in het geheugen staat, tenzij je verwerking daarna alle records opslaat.
Ook een scanner moet worden gesloten en heeft een extra foutcontrole nodig:
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Path;
import java.util.Scanner;
class ScanFile {
static void show(Path file) throws IOException {
try (Scanner sc = new Scanner(file, StandardCharsets.UTF_8)) {
while (sc.hasNextLine()) {
System.out.println(sc.nextLine());
}
if (sc.ioException() != null) throw sc.ioException();
}
}
}
Bij het openen kan direct een exceptie worden opgeworpen. Tijdens het lezen behandelt Scanner een onderliggende invoer- of uitvoerfout als het einde van de invoer en slaat die op; ioException() maakt de fout beschikbaar. Zonder die laatste controle kan een gedeeltelijke leesbewerking geslaagd lijken.
Tel de daadwerkelijke regels en parse daarna
Verhoog bij de invoerregels “12”, “15”, “abc”, een lege regel en “9” het regelnummer voordat je beslist of je een lege regel overslaat. Zet elke niet-lege regel om met Integer.parseInt(line.trim()) binnen een eigen try. Meld “Line 3: not a number: abc” en ga verder. De som van de geldige getallen is 36 en het aantal is 3, wat een gemiddelde van 12,0 geeft. Bereken (double) sum / count om een resultaat met een deel achter de komma te behouden en controleer eerst of het aantal niet nul is.
Controleer velden voordat je indexen gebruikt
Eenvoudige kommagescheiden gegevens kunnen objecten worden. Hier is de eerste regel een verplichte kopregel; elk volgend record heeft een niet-lege naam en een gehele leeftijd:
String[] parts = line.split(",", -1);
if (parts.length != 2 || parts[0].isBlank() || parts[1].isBlank()) {
System.out.println("Line " + lineNumber + ": expected name and age");
continue;
}
try {
int age = Integer.parseInt(parts[1].trim());
people.add(new Person(parts[0].trim(), age));
} catch (NumberFormatException e) {
System.out.println("Line " + lineNumber + ": invalid age");
}
Dit fragment uit de lusbody gaat uit van line, lineNumber, een List<Person> met de naam people en een constructor Person(String, int). Sla de kopregel “name,age” over vóór deze parseerstap; anders mislukt de numerieke conversie van “age”. De records “Ana,34”, “Ben,29” en “Chen,41” hebben samen 104 jaar en gemiddeld ongeveer 34,67 jaar.
Een gewone split(",") laat lege velden aan het einde weg: “Eve,” levert maar één deel op. De limiet -1 behoudt het lege tweede veld, zodat de validatie dit kan melden. Controleer altijd de lengte vóór parts[1]. Alleen splitsen kan niet omgaan met komma’s tussen aanhalingstekens, zoals bij de naam in “"Smith, Ana",34”; voor volledige CSV is een CSV-parser nodig.
Belangrijkste punten
- Kies methoden die het hele bestand lezen voor invoer van beheersbare omvang of een leeslus voor grote invoer.
- Controleer
Scanner.ioException()na het lezen om een verborgen leesfout op te sporen. - Tel de oorspronkelijke bestandsregels ook wanneer lege regels of records worden overgeslagen.
- Controleer het aantal velden en de vereiste waarden voordat je indexen gebruikt en waarden omzet.
- Sla een kopregel over voordat je records parset.
- Gewoon splitsen kan CSV-velden tussen aanhalingstekens niet parsen.
5.2 Schrijf volledige tekst en voeg bewust achteraan toe
Het openen van een uitvoerbestand kan het bestand direct veranderen. Kies vóór het openen van de schrijver tussen vervangen en achteraan toevoegen, en sluit de schrijver aan het einde zodat gebufferde uitvoer niet in het geheugen achterblijft.
Vervangen of achteraan toevoegen
Files.writeString(path, text) schrijft één string. Files.write(path, lines) schrijft een doorloopbare verzameling regels en voegt na elke regel het regelscheidingsteken van het systeem toe, ook na de laatste. Met hun standaardopties maken beide een ontbrekend bestand aan en kappen ze een bestaand bestand af.
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
class RunLog {
static void recordStart(Path log) throws IOException {
Files.writeString(log, "started" + System.lineSeparator(),
StandardCharsets.UTF_8,
StandardOpenOption.CREATE, StandardOpenOption.APPEND);
}
}
CREATE maakt een nieuw bestand mogelijk; APPEND behoudt oude inhoud en voegt aan het einde toe. Alleen APPEND mislukt als het bestand ontbreekt. Achteraan toevoegen plaatst geen ontbrekend scheidingsteken vóór je tekst: als een bestaand bestand halverwege een regel eindigt, gaan de volgende tekens op die regel verder.
De oudere FileWriter heeft een vlag voor achteraan toevoegen: new FileWriter("log.txt", StandardCharsets.UTF_8, true). Zonder die vlag kapt een nieuwe FileWriter of een PrintWriter met bestandsnaam een bestaand bestand ook af. Open nooit een schrijver die vervangt op invoer die je nog moet lezen.
Kies een schrijver en merk fouten op
BufferedWriter ondersteunt write en newLine() en meldt schrijffouten als gecontroleerde excepties:
import java.io.BufferedWriter;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Locale;
class ReportWriter {
static void write(Path file) throws IOException {
try (BufferedWriter out =
Files.newBufferedWriter(file, StandardCharsets.UTF_8)) {
out.write("Readings: 3");
out.newLine();
out.write(String.format(Locale.ROOT, "Average: %.2f", 3.0));
out.newLine();
}
}
}
Het bestand heeft twee regels: “Readings: 3” en “Average: 3.00”. Locale.ROOT legt de decimale punt vast. newLine(), println en %n gebruiken allemaal het regeleinde van het platform; een letterlijke \n schrijft op elk systeem LF.
Een PrintWriter biedt de handige methoden print, println en printf, maar die registreren invoer- en uitvoerfouten in plaats van ze op te werpen. checkError() schrijft de buffer door en geeft de foutvlag terug. Als je ook op fouten bij de definitieve sluiting controleert, zorg dan dat je toegang tot de schrijver houdt en controleer na het sluiten. Gebruik BufferedWriter of de Files-methoden voor code die schrijffouten betrouwbaar moet doorgeven.
Maak de kringloop rond
Bestandsschrijvers bufferen uitvoer. flush() stuurt wachtende tekst verder; close() schrijft de buffer door en geeft het bestand vrij. Try-with-resources zorgt dat bij elke manier van verlaten wordt geprobeerd te sluiten, maar kan een schrijfbewerking naar de schijf niet laten slagen.
Om een persoon op te slaan, schrijf je de naam, een komma en de gehele leeftijd, gevolgd door een regeleinde. Maak kommagetallen op met Locale.ROOT, zodat een latere Double.parseDouble ze kan lezen. Een ontbrekende bovenliggende map wordt niet aangemaakt door een bestand te openen: gebruik Files.createDirectories(folder) als het de bedoeling is die aan te maken, of meld de fout. Een schrijfbewerking kan mislukken na het afkappen of na gedeeltelijke uitvoer; gebruik tijdelijke uitvoer en een atomaire verplaatsing wanneer het doelbestand moet worden beschermd.
Belangrijkste punten
- Standaard schrijven maakt een ontbrekend bestand aan en kapt een bestaand bestand af.
- Gebruik
CREATEmetAPPENDom oude inhoud te behouden en een nieuw logbestand mogelijk te maken. - Sluiten schrijft gebufferde uitvoer door en geeft de systeembron vrij.
PrintWriterregistreert schrijffouten;checkError()maakt die zichtbaar.BufferedWriteren de methoden vanFilesgeven invoer- en uitvoerfouten door.- Gebruik UTF-8 en
Locale.ROOTom tekst uitwisselbaar te schrijven en terug te lezen. - Een bestand schrijven maakt ontbrekende bovenliggende mappen niet aan.
5.3 Bouw een rapport zonder fouten te verbergen
Een robuust programma voor bestandsverwerking heeft twee herstelniveaus nodig. Een ongeldig record kan worden overgeslagen en gemeld; als het lezen of opslaan van het bestand mislukt, stopt de taak. Bereken een volledig resultaat voordat je het doelbestand aanraakt.
Lezen, valideren, berekenen, opslaan
Het volledige programma hieronder leest de optionele kopregel “date,temperature”, gevolgd door eenvoudige records met twee velden. Datums zijn niet-lege labels; temperaturen zijn eindige getallen in graden Celsius. Het slaat onjuist gevormde records over en meldt die, inclusief lege regels. De geneste gecontroleerde exceptie houdt recordproblemen gescheiden van bestandsproblemen.
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.AtomicMoveNotSupportedException;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.List;
import java.util.Locale;
public class TemperatureReport {
static class InvalidRecordException extends Exception {
InvalidRecordException(String message) { super(message); }
InvalidRecordException(String message, Throwable cause) {
super(message, cause);
}
}
static List<String> readLines(Path file) throws IOException {
return Files.readAllLines(file, StandardCharsets.UTF_8);
}
static double parseTemperature(String line, int lineNumber)
throws InvalidRecordException {
String[] parts = line.split(",", -1);
if (parts.length != 2 || parts[0].isBlank()
|| parts[1].isBlank()) {
throw new InvalidRecordException("Line " + lineNumber
+ ": expected a date label and a temperature");
}
try {
double value = Double.parseDouble(parts[1].trim());
if (!Double.isFinite(value)) {
throw new InvalidRecordException("Line " + lineNumber
+ ": temperature must be finite");
}
return value;
} catch (NumberFormatException e) {
throw new InvalidRecordException("Line " + lineNumber
+ ": not a number: " + parts[1], e);
}
}
static String summarize(List<String> lines) {
int count = 0;
int skipped = 0;
double sum = 0;
double min = Double.POSITIVE_INFINITY;
double max = Double.NEGATIVE_INFINITY;
for (int i = 0; i < lines.size(); i++) {
String line = lines.get(i);
if (i == 0 && line.equals("date,temperature")) continue;
try {
double value = parseTemperature(line, i + 1);
sum += value;
count++;
min = Math.min(min, value);
max = Math.max(max, value);
} catch (InvalidRecordException e) {
skipped++;
System.err.println(e.getMessage());
}
}
if (count == 0) {
return String.format(Locale.ROOT,
"No valid readings%nSkipped lines: %d%n", skipped);
}
return String.format(Locale.ROOT,
"Valid readings: %d%nAverage (°C): %.2f%n"
+ "Minimum (°C): %.2f%nMaximum (°C): %.2f%n"
+ "Skipped lines: %d%n",
count, sum / count, min, max, skipped);
}
static void writeSummary(Path file, String text) throws IOException {
Path target = file.toAbsolutePath();
Path temp = Files.createTempFile(target.getParent(), "report-", ".tmp");
try {
Files.writeString(temp, text, StandardCharsets.UTF_8);
Files.move(temp, target, StandardCopyOption.ATOMIC_MOVE);
} catch (IOException e) {
try {
Files.deleteIfExists(temp);
} catch (IOException cleanup) {
e.addSuppressed(cleanup);
}
throw e;
}
}
public static void main(String[] args) {
Path input = Path.of("readings.csv");
Path output = Path.of("summary.txt");
try {
List<String> lines = readLines(input);
String report = summarize(lines);
writeSummary(output, report);
System.out.println("Saved: " + output.toAbsolutePath());
} catch (NoSuchFileException e) {
System.err.println("File or folder not found: " + e.getFile());
} catch (AtomicMoveNotSupportedException e) {
System.err.println("Could not save atomically: "
+ output.toAbsolutePath());
} catch (IOException e) {
System.err.println("Could not process " + input.toAbsolutePath()
+ " or save " + output.toAbsolutePath() + ": " + e.getMessage());
}
}
}
Volg één uitvoering
Gebruik deze vijf invoerregels zonder kopregel:
2026-03-01,4.5
2026-03-02,6.0
2026-03-03,abc
2026-03-04,-1.5
missing comma
Regels 1, 2 en 4 leveren drie geldige metingen op. De som is 9,0; het gemiddelde is 3,00 °C, het minimum is -1,50 °C en het maximum is 6,00 °C. Regels 3 en 5 leveren foutmeldingen op en het rapport vermeldt twee overgeslagen regels. De parseerafhandelaar staat binnen de lus, zodat na elk ongeldig record de latere records bereikbaar blijven. Door totalen pas na geslaagd parsen bij te werken, blijven afgewezen waarden buiten de totalen.
Double.parseDouble accepteert speciale strings zoals “NaN” en “Infinity”. Double.isFinite wijst die hier af omdat ze niet als gewone gemeten temperaturen kunnen dienen. Een leeg bestand, een bestand met alleen een kopregel of uitsluitend ongeldige records bereikt de tak voor een aantal van nul voordat wordt gedeeld of het oorspronkelijke minimum en maximum worden gebruikt.
Bescherm het doelbestand
Het lezen en berekenen zijn voltooid voordat het opslaan begint. Files.writeString opent en sluit zelf een bestand, maar kan mislukken nadat een deel is geschreven. Het programma schrijft daarom naar een tijdelijk bestand in de doelmap, sluit dat en vraagt om ATOMIC_MOVE. De verplaatsing maakt het voltooide resultaat, als de bewerking wordt ondersteund, in één bestandssysteembewerking zichtbaar.
Of een bestaand doelbestand atomair kan worden vervangen, hangt af van de implementatie. Het programma meldt een niet-ondersteunde verplaatsing of vervanging en probeert nooit opnieuw met een niet-atomaire verplaatsing. Een opruimfout wordt aan de oorspronkelijke exceptie toegevoegd in plaats van die te vervangen. Atomaire zichtbaarheid garandeert niet dat het resultaat een stroomuitval overleeft.
Vooraf Files.exists(input) controleren kan de latere leesbewerking niet garanderen: een bestand kan na de controle verdwijnen of ontoegankelijk worden. Handel de daadwerkelijke bewerking af. Probeer voordat je op een verwerkingsprogramma vertrouwt ontbrekende invoer, ontbrekende uitvoermappen, lege bestanden en bestanden met alleen een kopregel, CRLF-regeleinden, tekst met accenttekens en onjuist gevormde records. Gebruikersmeldingen moeten het pad noemen waarbij de fout optrad; voor het opsporen van fouten in een wrapper zijn ook de oorspronkelijke oorzaak en stacktrace nodig.
Belangrijkste punten
- Handel recordfouten binnen de lus af en fouten op bestandsniveau erbuiten.
- Valideer een volledig record voordat je aantallen of totalen bijwerkt.
- Controleer of het aantal geldige records nul is voordat je een gemiddelde berekent.
- Voltooi het lezen en berekenen voordat je de uitvoer opent.
- Schrijf en sluit een tijdelijk bestand en vraag daarna om een atomaire verplaatsing om het doelbestand te beschermen.
- Meld niet-ondersteunde atomaire vervanging in plaats van ongemerkt terug te vallen op een andere werkwijze.
- Houd oorspronkelijke oorzaken en opruimfouten beschikbaar voor het opsporen van fouten.
Excepties en bestanden in Java: voorbeelden
1Volg een fout terug naar de oorzaak
11 stappen
- 1IntroductieEen programma kan compileren en toch mislukken
Een invoerwaarde kan geldige Java-code tijdens de uitvoering laten mislukken. Het exceptieobject vertelt je welk soort probleem optrad en de stacktrace toont welke methodeaanroepen actief waren.
Volg eerst een mislukte conversie naar een geheel getal. Gebruik daarna dezelfde overervingsregels die objecten van een subklasse bruikbaar maken als objecten van een superklasse om te bepalen wat je van de compiler moet afhandelen.
- 2VoorbeeldBekijk hoe de conversie stopt
Een methode
loadCountvoert deze instructies uit:System.out.print("start "); int count = Integer.parseInt("eight"); System.out.print("finish");De methode geeft
startweer en werpt daarnaNumberFormatExceptionop. De toewijzing wordt niet voltooid enfinishwordt overgeslagen. Een passende afhandelaar kan het overnemen, maar de mislukte instructie wordt na herstel niet hervat. - 3UitlegVolg de actieve aanroepen
Stel dat
mainde methodeloadCountaanriep, die vervolgensInteger.parseIntaanriep. Die aanroepen maken deel uit van de aanroepstack. De exceptie gaat naar buiten wanneer een methode geen passende afhandelaar heeft.parseIntmislukt als eerste,loadCountkan de normale werkzaamheden niet voltooien en de aanroeper krijgt de kans de fout af te handelen. Als de exceptiemainonverwerkt verlaat, meldt Java de exceptie en eindigt de hoofdthread. - 4VoorbeeldZoek je eerste frame
Een stacktrace kan na de bibliotheekframes van Java deze applicatieframes bevatten:
at Counts.loadCount(Counts.java:14) at Counts.main(Counts.java:5)Bekijk eerst regel 14: dat is de aanroep in je code die het dichtst bij de plaats ligt waar het probleem is vastgesteld. Regel 5 vertelt je waar
loadCountis aangeroepen. De eerste regel van de exceptie geeft de klasse en melding; verwar het lagere frame van de aanroeper niet met de instructie die de ongeldige tekst als eerste gebruikte. - 5UitdagingZoek de overgeslagen uitvoer
Een hulpmethode geeft
Aweer, roeptInteger.parseInt("q2")aan en zou daarnaBweergeven. De aanroeper vangtNumberFormatExceptionop en geeftCweer. Welke letters worden weergegeven en waarom?---reveal---
Aen daarnaC. De conversie werpt vóórBeen exceptie op; door doorgifte komt de fout bij de passende afhandelaar van de aanroeper. De resterende instructies van de hulpmethode worden afgebroken, dus na herstel keert de uitvoering niet terug omBweer te geven. - 6TipKijk verder dan de instructie waar het probleem is vastgesteld
De instructie waar een fout wordt vastgesteld, kan een ongeldige waarde gebruiken die eerder is ontstaan. Bij een null-referentiefout moet je nagaan waar die referentie een object had moeten krijgen.
Gebruik
getMessage()voor de foutmelding enprintStackTrace()voor de aanroepketen. Een melding kannullzijn; een onderdeelCaused by:toont een andere exceptie die in een wrapper is bewaard. - 7UitlegGebruik de keten van superklassen
Throwableis de basisklasse voor objecten die kunnen worden opgeworpen. De hoofdtakken zijnErrorenException;RuntimeExceptionstaat onderException.RuntimeException,Erroren elke subklasse van een van beide zijn ongecontroleerd. Andere subklassen vanThrowablezijn gecontroleerd. Fouten uit beide categorieën treden tijdens de uitvoering op; “gecontroleerd” beschrijft de controle van de afhandeling door de compiler, niet een ander moment waarop de exceptie wordt opgeworpen. - 8VoorbeeldVergelijk een parser en een bestandslezer
NumberFormatExceptionbreidtIllegalArgumentExceptionuit, dat op zijn beurtRuntimeExceptionuitbreidt, dus heeft een aanroep vanInteger.parseIntgeen verplichte afhandelaar.IOExceptionbreidtExceptionuit buiten die tak. Een aanroep vanFiles.readStringmoet de mogelijkeIOExceptionopvangen of declareren metthrows. Een bestandsbewerking kan om externe redenen mislukken, ook wanneer de aanroepende code correct is. - 9UitdagingDeel een onbekende fout in
BadRecordExceptionbreidtExceptiondirect uit. Een hulpmethode declareert dat die dit type kan opwerpen. Wat moet de aanroeper doen en wanneer wordt die verplichting gecontroleerd?---reveal---
De exceptie opvangen of een toepasselijk gecontroleerd type declareren in de methodedeclaratie van de aanroeper. De compiler dwingt die verplichting af omdat de klasse buiten de ongecontroleerde takken valt. Of een afzonderlijke uitvoering slaagt, neemt de eis op declaratieniveau niet weg.
- 10UitdagingVerklaar een verrassend brede afhandelaar
Een blok dat een bestand leest vangt
Exceptionop en geeft “Bestandsprobleem” weer. In plaats daarvan mislukt het met eenNullPointerExceptiondoor een programmeerfout. Ontvangt de afhandelaar die exceptie en is de melding betrouwbaar?---reveal---
Ja:
NullPointerExceptionbreidtRuntimeExceptionuit, datExceptionuitbreidt. De melding is misleidend omdat deze fout een programmeerfout is en geen aanwijzing voor een bestandsprobleem. Lees de stacktrace en beperk de verwachte afhandelaar tot de fouten die die kan verklaren. - 11SamenvattingKies de volgende stap bij het opsporen van fouten
Begin een foutonderzoek met de klasse, de melding en het eerste applicatieframe. Volg de aanroepers en de oorsprong van ongeldige waarden als de instructie waar het probleem is vastgesteld de oorzaak niet op zichzelf verklaart.
Controleer daarna de overerving voordat je de afhandeling kiest: gecontroleerde fouten vereisen een beslissing die zichtbaar is voor de compiler; ongecontroleerde programmeerfouten moeten nog steeds worden hersteld. Voorkom dat brede afhandelaars het ene soort probleem als een ander soort voorstellen.
2Volg herstel- en opruimpaden
12 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieBepaal wat er daarna moet gebeuren
- 2VoorbeeldHerstel na de volledige instructie
- 3TipLees nieuwe tekst bij een nieuwe poging
- 4UitdagingHerstel de vastgelopen scanner
- 5UitlegGeef een specifieke fout een eigen reactie
- 6VoorbeeldLaat de lus doorgaan
- 7UitdagingVerplaats de grens van het beschermde codegebied
- 8KernpuntDeel reacties zonder overlappende typen
- 9UitlegOpruimcode wordt vóór het verlaten uitgevoerd
- 10UitdagingVolg een fout zonder passende afhandelaar
- 11UitdagingVoorkom dat opruimcode een fout bij een nieuwe poging verbergt
- 12SamenvattingVolg de volgende bereikbare instructie
3Wijs een verzoek af zonder de toestand te beschadigen
11 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieBehoud een bruikbaar object
- 2VoorbeeldValideer vóór het aftrekken
- 3UitlegOnderscheid throw van de declaratie
- 4UitdagingKies de juiste standaardexceptie
- 5VerbandEen overschrijvende methode houdt zich aan de belofte aan aanroepers
- 6VoorbeeldGeef onvoldoende voorraad een eigen type
- 7VoorbeeldHoud het mislukte verzoek afzonderlijk
- 8UitdagingBepaal welke toestand overblijft
- 9UitlegBehoud de oorspronkelijke conversiefout
- 10UitdagingGeef een bestandsfout door via een beperkte methode
- 11SamenvattingOntwerp een exceptie die de aanroeper kan gebruiken
4Open de juiste tekst en sluit die veilig
12 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieZowel locatie als levensduur is van belang
- 2VoorbeeldBepaal het pad vanuit de werkmap
- 3UitlegDecodeer met de tekenset die bij het schrijven is gebruikt
- 4TipVerwijder een volledig regeleinde
- 5UitdagingZorg dat een kommagetal kan worden teruggelezen
- 6UitlegDeclareer een te sluiten systeembron
- 7VerbandGebruik het sluitcontract
- 8VoorbeeldKeer de openingsvolgorde om
- 9UitdagingVolg een mislukte tweede opening
- 10KernpuntHoud een fout uit de body als hoofdmelding
- 11UitdagingOnderscheid een gegevensfout van een opruimfout
- 12SamenvattingBekijk een bestandsbewerking op volgorde
5Zet metingen om in een betrouwbaar rapport
16 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieEen rapport vereist meer dan correct rekenwerk
- 2UitlegKies hoe regels binnenkomen
- 3VoorbeeldDefinieer het record vóór het parsen
- 4VoorbeeldValideer voordat je het tweede veld gebruikt
- 5UitdagingZoek het afgewezen record
- 6UitlegKies tussen vervangen en achteraan toevoegen
- 7VoorbeeldSchrijf een uitwisselbare rapportregel
- 8TipMerk een mislukte schrijfbewerking op
- 9UitdagingBehoud oude en nieuwe logregels
- 10VerbandHerstel op twee verschillende niveaus
- 11VoorbeeldWerk totalen pas na validatie bij
- 12UitdagingHandel een waarde af die wel kan worden geparset maar niet bruikbaar is
- 13UitlegVoltooi de uitvoer voordat je het doelbestand vervangt
- 14TipHoud de opslagfout zichtbaar
- 15UitdagingKies wat veilig kan worden gerapporteerd
- 16SamenvattingBeoordeel het verwerkingsprogramma per foutpad
Excepties en bestanden in Java: oefenvragen
Set 1: Volg fouten en kies passend herstel10 Oefenvragen
Vraag 1 van 10
values[5] evalueert?Toon antwoordVerberg antwoord
ArrayIndexOutOfBoundsException op. Vijf elementen tellen maakt index 5 niet geldig: het vijfde element staat op index 4.Vraag 2 van 10
DataProblem breidt Exception direct uit. Welke verplichting heeft een methode die een hulpmethode aanroept met de declaratie throws DataProblem?Toon antwoordVerberg antwoord
DataProblem is gecontroleerd omdat die klasse Exception uitbreidt buiten de tak van RuntimeException. De aanroepende code moet de exceptie opvangen of een toepasselijk gecontroleerd type declareren. De compiler controleert de gedeclareerde mogelijkheid, ook als een bepaalde aanroep zou slagen.Vraag 3 van 10
Welke letters worden op volgorde weergegeven?
System.out.print("P");
try {
int n = Integer.parseInt("7q");
System.out.print("Q");
} catch (NumberFormatException e) {
System.out.print("R");
}
System.out.print("S");
Toon antwoordVerberg antwoord
NumberFormatException op vóór de instructie die Q weergeeft. De passende afhandelaar geeft R weer, waarna de uitvoering verdergaat na de instructie en S weergeeft. De overgeslagen instructie wordt na herstel niet uitgevoerd, dus PQRS kan de uitvoer niet zijn.Vraag 4 van 10
NoSuchFileException of een andere IOException opwerpen. Welke catch-volgorde geeft elk een eigen reactie?Toon antwoordVerberg antwoord
NoSuchFileException breidt IOException uit, dus moet de specifieke afhandelaar eerst staan. Als IOException eerst staat, vangt die al elke exceptie voor een ontbrekend bestand op en wordt de latere afhandelaar onbereikbaar. Een meervoudige catch-clausule kan deze verwante typen evenmin combineren.Vraag 5 van 10
Wat geeft een aanroep van pick() weer en wat retourneert die?
static int pick() {
try {
return 8;
} finally {
System.out.print("done");
}
}
Toon antwoordVerberg antwoord
finally uit voordat de waarde aan de aanroeper wordt geleverd. De methode geeft daarom done weer en retourneert 8. Een return binnen try slaat het opruimen niet over; alleen een concurrerende overdracht vanuit de opruimcode zou dat resultaat vervangen.Vraag 6 van 10
Toon antwoordVerberg antwoord
IllegalArgumentException beschrijft de fout. IllegalStateException zou een aanroep beschrijven die door de huidige toestand van het object verboden is. Er wordt geen parseerbewerking genoemd, dus een numeriek argument alleen maakt dit geen getalopmaakfout.Vraag 7 van 10
/work/app. Het maakt Path.of("data", "levels.txt") aan. Welke absolute locatie duidt dat relatieve pad aan?Toon antwoordVerberg antwoord
/work/app aan tot /work/app/data/levels.txt. Java voegt geen broncodemap in. Het bestand naast de broncode plaatsen zou alleen werken als dat toevallig de locatie is die het bepaalde pad aanduidt.Vraag 8 van 10
a wordt geopend, maar het openen van systeembron b werpt een exceptie op. Beide zijn in die volgorde in dezelfde try-with-resources-kop gedeclareerd. Ook het sluiten van a werpt een exceptie op. Welke fout bereikt de aanroeper?Toon antwoordVerberg antwoord
b mislukt, sluit Java de al geopende a nog steeds. De openingsfout blijft de hoofdfout en de latere sluitfout wordt als onderdrukte exceptie toegevoegd. Wie de sluitfout als hoofdfout kiest, verwart try-with-resources met handmatige opruimcode die een eerdere fout kan overschrijven.Vraag 9 van 10
BufferedReader heeft het einde van het bestand bereikt. Wat geeft de volgende readLine() terug?Toon antwoordVerberg antwoord
BufferedReader.readLine() geeft null terug als er geen tekens meer zijn om nog een regel te vormen. Een lege string stelt een daadwerkelijke lege regel voor, dus als je die als eindmarkering gebruikt, stop je te vroeg. Regeleinden worden uit teruggegeven regels verwijderd en zijn geen markering voor het einde van het bestand.Vraag 10 van 10
Files.writeString bestaande tekst behouden en nieuwe tekst aan het einde toevoegen?Toon antwoordVerberg antwoord
APPEND schrijft na bestaande inhoud en CREATE maakt het mogelijk een ontbrekend bestand aan te maken. Alleen APPEND gebruiken mislukt als het logbestand niet bestaat. TRUNCATE_EXISTING zou bestaande inhoud wissen, terwijl CREATE_NEW een pad afwijst dat al bestaat.Set 2: Pas contracten toe op records en bestandsuitvoer10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Een stacktrace begint met deze frames. Welke regel in de klasse van de cursist moet eerst worden bekeken?
java.lang.NumberFormatException: For input string: "six"
at java.base/java.lang.NumberFormatException.forInputString(...)
at java.base/java.lang.Integer.parseInt(...)
at Importer.readCount(Importer.java:18)
at Importer.main(Importer.java:6)
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
AmountProblem breidt IllegalArgumentException uit. Hoe wordt die klasse ingedeeld?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
InputMismatchException van Scanner.nextInt() op, geeft een melding weer en roept onmiddellijk opnieuw nextInt() aan. De volgende token is “many”. Waarom blijft de lus mislukken?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
void load() throws IOException. Een implementatie moet een fout voor een ontbrekend bestand declareren. Welke gecontroleerde declaratie is toegestaan bij het overschrijven?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
remove wijst verzoeken die groter zijn dan de huidige voorraad af voordat de map wordt gewijzigd. Aanroepen verwijderen eerst 6 en vragen daarna om 14; de tweede fout wordt ingepakt met de oorzaak en door de aanroeper opgevangen. Welke voorraad en foutinformatie blijven over?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Double.parseDouble("6,25") wordt uitgevoerd op een computer met Duitse taal- en regio-instellingen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
line.split(",") en line.split(",", -1) is van belang?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
PrintWriter, maar een volle schijf verhindert dat de tekst wordt opgeslagen. Wat moet het gebruiken om de geregistreerde fout van de schrijver te ontdekken?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 3: Volg samenwerkende fouten en opruimcode10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
NumberFormatException wanneer die door een afhandelaar voor een superklasse wordt opgevangen?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Welke waarde wordt weergegeven?
int count;
try {
count = Integer.parseInt("bad");
} catch (NumberFormatException e) {
count = 4;
}
System.out.println(count + 2);
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
Welke waarde bereikt de laatste weergave-instructie?
int total = 0;
try {
for (String text : new String[] {"8", "bad", "3"}) {
try {
total += Integer.parseInt(text);
} catch (NumberFormatException e) {
throw new IllegalStateException("record", e);
}
}
} catch (IllegalStateException e) {
total += 2;
}
System.out.println(total);
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Wat doet result()?
static int result() {
try {
throw new IllegalArgumentException("bad");
} finally {
return 9;
}
}
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
NumberFormatException e op. De zelfgedefinieerde InvalidRecordException heeft constructoren voor alleen een melding en voor een melding met een oorzaak. Hoe moet de parser “Line 8” toevoegen met behoud van de oorspronkelijke stacktrace?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
Files.readString(path).split("\n") en daarna Integer.parseInt op elk niet-leeg deel. Waarom kan de conversie van een deel met de cijfers “27” mislukken?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
a en b worden in die volgorde succesvol geopend. De body werpt E op; het sluiten van b werpt F op; het sluiten van a werpt G op. Wat bereikt de catch die bij die try-with-resources-instructie hoort?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Files.exists en daarna gelezen met een scanner. De lus eindigt na slechts enkele records omdat de onderliggende lezer faalde, en het programma begint direct met opslaan. Wat moet veranderen om te voorkomen dat deze gedeeltelijke invoer als geslaagd wordt beschouwd?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Set 4: Onderzoek en verbeter een volledig verwerkingsprogramma10 Oefenvragen
Sla deze les op om elk antwoord te zien en elke set te oefenen.
Vraag 1 van 10
Wat wordt weergegeven voordat de exceptie main verlaat en welk applicatieframe staat als eerste in de stacktrace?
public class Counter {
static int read() {
return Integer.parseInt("9z");
}
static void show() {
System.out.print("L");
System.out.print(read());
System.out.print("M");
}
public static void main(String[] args) {
show();
System.out.print("N");
}
}
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 3 van 10
catch (NumberFormatException | IllegalArgumentException e). Waarom compileert deze meervoudige catch-clausule niet?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
Wat geeft choose() weer en wat retourneert de methode?
static int choose() {
int value = 5;
try {
return value;
} finally {
value = 12;
System.out.print(value);
}
}
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
ReaderTask declareert String read() throws IOException. Klasse FixedTask implementeert die met public String read() en zonder gecontroleerde declaratie. Variabele task heeft type ReaderTask en verwijst naar een FixedTask. Wat moet code doen die task.read() aanroept?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
IOException kan opwerpen. Welke standaardwrapper behoudt die oorzaak en geeft een ongecontroleerde fout door?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
close() werpt daarna IOException op en de bijbehorende catch retourneert 2; het finally-blok geeft “closed” weer zonder een exceptie op te werpen. Wat doet de methode?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Double.parseDouble kan worden gelezen. Welke schrijfaanpak voldoet aan die opmaak?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Kom je niet uit je eigen opgave? Huiswerkhulp
Kernbegrippen
A
Aanroepstack
main de methode load aanroept, die parse aanroept, houdt de aanroepstack alle drie actieve methodeaanroepen bij.Absoluut pad
toAbsolutePath() toont het absolute pad waarmee je kunt onderzoeken of een bestand in de verkeerde map staat.Achteraan toevoegen
Afkappen
Files.writeString veroorzaakt afkappen, zodat een latere schrijffout een onvolledig resultaat kan achterlaten.ArithmeticException
ArithmeticException opwerpen.Atomaire verplaatsing
AutoCloseable
AutoCloseable implementeert, kan de naam weergeven wanneer de automatische opruimcode close() aanroept.B
Beschermd codegebied
Buffer
Buffer doorschrijven
BufferedReader
BufferedReader-lus verwerkt grote invoer totdat readLine() aan het einde null teruggeeft.BufferedWriter
BufferedWriter schrijft een rapportregel en gebruikt daarna newLine() om het regeleinde van het platform toe te voegen.C
catch-clausule
NumberFormatException kan om een ander getal vragen nadat de gebruiker “many” invoert.Closeable
BufferedReader implementeert Closeable en komt dus in aanmerking voor een try-with-resources-declaratie.CSV
D
Doorgifte van excepties
E
Error
OutOfMemoryError behoort tot Error; gewone ongeldige invoer opvangen lost een uitgeputte heap niet op.Exceptie
Exceptiehiërarchie
Exceptiemaskering
finally kan exceptiemaskering veroorzaken door een fout uit het try-blok te laten vervallen.Excepties ketenen
F
finally-blok
return voorbereide waarde de aanroeper bereikt.Fout op bestandsniveau
Fout op recordniveau
G
Gecontroleerde exceptie
IOException is een gecontroleerde exceptie, dus moet een leeshulpmethode die opvangen of declareren.H
Herstel
I
IllegalArgumentException
IllegalArgumentException opwerpen wanneer het vereiste positieve aantal als -2 wordt opgegeven.IllegalStateException
IllegalStateException opwerpen wanneer code probeert er nog een artikel aan toe te voegen.InputMismatchException
nextInt() werpt InputMismatchException op bij “several” en laat die token beschikbaar om te verwijderen.K
Kopregel
M
Meervoudige catch-clausule
N
NullPointerException
name.trim() wanneer name null is, werpt NullPointerException op voordat tekst zonder omringende witruimte wordt teruggegeven.NumberFormatException
Integer.parseInt("blue") werpt NumberFormatException op omdat de tekst geen geheel getal kan voorstellen.O
Onbereikbare exceptieafhandelaar
IOException eerst staat en NoSuchFileException daarna wordt opgevangen, ontstaat een onbereikbare exceptieafhandelaar.Onderdrukte exceptie
Ongecontroleerde exceptie
Oorzaak
InvalidRecordException kan de NumberFormatException zijn die het numerieke veld opwierp.Opruimen
Opvangen-of-declarerenregel
Files.readString aanroept, ook wanneer het bestand dat de methode leest bestaat.P
Parsen
Path
Path.of("data", "counts.txt") maakt een Path aan zonder te testen of de gegevensmap bestaat.PrintWriter
PrintWriter kan een gemiddelde opmaken met printf, maar succesvol terugkeren uit die aanroep bewijst niet dat het opslaan is geslaagd.R
Regeleinde
BufferedReader.readLine() wordt gelezen, wordt verwijderd voordat de methode de regeltekst teruggeeft.Relatief pad
data/counts.txt kan een ander bestand aanduiden wanneer het programma in een andere map start.RuntimeException
NumberFormatException bereikt RuntimeException via de superklasse IllegalArgumentException.S
Scanner
Scanner voor bestanden heeft na de lus een controle met ioException() nodig, zodat een mislukte leesbewerking niet succesvol lijkt.Stacktrace
Systeembron
T
Taal- en regio-instelling
Tekencodering
throw
throw vóór de veldtoewijzing wijst een ongeldige capaciteit af met behoud van de oude veldwaarde.Throwable
Exception tot Throwable, zodat Java die kan opwerpen en opvangen.throws-clausule
throws IOException laat een bestandshulpmethode een fout melden aan een aanroeper die kan herstellen.Tijdelijk bestand
try-blok
try-with-resources
U
UncheckedIOException
IOException kan declareren, kan een bestandsfout doorgeven met UncheckedIOException.UTF-8
V
Veld
Volgorde van catch-clausules
NoSuchFileException vóór IOException wanneer elk een afzonderlijke melding nodig heeft.Vroegtijdig afbreken
W
Wachtende terugkeer
finally evalueert de expressie van een wachtende terugkeer niet opnieuw.Werkmap
/work/app als werkmap verwijst data/counts.txt naar een locatie binnen /work/app/data.Z
Zelfgedefinieerde exceptie
- 1Eerste Java-programma’s
- 2Voorwaarden en lussen in Java
- 3Java-methoden
- 4Lijsten, arrays en strings in Java
- 5Java-klassen en objecten
- 6Objecten, referenties en gelijkheid in Java
- 7HashMaps en collecties in Java
- 8Overerving in Java
- 9Excepties en bestanden in Java
- 10Klassendiagrammen en unittests in Java
- 11Lambda-expressies en streams in Java
Over deze les
Door StudeliFout melden
Maak je eigen Studiemateriaal
Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.