Objecten, referenties en gelijkheid in Java

Leer wat een Java-variabele werkelijk bevat: een primitieve waarde of een referentie naar een object. Volg aliassen en null, geef objecten door aan methoden en laat methoden objecten teruggeven. Bouw klassen op uit andere objecten en uit lijsten met objecten. Schrijf daarna equals en hashCode, zodat lijsten, verzamelingen en maps je objecten op inhoud vergelijken, en houd de tekstinterface van een programma gescheiden van de logica.

BeginnerLes 6 van 11 in Cursus Java programmeren: klassen, overerving, collecties en streams

Wat je leert

  1. Leg uit wat een Java-variabele bij primitieve typen en referentietypen bevat, voorspel wat een toewijzing bij elk type doet en werk met variabelen die naar geen object verwijzen.
    • Vergelijk primitieve variabelen en referentievariabelen
      Noem de primitieve typen van Java, leg uit dat een referentievariabele een referentie naar een object bevat en voorspel het effect van het kopiëren van beide soorten variabelen.
    • Volg aliassen, hertoewijzingen en onbereikbare objecten
      Volg meerdere variabelen die naar één object verwijzen, onderscheid het wijzigen van een object van het laten verwijzen van een variabele naar een ander object, en leg uit wat er gebeurt met objecten waarnaar niets verwijst.
    • Werk veilig met de null-referentie
      Leg uit wat null betekent, herken waar null voorkomt, voorkom NullPointerException en bepaal wanneer null een redelijke waarde is.
  2. Leg uit hoe Java argumenten doorgeeft, schrijf methoden die objecten ontvangen en wijzigen of vergelijken met een ander object van dezelfde klasse, en geef nieuwe of bestaande objecten veilig terug.
    • Gebruik objecten als methodeparameters
      Voorspel wat een methode wel en niet kan wijzigen als die een primitieve waarde of een referentie ontvangt, en schrijf methoden die objecten van een andere klasse of van hun eigen klasse ontvangen.
    • Laat methoden objecten teruggeven
      Schrijf methoden die nieuwe objecten maken en teruggeven of referenties naar bestaande objecten teruggeven, en voorkom dat je per ongeluk de interne toestand van een object prijsgeeft.
  3. Ontwerp klassen waarvan de velden andere objecten of lijsten met objecten zijn, werk met lijsten die objecten bevatten en bepaal wanneer opgenomen objecten moeten worden gedeeld of gekopieerd.
    • Stel een klasse samen met andere objecten
      Modelleer relaties van het type “heeft een” met referentievelden, delegeer werk aan de opgenomen objecten en bepaal of een opgenomen object moet worden gedeeld of gekopieerd.
    • Sla objecten op in lijsten en verwerk die
      Voeg objecten toe aan een ArrayList, zoek, wijzig en verwijder ze, en leg uit waarom de lijst referenties bevat in plaats van kopieën.
    • Bouw een klasse rond een lijst met objecten
      Schrijf een klasse die een lijst met objecten als veld bijhoudt, die initialiseert, methoden aanbiedt om met de inhoud te werken en de lijst zelf afschermt.
  4. Onderscheid identiteit van inhoudelijke gelijkheid, schrijf correcte equals- en hashCode-methoden en voorspel hoe lijsten, verzamelingen en maps die gebruiken.
    • Onderscheid identiteit van inhoudelijke gelijkheid
      Leg uit wat == en equals vergelijken bij primitieve waarden, strings, wrappers en je eigen klassen, en voorspel het resultaat van elke vergelijking.
    • Schrijf een equals-methode
      Overschrijf equals met de juiste signatuur en stappen, vergelijk elk soort veld correct en houd de methode in overeenstemming met haar algemene contract.
    • Stem hashCode af op equals
      Leg het hashCode-contract uit, schrijf hashCode voor een klasse die equals overschrijft en voorspel hoe HashSet en HashMap zich met en zonder die methode gedragen.
  5. Deel een tekstprogramma zo in dat één klasse met de gebruiker communiceert en andere klassen de gegevens en regels bevatten. Breid het daarna uit en controleer het zonder die twee te vermengen.
    • Verdeel een programma in een gebruikersinterface en klassen voor de programmalogica
      Bepaal welke code in de gebruikersinterface en welke in de logica thuishoort, en verbind beide via constructorparameters en een startmethode.
    • Breid een programma met gescheiden gebruikersinterface en programmalogica uit en controleer het
      Voeg functies toe door de logica uit te breiden en vanuit de interface aan te roepen, controleer de logica rechtstreeks met bekende waarden en geef elke klasse één taak.

Objecten, referenties en gelijkheid in Java: uitleg

5 hoofdstukken

1

Volg waarden en referenties

Een variabele en een object zijn verschillende onderdelen van een Java-programma. Als je begrijpt wat elke variabele bevat, kun je uitleggen waarom de ene toewijzing onafhankelijke getallen oplevert, terwijl een andere twee namen toegang geeft tot hetzelfde object. Begin met de waarden die worden gekopieerd en volg daarna de wijzigingen instructie voor instructie. Hetzelfde beeld helpt je ontbrekende objecten te vinden, null-fouten te lezen en te bepalen of een wijziging gedeeld moet worden.

1.1 Wat een variabele bevat

Een Java-variabele bevat een waarde. De belangrijke vraag is of die waarde een gewoon gegeven is of een referentie die naar een object leidt. In beide gevallen kopieert een toewijzing die waarde.

Primitieve gegevens en objectreferenties

Java heeft acht primitieve typen: byte, short, int, long, float, double, char en boolean. Een primitieve variabele bevat zijn gegevens rechtstreeks. Een referentievariabele bevat een referentie naar een object of de speciale waarde null. Klassen zoals String, Scanner en ArrayList, je eigen klassen en arrays zijn referentietypen.

DeclaratieWat de variabele bevat
int count = 5;De gehele waarde 5
Counter c = new Counter();Een referentie naar een nieuwe teller
Counter c = null;Geen objectreferentie

In new Counter() maakt Java een object, voert de constructor ervan uit en levert een referentie op. De toewijzing slaat die referentie op in c. Teken de variabele als een doosje met een pijl naar een afzonderlijk object. Elke evaluatie van new maakt een afzonderlijk object, ook als de argumenten overeenkomen.

Kopieer de waarde en volg het resultaat

Bij primitieve waarden blijft de eerste variabele onaangeroerd als je de gekopieerde variabele wijzigt:

int a = 5;
int b = a;
b = b + 1;

De laatste regel leest 5 uit b, telt er 1 bij op en slaat 6 op in b. De waarde in a blijft 5.

Gebruik voor een voorbeeld met referenties deze kleine klasse:

class Counter {
    private int value;

    Counter() { this.value = 0; }
    Counter(int value) { this.value = value; }
    void increase() { value++; }
    int value() { return value; }
}

Volg nu deze instructies:

Counter first = new Counter();
Counter second = first;
second.increase();
System.out.println(first.value()); // 1

Alleen de eerste regel maakt een teller. De tweede regel kopieert de referentie, zodat de methodeaanroep de ene teller wijzigt die beide variabelen bereiken. Om een onafhankelijke teller met dezelfde gegevens te maken, schrijf je new Counter(first.value()). Het kopiëren van een variabele roept geen constructor aan.

Wrappers en onveranderlijke strings

Elk primitief type heeft een wrapperklasse, zoals Integer, Double, Boolean of Character. Wrappervariabelen bevatten referenties of null. Java kan een primitieve waarde en de bijbehorende wrapper automatisch in elkaar omzetten; deze conversies heten autoboxing en unboxing. Lijsten bevatten referenties, dus een lijst met gehele getallen is ArrayList<Integer>.

Ook String is een referentietype, maar de objecten ervan zijn onveranderlijk: hun tekens kunnen na het maken niet veranderen.

String s = "java";
String t = s;
t = t.toUpperCase();

Hier verwijst s nog steeds naar “java” en verwijst t naar “JAVA”. De laatste regel wijst het resultaat toe aan t; de toewijzing wijzigt de gedeelde oorspronkelijke string niet. String-methoden kunnen het oorspronkelijke object hergebruiken als er geen wijziging nodig is.

Het afdrukken van een object gebruikt de methode toString() ervan. Zonder overschrijving heeft het overgeërfde resultaat een vorm zoals Counter@7e9e5f8a: klassenaam, @ en een hexadecimale hashcode. Definieer toString() als de afgedrukte uitvoer de gegevens van het object moet tonen.

Belangrijkste punten

  • Een toewijzing kopieert een waarde: primitieve gegevens of een objectreferentie.
  • Elke evaluatie van new maakt een afzonderlijk object.
  • Als je een referentie kopieert, deel je het object; je kopieert de velden ervan niet.
  • Wrappers en strings zijn referentietypen; strings zijn onveranderlijk.
  • Het afdrukken van een object gebruikt toString().

1.2 Gedeelde objecten en veranderende pijlen

Twee variabelen die naar hetzelfde object verwijzen, zijn aliassen. Een alias biedt een andere route naar bestaande gegevens. Houd bij het volgen van een programma de variabelen gescheiden van de objecten die ze bereiken.

Mutatie wijzigt gegevens; hertoewijzing wijzigt een variabele

Een mutatie wijzigt de toestand van een object, zoals het opgeslagen gewicht. Een hertoewijzing plaatst een andere waarde in een variabele. Bij een referentievariabele verandert daardoor de pijl.

Gebruik een doos waarvan het gewicht in kilogram is:

class Box {
    private double weight;
    Box(double weight) { this.weight = weight; }
    double getWeight() { return weight; }
    void setWeight(double weight) { this.weight = weight; }
}

Volg deze reeks:

Box a = new Box(2);
Box b = a;
b.setWeight(5);
b = new Box(7);
  1. Teken variabele a met een pijl naar object A, met gewicht 2.
  2. Teken b met een pijl naar datzelfde object A. Er is nog steeds één doos.
  3. Verander het gewicht in object A in 5. Beide routes bereiken nu gewicht 5.
  4. Maak object B met gewicht 7 en richt alleen b daarop.

Aan het einde is a.getWeight() gelijk aan 5 en b.getWeight() gelijk aan 7. De vierde instructie zet object A niet terug en laat a ook niet naar object B verwijzen.

HandelingWijziging in het diagramEffect via andere aliassen
b.setWeight(5)Wijzig een veld in het objectZe zien het nieuwe gewicht
b = new Box(7)Richt de pijl vanuit b ergens anders opZe behouden hun bestaande pijlen
b = nullVerwijder de pijl vanuit bZe bereiken hun objecten nog steeds
Bereikbaarheid bepaalt de levensduur van een object

Een object is bereikbaar zolang het draaiende programma een bruikbare keten van referenties ernaartoe kan volgen. Die keten kan via een variabele, een objectveld of een lijstelement lopen. Als een lokale variabele verdwijnt, is dat dus niet genoeg om een object onbereikbaar te maken als een lijst het nog bevat.

Ga verder met a = null;. Object A, dat 5 kg weegt, heeft in dit voorbeeld nu geen bereikbaar pad meer. Het komt in aanmerking voor automatische geheugenopruiming (garbage collection). Java maakt het geheugen van onbereikbare objecten automatisch vrij, maar de garbage collector kiest wanneer; de toewijzing garandeert geen onmiddellijke opruiming. Object B blijft bereikbaar via b.

Je geeft Java-objecten niet handmatig vrij. Als een programma zijn laatste bruikbare referentie naar een object verliest, verliest het ook de mogelijkheid om de gegevens van dat object te bekijken of te wijzigen.

Zoek de verborgen aliassen

Aliassen ontstaan wanneer een object aan een lijst wordt toegevoegd, aan een methode wordt doorgegeven, door een methode wordt teruggegeven of in een veld van een ander object wordt opgeslagen. Elke bewerking kan een referentie doorgeven. Ga voordat je een ontvangen object wijzigt na welke andere code het nog kan bereiken.

Een veelvoorkomende fout begint met de gedachte: “Ik heb de variabele gekopieerd, dus ik heb mijn eigen doos.” Gebruik een nieuw object als wijzigingen onafhankelijk moeten zijn. Om de fout op te sporen, tel je het maken van objecten afzonderlijk van toewijzingen, geef je elk object één keer een naam en verwerk je elke instructie op volgorde. Teken een object niet opnieuw alleen omdat een tweede variabele er nu naar wijst.

Belangrijkste punten

  • Aliassen leiden naar één object en delen dus de mutaties ervan.
  • Een hertoewijzing richt één variabele ergens anders op en laat andere aliassen ongemoeid.
  • Volg velden en referentiepijlen afzonderlijk.
  • Een onbereikbaar object komt in aanmerking voor automatische geheugenopruiming; het tijdstip staat niet vast.

1.3 Geen object: null en veilige controles

De waarde null betekent dat een referentie naar geen object leidt. Dat is nuttig voor een ontbrekend resultaat, maar een programma moet die afwezigheid afhandelen voordat het een object probeert te gebruiken.

Ontbrekend is iets anders dan leeg

String text = ""; verwijst naar een echte string met lengte 0. String text = null; verwijst naar geen string, dus er is geen object waarop je length() kunt aanroepen.

Een primitieve waarde kan geen null bevatten: int x = null; compileert niet. Een wrapper kan die waarde wel bevatten: Integer x = null; compileert. Als je die wrapper met int y = x; omzet naar een primitieve waarde, ontstaat een NullPointerException, omdat voor unboxing een wrapperobject nodig is.

Velden krijgen initiële standaardwaarden wanneer een object wordt gemaakt. Referentievelden beginnen als null, numerieke velden als 0 en booleaanse velden als false, totdat de initialisatie andere waarden toewijst. Lokale variabelen hebben geen standaardwaarde. Als je een lokale variabele leest voordat je er een waarde aan toewijst, ontstaat een compilatiefout, zoals “variable might not have been initialized”.

Zoek de ontbrekende referentie

Als je via null een instantiemethode aanroept, een instantieveld benadert of de lengte van een array leest, ontstaat tijdens de uitvoering een NullPointerException. Bijvoorbeeld:

String name = null;
System.out.println(name.length());

