Java podporuje opětovné použití tříd prostřednictvím dědičnosti a složení. Tento dvoudílný výukový program vás naučí, jak používat dědičnost ve vašich programech Java. V části 1 se naučíte používat rozšiřuje
klíčové slovo pro odvození podřízené třídy z nadřazené třídy, vyvolání konstruktorů a metod nadřazené třídy a přepsání metod. V části 2 budete cestovat java.lang.Object
, což je nadtřída Javy, ze které dědí všechny ostatní třídy.
Chcete-li dokončit své učení o dědičnosti, nezapomeňte se podívat na můj tip Java vysvětlující, kdy použít složení vs dědičnost. Dozvíte se, proč je kompozice důležitým doplňkem dědičnosti a jak ji používat k ochraně před problémy se zapouzdřením ve vašich programech Java.
stáhnout Získat kód Stáhněte si zdrojový kód například pro aplikace v tomto výukovém programu. Vytvořil Jeff Friesen pro JavaWorld.Dědičnost Java: Dva příklady
Dědictví je programovací konstrukt, který vývojáři softwaru používají k vytvoření je-vztahy mezi kategoriemi. Dědičnost nám umožňuje odvodit konkrétnější kategorie od obecnějších. Přesnější kategorie je druh obecnější kategorie. Například běžný účet je druh účtu, na kterém můžete provádět vklady a výběry. Podobně je nákladní automobil druh vozidla používaného k přepravě velkých předmětů.
Dědičnost může sestoupit přes několik úrovní, což povede ke stále konkrétnějším kategoriím. Jako příklad ukazuje obrázek 1 osobní a nákladní vozidlo zděděné od vozidla; kombi dědí z automobilu; a popelářský vůz zděděný od kamionu. Šipky směřují od konkrétnějších „podřízených“ kategorií (nižší dolů) k méně konkrétním „nadřazeným“ kategoriím (vyšší nahoru).

Tento příklad ilustruje jedno dědictví ve kterém podřízená kategorie dědí stav a chování z jedné bezprostřední nadřazené kategorie. V porovnání, vícenásobné dědictví umožňuje podřízené kategorii zdědit stav a chování ze dvou nebo více bezprostředních nadřazených kategorií. Hierarchie na obrázku 2 ilustruje vícenásobnou dědičnost.

