IDK0051 2. loengutesti kordamisküsimused kevad 2015
1. Kas erind (exception) on objekt?
Spetsiaalne Throwable-tüüpi objekt vigade
haldamiseks Javas.
Oskame ette näha
olukordi , kus programm ei pruugi käituda soovitult
Võib-olla suudame programmi töö taastada
Informeerime kasutajat veast viisakal moel
try {
new Item(8);
}
catch (Exception e) {
System.out.
print (e.getMessage()); } Lisaks võimalik
finally osa – täidetakse alati kui programm ellu jääb
Exception ex = New Exception(„Viga”) :
tavaline uue objekti loomine
konstruktorile anname
String -tüüpi argumendi „Viga”
catch (SomeException e) :
püütakse kinni SomeException-tüüpi objekt, mida saab kasutada nime „e” abil
2. Mis vahe on kontrollitud (checked) ja kontrollimata (unchecked) erinditel?
Checked exceptions – kontrollitud erindid : Informeerivad kasutajat olukordadest, kus normaalse töö taastamine on võimalik,
Kasutaja peab nendega tegelema!
Kontrollime alati kas erind tekitati ..
Kui jah, siis töötleme seda
Võimalusel taastame programmi normaalse töö ...
... ja/või vihjame kasutajale, kuidas seda teha
Unchecked exceptions – kontrollimata Taastumine pole tavaliselt võimalik
Kood kompileerub, aga kuskil on (
loogika )viga
Näiteks meetod ootab objekti, aga saab hoopis null; või jagamine nulliga (Kasutame parem
Optionali ja vähendame tõenäosust)
Võib jätta töötlemata, eriti kui tulevad kellegi teise loodud API kaudu
Errors - vead Programmikoodist sõltumatud vead
Programmiväline viga, nt faili lugemine katkeb kõvaketta vea tõttu
Kasutajale kuvatakse töötlemata
veateade Reeglina on
arendaja jaoks olulised checked exceptions e kontrollitud erindid.
Unchecked exception viitab mingile (loogika)veale
programmis - tavaliselt ei püüa, vaid parandame
programmi.
Error on programmiväline viga – seda me ise kunagi ei loo, nt
riistvara rike.
Erindid on laiendatavad. Exception Runtime Exception
[checked exception]SomeOtherException
Erindi loomine :
Esmalt mõtle, kas erindit on vaja!
Võib-olla saab
asendada if-
else blokiga
3. Kas peale erindi kinnipüüdmist on võimalik programmi normaalse tööga jätkata? Checked exceptions ehk kontrollitud erindid Informeerivad kasutajat olukordadest, kus normaalse töö
taastamine on võimalik, seega on võimalik normaalse tööga jätkata.
printStackTrace() :
kuvab informatsiooni vea esinemise kohta – nn veapinu – leiate vea allika
getMessage() :
kuvab veateate
Enda erindite loomine:
Esmalt püüa kasutada
Java erindeid
Loomise eesmärgiks võib olla pakkuda spetsiifilisemaid
meetodeid vea põhjuste kohta
4. Mida teeb throws võtmesõna? Mitte kunagi ärge kasutage seda main
meetodis , oma programmi sisendpunktis Erindit ei ole sealt ju
mitte kuskile edasi suunata!
throw new StudentException();
meetod peab informeerima kontrollitud erindi võimalikkusest: throws StudentException
Kui
veaolukord tekib – loo kõige sobivamat tüüpi erind
Üldist tüüpi Exception objekti ei tohiks kunagi ise luua
5. Seleta voo ( stream ) mõistet üldiselt. Mis on voog ?
Stream ehk vood : Andmete liikumise kanal
Küllaltki
abstraktne mõiste
Stream ei
salvesta andmeid
Stream ei muuda algandmeid
Andmed, nt info.txt Voog, nt FileInputStream Töötleja: for (c = in.read() ...
Boilerplate – kood, mis funktsionaalsust ei lisa, kuid on vajalik, et programm töötaks. Näited: main
meetod, for
tsükkel jmt
Saate igast kollektsioonist voo moodustada.
Loetavam kood – kaob vajadus tsüklite ja itereerimise järele (seda teeb Java ise)
Efektiivsem kood – Java sisemine
optimeerimine töötab arvatavasti paremini kui
omalooming 6. Kas Java 8 stream'ide meetodid filter (), map(), count (), collect () on entusiastlikud ( eager ) või laisad
( lazy ) meetodid? Mis vahe neil on?
Eager methods (entusiastlikud meetodid) - alustavad kohe tööd.
Lazy
method (laisad meetodid) - viivitavad tööga kuni mõni entusiastlik meetod nad tööle paneb.
Filter, map on lazy; count,collect on eager .
7. Tooge näide, kus on rikutud kõrge kokkukuuluvuse ja vähese sõltuvuse printsiipi (high cohesion –
low coupling). Milles rikkumine seisneb?
Kõrge kokkukuuluvus (high cohesion) : Hea OOP
arhitektuur on selline, kus süsteem koosneb omavahel loogiliselt seotud klassidest,
kus igal klassil on oma kindel roll
Vähene sõltuvus (loose coupling) : Samas on iga klass võimalikult sõltumatu ja seda saab vajadusel kasutada ka mõnes muus
süsteemis
Miks vähendada sõltuvust? Komponentide
taaskasutus Muutmisel on loogika ühes kohas
Kuidas sõltuvuse ära tunnete?
o
Üks klass teab liiga palju teisest Register register = new StudentRegister();
int numOfStudents = register.numOfStudents;
Kõige lihtsam juhtum: kasutab teise välju .
Lahendus: eraldi meetod > int numOfStudents = register.getNumOfStudents();
Card c = new DebitCard();
if (c.hasFunds()) {
if (c.notBlocked()) {
if (c.onlinePaymentsAllowed()){
c.makePayment(
amount );}}}
Teab teise klassi detaile – kas on ikka vaja?
Lahendus: intiimsuse vähendamine Card c = new DebitCard();
if (!c.makePayment(amount)) { // do
something if
payment fails }
Kontrolli õnnestumist... try { c.makePayment(amount)
} catch (PaymentException e) {
// do something if payment fails } ..
või kasuta erindeid Detailid jäävad DebitCard klassi Öelge
objektile , mida te teha
soovite , laske objektil otsustada.
8. Kuidas määratakse lambda tüüp?
Lambda – anonüümne funktsioon, mis realiseerib teatud tüüpi liidest.
Eeldus: c on kollektsioon, nt List. See on source.
long cnt = c.stream()/**
loome kollektsioonist voo*/ .filter(s -> s.studies("IAPB"))./**
filtreerime,
intermediate operation*/filter(s -> s.hasGrade(3)) .count()/**
tulemus, terminal operation*/;
Voog on ühekordseks kasutamiseks – vaheoperatsioone võib olla mitu, kuid üks lõppoperatsioon.
Lambda tüüp ja argumentide tüübid määratakse selle meetodi kaudu, mis lambdat tarvitab, nt filter():
filter(Predicate predicate);
Use Case Examples of lambdas A boolean expression (List list) -> list.isEmpty()
Creating objects () -> new
Apple (10)
Consuming from an object from an object (Apple a) -> {
System.out.println(a.getWeight()); }
Select /extract from an object (String s) -> s.length()
Combine two values (int a, int b) -> a * b
Compare two objects (Apple a1, Apple a2) ->
a1.getWeight().compareTo(a2.getWeight())
(parameters) -> expression
return ’I ei ole vaja
(parameters) -> { statements; } return on vajalik
c -> c.getName().equals("yellow");
c argument/id; -> lambda
nool ; argumendi kasutus lambda kehas; lambda keha;
9. Mis on funktsionaalne liides ( functional interface ) ja milleks kasutatakse?
@FunctionalInterface
public interface Predicate {
boolean test(T t); ... }
Tähistatakse annotatsiooniga @FunctionalInterface
Liides, mis sisaldab täpselt ühte abstraktset meetodit
Functional interfaces provide
target types for lambda expressions and method references. Each
functional interface has a
single abstract method, called the
functional method for that functional
interface, to which the lambda expression's
parameter and return types are matched or adapted.
Function apply()
Predicate test()
Consumer accept()
Supplier get() Generic types
Olemas on palju spetsialiseeritud funktsionaalseid liideseid, mille kasutamine on efektiivsem kui üldise
Functioni kasutamine, nt LongToIntFunction kui
sisend on long ja väljund int
10. Mis on ja kuidas kasutatakse viidet meetodile obj::method süntaksiga
Nagu viitame tavalisele Java objektile, saame viidata ka meetodile
Viide ::meetodi_nimi
Viide saab olla viide klassile, objektile..
Kui viitate staatilisele meetodile: MyEntity(Viide klassile(klassinimi))::myMethod Sulgusid ei ole!
Kui viitate objektimeetodile: MyEntity m = new MyEntity();
Viide objektile m::myMethod. Sulgusid ei ole!
Viide meetodile ei ole väljakutse ! SuperHero.rescueWorld(); - Väljakutse, käivitab meetodi!
SuperHero::rescueWorld; - Viide, ei käivita meetodit!
Viidatud meetodile lisatakse argumendid, kutsudes välja funktsionaalses liideses defineeritud meetodi.
11. Mis on target typing?
Argumendi tüüp järeldatakse selle meetodi signatuurist, kellele ta argumendiks on:
filter(s -> hasGrade()) Väljakutse
filter(Predicate) { Definitsioon
Kuna filter() eeldab argumendina tüüpi Predicate, siis kontrollitakse, kas see lambda sobib Predicate'ks.
The type of a lambda is deduced from the
context in which the lambda is used. The type expected for
the lambda expression inside the context (for example, a method parameter that it’s
passed to or a local
variable that it’s assigned to) is called the target type.
The type-checking
process is deconstructed as follows:
First, you
look up the declaration of the filter method.
Second, it expects as the second formal parameter an object of type Predicate- (the target type).
Third, Predicate is a functional interface defining a single abstract method called test.
Fourth , the method test describes a function descriptor that accepts an Apple and
returns a boolean.
12. Mis on sõltuvuse sisestamine (dependency injection)? Sõltuvuse sisestamine klassi
Anname klassile mingi objekti, mida ta oma töös kasutab
Vastuvõttev klass ei pea ise mõtlema, millist tüüpi objekti luua või kasutada
Näide :
Klass DataParser töötleb andmeid
Andmed tulevad ja
kirjutatakse tagasi DataService-tüüpi teenusega
Olemas on mitu erinevat DataService teenust – millist kasutada?
Sõltuvuse sisestamine (dependency injection): klass kasutab mingit teenust, mis on spetsifitseeritud
liidesega. Klassi kasutaja/looja tarnib sellele liidesele vastava teenuse.
public class DigiDocContainer {
private CryptoAlgorithm cryto;
public DigiDocContainer( CryptoAlgorithm
crypto ) {
this.cryto = crypto; } }
DigiDocContainer kasutab krüpteerimiseks mingit liidesele CryptoAlgorithm vastavat
algoritmi objekti
Konkreetne
realisatsioon (
algoritm ) sisestatakse nt konstruktoris
13. Mis on race condition (konkurentsiolukord)?
Programmi
korrektne täitmine sõltub tegevuste järjestusest.
Kui järjestus ei ole tagatud (nt atomaarse operatsiooni, sünkroniseerimise vmt abil), siis võib race
condition põhjustada
bugi .
Race conditionist põhjustatud mitmelõimelise programmi viga on väga raske tuvastada, sest see ei ole
üheselt korratav – vea ilmnemine sõltub lõimede tööaja jagunemisest ja täitmise järjekorrast, mis erineb
igal täitmisel
14. Millised võimalused on turvaliseks andmete jagamiseks lõimede vahel?
1. võimalus:
final muutuja abil Kõik
lõimed näevad garanteeritult final muutuja õige väärtust esimesel korral kui nad seda
loevad
Samas see ei ole ülearu kasulik, sest final muutuja tuleks initsialiseerida
hiljemalt konstruktoris
Final tuleb kasutada kui soovite, et teie
lõim näeks kindlasti mingit objekti, mille olete loonud enne
lõime käivitamist
Esimesena uurige alati, kas on võimalik andmete jagamist vältida, korraldada enne lõime käivitamist või
minimiseerida.
Võimalusel eelistage muudetamatuid andmeid:
Mutable types Sisu võib peale loomist muutuda
Immutable types Peale loomist sisu ei muutu (Final keyword, Effectively final – peale loomist
sisu ei muutu)
15. Selgitage kahte olulist ideed, miks andmete kasutust sünkroniseerida?
Kui mitu lõime toimetavad sünkroniseeritud objektiga, siis on kindlustatud, et järgmine lõim näeb
eelmise lõime töö tulemust.
1. Üks lõim ei näe teise tööd poolikult (inconsistent state)
2. Üks lõim näeb, mida teine objektiga teinud on
Sünkroniseerimine on vajalik ka selleks, et see koopia oleks ajakohane
Ligipääs objektile või meetodile sünkroniseeritakse:
Tagame, et vaid üks lõim korraga antud koodi kasutab
Tagame koodi järjestikuse täitmise (synchronized keyword abil)
Oht: kui sünkroniseerite lõimede põhitööks vajalikud andmed, siis ei pruugi lõimede kasutamine efekti
anda
Halval juhul võtab rohkem aega kui ühelõimeline lahendus (lõimede haldamise overhead)
Sünkroniseerida saab:
Objekti
Meetodit (sünkroniseerib THIS objekti)
Staatilist meetodit (sünkroniseerib Class objekti)
Miks lõimed ei näe üksteise tööd? Iga lõim töötab oma koopiaga muutujatest ja sünkroniseerib neid
aeg-ajalt. Muutujate ajakohasus ei ole ilma kasutajapoolse sünkroniseerimisega tagatud.
16. Mitu objekti võivad korraga objekti monitori lukus hoida (kindlasti uurige, kas ootav lõim hoiab
monitori lukus või mitte)?
Monitor on
kontseptsioon , mis on vajalik lõimede töö koordineerimiseks
Igal objektil on monitor, mida lõim saab lukustada
Kui monitor on lukus, siis teine lõim seda kasutada ei saa
Objekti kasutamist saab lukustada synchronized võtmesõna abil.
17. Kirjeldage, kuidas tekib deadlock ja kuidas see on seotud einestavate filosoofide probleemiga?
Üks lõim lukustab objekti monitori ja kutsub välja teise lõime, mis soovib sama monitori kasutada. Üks
ootab teise taga, kumbki ei saa jätkata.
Sama einestavate filosoofide korral: iga
filosoof tahab einestamiseks kaks kahvlit. Seega iga filosoof ei
saa hakata sööma, kuni vasakpoolne
kahvel vabaneb.
18. Milline praktiline kasu on sellest, et objekti monitori lukk on taassisenetav (reentrant)?
Java objektide monitorid on taassisenetavad (reentrant).
Kui üks lõim paneb objekti monitori lukku ja kutsub sama lõime piires välja meetodi, mis seda uuesti
lukustab, siis deadlock'i ei teki, kuna sama lõim võib uuesti siseneda
Enamasti kasulik
Ohtlik juhul kui sama lõim teeb mitu omavahel
vastuolus olevat tegevust
Tulnukmeetodid on sellised, mille üle te ei oma kontrolli – nt mõni ülekirjutatav meetod objektil, mida
kasutaja võib muuta
Miks selliseid meetodeid võimalusel vältida? Vastus: Võivad põhjustada deadlock'i
19. Mis vahe on sisemisel ja välimisel sünkroniseerimisel? Leidke mõlema näide viimasest
praktikumitööst, et veenduda oma arusaamises (näidislahendus Moodles).
Sisemine sünkroniseerimine – objekt hoolitseb ise, et teda oleks turvaline lõimedes kasutada
while(true)
Väline sünkroniseerimine – kasutaja peab sünkroniseerima objekti kasutust synchronize()
20. Kirjeldage lõimeturvalisuse viit taset Blochi järgi.
Lõimeturvalisuse tasemed (Bloch)
Muudetamatud tüübid (immutable) – sünkroniseerimist ei ole tarvis, jagage julgelt lõimede
vahel
Tingimusteta turvalised ( thread - safe ) – muudetavad, kuid sisemiselt sünkroniseeritud – võite
julgelt kasutada
Teatud tingimustel turvalised – nt mõni meetod ei ole lõimedega kasutamiseks turvaline ja selle
kasutamisel vajab objekt eraldi sünkroniseerimist
Lõimedes kasutamiseks ohtlikud – tavaliselt muudetavad
klassid , mille kasutamisel tuleb alati
väliselt sünkroniseerida
Lõimevaenulikud klassid (thread-hostile) – klassid, mille kasutamine isegi korraliku välise
sünkroniseerimise puhul ei taga lõimeturvalisust. Selliseid
klasse ei tohiks lõimedes kasutada
Enamasti tingitud klassi sees sünkroniseerimata static muutujate kasutamisest
Mõelge programmeerides, kas teie programm on turvaline lõimedega töö seisukohalt
Seda isegi juhul, kui te ise oma programmis lõimesid ei kasuta
21. Millal on ülesande lahendamine lõimedega kiirem kui ühe lõimega? Millal aeglasem?
Kui
arvutil on üle 1 tuuma ja task on piisvalt suur (s.t 1 thread ei tee seda kiiremini ära kui N threadi alles
ennast püsti veavad) ja ülesanne on tükeldatav (Võimalik anda igale threadile oma töö, nii, et see ei häiri
teisi, pisike häirmine lubatud, selleks on synchrnoized keyword, kuid üldjoontes nagu puude tassimine,
vahet pole, saadad 10
mehikest rabama, üksteist nad ei sega)
22. Mille alusel valida sobiv funktsionaalne liides?
Võid funktsionaalsest liidesest mõelda kui muutuja tüübist. Kui kasutada funktsionaalset liidest oma
funktsiooni realiseerimisel void a(MyInterface), siis saad sellele funktsioonile ette sööta teise
funktsiooni, mis täidab MyInterface reegleid. Üldjuhul pead arvestama sisend ja väljundandmetega. (Sa
ei saa kasutada liidest, mis soovib tagastada
inti , kui vajad double ntx)
Veel erinditest:
Erindite aheldamine e chaining : Liiga üldine viga täpsemaks
Liiga täpne viga üldisemaks
Mitme vea
liitmine Kus püüda erindit? Kui püüda probleemi
tekkekohale lähemal, siis on rohkem võimalusi probleemi lahendamiseks
Kui püüda võimalikult kõrgel tasemel, siis ei pea allpool liiga palju koodi erinditele kulutama
(tsentraliseeritud veahaldus)
Säilita veaeelne olukord (nn failure atomicity) Kui erind tekib, püüa see luua nii, et objekti olek ei saaks muudetud - nii on võimalik objekti peale vea
põhjuse kõrvaldamist edasi kasutada
Finally : Finally
blokk võimaldab lõputegevusi, mis tehakse igal juhul, sõltumata sellest, kas kood läbis try
või kukkus catchi
Üldiselt ei kasutata eriti
Voog vs kollektsioon: Kollektsioonid on mõeldud objektide hoidmiseks ja efektiivseks haldamiseks - Reeglina huvitume
objektidest Vood on mõeldud objektikogumi töötlemiseks, teisendamiseks, üldandmete kogumiseks jms -
Reeglina huvitume üldistustest
Lõim ja protsess: Põhimõtteliselt mõlemad tagavad koodi täitmise järjestatuse
Protsess – eraldi mälueraldusega
Thread e lõim – jagatud mälu
Kasutaja vaatevinklist on üks programm sageli üks protsess (kuid mitte alati!)
Üks protsess võib aga käivitada erinevaid lõimesid
Paralleeltöö:
Paralleeltöö ehk lõimedega
programmeerimine on vajalik eelkõige kahel otstarbel:
teha mitut asja samal ajal
kasutada efektiivselt arvuti ressurssi (eelkõige protsessori tuumasid)
Lõim ehk thread Meie jaoks Java objekt
Oluline meetod run()
Kaks põhilist eesmärki:
o Teha mitut asja paralleelselt (nt UI ja taustategevused) [Efekt saavutatakse ka ühe
tuumaga arvutil]
o Kasutada maksimaalselt arvuti ressursse (nt andmetöötlus, arvutused jms) [Efekt
saavutatakse mitme tuumaga arvutil]
Lõimede loomine Ise hallates ja Thread ja/või Runnable objekte
luues
Lastes mõnel API-l lõimesid hallata
join ()
Käesolev thread peab ootama kuni viidatud thread on töö lõpetanud: th.join();
Millise nimega threadi objekt ootab? Vastus : Ei tea nime.. Pealõim ootas
Paralleelne vs järjestikune täitmine Lõimed töötavad iseseisvalt, mitte nii nagu me neid välja kutsume...
... muidu poleks mõtet lõimesid luau
Sleep peatab threadi täitmise
näidatud ajaks (millisekundites);
Lõime töö lõpeb kui run() meetod lõpetab
Lõimede tööd saab katkestada Thread.interrupt();
Katkestamise korral väljastatakse InterruptedException, millega saab
Optimiseerimine
JVM-il on lubatud lugemis- ja kirjutamisoperatsioone ümber korralda oma
äranägemise järgi – ta ei pea
neid
teostama programmeerija antud järjekorras
volatile Volatile võtmesõnaga tähistatud välja kõik lugemise ja kirjutamise
operatsioonid toimuvad otse
mälus, mitte lõime koopia peal
Volatile muutuja kirjutamist/lugemist ei tohi JVM
optimiseerida operatsioonide
ümberkorraldamisega
Atomaarsus Kui kaks lõime kasutavad sama muutujat, siis atomaarne operatsioon tähendab, et kõik ühe
lõime poolt selle operatsiooni käigus tehtavad asjad tehakse järjest
Nt counteri realiseerimisel pole vaja atomaarset muutujat sünkroniseerida
Operatsioonid long ja double tüüpidega ei ole atomaarsed (isegi mitte read ja write) Vajalik
sünkroniseerimine!
java.util.concurrent.
atomic Objektid, mis
toetavad atomaarseid operatsioone ilma
sünkroniseerimiseta
InterruptedException
Lõime tööd saab katkestada ja lõim saab sellega tegeleda
püüdes kinni InterruptedExceptioni
Deemonid
Lõim, mille töö lõppeb, kui pealõime töö lõppeb
wait():
Kui lõime töö jaoks vajalik eeltingimus ei ole täidetud, võib ta synchronized bloki sees oodata kuni
tingimus saab täidetud
notify():
Lõim, mis seda eeltingimust täidab, saab ootavale lõimele märku anda, et ta üles ärkaks ja uuesti
prooviks
Mis juhtub kui lukustada immutable objekt ja siis seda muuta? Me tegelikult ei peaks muutmiseks lukustama Integeri, Stringi jt muudetamatuid immutable
objektitüüpe, kus objekt uuendatakse Lukustatakse objekt, mitte viide. Kui muudate nt Integeri väärtust,
luuakse uus objekt, millele te ei oma lukku
Kõik kommentaarid