Datové typy: primitivní, objektové, pole. Výrazy.

Obsah

Úvod k datovým typům v Javě
Primitivní vs. objektové datové typy - opakování
Přiřazení proměnné primitivního typu - opakování
Přiřazení objektové proměnné - opakování
Primitivní datové typy
Primitivní datové typy
Integrální typy - celočíselné
Integrální typy - "char"
Typ charsearch in Czech Wikipedia - kódování
Čísla s pohyblivou řádovou čárkou
Vestavěné konstanty s pohyblivou řádovou čárkou
Typ logických hodnot - boolean
Typ voidsearch in Czech Wikipedia
Pole
Pole v Javě
Pole (2)
Pole - co když deklarujeme, ale nevytvoříme?
Pole - co když deklarujeme, vytvoříme, ale nenaplníme?
Kopírování polí
Operátory a výrazy
Aritmetické
Logické
Relační (porovnávací)
Bitové
Operátor podmíněného výrazu ? :search in Czech Wikipedia
Operátory typové konverze (přetypování)
Operátor zřetězení +search in Czech Wikipedia
Priority operátorů a vytváření výrazů
Porovnávání objektů
Relační (porovnávací)
Porovnávání objektů
Porovnávání objektů - příklad
Metoda hashCode
Metoda hashCode - příklad

Úvod k datovým typům v Javě

Cíl

Naučit se pracovat s primitivními a objektovými datovými typy v Javě, vymezit to vůči obecně známým principům (např. z Pascalu)

Předpoklady

Znát základní datové typy (číselné, logické, znakové) - např. z Pascalu

  • Primitivní vs. objektové typy

  • Kategorie primitivních typů: integrální, boolean, čísla s pohyblivou řádovou čárkou

  • Pole: deklarace, vytvoření, naplnění, přístup k prvkům, rozsah indexů

Primitivní vs. objektové datové typy - opakování

Java striktně rozlišuje mezi hodnotami

  • primitivních datových typů (čísla, logické hodnoty, znaky) a

  • objektových typů (řetězce a všechny uživatelem definované [tj. vlastní] typy-třídy)

Základní rozdíl je v práci s proměnnými:

  • proměnné primitivních typů přímo obsahují danou hodnotu, zatímco

  • proměnné objektových typů obsahují pouze odkaz na příslušný objekt

Důsledek -> dvě objektové proměnné mohou nést odkaz na tentýž objekt

Přiřazení proměnné primitivního typu - opakování

  • Příklad:

    double a = 1.23456; 
    double b = a; 
    a += 2;
    

Přiřazení objektové proměnné - opakování

  • Příklad, deklarujeme třídu Counter search in Czech Wikipediatakto:

    public class Counter { 
       private double value; 
       public Counter(double v) { 
          value = v; 
       } 
       public void add(double v) { 
          value += v; 
       }
       public void show() { 
          System.out.println(value); 
       }
    }    
    
  • nyní ji použijeme:

    Counter c1 = new Counter(1.23456); 
    Counter c2 = c1; 
    c1.add(2); 
    c1.show(); 
    c2.show();    
    

    dostaneme:

    3.23456 
    3.23456
    

Primitivní datové typy

Primitivní datové typy

Proměnné těchto typů nesou elementární, z hlediska Javy atomické, dále nestrukturované hodnoty.

Deklarace takové proměnné (kdekoli) způsobí:

  1. rezervování příslušného paměťového prostoru (např. pro hodnotu intsearch in Czech Wikipedia čtyři bajty)

  2. zpřístupnění (pojmenování) tohoto prostoru identifikátorem proměnné

V Javě existují tyto skupiny primitivních typů:

  1. integrální typy (obdoba ordinálních typů v Pascalu) - zahrnují typy celočíselné (bytesearch in Czech Wikipedia, shortsearch in Czech Wikipedia, intsearch in Czech Wikipedia a longsearch in Czech Wikipedia) a typ charsearch in Czech Wikipedia;

  2. typy čísel s pohyblivou řádovou čárkou (floatsearch in Czech Wikipedia a doublesearch in Czech Wikipedia)

  3. typ logických hodnot (booleansearch in Czech Wikipedia).

