Přednášky KIV/PPA1,A. Netrvalová, 2016 6. přednáška Obsah 6. přednášky: Třídy a objekty Deklarace třídy a objektu, vytvoření objektu Proměnné primitivní vs. objektové Přímý přístup k atributům a metodám Program sestávající z více tříd Konstruktor a klíčové slovo this Autorizovaný přístup Vytváření, volání a přetěžování metod Pole objektů třídy String - příklad knihovní třídy Metody třídy String a jejich použití Třída StringBuffer* Programování procedurální vs. objektové Tato tematika je zpracována v Záznamy přednášek: str. 123 – 152 Prostudujte i motivační příklad Osoba - BMI! Budete ho potřebovat na cvičení a při řešení domácích úloh.
37
Embed
Obsah 6. přednášky - zcu.cznetrvalo/vyuka/ppa1/portal/prednasky/... · 2016. 11. 2. · Přednášky KIV/PPA1, A. Netrvalová, 2016 6. přednáška Obsah 6. přednášky: Třídy
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Přednášky KIV/PPA1, A. Netrvalová, 2016 6. přednáška
Obsah 6. přednášky:
Třídy a objekty
Deklarace třídy a objektu, vytvoření objektu
Proměnné primitivní vs. objektové
Přímý přístup k atributům a metodám
Program sestávající z více tříd
Konstruktor a klíčové slovo this
Autorizovaný přístup
Vytváření, volání a přetěžování metod
Pole objektů třídy
String - příklad knihovní třídy
Metody třídy String a jejich použití
Třída StringBuffer*
Programování procedurální vs. objektové
Tato tematika je zpracována v
Záznamy přednášek: str. 123 – 152
Prostudujte i motivační příklad Osoba - BMI! Budete ho potřebovat na cvičení a při řešení domácích úloh.
Zrušení objektu (Garbage collector) - pokud na objekt není reference Příklad:
Kruh mujKruh = new Kruh();
mujKruh = null;
mujKruh
null
referenční
proměnná
polomer = 1
Kruh
objekt
Strana 7 (celkem 37)
Proměnné primitivní vs. objektové
Rozdíl v pojetí
- při deklaraci a vzniku
- při přiřazení proměnných
int i = 1; Kruh k1 = new Kruh(5); int j = 2; Kruh k2 = new Kruh(9);
???
Kruh
polomer = 1
Primitivní typ int i; i
i = 1; i
Objektový typ Kruh k = new Kruh();
null
k
k při deklaraci
při vytvoření
1
při deklaraci
při vytvoření
1
Kruh
polomer = 5
Přiřazení: primitivní typ i = j
před:
i
2 j
2
po:
i
2 j
Přiřazení: typ objekt k1 = k2
před:
k1
k2
po:
k1
k2
polomer = 9
Kruh
Strana 8 (celkem 37)
Přímý přístup k objektu tj. k atributům a metodám
Reference atributu či metody - tečkovaná notace
Komunikace s objektem - zasíláním zpráv (volání instančních metod)
Reference atributu
jmenoObjektu.promenna
Př.:
Reference metody
jmenoObjektu.metoda()
Př.:
Program sestávající z více tříd
Deklaraci, vytvoření a použití objektu (tj. atributů a metod) provádíme v jiné třídě. Na počátku, pro jednoduchost, budeme vytvářet a používat programy sestávající pouze ze dvou tříd.
Hlavní program - tj. třída obsahující main()
- zde budeme programy spouštět.
Pokud program sestává z více tříd, každou třídu ukládáme do samostatného souboru (výjimkou je
odevzdání DÚ na validátor). Důvodem je snadné použití třídy v jiném programu či předání třídy (souboru) jinému programátorovi.
mujKruh.spoctiPlochu()
mujKruh.polomer
Strana 9 (celkem 37)
Poznámky:
Uložení programu - pokud jsou třídy výjimečně uloženy společně v jednom souboru, potom je pouze třída s hlavním programem, tj. s metodou main(), opatřena modifikátorem public!
Překlad programu
třída:
- příkazová řádka: javac JmenoTridy.java
- Scite: Ctrl+F7
celý program:
- příkazová řádka: javac *.java
- Scite: F7
Spouštění programu
třída:
- příkazová řádka: java JmenoTridy
- Scite: F5
celý program:
- příkazová řádka: java HlavniProgram
- Scite: F5 v hlavní třídě
Terminologie - poznámka
- pro označení třídy jakožto šablony se lze setkat s pojmem „datová třída“ (Data Model Class), tj. třída sloužící zejména k uchování dat a manipulaci s nimi. Instancí takové třídy je datový objekt (Data Object).
Strana 10 (celkem 37)
Příklad vytvoření a použití objektu
Výpis výsledků v předchozím příkladu je zbytečně zdlouhavý, a kdykoliv budeme potřebovat vypsat poloměr či plochu musíme příkazy zopakovat.
mujKruh.polomer = 10;// nastaveni nove hodnoty polomeru
// vypis
System.out.println("Plocha kruhu o polomeru " + mujKruh.polomer + " je " + mujKruh.spoctiPlochu() + ".");
} }
Polomer = 1.0 Plocha kruhu o polomeru 10.0 je 314.1592653589793.
HLAVNÍ PROGRAM TŘÍDA - obsahující main() (ŘÍDICÍ, APLIKAČNÍ, UŽIVATELSKÁ)
Strana 11 (celkem 37)
Řešením je použití metody String toString(),
(vrací řetězec se stavem objektu), napíšeme ji dle potřeby ve třídě Kruh a pak tuto metodu zavoláme (v metodě pro výpis) v hlavním programu (viz dále).
Konstruktor - speciální metoda, která umožňuje vytvoření objektu (přidělení paměti a inicializace členských proměnných)
Liší se od ostatních metod: - nelze volit jeho jméno, má jméno třídy
- nemá žádnou návratovou hodnotu (tj. ani void)
- nelze volat rekurzivně
- použití operátoru new
Konstruktor
- implicitní – vytvoří se, i když ho nenapíšeme
(předchozí příklad fungoval! Volali jsme Kruh(), ale ve třídě Kruh konstruktor zapsán nebyl)
- explicitní – nutno napsat (může být i přetížen, tj. konstruktorů můžeme napsat více)
Použití klíčového slova this
- typicky se vyskytuje v konstruktoru, ale i jinde (viz dále)
Formální parametry konstruktoru mají často jméno shodné s atributem – nutno odlišit.
Řešením je
this.jmenoAtributu
neboť this odkazuje vždy na samotný objekt.
Upravíme tedy náš předchozí příklad:
Strana 13 (celkem 37)
public class Kruh { double polomer;
Kruh(double polomer) { // konstruktor
this.polomer = polomer; // this zde musí byt
}
Kruh(){ /* konstruktor bez parametru, musi byt napsan, ale jen pokud jej potrebujeme, tzv. pretizeny konstruktor */
polomer = 1.0; // this zde neni nutné, nelze pochybit
} double spoctiPlochu(){ return polomer*polomer*Math.PI; } // this by mohlo byt uvedeno, ale zatim nadbytecne
.... } public class TestKruhuSKonstruktory { //aplikace
public static void main(String[] args) {
// nastav polomer 5.0
Kruh mujKruh = new Kruh(5.0);
// vypocet a tisk
System.out.println(mujKruh);
// defaultni polomer
Kruh tvujKruh = new Kruh();
// vypocet a tisk
System.out.println(tvujKruh); } }
<Kruh: polomer = 5.0, plocha = 78.53981633974483>
<Kruh: polomer = 1.0, plocha = 3.141592653589793>
Strana 14 (celkem 37)
Autorizovaný přístup
Uživatel se pohodlně dostává přímým přístupem k nastavení atributu - výhoda, ale tím pádem může
tento atribut i nevhodně změnit - nevýhoda (např. v našem případě zadat záporný poloměr!)
Řešení - použít modifikátor (specifikátor) private
(privátní, soukromý)
- zapouzdření (encapsulation) – skrytí atributů před „vnějšími vlivy“
Přístupová práva:
Důsledek - použitím specifikátoru private přímý
přístup k atributu již nefunguje!
Nutný autorizovaný přístup, tj. - vytvoření přístupových metod pro nastavení a vrácení hodnoty atributu (setters, getters)
Poznámka:
Přístupové právo metod bývá většinou public, ale i metody je
možné (někdy vhodné) skrývat, potom je použit modifikátor private (s důsledky obdobnými jako u atributu).
Specifikátor Povolený přístup
public Z libovolné třídy.
private Pouze zevnitř dané třídy, žádný přístup z vnějšku.
protected Z kterékoliv třídy téhož balíku, případně z potomka třídy kdekoliv.
žádný (tzv. package friendly)
Z kterékoliv třídy téhož balíku.
Strana 15 (celkem 37)
Úprava: přidáme modifikátor private pro atribut
polomer a přístupové metody getPolomer() a setPolomer()
public class Kruh { private double polomer; // privatni atribut
public Kruh() { polomer = 1.0; }
public Kruh(double polomer) { setPolomer(polomer); // volani setru v konstruktoru }
// metoda pristupu k atributu vraceni hodnoty = getr
public double getPolomer() { return polomer; }
// metoda pristupu k atributu nastaveni hodnoty = setr public void setPolomer(double polomer) { if(polomer > 0){ //osetri pripustnost vstupu this.polomer = polomer; } }
public static void main(String[] args) { Pozdrav pozdrav = new Pozdrav("Ahoj světe!"); pozdrav.print(); // 1. zpusob System.out.println(pozdrav); // 2. zpusob
// nyni se pozdrav zmeni, puvodni je uz nedostupny
pozdrav = new Pozdrav("Hello world!"); pozdrav.print(); System.out.println(pozdrav); } }
class Pozdrav { private String text;
public Pozdrav(String pozdrav) { // konstruktor
text = pozdrav; } // privatni metoda - privatni getr
private String getText() { // zde private ok
return text; } // 1 - metoda pro vypis
public void print() { // public metoda vola privatni getr System.out.println(getText()); } // 2 - metoda pro vypis
public String toString(){ // public metoda vola privatni getr return getText(); } }
Ahoj světe! Ahoj světe! Hello world! Hello world!
Poznámka pro odevzdávání DÚ na validátor: Validátor pracuje pouze s jedním souborem. Takže, kdybychom chtěli např. odevzdat předchozí příklad, pak třídu Pozdrav uvedeme bez specifikátoru
public a uložíme spolu do souboru s HelloWorld (v tomto souboru tedy
budou dvě třídy). Pouze aplikační třída může mít specifikátor public.
Takto upravený výsledný soubor odešleme na validátor.
Strana 21 (celkem 37)
Poznámka (podrobně - viz Záznamy - str. 127): Na cvičení a
v DÚ bude používán lékařský termín BMI (Body Mass Index), který nemá nic společného s programováním. Je pouze použit pouze pro příklad výpočtu dat odvozených z dat základních.
BMI: podíl hmotnosti [kg] a čtverce výšky [m], tj.
BMI = m / (v * v)
Hodnoty BMI:
< 18 – podváha 18 až 25 – ideální hmotnost 25 až 30 – nadváha > 30 – obezita ohrožující zdraví
Příklad: vytvoříme třídu Osoba s atributy jmeno, vaha, vyska s konstruktorem Osoba() s metodami: getJmeno(), getVaha(), setVaha(),
getVyska() a vypoctiBMI() (viz slide na následující stránce)
Dále si vytvoříme v aplikační třídě objekt třídy Osoba, tj. osobu Hana, s vahou 50 kg a výškou 170 cm. Vypíšeme informace o objektu, jméno a spočtené BMI. Potom nastavíme váhu Hany na 55 kg a opět „objekt“ (tj. informace o něm) vypíšeme.
public class OsobyObjektyAplikace { public static void main(String[] args) { Osoba o; o = new Osoba("Hana", 50, 170); System.out.println(o.toString()); System.out.println(o.getJmeno() + " ma BMI " + o.vypoctiBMI()); o.setVaha(55.0); System.out.println(o); } }
<Hana, vaha: 50.0, vyska: 170, BMI: 17> Hana ma BMI 17
<Hana, vaha: 55.0, vyska: 170, BMI: 19>
Strana 22 (celkem 37)
class Osoba { private String jmeno; // atributy private double vaha; private int vyska; // konstruktor public Osoba(String jmeno, double vaha, int vyska) { this.jmeno = jmeno; setVaha(vaha); this.vyska = vyska; } // getry a setry public String getJmeno() { return jmeno; } public double getVaha() { return vaha; } public void setVaha(double vaha) { if (vaha > 0.0) { this.vaha = vaha; } } public int getVyska() { return vyska; } // ostatni metody public int vypoctiBMI() { double vyskaMetry = vyska / 100.0; double bmi = vaha / (vyskaMetry * vyskaMetry); return (int) Math.round(bmi); // zaokrouhleni }
int a, b; while (sc.hasNext()) { input = sc.nextLine().trim(); idata = input.split(" "); // oddelovac 1 mezera a = Integer.parseInt(idata[0]); b = Integer.parseInt(idata[1]); }
// Prevod celociselneho typu na retezec
// 1. zpusob int i = 123; String s3 = Integer.toString(i); // s3 = "123"
// 2. zpusob
double p = 123.45;
String s4 = String.valueOf(p) ; // s4 = "123.45"
Strana 28 (celkem 37)
Příklad 2: Zjistěte, zda se zadané písmeno v řetězci
vyskytuje, a pokud ano, pak na které pozici je jeho první
výskyt, pokud ne, pouze vypište zprávu.
import java.util.Scanner; public class HledaniPismenaVRetezci { private static Scanner sc = new Scanner(System.in); public static void main(String[] args) { System.out.print("Zadejte retezec:"); String retezec = sc.nextLine(); System.out.print("Zadejte hledane pismeno:"); char hledej = sc.nextLine().charAt(0); boolean nalezeno = false; int index = 0;
// nasledujici programovou sekvenci bychom museli napsat
/* for(int i = 0; i < retezec.length(); i++) { // cyklem
Zadejte retezec:alenka Zadejte retezec:alenka Zadejte hledane pismeno:a Zadejte hledane pismeno:o Pismeno 'a' je na pozici:1 Pismeno 'o' nenalezeno!
Strana 29 (celkem 37)
Př. 3: Césarova šifra - vytvořte program pro šifrování
textu (pouze malá písmena anglické abecedy), které
spočívá v posouvání znaku v abecedě o určitý, pevně
stanovený počet znaků. Např. slovo "ahoj" se posunem
textu o 1 znak transformuje na "bipk".
Další podobnou úložku hledejte na konci přednášky…
import java.util.*; public class CesarovaSifra { private static Scanner sc = new Scanner(System.in); public static void main(String [] args){ // inicializace promennych
String s = "abcdefghijklmnopqrstuvwxyz"; System.out.printf("Puvodni zprava: %s\n", s); String zprava = ""; int posun = 1; // posun mozno nastavit: sc.nextInt(); // cyklus prochazejici jednotlive znaky for (char c : s.toCharArray()) { // for Each
int i = (int)c; i += posun; // presun na zacatek abecedy if (i > (int)'z') { i -= 26; //pocet pismen } char znak = (char)i; zprava += znak; } // vypis System.out.printf("Zasifrovana zprava: %s\n", zprava); } }
// zjednoduseni vypisu pouzitim metody toString() System.out.println(pipina); } }
Strana 32 (celkem 37)
Třída StringBuffer* (pouze informace) - objekty typu String jsou řetězce neměnitelné - vyšší
odolnost vůči chybám a efektivnější zacházení
- měnitelné řetězce - použít instance třídy StringBuffer.
- doporučení - používat instance třídy String, je-li třeba změny - zkonvertovat na typ StringBuffer a pak opět uchovat v objektech typu String.
Převod objektu StringBuffer na String a zpět
Převod StringBuffer na String se provede metodou toString. Převod ze String na StringBuffer se provede vhodným použitím operátoru new (je to zcela intuitivní).
Ze String do StringBuffer: StringBuffer jmenobuf = new StringBuffer("Pepa "); Ze StringBuffer do String String jmeno = jmenobuf.toString();
Základy práce s třídou StringBuffer append(), insert ()- přidávání a vkládání textu
// vytvoreni objektu StringBuffer (s kapacitou 16 znaku)
StringBuffer jmenobuf2 = new StringBuffer(); String jmeno = "princezna Lada";
// vytvoreni objektu StringBuffer s pouzitim tridy String StringBuffer jmenobuf1 = new StringBuffer(jmeno);
// vytvoreni objektu StringBuffer s kapacitou zadanou cislem
StringBuffer jmenobuf2 = new StringBuffer(20);
StringBuffer buf = new StringBuffer("jmeno :"); buf.append(" Koza"); buf.insert(8, " Oliver") // vystup: "jmeno : Oliver Koza"; System.out.println(buf.toString());
Strana 33 (celkem 37)
replace() - změna textu ve StringBufferu
capacity() - zjištění kapacity StringBufferu.
Kapacita představuje maximalní počet znaků, který lze do StringBufferu uložit.
reverse() - obrácení textu ve StringBufferu
Užitečné testování znaků
Character.isDigit()- je znak číslice?
Character.isLetter()- je znak písmeno?
Character.isLetterOrDigit()- je znak písmeno
nebo číslice?
Character.isLowerCase()- je znak malé písmeno?
Character.isUpperCase()- je znak velké písmeno?
Character.isWhitespace()- je to „bílý“ znak?
StringBuffer buf = new StringBuffer("Miroslav Koula"); String s1 = "Mirek"; // nahradi text zacinajici na pozici 1 až 8 novym textem buf.replace(1,8,s1);
StringBuffer buf = new StringBuffer("delsi retezec"); // delka je nastavena na 10 buf.setLength(10); // ale do kapacity je ulozena hodnota 14
int kapacita = buf.capacity();
StringBuffer obraceni = new StringBuffer("Radar"); System.out.println("Obraceni Radar: " + obraceni.reverse());
Strana 34 (celkem 37)
Programování procedurální vs. objektové V následujícím příkladu je patrný rozdíl mezi procedurálním a objektovým programováním.
Obě formy mají své výhody a nevýhody!
Objektové programování představuje více „administrativy“, kód je však strukturovaný a přehledný, provedení ale zabere relativně více času.
Procedurální programování „míchá“ data a metody, je méně přehledné, ale má relativně rychlejší běh. (více v PPA2)
Příklad (Kruh) – ilustrace obou forem programování
Vytvoříme dva kruhy a napíšeme další metodu, která spočte rozdíl ploch obou kruhů.
Počítáme rozdíl plochy kruhu referencovaného, tj. „našeho“ (this) objektu - vůči ploše kruhu jiného objektu, který je předán skutečným parametrem v metodě.
public class SpoctiRozdilPlochProc { static double spoctiPlochu(double polomer){ return polomer*polomer*Math.PI; }