Letní akce! Lákají tě IT školení C#, Javy a PHP v Brně? Přihlas se a napiš nám do zpráv kód "BRNO 500" pro slevu 500 Kč na libovolný brněnský kurz. Lze kombinovat se slevami uvedenými u školení i použít pro více kurzů. Akce končí 28.7.

Lekce 4 - Typový systém podruhé: Datové typy

Java Základní konstrukce Typový systém podruhé: Datové typy

ONEbit hosting Unicorn College Tento obsah je dostupný zdarma v rámci projektu IT lidem. Vydávání, hosting a aktualizace umožňují jeho sponzoři.

V minulé lekci Java kurzu, Proměnné, typový systém a parsování, jsme si ukázali základní datové typy, byly to int, String a float. Nyní se na datové typy podíváme více zblízka a vysvětlíme si, kdy jaký použít. Dnešní lekce bude hodně teoretická, ale o to více bude praktická ta příští. Na konci si vytvoříme pár jednoduchých ukázek.

Java rozeznává dva druhy datových typů, primitivní a referenční.

Primitivní datové typy

Proměnné primitivního datového typu si dokážeme jednoduše představit. Může se jednat např. o číslo nebo znak. V paměti je jednoduše uložena přímo hodnota a my k této hodnotě můžeme z programu přímo přistupovat. Slovo přímo jsem tolikrát nepoužil jen náhodou. V této sekci tutoriálů se budeme věnovat výhradně těmto proměnným.

Celočíselné datové typy

Podívejme se nyní na tabulku všech vestavěných celočíselných datových typů v Javě, všimněte si typu int, který již známe zminule.

Datový typ Rozsah Velikost
byte -128 až 127 8 bitů
short -32 768 až 32 767 16 bitů
int -2 147 483 648 až 2 147 483 647 32 bitů
long -9 223 372 036 854 775 808 až 9 223 372 036 854 775 807 64 bitů

Asi vás napadá otázka, proč máme tolik možných typů pro uložení čísla. Odpověď je prostá, záleží na jeho velikosti. Čím větší číslo, tím více spotřebuje paměti. Pro věk uživatele tedy zvolíme byte, protože se jistě nedožije více, než 127 let. Představte si databázi milionu uživatelů nějakého systému, když zvolíme místo byte int, bude zabírat 4x více místa. Naopak když budeme mít funkci k výpočtu faktoriálu, stěží nám bude stačit rozsah intu a použijeme long.

Nad výběrem datového typu nemusíte moc přemýšlet a většinou se používá jednoduše int. Typ řešte pouze v případě, když jsou proměnné v nějakém poli (obecně kolekci) a je jich tedy více, potom se vyplatí zabývat se paměťovými nároky. Tabulky sem dávám spíše pro úplnost. Mezi typy samozřejmě funguje již zmíněná implicitní konverze, tedy můžeme přímo přiřadit int do proměnné typu long a podobně, aniž bychom něco konvertovali.

Desetinná čísla

U desetinných čísel je situace poněkud jednodušší, máme na výběr pouze dva datové typy. Samozřejmě se liší opět v rozsahu hodnoty, dále však ještě v přesnosti (vlastně počtu des. míst). Double má již dle názvu dvojnásobnou přesnost oproti float.

Datový typ Rozsah Přesnost
float +-1.5 * 10−45 až +-3.4 * 1038 7 čísel
double +-5.0 * 10−324 až +-1.7 * 10308 15-16 čísel

Pozor, vzhledem k tomu, že desetinná čísla jsou v počítači uložena ve dvojkové soustavě, dochází k určité ztrátě přesnosti. Odchylka je sice téměr zanedbatelná, nicméně když budete programovat např. finanční systém, nepoužívejte tyto dat. typy pro uchování peněz, mohlo by dojít k malým odchylkám.

Když do floatu chceme dosadit přímo ve zdrojovém kódu, musíme použít sufix F, u double sufix D (u double ho můžeme vypustit, jelikož je výchozí desetinný typ):

float f = 3.14F;
double d = 2.72;

Jako desetinný separátor používáme ve zdrojovém kódu vždy tečku, nehledě na to, jaké máme v operačním systému regionální nastavení.

Další vestavěné datové typy

Podívejme se na další datové typy, které nám Java nabízí:

Datový typ Rozsah Velikost/Přesnost
char U+0000 až U+ffff 16 bitů
boolean true nebo false 8 bitů
Char

Char nám reprezentuje jeden znak, narozdíl od Stringu, který reprezentoval celý řetězec charů. Znaky v Javě píšeme do apostrofů:

char c = 'A';

Char patří v podstatě do celočíselných proměnných (obsahuje číselný kód znaku), ale přišlo mi logičtější uvést ho zde.

BigDecimal