Kategorie jsou popsány třídami. Java podporuje jediné dědictví prostřednictvím rozšíření třídy, ve kterém jedna třída přímo dědí přístupná pole a metody z jiné třídy rozšířením této třídy. Java však nepodporuje vícenásobné dědění prostřednictvím rozšíření třídy.
Při prohlížení hierarchie dědičnosti můžete snadno zjistit vícenásobnou dědičnost podle přítomnosti diamantového vzoru. Obrázek 2 ukazuje tento vzorec v kontextu vozidla, pozemního vozidla, vodního vozidla a vznášedla.
Klíčové slovo extends
Java podporuje rozšíření třídy přes rozšiřuje
klíčové slovo. Pokud jsou přítomny, rozšiřuje
určuje vztah rodič-dítě mezi dvěma třídami. Níže používám rozšiřuje
navázat vztah mezi třídami Vozidlo
a Auto
a poté mezi Účet
a Spořící účet
:
Výpis 1. The rozšiřuje
klíčové slovo určuje vztah rodič-dítě
třída Vehicle {// deklarace členů} třída Car rozšiřuje Vehicle {// dědí přístupné členy z Vehicle // poskytuje vlastní deklarace členů} třída Účet {// deklarace členů} třída SavingsAccount rozšiřuje Účet {// dědí přístupné členy z Účtu // poskytovat vlastní prohlášení člena}
The rozšiřuje
klíčové slovo je zadáno za názvem třídy a před názvem jiné třídy. Název třídy dříve rozšiřuje
identifikuje dítě a název třídy za rozšiřuje
identifikuje rodiče. Je nemožné zadat více názvů tříd po rozšiřuje
protože Java nepodporuje vícenásobné dědictví založené na třídách.
Tyto příklady kodifikují vztahy typu is-a: Auto
je specializované Vozidlo
a Spořící účet
je specializované Účet
. Vozidlo
a Účet
jsou známé jako základní třídy, mateřské třídynebo nadtřídy. Auto
a Spořící účet
jsou známé jako odvozené třídy, dětské třídynebo podtřídy.
Závěrečné třídy
Můžete deklarovat třídu, která by neměla být rozšířena; například z bezpečnostních důvodů. V Javě používáme finále
klíčové slovo, aby se zabránilo rozšíření některých tříd. Jednoduše zadejte předponu záhlaví třídy pomocí finále
, jako v poslední třída Heslo
. Vzhledem k tomuto prohlášení kompilátor nahlásí chybu, pokud se někdo pokusí o prodloužení Heslo
.
Podřízené třídy dědí přístupná pole a metody od svých nadřazených tříd a dalších předků. Nikdy však nezdědí konstruktory. Místo toho podřízené třídy deklarují své vlastní konstruktory. Dále mohou deklarovat svá vlastní pole a metody, jak je odlišit od svých rodičů. Zvažte výpis 2.
Výpis 2. An Účet
mateřská třída
třída Účet {soukromý název řetězce; soukromá dlouhá částka; Účet (název řetězce, dlouhá částka) {this.name = name; setAmount (částka); } neplatný vklad (dlouhá částka) {this.amount + = částka; } Řetězec getName () {návratové jméno; } long getAmount () {vrácená částka; } void setAmount (dlouhé množství) {this.amount = amount; }}
Výpis 2 popisuje obecnou třídu bankovního účtu, která má název a počáteční částku a které jsou obě nastaveny v konstruktoru. Umožňuje také uživatelům provádět vklady. (Výběry můžete provádět vkladem záporných částek peněz, ale tuto možnost budeme ignorovat.) Pamatujte, že při vytváření účtu musí být nastaven název účtu.
Reprezentující hodnoty měny
počet haléřů. Můžete raději použít a dvojnásobek
nebo a plovák
ukládat peněžní hodnoty, ale to může vést k nepřesnostem. Pro lepší řešení zvažte BigDecimal
, která je součástí standardní knihovny tříd Java.
Výpis 3 představuje a Spořící účet
dětská třída, která rozšiřuje své Účet
mateřská třída.
Výpis 3. A Spořící účet
dětská třída rozšiřuje své Účet
mateřská třída
třída SavingsAccount rozšiřuje Účet {SavingsAccount (dlouhá částka) {super ("úspory", částka); }}
The Spořící účet
třída je triviální, protože nepotřebuje deklarovat další pole nebo metody. Deklaruje však konstruktor, který inicializuje pole ve svém Účet
nadtřída. Inicializace se stane, když Účet
Konstruktor se volá přes Java super
klíčové slovo následované seznamem argumentů v závorkách.
Kdy a kde volat super ()
Stejně jako tento()
musí být prvním prvkem v konstruktoru, který volá jiný konstruktor ve stejné třídě, super ()
must be the first element in a constructor that calls a constructor in its superclass. Pokud toto pravidlo porušíte, kompilátor ohlásí chybu. Kompilátor také nahlásí chybu, pokud zjistí a super ()
zavolat metodu; jen zavolat super ()
v konstruktoru.
Výpis 4 se dále rozšiřuje Účet
s Kontrolní účet
třída.
Výpis 4. A Kontrolní účet
dětská třída rozšiřuje své Účet
mateřská třída
třída CheckingAccount rozšiřuje účet {CheckingAccount (dlouhá částka) {super ("kontrola", částka); } zrušit výběr (dlouhá částka) {setAmount (getAmount () - částka); }}
Kontrolní účet
je o něco podstatnější než Spořící účet
protože deklaruje a ustoupit()
metoda. Všimněte si volání této metody na setAmount ()
a getAmount ()
, který Kontrolní účet
dědí z Účet
. Nemůžete přímo přistupovat k množství
pole v Účet
protože toto pole je deklarováno soukromé
(viz Výpis 2).
super () a konstruktor bez argumentů
Li super ()
není zadán v konstruktoru podtřídy, a pokud nadtřída nedeklaruje a žádný argument
konstruktor, pak kompilátor ohlásí chybu. Je to proto, že konstruktor podtřídy musí volat a žádný argument
konstruktor nadtřídy, když super ()
není přítomen.
Příklad hierarchie tříd
Vytvořil jsem Demo účtu
třída aplikace, která vám umožní vyzkoušet Účet
hierarchie tříd. Nejprve se podívejte na Demo účtu
zdrojový kód.
Výpis 5. Demo účtu
ukazuje hierarchii tříd účtů
třída AccountDemo {public static void main (String [] args) {SavingsAccount sa = nový SavingsAccount (10 000); System.out.println ("název účtu:" + sa.getName ()); System.out.println ("počáteční částka:" + sa.getAmount ()); sa.deposit (5000); System.out.println ("nová částka po vkladu:" + sa.getAmount ()); CheckingAccount ca = nový CheckingAccount (20000); System.out.println ("název účtu:" + ca.getName ()); System.out.println ("počáteční částka:" + ca.getAmount ()); ca. vklad (6000); System.out.println ("nová částka po vkladu:" + ca.getAmount ()); ca. výběr (3000); System.out.println ("nová částka po výběru:" + ca.getAmount ()); }}
The hlavní()
metoda v seznamu 5 nejprve ukazuje Spořící účet
, pak Kontrolní účet
. Za předpokladu Účet.java
, SavingsAccount.java
, CheckingAccount.java
, a AccountDemo.java
zdrojové soubory jsou ve stejném adresáři, proveďte některý z následujících příkazů a zkompilujte všechny tyto zdrojové soubory:
javac AccountDemo.java javac * .java
Spusťte aplikaci pomocí následujícího příkazu:
java AccountDemo
Měli byste dodržovat následující výstup:
název účtu: počáteční částka spoření: 10000 nová částka po vkladu: 15000 název účtu: kontrola počáteční částky: 20000 nová částka po vkladu: 26000 nová částka po výběru: 23000
Přepsání metody (a přetížení metody)
Podtřída může přepsat (nahradit) zděděnou metodu, aby se místo toho volala verze metody podtřídy. Přepisující metoda musí zadat stejný název, seznam parametrů a návratový typ jako přepsaná metoda. K prokázání jsem prohlásil tisk()
metoda v Vozidlo
třída níže.
Výpis 6. Deklarace a tisk()
způsob, který má být přepsán
třída Vehicle {private String make; soukromý řetězcový model; soukromý int rok; Vehicle (String make, String model, int year) {this.make = make; this.model = model; this.year = rok; } String getMake () {return make; } String getModel () {návratový model; } int getYear () {rok návratu; } void print () {System.out.println ("Make:" + make + ", Model:" + model + ", Year:" + year); }}
Dále přepíšu tisk()
v Kamion
třída.
Výpis 7. Přepsání tisk()
v Kamion
podtřída
třída Truck rozšiřuje vozidlo {soukromá dvojnásobná prostornost; Truck (značka, řetězec, model, rok výroby, dvojitá tonáž) {super (značka, model, rok); this.tonnage = prostornost; } dvojnásobek getTonnage () {návratnost; } void print () {super.print (); System.out.println ("Tonáž:" + tonáž); }}
Kamion
je tisk()
metoda má stejný název, návratový typ a seznam parametrů jako Vozidlo
je tisk()
metoda. Všimněte si také, že Kamion
je tisk()
metoda první volání Vozidlo
je tisk()
metoda předponou super.
na název metody. Často je dobré nejprve provést logiku nadtřídy a poté provést logiku podtřídy.
Volání metod nadtřídy z metod podtříd
Chcete-li volat metodu nadtřídy z převažující metody podtřídy, zadejte před název metody vyhrazené slovo super
a operátor přístupu členů. Jinak nakonec rekurzivně zavoláte přepisující metodu podtřídy. V některých případech bude podtřída maskovatsoukromé
pole nadtřídy deklarováním stejnojmenných polí. Můžeš použít super
a operátor přístupu členů k přístupu ksoukromé
pole nadtřídy.
Pro dokončení tohoto příkladu jsem výňatek a VehicleDemo
třídy hlavní()
metoda:
Truck truck = new Truck ("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 ();
Poslední řádek, truck.print ();
, volá kamion
je tisk()
metoda. Tato metoda nejprve volá Vozidlo
je tisk()
vydat značku, model a rok nákladního vozidla; poté vydá tonáž nákladního vozidla. Tato část výstupu je uvedena níže:
Značka: Ford, Model: F150, rok: 2008 Tonáž: 0,5
K zablokování přepsání metody použijte final
Občas možná budete muset deklarovat metodu, která by neměla být přepsána, z bezpečnostních nebo jiných důvodů. Můžete použít finále
klíčové slovo pro tento účel. Chcete-li zabránit přepsání, jednoduše předponu záhlaví metody finále
, jako v final String getMake ()
. Kompilátor poté nahlásí chybu, pokud se někdo pokusí přepsat tuto metodu v podtřídě.
Přetížení metody a přepsání
Předpokládejme, že jste nahradili tisk()
metoda v seznamu 7 s níže uvedenou:
void print (vlastník řetězce) {System.out.print ("Vlastník:" + vlastník); super.print (); }
Upravený Kamion
třída má nyní dva tisk()
metody: předchozí explicitně deklarovaná metoda a metoda zděděná od Vozidlo
. The neplatný tisk (majitel řetězce)
metoda nepřepíše Vozidlo
je tisk()
metoda. Místo toho přetížení to.
Můžete detekovat pokus o přetížení namísto přepsání metody v době kompilace předponou záhlaví metody podtřídy pomocí @ Přepis
anotace:
@Override void print (vlastník řetězce) {System.out.print ("Vlastník:" + vlastník); super.print (); }
Upřesnění @ Přepis
řekne kompilátoru, že daná metoda přepíše jinou metodu. Pokud se místo toho někdo pokusí přetížit metodu, kompilátor nahlásí chybu. Bez této anotace by kompilátor nehlásil chybu, protože přetížení metody je legální.
Kdy použít @Override
Rozvíjejte ve zvyku předepisovat přednostní metody @ Přepis
. Tento zvyk vám pomůže odhalit chyby přetížení mnohem dříve.