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
Tomáš Pitner, Radek Ošlejšek, Marek Šabo
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
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
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ěť
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
final
final Person p = new Person("Marek");
// p = new Person("Jan"); // cannot be done
p.setName("Haha I changed it"); // this works!
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žít StringBuilder
, který je modifikovatelný (mutable), viz dále
Podívejme se na rozdíl "Hello"
a new 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
String
char charAt(int index)
— vráti prvek na daném indexu
static String format(String format, Object… args)
— stejné jako printf
v C
boolean isEmpty()
— vráti true
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!
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í varianta StringBuffer
.
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;
Wrappers (např. Double
) mají různé konstanty:
MIN_VALUE
je minimální hodnota jakou může double
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;
např. pro Double
existuje static double parseDouble(String s)
— udělá ze String
číslo, z "1.0"
, vytvoří číslo 1.0
obdobně pro Integer
a další číselné typy
pro převody na číselné typy dále int intValue()
převod double
do typu int
boolean isNaN()
— test, jestli není hodnotou číslo
Více konstant a metod popisuje javadoc. |
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
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ísto int
může mít dopad na výkon
a zcela jistě zabere dost paměti, asi zbytečně
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
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ě :-)
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 o equals
).