Typ BigDecimal řeší problém ukládání desetinných čísel v binární podobě, ukládá totiž číslo vnitřně jako pole. Používá se tedy pro uchování peněžních hodnot. Nebudeme si zde ukazovat použití, protože se používá jako objekt. Pokud budete dělat někdy finanční výpočty, tak si na něj vzpomeňte.

Pozn.: V Javě jsou čísla tzv. odděděna od třídy Number. To je spíše informace do budoucna. Jelikož nyní nevíme, co dědičnost znamená, důležitá pro nás není. Number obsahuje ještě čtyři podtřídy, kterými se nebudeme podrobněji zabývat. BigDecimal a BigInteger slouží k výpočtům s vysokou přesností. Třídy AtomicInteger a AtomicLong se používají v aplikacích s více podprocesy. Opět je důležité, abyste věděli, že něco takového existuje a případně se sem později vrátili.

Boolean

Boolean nabývá dvou hodnot: true (pravda) a false (nepravda). Budeme ho používat zejména tehdy, až se dostaneme k podmínkám. Do proměnné typu boolean lze uložit jak přímo hodnotu true/false, tak i logický výraz. Zkusme si jednoduchý příklad:

boolean b = false;
boolean vyraz = (15 > 5);
System.out.println(b);
System.out.println(vyraz);

Výstup programu:

Konzolová aplikace
false
true

Výrazy píšeme do závorek. Vidíme, že výraz nabývá hodnoty true (pravda), protože 15 je opravdu větší než 5. Od výrazů je to jen krok k podmínkám, na ně se podíváme příště.

Referenční datové typy

K referenčním typům se dostaneme až u objektově orientovaného programování, kde si také vysvětlíme zásadní rozdíly. Zatím budeme pracovat jen s tak jednoduchými typy, že rozdíl nepoznáme. Spokojíme se s tím, že referenční typy jsou složitější, než ty primitivní. Jeden takový typ již známe, je jím String. Možná vás napadá, že String nemá nijak omezenou délku, je to tím, že s referenčními typy se v paměti pracuje jinak. Hodnotové typy začínají narozdíl od typů primitivních velkým písmenem.

String má na sobě řadu opravdu užitečných metod. Některé si teď probereme a vyzkoušíme:

String

StartsWith(), endsWith() a contains()

Můžeme se jednoduše zeptat, zda řetězec začíná, končí nebo zda obsahuje určitý podřetězec (substring). Podřetězcem myslíme část původního řetězce. Všechny tyto metody budou jako parametr brát samozřejmě podřetězec a vracet hodnoty typu boolean (true/false). Zatím na výstup neumíme reagovat, ale pojďme si ho alespoň vypsat:

String s = "Krokonosohroch";
System.out.println(s.startsWith("krok"));
System.out.println(s.endsWith("hroch"));
System.out.println(s.contains("nos"));
System.out.println(s.contains("roh"));

Výstup programu:

Konzolová aplikace
false
true
true
false

Vidíme, že vše funguje podle očekávání. První výraz samozřejmě neprošel díky tomu, že řetězec ve skutečnosti začíná velkým písmenem.

toUpperCase() a toLowerCase()

Rozlišování velkých a malých písmen může být někdy na obtíž. Mnohdy se budeme potřebovat zeptat na přítomnost podřetězce tak, aby nezáleželo na velikosti písmen. Situaci můžeme vyřešit pomocí metod toUpperCase() a toLowerCase(), které vrací řetězec ve velkých a v malých písmenech. Uveďme si reálnější příklad než je Krokonosohroch. Budeme mít v proměnné řádek konfiguračního souboru, který psal uživatel. Jelikož se na vstupy od uživatelů nelze spolehnout, musíme se snažit eliminovat možné chyby, zde např. s velkými písmeny.

String konfig = "Fullscreen shaDows autosave";
konfig = konfig.toLowerCase();
System.out.println("Poběží hra ve fullscreenu?");
System.out.println(konfig.contains("fullscreen"));
System.out.println("Budou zapnuté stíny?");
System.out.println(konfig.contains("shadows"));
System.out.println("Přeje si hráč vypnout zvuk?");
System.out.println(konfig.contains("nosound"));
System.out.println("Přeje si hráč hru automaticky ukládat?");
System.out.println(konfig.contains("autosave"));

Výstup programu:

Konzolová aplikace
Poběží hra ve fullscreenu?
true
Budou zapnuté stíny?
true
Přeje si hráč vypnout zvuk?
false
Přeje si hráč hru automaticky ukládat?
true

Vidíme, že jsme schopni zjistit přítomnost jednotlivých slov v řetězci tak, že si nejprve řetězec převedeme celý na malá písmena (nebo na velká) a potom kontrolujeme přítomnost slova jen malými (nebo velkými) písmeny. Takhle by mimochodem mohlo opravdu vypadat jednoduché zpracování nějakého konfiguračního skriptu.