Lees het regelnummer van de exceptie en bepaal welke referentie op die regel ontbrak. Verhelderende meldingen zijn vanaf Java 15 standaard ingeschakeld. Een melding kan luiden: Cannot invoke "SimpleDate.getYear()" because "this.birthday" is null. Lokale namen kunnen verschijnen als plaatsaanduidingen zoals <local4>, afhankelijk van de manier van compileren. Ga terug naar de constructor, toewijzing of zoekactie die het object had moeten leveren.

Het afdrukken van een String-variabele met de waarde null drukt “null” af; concatenatie levert tekst op zoals “Hi null”. Voor deze bewerkingen hoeft geen instantiemethode via die variabele te worden aangeroepen. Ze kunnen ontbrekende gegevens zichtbaar maken voordat een latere methodeaanroep mislukt.

Controleer vóór gebruik

De operator && gebruikt kortsluitevaluatie: die evalueert de rechterkant alleen als de linkerkant waar is.

if (name != null && name.length() > 0) {
    System.out.println(name);
}

Als name gelijk is aan null, is de eerste test onwaar en wordt length() overgeslagen. Als je de twee tests omdraait, vervalt die bescherming: de methodeaanroep zou eerst worden uitgevoerd.

DoelVeilige expressie
Afwezigheid controlerenname == null
Aanwezigheid controlerenname != null
Een vaste opdracht vergelijken"quit".equals(command)

De vaste string is een bekend object, dus equals ervan kan null ontvangen en false teruggeven. command.equals("quit") mislukt als command null is. Evenzo is x.equals(null) geen manier om te controleren of x bestaat: de aanroep vereist al een object.

Maak afwezigheid een duidelijke ontwerpkeuze

Initialiseer een lijstveld met new ArrayList<>(), zodat meteen een lege collectie beschikbaar is. Als een zoekactie null teruggeeft voor “niet gevonden”, documenteer dat resultaat dan en controleer het op de plaats van de aanroep. Vul niet elk veld met willekeurige objecten alleen om een exceptie te voorkomen: lever het object dat het programma nodig heeft, of geef afwezigheid een duidelijke betekenis die de aanroepende code afhandelt.

Belangrijkste punten

  • null betekent geen object; een lege string is een bruikbaar object.
  • Velden krijgen standaardwaarden, maar lokale variabelen moeten vóór het lezen een waarde toegewezen krijgen.
  • Een null-referentie derefereren en een null-wrapper uitpakken mislukken tijdens de uitvoering.
  • Zet de null-controle in && vóór de toegang tot het lid.
  • Roep vergelijkingen met vaste tekst aan op de bekende string.
  • Initialiseer collecties en handel ontbrekende zoekresultaten bewust af.
2

Gebruik objecten bij methodeaanroepen

Met methoden kunnen objecten samenwerken. Een aanroep kan een gedeeld object lezen of wijzigen, een onafhankelijk resultaat maken of een object teruggeven dat al elders wordt bewaard. Deze keuzes beïnvloeden wat de aanroeper na de aanroep ziet. Volg de referenties in parameters en retourwaarden expliciet en plaats vergelijkingen in de klasse die de gegevens beheert. Bepaal daarna welke methoden bestaande toestand moeten delen en welke die met een kopie moeten beschermen.

2.1 Parameters ontvangen gekopieerde waarden

Java geeft elk argument als waarde door. Een parameter is een nieuwe variabele die een kopie van de waarde van het argument ontvangt. Bij een objectargument is de gekopieerde waarde een referentie.

De parameter wijzigen of het object wijzigen?

Een primitieve parameter heeft zijn eigen gegevens:

static void addTen(int n) {
    n = n + 10;
}

Als x gelijk is aan 5, laat de aanroep addTen(x) de waarde van x op 5 staan. Alleen de lokale parameter n wordt 15.

Een referentieparameter kan hetzelfde object bereiken als de aanroeper. Stel dat Person.growOlder() de opgeslagen leeftijd verhoogt:

static void celebrate(Person p) {
    p.growOlder();
}

static void replace(Person p) {
    p = new Person("Bo", 30);
}

Tijdens celebrate(ana) zijn de parameter p en de variabele ana aliassen. De gewijzigde leeftijd blijft daarna zichtbaar. Tijdens replace(ana) richt de toewijzing aan p alleen die parameter ergens anders op. De variabele ana van de aanroeper blijft naar de oorspronkelijke persoon verwijzen. Een methode die twee referentieparameters verwisselt, heeft dezelfde beperking: de methode verwisselt de eigen kopieën, niet de variabelen van de aanroeper.

Binnen een methodeDe aanroeper ziet
Een waarde toewijzen aan een primitieve parameterGeen wijziging van de variabele van de aanroeper
Het object van de parameter wijzigenGewijzigde toestand van het gedeelde object
Een referentieparameter opnieuw toewijzenGeen wijziging van de variabele van de aanroeper
Een object van een andere klasse ontvangen

Een toegangspoort kan de lengte van een persoon lezen en zelf het aantal controles bijhouden. Deze klasse verwacht een persoon die niet null is en een methode getHeight() heeft die centimeters teruggeeft:

class HeightGate {
    private int minHeight;
    private int checks;

    HeightGate(int minHeight) {
        this.minHeight = minHeight;
    }

    boolean allowIn(Person p) {
        checks++;
        return p.getHeight() >= minHeight;
    }

    int checks() { return checks; }
}

Bij een poort die is ingesteld op 120 cm geven controles van personen van 132 cm en 115 cm respectievelijk true en false terug. De poort meldt daarna 2 controles. De poort leest de lengte van de persoon via een publieke methode en wijzigt de eigen teller.

Een ander object van dezelfde klasse ontvangen

Binnen SimpleDate.before(SimpleDate other) is this de datum waarop de methode wordt aangeroepen en is other het argument. Een klasse kan de private velden van elk object van dezelfde klasse lezen.

Vergelijk bij de velden day, month en year in deze volgorde:

boolean before(SimpleDate other) {
    if (year != other.year) {
        return year < other.year;
    }
    if (month != other.month) {
        return month < other.month;
    }
    return day < other.day;
}

Bij 14 maart 2001 en 2 november 2001 zijn de jaren gelijk en is maand 3 kleiner dan maand 11, dus de eerste datum is eerder. Gelijke datums geven false terug, omdat geen van beide vóór de andere ligt.

Person.olderThan(other) kan het werk delegeren aan birthday.before(other.birthday). De datumvergelijking hoort bij de datumgegevens. Methoden die instantiemethoden op een parameter aanroepen, hebben een argument nodig dat niet null is; controleer dat of vermeld die eis in de documentatie voor het gebruik van de methode.

Belangrijkste punten

  • Elke parameter ontvangt een gekopieerde waarde, ook een referentieparameter.
  • Een mutatie kan een gedeeld object beïnvloeden; hertoewijzing van een parameter kan de variabele van de aanroeper niet vervangen.
  • this duidt de ontvanger aan; een objectparameter duidt het argument aan.
  • Een klasse kan private velden van een ander object van dezelfde klasse benaderen.
  • Delegeer vergelijkingen aan de klasse die de relevante gegevens beheert.

2.2 Een nieuw of een bestaand object teruggeven

Een methode die een object teruggeeft, geeft een referentie terug. Het type vertelt je welk soort object kan worden teruggegeven, maar niet of het object nieuw, gedeeld of afwezig is. Het doel van de methode moet die keuze duidelijk maken.

Maak een resultaat

Een fabrieksmethode levert een object. De methode kan er een maken of een bestaand object hergebruiken. Hier maakt elke aanroep bewust een afzonderlijk ticket:

class TicketMachine {
    private int nextNumber = 1;

    Ticket issue() {
        Ticket t = new Ticket(nextNumber);
        nextNumber++;
        return t;
    }
}

De automaat verwacht een Ticket-constructor die het nummer opslaat. Drie aanroepen maken drie tickets met de nummers 1, 2 en 3. Een lokale variabele teruggeven is veilig: de referentie wordt gekopieerd naar de aanroeper, die het object bereikbaar kan houden nadat de lokale variabele buiten bereik is geraakt.

Een kopieermethode belooft een onafhankelijk object. In de klasse Counter:

Counter copy() {
    return new Counter(this.value);
}

Na Counter a = new Counter(4); Counter b = a.copy(); b.increase(); is a.value() gelijk aan 4 en b.value() gelijk aan 5. Als je a.copy() vervangt door a, wordt de teller gedeeld.

Een methode kan ook een berekend object maken. Voor een Point met double-velden x en y:

Point midpoint(Point other) {
    return new Point((x + other.x) / 2.0,
                     (y + other.y) / 2.0);
}

De punten (2, 4) en (6, 10) geven (4,0; 7,0). De methode berekent elke coördinaat en geeft beide door aan een nieuw punt; geen van beide oorspronkelijke punten verandert.

Geef een al opgeslagen object terug

De methode findByName van een register moet de overeenkomende opgeslagen persoon teruggeven als aanroepers met dat item moeten werken. Als je new Person(name, 0) maakt, geef je verzonnen vervangende gegevens terug en hebben latere wijzigingen geen invloed op het register.

Een zoekactie kan null teruggeven als er geen overeenkomst is:

Person p = register.findByName("Lin");
if (p != null) {
    System.out.println(p);
} else {
    System.out.println("Not found");
}

Als p de opgeslagen persoon is, wijzigen veranderingen via p ook de persoon die via het register wordt bereikt. Een return-instructie kopieert het object niet.

Houd veranderlijke interne toestand onder controle

Stel dat Person.getBirthday() het veld SimpleDate teruggeeft en dat die datum setters heeft. Een aanroeper kan dan de geboortedatum van de persoon wijzigen zonder een methode van Person aan te roepen. Dit is het prijsgeven van veranderlijke toestand.

Om het veld te beschermen, geef je een nieuwe datum terug die is opgebouwd uit de waarden ervan:

SimpleDate getBirthday() {
    return new SimpleDate(birthday.getDay(),
                          birthday.getMonth(),
                          birthday.getYear());
}

Deze versie vereist dat een geboortedatumobject is geïnitialiseerd. Andere ontwerpen gebruiken een onveranderlijke datum of bieden specifieke zoekmethoden zonder een datumgetter. Kies het gedrag voor elke methode: find deelt normaal gesproken een opgeslagen item, terwijl copy onafhankelijkheid creëert. De korte naam van een getter alleen garandeert geen bescherming.

Belangrijkste punten

  • Als een methode een object teruggeeft, geeft ze een referentie door aan de aanroeper.
  • Fabrieksmethoden kunnen objecten maken of hergebruiken; een kopieermethode maakt een afzonderlijk object.
  • Zoekmethoden kunnen bestaande items delen en moeten een ontbrekend resultaat definiëren.
  • Als een methode een veranderlijk veld teruggeeft, kunnen aanroepers de methoden van het beherende object omzeilen.
3

Verbind objecten en lijsten

Een groter model is een netwerk van objecten: een persoon heeft een datum, een afspeellijst heeft een lijst en de lijst bevat nummers. Elke verbinding is een referentie met gevolgen voor delen en wijzigen. Bouw die verbindingen bewust op, delegeer werk aan het object dat ermee kan omgaan en scherm collectieregels af met bruikbare methoden. Een gekopieerde houder kan beschermen welke elementen erin staan, terwijl alle objecten erin gedeeld blijven.

3.1 Objecten met referentievelden

Compositie bouwt een klasse op met velden waarvan de typen andere klassen zijn. Daarmee druk je een relatie van het type “heeft een” uit: een persoon heeft een geboortedatum; een cursus heeft een docent. Het buitenste object slaat een referentie naar het opgenomen object op.

Maak en verbind de objecten

Een constructor kan een bestaande datum ontvangen:

class Person {
    private String name;
    private SimpleDate birthday;

    Person(String name, SimpleDate birthday) {
        this.name = name;
        this.birthday = birthday;
    }

    boolean olderThan(Person other) {
        return birthday.before(other.birthday);
    }

    public String toString() {
        return name + ", born " + birthday;
    }
}

De aanroep new Person("Ana", new SimpleDate(14, 3, 2001)) maakt eerst een datum en geeft de referentie daarna aan de persoon. Een constructor voor een persoon kan ook dag, maand en jaar ontvangen en de datum binnen zijn body maken. Bij die aanpak behoudt de aanroeper geen referentie naar de nieuw gemaakte datum.

Delegeer werk aan het opgenomen object

De aanroep birthday.before(other.birthday) vraagt het datumobject om datums te vergelijken. Person hoeft de vergelijkingen van jaar, maand en dag niet te herhalen. Concatenatie in toString() delegeert ook: Java roept toString() van het geboortedatumobject aan om de tekst ervan te verkrijgen.

Een datumobject kan helpen de leeftijd te berekenen. Begin met het huidige jaar min het geboortejaar. Stel daarna de verjaardag van dit jaar samen en trek er één af als de huidige datum daarvoor ligt. Een persoon die op 14 maart 2001 is geboren, is op 1 januari 2026 24: het verschil tussen de jaren is 25, maar de verjaardag is nog niet geweest. Op 14 maart 2026 is de verjaardag geweest en is de leeftijd 25. Door de datumvergelijking in SimpleDate te houden, hoef je de regels ervan niet in elke methode voor personen te herhalen.

Bepaal of het binnenste object wordt gedeeld

De constructor hierboven slaat het argument rechtstreeks op. Als twee personen één veranderlijke SimpleDate ontvangen, wijzen beide geboortedatumvelden naar dezelfde datum. Een wijziging van die datum heeft invloed op beide personen.

Keuze in de constructorGevolg voor de referentie
De ontvangen datumreferentie opslaanAanroeper en persoon kunnen de datum delen
Een nieuwe datum uit de ontvangen waarden makenDe persoon begint met een eigen datum
Een onveranderlijke datum opslaanDelen kan de datumwaarden niet wijzigen

Een defensieve constructor kan de dag, maand en het jaar van de ontvangen datum kopiëren. Een onveranderlijke datum heeft geen methoden die de toestand wijzigen en houdt de waarden vast; eenvoudige primitieve velden kunnen final zijn. Kopiëren bij invoer is ook belangrijk als een getter later een kopie teruggeeft, omdat de oorspronkelijke aanroeper het ontvangen object nog kan hebben.

Handel optionele verbindingen af

Een Course heeft mogelijk nog geen docent. Als dat een betekenisvolle toestand is, kan teacherName() het veld controleren en “nog geen docent” teruggeven. Vereis anders een docent bij het maken van het object.

