- Leren
- Informatica & ICT
- Programmeren
- Java programmeren: klassen, overerving, collecties en streams
- Objecten, referenties en gelijkheid in Java
Objecten, referenties en gelijkheid in Java
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
- 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 referentievariabelenNoem 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 objectenVolg 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-referentieLeg uit wat null betekent, herken waar null voorkomt, voorkom NullPointerException en bepaal wanneer null een redelijke waarde is.
- Vergelijk primitieve variabelen en referentievariabelen
- 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 methodeparametersVoorspel 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 teruggevenSchrijf 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.
- Gebruik objecten als methodeparameters
- 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 objectenModelleer 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 dieVoeg 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 objectenSchrijf 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.
- Stel een klasse samen met andere objecten
- Onderscheid identiteit van inhoudelijke gelijkheid, schrijf correcte equals- en hashCode-methoden en voorspel hoe lijsten, verzamelingen en maps die gebruiken.
- Onderscheid identiteit van inhoudelijke gelijkheidLeg uit wat == en equals vergelijken bij primitieve waarden, strings, wrappers en je eigen klassen, en voorspel het resultaat van elke vergelijking.
- Schrijf een equals-methodeOverschrijf 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 equalsLeg 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.
- Onderscheid identiteit van inhoudelijke gelijkheid
- 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 programmalogicaBepaal 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 hetVoeg 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.
- Verdeel een programma in een gebruikersinterface en klassen voor de programmalogica
Objecten, referenties en gelijkheid in Java: uitleg
1Volg 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.
| Declaratie | Wat 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
newmaakt 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);
- Teken variabele
amet een pijl naar object A, met gewicht 2. - Teken
bmet een pijl naar datzelfde object A. Er is nog steeds één doos. - Verander het gewicht in object A in 5. Beide routes bereiken nu gewicht 5.
- Maak object B met gewicht 7 en richt alleen
bdaarop.
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.
| Handeling | Wijziging in het diagram | Effect via andere aliassen |
|---|---|---|
b.setWeight(5) | Wijzig een veld in het object | Ze zien het nieuwe gewicht |
b = new Box(7) | Richt de pijl vanuit b ergens anders op | Ze behouden hun bestaande pijlen |
b = null | Verwijder de pijl vanuit b | Ze 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.
| Doel | Veilige expressie |
|---|---|
| Afwezigheid controleren | name == null |
| Aanwezigheid controleren | name != 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
nullbetekent 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.
2Gebruik 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 methode | De aanroeper ziet |
|---|---|
| Een waarde toewijzen aan een primitieve parameter | Geen wijziging van de variabele van de aanroeper |
| Het object van de parameter wijzigen | Gewijzigde toestand van het gedeelde object |
| Een referentieparameter opnieuw toewijzen | Geen 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.
thisduidt 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.
3Verbind 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 constructor | Gevolg voor de referentie |
|---|---|
| De ontvangen datumreferentie opslaan | Aanroeper en persoon kunnen de datum delen |
| Een nieuwe datum uit de ontvangen waarden maken | De persoon begint met een eigen datum |
| Een onveranderlijke datum opslaan | Delen 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 kopie | Effect op de afspeellijst |
|---|---|
| Een item verwijderen | De inhoud van de afspeellijst blijft gelijk |
| De kopie leegmaken | De inhoud van de afspeellijst blijft gelijk |
| Een gedeeld nummer wijzigen | De 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.
4Bepaal 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 waarden | Betekenis van == | Betekenis van equals |
|---|---|---|
| Primitieve waarden | Vergelijkt primitieve waarden | Geen instantiemethode op een primitieve waarde |
| Referenties | Hetzelfde object, of beide null | De gelijkheidsregel van de klasse |
| Strings | Hetzelfde stringobject | Overeenkomende tekeninhoud |
Klasse die Object.equals erft | Hetzelfde object | Hetzelfde 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, terwijlequalsde regel van de klasse volgt. Object.equalsvergelijkt identiteit totdat de methode wordt overschreven.- Gebruik
String.equalsvoor 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)eny.equals(x)komen overeen. - Transitief: als
xgelijk is aanyenygelijk is aanz, isxgelijk aanz. - 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 vervangtequals(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.
| Hashcoderegel | Gevolg voor het contract |
|---|---|
| Dezelfde velden voor gelijkheidsbepaling gebruiken | Gelijke objecten krijgen gelijke codes |
| Een veld toevoegen dat gelijkheid negeert | Gelijke objecten kunnen verschillende codes krijgen |
| Minder velden voor gelijkheidsbepaling gebruiken | Toegestaan, maar kan meer botsingen veroorzaken |
| Altijd 1 teruggeven | Toegestaan, 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
hashCodetelkens wanneer jeequalsoverschrijft. - 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.
5Geef 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.
| Werk | Verantwoordelijke klasse |
|---|---|
| Opdrachttekst lezen | Gebruikersinterface |
| Een meting opslaan | Temperatuurlogboek |
| Gemiddelde of maximum berekenen | Temperatuurlogboek |
| Een resultaat afronden en afdrukken | Gebruikersinterface |
| Een onmogelijke temperatuur weigeren | Temperatuurlogboek |
| Ongeldige invoer uitleggen aan de gebruiker | Gebruikersinterface |
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.
mainmaakt 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
1Volg variabelen zonder de objecten uit het oog te verliezen
15 stappen
- 1IntroductieTeken 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.
- 2UitlegEen waarde kan een gegeven of een referentie zijn
Java heeft acht primitieve typen:
byte,short,int,long,float,double,charenboolean. Hun variabelen bevatten gegevens rechtstreeks. Klassen en arrays zijn referentietypen; hun variabelen bevatten referenties ofnull.In
int count = 13;is de waarde 13. InTally t = new Tally(13);is de waarde inteen referentie naar een object dat zijn eigen gegevens bevat. - 3VoorbeeldKopieer 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.startblijft 13 omdat geen instructie er een nieuwe waarde aan toewijst. Teken twee doosjes met afzonderlijke getallen; er is geen gedeeld object bij betrokken. - 4VoorbeeldKopieer een referentiewaarde
Stel dat
Tallyeen geheel getal opslaat,addOne()het verhoogt envalue()het leest.Tally first = new Tally(13); Tally next = first; next.addOne();Eén
newmaakt één teller. De toewijzing geeftnexteen andere route ernaartoe. De aanroep vanaddOne()verandert de opgeslagen waarde in 14, diefirst.value()nu ook leest. De tweede variabele is geen tweede teller. - 5UitdagingOnderscheid een object van zijn alias
Wat geeft
a.value()terug met dezelfde methoden vanTally?Tally a = new Tally(20); Tally b = new Tally(a.value()); b.addOne();---reveal---
De aanroep geeft 20 terug. De tweede
newmaakt 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. - 6VerbandSommige gedeelde objecten kunnen niet worden gewijzigd
Strings en wrappers zoals
Integerzijn referentietypen. Autoboxing zet primitieve waarden om in wrappers voor toepassingen zoalsArrayList<Integer>.Strings zijn onveranderlijk: als
leftenright“ceder” delen, verandert de toewijzingright = right.toUpperCase()de oorspronkelijke tekens niet.leftblijft “ceder”. Het teruggegeven resultaat in hoofdletters wordt alleen aanrighttoegewezen. - 7AnalogieVolg 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.
- 8VoorbeeldWijzig eerst gedeelde toestand
Laat
Crateeen massa in kilogram opslaan, met de methodensetMassenmass.Crate a = new Crate(3); Crate b = a; b.setMass(6);Teken
aenbmet pijlen naar één krat. De setter wijzigt de massa in die krat van 3 naar 6. Zowela.mass()alsb.mass()geeft nu 6 terug; de setter heeft geen van beide variabelen naar iets anders laten verwijzen. - 9VoorbeeldRicht 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
bdaarop. Variabeleawijst 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. - 10UitdagingBehoud 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
cen komt dus niet in aanmerking voor opruiming. De eerdere toewijzing kopieerde de referentie naarc; dooraop null te zetten, is slechts één route verwijderd. De afzonderlijke krat van 9 kg blijft bereikbaar viab. - 11KernpuntEen 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.
- 12UitlegNull betekent dat de bestemming ontbreekt
nullis geen leeg object. EenStringdie null is, heeft geen object waarop jelength()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 eenNullPointerExceptionwanneer unboxing een primitieve waarde uit de ontbrekende wrapper probeert te halen. - 13TipLees 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.
- 14UitdagingControleer 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&&slaatmass()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. - 15SamenvattingControleer 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.
2Volg een object bij het doorgeven aan en teruggeven door een methode
12 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieVolg een aanroep en het resultaat
- 2UitlegEen parameter krijgt een eigen waarde
- 3VoorbeeldWijzig het gedeelde object
- 4VoorbeeldWijs de lokale route opnieuw toe
- 5UitdagingKan een methode variabelen van de aanroeper verwisselen?
- 6VerbandVergelijk de ontvanger met het argument
- 7UitlegHet retourtype belooft geen kopie
- 8VoorbeeldMaak een onafhankelijke draaiknop als retourwaarde
- 9UitdagingZoek een item dat kan worden bijgewerkt
- 10TipEen getter kan een verborgen alias blootleggen
- 11UitdagingCombineer een retourwaarde met een aanroep met een parameter
- 12SamenvattingOnderzoek een methode bij aanroep en terugkeer
3Bouw en bescherm een collectie objecten
16 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieHoud de lagen van toestand gescheiden
- 2UitlegModelleer een relatie van het type “heeft een”
- 3VoorbeeldDelegeer de datumregel
- 4UitdagingBescherm de ontvangen datum
- 5UitlegEen item is een andere route
- 6VoorbeeldBereken een totaal met een lus
- 7VoorbeeldBewaar het geselecteerde object
- 8UitdagingGeef afwezigheid op het juiste moment terug
- 9TipScheid verwijderen van doorlopen
- 10UitlegGeef het beherende object een geïnitialiseerde lijst
- 11VoorbeeldBied bruikbare bewerkingen aan
- 12UitdagingOnderscheid een lijstkopie van de elementen
- 13VerbandBescherm beide richtingen
- 14VoorbeeldFilter naar een nieuw resultaat
- 15UitdagingVolg een kopie via een ander object
- 16SamenvattingControleer de grenzen van de collectie
4Bouw gelijkheid waarop collecties kunnen vertrouwen
16 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieBepaal wat een overeenkomst betekent
- 2VoorbeeldGelijke tekst in afzonderlijke objecten
- 3TipLaat de cache de regel niet bepalen
- 4UitdagingGeef een klasse inhoudelijke gelijkheid
- 5UitlegKies velden en de signatuur
- 6VoorbeeldAccepteer identiteit en wijs verkeerde typen af
- 7VoorbeeldVergelijk de geselecteerde velden
- 8UitdagingOntdek een geschonden contract
- 9KernpuntControleer meer dan één overeenkomend paar
- 10UitlegHashcodes beperken de mogelijke resultaten
- 11VoorbeeldGenereer de bijpassende hashcode
- 12UitdagingHoud een botsing gescheiden van gelijkheid
- 13VoorbeeldBekijk wat een gewijzigde sleutel doet
- 14VerbandEen eenvoudig record levert beide methoden
- 15UitdagingVolg de verzameling met gegenereerde gelijkheid
- 16SamenvattingControleer een waardeklasse
5Voeg een functie toe zonder invoer en regels te vermengen
11 stappen
Sla deze les op om elke Walkthrough stap voor stap te volgen.
- 1IntroductieVerbind regels met een gesprek
- 2UitlegBenoem de twee taken
- 3VoorbeeldVerbind één verzameling gedeelde objecten
- 4VoorbeeldGeef het resultaat terug voor weergave
- 5UitdagingPlaats een weigering in de juiste klasse
- 6UitlegGeef de nieuwe zoekmethode een plaats in de logica
- 7VoorbeeldControleer met bekende invoer
- 8UitdagingBeoordeel het opslagvoorstel
- 9TipBreid het programma stap voor stap uit en controleer elke stap
- 10UitdagingStap over naar een andere interface
- 11SamenvattingControleer een nieuwe functie op de grens
Objecten, referenties en gelijkheid in Java: oefenvragen
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;
Toon antwoordVerberg antwoord
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();
Toon antwoordVerberg antwoord
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?Toon antwoordVerberg antwoord
&& 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
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?Toon antwoordVerberg antwoord
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();
Toon antwoordVerberg antwoord
Vraag 6 van 10
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?Toon antwoordVerberg antwoord
Vraag 7 van 10
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?Toon antwoordVerberg antwoord
Vraag 8 van 10
Wat geven deze twee expressies terug?
String word = new String("maple");
word == "maple"
word.equals("maple")
Toon antwoordVerberg antwoord
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
Object is geërfd?Toon antwoordVerberg antwoord
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
Toon antwoordVerberg antwoord
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();
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;
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);
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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
return null in een else na de naamtest binnen de lus. Waar moet die return staan?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?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"))
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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
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?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;
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 2 van 10
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?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);
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
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?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?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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 8 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
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?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)?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
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);
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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 4 van 10
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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 5 van 10
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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 6 van 10
x == y vast als het true is voor objectreferenties x en y die niet null zijn?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 7 van 10
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?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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 9 van 10
Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Vraag 10 van 10
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?Toon antwoordVerberg antwoord
Sla deze les op om dit antwoord te zien.
Kom je niet uit je eigen opgave? Huiswerkhulp
Kernbegrippen
A
Alias
Box second = first; is second een alias en wijzigt de setter ervan de doos die via first wordt bereikt.Argument
gate.allowIn(ana) is het argument de referentiewaarde die uit ana wordt gelezen en door de parameter wordt gekopieerd.ArrayList
ArrayList<Parcel> kan drie pakketreferenties bevatten en een lus hun gewichten in kilogram laten optellen.Autoboxing
numbers.add(12) een Integer-waarde doorgeven als numbers een ArrayList<Integer> is.Automatische geheugenopruiming
B
Beherend object
add.Bereikbaarheid
C
Collectiebevraging
songsLongerThan(200) bouwt een nieuwe lijst en behoudt daarbij de referenties naar de overeenkomende nummers.Compositie
ConcurrentModificationException
ConcurrentModificationException veroorzaken voordat de lus klaar is.Consistentie
Constructorinjectie
D
Defensieve kopie
Delegatie
olderThan voor de datumvergelijking birthday.before(other.birthday) aanroept.Directe controle
E
Encapsulatie
F
Fabrieksmethode
issue() van een ticketautomaat maakt een ticket met het volgende nummer en geeft dat terug.final-referentieveld
add niet tegen.for-each-lus
getWeight() optellen zonder het lijstlidmaatschap te wijzigen.G
Gebruikersinterface
add van een temperatuurlogboek aan en toont eventuele meldingen van weigering.Gegevensvalidatie
H
Hashbotsing
equals ze correct onderscheidt.Hashcode
Objects.hash(title, year) als die velden gelijkheid bepalen.HashMap-sleutel
HashSet
HashSet<Book> bewaart één item voor twee afzonderlijke gelijke boeken als hun gelijkheid en hashcodes overeenstemmen.Hertoewijzing
current = new Counter(); laat current naar een ander object verwijzen, terwijl andere aliassen de oude teller behouden.I
Identiteit
== wordt getest; overeenkomende veldwaarden alleen stellen dit niet vast.Inhoudelijke gelijkheid
K
Kopieermethode
new Counter(value), zodat het verhogen van de kopie het origineel ongewijzigd laat.Kortsluitevaluatie
&& over als de linkerkant false is, zodat een eerdere controle een latere aanroep kan beschermen.name != null && name.length() > 0 de methodeaanroep over bij null.L
Lijstlidmaatschap
M
Methodeoverschrijving
equals(Object) in een nieuwe klasse.@Override kan de compiler controleren of de gelijkheidsmethode van een boek een geldige methodeoverschrijving is.Mutatie
parcel.setWeight(3.5) is een mutatie die ook zichtbaar is via een lijst die dat pakket bevat.N
null-referentie
null, die naar geen object leidt en in een referentievariabele kan staan, maar niet in een primitieve variabele.String name kun je length() niet aanroepen, terwijl een lege string lengte 0 kan teruggeven.NullPointerException
name.length() veroorzaakt een NullPointerException als de variabele name de waarde null bevat.O
Ondiepe kopie
Ontvanger
this naar dat object.earlier.before(later) is earlier de ontvanger; de andere datum wordt als argument opgegeven.Onveranderlijkheid
toUpperCase() de tekens in de oorspronkelijke string niet wijzigen.Overladen methodevariant
equals(Book other) schrijft, maak je een overladen methodevariant terwijl equals(Object) beschikbaar blijft voor collecties.P
Parameter
allowIn(Person visitor) ontvangt de parameter visitor de door de aanroeper opgegeven persoonsreferentie.Primitief type
int kan count 12 rechtstreeks bevatten, zodat een toewijzing aan een andere variabele 12 kopieert.Programmalogica
Publiek contract
R
Record
Book(String title, int year) vergelijkt beide componenten en biedt de accessors title() en year().Referentie
Counter second = first; wordt toegewezen, kunnen beide variabelen de oorspronkelijke teller bereiken.Referentietype
null kunnen bevatten.String is een referentietype, dus een variabele kan naar een tekstobject verwijzen of geen objectreferentie bevatten.Referentieveld
birthday in Person kan naar een SimpleDate leiden die datumvergelijkingen uitvoert.Reflexiviteit
x.equals(x).this == other in equals bevestigt reflexiviteit voordat veldwaarden worden vergeleken.Retourwaarde
findByName in een register kan naar een opgeslagen persoon leiden die de aanroeper en het register vervolgens delen.S
Samenvattende waarde
Standaardveldwaarde
ArrayList<Song>-veld is null, dus er bestaat nog geen lijst.Stringinterning
"oak" == "oak" true zijn, waardoor een fout verborgen blijft die verschijnt als gelijke tekst uit invoer komt.Symmetrie
T
Transitiviteit
U
Unboxing
NullPointerException.int count = boxed;, dus boxed moet naar een echt Integer-object verwijzen.V
Velden voor gelijkheidsbepaling
Veranderlijk object
W
Waardeoverdracht
Wrappercache
equals in plaats van te vertrouwen op == voor wrappers.Wrapperklasse
Integer levert het elementtype voor ArrayList<Integer>; het primitieve type int kan dat niet.Z
Zoekresultaat
- 1Eerste Java-programma’s
- 2Voorwaarden en lussen in Java
- 3Java-methoden
- 4Lijsten, arrays en strings in Java
- 5Java-klassen en objecten
- 6Objecten, referenties en gelijkheid in Java
- 7HashMaps en collecties in Java
- 8Overerving in Java
- 9Excepties en bestanden in Java
- 10Klassendiagrammen en unittests in Java
- 11Lambda-expressies en streams in Java
Over deze les
Door StudeliFout melden
Maak je eigen Studiemateriaal
Maak van je aantekeningen, slides en documenten Leermodules, Oefenvragen, Flashcards en 6 andere tools.