Trim()

Další nástrahou mohou být mezery a obecně všechny tzv. bílé znaky, které nejsou vidět, ale mohou nám uškodit. Obecně může být dobré trimovat všechny vstupy od uživatele. Zkuste si v následující aplikaci před číslo a za číslo zadat několik mezer, trim() je odstraní. Odstraňují se vždy bílé znaky kolem řetězce, nikoli uvnitř:

System.out.println("Zadejte číslo:");
String s = sc.nextLine();
System.out.println("Zadal jste text: " + s);
System.out.println("Text po funkci trim: " + s.trim());
int a = Integer.parseInt(s.trim());
System.out.println("Převedl jsem zadaný text na číslo parsováním, zadal jste: " + a);
Replace()

Asi nejdůležitější metodou na Stringu je nahrazení určité jeho části jiným textem. Jako parametry zadáme dva podřetězce, jeden co chceme nahrazovat a druhý ten, kterým to chceme nahradit. Metoda vrátí nový String, ve kterém proběhlo nahrazení. Když daný podřetězec metoda nenajde, vrátí původní řetězec. Zkusme si to:

String s = "C# je nejlepší!";
s = s.replace("C#", "Java");
System.out.println(s);

Výstup programu:

Konzolová aplikace
Java je nejlepší!
Format()

Format() je velmi užitečná metoda, která nám umožňuje vkládat do samotného textového řetězce zástupné značky. Ty jsou reprezentovány jako procento a zkratka datového typu. Metoda se volá na typu String, prvním parametrem je textový řetězec se značkami, další dále následují proměnné v tom pořadí, v kterém se mají do textu místo značek vložit. Všimněte si, že se metoda nevolá na konkrétní proměnné (přesněji instanci, viz další díly), ale přímo na typu String.

int a = 10;
int b = 20;
int c = a + b;
String s = String.format("Když sečteme %d a %d, dostaneme %d", a, b, c);
System.out.println(s);

Výstup programu:

Konzolová aplikace
Když sečteme 10 a 20, dostaneme 30

Značky jsou:

  • %d pro celá čísla
  • %s pro Stringy
  • %f pro float. U float můžeme definovat délku desetinné části, např: %.2f pro dvě desetinná místa.

Konzole sama umí přijímat text v takovémto formátu, jen musíme místo println() volat printf(). Můžeme tedy napsat:

int a = 10;
int b = 20;
int c = a + b;
System.out.printf("Když sečteme %d a %d, dostaneme %d", a, b, c);

Toto je velmi užitečná a přehledná cesta, jak sestavovat řetězce, i přesto se však v Javě řetězce spojují spíše pomocí operátoru "+".

Length()

Poslední, ale nejdůležitější je length(), tedy délka. Vrací celé číslo, které představuje počet znaků v řetězci.

System.out.println("Zadejte vaše jméno:");
String jmeno = sc.nextLine();
System.out.printf("Délka vašeho jména je: %d", jmeno.length());

Je toho ještě spoustu k vysvětlování a jsou další datové typy, které jsme neprobrali. Aby jsme však stále neprobírali jen teorii, ukážeme si již v příští lekci, Podmínky (větvení), podmínky a cykly. Potom bude naše programátorská výbava dostatečně velká k tomu, abychom tvořili zajímavé programy :)


 

 

Článek pro vás napsal David Čápka
Avatar
Jak se ti líbí článek?
93 hlasů
Autor pracuje jako softwarový architekt a pedagog na projektu ITnetwork.cz (a jeho zahraničních verzích). Velmi si váží svobody podnikání v naší zemi a věří, že když se člověk neštítí práce, tak dokáže úplně cokoli.
Unicorn College Autor sítě se informační technologie naučil na Unicorn College - prestižní soukromé vysoké škole IT a ekonomie.
Miniatura
Všechny články v sekci
Základní konstrukce jazyka Java
Miniatura
Následující článek
Cvičení k 4. lekci Javy
Aktivity (9)

 

 

Komentáře
Zobrazit starší komentáře (109)

Avatar
Veronika Nováková:8. března 19:40

Ahojte, na chvilu som sa odmlcala, ale uz som tu. Dakujem za odpovede, aj za priklady ako sa daju znalosti z lekcie vyuzit. To ze tieto veci su ako "cihla" si uvedomujem, no moja zvedavost je vacsia a chcem aby mi to vsetko davalo zmysel uz od zakladu :)

 
Odpovědět 8. března 19:40
Avatar
Tomáš Duda
Člen
Avatar
Tomáš Duda:12. března 19:12