In course.getTeacher().getName() heeft elke instantieaanroep een object nodig: een cursus die null is, mislukt bij getTeacher() en een docent die null is, mislukt bij getName(). Een methode op de cursus kan die controle op afwezigheid op één plek houden, zodat niet elke aanroeper dezelfde keten hoeft te kennen.

Belangrijkste punten

  • Compositie verbindt een buitenste object via velden met andere objecten.
  • Delegeer bewerkingen aan het object dat de relevante gegevens beheert.
  • Als je een ontvangen veranderlijke referentie opslaat, kun je toestand delen met de aanroeper.
  • Kopieën of onveranderlijke binnenste objecten kunnen onafhankelijke toestand beschermen.
  • Optionele referentievelden hebben een duidelijke aanpak voor null nodig.

3.2 Lijsten bevatten referenties naar hun elementen

Een ArrayList<Person> is een object waarvan de elementen referenties naar personen bevatten. De lijst beheert de volgorde van de items; elke persoon beheert zijn eigen velden. Dit zijn verschillende soorten wijzigingen.

Voeg een object toe en bereik het opnieuw
ArrayList<Person> people = new ArrayList<>();
people.add(ana);
people.get(0).growOlder();

Deze code heeft import java.util.ArrayList; nodig en een bestaande persoon ana die niet null is. add kopieert de referentie. De aanroep growOlder() bereikt dezelfde persoon als ana, dus de leeftijdswijziging is via beide routes zichtbaar. De lijst maakt hier geen tweede persoon.

Je kunt een object maken terwijl je het toevoegt: people.add(new Person("Lin", 1998));, met een constructor voor een persoon die een naam en geboortejaar accepteert. De lijst biedt het programma dan een route naar die nieuwe persoon via get of een lus. Typeargumenten van lijsten moeten referentietypen zijn, dus gebruik ArrayList<Integer> voor gehele getallen.

Bereken met objecten en selecteer een object

Stel dat een lijst pakketten bevat met gewichten van 2,5 kg; 4,0 kg en 1,5 kg. Een lus telt het gewicht van elk pakket op:

double total = 0;
for (Parcel p : parcels) {
    total += p.getWeight();
}

De lopende totalen zijn 2,5; 6,5 en 8,0 kg. Voor het selecteren van het zwaarste pakket zijn zowel een referentie als een vergelijking nodig:

Parcel heaviest = parcels.get(0);
for (Parcel p : parcels) {
    if (p.getWeight() > heaviest.getWeight()) {
        heaviest = p;
    }
}

Deze selectie verwacht een niet-lege lijst. Het resultaat is het pakketobject van 4,0 kg, zodat de aanroeper ook de andere methoden ervan kan gebruiken. Bepaal bij een lege lijst wat het ontbrekende resultaat is voordat je element 0 leest.

Doorzoek elk item

Een zoekactie op naam in een klasse die people opslaat, kan het volgende gebruiken:

Person findByName(String name) {
    for (Person p : people) {
        if (p.getName().equals(name)) {
            return p;
        }
    }
    return null;
}

Deze versie verwacht personen die niet null zijn en namen hebben die niet null zijn. De eerste overeenkomst wordt direct teruggegeven. De laatste return null hoort na de lus: een mislukte vergelijking bij de eerste persoon sluit latere overeenkomsten niet uit.

contains, indexOf en remove(Object) zoeken met equals. Als Person gelijkheid op basis van identiteit erft, komt een nieuw gemaakte persoon niet overeen met een bestaande persoon alleen omdat de velden overeenkomen.

Verwijder een item zonder een object te vernietigen

Eén object kan twee keer in één lijst of in meerdere lijsten voorkomen. Als je een item verwijdert, vernietig je het object niet en verwijder je ook geen andere referenties ernaar.

Verwijder tijdens een for-each-lus geen items rechtstreeks uit dezelfde lijst. Dat kan een ConcurrentModificationException veroorzaken. Verzamel eerst de te verwijderen items en verwijder ze daarna:

ArrayList<Person> toRemove = new ArrayList<>();
for (Person p : people) {
    if (p.getName().equals("Lin")) {
        toRemove.add(p);
    }
}
people.removeAll(toRemove);

De lus is klaar voordat de lijstinhoud verandert. removeAll gebruikt gelijkheid en verwijdert dus items die gelijk zijn aan de verzamelde items.

Belangrijkste punten

  • Als je een object aan een lijst toevoegt, deel je een objectreferentie.
  • Een lus kan een totaal berekenen of een referentie naar een geselecteerd object bewaren.
  • Een mislukte zoekactie geeft pas een resultaat terug nadat alle kandidaten zijn gecontroleerd.
  • Lijstbewerkingen die lidmaatschap bepalen, zijn afhankelijk van equals.
  • Als je een item verwijdert, blijven andere referenties intact.
  • Verzamel eerst wat je wilt verwijderen en wijzig de lijst nadat je die hebt doorlopen.

3.3 Een klasse die een lijst beheert

Een klasse kan een lijst in een privaat veld bewaren en bewerkingen aanbieden die het doel ervan beschrijven. Een afspeellijst slaat nummers op, telt ze en berekent de duur. Aanroepers zouden de lijst niet rechtstreeks hoeven te beheren.

Initialiseer de collectie en scherm die af

Deze afspeellijst verwacht Song-objecten die niet null zijn en een methode seconds() hebben:

import java.util.ArrayList;

class Playlist {
    private String name;
    private ArrayList<Song> songs;

    Playlist(String name) {
        this.name = name;
        this.songs = new ArrayList<>();
    }

    void add(Song song) { songs.add(song); }
    int count() { return songs.size(); }

    int totalSeconds() {
        int total = 0;
        for (Song song : songs) {
            total += song.seconds();
        }
        return total;
    }
}

Zonder new ArrayList<>() blijft het referentieveld null; de eerste aanroep van add mislukt dan. Een lege lijst is al een bruikbaar object, met grootte 0.

Voor nummers van 210 s, 185 s en 247 s geeft count() 3 terug. Het totaal groeit van 210 naar 395 naar 642 s. Geheeltallige deling door 60 geeft 10 minuten, met rest 42 seconden. Een methode longest() kan het nummer van 247 s selecteren en teruggeven, met hetzelfde patroon voor het selecteren van een referentie als bij het zwaarste pakket. Definieer een ontbrekend resultaat voor een lege afspeellijst.

Bescherm de lijstinhoud met methoden

Als je songs private houdt, blokkeer je directe toegang tot het veld, maar return songs; geeft juist die lijst prijs. Een aanroeper zou clear() kunnen aanroepen en elke regel van de afspeellijst kunnen omzeilen.

Specifieke bewerkingen zoals add, count en totalSeconds houden de verantwoordelijkheid bij de afspeellijst. De methode add kan een maximumgrootte handhaven of duplicaten weigeren met songs.contains(song). Voor controles op duplicaten op basis van inhoud moet equals van de nummerklasse bepalen welke velden twee nummers gelijk maken.

Als een constructor een beginlijst ontvangt, kopieer je de structuur met this.songs = new ArrayList<>(initialSongs);. Als je de parameter rechtstreeks zou opslaan, zou de aanroeper een alias naar de interne lijst van de afspeellijst kunnen behouden.

Een nieuwe lijst kan nog steeds nummers delen

Een zoekmethode kan new ArrayList<>(songs) teruggeven. Dit is een ondiepe kopie: een nieuw lijstobject dat de bestaande referenties naar nummers bevat.

Handeling van de aanroeper op de teruggegeven kopieEffect op de afspeellijst
Een item verwijderenDe inhoud van de afspeellijst blijft gelijk
De kopie leegmakenDe inhoud van de afspeellijst blijft gelijk
Een gedeeld nummer wijzigenDe afspeellijst ziet de gewijzigde toestand van dat nummer

Een zoekmethode songsLongerThan(200) maakt een nieuwe resultaatlijst, doorloopt de opgeslagen nummers en voegt de nummers toe waarvan de duur langer is dan 200 s. Voor de drie nummers hierboven geeft de methode de nummers van 210 s en 247 s terug. Als ook de waarden van de nummers onafhankelijk moeten zijn, kopieer dan de veranderlijke nummers of maak Song onveranderlijk.

Belangrijkste punten

  • Een beherend object, de lijst en de elementen daarvan zijn afzonderlijke objecten.
  • Initialiseer de lijst voordat je die gebruikt.
  • Bied bewerkingen aan die de collectieregels van het beherende object handhaven.
  • Kopieer ontvangen lijsten om een externe alias naar de interne lijstinhoud te voorkomen.
  • Een ondiepe kopie van een lijst deelt de objecten die de elementen vormen.
4

Bepaal wat objecten gelijk maakt

Twee objecten kunnen hetzelfde beschrijven en toch afzonderlijke objecten zijn. Java biedt verschillende tests voor identiteit en inhoud, en collecties zijn afhankelijk van de keuze die je klasse maakt. Kies eerst de velden die gelijkheid bepalen en implementeer daarna equals en hashCode als paar. Hun contracten verklaren waarom een lijst een gelijk object kan vinden, waarom een hashbotsing is toegestaan en waarom het wijzigen van een opgeslagen sleutel een zoekactie kan verstoren.

4.1 Identiteit en overeenkomende inhoud

Identiteit gaat over de vraag of twee referenties naar hetzelfde object leiden. Inhoudelijke gelijkheid gaat over de vraag of objecten dezelfde relevante gegevens hebben, volgens de regels van de klasse. Twee afzonderlijke boekobjecten kunnen dezelfde titel en hetzelfde publicatiejaar beschrijven zonder identiek te zijn.

Kies de juiste vergelijking
Vergeleken waardenBetekenis van ==Betekenis van equals
Primitieve waardenVergelijkt primitieve waardenGeen instantiemethode op een primitieve waarde
ReferentiesHetzelfde object, of beide nullDe gelijkheidsregel van de klasse
StringsHetzelfde stringobjectOvereenkomende tekeninhoud
Klasse die Object.equals erftHetzelfde objectHetzelfde object

Elke klasse erft equals(Object) van Object. Die standaardversie controleert identiteit. Java onderzoekt niet automatisch alle velden van een nieuwe klasse.

String a = "java";
String b = new String("java");
System.out.println(a == b);      // false
System.out.println(a.equals(b)); // true

new String maakt een afzonderlijk object, terwijl String.equals de tekens vergelijkt. Tekstvergelijking is hoofdlettergevoelig: "Java".equals("java") is false. equalsIgnoreCase vergelijkt zonder onderscheid tussen hoofdletters en kleine letters wanneer dat de bedoelde regel is.

Waarom identiteit soms inhoud lijkt te vergelijken

Gelijke stringliterals delen een stringobject, dus "java" == "java" is true. Daardoor kan een fout in een kleine test verborgen blijven. Tekst uit invoer of tekst die tijdens de uitvoering van een programma wordt opgebouwd, hoeft dat object niet te delen. Gebruik equals voor de inhoud, ongeacht hoe de tekst is verkregen.

Bij wrappervergelijkingen bestaat een vergelijkbare valkuil:

Integer a = 127, b = 127;
Integer c = 1000, d = 1000;
System.out.println(a == b);      // true
System.out.println(c.equals(d)); // true

Autoboxing hergebruikt Integer-objecten voor waarden van -128 tot en met 127. Of grotere waarden een object delen, is geen overdraagbare regel voor getalvergelijking. Bij een standaardconfiguratie zijn de twee wrappers voor 1000 afzonderlijk, maar een grotere cache is toegestaan. Gebruik equals van de wrapper voor overeenkomende wrapperwaarden of vergelijk primitieve waarden. Als één kant van == een int is, pakt Java de wrapper uit en vergelijkt het de numerieke waarden; het uitpakken van een null-wrapper mislukt.

Bepaal welke velden gelijkheid bepalen

Voor een boekencatalogus kunnen titel en publicatiejaar de gelijkheid bepalen, terwijl de huidige lener wordt genegeerd. Die gekozen velden moeten de gelijkheidsmethode bepalen. Een ander programma kan in plaats daarvan een unieke identificator kiezen; welke regel bruikbaar is, hangt af van wat de objecten voorstellen.

Een ArrayList gebruikt equals in contains, indexOf en remove(Object). Stel dat Book alleen velden opslaat en equals erft. Als je één new Book("Dune", 1965) toevoegt, wordt contains(new Book("Dune", 1965)) niet true: de tweede expressie maakt een ander object. Door gelijkheid op titel en jaar te baseren, kan een zoekactie die gelijke inhoud vinden.

HashSet en HashMap gebruiken ook hashCode om de mogelijke objecten te beperken voordat ze gelijkheid controleren. Inhoudelijke gelijkheid definiëren vereist daarom ook een passende regel voor hashcodes.

Belangrijkste punten

  • Bij referenties test == identiteit, terwijl equals de regel van de klasse volgt.
  • Object.equals vergelijkt identiteit totdat de methode wordt overschreven.
  • Gebruik String.equals voor de inhoud van tekst.
  • Gedeelde literals en de wrappercache kunnen identiteitstests misleidend maken.
  • Kies velden voor gelijkheidsbepaling bewust; collecties zijn afhankelijk van die keuze.

4.2 Equals correct implementeren

Een gelijkheidsmethode moet de overgeërfde methode equals(Object) vervangen en de velden vergelijken die de betekenis van je object bepalen. De juiste signatuur is net zo belangrijk als de vergelijkingen binnen de methode.

Een volledig patroon voor gelijkheid

Deze klasse gebruikt titel en jaar, staat een titel met de waarde null toe en houdt de velden voor gelijkheidsbepaling vast:

import java.util.Objects;

final class Book {
    private final String title;
    private final int year;

    Book(String title, int year) {
        this.title = title;
        this.year = year;
    }

    @Override
    public boolean equals(Object other) {
        if (this == other) {
            return true;
        }
        if (!(other instanceof Book)) {
            return false;
        }
        Book book = (Book) other;
        return year == book.year
            && Objects.equals(title, book.title);
    }

    @Override
    public int hashCode() {
        return Objects.hash(title, year);
    }
}

Lees equals in vier stappen. De identiteitscontrole accepteert hetzelfde object direct. De typecontrole weigert alles wat geen boek is, inclusief null. De cast levert een Book-variabele op waarmee de velden kunnen worden gelezen. De laatste expressie controleert beide velden voor gelijkheidsbepaling.

