Java støtter gjenbruk av klasser gjennom arv og komposisjon. Denne todelte opplæringen lærer deg hvordan du bruker arv i Java-programmene dine. I del 1 lærer du hvordan du bruker strekker
nøkkelord for å utlede en underordnet klasse fra en overordnet klasse, påkalle foreldreklassekonstruktører og -metoder og overstyre metoder. I del 2 turnerer du java.lang.Objekt
, som er Javas superklasse som alle andre klasser arver fra.
For å fullføre læringen om arv, må du sjekke ut Java-tipset mitt som forklarer når du skal bruke komposisjon vs arv. Du lærer hvorfor komposisjon er et viktig supplement til arv, og hvordan du bruker den til å beskytte mot problemer med innkapsling i Java-programmene dine.
last ned Få koden Last ned kildekoden for eksempel applikasjoner i denne opplæringen. Skapt av Jeff Friesen for JavaWorld.Java-arv: To eksempler
Arv er en programmeringskonstruksjon som programvareutviklere bruker for å etablere er-et forhold mellom kategorier. Arv gjør det mulig for oss å utlede mer spesifikke kategorier fra mer generiske. Den mer spesifikke kategorien er en slags den mer generiske kategorien. For eksempel er en brukskonto en slags konto der du kan gjøre innskudd og uttak. På samme måte er en lastebil et slags kjøretøy som brukes til å hale store gjenstander.
Arv kan synke gjennom flere nivåer, noe som fører til stadig mer spesifikke kategorier. Som et eksempel viser figur 1 bil og lastebil som arver fra kjøretøy; stasjonsvogn som arver fra bil; og søppelbil som arver fra lastebil. Pilene peker fra mer spesifikke "underordnede" kategorier (lavere ned) til mindre spesifikke "overordnede" kategorier (høyere opp).
Jeff FriesenDette eksemplet illustrerer enkelt arv der en underordnet kategori arver tilstand og atferd fra en umiddelbar foreldrekategori. I motsetning, flere arv gjør det mulig for en underordnet kategori å arve tilstand og atferd fra to eller flere umiddelbare foreldrekategorier. Hierarkiet i figur 2 illustrerer flere arv.
Jeff FriesenKategorier er beskrevet av klasser. Java støtter enkelt arv gjennom klasseutvidelse, der en klasse direkte arver tilgjengelige felt og metoder fra en annen klasse ved å utvide den. Java støtter imidlertid ikke flere arv gjennom klasseutvidelser.
Når du ser på et arvshierarki, kan du enkelt oppdage flere arv ved tilstedeværelsen av et diamantmønster. Figur 2 viser dette mønsteret i sammenheng med kjøretøy, landbil, vannbil og luftfartøy.
Søkeordet utvider
Java støtter klasseutvidelse via strekker
nøkkelord. Når det er tilstede, strekker
spesifiserer et foreldre-barn-forhold mellom to klasser. Nedenfor bruker jeg strekker
å etablere et forhold mellom klassene Kjøretøy
og Bil
, og deretter mellom Regnskap
og Sparekonto
:
Oppføring 1. The strekker
nøkkelord angir et forhold mellom foreldre og barn
klasse Kjøretøy {// medlemserklæringer} klasse Bil utvider kjøretøy {// arver tilgjengelige medlemmer fra kjøretøy // gir egne medlemserklæringer} klassekonto {// medlemserklæringer} klasse SavingsAccount utvider konto {// arver tilgjengelige medlemmer fra konto // gir egne medlemserklæringer}
De strekker
nøkkelord er spesifisert etter kursnavnet og før et annet kursnavn. Klassenavnet før strekker
identifiserer barnet og klassenavnet etter strekker
identifiserer foreldrene. Det er umulig å spesifisere flere klassenavn etter strekker
fordi Java ikke støtter klassebasert flere arv.
Disse eksemplene kodifiserer is-a-forhold: Bil
er en spesialisert Kjøretøy
og Sparekonto
er en spesialisert Regnskap
. Kjøretøy
og Regnskap
er kjent som baseklasser, foreldrekurs, eller superklasser. Bil
og Sparekonto
er kjent som avledede klasser, barneklasser, eller underklasser.
Avsluttende klasser
Du kan erklære en klasse som ikke skal utvides; for eksempel av sikkerhetsmessige årsaker. I Java bruker vi endelig
nøkkelord for å forhindre at noen klasser utvides. Bare prefiks en klasseoverskrift med endelig
, som i siste klasse Passord
. Gitt denne erklæringen, vil kompilatoren rapportere en feil hvis noen prøver å utvide Passord
.
Barneklasser arver tilgjengelige felt og metoder fra foreldreklassene og andre forfedre. De arver imidlertid ikke konstruktører. I stedet erklærer barneklasser sine egne konstruktører. Videre kan de erklære sine egne felt og metoder for å skille dem fra foreldrene. Vurder oppføring 2.
Oppføring 2. An Regnskap
foreldreklasse
klassekonto {privat strengnavn; privat langt beløp; Konto (strengnavn, langt beløp) {this.name = name; setAmount (beløp); } ugyldig innskudd (langt beløp) {this.beløp + = beløp; } String getName () {return name; } lang getAmount () {returbeløp; } ugyldig setAmount (langt beløp) {this.amount = beløp; }}
Oppføring 2 beskriver en generell bankkontoklasse som har et navn og et startbeløp, som begge er satt i konstruktøren. Det lar brukerne også gjøre innskudd. (Du kan gjøre uttak ved å sette inn negative beløp, men vi vil ignorere denne muligheten.) Merk at kontonavnet må angis når en konto opprettes.
Representerer valutaverdier
antall øre. Du foretrekker kanskje å bruke en dobbelt
eller a flyte
å lagre pengeverdier, men å gjøre det kan føre til unøyaktigheter. For en bedre løsning, vurder BigDecimal
, som er en del av Java sitt standard klassebibliotek.
Oppføring 3 presenterer a Sparekonto
barneklasse som utvider sin Regnskap
foreldreklasse.
Oppføring 3. A Sparekonto
barneklasse utvider sitt Regnskap
foreldreklasse
klasse SavingsAccount utvider konto {SavingsAccount (langt beløp) {super ("besparelser", beløp); }}
De Sparekonto
klasse er trivielt fordi den ikke trenger å erklære flere felt eller metoder. Det erklærer imidlertid en konstruktør som initialiserer feltene i sin Regnskap
superklasse. Initialisering skjer når Regnskap
sin konstruktør kalles via Java super
nøkkelord, etterfulgt av en parentes-argumentliste.
Når og hvor du skal ringe super ()
Akkurat som dette()
må være det første elementet i en konstruktør som kaller en annen konstruktør i samme klasse, super()
må være det første elementet i en konstruktør som kaller en konstruktør i sin superklasse. Hvis du bryter denne regelen, rapporterer kompilatoren en feil. Kompilatoren vil også rapportere en feil hvis den oppdager en super()
ring inn en metode; bare ringe noen gang super()
i en konstruktør.
Oppføring 4 utvides ytterligere Regnskap
med en Kontrollerer konto
klasse.
Oppføring 4. A Kontrollerer konto
barneklasse utvider sin Regnskap
foreldreklasse
klasse CheckingAccount utvider konto {CheckingAccount (langt beløp) {super ("sjekker", beløp); } ugyldig uttak (langt beløp) {setAmount (getAmount () - beløp); }}
Kontrollerer konto
er litt mer omfattende enn Sparekonto
fordi det erklærer en ta ut()
metode. Legg merke til at denne metoden ringer til setAmount ()
og getAmount ()
, hvilken Kontrollerer konto
arver fra Regnskap
. Du har ikke direkte tilgang til beløp
felt i Regnskap
fordi dette feltet er erklært privat
(se Oppføring 2).
super () og konstruktøren uten argument
Hvis super()
er ikke spesifisert i en underklassekonstruktør, og hvis superklassen ikke erklærer en nei-argument
, vil kompilatoren rapportere en feil. Dette er fordi underklassekonstruktøren må ringe a nei-argument
superklassekonstruktør når super()
er ikke til stede.
Eksempel på klassehierarki
Jeg har opprettet en AccountDemo
applikasjonsklasse som lar deg prøve Regnskap
klassehierarki. Ta først en titt på AccountDemo
kildekoden.
Oppføring 5. AccountDemo
viser kontoklassens hierarki
klasse AccountDemo {public static void main (String [] args) {SavingsAccount sa = new SavingsAccount (10000); System.out.println ("kontonavn:" + sa.getName ()); System.out.println ("initial beløp:" + sa.getAmount ()); sa. innskudd (5000); System.out.println ("nytt beløp etter innskudd:" + sa.getAmount ()); CheckingAccount ca = ny CheckingAccount (20000); System.out.println ("kontonavn:" + ca.getName ()); System.out.println ("initial beløp:" + ca.getAmount ()); ca innskudd (6000); System.out.println ("nytt beløp etter innskudd:" + ca.getAmount ()); ca. med trekk (3000); System.out.println ("nytt beløp etter uttak:" + ca.getAmount ()); }}
De hoved()
metoden i Listing 5 viser først Sparekonto
, deretter Kontrollerer konto
. Forutsatt Konto.java
, SavingsAccount.java
, CheckingAccount.java
, og AccountDemo.java
kildefiler er i samme katalog, utfør en av følgende kommandoer for å kompilere alle disse kildefilene:
javac AccountDemo.java javac * .java
Utfør følgende kommando for å kjøre applikasjonen:
java AccountDemo
Du bør følge følgende utdata:
kontonavn: besparelse opprinnelig beløp: 10000 nytt beløp etter innskudd: 15000 kontonavn: sjekke opprinnelig beløp: 20000 nytt beløp etter innskudd: 26000 nytt beløp etter uttak: 23000
Overstyring av metoden (og overbelastning av metoden)
En underklasse kan overstyring (erstatt) en arvet metode slik at underklassens versjon av metoden kalles i stedet. En overstyrende metode må angi samme navn, parameterliste og returtype som metoden som overstyres. For å demonstrere har jeg erklært en skrive ut()
metoden i Kjøretøy
klasse nedenfor.
Oppføring 6. Erklæring om a skrive ut()
metoden som skal overstyres
klasse Kjøretøy {private strengmerker; privat streng modell; privat int år; Vehicle (String make, String model, int year) {this.make = make; this.model = modell; dette.år = år; } String getMake () {return make; } String getModel () {returmodell; } int getYear () {returår; } ugyldig utskrift () {System.out.println ("Make:" + make + ", Model:" + model + ", Year:" + year); }}
Deretter overstyrer jeg skrive ut()
i Lastebil
klasse.
Oppføring 7. Overstyring skrive ut()
i en Lastebil
underklasse
klasse Truck utvider kjøretøy {privat dobbel tonnasje; Lastebil (strengmerke, strengmodell, int år, dobbel tonnasje) {super (merke, modell, år); this.tonnage = tonnasje; } dobbel getTonnage () {retur tonnasje; } ugyldig utskrift () {super.print (); System.out.println ("Tonnage:" + tonnage); }}
Lastebil
s skrive ut()
metoden har samme navn, returtype og parameterliste som Kjøretøy
s skrive ut()
metode. Legg også merke til det Lastebil
s skrive ut()
metoden første samtaler Kjøretøy
s skrive ut()
metode ved å prefiks super.
til metodens navn. Det er ofte en god ide å utføre superklasselogikken først og deretter utføre underklasselogikken.
Kaller superklassemetoder fra underklassemetoder
For å kalle en superklassemetode fra den overordnede underklassemetoden, prefikser du metodenavnet med det reserverte ordet super
og medlemstilgangsoperatøren. Ellers vil du ende opp med å ringe til underklassens overordnede metode. I noen tilfeller vil en underklasse maskere ikke-privat
superklassefelt ved å erklære felt med samme navn. Du kan bruke super
og medlemstilgangsoperatøren for å få tilgang til ikke-privat
superklassefelt.
For å fullføre dette eksemplet har jeg utdraget a VehicleDemo
klassen hoved()
metode:
Lastebil = ny lastebil ("Ford", "F150", 2008, 0.5); System.out.println ("Make =" + truck.getMake ()); System.out.println ("Model =" + truck.getModel ()); System.out.println ("Year =" + truck.getYear ()); System.out.println ("Tonnage =" + truck.getTonnage ()); truck.print ();
Den endelige linjen, truck.print ();
, samtaler lastebil
s skrive ut()
metode. Denne metoden kaller først Kjøretøy
s skrive ut()
å levere lastebilens merke, modell og årstall; da gir den lastebilens tonnasje. Denne delen av utgangen er vist nedenfor:
Merke: Ford, Modell: F150, År: 2008 Tonnage: 0.5
Bruk final for å blokkere metodeoverstyring
Noen ganger kan det hende du trenger å erklære en metode som av sikkerhetsgrunner eller av en annen grunn ikke bør overstyres. Du kan bruke endelig
nøkkelord for dette formålet. For å forhindre overstyring, bare prefiks en metodetittel med endelig
, som i endelig String getMake ()
. Kompilatoren vil da rapportere en feil hvis noen prøver å overstyre denne metoden i en underklasse.
Metodeoverbelastning vs overstyring
Anta at du byttet ut skrive ut()
metode i liste 7 med den nedenfor:
ugyldig utskrift (streng eier) {System.out.print ("Eier:" + eier); super.print (); }
Den modifiserte Lastebil
klassen har nå to skrive ut()
metoder: den forrige eksplisitt erklærte metoden og metoden arvet fra Kjøretøy
. De ugyldig utskrift (streng eier)
metoden overstyrer ikke Kjøretøy
s skrive ut()
metode. I stedet for det overbelastning den.
Du kan oppdage et forsøk på å overbelaste i stedet for å overstyre en metode ved kompileringstidspunktet ved å prefiksere en underklasses metodehode med @Overstyring
kommentar:
@Override ugyldig utskrift (streng eier) {System.out.print ("Eier:" + eier); super.print (); }
Spesifisering @Overstyring
forteller kompilatoren at den gitte metoden overstyrer en annen metode. Hvis noen forsøkte å overbelaste metoden i stedet, ville kompilatoren rapportere en feil. Uten denne kommentaren rapporterer ikke kompilatoren en feil fordi metodeoverbelastning er lovlig.
Når skal du bruke @Override
Utvikle vanen med å prefiksere overordnede metoder med @Overstyring
. Denne vanen vil hjelpe deg med å oppdage feil ved overbelastning mye raskere.