{
  "assessmentTests": {
    "java_test": {
      "name": "Java Test",
      "desc": "30 scenariavragen over syntax en types, methoden/overloading/scope, collections/generics/control flow en exceptions/concurrency/idioms — ontdek of uw Java overeenkomt met wat een vacature bedoelt met sterke Java-vaardigheden.",
      "recommendation": "Uw Java-vaardigheidsprofiel",
      "results": {
        "beginner": {
          "name": "Beginner",
          "desc": "U bent vertrouwd met de basisbegrippen van Java — variabelen, if/else, loops, arrays — maar de vragen die u hebt gemist clusteren rond wat code werkelijk doet wanneer het wordt uitgevoerd, in tegenstelling tot wat het eruit ziet alsof het zou moeten doen. Een String vergelijking met == die stilletjes referenties in plaats van inhoud controleert. Een Integer-autoboxing cache die == lijkt te doen werken onder 128 maar stilletjes faalt boven. Een finally-blok dat een retourwaarde overschrijft zonder dat iemand het opmerkt. Dit zijn niet syntaxisfouten; dit zijn de regels die Java-implementatie stil gedraagt terwijl de code er onschuldig uitziet.",
          "recommendation": "Begin met drie dingen, in deze volgorde: waarom == objectidentiteit controleert (referenties), niet inhoudsgelijkheid — zelf voor String die u zou verwachten te werken — dus .equals() vereist is voor echte vergelijking; hoe Integer autoboxing cache werkt en waarom nieuwe Integer(128) == 128 onwaar is; en wat een finally-blok werkelijk doet wanneer er een return of throw in het try-blok staat. Effectief Java van Joshua Bloch behandelt alle drie met werkende voorbeelden."
        },
        "intermediate": {
          "name": "Intermediate",
          "desc": "U schrijft gewone Java met generics, collections en exception handling zonder veel nadenken — u weet wanneer u ArrayList moet gebruiken in plaats van array, u begrijpt try/catch, u kunt een method signatuur lezen. De kloof tussen hier en Advanced is voornamelijk wat er gebeurt wanneer regels interageren: een ConcurrentModificationException die stilletjes verschijnt wanneer u een List wijzigt terwijl u eruit itereert, een generic-erasure die een werking maakt ClassCastException die slechts bij runtime verschijnt, een static method van een instance object aanroepen en het compileren zonder dat iemand het opmerkt, overload resolution dat de verkeerde method kiest wanneer null in een argument staat. Dit zijn het soort bugs die syntaxiscontrole doorstaat en alleen verschijnen in tests of in productie.",
          "recommendation": "Richt u op hoe Java-regels interageren in plaats van wat elk afzonderlijk doet: waarom u niet in een List kunt wijzigen terwijl u door deze itereert (iterator-invalidation), hoe generics werken op compilatietijd maar volledig worden gewist op runtime (type erasure), waarom static methods geen polymorfisme hebben zelfs als u ze aanroept op een instance, en hoe overload resolution null plaatst (het kiest de meest specifieke methode, dus null kan het kiezen verrassend maken). Vervolgens method-precedence: hoe Java kiest welke overload moet worden gebruikt wanneer meerdere passen."
        },
        "advanced": {
          "name": "Advanced",
          "desc": "Dit is het niveau dat de meeste vacatures met \"sterke Java\" bedoelen. U leest een multi-line codefragment en kunt voorspellen wat het zal afdrukken of welke exception het zal gooien voordat u het uitvoert, u weet waarom iets gebeurde in plaats van alleen dat het gebeurde, en u bereikt naar een Stream of een bijzondere exception in plaats van een eenvoudige for-lus wanneer dat het duidelijkere gereedschap is. Wat deze band van de top scheidt, is de defensieve kant van het werk: weten welke lock-strategie nodig is wanneer threads hetzelfde geheugen delen, waarom een volatile veld subtiel anders is dan synchronized, en wat een generieke wildcard beperking werkelijk doet.",
          "recommendation": "Duw in de delen die code beschermen wanneer threads, klassenladers of compilers ermee spelen: hoe synchronized en volatile zich verhouden tot geheugenvisibiliteit en atomaire operaties, hoe generieke wildcards (? extends, ? super) werken en waarom PECS helpt, en wat de semantiek van ConcurrentHashMap is voor threads die elk hun eigen sleutel aanpassen. Lees Java Concurrency in Practice van Brian Goetz en de documentatie van volatile voor voelbare voorbeelden van beiden."
        },
        "expert": {
          "name": "Expert",
          "desc": "U scoorde aan de top van elke sectie — syntax en types, methods en scope, collections en generics, exceptions en concurrency. In de praktijk betekent dit dat u een vreemd codefragment kunt nemen, kan verklaren waarom het wat het doet retourneert of gooit, niet alleen wat de syntaxis zegt dat het moet doen, wat de moeilijker en waardevolere vaardigheid is. Op dit niveau is de taal zelden de beperkende factor; de limiet is meestal het ontwerp of de prestaties eronder.",
          "recommendation": "De opbrengsten zijn nu in architectuur en prestaties: weten wanneer een Stream beter is dan een lus en wanneer niet (lazy evaluation), indexeringsstrategie en het verschil tussen itereren en random-access, en klassenladertopologie als u plug-in-systemen of module-hiërarchieën bouwt. Als u in een rol wordt gescreend, beschrijf een bug als de race condition die u hebt gevonden onder gelijktijdige aantekeningen of de generic-erasure-valkuil die u in productie hebt gevonden in plaats van Java-functies op te noemen — dit demonstreert het redeneren, niet alleen het vocabulaire."
        }
      },
      "questions": [
        {
          "question": "U maakt twee String-objecten: String a = new String(\"cat\"); String b = new String(\"cat\"); Wat geeft a == b terug?",
          "options": [
            {
              "icon": "",
              "label": "true — String-letteralen met dezelfde karakters zijn altijd hetzelfde object"
            },
            {
              "icon": "",
              "label": "true — new String(...) hergebruikt de string pool, dus identieke tekst deelt altijd één object"
            },
            {
              "icon": "",
              "label": "Een compilerfout — == kan niet worden gebruikt om String-objecten te vergelijken"
            },
            {
              "icon": "",
              "label": "false — new String(...) wijst altijd een nieuw object op de heap toe, dus == vergelijkt twee verschillende referenties, hoewel .equals(b) true zou retourneren"
            }
          ]
        },
        {
          "question": "Integer x = 100; Integer y = 100; System.out.println(x == y); daarna Integer x2 = 200; Integer y2 = 200; System.out.println(x2 == y2); Wat drukken de twee regels af?",
          "options": [
            {
              "icon": "",
              "label": "true dan true — geautomatiseerde Integer-objecten worden altijd in de cache opgeslagen, ongeacht de waarde"
            },
            {
              "icon": "",
              "label": "false dan false — == op Integer-objecten is altijd een referentievergelijking, dus gelijke waarden komen nooit overeen"
            },
            {
              "icon": "",
              "label": "true dan false, maar alleen omdat 200 een byte overloopt — de cachegrens heeft niets te maken met -128..127"
            },
            {
              "icon": "",
              "label": "true dan false — geautomatiseerde Integer-waarden van -128 tot 127 worden in de cache opgeslagen en gedeeld, dus 100 hergebruikt één object, maar 200 valt buiten de cache en automatiseert naar twee afzonderlijke objecten"
            }
          ]
        },
        {
          "question": "int max = Integer.MAX_VALUE; System.out.println(max + 1); Wat gebeurt er?",
          "options": [
            {
              "icon": "",
              "label": "Het gooit een ArithmeticException voor integer-overloop"
            },
            {
              "icon": "",
              "label": "Het drukt Integer.MAX_VALUE af opnieuw, omdat Java bij het maximum van het type afbreekt"
            },
            {
              "icon": "",
              "label": "Het drukt Integer.MIN_VALUE af — int-rekenkundig loopt stilletjes om in plaats van te gooien bij overloop"
            },
            {
              "icon": "",
              "label": "Het is een compilerfout — de compiler detecteert de overloop van tevoren"
            }
          ]
        },
        {
          "question": "System.out.println(0.1 + 0.2 == 0.3); Wat drukt dit af, en waarom?",
          "options": [
            {
              "icon": "",
              "label": "false — 0.1, 0.2 en 0.3 kunnen niet exact in binaire drijvende-komma worden weergegeven, dus de som bevat afrondingsfout die deze niet bit-voor-bit gelijk maakt aan 0.3"
            },
            {
              "icon": "",
              "label": "true — Java rondt double-rekenkundig af naar het dichtstbijzijnde weergeefbare decimaal vóór vergelijking"
            },
            {
              "icon": "",
              "label": "true — optelling van twee doubles is altijd exact voor waarden met twee decimaalplaatsen"
            },
            {
              "icon": "",
              "label": "Het gooit een exception, omdat == niet is gedefinieerd voor double in Java"
            }
          ]
        },
        {
          "question": "Een klasse declareert twee instance-velden zonder initializer: int count; en Integer total; Wat zijn hun standaardwaarden voordat de constructor loopt?",
          "options": [
            {
              "icon": "",
              "label": "Beide standaard 0, omdat Integer bij veldinitialisatie automatiseert naar int"
            },
            {
              "icon": "",
              "label": "count is 0 en total is 0, automatisch geautomatiseerd de eerste keer dat het wordt gelezen"
            },
            {
              "icon": "",
              "label": "count is 0 en total is null — primitieve numerieke velden standaard nul, maar een niet-geïnitialiseerd wrapper-referentieveld standaard null, zoals elke andere objectreferentie"
            },
            {
              "icon": "",
              "label": "Beide standaard null totdat expliciet toegewezen, omdat Java geen impliciete numerieke standaardwaarden heeft"
            }
          ]
        },
        {
          "question": "Een lus loopt 1000 keer, elke keer result += \"x\"; op een String result. Wat gebeurt er eigenlijk onder de motorkap op elke iteratie?",
          "options": [
            {
              "icon": "",
              "label": "De bestaande String-object's interne karakterarray wordt in plaats uitgebreid"
            },
            {
              "icon": "",
              "label": "Java batcht automatisch de aaneenschakelingen en wijst slechts één final String-object toe"
            },
            {
              "icon": "",
              "label": "Het compileert naar een enkel StringBuilder.append()-oproep gedeeld over alle 1000 iteraties, zonder extra object per iteratie"
            },
            {
              "icon": "",
              "label": "Een gloednieuw String-object wordt gemaakt en result wordt opnieuw ingesteld om ernaar te wijzen — het vorige String-object wordt verwijderd, omdat String onveranderlijk is en += dit niet op zijn plaats kan wijzigen"
            }
          ]
        },
        {
          "question": "final List<String> names = new ArrayList<>(); Welke van deze is waar?",
          "options": [
            {
              "icon": "",
              "label": "names.add(\"Ana\") compileert en werkt prima — final voorkomt alleen hertoewijs van de names-referentie zelf, niet muteren van het object dat het naar wijst"
            },
            {
              "icon": "",
              "label": "names.add(\"Ana\") is een compilerfout, omdat final de lijst zelf onveranderlijk maakt"
            },
            {
              "icon": "",
              "label": "De lijst is thread-veilig voor gelijktijdige schrijven omdat deze als final werd gedeclareerd"
            },
            {
              "icon": "",
              "label": "final op een lokale variabele heeft geen effect tenzij het type ook als onveranderlijk is gedeclareerd"
            }
          ]
        },
        {
          "question": "int[] a = {1, 2, 3}; int[] b = {1, 2, 3}; System.out.println(a == b); System.out.println(Arrays.equals(a, b)); Wat drukken de twee regels af?",
          "options": [
            {
              "icon": "",
              "label": "false dan true — == vergelijkt array-referenties (twee verschillende array-objecten), terwijl Arrays.equals de elementen vergelijkt"
            },
            {
              "icon": "",
              "label": "true dan true — arrays met identieke inhoud zijn hetzelfde object in Java"
            },
            {
              "icon": "",
              "label": "false dan false — Arrays.equals werkt alleen voor object-arrays, niet voor primitieve int-arrays"
            },
            {
              "icon": "",
              "label": "true dan false — == op arrays vergelijkt inhoud, en Arrays.equals is overbodig"
            }
          ]
        },
        {
          "question": "void show(Object o) { print(\"Object\"); } void show(String s) { print(\"String\"); } Object ref = \"hello\"; show(ref); Welke overload loopt, en waarom?",
          "options": [
            {
              "icon": "",
              "label": "show(String) loopt, omdat Java de werkelijke runtime-klasse van het object inspecteert voordat het de overload kiest"
            },
            {
              "icon": "",
              "label": "show(Object) loopt — overload-resolutie wordt op compilatietijd besloten met het gedeclareerde type van de variabele, niet het werkelijke runtime-type van het object dat ernaar wijst"
            },
            {
              "icon": "",
              "label": "Het is een compilerfout — Object kan niet worden doorgegeven waar een meer specifieke overload bestaat"
            },
            {
              "icon": "",
              "label": "Beide methoden lopen, elk eenmaal, omdat Java overloads oplost door elke match te proberen"
            }
          ]
        },
        {
          "question": "Class Base heeft static void greet() { print(\"Base\"); }. Class Derived extends Base en declareert ook static void greet() { print(\"Derived\"); }. U schrijft: Base ref = new Derived(); ref.greet(); Wat wordt er afgedrukt?",
          "options": [
            {
              "icon": "",
              "label": "Derived — static methoden overridden net als instance-methoden, volgend het werkelijke runtime-type van het object"
            },
            {
              "icon": "",
              "label": "Het is een compilerfout — static methoden kunnen niet via een instance-referentie worden aangeroepen"
            },
            {
              "icon": "",
              "label": "Base — static methoden zijn niet polymorf; een oproep via een referentie wordt opgelost door het gedeclareerde type van de referentie op compilatietijd, niet het werkelijke type van het object"
            },
            {
              "icon": "",
              "label": "Zowel Base als Derived drukken af, omdat de oproep naar beide verborgen en verbergende versies oplost"
            }
          ]
        },
        {
          "question": "Binnen een methode schrijft u int count = 0; en probeert u vervolgens count te refereren van binnen een lambda die aan een ander methode wordt doorgegeven. Wat moet waar zijn over count om dit te compileren?",
          "options": [
            {
              "icon": "",
              "label": "count moet effectief final zijn — nooit na de initiële waarde opnieuw toegewezen — omdat een lambda een moment vastlegt, niet een live-referentie naar een veranderlijke lokale variabele"
            },
            {
              "icon": "",
              "label": "Niets — elke lokale variabele kan vrij uit de lambda worden gelezen en opnieuw toegewezen"
            },
            {
              "icon": "",
              "label": "count moet volatile worden verklaard zodat de lambda altijd de meest recente waarde ziet"
            },
            {
              "icon": "",
              "label": "count moet een veld zijn, geen lokale variabele — lambdas kunnen locals helemaal niet vastleggen"
            }
          ]
        },
        {
          "question": "void rename(StringBuilder sb) { sb = new StringBuilder(\"new\"); } wordt aangeroepen als StringBuilder original = new StringBuilder(\"old\"); rename(original); Wat is original na de oproep?",
          "options": [
            {
              "icon": "",
              "label": "Wordt \"new\" — objecten worden per referentie doorgegeven in Java, dus de parameter opnieuw toewijzen verandert ook de variabele van de beller"
            },
            {
              "icon": "",
              "label": "Wordt \"new\" alleen voor veranderlijke typen als StringBuilder, maar niet voor onveranderlijke typen"
            },
            {
              "icon": "",
              "label": "Blijft \"old\" — Java geeft de referentie zelf per waarde door, dus de parameter opnieuw toewijzen in de methode wijst alleen de lokale kopie van de referentie opnieuw aan, waardoor het origineel van de beller ongeraakt blijft"
            },
            {
              "icon": "",
              "label": "Gooit een runtime-exception, omdat sb in de methode opnieuw werd toegewezen"
            }
          ]
        },
        {
          "question": "Een klasse heeft een static initializer-blok, een instance initializer-blok en een constructor, in die volgorde van bron. U maakt twee objecten van deze klasse één na de ander. In welke volgorde lopen deze?",
          "options": [
            {
              "icon": "",
              "label": "Het static-blok loopt exact eenmaal, de eerste keer dat de klasse wordt geladen; vervolgens loopt voor elk object het instance-blok en vervolgens de constructor-tekst"
            },
            {
              "icon": "",
              "label": "Alle drie lopen vers, in volgorde van bron, voor elk enkel object dat wordt gemaakt"
            },
            {
              "icon": "",
              "label": "De constructor loopt eerst voor elk object, vervolgens het instance-blok, vervolgens het static-blok eenmaal helemaal aan het einde"
            },
            {
              "icon": "",
              "label": "Het static-blok loopt eenmaal per object, net vóór het instance-blok"
            }
          ]
        },
        {
          "question": "Een klasse heeft void log(String s) en void log(String... args). U roept log(\"hi\") aan. Welke loopt?",
          "options": [
            {
              "icon": "",
              "label": "log(String... args) — varargs-overloads worden altijd voorkeur gegeven wanneer beide van toepassing zijn"
            },
            {
              "icon": "",
              "label": "Het is een compilerfout — de oproep is dubbelzinnig tussen de twee overloads"
            },
            {
              "icon": "",
              "label": "log(String s) — wanneer een overload met vaste ariteit exact overeenkomt, geeft Java altijd voorkeur aan een varargs-overload, die alleen als laatste toevlucht wordt gebruikt"
            },
            {
              "icon": "",
              "label": "Welke ook als eerste in het bronbestand wordt gedeclareerd loopt"
            }
          ]
        },
        {
          "question": "De eerste regel van een constructor roept this(0); aan om aan een andere constructor in dezelfde klasse over te dragen. Waar mag dit verschijnen?",
          "options": [
            {
              "icon": "",
              "label": "Overal in de constructor-tekst, zolang het loopt voordat het object wordt geretourneerd"
            },
            {
              "icon": "",
              "label": "Alleen als de allereerste verklaring van de constructor — this() (of super()) moet de eerste regel zijn, en een constructor kan niet zowel this() als super() aanroepen"
            },
            {
              "icon": "",
              "label": "Alleen als de laatste verklaring, nadat alle veldinitialisatie is voltooid"
            },
            {
              "icon": "",
              "label": "Alleen in constructors die expliciet als delegerend zijn gemarkeerd, met behulp van een delegate-sleutelwoord"
            }
          ]
        },
        {
          "question": "U hebt een lijst nodig die elementen aan de voorkant zeer vaak zal hebben ingevogen, en random-access-lezingen zijn zeldzaam. Welke is de betere structurele pasvorm, en waarom?",
          "options": [
            {
              "icon": "",
              "label": "ArrayList — de contigue ondersteunende array maakt elke operatie, inclusief voor-insteking, sneller dan een gekoppelde structuur"
            },
            {
              "icon": "",
              "label": "Ze zijn identiek prestaties, omdat beide de List-interface implementeren met dezelfde tijdgaranties"
            },
            {
              "icon": "",
              "label": "LinkedList — het invoegen aan de voorkant is O(1) omdat het alleen een knooppunt opnieuw koppelt, terwijl ArrayList elk bestaand element moet verschuiven, waardoor voorkant-insteking O(n) wordt"
            },
            {
              "icon": "",
              "label": "LinkedList, omdat het random-toegang in O(1) ondersteunt terwijl ArrayList niet"
            }
          ]
        },
        {
          "question": "U voegt vijf vermeldingen in een eenvoudige HashMap in een specifieke volgorde in en itereert er vervolgens met een for-each-lus overheen. In welke volgorde komen de vermeldingen eruit?",
          "options": [
            {
              "icon": "",
              "label": "Dezelfde volgorde als de vermeldingen zijn ingevouwd, omdat Java-maps altijd insteekingsvolorder behouden"
            },
            {
              "icon": "",
              "label": "Geen gegarandeerde volgorde — HashMap's herhalingsvolgorde hangt af van hashing-bucket-plaatsing, niet insteekingsvolgorde, en kan zelfs tussen runs veranderen; LinkedHashMap is wat insteekingsvolgorde behoudt"
            },
            {
              "icon": "",
              "label": "Automatisch gesorteerd op sleutel, op dezelfde manier als een TreeMap"
            },
            {
              "icon": "",
              "label": "Omgekeerde volgorde van insteking, omdat HashMap intern een stapel gebruikt"
            }
          ]
        },
        {
          "question": "Hoeveel null-sleutels kunnen een eenvoudige java.util.HashMap tegelijk bevatten, en hoeveel null-waarden?",
          "options": [
            {
              "icon": "",
              "label": "Geen null-sleutels en geen null-waarden zijn ooit toegestaan in enige Map-implementatie"
            },
            {
              "icon": "",
              "label": "Één null-sleutel op zijn hoogst, en onbeperkt veel null-waarden — HashMap staat één null-sleutel toe, in tegenstelling tot Hashtable die geen van beide toestaat"
            },
            {
              "icon": "",
              "label": "Onbeperkt veel null-sleutels en onbeperkt veel null-waarden, omdat null als elke andere sleutel wordt behandeld"
            },
            {
              "icon": "",
              "label": "Één null-sleutel en één null-waarde maximum, beiden op één beperkt"
            }
          ]
        },
        {
          "question": "U itereert een List met een for-each-lus en roept list.remove(item) rechtstreeks op de lijst aan van binnen de lusbody, op enkele maar niet alle elementen. Wat gebeurt er?",
          "options": [
            {
              "icon": "",
              "label": "Het werkt correct en verwijdert exact de bedoelde elementen"
            },
            {
              "icon": "",
              "label": "Het slaat stilletjes het element na het verwijderde over, maar voltooit anderszins zonder fout"
            },
            {
              "icon": "",
              "label": "Het gooit IndexOutOfBoundsException zodra de lus de oude grootte van de lijst bereikt"
            },
            {
              "icon": "",
              "label": "Het gooit ConcurrentModificationException — het wijzigen van de structuur van de lijst rechtstreeks terwijl een impliciete iterator erdoorheen loopt wordt gedetecteerd en afgewezen; Iterator.remove() moet in plaats daarvan worden gebruikt"
            }
          ]
        },
        {
          "question": "Bij runtime, gegeven een List<String> list, wat kunt u werkelijk via reflectie of instanceof bepalen over zijn generieke type-parameter?",
          "options": [
            {
              "icon": "",
              "label": "U kunt list.getElementType() aanroepen om String.class bij runtime op te halen"
            },
            {
              "icon": "",
              "label": "instanceof List<String> compileert en controleert correct het elementtype"
            },
            {
              "icon": "",
              "label": "De JVM slaat de type-parameter op als verborgen metadata toegankelijk via list.getGenericType()"
            },
            {
              "icon": "",
              "label": "Niets — generieke type-informatie wordt op compilatietijd gewist, dus het object is bij runtime gewoon een List, en er is geen manier om te herstellen of het List<String> of List<Integer> was"
            }
          ]
        },
        {
          "question": "switch (day) { case 1: case 2: print(\"Weekday\"); break; case 6: print(\"Saturday\"); case 7: print(\"Sunday\"); break; default: print(\"?\"); } Indien day 6 is, wat wordt er afgedrukt?",
          "options": [
            {
              "icon": "",
              "label": "Alleen Saturday — elk case-blok in een switch stopt altijd na zijn eigen print-verklaring"
            },
            {
              "icon": "",
              "label": "Alleen Sunday — overeenstemming begint bij case 6, maar alleen het laatste overeenkomende label vóór break voert uit"
            },
            {
              "icon": "",
              "label": "Niets drukt af, omdat day 6 geen overeenkomende case heeft met zijn eigen break er direct onder"
            },
            {
              "icon": "",
              "label": "Saturday dan Sunday — case 6 heeft geen break, dus uitvoering valt door naar de code van de volgende case voordat de volgende break wordt bereikt"
            }
          ]
        },
        {
          "question": "Een Product-klasse heeft één duidelijk \"natuurlijke\" sorteervolgorde op prijs nodig, plus de mogelijkheid om ook op naam of voorraadhoeveelheid op verschillende plaatsen in de codebase te sorteren. Welke combinatie is het juiste ontwerp?",
          "options": [
            {
              "icon": "",
              "label": "Implementeer Comparable<Product> drie afzonderlijke keren, één per bestel, en laat de beller kiezen welk compareTo loopt"
            },
            {
              "icon": "",
              "label": "Implementeer Comparable<Product> voor de natuurlijke prijsordering, en schrijf afzonderlijke Comparator<Product> exemplaren voor de naam- en voorraadhoeveelheid-ordeningen die elders worden gebruikt"
            },
            {
              "icon": "",
              "label": "Gebruik alleen Comparator voor elke bestel, inclusief prijs, omdat Comparable hier geen voordeel heeft"
            },
            {
              "icon": "",
              "label": "Gebruik alleen Comparable door drie overbelaste compareTo-methoden toe te voegen, één per bestel"
            }
          ]
        },
        {
          "question": "Een methode roept new FileReader(path) aan, die declares throws IOException. IOException extends Exception, niet RuntimeException. Wat moet u doen om dit te compileren?",
          "options": [
            {
              "icon": "",
              "label": "Niets — de compiler dwingt dit alleen voor uitzonderingen af die RuntimeException uitbreiden"
            },
            {
              "icon": "",
              "label": "Vang IOException op in een try/catch of declareer throws IOException op uw eigen methode — gecontroleerde uitzonderingen moeten expliciet worden afgehandeld of verspreid, in tegenstelling tot ongecontroleerde RuntimeException-subklassen"
            },
            {
              "icon": "",
              "label": "Wikkel de oproep in een try/catch voor RuntimeException, omdat IOException automatisch ongecontroleerd is in moderne Java"
            },
            {
              "icon": "",
              "label": "Declareer de methode als static — statische methoden zijn vrijgesteld van afhandeling van gecontroleerde uitzonderingen"
            }
          ]
        },
        {
          "question": "try (Resource a = new Resource(\"A\"); Resource b = new Resource(\"B\")) { ... } gebruikt twee middelen die AutoCloseable implementeren. Indien het try-blok normaal afsluit, in welke volgorde worden ze gesloten?",
          "options": [
            {
              "icon": "",
              "label": "A is eerst gesloten, daarna B, overeenkomend met de declaratievolgorde"
            },
            {
              "icon": "",
              "label": "B is eerst gesloten, daarna A — try-with-resources sluit middelen in omgekeerde volgorde van hun declaratie"
            },
            {
              "icon": "",
              "label": "Beide worden gelijktijdig gesloten, omdat try-with-resources de schoonmaak parallelliseert"
            },
            {
              "icon": "",
              "label": "Alleen B wordt automatisch gesloten — A moet nog steeds handmatig in een finally-blok worden gesloten"
            }
          ]
        },
        {
          "question": "int getValue() { try { return 1; } finally { return 2; } } Wat geeft het aanroepen van getValue() terug?",
          "options": [
            {
              "icon": "",
              "label": "1 — de returnwaarde van het try-blok is al vastgelegd voordat finally loopt, dus finally kan dit niet meer wijzigen"
            },
            {
              "icon": "",
              "label": "Het gooit een exception bij runtime, omdat een methode niet vanuit twee plaatsen kan retourneren"
            },
            {
              "icon": "",
              "label": "2 — een return-statement binnen finally overschrijft en vervangt elke return die al vanuit het try-blok in gang is gezet, waardoor waarde 1 volledig wordt verwijderd"
            },
            {
              "icon": "",
              "label": "Het is een compilerfout — finally mag geen return-statement bevatten"
            }
          ]
        },
        {
          "question": "try { ... } catch (IOException e) { ... } catch (FileNotFoundException e) { ... }, en FileNotFoundException extends IOException. Wat gebeurt er wanneer u dit probeert te compileren?",
          "options": [
            {
              "icon": "",
              "label": "Een compilefout — het FileNotFoundException catch-blok is onbereikbaar omdat het eerdere, meer algemene IOException catch-blok al elke FileNotFoundException overeenkomt"
            },
            {
              "icon": "",
              "label": "Het compileert prima, en het meer specifieke FileNotFoundException-blok loopt wanneer dat exacte type wordt gegooid"
            },
            {
              "icon": "",
              "label": "Het compileert prima, en beide catch-blokken lopen in volgorde voor een FileNotFoundException"
            },
            {
              "icon": "",
              "label": "Het is prima op compilatietijd, maar gooit een runtime error de eerste keer dat een FileNotFoundException werkelijk optreedt"
            }
          ]
        },
        {
          "question": "Een klasse heeft een synchronized instance-methode process() en een synchronized static-methode configure(). Wat, specifiek, vergrendelt elk?",
          "options": [
            {
              "icon": "",
              "label": "process() vergrendelt de monitor van het specifieke object-exemplaar waarop het wordt aangeroepen, terwijl configure() de monitor van het Class-object zelf vergrendelt — gedeeld door elk exemplaar"
            },
            {
              "icon": "",
              "label": "Beide vergrendelen dezelfde enkele globale vergrendeling voor de gehele JVM, ongeacht exemplaar of klasse"
            },
            {
              "icon": "",
              "label": "process() vergrendelt het Class-object, en configure() vergrendelt welk exemplaar het ook aanroept"
            },
            {
              "icon": "",
              "label": "Geen van beiden vergrendelt werkelijk iets tenzij een synchronized-blok ook in het method-body wordt gebruikt"
            }
          ]
        },
        {
          "question": "Een veld wordt gedeclareerd als volatile int counter = 0;, en meerdere threads voeren counter++ erop tegelijk uit. Voorkomt volatile verloren updates hier?",
          "options": [
            {
              "icon": "",
              "label": "Ja — volatile maakt elke operatie op het veld atomair, inclusief increments"
            },
            {
              "icon": "",
              "label": "Nee — volatile garandeert alleen dat lezingen de meest recente schrijving in alle threads zien (zichtbaarheid); counter++ is een lees-wijzig-schrijf met meerdere stappen, en volatile doet niets om die stappen atomair te maken"
            },
            {
              "icon": "",
              "label": "Ja, maar alleen voor int- en long-velden specifiek, vanwege hoe de JVM 64-bits waarden handelt"
            },
            {
              "icon": "",
              "label": "Nee, en volatile mislukt ook om zichtbaarheid voor primitieve typen als int te garanderen"
            }
          ]
        },
        {
          "question": "Interface A en interface B declareren elk een default-methode describe(). Een klasse implementeert zowel A als B en overschrijft describe() zelf niet. Wat gebeurt er?",
          "options": [
            {
              "icon": "",
              "label": "De compiler kiest automatisch de versie van interface A, omdat die eerst in het implements-component wordt vermeld"
            },
            {
              "icon": "",
              "label": "Beide versies lopen, na elkaar, wanneer describe() wordt aangeroepen"
            },
            {
              "icon": "",
              "label": "Het is prima op compilatietijd, maar gooit AmbiguousMethodException de eerste keer dat describe() wordt aangeroepen"
            },
            {
              "icon": "",
              "label": "Een compilerfout — wanneer twee interfaces dezelfde default-methode bijdragen, moet de implementerende klasse deze zelf overridden om de ambiguïteit op te lossen, omdat Java niet zal raden welke u bedoelt"
            }
          ]
        },
        {
          "question": "list.stream().filter(x -> x > 0).map(x -> x * 2); wordt geschreven, maar het resultaat wordt nooit aan een terminal-operatie als .collect() of .forEach() toegewezen. Wat gebeurt er werkelijk wanneer deze regel wordt uitgevoerd?",
          "options": [
            {
              "icon": "",
              "label": "Helemaal niets gebeurt met de elementen van de lijst — filter en map zijn luie intermediate-operaties die alleen een pipelinebeschrijving bouwen; zonder een terminal-operatie loopt geen van die pipeline werkelijk"
            },
            {
              "icon": "",
              "label": "Elk element wordt onmiddellijk gefilterd en in kaart gebracht, precies alsof een terminal-operatie zou zijn aangeroepen"
            },
            {
              "icon": "",
              "label": "Alleen filter loopt onmiddellijk; map wordt uitgesteld totdat een terminal-operatie verschijnt"
            },
            {
              "icon": "",
              "label": "Het gooit een IllegalStateException, omdat een stream-pipeline een terminal-operatie vereist om te compileren"
            }
          ]
        }
      ],
      "optionOrderVersion": "0154f9576054ad6f"
    }
  }
}