Het parametertype is Object, niet Book. Als je equals(Book other) schrijft, overlaad je de naam met een andere methode en laat je de overgeërfde equals(Object) ongewijzigd. Een directe aanroep kan dan lijken te werken, terwijl ArrayList.contains nog steeds identiteit gebruikt. Met @Override laat je de compiler die fout in de signatuur ontdekken.

Vanaf Java 16 kun je de typecontrole en cast combineren: if (!(other instanceof Book book)) { return false; }. Nadat de controle is geslaagd, is book beschikbaar om velden te vergelijken.

Stem vergelijkingen af op veldtypen

Gebruik == voor velden met gehele getallen, tekens en booleaanse waarden. Gebruik equals van een object voor de inhoud. Voor een objectveld dat null kan zijn, geeft Objects.equals(a, b) true terug als beide null zijn, false als slechts één null is en gebruikt de methode anders gelijkheid.

Voor double-velden zorgt Double.compare(x, other.x) == 0 voor een consistente behandeling van speciale waarden: twee NaN-waarden worden als gelijk beschouwd en positieve en negatieve nul worden onderscheiden. Combineer die keuze met een bijpassende hashcoderegel. Een directe vergelijking van zwevendekommagetallen met == behandelt NaN anders.

Controleer het gelijkheidscontract

Voor referenties die niet null zijn, moet gelijkheid zijn:

  • Reflexief: x.equals(x) is true.
  • Symmetrisch: x.equals(y) en y.equals(x) komen overeen.
  • Transitief: als x gelijk is aan y en y gelijk is aan z, is x gelijk aan z.
  • Consistent: herhaalde aanroepen komen overeen zolang de vergeleken gegevens ongewijzigd blijven.
  • False voor null: x.equals(null) is false.

Twee boeken voor “Dune”, 1965 zijn bij vergelijking gelijk; als je één jaar verandert in 1984, zijn ze ongelijk. Een boek mag bij vergelijking niet gelijk zijn aan een string met de titel: de string zou het boek weigeren, waardoor de symmetrie wordt verbroken.

Sommige implementaties gebruiken other == null || getClass() != other.getClass() voor de typecontrole. Die controle weigert verschillende werkelijke klassen. instanceof kan subklassen accepteren; voor de final-klasse hierboven bestaat geen subklasse en komen de twee controles overeen.

Belangrijkste punten

  • Overschrijf met public boolean equals(Object other) en @Override.
  • Controleer identiteit en type, zet de referentie om en vergelijk de velden voor gelijkheidsbepaling.
  • Gebruik vergelijkingen die veilig zijn bij null wanneer velden kunnen ontbreken.
  • Houd je aan reflexiviteit, symmetrie, transitiviteit, consistentie en afwijzing van null.
  • equals(Book) overlaadt de methode en vervangt equals(Object) niet.
  • De keuze van de typecontrole is belangrijk als subklassen zijn toegestaan.

4.3 Passende hashcodes en stabiele sleutels

Een hashcode is een geheel getal dat wordt gebruikt om een zoekactie te beperken. HashSet en HashMap gebruiken de hashcode om mogelijke objecten te vinden en gebruiken daarna equals om een overeenkomst te bevestigen. Een hashcode is geen unieke objectidentificator.

Het contract geldt in één richting

Gelijke objecten moeten dezelfde hashcode teruggeven. Ongelijke objecten mogen ook een code delen; dat is een hashbotsing. Een botsing maakt de objecten niet gelijk, omdat gelijkheid nog steeds wordt gecontroleerd.

Een hashcode moet consistent blijven zolang de informatie die equals gebruikt ongewijzigd blijft. Bij een correcte implementatie sluiten verschillende hashcodes gelijkheid uit, maar bewijzen gelijke hashcodes geen gelijkheid.

Als een klasse equals overschrijft maar Object.hashCode erft, krijgen afzonderlijke gelijke objecten vaak verschillende codes. Een verzameling kan dan beide bewaren en een zoekactie in een map met een nieuwe gelijke sleutel kan niets vinden. De precieze fout is niet gegarandeerd, omdat verschillende objecten dezelfde overgeërfde code kunnen krijgen. Herstel het contract in plaats van te vertrouwen op een specifieke mislukte zoekactie.

Bereken de hashcode uit de velden voor gelijkheidsbepaling

Voor de boekklasse die titel en jaar vergelijkt:

@Override
public int hashCode() {
    return Objects.hash(title, year);
}

Importeer java.util.Objects. Dezelfde veldwaarden leveren nu hetzelfde resultaat op. Een verzameling boeken bevat één item nadat twee gelijke boeken zijn toegevoegd en een map kan een waarde ophalen met een nieuwe gelijke boeksleutel.

Objects.hash combineert de hashcodes van de velden met 31. Voor “Dune” is de stringhashcode 2.141.736. De combinatie van die titel en het jaar 1965 geeft 31 vermenigvuldigd met de som van 31 en 2.141.736, plus 1965: het resultaat is 66.396.742.

Ook bij het berekenen van stringhashcodes wordt 31 gebruikt. Voor “ab” geven de tekencodes 97 en 98: 97 vermenigvuldigd met 31 plus 98, oftewel 3105. Langere berekeningen kunnen het bereik van int overschrijden en overlopen, dus een negatieve hashcode is normaal.

HashcoderegelGevolg voor het contract
Dezelfde velden voor gelijkheidsbepaling gebruikenGelijke objecten krijgen gelijke codes
Een veld toevoegen dat gelijkheid negeertGelijke objecten kunnen verschillende codes krijgen
Minder velden voor gelijkheidsbepaling gebruikenToegestaan, maar kan meer botsingen veroorzaken
Altijd 1 teruggevenToegestaan, maar maakt zoeken traag
Wijzig de sleutel niet tijdens opslag

Stel dat het jaar van een boek veranderlijk is en invloed heeft op de hashcode. Als je na toevoeging aan een verzameling het jaar wijzigt, kan contains(book) mislukken, zelfs met dezelfde referentie: de zoekactie zoekt nu met een andere code. De verzameling kan nog steeds grootte 1 melden.

Houd velden voor gelijkheidsbepaling stabiel zolang een object een element van een verzameling of een sleutel in een map is. Als ze moeten veranderen, verwijder dan het object terwijl de oude waarden nog gelden, wijzig het en voeg het opnieuw toe. Als je pas na de wijziging probeert te verwijderen, kun je hetzelfde zoekprobleem tegenkomen.

Ook bij gegenereerde gelijkheid moet je de gegevens zorgvuldig kiezen

Java 16 en later ondersteunen records, zoals record Book(String title, int year) {}. Een record genereert accessors title() en year(), plus gelijkheid, hashcodes en een leesbare stringvorm op basis van alle componenten. Componentvelden zijn final. Dit voorbeeld is een bruikbare onveranderlijke waarde omdat de componenten een string en een geheel getal zijn; een record met een veranderlijke lijst zou nog steeds de toestand van die lijst delen.

Voor gewone klassen kan een IDE equals en hashCode samen genereren. Selecteer de velden die de bedoelde gelijkheid bepalen en houd beide methoden op elkaar afgestemd als die keuze verandert.

Belangrijkste punten

  • Gelijke objecten vereisen gelijke hashcodes; botsingen tussen ongelijke objecten zijn toegestaan.
  • Overschrijf hashCode telkens wanneer je equals overschrijft.
  • Bereken de hashcode alleen uit gegevens die passen bij de gelijkheidsregel.
  • Houd velden die de hashcode bepalen stabiel zolang het object lid is van een verzameling of als mapsleutel wordt gebruikt.
  • Verwijder een sleutel voordat je die wijzigt en voeg die daarna opnieuw toe.
  • Records genereren gelijkheid uit hun componenten; veranderlijke componenten vragen nog steeds aandacht.
5

Geef elke klasse een duidelijke taak

Een tekstprogramma heeft twee soorten werk: communiceren met een gebruiker en de gegevens en regels beheren. Als je die taken in afzonderlijke klassen onderbrengt, zijn de regels gemakkelijker te controleren en te hergebruiken. Verbind de klassen via constructorparameters en kleine publieke methoden. Voeg een functie daarna eerst toe waar de regel thuishoort, controleer het resultaat met bekende waarden en geef de interface een opdracht die de functie aanroept en het antwoord toont.

5.1 Invoer, uitvoer en programmaregels

De gebruikersinterface leest invoer en toont uitvoer. De programmalogica slaat gegevens op en past regels toe. In een ontwerp met gescheiden verantwoordelijkheden geef je waarden door aan de logica en krijg je resultaten terug; de klassen voor de logica gebruiken geen Scanner of System.out.

Verbind de objecten in main
public static void main(String[] args) {
    Scanner scanner = new Scanner(System.in);
    TemperatureLog log = new TemperatureLog();
    UserInterface ui = new UserInterface(scanner, log);
    ui.start();
}

Met import java.util.Scanner; maakt deze samenstelling één invoerlezer, één logboek en één interface. De code hoort in de startklasse van het programma. UserInterface slaat de ontvangen objecten op in velden, zodat elke opdracht met hetzelfde logboek werkt.

WerkVerantwoordelijke klasse
Opdrachttekst lezenGebruikersinterface
Een meting opslaanTemperatuurlogboek
Gemiddelde of maximum berekenenTemperatuurlogboek
Een resultaat afronden en afdrukkenGebruikersinterface
Een onmogelijke temperatuur weigerenTemperatuurlogboek
Ongeldige invoer uitleggen aan de gebruikerGebruikersinterface
Geef waarden terug die elke aanroeper kan gebruiken

Een logboek kan add(double celsius), count(), average() en max() aanbieden. Metingen van 18,5; 21,0 en 19,5 °C hebben een som van 59,0, een aantal van 3, een maximum van 21,0 en een gemiddelde van ongeveer 19,6667 °C. De gebruikersinterface kan het teruggegeven gemiddelde afronden met Math.round(average * 10) / 10.0 en 19,7 °C tonen.

Als de methode het getal teruggeeft, kun je het gemakkelijk rechtstreeks controleren. Een methode met de naam printAverage() die alleen afdrukt, koppelt een berekening aan console-uitvoer, waardoor hergebruik lastiger wordt.

Stel voor een eenvoudige opdrachtlus dat add geldige temperaturen accepteert en count het aantal teruggeeft:

class UserInterface {
    private Scanner scanner;
    private TemperatureLog log;

    UserInterface(Scanner scanner, TemperatureLog log) {
        this.scanner = scanner;
        this.log = log;
    }

    void start() {
        while (true) {
            String command = scanner.nextLine();
            if ("quit".equals(command)) {
                break;
            }
            if ("count".equals(command)) {
                System.out.println(log.count());
            }
        }
    }
}

De lus handelt de interactie af. De lus vraagt het logboek om een resultaat en drukt dat af zonder te weten hoe de metingen worden opgeslagen. Een opdracht voor het gemiddelde moet een leeg logboek afhandelen volgens het gedocumenteerde resultaat van de logica, of count() controleren voordat een methode wordt aangeroepen die metingen vereist.

Valideer op de juiste grens

De logica kan add false laten teruggeven zonder een meting onder -273,15 °C op te slaan. Elke aanroeper krijgt dan dezelfde gegevensregel. De gebruikersinterface bepaalt hoe die de weigering meldt en opnieuw om invoer vraagt. Ook niet-numerieke invoer omzetten in een bruikbare foutmelding is werk voor de interface.

Gebruik één Scanner voor System.in en geef die door aan de code die invoer leest. Meerdere scanners kunnen om gebufferde invoer concurreren en als je er één sluit, sluit je de onderliggende invoerstroom. Laat de code die de programma-invoer beheert ook de levensduur van de gedeelde scanner beheren.

De logica kan later via dezelfde publieke methoden een andere interface ondersteunen. Hergebruik ontstaat doordat berekeningen en geldigheidsregels als methoderesultaten beschikbaar blijven, onafhankelijk van een bepaalde opdrachtlus.

Belangrijkste punten

  • De gebruikersinterface leest en toont; de logica slaat gegevens op, handhaaft regels en geeft resultaten terug.
  • main maakt de gedeelde objecten en start de interface.
  • Geef één scanner en het object voor de logica door aan de constructor van de gebruikersinterface.
  • Gegevensregels horen in de logica; invoermeldingen en opmaak van de weergave horen in de gebruikersinterface.
  • Resultaten teruggeven maakt rechtstreekse controles en hergebruik met een andere interface gemakkelijker.

5.2 Voeg een functie toe en controleer het gedrag

Een programma met gescheiden verantwoordelijkheden kan met kleine wijzigingen groeien. Geef de logica een bruikbare methode, controleer die rechtstreeks en verbind daarna een opdracht die de invoer verzamelt en het resultaat toont.

Voeg een zoekmethode toe aan de logica

Een TemperatureLog die een geïnitialiseerde ArrayList<Double> readings opslaat, kan metingen boven een grenswaarde tellen:

int countAbove(double limit) {
    int count = 0;
    for (double reading : readings) {
        if (reading > limit) {
            count++;
        }
    }
    return count;
}

De strikte vergelijking sluit metingen uit die precies gelijk zijn aan de grenswaarde. Voor 18,5; 21,0; 19,5 en 23,0 °C geeft countAbove(20.0) 2 terug. Plaats de vergelijking in het logboek, zodat elke aanroeper dezelfde regel krijgt.

De opdracht in de gebruikersinterface leest een grenswaarde, roept log.countAbove(limit) aan en drukt het aantal af. Als elke opdracht lang wordt, splits start() dan op in private hulpmethoden zoals addReading() en showStatistics(). De hulpmethoden organiseren de interactie; ze halen de gegevensregels niet uit het logboek.

Controleer zonder opdrachten te typen

Maak het object voor de logica en roep de methoden aan met waarden waarvan je de antwoorden kent:

TemperatureLog log = new TemperatureLog();
log.add(18.5);
log.add(21.0);
log.add(19.5);
log.add(23.0);
System.out.println(log.count() == 4);
System.out.println(log.average() == 20.5);
System.out.println(log.max() == 23.0);
System.out.println(log.countAbove(20.0) == 2);

