Násedují úkoly ze cvičení (2019/2020), kde to bylo možné tak se správnou odpovědí a vysvětlením. Důležité příklady, které bývají na zkouškách, jsou označeny jako zkouškový příklad.
Cvičení 1
Co se vypíše?
Integeri1=newInteger(1);
Integeri2=newInteger(1);
if (i1==i2)
System.out.println("ANO");
else
System.out.println("NE");
Řešení: NE
== porovnává pro referenční typy (což jsou všechny objekty, i Integer) pouze ukazatele do paměti, ne hodnoty. i1 a i2 jsou v tomto případě různé objekty s různými pozicemi v paměti.
Co se vypíše?
xxxxxxxxxx
publicclassOverflow {
publicstaticvoidmain(String[] argv) {
intb=2147483647;
System.out.println(b);
b=b+1;
System.out.println(b);
}
}
Řešení: 2147483647 -2147483648
2147483647 je Integer.MAX_VALUE, když k ní přičteme jedničku, dojde k overflow (jede se opět "od začátku" intu).
Co se vypíše?
xxxxxxxxxx
classURL {
publicstaticvoidmain(String[] argv) {
System.out.println("url:");
http://google.com/
System.out.println(":url");
}
}
Řešení: url: :url
V http://google.com/ se vše po // bere jako komentář a http: se bere jako label. Labely fungují podobně jako v Pascalu, lze na ně skočit pomocí continue.
Co se vypíše?
xxxxxxxxxx
publicclassSwap {
publicstaticvoidmain(String[] argv) {
intx=10;
inty=20;
x^=y^=x^=y;
System.out.println(x);
System.out.println(y);
}
}
Řešení: 0 10
a ^= b odpovídá a = a ^ b. a ^ b je operace bitwise XOR, při níž se a a b berou jako bity (čili pokud to jsou integery, berou se převedené do binární soustavy). Kratší z nich se zepředu doplní nulami, aby byla obě čísla stejně dlouhá. Poté se porovná i-tý bit z a s i-tým bitem z b pomocí operace XOR.
x ^= y ^= x ^= y lze pak přepsat jako x = x ^ (y = y ^ (x = x ^ y)), což je výraz, který se vyhodnocuje zleva. Po dosazení vyjde x = 10 ^ (y = 20 ^ (x = 10 ^ y)) atd.
Co se vypíše?
xxxxxxxxxx
classForCycle {
publicstaticvoidmain(String[] argv) {
intj=0;
for (inti=Integer.MAX_VALUE-10; i<=Integer.MAX_VALUE; i++) {
j++;
}
System.out.println(j);
}
}
Řešení: Nekonečný cyklus (nevypíše se nic)
int i nikdy nebude větší než Integer.MAX_VALUE, protože je to jeho horní hranice.
Cvičení 2
Doplňte deklaraci i, aby se vypsalo ANO
xxxxxxxxxx
if (i==-i&&i!=0) {
System.out.println(“ANO“);
} else {
System.out.println(“NE“);
}
Řešení:i = Integer.MIN_VALUE
Když ho znegujeme, dostaneme Integer.MAX_VALUE + 1, což je zase Integer.MIN_VALUE.
Co se vypíše?
x
publicclassLoopTest {
publicstaticvoidmain(String[] argv) {
intSTART=2000000000;
intcount=0;
for (floatf=START; f<START+50; f++) {
count++;
}
System.out.println(count);
}
}
Řešení: 0
Má to co dočinění s floating-point čísly. START je sice na začátku int, kvůli porovnání s f je však převeden na float. START jen o málo menší než , proto v jeho binárním zápisu bude určitě 31. bit jednička. float čísla jsou sice 32 bitová, ale 1 bit z toho je na znaménko, dalších 8 je na exponent a tak na samotné číslo zbude pouze 23 bitů.
Protože k uložení start potřebujeme mít 31. bit nastavený na 1, při převodu na float si ze START necháme následujících 23 bitů: [31., 30., 29., ..., 8.] — jinými slovy prvních sedm bitů ze START odstřihneme, neboť se nám do floatu nevlezou. Protože přičtená 50 se vleze do prvních sedmi bitů, a my těchto sedm bitů poté odstřihneme, přičtení 50 se vůbec neprojeví a platí START == f == START + 50 (pokud je START převeden na float).
Cvičení 3
Co se vypíše?
xxxxxxxxxx
publicclassTest01 {
publicstaticvoidmain(String[] argv) {
System.out.println(test());
}
publicstaticbooleantest() {
try {
returntrue;
} finally {
returnfalse;
}
}
}
Řešení:false
finally přebíjí většinu věcí, i return.
Co se vypíše?
xxxxxxxxxx
publicclassTest01 {
publicstaticvoidmain(String[] argv) {
try {
System.out.println("Hello world!");
System.exit(0);
} finally {
System.out.println("Goodbye");
}
}
}
Řešení: "Hello world!"
finally přebíjí většinu věcí, ale System.exit() je jedna z výjimek — prostě rovnou ukončí průběh programu. Kromě něj takto funguje i halt a pak už většinou jen pády JVM nebo samotného operačního systému.
Co se vypíše?
xxxxxxxxxx
classParamsTest {
publicParamsTest(Objecto) {
System.out.println("ParamsTest(Object o)");
}
publicParamsTest(long[] a) {
System.out.println("ParamsTest(long[] a)");
}
publicstaticvoidmain(String[] argv) {
newParamsTest(null);
}
}
Řešení: ParamsTest(long[] a)
Podle skutečných parametrů se vybere ta nejspecifičtější vyhovující implementace (zde long[] a).
Co se vypíše? (zkouškový příklad)
x
classA {
intx=1;
}
classBextendsA {
intx=2;
publicvoidfoo() {
System.out.println(this.x);
System.out.println(super.x);
System.out.println(((A)this).x);
}
publicstaticvoidmain(String[] args) {
Bb=newB();
b.foo();
}
}
Řešení: 2 1 1
Statické metody se koukají pouze na typ objektu, ne na jeho hodnotu; podle toho se pak zavolá konkrétní implementace. Viz také tato otázka na Stack Overflow.
Cvičení 4
Co program udělá? (zkouškový příklad)
xxxxxxxxxx
publicclassNull {
publicstaticvoidmain(String[] argv) {
((Null) null).hello();
}
publicstaticvoidhello() {
System.out.println("Hello world!");
}
}
Řešení: vypíše se "Hello world!"
Že se třída jmenuje Null je v pořádku. Poté sice voláme null.hello(), ale hello() je statická metoda, čili vůbec nekouká na reálnou hodnotu objektu — pouze na jeho typ (a to je Null). Vše tedy funguje, jak má.
Co program udělá?
xxxxxxxxxx
classTest01 {
publicstaticvoidmain(String[] argv) {
run();
System.out.println("Konec");
}
publicstaticvoidrun() {
try {
run();
} finally {
run();
}
}
}
Řešení: Myslím, že vyhodí StackOverflowError.
Co program vypíše?
xxxxxxxxxx
classA {
publicStringclassName="A";
}
classBextendsA {
privateStringclassName="B";
}
publicclassTest02 {
publicstaticvoidmain(String[] argv) {
System.out.println(newB().className);
}
}
Řešení: Nepřeloží se, ve třídě Test02 je chyba.
Deklarace className v B schová atribut className z A (doslova se to jmenuje field hiding). To, že je sama private na věci nic nemění. Protože původní className z A je schovaná a className z B je zase private, je chyba vůbec se snažit tohoto atributu dosáhnout.
Cvičení 5
Co se vypíše?
xxxxxxxxxx
publicinterfaceTest {
publicstaticvoidmain(String[] argv) {
System.out.println("Hello");
}
}
Řešení: Je to úplně v pořádku, vypíše se Hello.
V interfacu mohou totiž být i statické metody.
Co se vypíše? (zkouškový příklad)
xxxxxxxxxx
publicenumTest {
RED, GREEN, BLUE;
publicstaticvoidmain(String[] argv) {
System.out.println("Hello");
}
}
Řešení: Je to úplně v pořádku, vypíše se Hello.
enum je v podstatě jen vylepšená třída, proto může obsahovat statické metody. Nemůže však dědit, neboť implicitně již dědí od java.lang.Enum. Může dokonce obsahovat i abstraktní metody, pak je ale musí implementovat každý z prvků enumu.
Cvičení 6
Co se vypíše?
xxxxxxxxxx
publicclassGreeter {
publicstaticvoidmain (String[] args) {
Stringgreeting="Hello world";
for (inti=0; i<greeting.length(); i++) {
System.out.write(greeting.charAt(i));
}
}
}
Řešení: Nevypíše se nic.
PrintStream.write() sice na stream přidá dané znaky, ale stream pak automaticky nevyprázdní do konzole — proto nejde nic vidět. Naproti tomu println() se o toto stará, čili kdybychom za loop přidali System.out.println("");, vypsalo by se už Hello world.
System.out.println("Trida "+fullClassName+" musi byt v souboru "+fileName);
}
}
Řešení: Trida cz.cuni.mff.java.io.Slasher musi byt v souboru ///////////////////////////.java
String.replaceAll() bere jako první argument regex, ne obyčejný string. Proto . je interpretováno regex enginem jako "jakýkoli znak" a všechny znaky jsou proto nahrazeny lomítkem.
Cvičení 7
Co program udělá?
xxxxxxxxxx
publicclassTestString {
publicstaticvoidmain(String[] args) {
Strings=newString("Hello world");
System.out.println(s);
}
}
classString {
privatefinaljava.lang.Strings;
publicString(java.lang.Strings) {
this.s=s;
}
publicjava.lang.StringtoString() {
returns;
}
}
Řešení: Přeloží se, ale při runtimu vyhodí chybu, že mu chybí metoda main.
main má totiž v této chvíli mít hlavičku public static void main(java.lang.String[] args), protože String je teď ta naše nová třída a ne původní String.
Lze třídu B nadeklarovat tak, aby program vypsal false? (bez přepsání equals)
xxxxxxxxxx
publicclassA {
publicstaticvoidmain(String[] args) {
Bb=newB();
System.out.println(b.equals(b));
}
}
Řešení: Ano, ale je to dost podvod.
xxxxxxxxxx
classB {
publicB() {
System.out.println(false);
System.exit(0);
}
}
Cvičení 8
Co se vypíše?
xxxxxxxxxx
publicclassTest01 {
publicstaticsynchronizedvoidmain(String[] a) {
Threadt=newThread() {
publicvoidrun() {
pong();
}
};
t.start();
System.out.println("Ping");
}
staticsynchronizedvoidpong() {
System.out.println("Pong");
}
}
Řešení: Ping Pong
Obě metody jsou static synchronized, čili berou zámek přímo od třídy Test01. Když už jsme v main, má současné vlákno zámek (protože vstoupilo do synchronized bloku) a tak se vlákno t (potažmo metoda pong()) spustí až poté, co skončí to naše vlákno.
Thread.interrupted() totiž nejen vrátí současnou hodnotu .interrupted(), ale také ji resetuje na false. Kdyby se místo toho použilo this.isInterrupted(), který po vrácení žádný reset neprovádí, vypsalo by se Interrupted: true.
V case nejsou breaky, takže word je vždy nakonec nastaven na new StringBuffer('M'). Protože StringBuffer dostal char (potažmo int) bere to jako hranici kapacity (ne jako první písmeno — to by musel dostat "M").
Co se vypíše?
xxxxxxxxxx
publicclassTest02 {
publicstaticvoidmain(Stringargs[]) {
System.out.println("H"+"a");
System.out.println('H'+'a');
}
}
Řešení: Ha 169
char + char je v tomto případě interpretováno jako součet intů. Pro zajímavost, println("" + 'H' + 'a') by vypsalo skutečně Ha [laughs in javascript].
Cvičení 10
Lze definovat i tak, aby byl cyklus nekonečný?
xxxxxxxxxx
while (i!=i) { }
Řešení: Ano, Double.NaN se nerovná ničemu, ani sám sobě.
Co se vypíše?
xxxxxxxxxx
publicclassIncrement {
publicstaticvoidmain(String[] args) {
intj=0;
for (inti=0; i<100; i++) {
j=j++;
}
System.out.println(j);
}
}
Řešení: 0
(Myslím, že) j++ vrátí 0 a nastaví j na 1; j = j++ pak vezme 0 z j++ a uloží jí do j.
Cvičení 11
Lze od této třídy (bez použití reflection API) vytvořit další instance (kromě instance v atributu INSTANCE)?
xxxxxxxxxx
publicclassDogimplementsSerializable {
publicstaticfinalDogINSTANCE=newDog();
privateDog() { }
publicStringtoString() {
return"Woof";
}
}
Řešení: Leda bychom přidali něco do třídy samotné (jinak máme příklad tzv. singleton pattern, což je třída pouze s jednou instancí, zde konkrétně INSTANCE).
xxxxxxxxxx
publicclassDogimplementsSerializable {
publicstaticfinalDogINSTANCE=newDog();
privateDog() { }
publicStringtoString() {
return"Woof";
}
publicDoggetDog() {
returnnewDog();
}
}
Dogd=Dog.getDog();
V tomto případě naopak používám tzv. factory pattern (máme metody, které nejsou konstruktory, ale přesto vyrábějí a vrací objekt své třídy).
Cvičení 12
Lze napsat deklaraci proměnné i tak, aby následující cyklus byl nekonečný?
xxxxxxxxxx
while (i!=i+0) { }
Řešení: Ano, např. String i = "a", potom i + 0 == "a0".
Lze napsat deklaraci proměnných i a j tak, aby následující cyklus byl nekonečný?
xxxxxxxxxx
while (i<=j&&j<=i&&i!=j) { }
Řešení: Ano, například i = new Integer(1) a j = new Integer(1).
Protože se jedná o referenční typy (objekty), výraz i != j je pravda, protože porovnává umístění v paměti a ne samotné hodnoty i a j.
581 |
582 |
--------------------------------------------------------------------------------
/java-cvika.md:
--------------------------------------------------------------------------------
1 | # Java (úkoly ze cvičení)
2 |
3 | Násedují úkoly ze cvičení (2019/2020), kde to bylo možné tak se správnou odpovědí a vysvětlením. Důležité příklady, které bývají na zkouškách, jsou označeny jako *zkouškový příklad*.
4 |
5 | ## Cvičení 1
6 |
7 | **Co se vypíše?**
8 |
9 | ```java
10 | Integer i1 = new Integer(1);
11 | Integer i2 = new Integer(1);
12 | if (i1 == i2)
13 | System.out.println("ANO");
14 | else
15 | System.out.println("NE");
16 | ```
17 |
18 | *Řešení:* NE
19 |
20 | `==` porovnává pro referenční typy (což jsou všechny objekty, i `Integer`) pouze ukazatele do paměti, ne hodnoty. `i1` a `i2` jsou v tomto případě různé objekty s různými pozicemi v paměti.
21 |
22 | **Co se vypíše?**
23 |
24 | ```java
25 | public class Overflow {
26 | public static void main(String[] argv) {
27 | int b = 2147483647;
28 | System.out.println(b);
29 | b = b + 1;
30 | System.out.println(b);
31 | }
32 | }
33 | ```
34 |
35 | *Řešení:* 2147483647 -2147483648
36 |
37 | 2147483647 je Integer.MAX_VALUE, když k ní přičteme jedničku, dojde k *overflow* (jede se opět "od začátku" intu).
38 |
39 | **Co se vypíše?**
40 |
41 | ```java
42 | class URL {
43 | public static void main(String[] argv) {
44 | System.out.println("url:");
45 | http://google.com/
46 | System.out.println(":url");
47 | }
48 | }
49 | ```
50 |
51 | *Řešení:* url: :url
52 |
53 | V `http://google.com/` se vše po `//` bere jako komentář a `http:` se bere jako label. Labely fungují podobně jako v Pascalu, lze na ně skočit pomocí `continue`.
54 |
55 | **Co se vypíše?**
56 |
57 | ```java
58 | public class Swap {
59 | public static void main(String[] argv) {
60 | int x = 10;
61 | int y = 20;
62 | x ^= y ^= x ^= y;
63 | System.out.println(x);
64 | System.out.println(y);
65 | }
66 | }
67 | ```
68 |
69 | *Řešení:* 0 10
70 |
71 | `a ^= b` odpovídá `a = a ^ b`. `a ^ b` je operace bitwise `XOR`, při níž se `a` a `b` berou jako bity (čili pokud to jsou integery, berou se převedené do binární soustavy). Kratší z nich se zepředu doplní nulami, aby byla obě čísla stejně dlouhá. Poté se porovná i-tý bit z `a` s i-tým bitem z `b` pomocí operace XOR.
72 |
73 | `x ^= y ^= x ^= y` lze pak přepsat jako `x = x ^ (y = y ^ (x = x ^ y))`, což je výraz, který se vyhodnocuje zleva. Po dosazení vyjde `x = 10 ^ (y = 20 ^ (x = 10 ^ y))` atd.
74 |
75 | **Co se vypíše?**
76 |
77 | ```java
78 | class ForCycle {
79 | public static void main(String[] argv) {
80 | int j = 0;
81 | for (int i = Integer.MAX_VALUE - 10; i <= Integer.MAX_VALUE; i++) {
82 | j++;
83 | }
84 | System.out.println(j);
85 | }
86 | }
87 | ```
88 |
89 | *Řešení:* Nekonečný cyklus (nevypíše se nic)
90 |
91 | `int i` nikdy nebude větší než `Integer.MAX_VALUE`, protože je to jeho horní hranice.
92 |
93 | ## Cvičení 2
94 |
95 | **Doplňte deklaraci `i`, aby se vypsalo `ANO`**
96 |
97 | ```java
98 | if (i == -i && i != 0) {
99 | System.out.println(“ANO“);
100 | } else {
101 | System.out.println(“NE“);
102 | }
103 | ```
104 |
105 | *Řešení:* `i = Integer.MIN_VALUE`
106 |
107 | Když ho znegujeme, dostaneme `Integer.MAX_VALUE + 1`, což je zase `Integer.MIN_VALUE`.
108 |
109 | **Co se vypíše?**
110 |
111 | ```java
112 | public class LoopTest {
113 | public static void main(String[] argv) {
114 | int START = 2000000000;
115 | int count = 0;
116 | for (float f = START; f < START + 50; f++) {
117 | count++;
118 | }
119 | System.out.println(count);
120 | }
121 | }
122 | ```
123 |
124 | *Řešení:* 0
125 |
126 | Má to co dočinění s *floating-point* čísly. `START` je sice na začátku `int`, kvůli porovnání s `f` je však převeden na `float`. `START` jen o málo menší než $2^{31}$, proto v jeho binárním zápisu bude určitě 31. bit jednička. `float` čísla jsou sice 32 bitová, ale 1 bit z toho je na znaménko, dalších 8 je na exponent a tak na samotné číslo zbude pouze 23 bitů.
127 |
128 | Protože k uložení start potřebujeme mít 31. bit nastavený na 1, při převodu na `float` si ze `START` necháme následujících 23 bitů: [31., 30., 29., ..., 8.] — jinými slovy prvních sedm bitů ze `START` odstřihneme, neboť se nám do floatu nevlezou. Protože přičtená 50 se vleze do prvních sedmi bitů, a my těchto sedm bitů poté odstřihneme, přičtení 50 se vůbec neprojeví a platí `START == f == START + 50` (pokud je `START` převeden na `float`).
129 |
130 | ## Cvičení 3
131 |
132 | **Co se vypíše?**
133 |
134 | ```java
135 | public class Test01 {
136 | public static void main(String[] argv) {
137 | System.out.println(test());
138 | }
139 | public static boolean test() {
140 | try {
141 | return true;
142 | } finally {
143 | return false;
144 | }
145 | }
146 | }
147 | ```
148 |
149 | *Řešení:* `false`
150 |
151 | `finally` přebíjí většinu věcí, i `return`.
152 |
153 | **Co se vypíše?**
154 |
155 | ```java
156 | public class Test01 {
157 | public static void main(String[] argv) {
158 | try {
159 | System.out.println("Hello world!");
160 | System.exit(0);
161 | } finally {
162 | System.out.println("Goodbye");
163 | }
164 | }
165 | }
166 | ```
167 |
168 | *Řešení:* "Hello world!"
169 |
170 | `finally` přebíjí většinu věcí, ale `System.exit()` je jedna z výjimek — prostě rovnou ukončí průběh programu. Kromě něj takto funguje i `halt` a pak už většinou jen pády JVM nebo samotného operačního systému.
171 |
172 | **Co se vypíše?**
173 |
174 | ```java
175 | class ParamsTest {
176 | public ParamsTest(Object o) {
177 | System.out.println("ParamsTest(Object o)");
178 | }
179 | public ParamsTest(long[] a) {
180 | System.out.println("ParamsTest(long[] a)");
181 | }
182 | public static void main(String[] argv) {
183 | new ParamsTest(null);
184 | }
185 | }
186 | ```
187 |
188 | *Řešení:* ParamsTest(long[] a)
189 |
190 | Podle skutečných parametrů se vybere ta nejspecifičtější vyhovující implementace (zde `long[] a`).
191 |
192 | **Co se vypíše?** (zkouškový příklad)
193 |
194 | ```java
195 | class A {
196 | int x = 1;
197 | }
198 |
199 | class B extends A {
200 | int x = 2;
201 | public void foo() {
202 | System.out.println(this.x);
203 | System.out.println(super.x);
204 | System.out.println(((A)this).x);
205 | }
206 | public static void main(String[] args) {
207 | B b = new B();
208 | b.foo();
209 | }
210 | }
211 | ```
212 |
213 | *Řešení:* 2 1 1
214 |
215 | Statické metody se koukají pouze na typ objektu, ne na jeho hodnotu; podle toho se pak zavolá konkrétní implementace. Viz také [tato otázka na Stack Overflow](https://stackoverflow.com/questions/60065506/inherited-attribute-method-static-method-behavior?noredirect=1).
216 |
217 | ## Cvičení **4**
218 |
219 | **Co program udělá?** (zkouškový příklad)
220 |
221 | ```java
222 | public class Null {
223 | public static void main(String[] argv) {
224 | ((Null) null).hello();
225 | }
226 | public static void hello() {
227 | System.out.println("Hello world!");
228 | }
229 | }
230 | ```
231 |
232 | *Řešení:* vypíše se "Hello world!"
233 |
234 | Že se třída jmenuje `Null` je v pořádku. Poté sice voláme `null.hello()`, ale `hello()` je statická metoda, čili vůbec nekouká na reálnou hodnotu objektu — pouze na jeho typ (a to je `Null`). Vše tedy funguje, jak má.
235 |
236 | **Co program udělá?**
237 |
238 | ```java
239 | class Test01 {
240 | public static void main(String[] argv) {
241 | run();
242 | System.out.println("Konec");
243 | }
244 |
245 | public static void run() {
246 | try {
247 | run();
248 | } finally {
249 | run();
250 | }
251 | }
252 | }
253 | ```
254 |
255 | *Řešení:* Myslím, že vyhodí `StackOverflowError`.
256 |
257 | **Co program vypíše?**
258 |
259 | ```java
260 | class A {
261 | public String className = "A";
262 | }
263 |
264 | class B extends A {
265 | private String className = "B";
266 | }
267 |
268 | public class Test02 {
269 | public static void main(String[] argv) {
270 | System.out.println(new B().className);
271 | }
272 | }
273 | ```
274 |
275 | *Řešení:* Nepřeloží se, ve třídě Test02 je chyba.
276 |
277 | Deklarace `className` v `B` schová atribut `className` z `A ` (doslova se to jmenuje *field hiding*). To, že je sama `private` na věci nic nemění. Protože původní `className` z `A` je schovaná a `className` z `B` je zase `private`, je chyba vůbec se snažit tohoto atributu dosáhnout.
278 |
279 | ## Cvičení 5
280 |
281 | **Co se vypíše?**
282 |
283 | ```java
284 | public interface Test {
285 | public static void main(String[] argv) {
286 | System.out.println("Hello");
287 | }
288 | }
289 | ```
290 |
291 | *Řešení:* Je to úplně v pořádku, vypíše se Hello.
292 |
293 | V interfacu mohou totiž být i statické metody.
294 |
295 | **Co se vypíše?** (zkouškový příklad)
296 |
297 | ```java
298 | public enum Test {
299 | RED, GREEN, BLUE;
300 |
301 | public static void main(String[] argv) {
302 | System.out.println("Hello");
303 | }
304 | }
305 | ```
306 |
307 | *Řešení:* Je to úplně v pořádku, vypíše se Hello.
308 |
309 | `enum` je v podstatě jen vylepšená třída, proto může obsahovat statické metody. Nemůže však dědit, neboť implicitně již dědí od `java.lang.Enum`. Může dokonce obsahovat i abstraktní metody, pak je ale musí implementovat každý z prvků enumu.
310 |
311 | ## Cvičení 6
312 |
313 | **Co se vypíše?**
314 |
315 | ```java
316 | public class Greeter {
317 | public static void main (String[] args) {
318 | String greeting = "Hello world";
319 | for (int i = 0; i < greeting.length(); i++) {
320 | System.out.write(greeting.charAt(i));
321 | }
322 | }
323 | }
324 | ```
325 |
326 | *Řešení:* Nevypíše se nic.
327 |
328 | `PrintStream.write()` sice na stream přidá dané znaky, ale stream pak automaticky nevyprázdní do konzole — proto nejde nic vidět. Naproti tomu `println()` se o toto stará, čili kdybychom za loop přidali `System.out.println("");`, vypsalo by se už `Hello world`.
329 |
330 | **Co se vypíše?**
331 |
332 | ```java
333 | class Slasher {
334 | public static void main(String[] argv) {
335 | String fullClassName = "cz.cuni.mff.java.io.Slasher";
336 | String fileName = fullClassName.replaceAll(".", "/") + ".java";
337 | System.out.println("Trida " + fullClassName + " musi byt v souboru " + fileName);
338 | }
339 | }
340 | ```
341 |
342 | *Řešení*: Trida cz.cuni.mff.java.io.Slasher musi byt v souboru ///////////////////////////.java
343 |
344 | `String.replaceAll()` bere jako první argument `regex`, ne obyčejný string. Proto `.` je interpretováno regex enginem jako "jakýkoli znak" a všechny znaky jsou proto nahrazeny lomítkem.
345 |
346 | ## Cvičení 7
347 |
348 | **Co program udělá?**
349 |
350 | ```java
351 | public class TestString {
352 | public static void main(String[] args) {
353 | String s = new String("Hello world");
354 | System.out.println(s);
355 | }
356 | }
357 |
358 | class String {
359 | private final java.lang.String s;
360 | public String(java.lang.String s) {
361 | this.s = s;
362 | }
363 | public java.lang.String toString() {
364 | return s;
365 | }
366 | }
367 | ```
368 |
369 | *Řešení:* Přeloží se, ale při runtimu vyhodí chybu, že mu chybí metoda `main`.
370 |
371 | `main` má totiž v této chvíli mít hlavičku `public static void main(java.lang.String[] args)`, protože `String` je teď ta naše nová třída a ne původní `String`.
372 |
373 | **Lze třídu `B` nadeklarovat tak, aby program vypsal `false`? (bez přepsání `equals`)**
374 |
375 | ```java
376 | public class A {
377 | public static void main(String[] args) {
378 | B b = new B();
379 | System.out.println(b.equals(b));
380 | }
381 | }
382 | ```
383 |
384 | *Řešení:* Ano, ale je to dost podvod.
385 |
386 | ```java
387 | class B {
388 | public B() {
389 | System.out.println(false);
390 | System.exit(0);
391 | }
392 | }
393 | ```
394 |
395 | ## Cvičení 8
396 |
397 | **Co se vypíše?**
398 |
399 | ```java
400 | public class Test01 {
401 | public static synchronized void main(String[] a) {
402 | Thread t = new Thread() {
403 | public void run() {
404 | pong();
405 | }
406 | };
407 | t.start();
408 | System.out.println("Ping");
409 | }
410 |
411 | static synchronized void pong() {
412 | System.out.println("Pong");
413 | }
414 | }
415 | ```
416 |
417 | *Řešení:* Ping Pong
418 |
419 | Obě metody jsou `static synchronized`, čili berou zámek přímo od třídy `Test01`. Když už jsme v `main`, má současné vlákno zámek (protože vstoupilo do `synchronized` bloku) a tak se vlákno `t` (potažmo metoda `pong()`) spustí až poté, co skončí to naše vlákno.
420 |
421 | **Co se vypíše?**
422 |
423 | ```java
424 | class SelfInterruption {
425 | public static void main(String[] args) {
426 | Thread.currentThread().interrupt();
427 | if (Thread.interrupted()) {
428 | System.out.println("Interrupted: " + Thread.interrupted());
429 | } else {
430 | System.out.println("Not interrupted: " + Thread.interrupted());
431 | }
432 | }
433 | }
434 | ```
435 |
436 | *Řešení:* Interrupted: false
437 |
438 | `Thread.interrupted()` totiž nejen vrátí současnou hodnotu `.interrupted()`, ale také ji resetuje na `false`. Kdyby se místo toho použilo `this.isInterrupted()`, který po vrácení žádný reset neprovádí, vypsalo by se *Interrupted: true*.
439 |
440 | ## Cvičení 9
441 |
442 | **Co se vypíše?**
443 |
444 | ```java
445 | public class Test01 {
446 | private static java.util.Random rnd = new java.util.Random();
447 |
448 | public static void main(String[] args) {
449 | StringBuffer word = null;
450 | switch (rnd.nextInt(2)) {
451 | case 1:
452 | word = new StringBuffer('P');
453 | case 2:
454 | word = new StringBuffer('G');
455 | default:
456 | word = new StringBuffer('M');
457 | }
458 | word.append('a');
459 | word.append('i');
460 | word.append('n');
461 | System.out.println(word);
462 | }
463 | }
464 | ```
465 |
466 | *Řešení:* ain
467 |
468 | V `case` nejsou breaky, takže word je vždy nakonec nastaven na `new StringBuffer('M')`. Protože `StringBuffer` dostal `char` (potažmo `int`) bere to jako hranici kapacity (ne jako první písmeno — to by musel dostat `"M"`).
469 |
470 | **Co se vypíše?**
471 |
472 | ```java
473 | public class Test02 {
474 | public static void main(String args[]) {
475 | System.out.println("H" + "a");
476 | System.out.println('H' + 'a');
477 | }
478 | }
479 | ```
480 |
481 | *Řešení:* Ha 169
482 |
483 | `char` + `char` je v tomto případě interpretováno jako součet intů. Pro zajímavost, `println("" + 'H' + 'a')` by vypsalo skutečně `Ha` [[*laughs in javascript*](https://www.destroyallsoftware.com/talks/wat)].
484 |
485 | ## Cvičení 10
486 |
487 | **Lze definovat `i` tak, aby byl cyklus nekonečný?**
488 |
489 | ```java
490 | while (i != i) { }
491 | ```
492 |
493 | *Řešení:* Ano, `Double.NaN` se nerovná ničemu, ani sám sobě.
494 |
495 | **Co se vypíše?**
496 |
497 | ```java
498 | public class Increment {
499 | public static void main(String[] args) {
500 | int j = 0;
501 | for (int i = 0; i < 100; i++) {
502 | j = j++;
503 | }
504 | System.out.println(j);
505 | }
506 | }
507 | ```
508 |
509 | *Řešení:* 0
510 |
511 | (Myslím, že) `j++` vrátí 0 a nastaví `j` na 1; `j = j++` pak vezme 0 z `j++` a uloží jí do `j`.
512 |
513 | ## Cvičení 11
514 |
515 | **Lze od této třídy (bez použití reflection API) vytvořit další instance (kromě instance v atributu `INSTANCE`)?**
516 |
517 | ```java
518 | public class Dog implements Serializable {
519 | public static final Dog INSTANCE = new Dog();
520 | private Dog() { }
521 | public String toString() {
522 | return "Woof";
523 | }
524 | }
525 | ```
526 |
527 | *Řešení:* Leda bychom přidali něco do třídy samotné (jinak máme příklad tzv. **singleton pattern**, což je třída pouze s jednou instancí, zde konkrétně `INSTANCE`).
528 |
529 | ```java
530 | public class Dog implements Serializable {
531 | public static final Dog INSTANCE = new Dog();
532 | private Dog() { }
533 | public String toString() {
534 | return "Woof";
535 | }
536 |
537 | public Dog getDog() {
538 | return new Dog();
539 | }
540 | }
541 |
542 | Dog d = Dog.getDog();
543 | ```
544 |
545 | V tomto případě naopak používám tzv. **factory pattern** (máme metody, které nejsou konstruktory, ale přesto *vyrábějí* a vrací objekt své třídy).
546 |
547 | ## Cvičení 12
548 |
549 | **Lze napsat deklaraci proměnné `i` tak, aby následující cyklus byl nekonečný?**
550 |
551 | ```java
552 | while (i != i + 0) { }
553 | ```
554 |
555 | *Řešení:* Ano, např. `String i = "a"`, potom `i + 0 == "a0"`.
556 |
557 | **Lze napsat deklaraci proměnných `i` a `j` tak, aby následující cyklus byl nekonečný?**
558 |
559 | ```java
560 | while (i <= j && j <= i && i != j) { }
561 | ```
562 |
563 | *Řešení:* Ano, například `i = new Integer(1)` a `j = new Integer(1)`.
564 |
565 | Protože se jedná o referenční typy (objekty), výraz `i != j` je pravda, protože porovnává umístění v paměti a ne samotné hodnoty `i` a `j`.
566 |
567 |
--------------------------------------------------------------------------------
/java.md:
--------------------------------------------------------------------------------
1 | # Java (zkouška)
2 |
3 | Následuje seznam důležitých věcí, které je třeba vědět, a pod tím zkouškové příklady.
4 |
5 | ## Co je potřeba vědět
6 |
7 | Seznam věcí, na které se na zkoušce často (i nepřímo) ptá.
8 |
9 | ### Keywordy
10 |
11 | běžné **keywordy** jsou následující: `abstract`, `assert`, `boolean`, `break`, `byte`, `case`, `catch`, `char`, `class`, `continue`, `default`, `double`, `else`, `enum`, `extends`, `finally`, `float`, `for`, `if`, `implements`, `import`, `int`, `interface`, `long`, `new`, `package`, `private`, `protected`, `public`, `return`, `short`, `static`, `switch`, `this`, `throw`, `throws`, `try`, `void`, `while`
12 |
13 | zatímco ty nečekané jsou:
14 |
15 | - **const** a **goto**: zatím nedělají nic, jsou pouze rezervované
16 | - **do**: do/while loop
17 | - **final**: viz níže
18 | - **instanceof**: check třídy
19 | - **native**: metoda je implementovaná v Java Native Interface, kde Java spolupracuje s C++, C, Assemblerem apod.
20 | - **strictfp**: třída/interface/metoda, zaručí, že floating-point aritmentika vyjde na všech platformách stejně
21 | - **super**: k použití proměnných/metod z rodičovské třídy
22 | - **synchronized**: viz níže v multithreadingu
23 | - **transient**: při ukládání objektů pomocí Seriazable interface se takto označená proměnná neuloží
24 | - **volatile**: viz níže v multithreadingu
25 |
26 | ### Access modifiers
27 |
28 | - `private`: přístupné pouze z dané třídy
29 | - třídy a interfacy nemohou být `private` (pokud to nejsou vnitřní třídy)
30 | - žádný: přístupné všem třídám v současném balíku
31 | - `protected`: přístupné všem podtřídám dané třídy (i v jiném balíku) a všem třídám v současném balíku
32 | - třídy, interfacy a pole a metody v interfacu nemohou být `protected`
33 | - `public`: přístupné všemu, i z jiného balíku
34 |
35 | ### Final
36 |
37 | - `final` proměnná je konstantní (pokud se jedná o referenční proměnnou, její samotný stav se měnit může, pouze reference na ni ne)
38 | - `final` metoda se nedá overloadovat
39 | - `final` třída nejde subclassovat
40 |
41 | Používá se i při tvoření anonymních vnitřních tříd v metodách, objekty, které ve vnitřní třídě použijeme, musí být `final`, nebo alespoň efektivně `final` (tj. nemají explicitně `final`, ale nemění se).
42 |
43 | ```java
44 | public interface MyIface {
45 | int value();
46 | }
47 |
48 | public class MyClass {
49 | public MyIface add(final int val) {
50 | return new MyIface() {
51 | private int i = val;
52 | public int value() { return i; }
53 | };
54 | }
55 | }
56 | ```
57 |
58 | ### Refereční a hodnotové typy
59 |
60 | - referenční: jsou někde v paměti a máme na ně pouze pointer
61 | - v Javě se píší s velkým písmenem
62 | - jedná se o všechny třídy a objekty (`String`, `ArrayList` atd.)
63 | - je možné do nich nastavit `null` jako nulový pointer
64 | - když je posíláme nějaké objekty jako argumenty do nějaké metody, posíláme tedy jen *pointer* na tyto objekty, a pokud metoda nějakým způsobem bude své argumenty měnit, projeví se to i u nás
65 |
66 | - hodnotové: jsou malé, pracujeme přímo s jejich hodnotou
67 | - v Javě se píší s malým písmenem
68 | - např. `boolean`, `int`, `byte`, `float` atd.
69 | - když je posíláme jako argumenty, pošleme pouze jejich hodnotu (zkopírují se), takže i pokud je metoda bude měnit, ty naše zůstanou nezměněny
70 |
71 | Porovnání `a == b` porovnává hodnoty uložené v `a` a `b`. U referečních typů jsou v `a` a `b` uložen pouze pointery na nějaké objekty, ne ty objekty samotné, proto například neplatí
72 |
73 | ```java
74 | String a = new String("a");
75 | String b = new String("a");
76 | a == b // false
77 | ```
78 |
79 | `a` a `b` jsou v tomto případě pointery na dvě různá místa v paměti, tedy se nerovnají. To, jestli jsou na těchto *dvou různých místech* uloženy stringy se stejnou hodnotou zjistíme až pomocí `a.equals(b)`.
80 | Pozor ale na následující případ
81 |
82 | ```java
83 | String a = "a";
84 | String b = "a";
85 | a == b // true
86 | ```
87 |
88 | Tady Java compiler použije tzv. **string pool** a obě proměnné odkáže na stejný string v paměti.
89 |
90 | ### Literály
91 |
92 | - neboli způsoby zapsání dat *doslova* v rámci kódu (běžné příklady: `10`, `"string"`, `3.3`, `false`)
93 | - `null` vyjadřuje prázdný pointer, proto jej lze nastavit **pouze u referenčních typů**
94 | - do `boolean` lze nastavit **pouze `false` a `true`**
95 |
96 | #### Chary
97 |
98 | - **jeden** znak v jednoduchých uvozovkách: `'c'`
99 | - číselný literál: `99`
100 | - případně i speciální znaky jako `'\n'` nebo unicodové znaky
101 |
102 | #### Čísla
103 |
104 | - `byte` má rozsah -128 až 127
105 | - integery lze zapisovat v různých soustavách:
106 | - osmičková: začínají na 0, např. `07`
107 | - pozor, `08` už je špatně, 8 v osmičkové neexistuje
108 | - binární: `0b01`
109 | - šesnáctková: `0xAB`
110 | - double lze psát i s exponenty: `1e10`
111 | - v rámci všech čísel jde pro přehlednost použít podtržítka: `1234_5678 `
112 |
113 | ### Výjimky — teorie
114 |
115 | - všechny instance `Throwable`
116 | - z podtřídy `Error`: nikdy by se neměly odchytávat, signalizují velký problém
117 | - nebo z podtřídy `Exception`: někdy je možná chcete odchytit
118 | - `Exception` se dále dělí
119 | - *unchecked* (pouze podtřídy `RuntimeException`): kompilátoru nevadí, že je neřešíte
120 | - *checked* (všechny ostatní): musí být odchyceny nebo vyhozeny výše
121 |
122 | Ošetřují se pomocí try/catch/finally bloku.
123 |
124 | - v `try` lze použít i objekty, které jsou `AutoClosable`, poté lze vynechat `catch` i `finally` a objekty se samy zavřou
125 | - v `catch` lze odchytit více různých exception pomocí oddělení `|`, nebo lze za `try` dát více catchů
126 | - v druhém případě se to chová podobně jako if/else, jakmile se matchne jeden catch, další už se nezkoušejí
127 | - finally proběhne nehledě na to, jak dopadly věci v try/catch
128 |
129 | To, že metoda vyhazuje checked `Exception`, signalizujeme pomocí `throws`.
130 |
131 | - `Error` a `RuntimeException` nemusíme explicitně do `throws` dávat, je možné je vyhazovat vždy
132 |
133 | ```java
134 | class MyException extends java.lang.Exception {}
135 |
136 | public class A {
137 | public void foo(int i) throws MyException {
138 | if (i < 0) {
139 | throw new MyException();
140 | } else if (i == 0) {
141 | throw new Error(); // V pořádku, i když není ve throws
142 | } else {
143 | throw new RuntimeException(); // V pořádku, i když není ve throws
144 | }
145 | }
146 | }
147 | ```
148 |
149 | ### Multithreading — teorie
150 |
151 | - spouštění více *vláken* najednou
152 | - každé vlákno potřebuje vědět, co na něm poběží
153 | - v konstruktoru může dostat objekt, který implementuje `Runnable` interface (konkrétně metodu `run`)
154 | - samotná třída `Thread` je runnable, takže jí můžeme subclassovat a implementovat `run` sami (nedoporučuje se)
155 | - vlákno se po konstrukci musí spustit metodou `.start()`
156 | - vlákno jde přerušit pomocí `.interrupt()`, musí na to být ale připraveno (odchytnout `InterruptedException` nebo kontrolovat `Thread.interrupted`)
157 | - současné vlákno lze pozastavit do doby, než se dokončí vlákno `t` pomocí `t.join()`
158 |
159 | Kdyby dvě vlákna najednou upravovala jeden objekt, mohlo by dojít k chybám; proto má každý objekt *zámek*, který určuje, které vlákno s daným objektem zrovna pracuje.
160 |
161 | - vlákno si zámek daného objektu vezme, poté s objektem může pracovat a když je hotové, zámek uvolní
162 |
163 | - `synchronized (object) { ... }` zařídí, že kód v bloku bude spouštěn v jednu chvíli pouze jedním vláknem, kterému poskytne zámek objekt `object` (může jít o jakýkoli objekt, nemusí se poté v bloku vůbec vyskytnout)
164 |
165 | - vhodné pro stavy, kdy je hodně writerů i hodně readerů
166 |
167 | - `synchronized` může být i metoda
168 |
169 | ```java
170 | class C {
171 | synchronized void method() {
172 | /* ... */
173 | }
174 | }
175 | ```
176 |
177 | se chová jako
178 |
179 | ```java
180 | class C {
181 | void method() {
182 | synchronized (this) {
183 | /* ... */
184 | }
185 | }
186 | }
187 | ```
188 |
189 | Všechny `synchronized` ne-statické metody jednoho objektu se tedy blokují navzájem (mohou být najednou používany pouze jedním vláknem). Statické `synchronized` metody používají jako objekt k získání zámku samotnou třídu.
190 |
191 | Když ale nepotřebujeme udržovat *posloupnost* úprav, jako to dělá `synchronized`, stačí nám modifikátor **atributů** `volatile`. `volatile` je rychlejší (ale slabší) než `synchronized`.
192 |
193 | - `volatile` garantuje, že pokud nějaké vlákno do této proměnné zrovna zapisuje, jakékoli čtení této proměnné proběhne až poté, co zapisovací vlákno dokončí svou práci
194 | - každé vlákno tedy vždy vidí nejnovější verzi `volatile` proměnné
195 | - vhodné pro vztahy jeden writer, mnoho readerů
196 |
197 | Pokud potřebujeme, aby jedno vlákno čekalo na znamení, že se má spustit, od jiného vlákna, můžeme použí `wait` a `notify` (`notifyAll`).
198 |
199 | - `wait` pustí zámek současného objektu a suspeduje současné vlákno
200 | - `notifyAll` probudí všechna čekající vlákna, která poté mohou zkontrolovat, jestli už mají běžet (a buďto se spustí, nebo se zase suspendují přes `wait`)
201 |
202 | #### High level multithreading
203 |
204 | - jednodušší než se ručně starat o vlákna a mít jeden task (Runnable objekt) = jedno vlákno
205 | - používají se množiny dlouho existujících vláken (**thread pool**), z nichž každé dělá >1 task (nemusí se tak často rušit a zase vyrábět)
206 | - `FixedThreadPool` operuje s konstantním počtem vláken, zatímco `ForkJoinPool` se hodí pro rekurzivní problémy (buďto vyřeším problém na svém vlákně, nebo ho rozpůlíme mezi dvě)
207 | - počet dostupných jader je možno získat pomocí `Runtime.getRuntime().availableProcessors()`
208 |
209 | ### Abstraktní třídy
210 |
211 | - deklarovány pomocí `abstract class ...`
212 | - nelze z nich tvořit objekty, ale lze z nich dědit (jsou vlastně podobné interfacům)
213 | - mohou, ale nemusí, obsahovat abstraktní metody
214 | - mohou, ale nemusí obsahovat neabstraktní (běžné) metody
215 |
216 | Co jsou abstraktní metody?
217 |
218 | - abstraktní metody nemají implementaci (podobně jako např. metody v interfacu, tam ale nejsou označeny `abstract`)
219 | - jsou také označeny `abstract`
220 | - vždy musí být v abstraktní třídě
221 |
222 | Jak se tedy abstraktní třídy od interfaců liší?
223 |
224 | - mohou mít atributy, které nejsou `static` a `final`
225 | - v interfacech jsou implicitně obojí
226 | - mohou mít `public`, `protected`, i `private` konrétní (neabstraktní) metody
227 | - v interfacech jsou běžně všechny metody `public`, i když se jedná o `default` implementace
228 | - `private` metody byly do interfaců přidány v Javě 9
229 |
230 | ### Funkcionální interface a lambda výrazy
231 |
232 | - interface s právě jednou abstraktní metodou (SAM, single abstract method)
233 | - právě jedna SAM se dá ověřit s `@FunctionalInterface`
234 | - pokud má právě jednu abstraktní a spoustu defaultních, taky to projde
235 | - nesmí ani dědit další abstraktní z jiného interfacu
236 | - používá se jako *typ* pro lambda výrazy (anonymní funkce)
237 | - argumenty lambda výrazu musí odpovídat argumentům SAM
238 | - výsledný typ těla lambda výrazu musí odpovídat návratové hodnotě SAM
239 |
240 | ```java
241 | @FunctionalInterface
242 | interface Predicate {
243 | boolean isTrue(T a); // SAM
244 | }
245 |
246 | public class Main {
247 | public static void main(String[] args) {
248 | // input = čísla 0 až 9
249 | List input = IntStream.range(0, 10).boxed().collect(Collectors.toList());
250 | System.out.println(filter(input, n -> n > 5));
251 | // výsledek: [6, 7, 8, 9]
252 | // n -> n > 5 má typ Predicate, je automaticky zjištěný
253 | }
254 |
255 | // vrací prvky seznamu, pro které je predikát pravdivý
256 | static List filter(List list, Predicate pred) {
257 | ArrayList result = new ArrayList<>();
258 | for (T a: list) {
259 | if (pred.isTrue(a)) {
260 | result.add(a);
261 | }
262 | }
263 | return result;
264 | }
265 | }
266 | ```
267 |
268 | ## Zkouškové úlohy
269 |
270 | Jsou seřazeny podle tématu.
271 |
272 | 1. [Multithreading](#Multithreading)
273 | 2. [Třídy](#Třídy)
274 | 3. [Interfacy](#Interfacy)
275 | 4. [Lambda výrazy](#Lambda výrazy)
276 | 5. [Hodnoty proměnných](#Hodnoty proměnných)
277 | 6. [Triky](#Triky)
278 | 7. [Základní znalosti](#Základní znalosti)
279 | 8. [Výjimky](#Výjimky)
280 | 9. [Jednoduché úkoly na psaní kódu](#Jednoduché úkoly na psaní kódu)
281 |
282 | ### Multithreading
283 |
284 | Zpět na [Zkouškové úlohy](#Zkouškové úlohy).
285 |
286 | **Uvažujme následující třídu a předpokládejme, že nějaké vlákno získalo přístup a je uvnitř metody `setX(int, value)`. Pak jiným vláknem:**
287 |
288 | ```java
289 | public class A {
290 | private int x;
291 |
292 | public synchronized void setX(int value) {
293 | x = value;
294 | }
295 |
296 | public synchronized int getX() {
297 | return x;
298 | }
299 | }
300 | ```
301 |
302 | 1. nelze pristupovat ani k `getX()` ani k `setX(int value)`
303 | 2. lze pristupovat k `getX()`, ale ne k `setX(int value)`
304 | 3. kod nelze prelozit, u metod nejsou deklarovany `throws` parametry
305 |
306 | *Odpověď:* [1]
307 |
308 | **Mějme následující třídu, co platí?**
309 |
310 | ```java
311 | class Test {
312 | public synchronized int foo() {...}
313 | public static synchronized void bar() {...}
314 | }
315 | ```
316 |
317 | 1. `foo()` a `bar()`jsou pro přístup více vlákny vyloučeny každá sama se sebou i mezi sebou navzájem
318 | 2. `foo()` a `bar()` jsou pro přístup více vlákny vyloučeny každá sama se sebou, ale nikoliv mezi sebou navzájem
319 | 3. chyba překladu, nedeklaruje se výjimka `IllegalMonitorStateException`
320 |
321 | *Odpověď:* [2]
322 |
323 | Jedna metoda je static (používá zámek na třídě jako takové) a druhá není (používá zámek na instanci), proto nejsou navzájem vyloučené.
324 |
325 | **Co vypíše následující kód (pokud něco):**
326 |
327 | ```java
328 | public class Main {
329 | synchronized public void foo() {
330 | synchronized (this) {
331 | System.out.print("A");
332 | synchronized (this) {
333 | System.out.print("B");
334 | }
335 | }
336 | }
337 | public static void main(String[] args) {
338 | new Main().foo();
339 | }
340 | }
341 | ```
342 |
343 | 1. nepůjde přeložit, synchronized nelze napsat uvnitř metody
344 | 2. nic, vlákno se zablokuje
345 | 3. AB
346 |
347 | *Odpověď:* [3]
348 |
349 | Když metoda jednou získá zámek na objektu, tak už ho další bloky kódu v té metodě taky dostanou.
350 |
351 | **Která definice `s` je možná, aby se dal kód přeložit?**
352 |
353 | ```java
354 | synchronized (s) {
355 | /* ... */
356 | }
357 | ```
358 |
359 | 1. `synchronized` se takto nedá použít
360 | 2. `Thread s = new Thread(); `
361 | 3. `Object s = new Object(); `
362 | 4. `String s = "Hello"; `
363 | 5. `int s = 100; `
364 | 6. `Runnable s = () -> {}; `
365 |
366 | *Odpověď*: [2, 3, 4, 6] Za `s` je možno dosadit jakýkoli objekt.
367 |
368 | **Doplňte deklaraci hashTable tak, aby obsahovala základní sémantiku hash tabulky — metody `V get (K key)` a `void put(K key, V value)`. Navíc k objektu musí bezpečně přistupovat více vláken najednou (tedy volání metod více vlákny najendou je vyloučeno). Můžete si definovat libovolné další třídy nebo použít cokoliv ze standardní knihovny.**
369 |
370 | Metody `get` a `put` by obě měly být `synchronized`, zbytek je jednoduchý.
371 |
372 | ### Třídy
373 |
374 | Zpět na [Zkouškové úlohy](#Zkouškové úlohy).
375 |
376 | **Upravte následující kód tak, aby se zkompiloval:**
377 |
378 | ```java
379 | public class Class {
380 | abstract void foo();
381 | }
382 | ```
383 |
384 | *Řešení*:
385 |
386 | ```java
387 | public abstract class Class {
388 | abstract void foo();
389 | }
390 | ```
391 |
392 | Abstraktní metody musí být v abstraktní třídě.
393 |
394 | **Mějme abstraktní třídu, pak:**
395 |
396 | 1. od ní nelze vytvářet instance
397 | 2. lze od ni dědit, ale nelze předefinovat žádnou její metodu
398 | 3. nelze od ni dědit
399 | 4. všechny jeji metody jsou také abstraktní
400 |
401 | *Odpověď:* [1]
402 |
403 | **Jaký modifier může mít vnitřní (inner) třída?**
404 |
405 | 1. public
406 | 2. private
407 | 3. static
408 | 4. friendly
409 | 5. volatile
410 |
411 | *Odpověď:* [1, 2, 3]
412 |
413 | **Co je pravda?**
414 |
415 | 1. vnitřní třídy musí implementovat alespoň jeden interface
416 | 2. vnitřní třídy mají přístup ke všem (i private) elementům třídy, která je obsahuje
417 | 3. vnitřní třídy nedědí od třídy `Object`
418 | 4. vnitřní třídy dědí od té vnější
419 |
420 | *Odpověď*: [2]
421 |
422 | **O jakékoliv třídě Enum platí:**
423 |
424 | 1. nemůže mít konstruktor
425 | 2. není potomkem žádné třídy
426 | 3. dědí od `java.lang.Enum `
427 | 4. nemůže obsahovat žádné metody
428 | 5. může obsahovat `public static void main(String[] args) `
429 |
430 | *Odpověď:* [3, 5]
431 |
432 | **Co vypíše (pokud něco) kód:**
433 |
434 | ```java
435 | class A {
436 | static int x = 1;
437 | }
438 |
439 | public class Main {
440 | static A a = null;
441 | public static void main(String[] args) {
442 | System.out.println(a.x);
443 | }
444 | }
445 | ```
446 |
447 | 1. 0
448 | 2. 1
449 | 3. pokažé něco jiného (závisí na okolnostech)
450 | 4. spadne na NullPointerException
451 | 5. nelze přeložit
452 |
453 | *Odpověď:* [2]
454 |
455 | U statického atributu *null* nevadí. Navíc statické atributy lze volat i na instancích třídy, nejen na třídě samé.
456 |
457 | **Co vypíše (pokud něco) kód:**
458 |
459 | ```java
460 | class A {
461 | int x = 1;
462 | }
463 |
464 | class B extends A {
465 | int x = 2;
466 | public void foo() {
467 | System.out.println(this.x);
468 | System.out.println(super.x);
469 | System.out.println(((A)this).x);
470 | }
471 | public static void main(String[] args) {
472 | B b = new B();
473 | b.foo();
474 | }
475 | }
476 | ```
477 |
478 | 1. 2 2 2
479 | 2. 2 2 1
480 | 3. 2 1 2
481 | 4. 2 1 1
482 | 5. chyba překladu, this nejde přetypovat
483 | 6. chyba překladu, super není na proměnné
484 |
485 | *Odpověď:* [4] Viz také [otázka na stack overflow](https://stackoverflow.com/questions/60065506/inherited-attribute-method-static-method-behavior/60065829?noredirect=1#comment106242033_60065829).
486 |
487 | **Co se vypíše?**
488 |
489 | ```java
490 | public class A {
491 | public static void foo() { System.out.println("A"); }
492 | }
493 |
494 | public class B extends A {
495 | public static void foo() { System.out.println("B"); }
496 | }
497 |
498 | public class Main {
499 | public static void main(String[] args){
500 | A a = new B();
501 | a.foo();
502 | }
503 | }
504 | ```
505 |
506 | 1. A
507 | 2. B
508 | 3. nelze určit
509 |
510 | *Odpověď*: [1]
511 |
512 | Protože voláme **statickou** funkci `foo`, ta se dívá pouze na typ `a` a to je `A` (jinými slovy, není *virtualizovaná*) Zavolá se tedy `A.foo()`. Kdyby se nejednalo o statickou funkci, zavolalo by se `foo()` od objektu `a`, který je reálně ze třídy `B`.
513 |
514 | **Rozhodněte, co bude na standardním výstupu po spuštění programu:**
515 |
516 | ```java
517 | class A {
518 | int x = 1;
519 | }
520 |
521 | class B extends A {
522 | int x = 2;
523 |
524 | public void foo() {
525 | System.out.println(this.x);
526 | System.out.println(super.x);
527 | }
528 |
529 | public static void main(String[] args) {
530 | B b = new B();
531 | b.foo();
532 | }
533 | }
534 | ```
535 |
536 | 1. 2 2
537 | 2. 1 1
538 | 3. 2 1
539 | 4. nelze aplikovat klicove slovo ***super*** na atributy
540 | 5. nelze prepisovat atributy tridy, od ktere se dedi
541 |
542 | *Odpověď:* [3] Podobné jako otázka výše. Tomuto se říká *field hiding*.
543 |
544 | **Co se vypíše?**
545 |
546 | ```java
547 | class MyClass{
548 | public static int i = 0;
549 |
550 | public MyClass() {
551 | i++;
552 | }
553 |
554 | public static void main(String[] args) {
555 | MyClass[] my = new MyClass[5];
556 | for(int i = 0; i < 5; i++){
557 | my[i] = new MyClass();
558 | }
559 | System.out.println(i);
560 | }
561 | }
562 | ```
563 |
564 | 1. 0
565 | 2. 1
566 | 3. 4
567 | 4. 5
568 | 5. Nelze určit
569 |
570 | *Odpověď*: [4]
571 |
572 | Vypíše se 5, protože při každém zavolání `new MyClass()` se ke statickému atributu `i` přičte jednička. Protože je atribut statický, jedná se vždy o stejné `i` (netvoří se pro každý `new MyClass` objekt zvlášť). 4 by se vypsalo v případě, že bychom vypisovali `i` z for loopu, to ale mimo loop neexistuje.
573 |
574 | **Co vypíše následující kód (pokud něco):**
575 |
576 | ```java
577 | class A {
578 | public A() {
579 | super();
580 | System.out.print("A");
581 | }
582 | }
583 | class B extends A {
584 | public B() {
585 | super();
586 | System.out.print("B");
587 | }
588 | }
589 | class C extends B {
590 | public C() {
591 | System.out.print("C");
592 | }
593 | }
594 | public class Main {
595 | public static void main(String[] args) {
596 | C c = new C();
597 | }
598 | }
599 | ```
600 |
601 | 1. nepůjde přeložit, konstruktor třídy A volá super() ale přitom nemá explicitně definovaného předka
602 | 2. C
603 | 3. ABC
604 |
605 | *Odpověď:* [3]
606 |
607 | `super()` se totiž z konstruktoru volá implicitně, a každá třída implicitně extenduje `Object` (nebo explicitně nějakou jeho podtřídu)
608 |
609 | ### Interfacy
610 |
611 | Zpět na [Zkouškové úlohy](#Zkouškové úlohy).
612 |
613 | **Co platí o rozhraních (*interface*):**
614 |
615 | 1. třída může implementovat nejvýše jeden interface
616 | 2. třída může implementovat žádný, jeden nebo i více interfaces
617 | 3. interface může implementovat nejvýše jedna třída
618 | 4. interface může dědit od nejvýše jednoho interface
619 | 5. interface může dědit od žádného, jednoho nebo i více interfaces
620 |
621 | *Odpověď:* [2, 5]
622 |
623 | **Co se stane s následujícím kódem?**
624 |
625 | ```java
626 | interface A {
627 | default void hello() { System.out.println("Hello A"); }
628 | }
629 |
630 | interface B {
631 | default void hello() { System.out.println("Hello B"); }
632 | }
633 |
634 | class C implements A, B { }
635 | ```
636 |
637 | 1. nepřeloží se nic
638 | 2. přeloží se A, B nepřeloží se C
639 | 3. nepřeloží se A ani B, ale C ano
640 |
641 | *Odpověď:* [2]
642 |
643 | C má metodu s dvěma implementacemi a neví, kterou si vybrat. Správně by mělo `hello()` být v C overridnuté a implementované znovu.
644 |
645 | **Která tvrzení jsou správná?**
646 |
647 | ```java
648 | /* soubor A.java */
649 | public interface A {
650 | void foo(int i);
651 | default void bar() {
652 | foo(0);
653 | }
654 | }
655 |
656 | /* soubor B.java */
657 | public class B implements A {
658 | public void foo( int i) {
659 | System.out.println( i);
660 | }
661 | public void bar() {
662 | System.out.println("Hello");
663 | }
664 | }
665 | ```
666 |
667 | 1. Obojí se přeloží bez chyb
668 | 2. A se nepřeloží, z defaultní metody se nedá volat nedefaultní
669 | 3. A se přeloží bez chyby, ale v B je chyba: defaultní metody z interface se nedají předefinovat
670 | 4. A se přeloží bez chyby, ale v B je chyba: před přepsáním metody `bar` chybí klíčové slovo `default`
671 |
672 | *Odpověď*: [1]
673 |
674 | Defaultní implementace je pouze "záloha" v případě, že metoda není implementována ve třídě.
675 |
676 | **Co se vypíše?**
677 |
678 | ```java
679 | interface Iface {
680 | default void foo() { System.out.println("Interface"); }
681 | }
682 |
683 | class A {
684 | public void foo() { System.out.println("Class"); }
685 | }
686 |
687 | class B extends A implements Iface {
688 | public static void main(String[] args) {
689 | B b = new B();
690 | b.foo();
691 | }
692 | }
693 | ```
694 |
695 | 1. Interface
696 | 2. Class
697 | 3. Nevypíše se nic
698 | 4. Nepůjde přeložit
699 |
700 | *Odpověď*: [2]
701 |
702 | ### Lambda výrazy
703 |
704 | Zpět na [Zkouškové úlohy](#Zkouškové úlohy).
705 |
706 | **Máme definované `Callable`, `Supplier` a `Predicate`. Která z následujících přiřazení lamda výrazu jsou správná (kompilátor je přeloží):**
707 |
708 | ```java
709 | interface Callable {
710 | T call();
711 | }
712 |
713 | interface Supplier {
714 | T get();
715 | }
716 |
717 | interface Predicate{
718 | boolean test(T t);
719 | }
720 | ```
721 |
722 | 1. `Supplier su = () -> { return true; };`
723 | 2. `Callable sa = () -> { return true; };`
724 | 3. `Predicate pr = () -> { return true; };`
725 |
726 | *Odpověď*: [1, 2] `Predicate` potřebuje jeden argument.
727 |
728 | **Do následujícího kódu doplňte do parametru metody map lambda výraz tak, aby výstupní stream obsahoval délky řetězců ve vstupním streamu.**
729 |
730 | ```java
731 | List list = ...;
732 | Stream st = list.stream().map();
733 | ```
734 |
735 | *Řešení:*
736 |
737 | ```java
738 | Stream st = list.stream().map(s -> s.length);
739 | ```
740 |
741 | **Metoda `static void sort( T[] array, Comparator c)` setřídí pole `array` a použije k tomu komparátor `c`. Doplňte do následujícího kódu implementeaci komparátoru lambda vyrázem tak, aby třídil řetězce podle délky.**
742 |
743 | ```java
744 | interface Comparator {
745 | int compare(T o1, T o2);
746 | }
747 |
748 | String[] strings = new String[1000];
749 | /* setup kód */
750 |
751 | Comparator comparator = ;
752 | sort(strings, comparator);
753 | ```
754 |
755 | *Řešení:*
756 |
757 | ```java
758 | Comparator comparator = (s1, s2) -> s1.length - s2.length;
759 | ```
760 |
761 | **Máme následující kód. Doplňte do `forEach` výraz tak, aby se vypsaly všechny prvky seznamu.**
762 |
763 | ```java
764 | interface Consumer {
765 | void accept(T t);
766 | }
767 |
768 | void forEach(Consumer super T> action) { ... } // hlavička
769 |
770 | List list = ...;
771 | list.stream.forEach();
772 | ```
773 |
774 | *Řešení*:
775 |
776 | ```java
777 | list.stream.forEach(a -> System.out.println(a));
778 | ```
779 |
780 | ### Hodnoty proměnných
781 |
782 | Zpět na [Zkouškové úlohy](#Zkouškové úlohy). Viz [Literály](#Literály).
783 |
784 | **Atribut (= field, pozn. Evžena) typu `int` bez explicitní inicializace:**
785 |
786 | 1. je inicializován hodnotou 0
787 | 2. má nedefinovanou hodnotu a při čtení je vráceno předem nestanovitelné číslo
788 | 3. má nedefinovanou hodnotu a při čtení je vyvolána výjimka typu UndefinedValueException
789 | 4. má nedefinovanou hodnotu a překladač nedovolí použití, dokud není jisté, že se napřed nějaká hodnota nastaví.
790 | 5. je inicializován maximální hodnotou, která se do typu int vejde
791 |
792 | *Odpověď:* [1]
793 |
794 | Atribut je defaultně 0 (případně *false* nebo *null*, podle typu), ale kdyby se jednalo o proměnnou v metodě, neprojde to přes kompilaci.
795 |
796 | **Co se nepřeloží?**
797 |
798 | 1. `byte x = 1024; `
799 | 2. `int x = 08; `
800 | 3. `long x = null; `
801 | 4. `char x = "a"; `
802 | 5. `int x = 0b01; `
803 |
804 | *Odpověď*: [1, 2, 3, 4] Viz část o literálech výše.
805 |
806 | **Lokální proměnná typu `int` vyskytující se uvnitř metody:**
807 |
808 | 1. je od mista deklarace inicializovana hodnotou 0
809 | 2. ma nedefinovanou hodnotu a pri cteni je vraceno predem nestanovitelne cislo
810 | 3. ma nedefinovanou hodnotu a prekladac nedovoli pouziti
811 | 4. je inicializovana maximalni hodnotou, ktera se do typu int vejde
812 |
813 | *Odpověď:* [3] viz otázka výše, ale naopak.
814 |
815 | **Co se nezkompiluje?**
816 |
817 | 1. `System.out.println(1+1);`
818 | 2. `int x = 42 + '25';`
819 | 3. `String s = "foo" + 'bar';`
820 | 4. `byte x = 255;`
821 |
822 | *Odpověď:* [2, 3, 4]
823 |
824 | Mezi `''` musí být `char` (tj. jeden znak) a `byte` má rozsah -128 až 127.
825 |
826 | **Co jde přiřadit do proměnné typu boolean?**
827 |
828 | *Odpověď:* pouze `true` a `false`
829 |
830 | **Co se nepřeloží?**
831 |
832 | 1. `int i = 1234_5678; `
833 |
834 | 2. `double d = 3.14_15; `
835 |
836 | 3. `int j = 0x12_ab_12; `
837 |
838 | 4. `int k = null; `
839 |
840 | 5. `boolean b = 0; `
841 |
842 | 6. ```java
843 | char c = '
844 | ';
845 | ```
846 |
847 | *Odpověď*: [4, 5, 6] Viz literály.
848 |
849 | **Co se vypíše?**
850 |
851 | ```java
852 | Integer i1 = 5;
853 | int i2 = i1;
854 | i1 += 1;
855 | System.out.println(i1);
856 | System.out.println(i2);
857 | ```
858 |
859 | 1. 5 5
860 | 2. 5 6
861 | 3. 6 5
862 | 4. 6 6
863 | 5. nic
864 |
865 | *Odpověď*: [3]
866 |
867 | Do `i2` se uloží (zkopíruje) pouze unwrapovaná hodnota z `i1`; změna `i1` tedy `i2` neovlivní.
868 |
869 | ### Triky
870 |
871 | Zpět na [Zkouškové úlohy](#Zkouškové úlohy).
872 |
873 | **Lze napsat deklaraci proměnné `i` tak, aby následující
874 | cyklus byl nekonečný?**
875 |
876 | ```java
877 | while (i != i ) {} // 1.
878 | while (i != i + 0) {} // 2.
879 | while (i <= j && j <= i && i != j) {} // 3.
880 | ```
881 |
882 | *Odpovědi:*
883 |
884 | 1. `i = Double.NaN`, protože `NaN` se nerovná ničemu.
885 |
886 | 2. `i = ""`, stringy lze sčítat s čísly, číslo se převede na string
887 | 3. `i = new Integer(1)` a `j = new Integer(1)`, jejich porovnání poté porovnává reference
888 |
889 | **Příkazem `import static ...` lze naimportovat do lokálního jmenného prostoru:**
890 |
891 | 1. všechny atributy a metody třídy
892 | 2. pouze statické atributy třídy
893 | 3. pouze statické metody třídy
894 | 4. pouze statické metody a statické atributy třídy
895 | 5. pouze atributy a metody označené anotací ``@exportStatic``
896 |
897 | *Odpověď:* [4]
898 |
899 | Analogicky k běžnému `import`, který importuje třídy z balíků, `import static` importuje statické věci ze tříd.
900 |
901 | **Výstupem násedujícího úseku kódu?**
902 |
903 | ```java
904 | public class A {
905 | public int x = 0;
906 |
907 | // [pozn. Evžena] toto je ta zajímavá část
908 | {
909 | x += 1;
910 | }
911 | // konec zajímavé části
912 |
913 | public A() {
914 | x += 1;
915 | }
916 |
917 | public static void main(String[] args) {
918 | A a = new A();
919 | System.out.println(a.x);
920 | }
921 | }
922 | ```
923 |
924 | 1. nelze přelozit
925 | 2. 0
926 | 3. 1
927 | 4. 2
928 | 5. hodnota se můze lišit při opakovaném spuštění programu
929 |
930 | *Odpověď:* [4]
931 |
932 | Jedná se o tzv. *inicializační blok*. Je to blok kódu, který proběhne při každém vytvoření objektu od dané třídy, a to *před* konstruktorem. Inicializačních bloků může mít třída více, poté jsou spuštěny odshora jeden po druhém (a po nich teprve konstruktor).
933 |
934 | **Napište deklaraci proměnné `x` tak, aby po provedení `x = x + 0 `neměla původní hodnotu. Pokud to nejde, zdůvodněte. **
935 |
936 | *Řešení:*
937 |
938 | ```java
939 | String x = "";
940 | x = x + 0; // x je "0"
941 | ```
942 |
943 | **Máme kolekci `ArrayList`. Které přiřazení se přeloží bez chyby?**
944 |
945 | 1. `Collection