Java understøtter genbrug af klasser gennem arv og komposition. Denne tutorial i to dele lærer dig, hvordan du bruger arv i dine Java-programmer. I del 1 lærer du, hvordan du bruger strækker sig
nøgleord for at udlede en underordnet klasse fra en overordnet klasse, påberåbe sig overordnede klassekonstruktører og metoder og tilsidesætte metoder. I del 2 turnerer du java.lang.Objekt
, som er Java's superklasse, som hver anden klasse arver fra.
For at fuldføre din læring om arv skal du sørge for at tjekke mit Java-tip, der forklarer, hvornår du skal bruge komposition vs arv. Du lærer, hvorfor komposition er et vigtigt supplement til arv, og hvordan du bruger den til at beskytte mod problemer med indkapsling i dine Java-programmer.
download Hent koden Download kildekoden for eksempel applikationer i denne vejledning. Oprettet af Jeff Friesen til JavaWorld.Java-arv: To eksempler
Arv er en programmeringskonstruktion, som softwareudviklere bruger til at etablere er-et forhold mellem kategorier. Arv giver os mulighed for at udlede mere specifikke kategorier fra mere generiske. Den mere specifikke kategori er en slags den mere generiske kategori. For eksempel er en kontrolkonto en slags konto, hvor du kan foretage indbetalinger og udbetalinger. Tilsvarende er en lastbil en slags køretøj, der bruges til at trække store genstande.
Arv kan falde ned ad flere niveauer, hvilket fører til stadig mere specifikke kategorier. Som et eksempel viser figur 1 bil og lastbil arvet fra køretøjet; stationcar arve fra bil; og skraldebil arve fra lastbil. Pile peger fra mere specifikke "underordnede" kategorier (lavere ned) til mindre specifikke "overordnede" kategorier (højere op).

Dette eksempel illustrerer enkelt arv hvor en barnekategori arver tilstand og adfærd fra en umiddelbar forældrekategori. I modsætning, flere arv gør det muligt for en underkategori at arve tilstand og adfærd fra to eller flere umiddelbare forældrekategorier. Hierarkiet i figur 2 illustrerer flere arv.