De som is 82,0, dus het verwachte gemiddelde is 20,5. Deze specifieke waarden hebben een exacte binaire representatie, waardoor deze directe gelijkheidscontroles geschikt zijn voor het voorbeeld. Controleer ook een leeg logboek volgens het gedocumenteerde gedrag, een grenswaarde die overeenkomt met een meting en de weigering van ongeldige invoer zonder wijziging van het aantal. Geautomatiseerde testhulpmiddelen bouwen voort op hetzelfde idee van een bekende invoer en een verwacht resultaat.

Wijzig de opslag alleen als die de zoekvragen blijft ondersteunen

Een interface die alleen average, count en max aanroept, hoeft niet te weten of het logboek een lijst bevat. Velden voor een lopende som, een aantal en een maximum kunnen die drie vragen beantwoorden zonder elke meting te bewaren.

Voor een vraag met een willekeurige countAbove(limit) is meer informatie nodig. De meetreeksen 18, 22, 23 en 20, 20, 23 hebben dezelfde som 63, hetzelfde aantal 3 en hetzelfde maximum 23, maar hun aantallen boven 20 zijn 2 en 1. Als je alleen die drie samenvattende waarden bewaart, gaat het onderscheid verloren. De publieke methoden beschrijven wat na een wijziging van de opslag moet blijven werken.

Combineer de ontwerpideeën

Een receptenboek kan een private lijst met recepten beheren. Elk recept kan ingrediënten bevatten die in gram worden gemeten en een bereidingstijd in minuten. Gelijkheid kan controles op duplicaten ondersteunen, terwijl een gebruikersinterface via methoden van het boek recepten toevoegt, toont en doorzoekt. Een gekopieerde resultaatlijst kan de lijstinhoud beschermen terwijl receptobjecten gedeeld blijven; bepaal of aanroepers die recepten mogen wijzigen.

Werk in kleine stappen: implementeer één methode, controleer die en voeg dan de bijbehorende opdracht toe. Als een wijziging een bestaand resultaat stukmaakt, keer dan terug naar een werkende toestand en onderzoek die wijziging afzonderlijk voordat je een andere functie toevoegt.

Belangrijkste punten

  • Voeg de methode voor de logica toe en controleer die voordat je de bijbehorende opdracht in de gebruikersinterface verbindt.
  • Gebruik bekende invoer en verwachte resultaten om de logica rechtstreeks te controleren.
  • Neem grensgevallen mee, zoals een lege collectie of een exacte drempelwaarde.
  • Een wijziging van de opslag moet voldoende informatie bewaren voor elke publieke zoekvraag.

Objecten, referenties en gelijkheid in Java: voorbeelden

5 Walkthroughs

1

Volg variabelen zonder de objecten uit het oog te verliezen

15 stappen
Volg kopieën van primitieve waarden, gedeelde referenties, hertoewijzing en null in een kort programma.
  1. 1
    IntroductieTeken variabelen en objecten

    Een variabele kan rechtstreeks een getal bevatten of een referentie die naar een object leidt. Dat verschil verklaart waarom het kopiëren van een variabele soms onafhankelijke gegevens oplevert en soms een wijziging deelt.

    We tekenen variabelen afzonderlijk van objecten en volgen daarna elke instructie. Houd het aantal pijlen gescheiden van het aantal objecten: meerdere pijlen kunnen bij één object eindigen.

  2. 2
    UitlegEen waarde kan een gegeven of een referentie zijn

    Java heeft acht primitieve typen: byte, short, int, long, float, double, char en boolean. Hun variabelen bevatten gegevens rechtstreeks. Klassen en arrays zijn referentietypen; hun variabelen bevatten referenties of null.

    In int count = 13; is de waarde 13. In Tally t = new Tally(13); is de waarde in t een referentie naar een object dat zijn eigen gegevens bevat.

  3. 3
    VoorbeeldKopieer een primitieve waarde

    Volg de toewijzingen op volgorde:

    int start = 13;
    int next = start;
    next += 2;
    

    De tweede instructie kopieert 13 naar next. De derde leest die kopie en vervangt die door 15. start blijft 13 omdat geen instructie er een nieuwe waarde aan toewijst. Teken twee doosjes met afzonderlijke getallen; er is geen gedeeld object bij betrokken.

  4. 4
    VoorbeeldKopieer een referentiewaarde

    Stel dat Tally een geheel getal opslaat, addOne() het verhoogt en value() het leest.

    Tally first = new Tally(13);
    Tally next = first;
    next.addOne();
    

    Eén new maakt één teller. De toewijzing geeft next een andere route ernaartoe. De aanroep van addOne() verandert de opgeslagen waarde in 14, die first.value() nu ook leest. De tweede variabele is geen tweede teller.

  5. 5
    UitdagingOnderscheid een object van zijn alias

    Wat geeft a.value() terug met dezelfde methoden van Tally?

    Tally a = new Tally(20);
    Tally b = new Tally(a.value());
    b.addOne();
    

    ---reveal---

    De aanroep geeft 20 terug. De tweede new maakt een onafhankelijke teller die met 20 is geïnitialiseerd; de verhoging verandert alleen dat tweede object in 21. Een veldwaarde lezen en in een constructor gebruiken is iets anders dan de oorspronkelijke objectreferentie kopiëren.

  6. 6
    VerbandSommige gedeelde objecten kunnen niet worden gewijzigd

    Strings en wrappers zoals Integer zijn referentietypen. Autoboxing zet primitieve waarden om in wrappers voor toepassingen zoals ArrayList<Integer>.

    Strings zijn onveranderlijk: als left en right “ceder” delen, verandert de toewijzing right = right.toUpperCase() de oorspronkelijke tekens niet. left blijft “ceder”. Het teruggegeven resultaat in hoofdletters wordt alleen aan right toegewezen.

  7. 7
    AnalogieVolg wijzigingen met pijlen

    Stel je elke variabele voor als een kaartje met een route naar een object. Als je het kaartje kopieert, krijg je een andere route naar dezelfde bestemming. Als je op één kaartje een andere route schrijft, veranderen de andere kaartjes niet.

    Teken in een geheugendiagram een pijl van elke referentievariabele naar het bijbehorende object. Een setter wijzigt de veldwaarden van het object; een toewijzing wijzigt de pijl van één variabele. Houd die twee wijzigingen in de tekening duidelijk gescheiden.

  8. 8
    VoorbeeldWijzig eerst gedeelde toestand

    Laat Crate een massa in kilogram opslaan, met de methoden setMass en mass.

    Crate a = new Crate(3);
    Crate b = a;
    b.setMass(6);
    

    Teken a en b met pijlen naar één krat. De setter wijzigt de massa in die krat van 3 naar 6. Zowel a.mass() als b.mass() geeft nu 6 terug; de setter heeft geen van beide variabelen naar iets anders laten verwijzen.

  9. 9
    VoorbeeldRicht daarna één variabele ergens anders op

    Ga verder met het verloop van de krat:

    b = new Crate(9);
    

    Maak een tweede object met massa 9 en richt alleen b daarop. Variabele a wijst nog steeds naar de oorspronkelijke krat, waarvan de massa 6 blijft. Er zijn nu twee bereikbare kratobjecten. Dit is een hertoewijzing, dus de opgeslagen massa van de eerste krat wordt niet teruggezet.

  10. 10
    UitdagingBehoud de overgebleven route

    Ga verder met Crate c = a; a = null;. Via welke variabele is de krat van 6 kg nog bereikbaar en komt die in aanmerking voor automatische geheugenopruiming?

    ---reveal---

    De krat is nog bereikbaar via c en komt dus niet in aanmerking voor opruiming. De eerdere toewijzing kopieerde de referentie naar c; door a op null te zetten, is slechts één route verwijderd. De afzonderlijke krat van 9 kg blijft bereikbaar via b.

  11. 11
    KernpuntEen veld of lijst kan een route behouden

    Als het laatste bruikbare pad naar een object verdwijnt, wordt het onbereikbaar en komt het in aanmerking voor automatische geheugenopruiming. Java kiest het tijdstip van opruimen; het verlies van een referentie garandeert geen onmiddellijke vrijgave.

    Een pad kan via een ander object of een lijst lopen. Controleer voordat je een object onbereikbaar noemt zowel die verbindingen als de lokale variabelen. Een lijstitem kan een object behouden nadat de oorspronkelijke variabele buiten bereik is geraakt.

  12. 12
    UitlegNull betekent dat de bestemming ontbreekt

    null is geen leeg object. Een String die null is, heeft geen object waarop je length() kunt aanroepen, terwijl "" een bruikbare string met lengte nul is. Referentievelden zijn standaard null; lokale variabelen moeten vóór het lezen een waarde toegewezen krijgen.

    Wrappers kunnen ook null zijn. Integer n = null; int count = n; veroorzaakt een NullPointerException wanneer unboxing een primitieve waarde uit de ontbrekende wrapper probeert te halen.

  13. 13
    TipLees de fout bij de mislukte aanroep

    Zoek bij een null-fout het regelnummer en vervolgens de instantieaanroep, veldtoegang of unboxing op die regel. Verhelderende meldingen, die vanaf Java 15 standaard zijn ingeschakeld, kunnen het ontbrekende veld aanwijzen.

    Ga terug naar de constructor, het zoekresultaat of de toewijzing die een object had moeten leveren. Als je “null” in afgedrukte tekst ziet, is dat een aanwijzing: het afdrukken van een null-string kan slagen, ook al mislukt een latere instantieaanroep.

  14. 14
    UitdagingControleer veilig in het gecombineerde verloop

    Een zoekactie geeft een krat of null terug en het resultaat wordt toegewezen met Crate found = findCrate();. Welke voorwaarde controleert veilig of de massa groter is dan 5 kg? Waarom moeten de tests in die volgorde staan?

    ---reveal---

    Gebruik found != null && found.mass() > 5. De null-test wordt eerst uitgevoerd en && slaat mass() over als er geen krat bestaat. Als de methodeaanroep eerst stond, zou die null derefereren voordat de controle wordt bereikt. Voor een teruggegeven referentie gelden dezelfde regels als voor de aliassen die je eerder volgde.

  15. 15
    SamenvattingControleer een referentieverloop

    Markeer bij een nieuw verloop elke new, kopieer de waarde bij elke toewijzing en werk alleen het veld bij waarop een mutatie betrekking heeft. Volg daarna elk overgebleven pad voordat je bepaalt dat een object onbereikbaar is.

    Bepaal op een regel die mislukt welke precieze bewerking een object nodig heeft. Controleer vóór die bewerking op null of initialiseer het veld met het object dat het programma werkelijk nodig heeft, zoals een lege lijst.

2

Volg een object bij het doorgeven aan en teruggeven door een methode

12 stappen
Volg mutatie en hertoewijzing via een parameter en kies daarna tussen gedeelde en onafhankelijke retourwaarden.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVolg een aanroep en het resultaat
  2. 2
    UitlegEen parameter krijgt een eigen waarde
  3. 3
    VoorbeeldWijzig het gedeelde object
  4. 4
    VoorbeeldWijs de lokale route opnieuw toe
  5. 5
    UitdagingKan een methode variabelen van de aanroeper verwisselen?
  6. 6
    VerbandVergelijk de ontvanger met het argument
  7. 7
    UitlegHet retourtype belooft geen kopie
  8. 8
    VoorbeeldMaak een onafhankelijke draaiknop als retourwaarde
  9. 9
    UitdagingZoek een item dat kan worden bijgewerkt
  10. 10
    TipEen getter kan een verborgen alias blootleggen
  11. 11
    UitdagingCombineer een retourwaarde met een aanroep met een parameter
  12. 12
    SamenvattingOnderzoek een methode bij aanroep en terugkeer
3

Bouw en bescherm een collectie objecten

16 stappen
Verbind referentievelden en lijsten, zoek opgeslagen items en onderscheid kopieën van de lijst van kopieën van elementen.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieHoud de lagen van toestand gescheiden
  2. 2
    UitlegModelleer een relatie van het type “heeft een”
  3. 3
    VoorbeeldDelegeer de datumregel
  4. 4
    UitdagingBescherm de ontvangen datum
  5. 5
    UitlegEen item is een andere route
  6. 6
    VoorbeeldBereken een totaal met een lus
  7. 7
    VoorbeeldBewaar het geselecteerde object
  8. 8
    UitdagingGeef afwezigheid op het juiste moment terug
  9. 9
    TipScheid verwijderen van doorlopen
  10. 10
    UitlegGeef het beherende object een geïnitialiseerde lijst
  11. 11
    VoorbeeldBied bruikbare bewerkingen aan
  12. 12
    UitdagingOnderscheid een lijstkopie van de elementen
  13. 13
    VerbandBescherm beide richtingen
  14. 14
    VoorbeeldFilter naar een nieuw resultaat
  15. 15
    UitdagingVolg een kopie via een ander object
  16. 16
    SamenvattingControleer de grenzen van de collectie
4

Bouw gelijkheid waarop collecties kunnen vertrouwen

16 stappen
Kies tussen een vergelijking op identiteit of inhoud, implementeer equals en hashCode samen en redeneer over stabiele sleutels.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieBepaal wat een overeenkomst betekent
  2. 2
    VoorbeeldGelijke tekst in afzonderlijke objecten
  3. 3
    TipLaat de cache de regel niet bepalen
  4. 4
    UitdagingGeef een klasse inhoudelijke gelijkheid
  5. 5
    UitlegKies velden en de signatuur
  6. 6
    VoorbeeldAccepteer identiteit en wijs verkeerde typen af
  7. 7
    VoorbeeldVergelijk de geselecteerde velden
  8. 8
    UitdagingOntdek een geschonden contract
  9. 9
    KernpuntControleer meer dan één overeenkomend paar
  10. 10
    UitlegHashcodes beperken de mogelijke resultaten
  11. 11
    VoorbeeldGenereer de bijpassende hashcode
  12. 12
    UitdagingHoud een botsing gescheiden van gelijkheid
  13. 13
    VoorbeeldBekijk wat een gewijzigde sleutel doet
  14. 14
    VerbandEen eenvoudig record levert beide methoden
  15. 15
    UitdagingVolg de verzameling met gegenereerde gelijkheid
  16. 16
    SamenvattingControleer een waardeklasse
5

Voeg een functie toe zonder invoer en regels te vermengen

