Java fejtörők – csapdák, buktató, és szélsőséges esetek. Ez egy könyv címe, amelynek szerzői J. Bloch és N. Gafter. Magyar nyelven a Kiskapu Kft. jelentette meg. A 2010-es magyar kiadás a 2005-ös angol nyelvű kiadás fordítása. A könyv weboldaláról (http://www.javapuzzlers.com), letölthető a 95 fejtörőhöz tartozó mintapéldák gyűjteménye, és elérhető a 270 oldalból minta fejezetként 28 oldalnyi tartalom 9 fejtörővel és azok részletes magyarázataival.
Messze nem mai az anyag, de teljesen örökzöld. Ma is kifejezetten igazán izgalmas átgondolni ezeket a fejtörőket. Biztos vagyok benne, hogy az igazán profiknak is nyújt újdonságot egy-egy fejtörő mögötti részletes magyarázat. Sokszor kiderül az a ravasz és csavaros magyarázatok között, hogy mire gondolt a költő, azaz mi volt/lehetett a Java programozási nyelv tervezése során a szakemberek elképzelése, illetve előfordultak-e kompromisszumok, amiknek persze következményei vannak.
Két – literálként megadott – egész szám összegét kell kapni. Két egyforma értéket várunk:
66666. Mégsem ezt kapjuk. Az első kiírás
66666-ot, a második
17777-et jelenít meg a konzolon. A kulcsszó a különböző egész literálok megadása. Részletes indoklás a blog bejegyzés végén található.
2. fejtörő: Mit ír ki program a konzolra?
1
2
3
4
5
6
7
8
9
10
publicclassTest{
publicstaticvoidmain(String[]args){
Stringpig="length: 10";
Stringdog="length: "+pig.length();
System.out.println("Animals are equal: "+pig==dog);
System.out.println(("Animals are equal: "+pig)==dog);
System.out.println("Animals are equal: "+(pig==dog));
System.out.println("Animals are equal: "+pig.equals(dog));
}
}
Szöveges literálokat hasonlítunk össze, amelyek egyforma (
length:10) tartalommal jönnek létre. Döntések eredményeit várjuk,
boolean típusú változókat. Négy sorba tördelve ezt kapjuk:
false,
false,
Animals are equal:false,
Animals are equal:true. A kulcsszó a művelet végrehajtás sorrendje, másképpen kifejezések kiértékelési sorrendje. Részletes indoklás a blog bejegyzés végén található.
3. fejtörő: Mit ír ki a program a konzolra?
1
2
3
4
5
6
7
8
9
10
11
/*
* Generated by the IBM IDL-to-Java compiler, version 1.0
* from F:\TextRoot\apps\a1\units\include\PolicyHome.idl
* Wednesday, June 17, 1998 6:44:40 o'clock AM GMT+00:00
*/
publicclassTest{
publicstaticvoidmain(String[]args){
System.out.print("Hell");
System.out.println("o World!");
}
}
Természetesen a megjegyzéssel nem törődünk és arra gondolunk, hogy a konzolon a
Hello World! jelenik meg (a két kiíró utasítás eredménye egyetlen sorban egymás után) és nem is értjük, hogy mi a kérdés. Nyilván a helyzet nem ilyen triviális. A program nem futtatható. A kulcsszó az unikód escape szekvencia (védőkarakter). Részletes indoklás a blog bejegyzés végén található.
4. fejtörő: Mit ír ki a program a konzolra?
1
2
3
4
5
6
7
publicclassTest{
publicstaticvoidmain(String[]args){
System.out.print("browser:");
https://www.google.com;
System.out.println(":maximize");
}
}
Nyilván szintaktikai hibát feltételezünk, de a program hibátlan és futtatva ezt látjuk a konzolon:
browser::maximize. A kulcsszó a címke/utasításcímke. Részletes indoklás a blog bejegyzés végén található.
5. fejtörő: Mit ír ki a program a konzolra?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
publicclassTest{
privatestaticbooleandecision(){
try{
returntrue;
}
finally{
returnfalse;
}
}
publicstaticvoidmain(String[]args){
System.out.println(decision());
}
}
Gyanús a helyzet. Adott egy függvény, aminek kötelezően van visszatérési értéke. Ez rendben van. Tudjuk, hogy a
return utasítás kiugrik a függvényből, eljárásból, ciklusból. A kivételkezeléshez kötődő nyelvi kulcsszavakat is ismerjük:
try,
catch,
finally,
throw,
throws. Ezek működését is ismerjük. Azt feltételezhetjük, hogy a
try blokkból kiugrunk
true értékkel és a
decision() függvényt meghívó
main() metódusba visszatérve kiíródik a konzolra, hogy
true. Mintha a
finally blokk nem is lenne. Nem így történik. A programot futtatva
false jelenik meg a konzolon. A kulcsgondolat a
finally blokk végrehajtásának vezérléséhez kapcsolódik. Részletes indoklás a blog bejegyzés végén található.
6. fejtörő: Mit ír ki a program a konzolra?
1
2
3
4
5
6
7
publicclassTest{
publicstaticvoidmain(String[]args){
Stringhello="Hello World!";
for(inti=0;i<hello.length();i++)
System.out.write(hello.charAt(i));
}
}
Már biztosan gyanakszunk, de azért a
Hello World!-öt várjuk a konzolon. Ehelyett nem jelenik meg semmi. A kulcsszó a puffer ürítés. Részletes indoklás a blog bejegyzés végén található.
Részletes indoklások
1. fejtörő:
int típusú literál az
54321, de
long típusú literál az
5432l. Az
1 – mint numerikus karakter – nem egyezik meg a kis
l betűvel. Tanulság: használjuk nagy
L betűt a
long típusú literálok végén. További részletek a könyv 11-12. oldalán találhatók.
2. fejtörő: a konkatenálást végző
+ operátor erősebben kötődik, mint a két objektumreferencia azonosságát eldöntő
== operátor. Az első kiírásban látható művelet igazából a második kiírásban látható zárójeles formában kerül végrehajtásra. A harmadik kiírást az magyarázza, hogy a
String típusú literálokat memóriacímeik és nem a bennük tárolt karaktersorozat/érték alapján hasonítódnak össze. A helyes gondolatmenet implementálását a negyedik kiírás tartalmazza: (megegyezik-e a két szövegliterál tartalma). További részletek a könyv 29-31. oldalán találhatók.
3. fejtörő: a megjegyzés 3. sorában található
\u karaktert 4 db hexadecimális számnak kellene követnie. Ez hiányzik, ami szintaktikai hibát jelent. További részletek a könyv 33-34. oldalán találhatók.
4. fejtörő: az URL-ben lévő : egy ugyanolyan címke, amit a
switch utasításban a
case ágaknál szokás használni. Ez így is megengedett, de teljesen haszontalan. További részletek a könyv 47-48. oldalán találhatók.
5. fejtörő: a kivételkezelési mechanizmus úgy működik, hogy a
try blokkban lévő utasításoktól függetlenül – akár volt kivétel akár nem, akár
return utasítást tartalmaz a
try blokk – a
finally blokk mindenképpen végrehajtódik. Ebben az esetben a kivételkezelési mechanizmus erősebb. További részletek a könyv 77-78. oldalán találhatók.
6. fejtörő: a
System.out egy
PrintStream osztályú objektum. Többnyire automatikusan ürítik az átmeneti tárolóját az ezt használó utasítások, például
System.out.print() és
println(). A
write() metódus nem üríti ezt a puffert. További részletek a könyv 195-196. oldalán találhatók.
Tanfolyamainkon nem kifejezetten foglalkozunk hasonló problémákkal, de azért időnként feszegetjük a határokat. Természetesen részletesen indokoljuk, ha előkerül valamilyen hasonló eset. Általánosságban nem célunk, hogy extrém eseteken keresztül, a programozási nyelv gyenge pontjaira kihegyezve oktassuk a Java programozási nyelvet.
Én azt olvastam, hogy a törtek ábrázolása a C alapú nyelvekben elég pontatlan. Ki is próbáltam: double 0.0-hoz hozzáadva 10-szer egymás után 0.1-et, ez jött ki:
0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
Róbert: így van. Mindig vigyázni kell a lebegőpontos adattípusokkal. Ha nagyon pontos számítás szükséges, vagy nem szeretnéd sok egymást követő művelet hibáját „összehalmozni”, akkor használj a primitív float vagy double típusok, vagy a Float vagy Double csomagolóosztályaik helyett java.math.BigDecimal osztályt.
Úgy látom, már a generikusságot is érted. Ügyes vagy!
(Aki esetleg még nem van képben, errefelé keressen/kérdezzen, hogy összeálljon a kép ehhez a megoldáshoz: Number, Double, Float, Double.class, Double.valueOf(), doubleValue(), Class<T>, <T extends Number>, n instanceof Float és még: Generic, Autoboxing, Unboxing).
Weboldalunkon cookie-kat (sütiket) használunk, melyek célja, hogy teljesebb szolgáltatást nyújtsunk látogatóink részére. További böngészésével hozzájárul ezek használatához. ElfogadAdatkezelési szabályzat
Privacy Overview
This website uses cookies to improve your experience while you navigate through the website. Out of these, the cookies that are categorized as necessary are stored on your browser as they are essential for the working of basic functionalities of the website. We also use third-party cookies that help us analyze and understand how you use this website. These cookies will be stored in your browser only with your consent. You also have the option to opt-out of these cookies. But opting out of some of these cookies may affect your browsing experience.
Necessary cookies are absolutely essential for the website to function properly. This category only includes cookies that ensures basic functionalities and security features of the website. These cookies do not store any personal information.
Any cookies that may not be particularly necessary for the website to function and is used specifically to collect user personal data via analytics, ads, other embedded contents are termed as non-necessary cookies. It is mandatory to procure user consent prior to running these cookies on your website.
Én azt olvastam, hogy a törtek ábrázolása a C alapú nyelvekben elég pontatlan. Ki is próbáltam:
double
0.0-hoz hozzáadva 10-szer egymás után 0.1-et, ez jött ki:0.1
0.2
0.30000000000000004
0.4
0.5
0.6
0.7
0.7999999999999999
0.8999999999999999
0.9999999999999999
Ugyanez
float
-tal:0.1
0.2
0.3
0.4
0.5
0.6
0.70000005
0.8000001
0.9000001
1.0000001
Róbert: így van. Mindig vigyázni kell a lebegőpontos adattípusokkal. Ha nagyon pontos számítás szükséges, vagy nem szeretnéd sok egymást követő művelet hibáját „összehalmozni”, akkor használj a primitív
float
vagydouble
típusok, vagy aFloat
vagyDouble
csomagolóosztályaik helyettjava.math.BigDecimal
osztályt.Megírtam Róbert 11-elemű
double
listáját kiíró programot hagyományosan:double x=0.0, d=0.1;
for(int i=1; i<=10; i++) {
System.out.println(x);
x+=d;
}
System.out.println(x);
Funkcionálisan hogyan lehetne megcsinálni ugyanezt?
Ha elegendő egy folyamba tenni a számokat és ezt kiírni a konzolra, akkor így (de persze több megoldás is van):
System.out.println(Stream.iterate(0.0, i->i+0.1).limit(11).
collect(Collectors.toList()));
Ekkor – röptében létrehozott adatszerkezettel – más műveleteket nem tudunk végrehajtani.
Ezt kapjuk eredményül:
[0.0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6, 0.7, 0.7999999999999999, 0.8999999999999999, 0.9999999999999999]
Erre gondoltál Feri?
Igen, köszönöm. Tetszik ez a tömör forráskód. Kipróbáltam, hogy módosítva a generáló metódust a második listát is megkapom:
iterate(0.0f, i->i+0.1f)
.Úgy látom, már a generikusságot is érted. Ügyes vagy!
(Aki esetleg még nem van képben, errefelé keressen/kérdezzen, hogy összeálljon a kép ehhez a megoldáshoz:
Number
,Double
,Float
,Double.class
,Double.valueOf()
,doubleValue()
,Class<T>
,<T extends Number>
,n instanceof Float
és még: Generic, Autoboxing, Unboxing).