Třídu deklarujeme pomocí klíčového slova
class. Za ním následuje jméno třídy.
Jméno třídy obvykle začíná velkým písmenem. Pokud se skládá z více
slov, oddělujeme slova tak, že první písmeno každého slova je velké
(např. BarevnyObrazek). Není zvykem
používat podtržítka. Třída může obsahovat deklarace proměnných
a metod. Proměnné i metody mohou být buď třídní (statické) nebo
instanční. Instančním proměnným říkáme
instanční atributy nebo zkráceně
atributy
(angl. instance attributes nebo
fields). Třídní (statické) metody již
známe. Deklarují se pomocí klíčového slova
static. Dále si ukážeme, jak deklarovat
a používat instanční metody
(angl. instance methods).
Instanční metody se deklarují podobně jako statické. Na rozdíl od
statických metod však jejich hlavičky neobsahují klíčové slovo
static. Instanční metody často pracují
s instančními atributy.
class Moje {
int x; // instanční atribut
void vypisX() { // instanční metoda
System.out.println( x );
}
}
Od dané třídy můžeme vytvářet instance
(říkáme jim též objekty,
angl. instances či
objects). Třída je šablona, která
říká, jak budou objekty vypadat, tj. jaké budou mít atributy a
metody. V našem případě bude mít každá instance třídy
Moje jednu proměnnou typu
int.
Deklarací proměnné typu Moje zavedeme
proměnnou, do níž můžeme uložit referenci (odkaz) na instanci třídy
Moje.
Moje p;
Protože proměnné typu třída odkazují na objekty, nazývají se
referenční proměnné. Každá referenční proměnná zabírá v paměti
stejný prostor: 32 bitů v 32-bitové JVM a obvykle 64 bitů
v 64-bitové JVM. Naproti tomu objekty různých typů mají
zpravidla různou velikost. Velikost objektu je dána jeho atributy.
Objekty vytváříme pomocí klíčového slova
new:
p = new Moje();
Po provedení tohoto příkazu bude proměnná
p obsahovat odkaz na instanci třídy
Moje na haldě. K atributům a metodám
přistupujeme pomocí tečky. Volání metody zapisujeme pomocí jména
metody a kulatých závorek:
p.x = 1;
p.vypisX();
Od jedné třídy můžeme vytvořit libovolné množství instancí. Tyto
instance jsou na sobě nezávislé.
Moje p2 = new Moje();
p2.x = 2;
p2.vypisX();
Instanční metody slouží k provádění operací nad objekty daného
typu. Např. ve třídě Moje můžeme
deklarovat metodu, která zvýší hodnotu atributu
x o 1:
class Moje {
int x;
void zvysX() {
x++;
}
}
Instanční metody, stejně jako metody třídní, mohou mít parametry
a vracet hodnotu. Parametry a návratovou hodnotu stanovíme
v deklaraci metody.
class Moje {
int x;
// pričte dx k x a vrátí novou hodnotu x
int posunX( int dx ) {
x += dx;
return x;
}
}
Třída může obsahovat tzv. konstruktor
(angl. constructor). Konstruktor
vypadá jako metoda, která nemá návratový typ a má jméno shodné
se jménem třídy.
class Box {
double d;
Box() { // konstruktor
d = Math.random() * 100;
}
}
Konstruktor se volá při vytváření instance a obvykle provádí
inicializaci objektu. Volání konstruktoru zapisujeme za klíčové
slovo new. Konstruktor může mít parametry.
class Box {
double d;
Box( double dd ) { // konstruktor s parametrem
d = dd;
}
}
Má-li konstruktor parametry, musíme při vytváření objektu zadat
jejich hodnoty.
Box p = new Box( 3.5 );
K inicializaci atributu lze použít tzv.
inicializátor
(angl. initializator).
class Cislo {
int x = 1; // inicializátor
}
Inicializátor používáme pro jednoduchou inicializaci (např. výrazem,
jehož hodnota je známa při překladu). Složitější inicializace
provádíme v konstruktoru. Pokud atribut neinicializujeme
inicializátorem ani v konstruktoru, bude mít nulovou hodnotu.
Tj. hodnotu, jejíž vnitřní reprezentace je 0. Pro číselné typy je
to 0, pro typ boolean je to
false, pro typ
char znak na pozici 0 v tabulce Unicode
a pro referenční typy hodnota null.
Hodnota null znamená, že reference
neodkazuje na žádný objekt.
V jedné třídě je možné deklarovat více konstruktorů, pokud se liší
seznamem parametrů. Má-li třída alespoň dva konstruktory, říkáme
o konstruktoru, že je přetížený
(angl. overloaded).
class Cislo {
int x;
Cislo() {
x = 1;
}
Cislo( int px ) {
x = px;
}
}
Při vytváření objektu se zavolá pouze jeden konstruktor. Výběr
konstruktoru provede překladač podle skutečných parametrů.
Cislo p1 = new Cislo(); // volání konstruktoru bez parametrů
Cislo p2 = new Cislo( 97 ); // volání konstruktoru s parametrem
Vytvoření objektu zahrnuje několik kroků:
přidělení (alokace) paměti
inicializace na nulu
provedení inicializátorů
zavolání konstruktoru
Při provádění konstruktoru tedy atributy mají definovanou hodnotu.
Buď mají nulové hodnoty nebo hodnoty, které jsme jim přiřadili
pomocí inicializátorů.
Dále si ukážeme třídu Bod, která popisuje
bod v rovině:
class Bod {
int x, y;
// konstruktory
Bod( int px, int py ) {
x = px;
y = py;
}
Bod( Bod b ) {
x = b.x;
y = b.y;
}
// posune bod o dx a dy
void posun( int dx, int dy ) {
x += dx;
y += dy;
}
// vytiskne souřadnice bodu
void vytiskni() {
System.out.printf( "[%d,%d]", x, y );
}
// spočte vzdálenost bodu od počátku
double spoctiVzdalenostOdPocatku() {
return Math.sqrt( x * x + y * y );
}
}
Proměnná typu Bod obsahuje odkaz na
instanci třídy Bod. Obsah této proměnné
můžeme přiřadit do jiné proměnné stejného typu. Přitom dojde ke
zkopírování hodnoty odkazu. Odkazovaný objekt se nezmění.
Bod b1 = new Bod( 1, 2 );
Bod b2 = b1; // odkaz uložený v b1 se zkopíruje do b2
b2.vytiskni(); // vytiskne [1,2]
b1.posun( 1, 1 ); // posune bod na [2,3]
b2.vytiskni(); // vytiskne [2,3]
Proměnné b1 a b2
v tomto příkladě odkazují na stejný objekt (mají stejnou hodnotu).
Volání b2.vytiskni() je tedy totéž jako
b1.vytiskni().
Referenční typy lze použít i pro parametry metod. Při volání metody
se pak předává hodnota reference, tj. odkaz na objekt. Stejně jako
u parametrů primitivního typu jde o předávání hodnotou.
static void m1( Bod b ) {
b.posun( 1, 1 );
}
public static void main( String[] args ) {
Bod p = new Bod( 1, 2 );
m1( p );
p.vytiskni(); // vytiskne [2,3]
}
Při volání metody m1() se jako parametr
předá hodnota proměnné p. Jde o předávání
hodnotou, tj. reference z proměnné p se
zkopíruje do proměnné b. Z toho plyne,
že metoda m1() nemůže změnit hodnotu
skutečného parametru, tj. proměnné p.
Na začátku metody bude proměnná b
odkazovat na stejný objekt jako proměnná
p. Voláním metody
posun() dojde ke změně stavu tohoto
objektu (změní se hodnoty jeho atributů). Metoda
m1() tedy nemůže změnit hodnotu proměnné
p, ale může změnit objekt, na který
proměnná p odkazuje.
Parametry jsou vždy lokální v dané metodě, tj. proměnná
b je lokální v metodě
m1(). Změna hodnoty parametru v metodě
tedy neovlivní hodnotu skutečného parametru, v našem případě
proměnné p.
Pokud v deklaraci třídy neuvedeme konstruktor, překladač do třídy
automaticky vloží tzv.
implicitní konstruktor
(angl. default constructor).
Implicitní konstruktor nemá žádné parametry a má prázdné tělo.
class Bod {
int x, y;
// pokud nedeklarujeme konstruktor, překladač vloží
// implicitní konstruktor:
// Bod() { }
}
Implicitní konstruktor umožňuje vytvářet instance od tříd, které
neobsahují deklaraci konstruktoru. Jeho vkládáním nám překladač
ulehčuje práci. Pokud konstruktor nepotřebujeme, nemusíme jej
deklarovat. Překladač vloží implicitní konstruktor pouze tehdy,
pokud třída žádný konstruktor neobsahuje. Má-li třída např.
konstruktor se dvěma parametry, implicitní konstruktor se nevkládá.
Úloha 1
Doplňte metodu spoctiObsah(), která
vrátí obsah obdélníka.
Úloha 2
Doplňte konstruktor, který bude mít tři parametry. Parametry
použijte pro inicializaci atributů.
Úloha 3
Do třídy Datum doplňte deklaraci atributů
den, mesic
a rok a deklaraci konstruktoru se třemi
parametry.
Úloha 4
Do třídy RacionalniCislo doplňte dva
konstruktory. První konstruktor bude mít dva parametry typu
int a druhý konstruktor bude mít jeden
parametr typu RacionalniCislo.
Úloha 5
Určete, co bude výstupem následujícího kódu.
Úloha 6
Doplňte věty.
Úloha 7
Napište ve správném pořadí.
Otázky a odpovědi
Studentka:
Mistře, v jakém pořadí mám zapisovat deklarace atributů,
konstruktorů a metod ve třídě? Je toto pořadí významné?
Java guru:
Většinou není. Atributy, konstruktory a metody lze deklarovat
v libovolném pořadí. Inicializátory se vykonají před
konstruktorem i tehdy, jsou-li uvedeny až za ním.
class Bod {
int x = 1; // toto se provede první
Bod() {
x = y = 3; // toto třetí
}
int y = 2; // toto druhé
}
Po vytvoření instance třídy Bod
tedy budou mít atributy
x a y
hodnotu tři.
Na pořadí inicializátorů záleží, pokud jsou na sobě závislé.
class Souradnice {
double x = Math.random() * 100;
double y = x;
}
Takové situace se ovšem vyskytují poměrně zřídka. Složitější
inicializaci totiž většinou provádíme v konstruktoru. Navíc
nás překladač na nesprávné pořadí inicializátorů upozorní.
class Souradnice {
double x, y;
Souradnice() {
x = y = Math.random() * 100;
}
}
Pokud jde o vzájemné pořadí deklarací atributů, konstruktorů
a metod, zapisujeme je obvykle v tomto pořadí: atributy,
konstruktory, metody.
Studentka:
Vím, že přetížené konstruktory se mohou lišit jen pořadím
parametrů. Např.:
class Box {
Box( int x, double y ) {
System.out.println( "Box( int x, double y )" );
}
Box( double y, int x ) {
System.out.println( "Box( double y, int x )" );
}
}
Při vytváření objektu se zavolá konstruktor, jehož parametry
nejlépe odpovídají skutečným parametrům. Výběr konstruktoru
provede překladač podle typu skutečných parametrů.
Box b1 = new Box( 1, 2.5 ); // parametry int a double
Box b2 = new Box( 2.5, 1 ); // parametry double a int
Který konstruktor se ovšem zavolá, pokud budou oba parametry
typu int? Žádný konstruktor se dvěma
parametry typu int ve třídě
Box není. Existuje však implicitní
typová konverze z typu int na typ
double, takže je možné jeden
z parametrů převést na double.
Java guru:
Máš pravdu, v tomto případě přicházejí v úvahu dvě možnosti:
buď se převede první parametr na double
a zavolá se druhý konstruktor, nebo se převede druhý parametr
a zavolá se první konstruktor. Protože překladač nedokáže
rozhodnout, kterou možnost jsi měla na mysli, ohlásí chybu.
Výběr konstruktoru tak přenechá tobě.
Box b1 = new Box( 1, 2 ); // toto se nepřeloží
Box b2 = new Box( 1, 2.0 ); // takto vybereme první konstruktor
Box b3 = new Box( 1.0, 2 ); // a takto druhý
Při překladu jiných konstrukcí se překladač chová obdobně.
Vždy, když je něco nejednoznačné, ohlásí chybu a nechá
programátora, aby nejednoznačnost odstranil.