11 stappen
Verbind een console-interface met herbruikbare logica, controleer een nieuwe zoekmethode rechtstreeks en behoud de opslagvereisten ervan.

Sla deze les op om elke Walkthrough stap voor stap te volgen.

  1. 1
    IntroductieVerbind regels met een gesprek
  2. 2
    UitlegBenoem de twee taken
  3. 3
    VoorbeeldVerbind één verzameling gedeelde objecten
  4. 4
    VoorbeeldGeef het resultaat terug voor weergave
  5. 5
    UitdagingPlaats een weigering in de juiste klasse
  6. 6
    UitlegGeef de nieuwe zoekmethode een plaats in de logica
  7. 7
    VoorbeeldControleer met bekende invoer
  8. 8
    UitdagingBeoordeel het opslagvoorstel
  9. 9
    TipBreid het programma stap voor stap uit en controleer elke stap
  10. 10
    UitdagingStap over naar een andere interface
  11. 11
    SamenvattingControleer een nieuwe functie op de grens

Objecten, referenties en gelijkheid in Java: oefenvragen

40 Oefenvragen in 4 sets

Set 1: Volg waarden, aanroepen en eenvoudige vergelijkingen10 Oefenvragen

Vraag 1 van 10

Wat bevat original na deze code?

int original = 8;
int duplicate = original;
duplicate = 11;
  1. 11
  2. 8
  3. 19
  4. 0
Toon antwoordVerberg antwoord
Antwoord: B
De eerste toewijzing slaat 8 op in beide primitieve variabelen. De latere toewijzing wijzigt alleen duplicate, dus original bevat nog steeds 8. Als je de variabelen als aliassen zou behandelen, zou je 11 krijgen, maar een toewijzing van een int kopieert het getal zelf en maakt geen gedeeld object.

Vraag 2 van 10

Lamp heeft een booleaans veld dat aanvankelijk false is; turnOn() zet het op true en isOn() leest het. Wat geven a.isOn() en b.isOn() in die volgorde terug?

Lamp a = new Lamp();
Lamp b = a;
b.turnOn();
b = new Lamp();
  1. false, false
  2. true, true
  3. false, true
  4. true, false
Toon antwoordVerberg antwoord
Antwoord: D
De aanroep zet de lamp aan die a en b delen. Door de hertoewijzing verwijst b daarna naar een nieuwe lamp waarvan het veld als false begint, terwijl a naar de brandende lamp blijft verwijzen. Als je twee keer true voorspelt, zie je het nieuwe object over het hoofd dat de laatste instructie maakt.

Vraag 3 van 10

label kan null zijn. Welke voorwaarde controleert veilig of de tekst minstens drie tekens heeft?
  1. label != null && label.length() >= 3
  2. label.length() >= 3 && label != null
  3. label != null & label.length() >= 3
  4. label.equals(null) && label.length() >= 3
Toon antwoordVerberg antwoord
Antwoord: A
De null-controle wordt eerst uitgevoerd en && slaat de lengteaanroep over als die controle onwaar is. Als de lengteaanroep eerst staat, derefereert die al het ontbrekende object. Een enkele & evalueert ook beide kanten en biedt dus niet de nodige bescherming door kortsluitevaluatie.

Vraag 4 van 10

Een methode ontvangt een Robot r die niet null is. Ze voert alleen r = new Robot(); uit. Wat gebeurt er met de variabele van de aanroeper die als argument is doorgegeven?
  1. Die verwijst nu naar de nieuwe robot.
  2. Die wordt null wanneer de methode terugkeert.
  3. Die verwijst nog steeds naar de oorspronkelijke robot.
  4. Die verwijst naar een kopie van de oorspronkelijke robot.
Toon antwoordVerberg antwoord
Antwoord: C
De parameter ontvangt een kopie van de referentie van de aanroeper. Een nieuwe referentie aan die parameter toewijzen wijzigt alleen de lokale variabele van de methode. Om de referentie van de aanroeper te vervangen, is een toewijzing bij de aanroeper nodig; deze methode wijzigt de oorspronkelijke robot evenmin.

Vraag 5 van 10

Gauge.copy() geeft new Gauge(value) terug. Een meter begint bij 6 en raise() telt er 1 bij op. Welke waarden geven first en second aan?

Gauge first = new Gauge(6);
Gauge second = first.copy();
second.raise();
  1. 7 and 7
  2. 6 and 7
  3. 6 and 6
  4. 7 and 6
Toon antwoordVerberg antwoord
Antwoord: B
De kopieermethode maakt een tweede meter die wordt geïnitialiseerd met de waarde van de eerste. Het verhogen van dat nieuwe object verandert de waarde ervan in 7 en laat de eerste op 6 staan. Twee keer 7 zou volgen uit het delen van de oorspronkelijke referentie, maar de methode maakt expliciet een afzonderlijk object.

Vraag 6 van 10

Een constructor van Device slaat het veranderlijke Settings-argument rechtstreeks op. De getter geeft een kopie van de opgeslagen instellingen terug. Twee apparaten ontvangen hetzelfde instellingenobject en de aanroeper wijzigt dat oorspronkelijke object daarna. Welke ontwerpwijziging maakt elk apparaat onafhankelijk van die externe wijziging en van het andere apparaat?
  1. Sla instellingen rechtstreeks op; geef het opgeslagen object terug via de getter.
  2. Behoud elke constructor; kopieer instellingen alleen wanneer de getter wordt uitgevoerd.
  3. Kopieer instellingen in elke constructor; behoud de getter die kopieert.
  4. Maak één gedeelde kopie van de instellingen; geef die aan beide constructors.
Toon antwoordVerberg antwoord
Antwoord: C
Beide apparaten delen momenteel het ontvangen object, zodat de oorspronkelijke aanroeper hun toestand kan wijzigen zonder een getter te gebruiken. Elke constructor moet de ontvangen toestand kopiëren om elk apparaat een eigen object te geven. Een getter die kopieert beschermt toegang van buitenaf, maar kan een alias die tijdens de constructie is behouden niet verwijderen.

Vraag 7 van 10

Een Sensor s die niet null is, wordt aan twee lijsten toegevoegd. Eén lijst verwijdert het item voor s. Wat gebeurt er met de sensor in de andere lijst?
  1. Die blijft hetzelfde sensorobject.
  2. Die wordt uit de andere lijst verwijderd.
  3. Die wordt vervangen door een gekopieerde sensor.
  4. Die wordt een lijstelement met de waarde null.
Toon antwoordVerberg antwoord
Antwoord: A
Een item verwijderen wijzigt de inhoud van die ene lijst. De andere lijst bevat nog steeds haar eigen referentie naar de sensor en behoudt dus hetzelfde object. Door het verwijderen wordt de sensor niet vernietigd en worden niet alle andere houders op referenties ernaar doorzocht.

Vraag 8 van 10

Wat geven deze twee expressies terug?

String word = new String("maple");
word == "maple"
word.equals("maple")
  1. true, true
  2. false, false
  3. true, false
  4. false, true
Toon antwoordVerberg antwoord
Antwoord: D
De expliciete constructie maakt een ander stringobject dan dat van de literal, dus de identiteitstest is false. String.equals vergelijkt de tekens en geeft true terug. Gelijke tekst maakt afzonderlijk gemaakte strings niet identiek, ook al kunnen gelijke literals een object delen.

Vraag 9 van 10

Welke declaratie overschrijft de gelijkheidsmethode die van Object is geërfd?
  1. public boolean equals(Note other)
  2. public int equals(Object other)
  3. public boolean equals(Object other)
  4. public boolean equal(Object other)
Toon antwoordVerberg antwoord
Antwoord: C
De overgeërfde methode heeft de naam equals, retourtype boolean en parametertype Object. Een overeenkomende declaratie overschrijft de methode; door @Override toe te voegen laat je de compiler de overeenkomst controleren. Een parameter van het type Note maakt een overladen methodevariant en laat de overgeërfde methode bestaan.

Vraag 10 van 10

Een console-app houdt leestijden bij. Dezelfde logica zal later een webpagina ondersteunen. Waar horen de berekening van het totale aantal seconden en het afdrukken van “Totaal” thuis?
  1. Berekening in de logica; afdrukken in de console-interface.
  2. Zowel berekening als afdrukken in de console-interface.
  3. Zowel berekening als afdrukken in de klasse voor de logica.
  4. Afdrukken in de logica; berekening in de console-interface.
Toon antwoordVerberg antwoord
Antwoord: A
Het totaal is een berekening op gegevens die beide interfaces nodig hebben, dus de logica moet het teruggeven. De console-interface kiest het label en drukt het resultaat af. Als de berekening in de console-interface staat, moet de toekomstige webinterface die dupliceren of afhankelijk zijn van consolespecifieke code.
Set 2: Pas delen, collecties en programmagrenzen toe10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Wat gebeurt er met a na de laatste regel?

String a = "river";
String b = a;
b = b.toUpperCase();
  1. Die verwijst nu naar de tekst “RIVER”.
  2. Die wordt null na de toewijzing.
  3. Die verwijst nog steeds naar de tekst “river”.
  4. Die verwijst nu naar een lege string.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Wat gebeurt er wanneer deze code wordt uitgevoerd?

Integer reading = null;
int value = reading;
  1. Unboxing veroorzaakt een NullPointerException.
  2. value ontvangt de primitieve waarde 0.
  3. reading wordt een integerwrapper voor 0.
  4. Compilatie mislukt omdat wrappers geen null kunnen bevatten.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Meter slaat een geheel getal op en heeft set(int) en get(). Wat geeft m.get() na de aanroep terug?

static void adjust(Meter x) {
    x.set(14);
    x = new Meter(22);
    x.set(25);
}
Meter m = new Meter(9);
adjust(m);
  1. 9
  2. 22
  3. 25
  4. 14
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Bin.items() geeft de private lijst met veranderlijke Part-objecten rechtstreeks terug. Een aanroeper moet de teruggegeven lijst en onderdelen mogen bekijken en wijzigen zonder de inhoud van de bak of de toestand van de opgeslagen onderdelen te veranderen. Wat moet het resultaat bevatten?
  1. Een nieuwe lijst met de opgeslagen referenties naar onderdelen.
  2. Een nieuwe lijst met onafhankelijke kopieën van de onderdelen.
  3. De opgeslagen lijst nadat elk onderdeel ter plaatse is gekopieerd.
  4. De opgeslagen lijst met onveranderlijke numerieke indexen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een zoekactie op naam controleert een lijst op volgorde. Bij de namen “Iris”, “Noel”, “Sana” geeft de zoekactie null terug wanneer naar “Sana” wordt gezocht. Momenteel staat return null in een else na de naamtest binnen de lus. Waar moet die return staan?
  1. Voordat de lus items begint te controleren.
  2. Nadat de lus zonder overeenkomst is voltooid.
  3. Direct na de eerste geslaagde naamtest.
  4. Binnen de lus voordat elke nieuwe naam wordt gelezen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

copy wordt gemaakt met new ArrayList<>(original). Beide lijsten bevatten aanvankelijk dezelfde veranderlijke Clip c. De aanroeper wijzigt c via copy.get(0) en maakt daarna copy leeg. Wat blijft er in original over?
  1. Eén item voor de gewijzigde clip.
  2. Eén item voor de ongewijzigde clip.
  3. Geen items; beide lijsten zijn leeg.
  4. Eén null-item op de plaats van de clip.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Badge heeft velden en een constructor, maar overschrijft equals niet. Wat is het resultaat?

new Badge("Q7").equals(new Badge("Q7"))
  1. true, omdat de veldwaarden overeenkomen.
  2. true, omdat constructors gelijkheid bepalen.
  3. false, omdat strings geen velden voor gelijkheidsbepaling kunnen zijn.
  4. false, omdat dit afzonderlijke objecten zijn.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Tag.equals vergelijkt alleen code. Tag.hashCode gebruikt zowel code als het veranderlijke color. Twee tags hebben dezelfde code maar verschillende kleuren. Een ontwikkelaar stelt voor een constante hashcode terug te geven om de correctheid te herstellen. Hoe moeten de twee regels worden beoordeeld?
  1. De huidige regel kan het contract schenden; een constante is toegestaan maar inefficiënt.
  2. De huidige regel is geldig; een constante verhindert aparte hashcodes voor verschillende objecten.
  3. Beide regels schenden het contract omdat hashcodes uniek moeten zijn.
  4. Beide regels zijn even efficiënt omdat gelijkheid overeenkomsten bevestigt.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een programma dat tijdsduren bijhoudt, weigert negatieve tijdsduren. Waar moeten de weigering en de melding die de gebruiker vraagt het opnieuw te proberen worden geïmplementeerd?
  1. Weigering in de gebruikersinterface; de melding in de logica.
  2. Zowel weigering als melding alleen in de gebruikersinterface.
  3. Weigering in de logica; de melding in de gebruikersinterface.
  4. Zowel weigering als melding alleen in de logica.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een logboek biedt momenteel count(), average() en max() aan. De ontwikkelaar voegt countAbove(limit) toe voor elke grenswaarde die een aanroeper opgeeft en vervangt daarna alle metingen door alleen een som, aantal en maximum. Waarom kan de nieuwe zoekmethode niet meer voor elk logboek correct worden geïmplementeerd?
  1. Het gemiddelde wordt onjuist zodra de metingen een drempelwaarde bevatten.
  2. Verschillende meetlijsten kunnen dezelfde samenvattende waarden maar verschillende aantallen boven een drempelwaarde hebben.
  3. Een maximum kan alleen worden berekend als elk oorspronkelijk meetobject wordt bewaard.
  4. Een wijziging van een privaat veld vereist altijd ook een wijziging van elke opdracht in de gebruikersinterface.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 3: Spoor fouten op in aliassen, gelijkheid en collectieontwerp10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Alleen de getoonde referenties bestaan en alle variabelen blijven in gebruik. Cell heeft veranderlijke waardegegevens. Hoeveel celobjecten blijven bereikbaar?