Integrální typy - celočíselné

V Javě jsou celá čísla vždy interpretována jako znaménková

"Základním" celočíselným typem je 32bitový intsearch in Czech Wikipedia s rozsahem -2 147 483 648search in Czech Wikipedia2147483647search in Czech Wikipedia

větší rozsah (64 bitů) má longsearch in Czech Wikipedia, cca +/- 9*10^18search in Czech Wikipedia

menší rozsah mají

  • shortsearch in Czech Wikipedia (16 bitů), tj. -32768..32767

  • bytesearch in Czech Wikipedia (8 bitů), tj. -128..127

Pro celočíselné typy existují (stejně jako pro floating-point typy) konstanty - minimální a maximální hodnoty příslušného typu. Tyto konstanty mají název vždy Typ.MIN_VALUE search in Czech Wikipedia, analogicky MAX... Viz např. Minimální a maximální hodnoty

Integrální typy - "char"

charsearch in Czech Wikipedia představuje jeden 16bitový znak v kódování UNICODE

Konstanty typu charsearch in Czech Wikipedia zapisujeme

  • v apostrofech - 'a', 'Ř'

  • pomocí escape-sekvencí - \n (konec řádku) \t (tabulátor)

  • hexadecimálně - \u0040 (totéž, co 'a')

  • oktalově - \127

Typ charsearch in Czech Wikipedia - kódování

Java vnitřně kóduje znaky a řetězce v UNICODE, pro vstup a výstup je třeba použít některou za serializací (převodu) UNICODE na sekvence bajtů:

  • např. vícebajtová kódování UNICODE: UTF-8 a UTF-16

  • osmibitová kódování ISO-8859-x, Windows-125x a pod.

Problém může nastat při interpretaci kódování znaků národních abeced uvedených přímo ve zdrojovém textu programu.

Ve zdroj. textu správně napsaného javového vícejazyčného programu by žádné národní znaky VŮBEC neměly vyskytovat.

Je vhodné umístit je do speciálních souborů tzv. zdrojů (v Javě objekty třídy java.util.ResourceBundlesearch in Czech Wikipedia).

Čísla s pohyblivou řádovou čárkou

Kódována podle ANSI/IEEE 754-1985

Možné zápisy literálů typu floatsearch in Czech Wikipedia (klasický i semilogaritmický tvar) - povšimněte si "f" za číslem - je u float nutné!:

float f = -.777f, g = 0.123f, h = -4e6f, 1.2E-15f;search in Czech Wikipedia

doublesearch in Czech Wikipedia: tentýž zápis, ovšem bez "f" za konstantou!, s větší povolenou přesností a rozsahem

Vestavěné konstanty s pohyblivou řádovou čárkou

Kladné a záporné nekonečno:

Konstanta NaN search in Czech Wikipedia - Not A Number

Viz také Minimální a maximální hodnoty

Typ logických hodnot - boolean

Přípustné hodnoty jsou falsesearch in Czech Wikipedia a truesearch in Czech Wikipedia.

Na rozdíl od Pascalu na nich není definováno uspořádání, nelze je porovnávat pomocí <, >, <=, >=.

Typ voidsearch in Czech Wikipedia

Význam podobný jako v C/C++.

Není v pravém slova smyslu datovým typem, nemá žádné hodnoty.

Označuje "prázdný" typ pro sdělení, že určitá metoda nevrací žádný výsledek.

Pole

Pole v Javě

Pole v Javě je speciálním objektem

Můžeme mít pole jak primitivních, tak objektových hodnot

  • pole primitivních hodnot tyto hodnoty obsahuje

  • pole objektů obsahuje odkazy na objekty

Kromě pole v Javě existují i jiné objekty na ukládání více hodnot - tzn. kontejnery, viz dále

Syntaxe deklarace

typhodnoty [] jménopolesearch in Czech Wikipedia

[Poznámka]Poznámka

na rozdíl od C/C++ nikdy neuvádíme při deklaraci počet prvků pole - ten je podstatný až při vytvoření objektu pole

Syntaxe přístupu k prvkůmjménopole[indexprvku]Používáme