Kategorier er beskrevet af klasser. Java understøtter enkelt arv igennem klasseudvidelse, hvor en klasse direkte arver tilgængelige felter og metoder fra en anden klasse ved at udvide denne klasse. Java understøtter dog ikke flere arv gennem klasseudvidelser.
Når du ser et arvshierarki, kan du nemt registrere flere arv ved tilstedeværelsen af et diamantmønster. Figur 2 viser dette mønster i sammenhæng med køretøj, landkøretøj, vandkøretøj og svævefly.
Nøgleordet udvider
Java understøtter klasseudvidelse via strækker sig
nøgleord. Når til stede, strækker sig
angiver et forhold mellem forældre og barn mellem to klasser. Nedenfor bruger jeg strækker sig
at etablere et forhold mellem klasser Køretøj
og Bil
og derefter mellem Konto
og Opsparingskonto
:
Notering 1. strækker sig
nøgleord angiver et forhold mellem forælder og barn
klasse Køretøj {// medlemserklæringer} klasse Bil udvider Køretøj {// arver tilgængelige medlemmer fra køretøj // leverer egne medlemserklæringer} klasse Konto {// medlemserklæringer} klasse SavingsAccount udvider konto {// arver tilgængelige medlemmer fra konto // giver egne medlemserklæringer}
Det strækker sig
nøgleord er angivet efter holdnavnet og før et andet holdnavn. Klassens navn før strækker sig
identificerer barnet og klassens navn efter strækker sig
identificerer forælderen. Det er umuligt at specificere flere klassenavne efter strækker sig
fordi Java ikke understøtter klassebaseret flere arv.
Disse eksempler kodificerer is-a-forhold: Bil
er en specialiseret Køretøj
og Opsparingskonto
er en specialiseret Konto
. Køretøj
og Konto
er kendt som baseklasser, forældreklasser, eller superklasser. Bil
og Opsparingskonto
er kendt som afledte klasser, børneklasser, eller underklasser.
Afsluttende klasser
Du kan muligvis erklære en klasse, der ikke bør udvides; for eksempel af sikkerhedsmæssige årsager. I Java bruger vi endelig
nøgleord for at forhindre, at nogle klasser udvides. Du skal bare prefixe en klasseoverskrift med endelig
, som i sidste klasse adgangskode
. I betragtning af denne erklæring rapporterer compileren en fejl, hvis nogen forsøger at udvide Adgangskode
.
Børneklasser arver tilgængelige felter og metoder fra deres overordnede klasser og andre forfædre. De arver dog aldrig konstruktører. I stedet erklærer børneklasser deres egne konstruktører. Desuden kan de erklære deres egne felter og metoder til at skelne dem fra deres forældre. Overvej liste 2.
Notering 2. An Konto
forældreklasse
klassekonto {privat strengnavn; privat langt beløb Konto (strengnavn, langt beløb) {this.name = navn; setAmount (beløb); } ugyldigt depositum (langt beløb) {this.amount + = beløb; } String getName () {return name; } lang getAmount () {returbeløb; } ugyldigt setAmount (langt beløb) {this.amount = beløb; }}
Fortegnelse 2 beskriver en generisk bankkontoklasse, der har et navn og et indledende beløb, som begge er angivet i konstruktøren. Det lader også brugerne foretage indbetalinger. (Du kan foretage udbetalinger ved at deponere negative pengebeløb, men vi ignorerer denne mulighed.) Bemærk, at kontonavnet skal indstilles, når en konto oprettes.
Repræsenterer valuta værdier
antal øre. Du foretrækker måske at bruge en dobbelt
eller a flyde
at gemme monetære værdier, men at gøre det kan føre til unøjagtigheder. Overvej for en bedre løsning BigDecimal
, som er en del af Java's standard klassebibliotek.
Liste 3 præsenterer a Opsparingskonto
børneklasse, der udvider sin Konto
forældreklasse.
Liste 3. A Opsparingskonto
børneklasse udvider sin Konto
forældreklasse
klasse SavingsAccount udvider konto {SavingsAccount (langt beløb) {super ("besparelser", beløb); }}
Det Opsparingskonto
klasse er trivielt, fordi det ikke behøver at erklære yderligere felter eller metoder. Det erklærer dog en konstruktør, der initialiserer felterne i sin Konto
superklasse. Initialisering sker når Konto
konstruktør kaldes via Java super
nøgleord efterfulgt af en parentes-argumentliste.
Hvornår og hvor skal man kalde super ()
Ligesom det her()
skal være det første element i en konstruktør, der kalder en anden konstruktør i samme klasse, super()
skal være det første element i en konstruktør, der kalder en konstruktør i sin superklasse. Hvis du bryder denne regel, rapporterer compileren en fejl. Compileren rapporterer også en fejl, hvis den registrerer en super()
indkald en metode kun altid ringe super()
i en konstruktør.
Liste 4 udvides yderligere Konto
med en Checkkonto
klasse.
Fortegnelse 4. A Checkkonto
børneklasse udvider sin Konto
forældreklasse
klasse CheckingAccount udvider konto {CheckingAccount (langt beløb) {super ("kontrol", beløb); } ugyldig tilbagetrækning (langt beløb) {setAmount (getAmount () - beløb); }}
Checkkonto
er lidt mere omfattende end Opsparingskonto
fordi det erklærer en trække sig tilbage ()
metode. Bemærk denne metodes opkald til setAmount ()
og getAmount ()
, hvilken Checkkonto
arver fra Konto
. Du kan ikke direkte få adgang til beløb
felt i Konto
fordi dette felt er erklæret privat
(se liste 2).
super () og konstruktøren uden argument
Hvis super()
er ikke specificeret i en underklassekonstruktør, og hvis superklassen ikke erklærer en ikke-argument
konstruktør, så rapporterer compileren en fejl. Dette skyldes, at underklassekonstruktøren skal kalde en ikke-argument
superklasse konstruktør hvornår super()
er ikke til stede.
Eksempel på klassehierarki
Jeg har oprettet en AccountDemo
applikationsklasse, der giver dig mulighed for at prøve Konto
klassehierarki. Se først på AccountDemo
kildekode.
Liste 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 ("startbeløb:" + sa.getAmount ()); sa. deponering (5000); System.out.println ("nyt beløb efter indbetaling:" + sa.getAmount ()); CheckingAccount ca = ny CheckingAccount (20000); System.out.println ("kontonavn:" + ca.getName ()); System.out.println ("startbeløb:" + ca.getAmount ()); ca. deponering (6000); System.out.println ("nyt beløb efter indbetaling:" + ca.getAmount ()); ca. træk (3000); System.out.println ("nyt beløb efter tilbagetrækning:" + ca.getAmount ()); }}
Det hoved ()
metode i Listing 5 demonstrerer først Opsparingskonto
, derefter Checkkonto
. Antager Konto.java
, SavingsAccount.java
, CheckingAccount.java
og AccountDemo.java
kildefiler er i samme bibliotek, udfør en af følgende kommandoer for at kompilere alle disse kildefiler:
javac AccountDemo.java javac * .java
Udfør følgende kommando for at køre applikationen:
java AccountDemo
Du skal overholde følgende output:
kontonavn: besparelse startbeløb: 10000 nyt beløb efter indbetaling: 15000 kontonavn: kontrol af oprindeligt beløb: 20000 nyt beløb efter indbetaling: 26000 nyt beløb efter udbetaling: 23000
Metodeoverstyring (og metodeoverbelastning)
En underklasse kan tilsidesætte (erstat) en nedarvet metode, så subklassens version af metoden kaldes i stedet. En overordnet metode skal angive samme navn, parameterliste og returtype som den metode, der tilsidesættes. For at demonstrere har jeg erklæret en Print()
metode i Køretøj
klasse nedenfor.
Liste 6. Erklæring om a Print()
metode, der skal tilsidesættes
klasse køretøj {privat streng mærke; privat streng model; privat int år; Vehicle (String make, String model, int year) {this.make = make; this.model = model; dette.år = år; } String getMake () {return make; } String getModel () {returmodel; } int getYear () {returår; } ugyldig udskrivning () {System.out.println ("Make:" + make + ", Model:" + model + ", Year:" + year); }}
Dernæst tilsidesætter jeg Print()
i Lastbil
klasse.
Fortegnelse 7. Tilsidesættelse Print()
i en Lastbil
underklasse
klasse Truck udvider køretøj {privat dobbelt tonnage; Lastbil (streng mærke, streng model, int år, dobbelt tonnage) {super (mærke, model, år); denne.tonnage = tonnage; } dobbelt getTonnage () {retur tonnage; } ugyldig udskrivning () {super.print (); System.out.println ("Tonnage:" + tonnage); }}
Lastbil
's Print()
metoden har samme navn, returtype og parameterliste som Køretøj
's Print()
metode. Bemærk også det Lastbil
's Print()
metode første opkald Køretøj
's Print()
metode ved at prefikse super.
til metodens navn. Det er ofte en god ide at udføre superklasselogikken først og derefter udføre subklasselogikken.
Opkald til superklassemetoder fra underklassemetoder
For at kalde en superklassemetode fra den overordnede underklassemetode skal du prefikse metodens navn med det reserverede ord super
og medlemsadgangsoperatøren. Ellers vil du ende med at rekursivt kalde underklassens overordnede metode. I nogle tilfælde maskerer en underklasse ikkeprivat
superklassefelter ved at erklære felter med samme navn. Du kan bruge super
og medlemsadgangsoperatøren for at få adgang til den ikke-privat
superklasse felter.
For at fuldføre dette eksempel har jeg uddraget a VehicleDemo
klassens hoved ()
metode:
Lastbil = ny lastbil ("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 sidste linje, truck.print ();
, opkald lastbil
's Print()
metode. Denne metode kalder først Køretøj
's Print()
at levere truckens mærke, model og årgang så leverer den lastbilens tonnage. Denne del af output er vist nedenfor:
Mærke: Ford, Model: F150, Årgang: 2008 Tonnage: 0.5
Brug endelig til at blokere metode tilsidesættelse
Lejlighedsvis kan det være nødvendigt at deklarere en metode, der af sikkerhed eller af en anden grund ikke bør tilsidesættes. Du kan bruge endelig
nøgleord til dette formål. For at forhindre tilsidesættelse skal du blot foretage en metodeoverskrift med endelig
, som i endelig String getMake ()
. Compileren rapporterer derefter en fejl, hvis nogen forsøger at tilsidesætte denne metode i en underklasse.
Metodeoverbelastning vs tilsidesættelse
Antag at du udskiftede Print()
metode i liste 7 med nedenstående:
ugyldig udskrivning (streng ejer) {System.out.print ("Ejer:" + ejer); super.print (); }
Den modificerede Lastbil
klasse har nu to Print()
metoder: den foregående eksplicit deklarerede metode og metoden nedarvet fra Køretøj
. Det ugyldigt print (streng ejer)
metoden tilsidesætter ikke Køretøj
's Print()
metode. I stedet for det overbelastning det.
Du kan registrere et forsøg på at overbelaste i stedet for at tilsidesætte en metode på kompileringstidspunktet ved at prefikse en underklasses metodeoverskrift med @Override
kommentar:
@Override ugyldigt print (streng ejer) {System.out.print ("Ejer:" + ejer); super.print (); }
Specificering @Override
fortæller kompilatoren, at den givne metode tilsidesætter en anden metode. Hvis nogen forsøgte at overbelaste metoden i stedet, rapporterede compileren en fejl. Uden denne kommentar ville compileren ikke rapportere en fejl, fordi metodeoverbelastning er lovlig.
Hvornår skal jeg bruge @Override
Udvikle en vane med at prefixere overordnede metoder med @Override
. Denne vane hjælper dig med at opdage overbelastningsfejl meget hurtigere.