Obsah
Pojmy: třída, objekt
Deklarace a definice tříd, jejich vlastnosti (proměnné, metody)
Vytváření objektů (deklarace sama objekt nevytvoří...), proměnné odkazující na objekt
Jmenné konvence - jak tvořit jména tříd, proměnných, metod
Použití objektů, volání metod, přístupy k proměnným
Modifikátory přístupu/viditelnosti (public, protected...)
Konstruktory (dotvoří/naplní prázdný objekt)
Přetěžování metod (dvě metody se stejným názvem a jinými parametry)
Třída (také poněkud nepřesně zvaná objektový typ) představuje skupinu objektů, které nesou stejné vlastnosti
"stejné" je myšleno kvalitativně, nikoli kvantitativně, tj.
Objekt je jeden konkrétní jedinec (reprezentant, entita) příslušné třídy
pro konkrétní objekt nabývají vlastnosti deklarované třídou konkrétních hodnot
Objekt panProfesor
typu
Clovek
má vlastnost
jmeno
s hodnotou
"Václav Klaus"
.
Vlastnosti objektů - proměnné i metody - je třeba deklarovat.
viz Sun Java Tutorial / Trail: Learning the Java Language: Lesson: Classes and Inheritance
deklarujme třídu objektů - lidí
public class Clovek {
protected String jmeno;
protected int rokNarozeni;
public Clovek(String j, int rN) {
jmeno = j;
rokNarozeni = rN;
}
public void vypisInfo() {
System.out.println("Clovek:");
System.out.println("Jmeno="+jmeno);
System.out.println("Rok narozeni="+rokNarozeni);
}
}
Použijme ji v programu -
vytvořme instanci - objekt - typu
Clovek
vypišme informace o něm
Mějme deklarovánu třídu Clovek
Metoda main v následujícím programu:
vytvoří 2 lidi (pomocí new Clovek)
zavolá jejich metody
public class TestLidi {
public static void main(String[] args) {
Clovek ales = new Clovek("Ales Necas", 1966);
Clovek beata = new Clovek("Beata Novakova", 1970);
ales.vypisInfo();
beata.vypisInfo();
}
}
Tedy: vypíší se informace o obou vytvořených objektech - lidech.
Ve výše uvedeném programu znamenalo na řádku:
Clovek ales = new Clovek("Ales Necas", 1966);
Clovek ales: pouze deklarace (tj. určení typu)
proměnné ales - bude typu
Clovek.
ales = new Clovek ("Ales Necas", 1966):
vytvoření objektu Clovek se jménem
Ales
Necas
.
Lze napsat zvlášť do dvou řádků nebo (tak jak jsme to udělali) na řádek jeden.
Ve výše uvedených příkladech jsme objekty vytvářeli voláními
new Clovek(...) bezděčně jsme tak použili
jsou instance "své" třídy
vytváříme je operátorem new - voláním
konstruktoru
vytvořené objekty ukládáme do proměnné stejného typu (nebo typu předka či implementovaného rozhraní - o tom až později)
Položky jmeno
a
rokNarozeni
v předchozím příkladu jsou proměnné objektu
Clovek
.
Jsou deklarovány v těle deklarace třídy Clovek.
Výše uvedená proměnná rokNarozeni
měla datový
typ int (32bitové celé číslo). Tedy:
proměnná takového typu nese jednu hodnotu typu celé číslo (v rozsahu -2^31.. 2^31-1);
nese-li jednu hodnotu, pak se jedná o tzv. primitivní datový typ.
Kromě celých čísel int nabízí Java celou řadu dalších primitivních datových typů. Primitivní typy jsou dané napevno, programátor je jen používá, nedefinuje.
Tam, kde nestačí diskrétní hodnoty (tj. primitivní typy), musíme použít typy složené, objektové.
Na jméno (identifikátor) proměnné sice Java neklade žádná speciální omezení (tedy mimo omezení platná pro jakýkoli identifikátor), ale přesto bývá velmi dobrým zvykem dodržovat při pojmenovávání následující pravidla (blíže viz podrobný rozbor na FIXME):
jména začínají malým písmenem
nepoužíváme diakritiku (problémy s editory, přenositelností a kódováním znaků)
(raději ani český jazyk, angličtině rozumí "každý")
je-li to složenina více slov, pak je nespojujeme podtržítkem, ale další začne velkým písmenem (tzv. "CamelCase")
je identifikátor se správně (vhodně) utvořeným jménem, zatímco:
není vhodný identifikátor proměnné (začíná velkým písmenem)
Dodržování těchto jmenných konvencí je základem psaní srozumitelných programů a bude vyžadováno, sledováno a hodnoceno v odevzdávaných úlohách i písemkách.
Proměnné objektu odkazujeme pomocí "tečkové notace":
public class TestLidi2 {
public static void main(String[] args) {
...
Clovek ales = new Clovek("Ales Necas", 1966); // vytvoření objektu ...
System.out.println(ales.jmeno); // přístup k (čtení) jeho proměnné ...
ales.jmeno = "Aleš Novák"; // modifikace (zápis do) jeho proměnné
}
}
Přístup k proměnným (i metodám) může být řízen uvedením tzv. modifikátorů před deklaraci prvku, viz výše:
// protected = přístup pouze z třídy ve stejném balíku nebo z podtřídy:
protected String jmeno;
Modifikátorů je více typů, nejběžnéjší jsou právě zmíněné modifikátory přístupu (přístupových práv)
Objektů (tzv. instancí) stejného typu (tj. stejné třídy) si můžeme postupně vytvořit více:
public class TestLidi3 {
public static void main(String[] args) {
...
Clovek ales = new Clovek("Ales Necas", 1966); // vytvoření prvniho objektu
Clovek petr = new Clovek("Petr Svoboda", 1968); // vytvoření druheho objektu ...
System.out.println(ales.jmeno); // přístup k (čtení) proměnné - prvnímu objektu
System.out.println(petr.jmeno); // přístup k (čtení) proměnné - druhému objektu
}
}
Existují tady dva objekty, každý má své (obecně různé) hodnoty proměnných - např. jsou různá jména obou lidí.
Nad existujícími (vytvořenými) objekty můžeme volat jejich metody. Metoda je:
podporgram (funkce, procedura), který primárně pracuje s proměnnými "mateřského" objektu,
může mít další parametry
může vracet hodnotu podobně jako v Pascalu funkce.
Každá metoda se musí ve své třídě deklarovat.
V Javě neexistují metody deklarované mimo třídy (tj. Java nezná žádné "globální" metody).
Výše uvedená třída Clovek měla metodu na výpis informací o daném objektu/člověku:
public class Clovek {
protected String jmeno;
protected int rokNarozeni;
public Clovek(String j, int rN) {
jmeno = j;
rokNarozeni = rN;
}
// Metoda vypisInfo() na výpis informací o člověku:
public void vypisInfo() {
System.out.println("Clovek:");
System.out.println("Jmeno="+jmeno);
System.out.println("Rok narozeni="+rokNarozeni);
}
}
Samotnou deklarací (napsáním kódu) metody se žádný kód neprovede.
Chceme-li vykonat kód metody, musíme ji zavolat.
Volání se realizuje (tak jako u proměnných) "tečkovou notací", viz dále.
Volání lze provést, jen je-li metoda z místa volání přístupná - "viditelná". Přístupnost regulují pdobně jako u proměnných modifikátory přístupu.
Vracíme se k prvnímu příkladu: vytvoříme dva lidi a zavoláme postupně jejich metodu
public class TestLidi {
public static void main(String[] args) {
Clovek ales = new Clovek("Ales Necas", 1966);
Clovek beata = new Clovek("Beata Novakova", 1970);
ales.vypisInfo(); // volání metody objektu ales
beata.vypisInfo(); // volání metody objektu beata
}
}
V deklaraci metody uvádíme v její hlavičce tzv. formální parametry.
modifikatorytypVraceneHodnoty
nazevMetody
(
seznamFormalnichParametru) {
tělo (výkonný kód) metody}
seznamFormalnichParametru je tvaru:
typParametru nazevFormalnihoParametru, ...
Podobně jako v jiných jazycích parametr představuje v rámci metody lokální proměnnou.
Při volání metody jsou f. p. nahrazeny skutečnými parametry.
Hodnoty primitivních typů - čísla, logické hodnoty, znaky
Hodnoty objektových typů - všechny ostatní (tj. vč. všech uživatelem definovaných typů)
se předávají odkazem, tj. do lokální proměnné metody se nakopíruje odkaz na objekt - skutečný parametr
Pozn: ve skutečnosti se tedy parametry vždy předávají hodnotou, protože v případě objektových parametrů se předává hodnota odkazu na objekt - skutečný parametr.
V Javě tedy nemáme jako programátoři moc na výběr, jak parametry předávat
...
public void zakric(int kolikrat) {
System.out.println("Kricim " + kolikrat + "krat UAAAA!");
}
...
...
zakric(10);
...
Kricim 10krat UAAAA!
Následující třída Ucet modeluje jednoduchý bankovní účet s možnostmi:
public class Ucet {
// stav (zustatek) penez uctu
protected double zustatek;
public void pridej(double castka) {
zustatek += castka;
}
public void vypisZustatek() {
System.out.println(zustatek);
}
public void prevedNa(Ucet kam, double castka) {
zustatek -= castka;
kam.pridej(castka);
}
}
Metoda prevedNa pracovat nejen se svým "mateřským" objektem, ale i s objektem kam předaným do metody... opět přes tečkovou notaci.
...
public static void main(String[] args) {
Ucet petruvUcet = new Ucet();
Ucet ivanuvUcet = new Ucet();
petruvUcet.pridej(100);
ivanuvUcet.pridej(220);
petruvUcet.prevedNa(ivanuvUcet, 50);
petruvUcet.vypisZustatek();
ivanuvUcet.vypisZustatek();
}
Kód metody skončí, tj. předá řízení zpět volající metodě (nebo
systému - v případě startovní metody main
),
jakmile
dokončí poslední příkaz v těle metody nebo
dospěje k příkazu return
Metoda může při návratu vrátit hodnotu - tj. chovat se jako funkce (ve pascalském smyslu):
Pozn.: I když metoda něco vrátí, my to nemusíme použít, ale je to trochu divné...
Co a k čemu jsou konstruktory?
Konstruktury jsou speciální metody volané při vytváření nových instancí dané třídy.
Typicky se v konstruktoru naplní (inicializují) proměnné objektu.
Konstruktory lze volat jen ve spojení s operátorem
new
k vytvoření nové instance třídy - nového
objektu, evt. volat z jiného konstruktoru
public class Clovek {
protected String jmeno;
protected int rokNarozeni;
// konstruktor se dvěma parametry
// - inicializuje hodnoty proměnných ve vytvořeném objektu
public Clovek(String j, int rN) {
jmeno = j;
rokNarozeni = rn;
}
...
}
Příklad využití tohoto konstruktoru:
...
Clovek pepa = new Clovek("Pepa z Hongkongu", 1899);
...
Toto volání vytvoří objekt pepa a naplní ho jménem a rokem narození.
Jak je psát a co s nimi lze dělat?
nemají návratový typ (ani void
-
to už vůbec ne!!!)
mohou mít parametry
mohou volat konstruktor rodičovské třídy - ale jen jako svůj první příkaz
Ve třídě Ucet
přetížíme metodu
prevedNa
.
public void prevedNa(Ucet u) {
u.pridej(zustatek);
zustatek = 0;
}
koexistují dvě různé metody se stejným názvem, ale jinými parametry.
Pozn: I když jsou to teoreticky dvě úplně různé metody, pak když už se jmenují stejně, měly by dělat něco podobného.
Je ale otázka, zdali převod celého zůstatku raději nenapsat jako nepřetíženou, samostatnou metodu, např.:
public void prevedVseNa(Ucet u) {
prevedNa(u, zustatek);
}
Je to o něco instruktivnější, ale přibude další identifikátor - název metody - k zapamatování.
Což může být výhoda (je to výstižné) i nevýhoda (musíme si pamatovat další).
Deklarace proměnné objektového typu ještě žádný objekt nevytváří.
To se děje až příkazem - operátorem -
new.
V následující ukázce vytvoříme dva účty.
Odkazy na ně budou primárně v proměnných petruvUcet a ivanuvUcet.
V proměnné u nebude primárně odkaz na žádný účet.
Pak do ní přiřadíme (u = petruvUcet;) odkaz
na objekt skrývající se pod odkazem
petruvUcet.
Od této chvíle můžeme s účtem petruvUcet manipulovat přes odkaz (proměnnou) u.
Což se také děje: u.prevedNa(ivanuvUcet,
50);
...
public static void main(String[] args) {
Ucet petruvUcet = new Ucet();
Ucet ivanuvUcet = new Ucet();
Ucet u;
petruvUcet.pridej(100);
ivanuvUcet.pridej(220);
u = petruvUcet;
u.prevedNa(ivanuvUcet, 50); // odečte se z Petrova účtu
petruvUcet.vypisZustatek(); // vypíše 50
ivanuvUcet.vypisZustatek();
}
Metoda může vracet odkaz na objekt, nad nímž je volána pomocí
Příklad - upravený Ucet
s metodou
prevedNa
vracející odkaz na sebe
public class Ucet {
float zustatek;
public void pridej(float castka) {
zustatek += castka;
}
public void vypisZustatek() {
System.out.println(zustatek);
}
public Ucet prevedNa(Ucet u, float castka) {
zustatek -= castka; // nebo také vhodné je: pridej(-castka);
u.pridej(castka);
return this;
}
}
Vracení odkazu na sebe (tj. na objekt, na němž se metoda volala) lze s výhodou využít k "řetězení" volání:
...
public static void main(String[] args) {
Ucet petruvUcet = new Ucet();
Ucet ivanuvUcet = new Ucet();
Ucet igoruvUcet = new Ucet();
petruvUcet.pridej(100);
ivanuvUcet.pridej(100);
igoruvUcet.pridej(100);
// budeme řetězit volání:
petruvUcet.prevedNa(ivanuvUcet, 50).prevedNa(igoruvUcet, 20);
petruvUcet.vypisZustatek(); // vypíše 30
ivanuvUcet.vypisZustatek(); // vypíše 150
igoruvUcet.vypisZustatek(); // vypíše 120
}
Dosud jsme zmiňovali proměnné a metody (tj. souhrnně prvky - members) objektu.
Lze deklarovat také metody a proměnné patřící celé
třídě, tj. skupině všech objektů daného typu. Takové metody a
proměnné nazýváme statické a označujeme
v deklaraci modifikátorem static
Představme si, že si budeme pamatovat, kolik lidí se nám během chodu programu vytvořilo a vypisovat tento počet.
Budeme tedy potřebovat do třídy Clovek doplnit:
jednu proměnnou pocetLidi společnou pro celou třídu Clovek - každý člověk ji při svém vzniku zvýší o jedna.
jednu metodu kolikMamLidi, která vrátí počet dosud vytvořených lidí.
public class Clovek {
protected String jmeno;
protected int rokNarozeni;
protected static int pocetLidi = 0;
public Clovek(String j, int rN) {
jmeno = j;
rokNarozeni = rn;
pocetLidi++;
}
...
public static int kolikMamLidi() {
return pocetLidi;
}
...
}
Pozn: Všimněte si v obou případech modifikátoru/klíčového slova static.