Cell a = new Cell(3);
Cell b = new Cell(8);
Cell c = a;
a = b;
b = null;
c = a;
  1. Twee celobjecten.
  2. Drie celobjecten.
  3. Geen celobjecten.
  4. Eén celobject.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Een klasse declareert private ArrayList<Note> notes;. De constructor wijst nooit een waarde toe aan notes en add roept onmiddellijk notes.add(note) aan. Welke wijziging maakt een nieuw gemaakte instantie klaar om notities op te slaan?
  1. Declareer een lokale lijst binnen add en laat het veld ongemoeid.
  2. Wijs in de constructor expliciet null toe aan notes.
  3. Initialiseer notes met new ArrayList<>().
  4. Maak notes public zodat de aanroeper het veld kan benaderen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Box heeft een veld met een geheel getal dat door get() wordt gelezen en door set(int) wordt gewijzigd. Welke waarden geven left.get() en right.get() na deze aanroep aan?

static void revise(Box x, Box y) {
    x.set(10);
    x = y;
    x.set(16);
}
Box left = new Box(4);
Box right = new Box(7);
revise(left, right);
  1. 10 and 16
  2. 16 and 16
  3. 4 and 16
  4. 10 and 7
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Een Member slaat een SimpleDate joined op die niet null is en moet bepalen of het lid eerder is toegetreden dan een ander lid. Waar moet de vergelijking van jaar, maand en dag worden geïmplementeerd?
  1. In elke opdracht in de gebruikersinterface die leden vergelijkt.
  2. In SimpleDate.before, aangeroepen door de methode van het lid.
  3. In Member.toString, door de aanroeper teruggelezen als tekst.
  4. In elke aanroeper nadat alle datumvelden publiek zijn gemaakt.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een constructor van Queue slaat een ontvangen ArrayList<Job> rechtstreeks op, maar de getter geeft een nieuwe ondiepe lijst terug. De aanroeper maakt de oorspronkelijke invoerlijst leeg. De wachtrij moet de oorspronkelijke inhoud behouden, terwijl jobobjecten gedeeld mogen blijven. Welke oplossing voldoet aan die eis?
  1. Kopieer de ontvangen lijst ook in de constructor.
  2. Behoud de constructor en kopieer elke job in de getter.
  3. Geef de interne lijst terug zodat beide lijsten gelijk blijven.
  4. Wijzig de getter zodat die telkens een nieuwe lege lijst teruggeeft.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Token declareert public boolean equals(Token other) zonder @Override. Directe vergelijkingen lijken correct, maar een ArrayList<Token> vindt een nieuwe token met overeenkomende velden niet. Wat is het probleem met de signatuur?
  1. De methode overschrijft equals, maar lijsten vergelijken alleen hashcodes.
  2. De methode kan geen private velden in een ander tokenobject lezen.
  3. De methode overlaadt equals en laat equals(Object) ongewijzigd.
  4. De methode moet een geheel getal teruggeven zodat collecties kunnen zoeken.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Key.equals en hashCode gebruiken beide serial. Een veranderlijke sleutel wordt aan een HashSet toegevoegd, daarna verandert het serienummer en mislukt een zoekactie. Welke volgorde ondersteunt een geplande wijziging van het serienummer correct en behoudt het lidmaatschap?
  1. Wijzig het serienummer, verwijder met het nieuwe nummer en voeg opnieuw toe.
  2. Wijzig het serienummer, roep contains aan en laat het item daarna ongemoeid.
  3. Voeg de sleutel een tweede keer toe, wijzig het serienummer en verwijder één keer.
  4. Verwijder met het oude serienummer, wijzig het nummer en voeg opnieuw toe.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

Een methode van de gebruikersinterface heeft het aantal opgeslagen taken nodig. Wat moet de methode van de logica gewoonlijk doen?
  1. Het aantal afdrukken op de console.
  2. Het aantal als geheel getal teruggeven.
  3. Een opdracht lezen en daarna een label afdrukken.
  4. Een scanner openen om een aantal te vragen.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

countAbove(limit) telt metingen die strikt groter zijn dan het argument. Een logboek bevat 11,5; 15,0; 15,0 en 18,5 °C. Wat moet een rechtstreekse controle voor countAbove(15.0) verwachten?
  1. 0
  2. 1
  3. 2
  4. 3
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Mark.equals vergelijkt code. Een lijst bevat markeringen met de codes “A”, “B”, “A”; de twee A-markeringen zijn afzonderlijke objecten. Een tweede lijst bevat alleen een referentie naar de eerste A-markering. Wat blijft over na marks.removeAll(second)?
  1. Alleen de markering met code “B”.
  2. De B-markering en de tweede A-markering.
  3. De twee markeringen met code “A”.
  4. Geen markeringen in de lijst.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Set 4: Redeneer over kopieën, contracten en ontwerpwijzigingen10 Oefenvragen

Sla deze les op om elk antwoord te zien en elke set te oefenen.

Vraag 1 van 10

Een lijst heeft items met gehele getallen nodig die ook afwezig mogen zijn. Met welk elementtype kun je een getal of null opslaan?
  1. int
  2. Integer
  3. double
  4. boolean
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 2 van 10

Node bevat veranderlijke gegevens. Alleen de getoonde referenties bestaan. Hoeveel knoopobjecten zijn na de laatste regel bereikbaar via first of de lijst?

Node first = new Node();
ArrayList<Node> nodes = new ArrayList<>();
nodes.add(first);
first = new Node();
nodes.add(first);
nodes.remove(0);
  1. Eén knoopobject.
  2. Twee knoopobjecten.
  3. Drie knoopobjecten.
  4. Geen knoopobjecten.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 3 van 10

Locker.find(id) geeft momenteel een nieuw gemaakt veranderlijk Item terug met dezelfde veldwaarden als het overeenkomende opgeslagen item. Aanroepers gebruiken het resultaat om het label van het opgeslagen item bij te werken. Welk gedrag moet de methode bieden zodat die wijzigingen invloed hebben op de inhoud van het kluisje?
  1. Een ondiepe kopie van de lijst in het kluisje teruggeven.
  2. Een nieuw item met het bijgewerkte label teruggeven.
  3. De referentie naar het overeenkomende opgeslagen item teruggeven.
  4. Na elke labelwijziging een andere kopie teruggeven.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 4 van 10

Twee Schedule-objecten slaan een gedeeld veranderlijk SimpleDate-veld op. Een ontwikkelaar maakt alleen de velden van de schema’s final. Wat gebeurt er als het oorspronkelijke datumobject via zijn setter wordt gewijzigd?
  1. Alleen het schema waarvan de getter is aangeroepen, ziet de wijziging.
  2. Geen van beide schema’s ziet de gewijzigde datum.
  3. Elk schema behoudt een eigen kopie van de oude datum.
  4. Beide schema’s zien de gewijzigde datum nog steeds.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 5 van 10

Een bibliotheek houdt een private lijst met veranderlijke Document-objecten bij. matching(prefix) maakt een nieuwe lijst met bestaande documenten die overeenkomen. Een aanroeper verwijdert één resultaat en wijzigt de titel van een ander resultaat. Welk effect kan dit op de bibliotheek hebben?
  1. De lijstinhoud verandert; elke opgeslagen titel blijft gelijk.
  2. Zowel de lijstinhoud van de bibliotheek als de opgeslagen titel verandert.
  3. Noch de lijstinhoud van de bibliotheek, noch een opgeslagen titel verandert.
  4. De lijstinhoud blijft gelijk; een opgeslagen titel verandert.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 6 van 10

Wat stelt x == y vast als het true is voor objectreferenties x en y die niet null zijn?
  1. Hun resultaten van toString() bepalen gelijkheid.
  2. Ze verwijzen naar hetzelfde object.
  3. Hun klassen overschrijven equals samen.
  4. Hun velden zijn nooit gewijzigd.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 7 van 10

Een methode Score.equals(Object) accepteert een andere score als het absolute verschil tussen hun gehele waarden hoogstens 2 is. Scores x, y en z bevatten 10, 12 en 14. Er veranderen geen waarden. Welk probleem met het gelijkheidscontract doet zich bij deze drie objecten voor?
  1. Transitiviteit faalt: de eerste twee paren komen overeen, maar de uiteinden niet.
  2. Symmetrie faalt: het omdraaien van hetzelfde paar verandert het vergelijkingsresultaat.
  3. Reflexiviteit faalt: een score kan niet gelijk zijn aan zichzelf.
  4. Consistentie faalt: herhaling van één paar geeft telkens een ander resultaat.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 8 van 10

record Stamp(String code, int batch) {} gebruikt gegenereerde gelijkheid en hashcodes. Een verzameling ontvangt new Stamp("R", 4), een afzonderlijke new Stamp("R", 4) en daarna new Stamp("R", 5). Hoeveel items bevat de verzameling?
  1. 1
  2. 3
  3. 2
  4. 0
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 9 van 10

Een takenapp heeft een nieuwe opdracht nodig om de langste opgeslagen tijdsduur terug te geven. Welke implementatievolgorde maakt rechtstreekse controle mogelijk voordat console-interactie wordt toegevoegd?
  1. Lees en druk tijdsduren af in de gebruikersinterface en kopieer die code daarna naar de logica.
  2. Voeg een zoekmethode toe aan de logica, controleer bekende tijdsduren en verbind daarna de opdracht in de gebruikersinterface.
  3. Voeg console-invoerverzoeken toe aan de zoekmethode van de logica en roep die daarna vanuit elke opdracht aan.
  4. Maak de private lijst public en laat de gebruikersinterface die daarna rechtstreeks doorzoeken.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Vraag 10 van 10

Een waardeklasse gebruikt Double.compare(level, other.level) == 0 in equals. Een voorgestelde hashcodemethode geeft altijd 1 terug. Een andere geeft verschillende codes terug voor verschillende objecten waarvan beide niveaus NaN zijn. Hoe verhouden deze voorstellen zich tot het gelijkheidscontract?
  1. De constante is niet toegestaan; verschillende codes voor NaN-objecten zijn vereist.
  2. Beide zijn toegestaan omdat verschillende objecten willekeurige hashcodes mogen hebben.
  3. De constante is toegestaan; verschillende codes voor gelijke NaN-waarden zijn niet toegestaan.
  4. Beide zijn niet toegestaan omdat elke double-waarde een unieke code nodig heeft.
Toon antwoordVerberg antwoord

Sla deze les op om dit antwoord te zien.

Kom je niet uit je eigen opgave? Huiswerkhulp

Kernbegrippen

61 Kernbegrippen, elk met een definitie

A

Alias
Een alias is een andere variabele of referentie die hetzelfde object bereikt en zo meerdere routes naar één gedeelde toestand biedt.
Na Box second = first; is second een alias en wijzigt de setter ervan de doos die via first wordt bereikt.
Argument
Een argument is een waarde die bij een aanroep wordt opgegeven om een bijbehorende parameter in de aangeroepen methode of constructor te initialiseren.
In gate.allowIn(ana) is het argument de referentiewaarde die uit ana wordt gelezen en door de parameter wordt gekopieerd.
ArrayList
Een ArrayList is een lijstobject met aanpasbare grootte en geordende items; objectitems zijn referenties, dus als je er een toevoegt, kopieer je de velden ervan niet.
Een ArrayList<Parcel> kan drie pakketreferenties bevatten en een lus hun gewichten in kilogram laten optellen.
Autoboxing
Autoboxing is de automatische omzetting door Java van een primitieve waarde naar de bijbehorende wrapperreferentie tijdens een toewijzing of aanroep.
Door autoboxing kan numbers.add(12) een Integer-waarde doorgeven als numbers een ArrayList<Integer> is.
Automatische geheugenopruiming
Automatische geheugenopruiming is het automatisch vrijmaken door Java van geheugen van onbereikbare objecten, op een tijdstip dat de uitvoeringsomgeving kiest.
Een teller komt in aanmerking voor automatische geheugenopruiming nadat het draaiende programma zijn laatste pad ernaartoe verliest.

B

Beherend object
Een beherend object beheert toestand via eigen methoden, zoals een afspeellijst die toevoegingen aan haar private lijst met nummers regelt.
De afspeellijst is het beherende object dat verantwoordelijk is voor het handhaven van een maximumaantal in de methode add.
Bereikbaarheid
Bereikbaarheid betekent dat het draaiende programma een bruikbare keten van referenties naar een object kan volgen, eventueel via velden of lijsten.
Een lijst houdt een pakket bereikbaar, ook nadat de lokale variabele die het oorspronkelijk bevatte null wordt.

C

Collectiebevraging
Een collectiebevraging selecteert of berekent informatie uit opgeslagen elementen en geeft vaak een waarde, een object of een nieuwe resultaatlijst terug.
De collectiebevraging songsLongerThan(200) bouwt een nieuwe lijst en behoudt daarbij de referenties naar de overeenkomende nummers.
Compositie
Compositie modelleert een relatie van het type “heeft een” door een object velden te geven die naar andere objecten verwijzen, zoals de geboortedatum van een persoon.
Met compositie kan een cursus zijn docent als persoonsobject opslaan en de naam via een methode opvragen.
ConcurrentModificationException
Een ConcurrentModificationException kan aangeven dat een collectie structureel is gewijzigd tijdens het doorlopen met een iterator.
Rechtstreeks verwijderen uit een lijst tijdens een for-each-lus kan een ConcurrentModificationException veroorzaken voordat de lus klaar is.
Consistentie
Consistentie vereist dat herhaalde gelijkheidscontroles overeenkomen zolang de vergeleken informatie ongewijzigd blijft, zodat resultaten betrouwbaar blijven.
Consistentie staat toe dat een resultaat verandert nadat een veld voor gelijkheidsbepaling verandert, maar niet zolang de vergeleken gegevens vaststaan.
Constructorinjectie
Constructorinjectie levert objecten waarmee een object samenwerkt als constructorargumenten, zodat het gedeelde bestaande objecten kan gebruiken.
Constructorinjectie geeft een gebruikersinterface één scanner en één logboek in plaats van voor elke opdracht een afzonderlijk logboek te maken.

D

Defensieve kopie
Een defensieve kopie is een afzonderlijk object dat wordt gemaakt om te voorkomen dat code van buitenaf beschermde toestand via een gedeelde veranderlijke referentie wijzigt.
Een getter voor de geboortedatum geeft een defensieve kopie terug, zodat een setter van de datum door een aanroeper de geboortedatum van de persoon intact laat.
Delegatie
Delegatie betekent dat je een ander object via zijn methoden vraagt werk uit te voeren, zodat een regel bij het object blijft dat de gegevens beheert.
Een persoon gebruikt delegatie wanneer olderThan voor de datumvergelijking birthday.before(other.birthday) aanroept.
Directe controle
Een directe controle roept methoden van de logica aan met bekende invoer en vergelijkt hun resultaten met verwachte antwoorden zonder via een gebruikersinterface te gaan.
Een directe controle kan vier metingen toevoegen en het gemiddelde nagaan zonder opdrachttekst te lezen.

