Haladó Java kurzus
Általános linkek
Tesztelés (JUnit)
- A témakör feladataihoz általában
- A teszteket (vagy csak néhányat belőlük) fogd össze egy
test suite
-ba.
- futtatni lehet az egész suite-ot, vagy csak egyes teszteket
- Próbáld ki a JUnit-ot a parancssorból és fejlesztőkörnyezetből.
- fejlesztőkörnyezetben: a teszteket tedd külön projektbe
parancssorban: ha a JUnit két fájlja (junit-4.12.jar és a hamcrest-core-1.3.jar) a $JUNIT
, a program a $SRC
és a tesztelő a $TEST
könyvtár alatt található, így lehet fordítani és futtatni:
javac -cp $JUNIT\junit-4.12.jar;$SRC;$TEST $SRC\csomagneve\Tesztelendo.java
javac -cp $JUNIT\junit-4.12.jar;$SRC;$TEST $TEST\csomagneve\Tesztelo.java
java -cp $JUNIT\junit-4.12.jar;$JUNIT\hamcrest-core-1.3.jar;$SRC;$TEST org.junit.runner.JUnitCore csomagneve.Tesztelo
- A különböző ellenőrzésekhez írj magyarázó szöveget. (Egy szöveges első paramétert kell adni a különböző assert-variációknak.)
- Készítsd el a Fibonacci-függvény rekurzív és iteratív implementációját, és teszteld őket néhány konkrét értékre.
- Vedd figyelembe a szélsőséges eseteket is (nulla vagy negatív paraméter).
- Teszteld a kétféle Fibonacci implementációt azonos számpárokra. Eszköz: paraméterezett tesztelő.
- Teszteld, hogy az a metódus, amelynek törzse egy végtelen ciklust tartalmaz, elszáll (nem terminál rögzített, hosszú időtartam alatt).
- Teszteld, hogy ha egy metódusban a nullával való osztás, illetve tömb túlindexelése szerepel, akkor a megfelelő kivételek tényleg kiváltódnak. (Tehát az az elvárt működés, hogy a kivétel kiváltódik.)
- Készíts egy
Pont
osztályt, amelynek adj néhány jellemző műveletet, pl. eltolás. Mindegyik teszteset elején vedd fel az adott pontot (@Before
), és teszteld mindegyik műveletet.
- Készíts három osztályt: a
Posta
osztály kezdetben két Postafiok
példányt kap meg. A Posta
osztály valogat
metódusa egy Level
példányt kap, és kézbesíti: a páros címűeket az első, a páratlan címűeket a második postafiókba (fogad
metódus). A tesztelő küldjön el néhány levelet, és ellenőrizze, hogy a megfelelő helyre érkeznek-e.
- Az előző tesztelőt alakítsd át viselkedési tesztelővé, hogy a
Posta
osztályt önmagában, a Postafiok
használata nélkül tesztelje. Készíts két Postafiok
típusú mock-ot és ellenőrizd, hogy a megfelelő leveleket kapják meg. mockito
- Kényszerítsd ki, hogy a megfelelő sorrendben hívódjanak meg a
fogad
metódusok. InOrder
- Készíts egy teljesen egyszerű
"Helló világ"
programot. Teszteld, hogy tényleg azt írja-e ki a program. Ehhez szükséges a sztenderd kimenet átirányítása.
- A tesztelőt Javából is meg lehet hajtani. Tedd ezt meg, és írd ki először a sikeres, majd a sikertelen tesztesetek neveit.
- Az egységtesztelésre egy másik lehetőség, ha nem konkrét elvárt értékeket adunk meg, hanem valamilyen feltétel teljesülését próbáljuk ki sok, véletlenszerűen választott adattal. Erre jellemzően nem a JUnit eszközt használjuk.
- Írj olyan (sima Java) kódrészletet, amely megadott számú (pl. 100), véletlenül választott
n
értékre teszteli, hogy a Fibonacci-implementációdra fennáll-e a fib(n) == f(n-1) + f(n-2)
tulajdonság.
- (bónusz) teszteld tulajdonságalapú tesztelővel, pl. QuickCheck
Hibakeresés, program futásának vizsgálata (debugging)
A hibakereséshez a fenti Posta
feladatot módosítjuk. Most már nem egyetlen Posta
példányunk van, hanem tetszőlegesen sok állomás. Mindegyik postának van egy szöveges címe (tegyük fel, hogy mindegyik különböző), és tetszőlegesen sok postafiókot tartalmaz. Az állomások eltárolják, hogy adott című állomásra küldött leveleket melyik másik postának kell továbbítani. Ilyen továbbítási bejegyzésből is tetszőleges számú lehet mindegyik postán. Ha egy levél eléri a célállomását, a fenti feladat szerint kézbesítse az állomás, különben pedig dobja el. A levelek jegyezzék meg, hogy melyik állomásokon utaztak át.
- Egy fájl írja le a postaállomásokat.
- Egy fájl írja le a levélküldéseket: melyik állomáson adták fel a levelet, melyik állomásra megy, melyik postafiókba.
Az alábbi lépéseket, bár mind meg lehet őket tenni parancssorból is, a kényelem kedvéért csak fejlesztőkörnyezetben hajtjuk végre.
- Olvasd be a fájlok tartalmát, és írd ki egy fájlba mindegyik levélről, milyen utat járt be.
- Tegyél töréspontot oda, ahol a levél a következő állomásra kerül. Futtasd debug üzemmódban a programot.
- Figyeld meg, milyen adatok (változók, adattagok, adatszerkezetek) láthatóak.
- A megfelelő nézetben, vagy az egér fölé húzásával láthatóak a közvetlenül elérhető adatok.
- Ki lehet értékelni (evaluate/inspect) tetszőleges kifejezést.
- Ha gyakran szükség van rá, ki lehet emelni (display/watch). Legyen most ilyen az, hogy az adott helyről tovább lehet-e küldeni a levelet.
- Engedd tovább a vezérlést. Az adatok megváltoznak.
- Léptesd a programot utasításonként.
- A következő állomás meghatározása kerüljön külön metódusba. Állj meg, amikor épp rákerül a vezérlés.
- Léptesd át a metódushívást. Az eredménye rögtön látható.
- Léptesd bele a vezérlést a metódusba, és hajtsd azt végre lépésenként.
- Miközben benne jársz, nézd meg a külső veremkeretek tartalmát.
- Léptesd bele a vezérlést, lépj benne néhányat, majd lépj ki belőle.
- Több szintet is visszaléphetsz (drop to frame).
- Legyen a töréspontnak feltétele.
- A töréspont pontosan akkor aktiválódjon, amikor a program harmadszorra érinti.
- A töréspont pontosan akkor aktiválódjon, amikor egy levelet egy előre megadott nevű állomásra kézbesítünk.
- Több debug végrehajtás is futhat egyszerre. Próbáld ki: külön vezérelhetőek.
- Keress olyan konfigurációt, amelyre rosszul működik a program.
- Vizsgáld meg a debuggerben a helytelen működést.
- Ha a levél nem továbbítható és nem is kézbesíthető, a program váltson ki egy kivételt. Ha a levél túl sokáig utazik, egy másik fajta kivételt.
- Kapd el úgy, hogy kimondottan az adott fajta kivételre figyelsz.
- Kapd el úgy, hogy tetszőleges kivételre figyelsz.
- Készíts olyan programot, amely ellenőrzi egy konfigurációról, hogy helyes-e (sosem működik rosszul) és teljes-e (mindenhonnan eljuthatnak-e a levelek mindenhová).
- A program elkészítése során, ahol szükséges, használd a debuggert.
- Használd a debuggert szükség szerint a tesztelés során is.
Felsorolási típus
- Készítsd el a
Varos
felsorolási típust.
- a felsorolási típus elemei legyenek a városok nevei (csupa nagybetűvel)
- a típusnak legyen
cim
mezője, egy int
- lehessen az elemeket egy
int
-tel paraméterezni, amit vegyen át a mezőbe
- lehessen egy másik várossal paraméterezni az elemeket, ekkor az aktuális elem címe a paraméterből jöjjön
- készíts saját
toString
metódust
- Írd ki a
Varos
elemeit sorban.
- Készítsd el a
HetNapja
felsorolási típust.
- Az elemek legyenek a napok nevei rövidítve.
- Az elemek paraméterei legyenek a napok teljes nevei különböző nyelveken. Tetszőleges sok paramétert kaphasson az elem.
- Egy
static
mező írja le a támogatott nyelvek szöveges kódjait egy tömbben.
- Legyen egy
get(String lang)
metódus, amely adja vissza a nap nevét a megadott nyelven, illetve a "?"
szöveget, ha a nyelv nem ismert, vagy a paraméterezésben nincsen jelen.
- A levél most már ne közvetlenül a címét tartalmazza, hanem a címzett városát. A levélen legyen rajta a nap is: a postára feladáskor paraméterként kelljen megadni, hogy melyik napon történt a feladás. Amikor a levél a következő postaállomásra utazik, lépjen egy napot.
Lambda függvény
- Készíts különböző névtelen függvényeket. Készítsd el őket rövidebb és hosszabb alakjaikban is. Próbáld ki, mennyivel gyorsabb, ha primitív típussal dolgozol. Néhány a legfontosabb lambda-interfészek közül (az első kivételével a java.util.function csomag osztályai):
- Ø → Ø:
Runnable
- Ø → T:
Supplier
- Ø → primitív:
PPPSupplier
, pl. IntSupplier
- T → Ø:
Consumer
- primitív → Ø:
PPPConsumer
- T1 → T2:
Function
- T → boolean:
Predicate
- T1, T2 → T3:
BiFunction
- Készíts olyan lambdát, amely többször meghívva a pozitív egész számokat adja ki.
- Készíts olyan lambdát, amely többször kiírja a beérkező számokat.
- Készíts faktoriális lambdát.
- Készíts Fibonacci lambdát.
- Készíts olyan lambdát, amely két
Map<String, Integer>
paramétert kap, és ilyet is állít elő; az eredményben az adott szöveghez hozzárendelt érték az eredetiek összege legyen.
- Készíts olyan lambdát, amely megadja egy számról, hogy prím-e.
- Készíts olyan lambdát, amely a prímszámokat állítja elő sorban. (Ennek kell, hogy legyen állapota is.)
- Készíts olyan lambdát, amely a sík pontjait állítja elő (valamilyen) sorrend szerint.
- Készíts olyan lambdát, amely egy
Supplier
-t kap, és önmaga is Supplier
: először a “belső” Supplier
első elemét, aztán az első két elemét, aztán az első három elemét stb. adja ki.
- Készíts olyan lambdát, amely paraméterként egy
BiFunction
-t kap, és olyan BiFunction
az eredménye, amely az eredetivel megegyező működésű, de fordítva vannak a paraméterei.
- Készíts olyan lambdát, amely két
IntUnaryOperator
(matematikai értelemben vett) kompozícióját képzi. Azaz, a paraméterei két IntUnaryOperator
, és ilyennel is tér vissza: a két paraméter kompozíciójával.
- Készíts olyan lambdát is, amely egy
IntUnaryOperator
-t komponál egy IntBinaryOperator
mindkét paraméterére.
- Készíts olyan lambdát, amely két függvényt kap (ezek
Integer
értéket állítanak elő), és olyan predikátumot ad ki, amely akkor ad igaz értéket, ha a predikátum paraméterére az első függvényt alkalmazva nagyobb szám adódik, mint a második paramétert alkalmazva. A predikátum és a függvények bemenő paramétere legyen valamilyen rögzített típus, pl. String
.
- Az
Arrays.sort
művelet második paraméterként kaphat egy lambdát, ami az összehasonlító rendezés feltételét írja le. Rendezd a parancssori paramétereket…
- a hosszuk szerinti sorrendbe
- a számokat elöl (szám szerint), utána a nem-szám szövegeket (ábécésorrendben)
Folyam (stream)
A Stream
osztály segítségével sokszor tömör megoldások adhatók. A legtöbbjük lambdákat használ. Cheat sheet.
Három fázis: a folyam elkészül, dolgozunk vele, majd felhasználjuk.
- Elkészítés.
- Más adatokból: tömb (
Stream.of
, Arrays.stream
), lista (myList.stream()
), fájl sorai
- Ha az adatok sorrendje nem számít, a folyam elemeit lehet párhuzamosan is kezelni:
parallel()
.
- A folyamok “egyszer használatosak”, szemben az adatszerkezetekkel.
Supplier
segítségével: generate
- speciálisabban:
iterate
, IntStream.range
- A primitív típusú elemekből álló folyamok típusa más, pl.
IntStream
, és a kapcsolódó lambdák műveleteinek neve általában ...AsInt
- Átalakítás:
map
, mapTo...
, filter
, limit
, skip
, sorted
- Felhasználás: a
Collectors
műveletei segítségével. Ezeket a műveleteket (a JUnit assert
-jeihez és a Mockito műveleteihez hasonlóan) gyakran import static
importáljuk.
- elemenként felhasználva:
forEach
- adatszerkezetté alakítva:
to...
: tömb, lista, Map
- egy értékké alakítva
- a folyam egy eleme:
min
, max
, find...
- szöveggé összefűzve:
joining
- számmá alakítva:
count
- logikai:
anyMatch
, allMatch
, noneMatch
Map
-be csoportosítva: ...By
- általánosan:
reduce
, collect
A műveletek, ha nem biztos, hogy van eredményük (pl. find
), Optional<T>
-t adnak vissza. Ez kevésbé veszélyes, mint ha null
lenne ilyen esetekben az eredmény.
Az alábbi feladatok megoldása során, ahol csak lehet, használj folyamokat és lambdákat.
- Egy fájl mindegyik sorához fűzz hozzá egy rögzített szöveget, és az eredményt írd ki egy másik fájlba.
- … csak azokat a sorokat, amelyek hosszabbak 5 karakternél
- … az első 3 sor kivételével
- … az első 10 sor adatait feldolgozva
- … legfeljebb 10 kimeneti sort készítve
- … ábécésorrendben
- … aszerinti sorrendben, hány különböző betűt tartalmaznak
- … a palindrom szavakat úgy, ahogy vannak, a többieket “palindromosítva”:
"abcd"
-ből legyen "abcddcba"
- Készíts lambdát, amely egy
Scanner
-t használ fel, és olyan folyamot készít, amely a Scanner
-ből kiolvasott szavakat adja vissza, illetve, miután elfogytak, null
-t.
- Mivel a
limit
-nek csak Java 9-ben lesz predikátumos változata, ez a folyam csak korlátozottan használható.
- Egy fájlban mindegyik sor egy pont két Descartes-koordinátáját tartalmazza.
- Add meg, melyik pont van legközelebb/legtávolabb az origótól.
- Add meg a legelső olyan pontot, amelynek mindkét koordinátája prímszám.
- Összesen hány ilyen pont van?
- Van-e ilyen pont?
- Mindegyik pont ilyen-e?
- Készíts folyamot a nemnegatív számokból:
0..Integer.MAX_VALUE
. Add őket össze.
- Készíts különböző változatokat: különböző műveletekkel létrehozva a folyamot, primitív/referencia elemekből.
- Mi történik
parallel()
hatására?
- Egy fájlban egész számok vannak. Azokból, amelyek értéke legalább 2,
collect
segítségével készíts Map
-et, amelyben a legkisebb prímosztóhoz lesznek rendelve a számokból képezett halmazok.
Készíts eszközt, amit az alábbi módon lehet használni (st
a Scanner
ből érkező tokenek folyama, tipp itt):
withScanner("test.txt", st -> {
st.forEach(System.out::println);
});
- Próbáld ki a
Map
-be a Java 8-ban bekerült default metódusokat.
- Azokba a postafiókokba, amelyek üresek, tegyél be (közvetlenül) egy levelet.
Csoportosítsd a(z összes posta összes postafiókjába) beérkezett leveleket aszerint, hogy melyik nap érkeztek meg.
Generikus típusok
Hasznos anyag a típusparaméterek megszorításairól. Egy másik jó leírás.
- Készítsünk egy
BiMap
adatszerkezetet, ami kulcs és érték szerint is rendezve tárol kulcs-érték párokat. Két típusparamétere van, a kulcsok típusa és az értékek típusa. Implementációját megvalósíthatjuk két TreeMap
-pel.
- Legyen egy statikus
create
metódus, ami akkor használható, ha a létrehozott BiMap
mindkét paramétere Comparable
. A létrehozott BiMap
az alapértelmezett összehasonlítást fogja használni.
- Készítsünk egy másik
create
metódust, ami két Comparator
objektumot kap, egyet a kulcsokra, egyet az értékekre. A létrehozott BiMap
ezekkel végezze el az összehasonlítást.
- Készítsünk “kulcs-érték-pár beszúrás”, “érték keresése kulcs alapján”, “kulcs keresése érték alapján” metódusokat.
- Készítsünk egy metódust, ami két listát kap, az egyik paramétere a kulcstípus, vagy annak egy altípusa, a másiké az értéktípus vagy annak egy altípusa. Ha a két lista egyező hosszú, akkor végigmegy rajtuk és berakja őket a
BiMap
-be.
- Teszteljük a programot, adjunk meg olyan tesztesetet, amikor egy altípus listáját akarjuk beszúrni a
BiMap
-be.
- Készítsünk genetikus algoritmust alkalmazó metódust, amely a következőképpen működik. A kiemelten szedett nevek a metódus paraméterei lesznek; az egyedek típusa típusparaméter.
- Először
populationCount
darab egyedet készít a createRandomEntity()
többszöri meghívásával.
- Ezután két (véletlenszerűen kiválasztott) egyedre meghívja a
doCrossover(Entity e1, Entity e2)
metódust. Ezt crossoverCount
alkalommal ismétli.
- Ezután minden egyedet megváltoztat
mutationProbability
valószínűséggel, a mutateEntity(Entity e)
metódus segítségével. (Általában a mutationProbability
értéke viszonylag alacsony, pl. 0.1%.)
- A létrejött egyedeknek kiszámolja a fitneszértékét:
calculateFitness(Entity e)
. Ezek közül a legjobb pruneCount
darab egyedet tartja meg, a többi helyére új egyedeket generál a createRandomEntity()
segítségével.
- A fenti lépések egy új generációt állítanak elő. Összesen
generationCount
darab generációváltást tesz meg, majd a populáció legnagyobb fitneszű egyedével tér vissza.
- Keressünk közelítő megoldást a hátizsák-problémára genetikus algoritmus segítségével.
Szálkezelés
- Készíts olyan metódust, amely egyszámjátékot játszik az alábbiak szerint.
- A játékhoz tartozik egy vezérlő, ami egy szálon fut, valamint néhány játékos.
- A vezérlőnek van egy metódusa, amely egy számot fogad. A fogadott számokat eltárolja.
- A játékosok számokat generálnak (pl. 0 és 1000000 között), és beküldik azokat a vezérlőnek.
- A vezérlő másodpercenként kiírja, melyik a legkisebb szám, amit csak egyvalaki küldött be, valamint a beküldő játékos sorszámát.
- Szinkronizáció nélkül kivétel váltódik ki.
- Készíts olyan
applyAssoc
metódust, amely egy T→T
lambdát és T
típusú értékek egy tömbjét kapja meg. Feltételezve, hogy a lambda asszociatív függvényt ír le, számítsd ki, milyen eredményt ad az értékekre alkalmazva (pl. [1,2,3,4]
a (n,m) -> n + m
lambdával a 10
értéket adja). Feltételezzük, hogy van legalább egy elem a tömbben.
- Mindig felezd meg a tömböt (kivéve, ha már egyetlen elemnél jársz), és az alsó felére és a felső felére külön szál számítsa ki az eredményt, az
applyAssoc
rekurzív meghívásával.
- Oldd meg a feladatot a
RecursiveTask<T>
és ForkJoinPool
segítségével is. Készítsd el a RecursiveTask<T>
leszármazottját, AssocTask<T>
-t, ez lesz applyAssoc
megfelelője. Ennek a compute()
metódusát kell hasonlóan megírni az applyAssoc
-hoz: el kell készíteni két AssocTask<T>
példányt, majd meghívni rájuk a fork()
és a join()
metódusokat.
- Adott
n
-re készíts az Executors.newFixedThreadPool(n)
hívás segítségével egy eszközt, amely egyszerre legfeljebb n
tevékenységet hajt végre n
szálon.
n
-re gyakran jó választás Runtime.getRuntime().availableProcessors()
, mivel a processzor ennél több feladatot úgysem tud párhuzamosan végrehajtani.
- A pool
execute()
metódusának át lehet adni egy lambdát, amelyet végrehajt (ha van szabad szál, rögtön, különben sorba áll).
- Ha lambda eredményt is állít elő, a
submit()
metódust kell használni. Az eredményt a submit()
visszatérési értékeként kapott Future
-ön a get()
metódus meghívásával lehet megkapni.
- Adj át sok tevékenységet a pool-nak, mindegyik várjon véletlenszerű ideig. A
Future
-jeik segítségével írd ki másodpercenként, hogy melyik van készen.
- Készíts saját pool implementációt szálak segítségével.
- Az
n
szál mellett szükség lesz egy vezérlőre, ami kiosztja a feladatokat, és a szálak ennek jelzik, ha felszabadultak.
Önelemzés (reflection), annotációk
- Készíts metódust, amely ellenőrzi, hogy a paraméterben kapott osztály mindegyik adattagja
private
láthatóságú-e.
- Készíts
@Author
annotációt típusokhoz, amely a típus készítőjének nevét írja le.
- Készíts metódust, amely megkapja a típus nevét szövegesen, és kiírja a készítő nevét (ha az annotáció szerepel a típuson).
- Az annotáció legyen alkalmazható metódusokra is.
- Készíts metódust, amely megkapja a típus nevét szövegesen, és kiírja a típus azon metódusainak nevét, amelyeknek más a készítője, mint a típusé.
- Az annotáció legyen többszörözhető (
@Repeatable
).
- A fenti metódusok működjenek akkor is, ha egy szerző szerepel, és akkor is, ha több.
- Készíts
@Date
annotációt, amelyet metódusok kaphatnak meg.
- Készíts metódust, amely paraméterként kapott nevű osztály metódusai közül megkeresi azokat, amelyek rendelkeznek az annotációval, és paraméter nélküliek. Ezek közül hívd meg azokat, amelyek egy (paraméterben kapott) dátumnál nem régebbiek.
- Készíts
@ParameterFor
annotációt, amely metódusra alkalmazható. A metódus valamilyen értékek tömbjét adja vissza. Az annotáció kap egy szöveget, ami ugyanabban az osztályban egy metódus neve; ez a metódus olyan típusú paramétert vár, mint amilyen értékek a másik metódus visszatérési értékének tömbjében szerepelnek. Az annotáció legyen többszörözhető.
- Készíts metódust, amely paraméterként kapott nevű osztály metódusai közül megkeresi azokat, amelyek rendelkeznek az annotációval. Az annotációban szereplő nevű metódust hívd meg sorban azokkal a paraméterekkel, amelyeket az annotált metódus visszatérési értékének tömbjében szerepelnek.
Fejlett fájlkezelés
A java.nio
(“New I/O”) csomagban sok érdekes eszköz található meg.
- Az első parancssori paraméter egy fájlnév, ezután pozíciók (
long
) és értékek (byte
) párjai jönnek. Ellenőrizd, hogy a fájl megadott pozíciói a megadott értékeket tartalmazzák-e.
- A zip fájlok szerkezete ismert (pl. ennek a leírásnak a 4.3.7. fejezete tartalmazza). Tömöríts be tetszőleges fájlokat, majd az előállt zip fájlt megvizsgálva írd ki az alábbiakat. Tipp: a
ByteBuffer
alapértelmezés szerint MSB bájtsorrendet használ, mint a Java általában; a zipből való megfelelő olvasáshoz meg kell hívni rá a .order(ByteOrder.LITTLE_ENDIAN)
műveletet.
- a betömörített fájlok
- neve
- mérete
- az alkalmazott tömörítési módszer (ha 0, egyszerűen tárolva van a fájl tartalma)
- a betömörített fájlokat mentsd is el, ha szimplán tárolva vannak (a bájtsorrendet ekkor nem kell megforgatni)
- Figyeld meg egy megadott nevű könyvtár fájljainak megváltozásait: minden változáskor jegyezd fel a fájl új tartalmát. A felhasználó a sztenderd bemeneten küldhesse el egy fájl nevét és egy verziószámot; a program írja vissza a fájlba a kiválasztott korábbi tartalmat.
- Japán ismerősöd küldött egy egysoros üzenetet. Csak annyi ismert az üzenetről, hogy szerepel benne a
大
karakter. Találd ki, milyen kódolást használhatott, és mi az üzenet tartalma.
Reguláris kifejezések
- Tüntesd el egy szövegből a bezárójelezett részeket. Feltehető, hogy a zárójelek nem tartalmaznak zárójeleket.
- Készíts morzekódra és -ról konvertáló programot. Kódtáblázat itt érhető el.
- Adott néhány karakter és két fájl. Add meg azoknak a soroknak a sorszámát a két fájlból, amelyek azonosak; annyi eltérés engedhető meg, hogy a megadott karakterek esetén nem számít, kis- vagy nagybetűsek-e.
Osztálybetöltés (nem része a számonkérésnek)
- Készíts olyan programot, amelyik egy osztály több különböző változatát képes betölteni (természetesen különböző osztálybetöltő-példányok segítségével; az osztálybetöltéshez használt interfész ne változzon). Lehessen példányt készíteni bármelyik betöltött változatból.
- Kipróbálni egyszerű osztályokkal érdemes, pl. a faktoriális különböző változataival.
- Készíts olyan programot, amelyik egy könyvtár változásait figyeli, és ha abban megjelenik egy új
.class
fájl, akkor betölti belőle az osztályt, és készít belőle egy példányt. A program a sztenderd bemenetről sorokat olvas be; minden beolvasott sorral meghívja mindegyik betöltött példány processLine
metódusát.
- Az osztályok
processLine
metódusai legyenek egyszerűek, pl. írják ki a szót visszafelé vagy megkettőzve.
- Készíts minimális alkalmazásszervert, amely egy
Socket
-en keresztül várja a bejövő kapcsolatokat. A szerver legyen képes alkalmazásokat (osztályokat) betölteni. Amikor egy böngésző kapcsolódik, elküldi a szerverünknek az első sorban a kérés típusát (általában GET
vagy POST
) és a lekért URL-t. A szerver hívja meg a betöltött alkalmazások acceptsURL
metódusát az URL-lel; az első alkalmazás, amelyik igaz értéket ad vissza, fogja kezelni a kérést. Hívd meg az alkalmazás doGet
vagy doPost
metódusát a kérés típusától függően; ez előállítja a kiszolgálandó tartalmat, amelyet a megfelelő fejléccel együtt küldj vissza a böngészőnek. Ha egyik alkalmazás sem kezeli a kérést, akkor küldj vissza egy elutasító választ.
- Próbáld ki az
URLClassLoader
osztályt. Ennek segítségével pl. fájlokból és a netről kényelmesen be lehet tölteni osztályokat.