Neměnné objekty
-
Neměnný (immutable) objekt nemůže být po jeho vytvoření modifikován
-
Bezpečně víme, co v něm až do konce života bude
-
Tudíž může být souběžně používán z více míst, aniž by hrozily nekonzistence
-
Jsou prostředkem, jak psát robustní a bezpečný kód
-
K jejich vytvoření nepotřebujeme žádný speciální nástroj
Příklad neměnného objektu
public class Vertex1D {
private int x;
public Vertex1D(int x) {
this.x = x;
}
public int getX() {
return x;
}
}
...
Vertex1D v = new Vertex1D(1);
// x of value 1 cannot be changed anymore
Výhody a nevýhody
-
je to vláknově bezpečné (thread safe) — objekt může být bezpečně používán více vlákny naráz
-
programátor má jistotu, že se mu obsah objektu nezmění — silný předpoklad
-
kód je čitelnější, udržovanější i bezpečnější (např. útočník nemůže změnit náš token)
-
chceme-li objekt byť jen drobně změnit, musíme vytvořit nový
-
to stojí čas a paměť
Porovnání
-
Neměnný objekt vs. konstanta
-
konstanta je jenom jedna (je statická)
-
neměnných objektů může být víc s různými hodnotami
-
kdyby jich bylo více a měly stejnou hodnotu, postrádá smysl
-
-
Neměnný objekt vs.
final
-
final
prakticky zakáže změnu odkazu -
nelze tedy do dané proměnné přiřadit odkaz na jiný objekt
-
samotný objekt ale může být dál "vevnitř" modifikován
-
Příklad final
final Person p = new Person("Marek");
// p = new Person("Jan"); // cannot be done
p.setName("Haha I changed it"); // this works!
Vestavěné neměnné třídy
-
Neměnnou třídou v Javě je
String
. -
Má to řadu dobrých důvodů - tytéž jednou definované řetězce lze používat souběžně z více míst programu
-
Nicméně i negativní stránky - někdy větší režie spojená s nemodifikovatelností:
String s = "Hello " + "World";
-
Kód vytvoří 3 objekty:
"Hello "
,"World"
a"Hello World"
. -
Cože? To je tak neefektivní?
-
Pokud by vysloveně vadilo, lze místo
String
použítStringBuilder
, který je modifikovatelný (mutable), viz dále
String pod lupou
-
Podívejme se na rozdíl
"Hello"
anew String("Hello")
.new String("Hello")
-
vytvoří pokaždé nový objekt
"Hello"
-
funguje na principu: jestli taký objekt zatím neexistuje, tak ho vytvořím, jinak vrátím odkaz na již existující objekt (uložen v paměti String constant pool)
String s1 = "Hello";
String s2 = "Hello";
boolean bTrue = (s1 == s2); // true, identical objects
s1 = new String("Hello");
s2 = new String("Hello");
boolean bFalse = (s1 == s2); // false, different objects though with same value
Metody třídy String
-
char charAt(int index)
— vráti prvek na daném indexu -
static String format(String format, Object… args)
— stejné jakoprintf
v C -
boolean isEmpty()
— vrátitrue
jestli je prázdný -
int length()
— velikost řetězce -
matches
,split
,indexOf
,startsWith
…
Více metod najdete v dokumentaci třídy String. |
-
Doporučujeme javadoc prostudovat, používaním existujících metod jsi ušetříte spoustu práce!
Třída StringBuilder
StringBuilder builder = new StringBuilder("Hello ");
builder.append("cruel ").append("world"); // method chain
builder.append("!");
String result = builder.toString();
-
StringBuilder
se průběžně modifikuje, přidáváme do něj další znaky -
Na závěr vytvoříme výsledný řetězec
-
StringBuilder
není thread safe, proto existuje její variantaStringBuffer
.
Objektové obálky nad primitivními hodnotami
-
Java má primitivní typy —
int
,char
,double
, … -
Ke každému primitivnímu typu existuje varianta objektového typu —
Integer
,Character
,Double
, … -
Tyto objektové typy se nazývají wrappers.
-
Objekty jsou neměnné
-
Při vytváření takových objektů není nutné používat
new
,-
využije se tzv. autoboxing, např.
Integer five = 5;
-
Obdobně autounboxing,
int alsoFive = five;
-
Integer objectInt = new Integer(42);
Integer objectInt2 = 42;
Konstanty objektových obálek
-
Wrappers (např.
Double
) mají různé konstanty:-
MIN_VALUE
je minimální hodnota jakou můžedouble
obsahovat -
POSITIVE_INFINITY
reprezentuje konstantu kladné nekonečno -
NaN
je zkratkou Not-a-Number — dostaneme ji např. dělením nuly
-
-
Protože konstanty jsou statické, jejich hodnoty získáme přes název třídy:
double d = Double.MIN_VALUE;
d = Double.NEGATIVE_INFINITY;
Metody objektových obálek
-
např. pro
Double
existujestatic double parseDouble(String s)
— udělá zeString
číslo, z"1.0"
, vytvoří číslo1.0
-
obdobně pro
Integer
a další číselné typy -
pro převody na číselné typy dále
int intValue()
převoddouble
do typuint
-
boolean isNaN()
— test, jestli není hodnotou číslo
Více konstant a metod popisuje javadoc. |
Víc hodnot
-
Test: Objektové typy (
Integer
) mají od primitivních (int
) jednu hodnotu navíc — uhádněte jakou!
-
je to
null
-
Integer
je objektový typ, proměnná je odkaz na objekt
Rozdíl od primitivních typů
Proč tedy vůbec používat primitivní typy, když máme typy objektové?
int i = 1
-
zabere v paměti právě jen 32 bitů
-
používáme přímo danou paměť, jednička je uložena přímo v
i
Integer i = 1
-
je třeba alokace paměti pro objekt, zkonstruování objektu s obsahem
1
-
v proměnné
i
je pouze odkaz, je to (nepatrně) pomalejší -
Výkon může být u velkého počtu objektů problém, např. vytvoření miliónu proměnných typu
Integer
namístoint
může mít dopad na výkon a zcela jistě zabere dost paměti, asi zbytečně
Doporučení
-
Používejte hlavně primitivní typy
-
Využívejte metody objektových typů, hlavně statické, kde není třeba mít objekt
-
Řada objektových jazyků vůbec primitivní typy jako v Javě nemá, vše jsou objekty
Přechod mezi primitivními a objektovými typy
-
Java podporuje automatické balení (boxing) a vybalení (unboxing) mezi primitivními typy a wrappery.
-
Proto je následující kód je naprosto v pořádku
int primitiveInt = 42;
Integer objectInt = primitiveInt;
primitiveInt = new Integer(43);
-
Jak už bylo řečeno, použití primitivního typu je obvykle lepší nápad - jsme v Javě :-)
Zvláštnosti
-
Zajímavost, anebo spíš podraz Javy:
Integer i1 = 100; // between -127 and 128
Integer i2 = 100;
boolean referencesAreEqual = (i1 == i2); // true
i1 = 300;
i2 = 300;
boolean referencesNotEqual = (i1 == i2); // false
-
Poučení: objekty pomocí
==
obvykle neporovnáváme (budeme se učit oequals
).