E

Encapsulatie
Encapsulatie schermt toestand af met de methoden van een klasse, zodat de klasse wijzigingen kan regelen en regels voor opgeslagen gegevens kan handhaven.
De encapsulatie van een afspeellijst wordt zwakker als een getter de private lijst teruggeeft en aanroepers die rechtstreeks kunnen leegmaken.

F

Fabrieksmethode
Een fabrieksmethode levert een object aan de aanroeper, door een nieuwe instantie te maken of een bestaande te hergebruiken.
De fabrieksmethode issue() van een ticketautomaat maakt een ticket met het volgende nummer en geeft dat terug.
final-referentieveld
Aan een final-referentieveld kan na initialisatie geen andere referentie worden toegewezen, hoewel het object waarnaar het verwijst nog veranderlijk kan zijn.
Een final-referentieveld dat een lijst bevat, voorkomt dat de lijst wordt vervangen, maar houdt aanroepen van add niet tegen.
for-each-lus
Een for-each-lus bezoekt de elementen van een collectie achtereenvolgens en wijst elke elementwaarde voor verwerking toe aan een lusvariabele.
Een for-each-lus over pakketten kan elk resultaat van getWeight() optellen zonder het lijstlidmaatschap te wijzigen.

G

Gebruikersinterface
Een gebruikersinterface is de programmacode die met de gebruiker communiceert, zoals een opdrachtlus die tekst leest en resultaten afdrukt.
Een gebruikersinterface leest een temperatuur, roept de methode add van een temperatuurlogboek aan en toont eventuele meldingen van weigering.
Gegevensvalidatie
Gegevensvalidatie controleert of een opgegeven waarde aan de regels van de applicatie voldoet voordat die in de opgeslagen toestand wordt opgenomen.
Gegevensvalidatie in een logboek voor tijdsduren kan een negatieve waarde weigeren, ongeacht welke interface het logboek aanroept.

H

Hashbotsing
Een hashbotsing ontstaat wanneer ongelijke objecten dezelfde hashcode opleveren; dat is toegestaan omdat gelijkheid nog steeds een overeenkomst bevestigt.
Een hashbotsing voegt verschillende waarden in een verzameling niet samen als equals ze correct onderscheidt.
Hashcode
Een hashcode is een geheel getal dat wordt gebruikt om mogelijke resultaten in een collectie op basis van hashcodes te beperken; gelijke objecten moeten gelijke codes opleveren.
Een passende hashcode voor een boek kan worden berekend met Objects.hash(title, year) als die velden gelijkheid bepalen.
HashMap-sleutel
Een HashMap-sleutel identificeert een bijbehorende waarde via hashcodes en gelijkheid; de toestand die gelijkheid bepaalt, moet tijdens de opslag stabiel blijven.
Met een HashMap-sleutel op basis van titel en jaar kun je een planklabel ophalen met een afzonderlijk gelijk boekobject.
HashSet
Een HashSet slaat verschillende elementen op met behulp van hun hashcodes en gelijkheid, zodat het toevoegen van een gelijke waarde geen extra item oplevert.
Een HashSet<Book> bewaart één item voor twee afzonderlijke gelijke boeken als hun gelijkheid en hashcodes overeenstemmen.
Hertoewijzing
Een hertoewijzing slaat een nieuwe waarde op in een bestaande variabele; bij een referentievariabele verandert daardoor welk object de variabele bereikt.
De hertoewijzing current = new Counter(); laat current naar een ander object verwijzen, terwijl andere aliassen de oude teller behouden.

I

Identiteit
Identiteit betekent dat iets hetzelfde object is, wat voor twee referenties met == wordt getest; overeenkomende veldwaarden alleen stellen dit niet vast.
Twee afzonderlijk gemaakte boeken kunnen gelijke titels en jaren hebben en toch verschillende identiteiten hebben.
Inhoudelijke gelijkheid
Inhoudelijke gelijkheid betekent dat de velden die volgens de gelijkheidsregel van een klasse zijn gekozen overeenkomen, ook als de objecten verschillende identiteiten hebben.
Een boekencatalogus kan inhoudelijke gelijkheid definiëren met titel en jaar en de huidige lener negeren.

K

Kopieermethode
Een kopieermethode geeft een nieuw object terug dat is geïnitialiseerd met gegevens van een bestaand object en biedt daarmee de onafhankelijkheid die het ontwerp belooft.
De kopieermethode van een teller gebruikt new Counter(value), zodat het verhogen van de kopie het origineel ongewijzigd laat.
Kortsluitevaluatie
Kortsluitevaluatie slaat de rechterkant van && over als de linkerkant false is, zodat een eerdere controle een latere aanroep kan beschermen.
Door kortsluitevaluatie slaat name != null && name.length() > 0 de methodeaanroep over bij null.

L

Lijstlidmaatschap
Lijstlidmaatschap beschrijft welke items een lijst bevat; het lidmaatschap wijzigen staat los van het wijzigen van de velden van een opgeslagen object.
Een nummer verwijderen wijzigt het lidmaatschap van één lijst, terwijl een andere afspeellijst nog steeds naar datzelfde nummer kan verwijzen.

M

Methodeoverschrijving
Een methodeoverschrijving vervangt het gedrag van een overgeërfde instantiemethode met een overeenkomende declaratie, zoals equals(Object) in een nieuwe klasse.
Met de annotatie @Override kan de compiler controleren of de gelijkheidsmethode van een boek een geldige methodeoverschrijving is.
Mutatie
Een mutatie wijzigt de toestand van een bestaand object, waardoor de gewijzigde veldwaarden zichtbaar worden via alle referenties naar dat object.
De aanroep parcel.setWeight(3.5) is een mutatie die ook zichtbaar is via een lijst die dat pakket bevat.

N

null-referentie
Een null-referentie is de waarde null, die naar geen object leidt en in een referentievariabele kan staan, maar niet in een primitieve variabele.
Via een null-referentie in String name kun je length() niet aanroepen, terwijl een lege string lengte 0 kan teruggeven.
NullPointerException
Een NullPointerException is een uitvoeringsfout door bewerkingen die een ontbrekend object nodig hebben, zoals een instantieaanroep via null of het uitpakken van null.
De aanroep name.length() veroorzaakt een NullPointerException als de variabele name de waarde null bevat.

O

Ondiepe kopie
Een ondiepe kopie maakt een afzonderlijke buitenste houder, maar kopieert de elementreferenties, zodat de opgenomen objecten gedeeld blijven.
Uit een ondiepe kopie van een lijst met nummers kan onafhankelijk een item worden verwijderd, terwijl elk veranderlijk nummer gedeeld blijft.
Ontvanger
De ontvanger is het object waarop een instantiemethode wordt aangeroepen; binnen die methode verwijst this naar dat object.
In earlier.before(later) is earlier de ontvanger; de andere datum wordt als argument opgegeven.
Onveranderlijkheid
Onveranderlijkheid betekent dat de toestand van een object na de constructie niet kan veranderen, zodat het delen van dat object geen gedeelde mutatie mogelijk maakt.
Door de onveranderlijkheid van strings kan toUpperCase() de tekens in de oorspronkelijke string niet wijzigen.
Overladen methodevariant
Een overladen methodevariant is een andere methode met dezelfde naam maar andere parametertypen; die vervangt de oorspronkelijke declaratie niet.
Als je equals(Book other) schrijft, maak je een overladen methodevariant terwijl equals(Object) beschikbaar blijft voor collecties.

P

Parameter
Een parameter is een variabele die door een methode of constructor wordt gedeclareerd en bij een aanroep de gekopieerde waarde van een argument ontvangt.
In allowIn(Person visitor) ontvangt de parameter visitor de door de aanroeper opgegeven persoonsreferentie.
Primitief type
Een primitief type slaat gewone gegevens rechtstreeks op, zoals een getal of een waarheidswaarde; Java heeft acht primitieve typen.
Door het primitieve type int kan count 12 rechtstreeks bevatten, zodat een toewijzing aan een andere variabele 12 kopieert.
Programmalogica
Programmalogica slaat gegevens op en past de regels van de applicatie toe, met methoden die onafhankelijk van console-interactie kunnen werken.
Programmalogica berekent het temperatuurgemiddelde als een getal dat een console- of webinterface kan tonen.
Publiek contract
Een publiek contract is het beloofde gedrag waarop aanroepers vertrouwen bij het gebruik van de publieke methoden van een klasse, onafhankelijk van de private opslag.
Een opslagwijziging respecteert het publieke contract alleen als elke ondersteunde zoekvraag over temperaturen blijft werken.

R

Record
Een record is een Java-gegevensklasse met final-componentvelden en gegenereerde accessors, gelijkheid, hashcodes en een stringrepresentatie.
Het record Book(String title, int year) vergelijkt beide componenten en biedt de accessors title() en year().
Referentie
Een referentie is een waarde die naar een object leidt; als je die kopieert, krijgt een andere variabele een route naar hetzelfde object.
Door de referentie die met Counter second = first; wordt toegewezen, kunnen beide variabelen de oorspronkelijke teller bereiken.
Referentietype
Een referentietype is een klasse-, array- of interfacetype waarvan variabelen referenties naar objecten of de waarde null kunnen bevatten.
String is een referentietype, dus een variabele kan naar een tekstobject verwijzen of geen objectreferentie bevatten.
Referentieveld
Een referentieveld is een veld van een referentietype dat het beherende object met een ander object verbindt of null bevat als er geen object aanwezig is.
Het referentieveld birthday in Person kan naar een SimpleDate leiden die datumvergelijkingen uitvoert.
Reflexiviteit
Reflexiviteit vereist dat elk object dat niet null is bij vergelijking gelijk is aan zichzelf, zodat een geldige gelijkheidsmethode true teruggeeft voor x.equals(x).
Een eerste controle met this == other in equals bevestigt reflexiviteit voordat veldwaarden worden vergeleken.
Retourwaarde
Een retourwaarde is de waarde die een methode aan de aanroeper teruggeeft; bij een objectresultaat is dat een referentie of null.
De retourwaarde van findByName in een register kan naar een opgeslagen persoon leiden die de aanroeper en het register vervolgens delen.

S

Samenvattende waarde
Een samenvattende waarde is een gecombineerde statistiek zoals som, aantal of maximum die waarden samenvat en daarbij een deel van de informatie erover verliest.
Dezelfde samenvattende waarden kunnen lijsten beschrijven met verschillende aantallen boven een drempelwaarde, dus voor die zoekvraag zijn meer gegevens nodig.
Standaardveldwaarde
Een standaardveldwaarde is de beginwaarde die Java aan een veld geeft, zoals null voor referenties of false voor booleaanse waarden.
De standaardveldwaarde van een niet-geïnitialiseerd ArrayList<Song>-veld is null, dus er bestaat nog geen lijst.
Stringinterning
Stringinterning deelt stringobjecten voor overeenkomende literals, zodat twee identieke literalexpressies dezelfde referentie kunnen hebben.
Door stringinterning kan "oak" == "oak" true zijn, waardoor een fout verborgen blijft die verschijnt als gelijke tekst uit invoer komt.
Symmetrie
Symmetrie vereist dat het omdraaien van twee objecten die niet null zijn in een gelijkheidsvergelijking hetzelfde resultaat true of false behoudt.
Symmetrie faalt als een boek de string met zijn titel als gelijk accepteert, maar de string het boekobject afwijst.

T

Transitiviteit
Transitiviteit vereist dat als één object gelijk is aan een tweede en het tweede gelijk is aan een derde, het eerste ook gelijk is aan het derde.
Door transitiviteit behoren drie boeken met dezelfde velden voor gelijkheidsbepaling tot dezelfde groep gelijke waarden.

U

Unboxing
Unboxing zet de opgeslagen waarde van een wrapperobject om naar het primitieve type; bij een null-wrapper mislukt dit met een NullPointerException.
Unboxing vindt plaats in int count = boxed;, dus boxed moet naar een echt Integer-object verwijzen.

V

Velden voor gelijkheidsbepaling
Velden voor gelijkheidsbepaling zijn de gegevens die zijn gekozen om te bepalen wanneer twee instanties voor het doel van een programma dezelfde waarde voorstellen.
Titel en jaar kunnen velden voor gelijkheidsbepaling van boeken zijn, zodat een wijziging van alleen de lener die gelijkheid niet verandert.
Veranderlijk object
Een veranderlijk object staat wijzigingen van zijn toestand na de constructie toe, zodat aliassen wijzigingen via een andere referentie kunnen waarnemen.
Een datum met setters is een veranderlijk object; als twee personen die delen, kunnen beide opgeslagen geboortedatums veranderen.

W

Waardeoverdracht
Waardeoverdracht geeft een methodeparameter een eigen kopie van de waarde van een argument, inclusief een gekopieerde referentie voor een objectargument.
Door waardeoverdracht kan een methode haar persoonsparameter naar een ander object laten verwijzen zonder de variabele van de aanroeper te veranderen.
Wrappercache
Een wrappercache hergebruikt wrapperobjecten voor bepaalde waarden, waardoor referentie-identiteit afhangt van hergebruik in plaats van numerieke gelijkheid.
De Integer-wrappercache bevat kleine waarden, dus gebruik equals in plaats van te vertrouwen op == voor wrappers.
Wrapperklasse
Een wrapperklasse stelt een primitieve waarde voor als object, zodat waarden zoals gehele getallen kunnen worden gebruikt waar referentietypen nodig zijn.
De wrapperklasse Integer levert het elementtype voor ArrayList<Integer>; het primitieve type int kan dat niet.

Z

Zoekresultaat
Een zoekresultaat is de overeenkomende waarde die een zoekmethode teruggeeft; een zoekactie naar een object kan de opgeslagen referentie of null bij geen overeenkomst teruggeven.
Het zoekresultaat voor een naam kan een persoon zijn die in een register is opgeslagen, zodat die via de teruggegeven referentie kan worden bijgewerkt.

Over deze les

Door StudeliFout melden

Maak je eigen Studiemateriaal

Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.

Aan de slag