Syntaxe vytvoření objektu pole: jako u jiného objektu - voláním konstruktoru:

jménopole = new typhodnoty[ početprvků ]; nebo vzniklé pole rovnou naplníme hodnotami/odkazy

jménopole = new typhodnoty[] {prvek1, prvek2, ...};

Pole (2)

Pole je objekt, je třeba ho před použitím nejen deklarovat, ale i vytvořit:

Person[] lidi; 
lidi = new Person[5];    

Cloveksearch in Czech Wikipedia

Nyní můžeme pole naplnit:

lidi[0] = new Person("Václav Klaus"); 
lidi[1] = new Person("Libuše Benešová"); 
lidi[0].writeInfo(); 
lidi[1].writeInfo();    
  • Nyní jsou v poli lidisearch in Czech Wikipedia naplněny první dva prvky odkazy na objekty.

  • Zbylé prvky zůstaly naplněny prázdnými odkazy nullsearch in Czech Wikipedia.

Pole - co když deklarujeme, ale nevytvoříme?

Co kdybychom pole pouze deklarovali a nevytvořili:

Person[] lidi; 
lidi[0] = new Person("Václav Klaus");    

Toto by skončilo s běhovou chybou "NullPointerException", pole neexistuje, nelze do něj tudíž vkládat prvky!

Pokud tuto chybu uděláme v rámci metody:

public class Pokus { 
   public static void main(String args[]) { 
      String[] pole; 
      pole[0] = "Neco"; 
   }
}    

překladač nás varuje:

Pokus.java:4: variable pole might not have been
    initialized pole[0] = "Neco"; ^ 1 error 

Pokud ovšem

polesearch in Czech Wikipedia

bude proměnnou objektu/třídy:

public class Pokus {
   static String[] pole; 
   public static void main(String args[]) { 
      pole[0] = "Neco"; 
   }
} 

Překladač chybu neodhalí a po spuštění se objeví:

Exception in thread "main"
    java.lang.NullPointerException at Pokus.main(Pokus.java:4)    

Pole - co když deklarujeme, vytvoříme, ale nenaplníme?

Co kdybychom pole deklarovali, vytvořili, ale nenaplnili příslušnými prvky:

Person[] lidi; 
lidi = new Person[5]; 
lidi[0].writeInfo();

Toto by skončilo také s běhovou chybou NullPointerException:

  • pole existuje, má pět prvků, ale první z nich je prázdný, nelze tudíž volat jeho metody (resp. vůbec používat jeho vlastnosti)!

Kopírování polí

V Javě obecně přiřazení proměnné objektového typu vede pouze k duplikaci odkazu, nikoli celého odkazovaného objektu.

Nejinak je tomu u polí, tj.:

Person[] lidi2; 
lidi2 = lidi1;

V proměnnélidi2je nyní odkaz na stejné pole jako je vlidi1.

Zatímco, provedeme-li vytvoření nového pole + arraycopy, pak lidi2 obsahuje duplikát/klon/kopii původního pole.

Person[] lidi2 = new Person[5]; 
System.arraycopy(lidi, 0, lidi2, 0, lidi.length);

viz též Dokumentace API třídy "System"

[Poznámka]Poznámka

Samozřejmě bychom mohli kopírovat prvky ručně, např. pomocí forsearch in Czech Wikipedia cyklu, ale volání System.arraycopy je zaručeně nejrychlejší a přitom stále platformově nezávislou metodou, jak kopírovat pole.

Také arraycopy však do cílového pole zduplikuje jen odkazy na objekty, nevytvoří kopie objektů!

Operátory a výrazy

Cíl

Zvládnout použití operátorů v Javě a naučit se sestavovat výrazy různých typů

Předpoklady

Znát obecné principy syntaxe a vyhodnocování výrazů v pg. jazycích (např. Pascalu)

  • Operátory v Javě: aritmetické, logické, relační, bitové

  • Ternární operátor podmíněného výrazu

  • Typové konverze

  • Operátor zřetězení

Aritmetické