Ahoj, chci se vyjádřit k tvému problému se jménem a to že pokud tvoje jméno obsahuje háčky či čárky tak to program započítá jako další znak.
Moje jméno je: Tomáš Duda = 12 znaků
Tomas Duda = 10 znaků

 
Odpovědět 12. března 19:12
Avatar
Odpovídá na Tomáš Duda
Daniel Holánek:12. března 21:28

Aha, děkuji. To by mě nenapadlo :)

 
Odpovědět 12. března 21:28
Avatar
Josef Pospíšil:27. března 14:08

Dle mého to tam chybí, abychom si to už ohlídali a mysleli na to sami.

 
Odpovědět 27. března 14:08
Avatar
pocitac770
Redaktor
Avatar
Odpovídá na Josef Pospíšil
pocitac770:27. března 16:42

Ono je to hlavně proto, že se na to ptají hlavně ti co jenom bezmyšlenkovitě opisují kód, a nepřemýšlí nad tím + pořádně nečtou články, viz minulá lekce

Editováno 27. března 16:43
 
Odpovědět 27. března 16:42
Avatar
Jarick
Člen
Avatar
Jarick:3. dubna 18:54

Ahoj. Předpokládám, že typ double umí pracovat i s celými čísli. Jaký mám tedy důvod použít např. int pro celá čísla, když mohu použít double pro celá i desetinná? Vyhnu se tím případnému parsování na double.

 
Odpovědět 3. dubna 18:54
Avatar
Odpovídá na Jarick
Michal Šmahel:4. dubna 14:00

Ahoj, vysvětlení může být vícero.

Jeden z důvodů je přehlednost. Když si to tak vezmeš, můžeš rovnou použít i String, do něhož lze uložit vše (po přeparsování). Samozřejmě to v běžném případě neuděláš, protože by s tím bylo moc práce. Také by se rovnou datové typy nemusely využívat vůbec (resp. by byly dynamické). To však v Javě (a jiných jazycích na podobné úrovni, např. C#) není za běžných podmínek možné. Samozřejmě to má svůj důvod - datové typy nevznikly jen tak. Pro tebe jako programátora slouží především proto, aby ses v kódu lépe vyznal a věděl, jaké chování můžeš od určitých proměnných očekávat. V proměnné typu int celé číslo a v jiné typu double reálné s velkým desetinným rozvojem (nebo celé číslo, které se rozsahově nevejde do long).

Poté jsou tu rozsahy a místo, které jednotlivé datové typy zabírají v paměti. Double (primitivní) bude určitě zabírat více místa než int, který má oproti němu daleko menší rozsah. Můžeš tak správnými datovými typy ušetřit místo v paměti.

Pokud si někdo vzpomene na další opodstatnění, budu rád, když mě doplní. Doufám, že jsem trochu pomohl.

Odpovědět 4. dubna 14:00
Nejdůležitější je motivace, ovšem musí být doprovázena činy.
Avatar
Jarick
Člen
Avatar
Odpovídá na Michal Šmahel
Jarick:6. dubna 11:37

Díky. Ještě jsem čekal, za nějaký další názor. Pokud to tedy dobře chápu, tak na funkčnost to nemá vliv. Rozdíl je poznat až při tvorbě větších projektů, kvůli paměti. Je jasné, že je lepší se to učit se správnými typy proměnných. Jen jsem chtěl vědět, zda to má i nějaký hlubší smysl.

 
Odpovědět 6. dubna 11:37
Avatar
Jakub Exner
Člen
Avatar
Jakub Exner:3. května 0:13

Zdravím, moc rád bych se zeptal u příkladu Length(). Jak do kodu aplikuji i Trim() ? Rád bych aby mi to třeba u toho jména počítalo jen znaky bez mezer.
Zkoušel jsem první co mě napadlo a to zvlášť to naparsovat a toho použít Trim(). Hazí mi to chybu, takže je to blbost. Jsem začátečník. Díky moc.

System.out.prin­tln("Zadejte vaše jméno .");
String jmeno = sc.nextLine();
int a = Integer.parse­Int(jmeno.trim());
System.out.prin­tf("Délka vašeho jména je %d " , jmeno.length());

 
Odpovědět 3. května 0:13
Avatar
Odpovídá na Jakub Exner
Miroslav Bejlek:22. května 17:08

Ahoj, nejsem odborník, ale pokud ti jde jen o délku napsaného řetězce bez prázdných mezer, napsal bych to spíše:
System.out.prin­tln("Zadejte vaše jméno: ");
String jmeno = sc.nextLine()­.trim();
System.out.for­mat("Délka vašeho jména je: %d", jmeno.length());

sc.nextLine()­.trim() je jenom spojování příkazů, prosím o opravu, pokud to chápu špatně.

Díky

 
Odpovědět 22. května 17:08
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 10 zpráv z 119. Zobrazit vše