Programovací jazyk Java - přednáška č. 2
1z7
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/Java_02/index.html
Programovací jazyk Java 2. přednáška
Obsah Zápisy literálů – znakových hodnot a řetězců
Idenfitikátory, komentáře
Řídicí struktury
Vytváření a rušení objektů, zapouzdření, konstruktory
Organizace tříd do balíčků
Statické metody a statické proměnné
Proměnné z pohledu místa deklarace
Statické a nestatické inicializační bloky
Modifikátory přístupnosti tříd, proměnných a metod Dědičnost
Předávání skutečných parametrů metodám
Pole jako referenční datový typ
Zápisy literálů – znakových hodnot a řetězců char start = 's';
char ž = '\u017E'; // proměnná ž obsahuje znak ž.
String kůň = "kůň";
String kůň = "K\u016F\u0148";
Idenfitikátory, komentáře Viz Identifikátory a komentáře
Řídicí struktury Viz If, Switch, While, Do...While, For, Break, Continue Ternární operátor: Syntaxe: L-hodnota = výraz ? výraz1 : výraz2;
Supluje if-then-else
Příklad: int minimum = a < b ? a : b;
Třída a vytvoření objektu Třída je základním stavebním kamenem programu v objektově orientovaném programovacím jazyce.
Třída definuje datové složky (= atributy), ve kterých je uložen stav objektu a metody (tzn. podprogramy), které manipulují s atributy a tím mění stav objektu.
Objekt je struktura, která je vytvořena podle vzoru třídy. Často se říká, že objekt je instancí třídy.
Pro vytvoření objektu musíme deklarovat referenční proměnnou typu třída pomocí operátoru new nechat vytvořit v paměti objekt a získanou referenci na něj přiřadit do připravené proměnné.
Podle jednoho vzoru (třídy) lze vytvořit libovolné množství objektů, které mají sice stejnou strukturu, ale jinak jsou na sobě navzájem nezávislé a tudíž mohou mít různé stavy (tzn. obsahy členských proměnných – atributů).
Příklad třídy: class Obdelnik { int sirka; int vyska; int obvod() { int pom; pom = 2 * (sirka + vyska); return pom; } int obsah() { return (sirka * vyska); } }
14.1.2015 22:57
Programovací jazyk Java - přednáška č. 2
2z7
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/Java_02/index.html
Příklad použití třídy: ... Obdelnik obd = new Obdelnik(); obd.vyska = 5; obd.sirka = 3; int obvod = obd.obvod()); ...
Zapouzdření (encapsulation) Základní pojmy: Rozhraní třídy = množina informací, které o sobě třída zveřejňuje. Implementace je způsob, jak je třída naprogramována, tzn. jak jednotlivé metody dělají to, co dělají.
Do rozhraní bychom měli zařazovat jen to, co ostatní části programu opravdu musí o třídě vědět.
Můžeme například zamezit přímému přístupu k proměnným nebo přímému volání některých metod.
Objekt by měl být černá skříňka s jasně zdokumentovaným rozhraním, tzv. jakýmsi návodem na použití: jak lze objekt vytvořit, co a jak s ním lze dělat.
Zamezení přímého přístupu k proměnné se provádí uvedením modifikátoru přístupu (klíčového slova) private v deklaraci proměnné před názvem typu, např. private int delka;
Zamezení přímého přístupu k metodě se provádí uvedením modifikátoru přístupu private v hlavičce metody před názvem návratového typu, např. private int getName();
Když se zapouzdření projeví jako příliš silné, lze téměř vždy „povolit“. Zpřísnit naopak nelze prakticky nikdy, neboť nevíme, kdo dříve povolené rozhraní již používá.
Příklad – třída se zapouzdřenou metodou a proměnnými: class Osoba { private int vyska; private double hmotnost; private double bmi; private void setBMI(int v, double m) { bmi = m / (v * v); } void setUdaje(int v, double m) { if ((v >= 0) && (m > 0)) { vyska = v; hmotnost = m; setBMI(v, m); } } }
Příklad – povolené a nepovolené použití třídy Osoba v jiné třídě: Osoba o = new Osoba(); o.setUdaje(180, 75); // lze o.vyska = -10; // nelze o.setBMI(-165, 2); // nelze
Při pokusu o kompilaci hlásí překladač 2 chyby: vyska has private access in Osoba setBMI(int,double) has private access in Osoba
Otázka: Které z následujících úprav lze provést ve třídě, jejíž rozhraní již bylo zveřejněno? odebrat veřejnou proměnnou odebrat soukromou (private) proměnnou odebrat veřejnou metodu odebrat soukromou (private) metodu změnit implementaci (tzn. tělo) veřejné metody změnit implementaci (tzn. tělo) soukromé (private) metody změnit rozhraní (tzn. hlavičku) veřejné metody, např. typy parametrů nebo návratový typ změnit rozhraní (tzn. hlavičku) soukromé (private) metody, např. typy parametrů nebo návratový typ přetížit veřejnou metodu přetížit soukromou (private) metodu
Konstruktor Konstruktor je metoda, která v paměti vytvoří objekt jakožto instanci třídy, provede inicializaci proměnných (tzn. nastaví jejich počáteční hodnoty) a případně další akce spojené se vznikem objektu.
Konstruktor vrací referenci (odkaz) na místo v paměti, kde je objekt umístěn.
Název konstruktoru je shodný s názvem třídy, jejíž instanci tento konstruktor vytváří.
Ve výše uvedeném příkladu jsme vytvářeli objekt o jako instanci třídy Osoba takto: Osoba o = new Osoba();
Osoba() je zde voláním speciální metody – konstruktoru.
Metodu Osoba() jsme sice neimplementovali, ale v takovém případě Java volá implicitní konstruktor, který vytvoří objekt a nastaví hodnotu všech číselných proměnných na 0, resp. 0.0 (typ boolean na hodnotu false , typ char na hodnotu '\u0000', referenční typy na hodnotu null).
Můžeme se však rozhodnout vytvořit vlastní (explicitní) konstruktor, např. kvůli inicializaci proměnných instance určitými hodnotami nebo za účelem automatického provedení určitých akcí při vytváření instance.
Jakmile jednou vytvoříme libovolný konstruktor s parametry, pak překladač nevytvoří implicitní konstruktor! Budeme-li tedy současně potřebovat konstruktor bez parametrů, musíme si jej vytvořit sami (viz níže).
14.1.2015 22:57
Programovací jazyk Java - přednáška č. 2
3z7
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/Java_02/index.html
Vytvoření vlastního konstruktoru class Usecka { private int d; // proměnná instance private final static int IMPLIC_DELKA = 5; Usecka () { // konstruktor d = 5; } Usecka (int delka) { d = delka; }
// konstruktor
void zadejDelku (int delka) { d = delka; }
// metoda instance
int delka () { // metoda instance return d; } void vytvorObjekty() { // metoda instance Usecka prvni = new Usecka(); Usecka druha = new Usecka(3); System.out.println("Delka prvni usecky je " + prvni.delka() + "."); System.out.println("Delka druhe usecky je " + druha.delka() + "."); prvni.zadejDelku(1); System.out.println("Delka prvni usecky je nyni " + prvni.delka() + "."); } }
Metoda vytvorObjekty() vypíše
Delka prvni usecky je 5. Delka druhe usecky je 3. Delka prvni usecky je 1.
Využití this pro přístup k proměnným Klíčové slovo this můžeme použít uvnitř metody pro přístup k proměnným instance, mají-li stejný název jako formální parametry metody
Konstruktor z předchozího příkladu by tedy mohl vypadat také takto: class Usecka { private int d; Usecka (int d) { this.d = d; } }
Využití this pro přístup ke konstruktoru Klíčové slovo this má ještě speciální využití v přetížených konstruktorech. Pomocí něj (bez tečky, ale se závorkami) může konstruktor vyvolat jiný konstruktor stejné třídy. Volání this() se vztahuje na ten konstruktor, jehož parametry vyhovují co do počtu a pořadí typů.
Příklad: mám-li konstruktor s parametrem Usecka (int delka) { d = delka; }
// konstruktor
pak namísto Usecka () { d = IMPLIC_DELKA; }
mohu použít Usecka () { this(IMPLIC_DELKA); }
Vztahy mezi metodami ve třídě Metody instance mohou libovolně volat další metody instancí téže třídy. To znamená, že metody jedné třídy jsou na stejné úrovni a neexistuje mezi nimi žádný hierarchický vztah.
Konstruktor třídy může volat všechny metody téže třídy a může volat i libovolný jiný konstruktor téže třídy.
Metoda smí konstruktor volat pouze prostřednictvím operátoru new.
Rušení objektů Objekty vytváříme pomocí new. Jak je ale zrušit?
Nepotřebné objekty automaticky ruší garbage collector.
Jak se pozná, že je objekt nepotřebný? Neexistuje na něj žádný odkaz z existujících referenčních proměnných.
Zneplatnit odkaz můžeme například takto: Obdelnik obd = new Obdelnik (3, 5); obd = new Obdelnik (7, 10);
nebo takto: Obdelnik obd = new Obdelnik (3, 5); obd = null;
14.1.2015 22:57
Programovací jazyk Java - přednáška č. 2
4z7
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/Java_02/index.html
Organizace tříd do balíčků Třídy a rozhraní organizujeme do balíčků (packages), podobně jako související soubory na pevném disku organizujeme do adresářů.
V balíčku jsou vždy umístěny související třídy. Co znamená související? třídy, jejichž objekty spolupracují – Člověk, Úřad
třídy na podobné úrovni abstrakce – Chovatel, DomácíZvíře
třídy ze stejné části reality – ChovatelPsů, Pes
Balíčky obvykle organizujeme do hierarchií, např.:
svet
svet.chovatelstvi
svet.chovatelstvi.psi
svet.chovatelstvi.morcata
POZOR!!! Třídy „podbalíčku“ (např. svet.chovatelstvi.psi) nejsou současně třídami „rodičovského“ balíčku (svet.chovatelstvi). Hierarchie balíčků má význam spíše pro srozumitelnost a logické členění.
Příslušnost třídy k balíčku deklarujeme syntaxí package názevbalíčku;, kterou uvádíme obvykle jako první deklaraci ve zdrojovém souboru třídy (rozhraní).
Příslušnost k balíčku musíme současně potvrdit správným umístěním zdrojového souboru do adresářové struktury. Např. zdrojový soubor třídy Pes umístíme do podadresáře svet\chovatelstvi\psi.
Neuvedeme-li příslušnost k balíčku, stane se třída součástí implicitního balíčku – to však nelze pro jakékoli větší a/nebo znovupoužívané třídy či dokonce programy doporučit.
Balíčky pojmenováváme podle svého URL a dodržujeme hierarchii vycházející z URL. Používáme opačný zápis oproti klasickým URL, za doménou pokračujeme dalšími upřesňujícími názvy (např. podle projektů a jejich částí). Názvy píšeme malými písmeny.
Ve cvičeních tomto předmětu budeme pojmenovávat balíčky podle tohoto vzoru cz.mendelu.pef.pjj.xnovak.cv1, cz.mendelu.pef.pjj.xnovak.du1, cz.mendelu.pef.pjj.xnovak.projekt.
Chceme-li v programu použít třídu z jiného balíčku, než aktuálního a java.lang, musíme před hlavičku třídy vložit příkaz pro import: import názevbalíčku.názevtřídy; např. import java.io.File; import cz.mendelu.pef.pjj.xnovak.cv1.Zamestnanec;
případně můžeme jedním příkazem načíst všechny třídy balíčku: import import java.io.*; import cz.mendelu.pef.pjj.xnovak.cv1.*;
Shrnutí – ukázka celé třídy: package cz.mendelu.pef.pjj.xnovak.cv2; import cz.mendelu.pef.pjj.xnovak.cv1.*; import java.io.File; class Xyz { ... }
Statické metody a statické proměnné Pokud v hlavičce metody uvedeme klíčové slovo static, vznikne statická metoda, která bude patřit třídě a nikoliv jejím instancím.
Zatímco metody instance voláme obj.metoda(), metody třídy voláme Třída.metoda().
Metody třídy lze volat i v okamžiku, kdy neexistuje ani jedna instance třídy.
Pokud v hlavičce proměnné uvedeme klíčové slovo static, vznikne statická proměnná, která bude patřit třídě a nikoliv jejím instancím.
Zatímco k proměnným instance přistupujeme obj.proměnná, k proměnným třídy přistupujeme Třída.proměnná.
Ve statických metodách lze přímo volat pouze jiné statické metody a přímo přistupovat pouze ke statickým proměnným.
V metodách instance lze přímo volat statické metody i metody instance a přímo přistupovat ke statickým proměnným i proměnným instance.
V rámci dané třídy však není třeba při volání metody třídy nebo při použití proměnné třídy uvádět název třídy (tzn. používáme jen metoda(), resp. proměnná).
V téže třídě nelze deklarovat statickou proměnnou stejného jména, jako má proměnná instance.
V téže třídě nelze definovat statickou metodu stejného jména, jako má metoda instance (při stejných typech parametrů metod).
Příkladem statické metody je metoda main, která je po spuštění programu volána jako první: public static void main(String[] args) { ... }
Poznámka: Parametr této metody (args) je pole řetězců zadaných při spouštění programu jako parametry na příkazový řádek (např. java Vynasob 28 34). (V NetBeans je lze zadat v Project → Properties → Run → Arguments).
Příklad využití soukromého konstruktoru a statické metody ke kontrole instancí třídy (tzv. singleton): public class TiskovaFronta { static TiskovaFronta tf; private TiskovaFronta(){} static TiskovaFronta getInstance() { if (null == tf) tf = new TiskovaFronta(); return tf; } void tisk(Object o) { System.out.println(o.toString()); } }
14.1.2015 22:57
Programovací jazyk Java - přednáška č. 2
5z7
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/Java_02/index.html
public class Uloha { public static void main(String[] args) { TiskovaFronta m; m = new TiskovaFronta(); //Chyba při kompilaci: "TiskovaFronta() has private access in TiskovaFronta" m = TiskovaFronta.getInstance(); //Správný způsob získání instance. m.tisk("text"); } }
Proměnné z pohledu místa deklarace V Javě neexistují žádné „globální“ proměnné. Každá proměnná někomu patří, buď třídě (statická proměnná) nebo instanci.
Proměnné třídy deklarujeme ve třídě (mimo těla metod) s klíčovým slovem static.
Proměnné instance deklarujeme rovněž ve třídě (mimo těla metod).
Třetí možností je deklarace proměnné přímo v těle metody. Taková proměnná je viditelná pouze v této metodě. Pokud je deklarována v bloku, je její viditelnost omezena pouze na tento blok.
Proměnné tedy dělíme na proměnné třídy (= statické), proměnné instance a lokální proměnné (metod nebo jejich podčástí – bloků).
Příklad: public class Prom { static int i = 5; int j = 8; public static void tisk () { System.out.println(i); System.out.println(j); // Chyba při kompilaci: Z metody třídy nelze přímo přistupovat k proměnné instance. } public static void main (String[] args) { char i = 'a'; tisk(); // vytiskne 5 System.out.println(i + " " + Prom.i); // Vytiskne "a 5". Lokální proměnná i překryla proměnnou třídy stejného názvu, proto je k proměnné třídy nutno přistupovat přes název třídy. for(int k = 0; k < 10; k++) { System.out.println(k); ... } System.out.println(k); // Chyba při kompilaci: Platnost proměnné k je omezena pouze na příkaz for. } }
Statické a nestatické inicializační bloky K inicializaci třídy (resp. instance) použít jeden nebo více statických (resp. nestatických) bloků.
Syntaxe: class X { static int a; int b; static { a = 5; } { b = 7; } ... }
Statický inicializační blok vyvolává třída a to pouze jednou, při svém zavedení. Jedná se o tyto situace: je vyvolána metoda nebo konstruktor deklarovaný danou třídou, je proveden přístup k nekonstantní členské proměnné třídy nebo konstantě rozhraní, je proveden přístup ke členské proměnné třídy s modifikátorem final nebo static, která je inicializována hodnotou vypočítanou za běhu programu.
Nestatický inicializační blok vyvolávají všechny konstruktory třídy (nejprve proběhnou příkazy bloku, teprve poté příkazy konstruktoru).
Modifikátory přístupnosti třídy Před klíčovým slovem class může být uveden identifikátor public. Ten označuje veřejnou třídu, která je přístupná i mimo svůj balík. Bez tohoto modifikátoru je třída přístupná pouze ve svém balíku.
Veřejná třída musí být uložena v souboru stejného názvu (např. public class Student {...} v souboru Student.java)
Modifikátory přístupnosti proměnných a metod Java definuje čtyři stupně přístupu k metodám, které se nastavují pomocí modifikátorů v hlavičce metody: public – přístupné odkudkoliv,
protected – přístupné v rámci balíčku a potomků třídy (ty mohou být v jiných balíčcích),
implicitní (bez modifikátoru) – přístupné v rámci balíčku, private – viditelné pouze v rámci třídy.
Podrobněji ZDE.
V zájmu bezpečnosti a zapozdřenosti je vhodné postupovat od nejvyššího zapouzdření a v případě potřeby „zviditelňovat“. Opačně totiž prakticky nelze (např. zapouzdřit již zveřejněnou metodu).
14.1.2015 22:57
Programovací jazyk Java - přednáška č. 2
6z7
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/Java_02/index.html
Dědičnost Dědičnost představuje možnost přidat k základní třídě (též bázové, rodičovské, supertřídě nebo rodiči, případně předkovi) další vlastnosti a vytvořit tak odvozenou třídu (zděděnou třídu nebo potomka či dceřinou třídu). Odvozená třída bývá specializovanější, než základní.
Někdy není na první pohled jasné, zda použít dědičnost (tedy oddědit potomka od rodiče), nebo pouze využít kompozici (tzn. použít ve třídě instance jiných tříd).
Zde je vhodné použít tzv „JE test“. Pokud se dá říci, že nová třída JE současně původní třídou (přesněji její speciálnější, konkrétnější verzí), pak je možné použít dědění. Pokud se však jako vhodnější jeví použití slova MÁ, je třeba použít kompozici.
Relace dědění je vyznačena klíčovým slovem extends.
Následující příklad ukazuje vytvoření třídy Zamestnanec s využítím tříd Datum a Osoba. Třída Osoba umí evidovat jméno osoby. Třída Datum umí evidovat datum. U zaměstnance potřebujeme evidovat jméno, datum narození a datum nástupu do zaměstnání.
JE test: Zamestnanec JE (také) Osoba, proto třídu Zamestnanec oddědíme od třídy Osoba. Zamestnanec však MÁ nějaké datum narození a nějaké datum nástupu, proto třídu Datum využijeme formou kompozice (použijeme její instance). class Datum { private int den, mesic, rok; Datum(int den, int mesic, int rok) { this.den = den; this.mesic = mesic; this.rok = rok; } Datum(Datum d) { this(d.den, d.mesic, d.rok); } String toString() { StringBuffer b = new StringBuffer(100); b.append(den).append(".").append(mesic).append(".").append(rok); return b.toString(); } } class Osoba { String jmeno; Osoba(String jmeno) { this.jmeno = new String(jmeno); } String toString() { return jmeno; } } class Zamestnanec extends Osoba{ Datum narozeni, nastup; Zamestnanec(String jmeno, Datum narozeni, Datum nastup) { super(jmeno); this.narozeni = new Datum(narozeni); this.nastup = new Datum(nastup); } String toString() { StringBuffer b = new StringBuffer(200); b.append(jmeno).append(", narozen: ").append(narozeni.toString()); b.append("\nnastoupil: ").append(nastup.toString()); return b.toString(); } }
Jelikož je však dědičnost potenciálním zdrojem různých problémů, je doporučováno používat ji pouze v případech, kdy je mezi nadtřídou a podřídou jasný vztah specializace/generalizace, a všude jinde místo dědičnosti spíše kompozici. Viz např. http://dredwerkz.ic.cz/java_efektivne/JE_14.html.
Předávání skutečných parametrů metodám Parametry primitivních datových typů se předávají výhradně hodnotou. To znamená, že metoda si vytvoří kopie skutečných parametrů a s těmi pak pracuje. Metoda nemůže změnit obsah skutečných parametrů.
Parametry typu objekt nebo pole se metodám předávají také hodnotou, avšak je třeba počítat s tím, že touto hodnotou je odkaz neboli reference na objekt/pole. Metoda si tedy vytvoří kopii adresy objektu/pole v paměti a může trvale změnit datové složky objektu nebo prvky pole.
Pole jako referenční datový typ Java obsahuje dva neprimitivní datové typy: pole a objekty. Proměnné těchto typů jsou označovány jako referenční.
Referenční proměnné typu pole nebo objekt po své deklaraci nereprezentují žádná data. Jak pole, tak objekty vznikají dynamicky pomocí speciálního příkazu a zanikají, jakmile na ně neexistuje žádný odkaz.
Poznámky: Pro konkrétní hodnotu referenční proměnné používáme název odkaz. Hodnota neplatného (neexistujícího) odkazu je hodnota konstanty null.
Deklarace pole Skládá se ze dvou částí: z typu pole a jména proměnné.
Při deklaraci se neudává velikost pole. Pole jsou totiž alokována dynamicky, takže velikost pole se určuje až při žádosti o jeho vytvoření.
Příklad deklarace referenční proměnné pole typu int: int[] poleInt;
Protože deklarace nepřiděluje paměť, je třeba ji před prvním použitím pole přiřadit pomocí operátoru new: poleInt = new int[20];
Přidělení paměti je však možné již při deklaraci: int[] poleInt = new int[20];
Délka pole
14.1.2015 22:57
Programovací jazyk Java - přednáška č. 2
7z7
file:///C:/Users/JAKUB/Desktop/VS - ZS 2014/JAVA/Java_02/index.html
Délku pole zadanou při jeho vytvoření není nutné si pamatovat. Můžeme ji kdykoliv zjistit pomocí proměnné length. Tuto konstantu vlastní automaticky každé pole.
K prvkům pole se přistupuje běžným způsobem (přes hranatou závorku a index prvku). Počáteční prvek má vždy index 0.
Příklad: for (int i = 0; i < poleInt.length; i++) { poleInt[i] = i + 1; System.out.print(poleInt[i] + " "); }
Inicializované pole Pole nemusíme vytvářet jen pomocí new. Pokud potřebujeme mít pole naplněné určitými hodnotami, můžeme pole vytvořit pomocí statického inicializátoru. Příklad: int[] prvocisla = {1, 2, 3, 5, 7, 11};
Konstanta length v tomto případě vrací hodnotu 6, prvky pole jsou indexovány čísly 0 až 5.
Hodnoty jednotlivých prvků pole lze dále měnit, nelze však pole rozšiřovat o další prvky.
Dvourozměrné pole Příklad deklarace: int[][] a = new int[5][4];
Počet řádků (zde 5) získáme pomocí a.length, počet sloupců (zde 4) i-tého řádku pomocí a[i].length.
Každý řádek může mít jiný počet prvků (sloupců).
Dvourozměrné pole s různou délkou řádků Příklad: public class Pole5 { public static void main(String[] args) { int[][] a = new int[4][]; for (int i = 0; i < a.length; i++) { a[i] = new int[i + 1]; for (int j = 0; j < a[i].length; j++) { a[i][j] = i * 10 + j; System.out.print(a[i][j] + " "); } System.out.println(); } } }
Inicializace dvourozměrného pole I vícerozměrné pole lze vytvořit pomocí statického inicializátoru: int[][] b = {{ 1, 2, 3},{11, 12, 13},{21, 22, 23}}; int[][] c = {{ 1, 2, 3},{11, 12},{21}};
Troj- a vícerozměrná pole Vytvářejí se analogicky. V případě, že se pole vytváří postupně, nelze přeskakovat rozměry: int[][][] d = new int[5][5][5]; // OK int[][][] e = new int[5][5][]; e[0][1] = new int[8]; // OK int[][][] f = new int[5][][5]; // chyba f[0][][1] = new int[8]; // chyba
T he E nd
14.1.2015 22:57