+search in Czech Wikipedia, -search in Czech Wikipedia, *search in Czech Wikipedia, /search in Czech Wikipedia a %search in Czech Wikipedia (zbytek po celočíselném dělení)

Pozn: operátor dělení / je polymorfní, funguje pro celočíselné argumenty jako celočíselný, pro floating-point (float, double) jako "obyčejný".

Logické

Pracují nad logickými (booleovskými) hodnotami (samozřejmě vč. výsledků porovnávání <, >, ==, atd.).

logické součiny (AND):

  • & search in Czech Wikipedia (nepodmíněný - vždy se vyhodnotí oba operandy),

  • && search in Czech Wikipedia (podmíněný - líné vyhodnocování - druhý operand se vyhodnotí, jen nelze-li o výsledku rozhodnout z hodnoty prvního)

logické součty (OR):

  • | search in Czech Wikipedia (nepodmíněný - vždy se vyhodnotí oba operandy),

  • || search in Czech Wikipedia (podmíněný - líné vyhodnocování - druhý operand se vyhodnotí, jen nelze-li o výsledku rozhodnout z hodnoty prvního)

negace (NOT):

Relační (porovnávací)

Tyto lze použít na porovnávání primitivních hodnot:

Test na rovnost/nerovnost lze použít na porovnávání primitivních hodnot i objektů:

Upozornění:

  • Pozor na porovnávání objektů: == vrací true jen při rovnosti odkazů, tj. jsou-li objekty identické. Rovnost obsahu (tedy "rovnocennost") objektů se zjišťuje voláním metody o1.equals(Object o2)

  • Pozor na srovnávání floating-points čísel na rovnost: je třeba počítat s chybami zaokrouhlení; místo porovnání na přesnou rovnost raději používejme jistou toleranci: abs(expected-actual) < delta

Bitové

Bitové:

  • součin & search in Czech Wikipedia

  • součet | search in Czech Wikipedia

  • exkluzivní součet (XOR) ^ search in Czech Wikipedia (znak "stříška")

  • negace (bitwise-NOT) ~ search in Czech Wikipedia (znak "tilda") - obrátí bity argumentu a výsledek vrátí

Posuny:

  • vlevo <<search in Czech Wikipedia o stanovený počet bitů

  • vpravo >>search in Czech Wikipedia o stanovený počet bitů s respektováním znaménka

  • vpravo >>>search in Czech Wikipedia o stanovený počet bitů bez respektování znaménka

Dále viz např. Bitové operátory

Operátor podmíněného výrazu ? :search in Czech Wikipedia

Jediný ternární operátor, navíc polymorfní, pracuje nad různými typy 2. a 3. argumentu.

Platí-li první operand (má hodnotu truesearch in Czech Wikipedia) ->

  • výsledkem je hodnota druhého operandu

  • jinak je výsledkem hodnota třetího operandu

Typ prvního operandu musí být booleansearch in Czech Wikipedia, typy druhého a třetího musí být přiřaditelné do výsledku.

Operátory typové konverze (přetypování)

  • Podobně jako v C/C++

  • Píše se (typ)hodnota, např. (Person)o, kde o byla proměnná deklarovaná jako Object.

  • Pro objektové typy se ve skutečnosti nejedná o žádnou konverzi spojenou se změnou obsahu objektu, nýbrž pouze o potvrzení (tj. typovou kontrolu), že běhový typ objektu je požadovaného typu - např. (viz výše) že o je typu Person.

  • Naproti tomu u primitivních typů se jedná o úpravu hodnoty - např. int přetypujeme na short a „ořeže“ se tím rozsah.

Operátor zřetězení +search in Czech Wikipedia

Výsledkem je vždy řetězec, ale argumenty mohou být i jiných typů, např.

sekvence int i = 1; System.out.println("promenna i="+i);search in Czech Wikipedia je v pořádku

s řetězcovou konstantou se spojí řetězcová podoba dalších argumentů (např. čísla).

Pokud je argumentem zřetězení odkaz na objekt o search in Czech Wikipedia->

  • je-li o == nullsearch in Czech Wikipedia -> použije se řetězec "null"search in Czech Wikipedia

  • je-li o != nullsearch in Czech Wikipedia -> použije se hodnota vrácená metodou o.toString()search in Czech Wikipedia (tu lze překrýt a dosáhnout tak očekávaného řetězcového výstupu)

Priority operátorů a vytváření výrazů

nejvyšší prioritu má násobení, dělení, nejnižší přiřazení

Porovnávání objektů

  • Porovnávání primitivních hodnot a objektů je zásadně odlišné.

  • U objektů lze kromě == a != použít metodu equals.

Relační (porovnávací)

Na objekty nelze použít:

Zatímco test na rovnost/nerovnost lze použít na porovnávání primitivních hodnot i objektů:

Znovu podstatné upozornění:

  • pozor na porovnávání objektů: == vrací true jen při rovnosti odkazů, tj. jsou-li objekty identické. Rovnost obsahu (tedy "rovnocennost") objektů se zjišťuje voláním metody o1.equals(Object o2)

Porovnávání objektů

Použití ==

  • Porovnáme-li dva objekty (tzn. odkazy na objekty) prostřednictvím operátoru == dostaneme rovnost jen v případě, jedná-li se o dva odkazy na tentýž objekt - tj. dva totožné objekty.

  • Jedná-li se o dva obsahově stejné objekty existující samostatně, pak == vrátí false.

Chceme-li (intuitivně) chápat rovnost objektů podle obsahu, tj.

  • dva objekty jsou rovné (rovnocenné, nikoli totožné), mají-li stejný obsah, pak

  • musíme pro danou třídu překrýt metodu equals, která musí vrátit true, právě když se obsah výchozího a srovnávaného objektu rovná.

  • Fungování equals lze srovnat s porovnáváním dvou databázových záznamů podle primárního klíče.

  • Nepřekryjeme-li equals, funguje původní equals přísným způsobem, tj. rovné si budou jen totožné objekty.

Porovnávání objektů - příklad

Příklad: objekt třídy Clovek nese informace o člověku. Dva objekty položíme stejné (rovnocenné), nesou-li stejná příjmení:

Obrázek 1. Dva lidi jsou stejní, mají-li stejná příjmení

public class Person implements Comparable { 
   private String firstname;
   private String surname; 
   public Person (String j, String p) { 
      firstname = j;
      surname = p;
   } 
   public boolean equals(Object o) { 
      if (o instanceof Person) { 
         Person c = (Person)o; 
         // dva lidé se (v našem případě) rovnají, mají-li stejná příjmení
         return surname.equals(c.surname); 
      } else {
         // porovnáváme-li osobu s objektem jiného typu, nikdy se nerovnají
         return false;
      }
   }
}

Méně agresivní verze by nemusela při porovnávání s jiným objektem než Person vyhodit výjimku, pouze vrátit false.

Metoda hashCode

Jakmile u třídy překryjeme metodu equals, měli bychom současně překrýt objektů i metodu hashCode:

  • hashCode vrací celé číslo (int) „co nejlépe“ charakterizující obsah objektu, tj.

  • pro dva stejné (equals) objekty musí vždy vrátit stejnou hodnotu.

  • Pro dva obsahově různé objekty by hashCode naopak měl vracet různé hodnoty (ale není to stoprocentně nezbytné a ani nemůže být vždy splněno).

    Metoda hashCode totiž nemůže vždy být prostá.

Metoda hashCode - příklad

V těle hashCode s oblibou „přehráváme“ (delegujeme) řešení na volání hashCode jednotlivých složek objektu - a to těch, které figurují v equals:

Obrázek 2. Třída Clovek s metodami equals a hashCode

public class Person implements Comparable { 
   private String firstname;
   private String surname; 
   public Person (String j, String p) { 
      firstname = j;
      surname = p;
   } 
   public boolean equals(Object o) { 
      if (o instanceof Person) { 
         Person c = (Person)o; 
         // dva lidé se (v našem případě) rovnají, mají-li stejná příjmení
         return surname.equals(c.surname); 
      } else {
         // porovnáváme-li osobu s objektem jiného typu, nikdy se nerovnají
         return false;
      }
   }
   public int hashCode() { 
      return surname.hashCode(); 
   }
}